From fb3f6d0c134424585fbeae2603f084cb4416ffb4 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 5 Nov 2023 12:12:41 +0100 Subject: [PATCH 001/280] Created new files for the new analysis and copied content from affine equality analysis --- .../apron/linearTwoVarEquality.apron.ml | 39 ++ .../apron/linearTwoVarEquality.no.apron.ml | 3 + .../apron/linearTwoVarEqualityDomain.apron.ml | 650 ++++++++++++++++++ .../linearTwoVarEqualityDomain.no-apron.ml | 5 + src/dune | 8 + 5 files changed, 705 insertions(+) create mode 100644 src/analyses/apron/linearTwoVarEquality.apron.ml create mode 100644 src/analyses/apron/linearTwoVarEquality.no.apron.ml create mode 100644 src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml create mode 100644 src/cdomains/apron/linearTwoVarEqualityDomain.no-apron.ml diff --git a/src/analyses/apron/linearTwoVarEquality.apron.ml b/src/analyses/apron/linearTwoVarEquality.apron.ml new file mode 100644 index 0000000000..b95d0bd2d7 --- /dev/null +++ b/src/analyses/apron/linearTwoVarEquality.apron.ml @@ -0,0 +1,39 @@ +(** {{!RelationAnalysis} Relational integer value analysis} using an OCaml implementation of the linear two-variable equalities domain ([...]). + + @see A. Flexeder, M. Petter, and H. Seidl Fast Interprocedural Linear Two-Variable Equalities. *) + +open Analyses + +include RelationAnalysis + +(** TODO: modify code *) +let spec_module: (module MCPSpec) Lazy.t = + lazy ( + let module AD = AffineEqualityDomain.D2 (VectorMatrix.ArrayVector) (VectorMatrix.ArrayMatrix) in + let module RD: RelationDomain.RD = + struct + module Var = AffineEqualityDomain.Var + module V = AffineEqualityDomain.V + include AD + end + in + let module Priv = (val RelationPriv.get_priv ()) in + let module Spec = + struct + include SpecFunctor (Priv) (RD) (RelationPrecCompareUtil.DummyUtil) + let name () = "affeq" + end + in + (module Spec) + ) + +let get_spec (): (module MCPSpec) = + Lazy.force spec_module + +let after_config () = + let module Spec = (val get_spec ()) in + MCP.register_analysis (module Spec : MCPSpec); + GobConfig.set_string "ana.path_sens[+]" (Spec.name ()) + +let _ = + AfterConfig.register after_config diff --git a/src/analyses/apron/linearTwoVarEquality.no.apron.ml b/src/analyses/apron/linearTwoVarEquality.no.apron.ml new file mode 100644 index 0000000000..0a444baa9b --- /dev/null +++ b/src/analyses/apron/linearTwoVarEquality.no.apron.ml @@ -0,0 +1,3 @@ +(* This analysis is empty on purpose. It serves only as an alternative dependency + in cases where the actual domain can't be used because of a missing library. + It was added because we don't want to fully depend on Apron. *) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml new file mode 100644 index 0000000000..4b3ed42ca6 --- /dev/null +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -0,0 +1,650 @@ +(** OCaml implementation of the affine equalities domain. + + @see A. Flexeder, M. Petter, and H. Seidl Fast Interprocedural Linear Two-Variable Equalities. *) + +(** TODO: description *) + +open Batteries +open GoblintCil +open Pretty +module M = Messages +open Apron +open VectorMatrix + +(** TODO: modify code *) +module Mpqf = struct + include Mpqf + let compare = cmp + let zero = of_int 0 + let one = of_int 1 + let mone = of_int (-1) + + let get_den x = Z_mlgmpidl.z_of_mpzf @@ Mpqf.get_den x + + let get_num x = Z_mlgmpidl.z_of_mpzf @@ Mpqf.get_num x + let hash x = 31 * (Z.hash (get_den x)) + Z.hash (get_num x) +end + +module Var = SharedFunctions.Var +module V = RelationDomain.V(Var) + +(** It defines the type t of the affine equality domain (a struct that contains an optional matrix and an apron environment) and provides the functions needed for handling variables (which are defined by RelationDomain.D2) such as add_vars remove_vars. + Furthermore, it provides the function get_coeff_vec that parses an apron expression into a vector of coefficients if the apron expression has an affine form. *) +module VarManagement (Vec: AbstractVector) (Mx: AbstractMatrix)= +struct + include SharedFunctions.EnvOps + module Vector = Vec (Mpqf) + module Matrix = Mx(Mpqf) (Vec) + + type t = { + mutable d : Matrix.t option; + mutable env : Environment.t + } + [@@deriving eq, ord, hash] + + let empty_env = Environment.make [||] [||] + + let bot () = + {d = Some (Matrix.empty ()); env = empty_env} + + let bot_env = {d = None; env = empty_env} + + let is_bot_env t = t.d = None + + let copy t = {t with d = Option.map Matrix.copy t.d} + + let dim_add (ch: Apron.Dim.change) m = + Array.iteri (fun i x -> ch.dim.(i) <- x + i) ch.dim; + Matrix.add_empty_columns m ch.dim + + let dim_add ch m = timing_wrap "dim add" (dim_add ch) m + + let dim_remove (ch: Apron.Dim.change) m del = + if Array.length ch.dim = 0 || Matrix.is_empty m then m else ( + Array.iteri (fun i x-> ch.dim.(i) <- x + i) ch.dim; + let m' = if not del then let m = Matrix.copy m in Array.fold_left (fun y x -> Matrix.reduce_col_with y x; y) m ch.dim else m in + Matrix.remove_zero_rows @@ Matrix.del_cols m' ch.dim) + + let dim_remove ch m del = timing_wrap "dim remove" (dim_remove ch m) del + + let change_d t new_env add del = + if Environment.equal t.env new_env then t else + let dim_change = if add then Environment.dimchange t.env new_env + else Environment.dimchange new_env t.env + in match t.d with + | None -> bot_env + | Some m -> {d = Some (if add then dim_add dim_change m else dim_remove dim_change m del); env = new_env} + + let change_d t new_env add del = timing_wrap "dimension change" (change_d t new_env add) del + + let add_vars t vars = + let t = copy t in + let env' = add_vars t.env vars in + change_d t env' true false + + let add_vars t vars = timing_wrap "add_vars" (add_vars t) vars + + let drop_vars t vars del = + let t = copy t in + let env' = remove_vars t.env vars in + change_d t env' false del + + let drop_vars t vars = timing_wrap "drop_vars" (drop_vars t) vars + + let remove_vars t vars = drop_vars t vars false + + let remove_vars t vars = timing_wrap "remove_vars" (remove_vars t) vars + + let remove_vars_with t vars = + let t' = remove_vars t vars in + t.d <- t'.d; + t.env <- t'.env + + let remove_filter t f = + let env' = remove_filter t.env f in + change_d t env' false false + + let remove_filter t f = timing_wrap "remove_filter" (remove_filter t) f + + let remove_filter_with t f = + let t' = remove_filter t f in + t.d <- t'.d; + t.env <- t'.env + + let keep_filter t f = + let t = copy t in + let env' = keep_filter t.env f in + change_d t env' false false + + let keep_filter t f = timing_wrap "keep_filter" (keep_filter t) f + + let keep_vars t vs = + let t = copy t in + let env' = keep_vars t.env vs in + change_d t env' false false + + let keep_vars t vs = timing_wrap "keep_vars" (keep_vars t) vs + + let vars t = vars t.env + + let mem_var t var = Environment.mem_var t.env var + + include ConvenienceOps(Mpqf) + + let get_c v = match Vector.findi (fun x -> x <>: Mpqf.zero) v with + | exception Not_found -> Some Mpqf.zero + | i when Vector.compare_length_with v (i + 1) = 0 -> Some (Vector.nth v i) + | _ -> None + + let get_coeff_vec (t: t) texp = + (*Parses a Texpr to obtain a coefficient + const (last entry) vector to repr. an affine relation. + Returns None if the expression is not affine*) + let open Apron.Texpr1 in + let exception NotLinear in + let zero_vec = Vector.zero_vec @@ Environment.size t.env + 1 in + let neg v = Vector.map_with (fun x -> Mpqf.mone *: x) v; v in + let is_const_vec v = Vector.compare_length_with (Vector.filteri (fun i x -> (*Inefficient*) + Vector.compare_length_with v (i + 1) > 0 && x <>: Mpqf.zero) v) 1 = 0 + in + let rec convert_texpr texp = + begin match texp with + (*If x is a constant, replace it with its const. val. immediately*) + | Cst x -> let of_union union = + let open Coeff in + match union with + | Interval _ -> failwith "Not a constant" + | Scalar x -> (match x with + | Float x -> Mpqf.of_float x + | Mpqf x -> x + | Mpfrf x -> Mpfr.to_mpq x) in Vector.set_val zero_vec ((Vector.length zero_vec) - 1) (of_union x) + | Var x -> + let zero_vec_cp = Vector.copy zero_vec in + let entry_only v = Vector.set_val_with v (Environment.dim_of_var t.env x) Mpqf.one; v in + begin match t.d with + | Some m -> let row = Matrix.find_opt (fun r -> Vector.nth r (Environment.dim_of_var t.env x) =: Mpqf.one) m in + begin match row with + | Some v when is_const_vec v -> + Vector.set_val_with zero_vec_cp ((Vector.length zero_vec) - 1) (Vector.nth v (Vector.length v - 1)); zero_vec_cp + | _ -> entry_only zero_vec_cp end + | None -> entry_only zero_vec_cp end + | Unop (u, e, _, _) -> + begin match u with + | Neg -> neg @@ convert_texpr e + | Cast -> convert_texpr e (*Ignore since casts in apron are used for floating point nums and rounding in contrast to CIL casts*) + | Sqrt -> raise NotLinear end + | Binop (b, e1, e2, _, _) -> + begin match b with + | Add -> let v1 = convert_texpr e1 in Vector.map2_with (+:) v1 (convert_texpr e2); v1 + | Sub -> let v1 = convert_texpr e1 in Vector.map2_with (+:) v1 (neg @@ convert_texpr e2); v1 + | Mul -> + let x1, x2 = convert_texpr e1, convert_texpr e2 in + begin match get_c x1, get_c x2 with + | _, Some c -> Vector.apply_with_c_with ( *:) c x1; x1 + | Some c, _ -> Vector.apply_with_c_with ( *:) c x2; x2 + | _, _ -> raise NotLinear end + | _ -> raise NotLinear end + end + in match convert_texpr texp with + | exception NotLinear -> None + | x -> Some(x) + + let get_coeff_vec t texp = timing_wrap "coeff_vec" (get_coeff_vec t) texp +end + +(** As it is specifically used for the new affine equality domain, it can only provide bounds if the expression contains known constants only and in that case, min and max are the same. *) +module ExpressionBounds (Vc: AbstractVector) (Mx: AbstractMatrix): (SharedFunctions.ConvBounds with type t = VarManagement(Vc) (Mx).t) = +struct + include VarManagement (Vc) (Mx) + + let bound_texpr t texpr = + let texpr = Texpr1.to_expr texpr in + match get_coeff_vec t texpr with + | Some v -> begin match get_c v with + | Some c when Mpqf.get_den c = IntOps.BigIntOps.one -> + let int_val = Mpqf.get_num c + in Some int_val, Some int_val + | _ -> None, None end + | _ -> None, None + + + let bound_texpr d texpr1 = + let res = bound_texpr d texpr1 in + match res with + | Some min, Some max -> if M.tracing then M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string min) (IntOps.BigIntOps.to_string max); res + | _ -> res + + let bound_texpr d texpr1 = timing_wrap "bounds calculation" (bound_texpr d) texpr1 +end + +module D(Vc: AbstractVector) (Mx: AbstractMatrix) = +struct + include Printable.Std + include ConvenienceOps (Mpqf) + include VarManagement (Vc) (Mx) + + module Bounds = ExpressionBounds (Vc) (Mx) + + module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (SharedFunctions.Tracked) + + type var = V.t + + let show t = + let conv_to_ints row = + let module BI = IntOps.BigIntOps in + let row = Array.copy @@ Vector.to_array row + in + for i = 0 to Array.length row -1 do + let val_i = Mpqf.of_mpz @@ Z_mlgmpidl.mpzf_of_z @@ Mpqf.get_den row.(i) + in Array.iteri(fun j x -> row.(j) <- val_i *: x) row + done; + let int_arr = Array.init (Array.length row) (fun i -> Mpqf.get_num row.(i)) + in let div = Mpqf.of_mpz @@ Z_mlgmpidl.mpzf_of_z @@ Array.fold_left BI.gcd int_arr.(0) int_arr + in Array.iteri (fun i x -> row.(i) <- x /: div) row; + Vector.of_array @@ row + in + let vec_to_constraint vec env = + let vars, _ = Environment.vars env + in let dim_to_str var = + let vl = Vector.nth vec (Environment.dim_of_var env var) + in let var_str = Var.to_string var + in if vl =: Mpqf.one then "+" ^ var_str + else if vl =: Mpqf.mone then "-" ^ var_str + else if vl <: Mpqf.mone then Mpqf.to_string vl ^ var_str + else if vl >: Mpqf.one then Format.asprintf "+%s" (Mpqf.to_string vl) ^ var_str + else "" + in + let c_to_str vl = + if vl >: Mpqf.zero then "-" ^ Mpqf.to_string vl + else if vl <: Mpqf.zero then "+" ^ Mpqf.to_string vl + else "" + in + let res = (String.concat "" @@ Array.to_list @@ Array.map dim_to_str vars) + ^ (c_to_str @@ Vector.nth vec (Vector.length vec - 1)) ^ "=0" + in if String.starts_with res "+" then String.sub res 1 (String.length res - 1) else res + in + match t.d with + | None -> "Bottom Env" + | Some m when Matrix.is_empty m -> "⊤" + | Some m -> + let constraint_list = List.init (Matrix.num_rows m) (fun i -> vec_to_constraint (conv_to_ints @@ Matrix.get_row m i) t.env) + in Format.asprintf "%s" ("[|"^ (String.concat "; " constraint_list) ^"|]") + + let pretty () (x:t) = text (show x) + let printXml f x = BatPrintf.fprintf f "\n\n\nmatrix\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) + + let name () = "affeq" + + let to_yojson _ = failwith "ToDo Implement in future" + + + let is_bot t = equal t (bot ()) + + let bot_env = {d = None; env = Environment.make [||] [||]} + + let is_bot_env t = t.d = None + + let top () = failwith "D.top ()" + + let is_top _ = false + + let is_top_env t = (not @@ Environment.equal empty_env t.env) && GobOption.exists Matrix.is_empty t.d + + let meet t1 t2 = + let sup_env = Environment.lce t1.env t2.env in + let t1, t2 = change_d t1 sup_env true false, change_d t2 sup_env true false + in if is_bot t1 || is_bot t2 then bot() else + let m1, m2 = Option.get t1.d, Option.get t2.d in + match m1, m2 with + | x, y when is_top_env t1-> {d = Some (dim_add (Environment.dimchange t2.env sup_env) y); env = sup_env} + | x, y when is_top_env t2 -> {d = Some (dim_add (Environment.dimchange t1.env sup_env) x); env = sup_env} + | x, y -> + let rref_matr = Matrix.rref_matrix_with (Matrix.copy x) (Matrix.copy y) in + if Option.is_none rref_matr then bot () else + {d = rref_matr; env = sup_env} + + + let meet t1 t2 = + let res = meet t1 t2 in + if M.tracing then M.tracel "meet" "meet a: %s b: %s -> %s \n" (show t1) (show t2) (show res) ; + res + + let meet t1 t2 = timing_wrap "meet" (meet t1) t2 + + let leq t1 t2 = + let env_comp = Environment.compare t1.env t2.env in (* Apron's Environment.compare has defined return values. *) + if env_comp = -2 || env_comp > 0 then false else + if is_bot t1 || is_top_env t2 then true else + if is_bot t2 || is_top_env t1 then false else ( + let m1, m2 = Option.get t1.d, Option.get t2.d in + let m1' = if env_comp = 0 then m1 else dim_add (Environment.dimchange t1.env t2.env) m1 in + Matrix.is_covered_by m2 m1') + + let leq a b = timing_wrap "leq" (leq a) b + + let leq t1 t2 = + let res = leq t1 t2 in + if M.tracing then M.tracel "leq" "leq a: %s b: %s -> %b \n" (show t1) (show t2) res ; + res + + let join a b = + let rec lin_disjunc r s a b = + if s >= Matrix.num_cols a then a else + let case_two a r col_b = + let a_r = Matrix.get_row a r in + Matrix.map2i_with (fun i x y -> if i < r then + Vector.map2_with (fun u j -> u +: y *: j) x a_r; x) a col_b; + Matrix.remove_row a r + in + let case_three a b col_a col_b max = + let col_a, col_b = Vector.copy col_a, Vector.copy col_b in + let col_a, col_b = Vector.keep_vals col_a max, Vector.keep_vals col_b max in + if Vector.equal col_a col_b then (a, b, max) else + let a_rev, b_rev = (Vector.rev_with col_a; col_a), (Vector.rev_with col_b; col_b) in + let i = Vector.find2i (fun x y -> x <>: y) a_rev b_rev in + let (x, y) = Vector.nth a_rev i, Vector.nth b_rev i in + let r, diff = Vector.length a_rev - (i + 1), x -: y in + let a_r, b_r = Matrix.get_row a r, Matrix.get_row b r in + let sub_col = + Vector.map2_with (fun x y -> x -: y) a_rev b_rev; + Vector.rev_with a_rev; + a_rev + in + let multiply_by_t m t = + Matrix.map2i_with (fun i' x c -> if i' <= max then (let beta = c /: diff in + Vector.map2_with (fun u j -> u -: (beta *: j)) x t); x) m sub_col; + m + in + Matrix.remove_row (multiply_by_t a a_r) r, Matrix.remove_row (multiply_by_t b b_r) r, (max - 1) + in + let col_a, col_b = Matrix.get_col a s, Matrix.get_col b s in + let nth_zero v i = match Vector.nth v i with + | exception Invalid_argument _ -> Mpqf.zero + | x -> x + in + let a_rs, b_rs = nth_zero col_a r, nth_zero col_b r in + if not (Z.equal (Mpqf.get_den a_rs) Z.one) || not (Z.equal (Mpqf.get_den b_rs) Z.one) then failwith "Matrix not in rref form" else + begin match Int.of_float @@ Mpqf.to_float @@ a_rs, Int.of_float @@ Mpqf.to_float @@ b_rs with (* TODO: is it safe to go through floats? *) + | 1, 1 -> lin_disjunc (r + 1) (s + 1) a b + | 1, 0 -> lin_disjunc r (s + 1) (case_two a r col_b) b + | 0, 1 -> lin_disjunc r (s + 1) a (case_two b r col_a) + | 0, 0 -> let new_a, new_b, new_r = case_three a b col_a col_b r in + lin_disjunc new_r (s + 1) new_a new_b + | _ -> failwith "Matrix not in rref form" end + in + if is_bot a then b else if is_bot b then a else + match Option.get a.d, Option.get b.d with + | x, y when is_top_env a || is_top_env b -> {d = Some (Matrix.empty ()); env = Environment.lce a.env b.env} + | x, y when (Environment.compare a.env b.env <> 0) -> + let sup_env = Environment.lce a.env b.env in + let mod_x = dim_add (Environment.dimchange a.env sup_env) x in + let mod_y = dim_add (Environment.dimchange b.env sup_env) y in + {d = Some (lin_disjunc 0 0 (Matrix.copy mod_x) (Matrix.copy mod_y)); env = sup_env} + | x, y when Matrix.equal x y -> {d = Some x; env = a.env} + | x, y -> {d = Some(lin_disjunc 0 0 (Matrix.copy x) (Matrix.copy y)); env = a.env} + + let join a b = timing_wrap "join" (join a) b + + let join a b = + let res = join a b in + if M.tracing then M.tracel "join" "join a: %s b: %s -> %s \n" (show a) (show b) (show res) ; + res + let widen a b = + let a_env = a.env in + let b_env = b.env in + if Environment.equal a_env b_env then + join a b + else b + + let narrow a b = a + let pretty_diff () (x, y) = + dprintf "%s: %a not leq %a" (name ()) pretty x pretty y + + let remove_rels_with_var x var env imp = + let j0 = Environment.dim_of_var env var in + if imp then (Matrix.reduce_col_with x j0; x) else Matrix.reduce_col x j0 + + let remove_rels_with_var x var env imp = timing_wrap "remove_rels_with_var" (remove_rels_with_var x var env) imp + + let forget_vars t vars = + if is_bot t || is_top_env t then t + else + let m = Option.get t.d in + if List.is_empty vars then t else + let rec rem_vars m vars' = + begin match vars' with + | [] -> m + | x :: xs -> rem_vars (remove_rels_with_var m x t.env true) xs end + in {d = Some (Matrix.remove_zero_rows @@ rem_vars (Matrix.copy m) vars); env = t.env} + + let forget_vars t vars = + let res = forget_vars t vars in + if M.tracing then M.tracel "ops" "forget_vars %s -> %s\n" (show t) (show res); + res + + let forget_vars t vars = timing_wrap "forget_vars" (forget_vars t) vars + + let assign_texpr (t: VarManagement(Vc)(Mx).t) var texp = + let assign_invertible_rels x var b env = + let j0 = Environment.dim_of_var env var in + let a_j0 = Matrix.get_col x j0 in (*Corresponds to Axj0*) + let b0 = Vector.nth b j0 in + Vector.apply_with_c_with (/:) b0 a_j0; (*Corresponds to Axj0/Bj0*) + let recalc_entries m rd_a = Matrix.map2_with (fun x y -> Vector.map2i_with (fun j z d -> + if j = j0 then y + else if Vector.compare_length_with b (j + 1) > 0 then z -: y *: d + else z +: y *: d) x b; x) m rd_a + in + recalc_entries x a_j0; + if Matrix.normalize_with x then {d = Some x; env = env} else bot () + in + let assign_invertible_rels x var b env = timing_wrap "assign_invertible" (assign_invertible_rels x var b) env in + let assign_uninvertible_rel x var b env = + let b_length = Vector.length b in + Vector.mapi_with (fun i z -> if i < b_length - 1 then Mpqf.mone *: z else z) b; + Vector.set_val_with b (Environment.dim_of_var env var) Mpqf.one; + let opt_m = Matrix.rref_vec_with x b in + if Option.is_none opt_m then bot () else + {d = opt_m; env = env} + in + (* let assign_uninvertible_rel x var b env = timing_wrap "assign_uninvertible" (assign_uninvertible_rel x var b) env in *) + let is_invertible v = Vector.nth v @@ Environment.dim_of_var t.env var <>: Mpqf.zero + in let affineEq_vec = get_coeff_vec t texp + in if is_bot t then t else let m = Option.get t.d in + match affineEq_vec with + | Some v when is_top_env t -> if is_invertible v then t else assign_uninvertible_rel m var v t.env + | Some v -> if is_invertible v then let t' = assign_invertible_rels (Matrix.copy m) var v t.env in {d = t'.d; env = t'.env} + else let new_m = Matrix.remove_zero_rows @@ remove_rels_with_var m var t.env false + in assign_uninvertible_rel new_m var v t.env + | None -> {d = Some (Matrix.remove_zero_rows @@ remove_rels_with_var m var t.env false); env = t.env} + + let assign_texpr t var texp = timing_wrap "assign_texpr" (assign_texpr t var) texp + + let assign_exp (t: VarManagement(Vc)(Mx).t) var exp (no_ov: bool Lazy.t) = + let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in + match Convert.texpr1_expr_of_cil_exp t t.env exp (Lazy.force no_ov) with + | exp -> assign_texpr t var exp + | exception Convert.Unsupported_CilExp _ -> + if is_bot t then t else forget_vars t [var] + + let assign_exp t var exp no_ov = + let res = assign_exp t var exp no_ov in + if M.tracing then M.tracel "ops" "assign_exp t:\n %s \n var: %s \n exp: %a\n no_ov: %b -> \n %s\n" + (show t) (Var.to_string var) d_exp exp (Lazy.force no_ov) (show res) ; + res + let assign_var (t: VarManagement(Vc)(Mx).t) v v' = + let t = add_vars t [v; v'] in + let texpr1 = Texpr1.of_expr (t.env) (Var v') in + assign_texpr t v (Apron.Texpr1.to_expr texpr1) + + let assign_var t v v' = + let res = assign_var t v v' in + if M.tracing then M.tracel "ops" "assign_var t:\n %s \n v: %s \n v': %s\n -> %s\n" (show t) (Var.to_string v) (Var.to_string v') (show res) ; + res + + let assign_var_parallel t vv's = + let assigned_vars = List.map (function (v, _) -> v) vv's in + let t = add_vars t assigned_vars in + let primed_vars = List.init (List.length assigned_vars) (fun i -> Var.of_string (Int.to_string i ^"'")) in (* TODO: we use primed vars in analysis, conflict? *) + let t_primed = add_vars t primed_vars in + let multi_t = List.fold_left2 (fun t' v_prime (_,v') -> assign_var t' v_prime v') t_primed primed_vars vv's in + match multi_t.d with + | Some m when not @@ is_top_env multi_t -> let replace_col m x y = let dim_x, dim_y = Environment.dim_of_var multi_t.env x, Environment.dim_of_var multi_t.env y in + let col_x = Matrix.get_col m dim_x in + Matrix.set_col_with m col_x dim_y in + let m_cp = Matrix.copy m in + let switched_m = List.fold_left2 (fun m' x y -> replace_col m' x y) m_cp primed_vars assigned_vars in + let res = drop_vars {d = Some switched_m; env = multi_t.env} primed_vars true in + let x = Option.get res.d in + if Matrix.normalize_with x then {d = Some x; env = res.env} else bot () + | _ -> t + + let assign_var_parallel t vv's = + let res = assign_var_parallel t vv's in + if M.tracing then M.tracel "ops" "assign_var parallel: %s -> %s \n" (show t) (show res); + res + + let assign_var_parallel t vv's = timing_wrap "var_parallel" (assign_var_parallel t) vv's + + let assign_var_parallel_with t vv's = + let t' = assign_var_parallel t vv's in + t.d <- t'.d; + t.env <- t'.env + + let assign_var_parallel_with t vv's = + if M.tracing then M.tracel "var_parallel" "assign_var parallel'\n"; + assign_var_parallel_with t vv's + + let assign_var_parallel' t vs1 vs2 = + let vv's = List.combine vs1 vs2 in + assign_var_parallel t vv's + + let assign_var_parallel' t vv's = + let res = assign_var_parallel' t vv's in + if M.tracing then M.tracel "ops" "assign_var parallel'\n"; + res + + let substitute_exp t var exp no_ov = + let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in + let res = assign_exp t var exp no_ov in + forget_vars res [var] + + let substitute_exp t var exp ov = + let res = substitute_exp t var exp ov + in if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %s \n exp: %a \n -> \n %s\n" (show t) (Var.to_string var) d_exp exp (show res); + res + + let substitute_exp t var exp ov = timing_wrap "substitution" (substitute_exp t var exp) ov + + (** Assert a constraint expression. + + Additionally, we now also refine after positive guards when overflows might occur and there is only one variable inside the expression and the expression is an equality constraint check (==). + We check after the refinement if the new value of the variable is outside its integer bounds and if that is the case, either revert to the old state or set it to bottom. *) + + exception NotRefinable + + let meet_tcons_one_var_eq res expr = + let overflow_res res = if IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp expr) then res else raise NotRefinable in + match Convert.find_one_var expr with + | None -> overflow_res res + | Some v -> + let ik = Cilfacade.get_ikind v.vtype in + match Bounds.bound_texpr res (Convert.texpr1_of_cil_exp res res.env (Lval (Cil.var v)) true) with + | Some _, Some _ when not (Cil.isSigned ik) -> raise NotRefinable (* TODO: unsigned w/o bounds handled differently? *) + | Some min, Some max -> + assert (Z.equal min max); (* other bounds impossible in affeq *) + let (min_ik, max_ik) = IntDomain.Size.range ik in + if Z.compare min min_ik < 0 || Z.compare max max_ik > 0 then + if IntDomain.should_ignore_overflow ik then bot () else raise NotRefinable + else res + | exception Convert.Unsupported_CilExp _ + | _, _ -> overflow_res res + + let meet_tcons t tcons expr = + let check_const cmp c = if cmp c Mpqf.zero then bot_env else t + in + let meet_vec e = + (*Flip the sign of the const. val in coeff vec*) + Vector.mapi_with (fun i x -> if Vector.compare_length_with e (i + 1) = 0 then Mpqf.mone *: x else x) e; + let res = if is_bot t then bot () else + let opt_m = Matrix.rref_vec_with (Matrix.copy @@ Option.get t.d) e + in if Option.is_none opt_m then bot () else {d = opt_m; env = t.env} in + meet_tcons_one_var_eq res expr + in + match get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with + | Some v -> + begin match get_c v, Tcons1.get_typ tcons with + | Some c, DISEQ -> check_const (=:) c + | Some c, SUP -> check_const (<=:) c + | Some c, EQ -> check_const (<>:) c + | Some c, SUPEQ -> check_const (<:) c + | None, DISEQ + | None, SUP -> + begin match meet_vec v with + | exception NotRefinable -> t + | res -> if equal res t then bot_env else t + end + | None, EQ -> + begin match meet_vec v with + | exception NotRefinable -> t + | res -> if is_bot res then bot_env else res + end + | _, _ -> t + end + | None -> t + + let meet_tcons t tcons expr = timing_wrap "meet_tcons" (meet_tcons t tcons) expr + + let unify a b = + meet a b + + let unify a b = + let res = unify a b in + if M.tracing then M.tracel "ops" "unify: %s %s -> %s\n" (show a) (show b) (show res); + res + + let assert_cons d e negate no_ov = + let no_ov = Lazy.force no_ov in + if M.tracing then M.tracel "assert_cons" "assert_cons with expr: %a %b" d_exp e no_ov; + match Convert.tcons1_of_cil_exp d d.env e negate no_ov with + | tcons1 -> meet_tcons d tcons1 e + | exception Convert.Unsupported_CilExp _ -> d + + let assert_cons d e negate no_ov = timing_wrap "assert_cons" (assert_cons d e negate) no_ov + + let relift t = t + + let invariant t = + match t.d with + | None -> [] + | Some m -> + let earray = Lincons1.array_make t.env (Matrix.num_rows m) in + for i = 0 to Lincons1.array_length earray do + let row = Matrix.get_row m i in + let coeff_vars = List.map (fun x -> Coeff.s_of_mpqf @@ Vector.nth row (Environment.dim_of_var t.env x), x) (vars t) in + let cst = Coeff.s_of_mpqf @@ Vector.nth row (Vector.length row - 1) in + Lincons1.set_list (Lincons1.array_get earray i) coeff_vars (Some cst) + done; + let {lincons0_array; array_env}: Lincons1.earray = earray in + Array.enum lincons0_array + |> Enum.map (fun (lincons0: Lincons0.t) -> + Lincons1.{lincons0; env = array_env} + ) + |> List.of_enum + + let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 + + let env (t: Bounds.t) = t.env + + type marshal = Bounds.t + + let marshal t = t + + let unmarshal t = t +end + +module D2(Vc: AbstractVector) (Mx: AbstractMatrix): RelationDomain.S3 with type var = Var.t = +struct + module D = D (Vc) (Mx) + include SharedFunctions.AssertionModule (V) (D) + include D +end diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.no-apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.no-apron.ml new file mode 100644 index 0000000000..5fed2c883c --- /dev/null +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.no-apron.ml @@ -0,0 +1,5 @@ +(* This domain is empty on purpose. It serves only as an alternative dependency + in cases where the actual domain can't be used because of a missing library. + It was added because we don't want to fully depend on Apron. *) + +let reset_lazy () = () diff --git a/src/dune b/src/dune index acd5348acb..4feb4fa3cc 100644 --- a/src/dune +++ b/src/dune @@ -31,6 +31,14 @@ (apron -> affineEqualityDomain.apron.ml) (-> affineEqualityDomain.no-apron.ml) ) + (select linearTwoVarEquality.ml from + (apron -> linearTwoVarEquality.apron.ml) + (-> linearTwoVarEquality.no-apron.ml) + ) + (select linearTwoVarEqualityDomain.ml from + (apron -> linearTwoVarEqualityDomain.apron.ml) + (-> linearTwoVarEqualityDomain.no-apron.ml) + ) (select relationAnalysis.ml from (apron -> relationAnalysis.apron.ml) (-> relationAnalysis.no-apron.ml) From cda8170a459f2f8af155a00c36fd1ecf932d5d1c Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 8 Nov 2023 14:42:33 +0100 Subject: [PATCH 002/280] changed name of our analysis --- src/analyses/apron/linearTwoVarEquality.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/apron/linearTwoVarEquality.apron.ml b/src/analyses/apron/linearTwoVarEquality.apron.ml index b95d0bd2d7..32e4b7305f 100644 --- a/src/analyses/apron/linearTwoVarEquality.apron.ml +++ b/src/analyses/apron/linearTwoVarEquality.apron.ml @@ -21,7 +21,7 @@ let spec_module: (module MCPSpec) Lazy.t = let module Spec = struct include SpecFunctor (Priv) (RD) (RelationPrecCompareUtil.DummyUtil) - let name () = "affeq" + let name () = "lin2vareq" end in (module Spec) From 8a686fb77d242ecc13c5886e037faedc44314cc8 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 15 Nov 2023 14:10:33 +0100 Subject: [PATCH 003/280] implemented a possible datatype for our abstract domain --- .../apron/linearTwoVarEqualityDomain.apron.ml | 239 +++++++++++------- 1 file changed, 143 insertions(+), 96 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 4b3ed42ca6..f1cac89406 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -12,7 +12,7 @@ open Apron open VectorMatrix (** TODO: modify code *) -module Mpqf = struct +module Mpqf = struct (* multi-precision rational numbers *) include Mpqf let compare = cmp let zero = of_int 0 @@ -25,45 +25,91 @@ module Mpqf = struct let hash x = 31 * (Z.hash (get_den x)) + Z.hash (get_num x) end +module Array = struct + include Array + let zero = (0, Mpqf.zero) + let hash : 'a array -> int = (fun x -> 31 + x.(0)) (* TODO **) + let add_element m n = + let num_vars = Array.length m in + if num_vars = 0 then m else + if n > num_vars then failwith "n too large" else + let new_array = Array.make (num_vars + 1) zero in + if n = 0 then Array.blit m 0 new_array 1 (num_vars - 1) else + Array.blit m 0 new_array 0 n; if n <> num_vars then Array.blit m n new_array (n + 1) (num_vars - n); + new_array + + let add_elements m indexes = (** same as add_empty_columns for Matrix (see vectorMatrix.ml)*) + let nnc = Array.length indexes in + if Array.length m = 0 || nnc = 0 then m else + let nc = Array.length m in + let m' = Array.make (nc + nnc) zero in + let offset = ref 0 in + for j = 0 to nc - 1 do + while !offset < nnc && !offset + j = indexes.(!offset) do incr offset done; + m'.(j + !offset) <- m.(j); + done; + m' + + let del_cols m cols = + let n_c = Array.length cols in + if n_c = 0 || Array.length m = 0 then m + else + let m_c = Array.length m in + if m_c = n_c then [||] else + let m' = Array.make (m_c - n_c) zero in + let offset = ref 0 in + for j = 0 to (m_c - n_c) - 1 do + while !offset < n_c && !offset + j = cols.(!offset) do incr offset done; + m'.(j) <- m.(j + !offset); + done; + m' + + let del_cols m cols = timing_wrap "del_cols" (del_cols m) cols + + let remove_zero_elements m = + Array.filter (fun x -> x = 0) m + +end + + module Var = SharedFunctions.Var module V = RelationDomain.V(Var) (** It defines the type t of the affine equality domain (a struct that contains an optional matrix and an apron environment) and provides the functions needed for handling variables (which are defined by RelationDomain.D2) such as add_vars remove_vars. Furthermore, it provides the function get_coeff_vec that parses an apron expression into a vector of coefficients if the apron expression has an affine form. *) -module VarManagement (Vec: AbstractVector) (Mx: AbstractMatrix)= +module VarManagement = struct include SharedFunctions.EnvOps - module Vector = Vec (Mpqf) - module Matrix = Mx(Mpqf) (Vec) + module Vector = Array type t = { - mutable d : Matrix.t option; + mutable d : (int * Mpqf.t) Array.t option; mutable env : Environment.t } - [@@deriving eq, ord, hash] + [@@deriving eq, ord] (*TODO add hash**) let empty_env = Environment.make [||] [||] let bot () = - {d = Some (Matrix.empty ()); env = empty_env} + {d = Some [||]; env = empty_env} let bot_env = {d = None; env = empty_env} let is_bot_env t = t.d = None - let copy t = {t with d = Option.map Matrix.copy t.d} + let copy t = {t with d = Option.map Vector.copy t.d} let dim_add (ch: Apron.Dim.change) m = - Array.iteri (fun i x -> ch.dim.(i) <- x + i) ch.dim; - Matrix.add_empty_columns m ch.dim + Array.iteri (fun i x-> ch.dim.(i) <- x + i) ch.dim; (* ?? *) + Array.add_elements m ch.dim - let dim_add ch m = timing_wrap "dim add" (dim_add ch) m + let dim_add ch m = timing_wrap "dim add" (dim_add ch) m (*?*) let dim_remove (ch: Apron.Dim.change) m del = - if Array.length ch.dim = 0 || Matrix.is_empty m then m else ( - Array.iteri (fun i x-> ch.dim.(i) <- x + i) ch.dim; - let m' = if not del then let m = Matrix.copy m in Array.fold_left (fun y x -> Matrix.reduce_col_with y x; y) m ch.dim else m in - Matrix.remove_zero_rows @@ Matrix.del_cols m' ch.dim) + if Array.length ch.dim = 0 || (Vector.length m = 0) then m else ( + Array.iteri (fun i x-> ch.dim.(i) <- x + i) ch.dim;(* ?? *) + let m' = if not del then let m = Vector.copy m in Array.add_elements m ch.dim else m in + Array.del_cols m' ch.dim) let dim_remove ch m del = timing_wrap "dim remove" (dim_remove ch m) del @@ -73,7 +119,7 @@ struct else Environment.dimchange new_env t.env in match t.d with | None -> bot_env - | Some m -> {d = Some (if add then dim_add dim_change m else dim_remove dim_change m del); env = new_env} + | Some m -> {d = Some (dim_add dim_change m); env = new_env} let change_d t new_env add del = timing_wrap "dimension change" (change_d t new_env add) del @@ -130,10 +176,10 @@ struct let mem_var t var = Environment.mem_var t.env var include ConvenienceOps(Mpqf) - +(* let get_c v = match Vector.findi (fun x -> x <>: Mpqf.zero) v with | exception Not_found -> Some Mpqf.zero - | i when Vector.compare_length_with v (i + 1) = 0 -> Some (Vector.nth v i) + | i when Int.compare (Array.length v) (i + 1) = 0 -> Some (v.(i)) | _ -> None let get_coeff_vec (t: t) texp = @@ -161,10 +207,11 @@ struct let zero_vec_cp = Vector.copy zero_vec in let entry_only v = Vector.set_val_with v (Environment.dim_of_var t.env x) Mpqf.one; v in begin match t.d with - | Some m -> let row = Matrix.find_opt (fun r -> Vector.nth r (Environment.dim_of_var t.env x) =: Mpqf.one) m in + | Some m -> let row = Vector.nth m (Environment.dim_of_var t.env x) in begin match row with - | Some v when is_const_vec v -> - Vector.set_val_with zero_vec_cp ((Vector.length zero_vec) - 1) (Vector.nth v (Vector.length v - 1)); zero_vec_cp + | exception _ -> entry_only zero_vec_cp + | v -> + Vector.set_val_with zero_vec_cp ((Vector.length zero_vec) - 1) v; zero_vec_cp | _ -> entry_only zero_vec_cp end | None -> entry_only zero_vec_cp end | Unop (u, e, _, _) -> @@ -192,9 +239,9 @@ struct end (** As it is specifically used for the new affine equality domain, it can only provide bounds if the expression contains known constants only and in that case, min and max are the same. *) -module ExpressionBounds (Vc: AbstractVector) (Mx: AbstractMatrix): (SharedFunctions.ConvBounds with type t = VarManagement(Vc) (Mx).t) = +module ExpressionBounds (Vc: AbstractVector) (Mx: AbstractMatrix): (SharedFunctions.ConvBounds with type t = VarManagement(Vc).t) = struct - include VarManagement (Vc) (Mx) + include VarManagement (Vc) let bound_texpr t texpr = let texpr = Texpr1.to_expr texpr in @@ -220,7 +267,7 @@ module D(Vc: AbstractVector) (Mx: AbstractMatrix) = struct include Printable.Std include ConvenienceOps (Mpqf) - include VarManagement (Vc) (Mx) + include VarManagement (Vc) module Bounds = ExpressionBounds (Vc) (Mx) @@ -264,9 +311,9 @@ struct in match t.d with | None -> "Bottom Env" - | Some m when Matrix.is_empty m -> "⊤" + | Some m when Vector.length m = 0 -> "⊤" | Some m -> - let constraint_list = List.init (Matrix.num_rows m) (fun i -> vec_to_constraint (conv_to_ints @@ Matrix.get_row m i) t.env) + let constraint_list = List.init (Vector.length m) (fun i -> vec_to_constraint (conv_to_ints @@ m) t.env) in Format.asprintf "%s" ("[|"^ (String.concat "; " constraint_list) ^"|]") let pretty () (x:t) = text (show x) @@ -287,7 +334,7 @@ struct let is_top _ = false - let is_top_env t = (not @@ Environment.equal empty_env t.env) && GobOption.exists Matrix.is_empty t.d + let is_top_env t = (not @@ Environment.equal empty_env t.env) (*&& GobOption.exists Matrix.is_empty t.d*) let meet t1 t2 = let sup_env = Environment.lce t1.env t2.env in @@ -297,10 +344,10 @@ struct match m1, m2 with | x, y when is_top_env t1-> {d = Some (dim_add (Environment.dimchange t2.env sup_env) y); env = sup_env} | x, y when is_top_env t2 -> {d = Some (dim_add (Environment.dimchange t1.env sup_env) x); env = sup_env} - | x, y -> - let rref_matr = Matrix.rref_matrix_with (Matrix.copy x) (Matrix.copy y) in - if Option.is_none rref_matr then bot () else - {d = rref_matr; env = sup_env} + | x, y -> bot() + (*let rref_matr = Matrix.rref_matrix_with (Matrix.copy x) (Matrix.copy y) in + if Option.is_none rref_matr then bot () else + {d = rref_matr; env = sup_env}*) let meet t1 t2 = @@ -317,7 +364,7 @@ struct if is_bot t2 || is_top_env t1 then false else ( let m1, m2 = Option.get t1.d, Option.get t2.d in let m1' = if env_comp = 0 then m1 else dim_add (Environment.dimchange t1.env t2.env) m1 in - Matrix.is_covered_by m2 m1') + true) let leq a b = timing_wrap "leq" (leq a) b @@ -326,62 +373,62 @@ struct if M.tracing then M.tracel "leq" "leq a: %s b: %s -> %b \n" (show t1) (show t2) res ; res - let join a b = - let rec lin_disjunc r s a b = - if s >= Matrix.num_cols a then a else - let case_two a r col_b = - let a_r = Matrix.get_row a r in - Matrix.map2i_with (fun i x y -> if i < r then - Vector.map2_with (fun u j -> u +: y *: j) x a_r; x) a col_b; - Matrix.remove_row a r - in - let case_three a b col_a col_b max = - let col_a, col_b = Vector.copy col_a, Vector.copy col_b in - let col_a, col_b = Vector.keep_vals col_a max, Vector.keep_vals col_b max in - if Vector.equal col_a col_b then (a, b, max) else - let a_rev, b_rev = (Vector.rev_with col_a; col_a), (Vector.rev_with col_b; col_b) in - let i = Vector.find2i (fun x y -> x <>: y) a_rev b_rev in - let (x, y) = Vector.nth a_rev i, Vector.nth b_rev i in - let r, diff = Vector.length a_rev - (i + 1), x -: y in - let a_r, b_r = Matrix.get_row a r, Matrix.get_row b r in - let sub_col = - Vector.map2_with (fun x y -> x -: y) a_rev b_rev; - Vector.rev_with a_rev; - a_rev - in - let multiply_by_t m t = - Matrix.map2i_with (fun i' x c -> if i' <= max then (let beta = c /: diff in - Vector.map2_with (fun u j -> u -: (beta *: j)) x t); x) m sub_col; - m - in - Matrix.remove_row (multiply_by_t a a_r) r, Matrix.remove_row (multiply_by_t b b_r) r, (max - 1) - in - let col_a, col_b = Matrix.get_col a s, Matrix.get_col b s in - let nth_zero v i = match Vector.nth v i with - | exception Invalid_argument _ -> Mpqf.zero - | x -> x - in - let a_rs, b_rs = nth_zero col_a r, nth_zero col_b r in - if not (Z.equal (Mpqf.get_den a_rs) Z.one) || not (Z.equal (Mpqf.get_den b_rs) Z.one) then failwith "Matrix not in rref form" else - begin match Int.of_float @@ Mpqf.to_float @@ a_rs, Int.of_float @@ Mpqf.to_float @@ b_rs with (* TODO: is it safe to go through floats? *) - | 1, 1 -> lin_disjunc (r + 1) (s + 1) a b - | 1, 0 -> lin_disjunc r (s + 1) (case_two a r col_b) b - | 0, 1 -> lin_disjunc r (s + 1) a (case_two b r col_a) - | 0, 0 -> let new_a, new_b, new_r = case_three a b col_a col_b r in - lin_disjunc new_r (s + 1) new_a new_b - | _ -> failwith "Matrix not in rref form" end + let join a b = a + (*let rec lin_disjunc r s a b = + if s >= Vector.length a then a else + let case_two a r col_b = + let a_r = Matrix.get_row a r in + Matrix.map2i_with (fun i x y -> if i < r then + Vector.map2_with (fun u j -> u +: y *: j) x a_r; x) a col_b; + Matrix.remove_row a r + in + let case_three a b col_a col_b max = + let col_a, col_b = Vector.copy col_a, Vector.copy col_b in + let col_a, col_b = Vector.keep_vals col_a max, Vector.keep_vals col_b max in + if Vector.equal col_a col_b then (a, b, max) else + let a_rev, b_rev = (Vector.rev_with col_a; col_a), (Vector.rev_with col_b; col_b) in + let i = Vector.find2i (fun x y -> x <>: y) a_rev b_rev in + let (x, y) = Vector.nth a_rev i, Vector.nth b_rev i in + let r, diff = Vector.length a_rev - (i + 1), x -: y in + let a_r, b_r = Matrix.get_row a r, Matrix.get_row b r in + let sub_col = + Vector.map2_with (fun x y -> x -: y) a_rev b_rev; + Vector.rev_with a_rev; + a_rev + in + let multiply_by_t m t = + Matrix.map2i_with (fun i' x c -> if i' <= max then (let beta = c /: diff in + Vector.map2_with (fun u j -> u -: (beta *: j)) x t); x) m sub_col; + m + in + Matrix.remove_row (multiply_by_t a a_r) r, Matrix.remove_row (multiply_by_t b b_r) r, (max - 1) + in + let col_a, col_b = Matrix.get_col a s, Matrix.get_col b s in + let nth_zero v i = match Vector.nth v i with + | exception Invalid_argument _ -> Mpqf.zero + | x -> x + in + let a_rs, b_rs = nth_zero col_a r, nth_zero col_b r in + if not (Z.equal (Mpqf.get_den a_rs) Z.one) || not (Z.equal (Mpqf.get_den b_rs) Z.one) then failwith "Matrix not in rref form" else + begin match Int.of_float @@ Mpqf.to_float @@ a_rs, Int.of_float @@ Mpqf.to_float @@ b_rs with (* TODO: is it safe to go through floats? *) + | 1, 1 -> lin_disjunc (r + 1) (s + 1) a b + | 1, 0 -> lin_disjunc r (s + 1) (case_two a r col_b) b + | 0, 1 -> lin_disjunc r (s + 1) a (case_two b r col_a) + | 0, 0 -> let new_a, new_b, new_r = case_three a b col_a col_b r in + lin_disjunc new_r (s + 1) new_a new_b + | _ -> failwith "Matrix not in rref form" end in if is_bot a then b else if is_bot b then a else - match Option.get a.d, Option.get b.d with - | x, y when is_top_env a || is_top_env b -> {d = Some (Matrix.empty ()); env = Environment.lce a.env b.env} - | x, y when (Environment.compare a.env b.env <> 0) -> - let sup_env = Environment.lce a.env b.env in - let mod_x = dim_add (Environment.dimchange a.env sup_env) x in - let mod_y = dim_add (Environment.dimchange b.env sup_env) y in - {d = Some (lin_disjunc 0 0 (Matrix.copy mod_x) (Matrix.copy mod_y)); env = sup_env} - | x, y when Matrix.equal x y -> {d = Some x; env = a.env} - | x, y -> {d = Some(lin_disjunc 0 0 (Matrix.copy x) (Matrix.copy y)); env = a.env} - + match Option.get a.d, Option.get b.d with + | x, y when is_top_env a || is_top_env b -> {d = Some (Matrix.empty ()); env = Environment.lce a.env b.env} + | x, y when (Environment.compare a.env b.env <> 0) -> + let sup_env = Environment.lce a.env b.env in + let mod_x = dim_add (Environment.dimchange a.env sup_env) x in + let mod_y = dim_add (Environment.dimchange b.env sup_env) y in + {d = Some (lin_disjunc 0 0 (Matrix.copy mod_x) (Matrix.copy mod_y)); env = sup_env} + | x, y when Matrix.equal x y -> {d = Some x; env = a.env} + | x, y -> {d = Some(lin_disjunc 0 0 (Matrix.copy x) (Matrix.copy y)); env = a.env} + *) let join a b = timing_wrap "join" (join a) b let join a b = @@ -399,9 +446,9 @@ struct let pretty_diff () (x, y) = dprintf "%s: %a not leq %a" (name ()) pretty x pretty y - let remove_rels_with_var x var env imp = - let j0 = Environment.dim_of_var env var in - if imp then (Matrix.reduce_col_with x j0; x) else Matrix.reduce_col x j0 + let remove_rels_with_var x var env imp = (* + let j0 = Environment.dim_of_var env var in*) + if imp then x else x let remove_rels_with_var x var env imp = timing_wrap "remove_rels_with_var" (remove_rels_with_var x var env) imp @@ -410,11 +457,11 @@ struct else let m = Option.get t.d in if List.is_empty vars then t else - let rec rem_vars m vars' = - begin match vars' with - | [] -> m - | x :: xs -> rem_vars (remove_rels_with_var m x t.env true) xs end - in {d = Some (Matrix.remove_zero_rows @@ rem_vars (Matrix.copy m) vars); env = t.env} + (*let rec rem_vars m vars' = + begin match vars' with + | [] -> m + | x :: xs -> rem_vars (remove_rels_with_var m x t.env true) xs end + in *){d = Some m; env = t.env} let forget_vars t vars = let res = forget_vars t vars in @@ -423,7 +470,7 @@ struct let forget_vars t vars = timing_wrap "forget_vars" (forget_vars t) vars - let assign_texpr (t: VarManagement(Vc)(Mx).t) var texp = + let assign_texpr (t: VarManagement(Vc).t) var texp = let assign_invertible_rels x var b env = let j0 = Environment.dim_of_var env var in let a_j0 = Matrix.get_col x j0 in (*Corresponds to Axj0*) @@ -646,5 +693,5 @@ module D2(Vc: AbstractVector) (Mx: AbstractMatrix): RelationDomain.S3 with type struct module D = D (Vc) (Mx) include SharedFunctions.AssertionModule (V) (D) - include D + include D*) end From 4abb8efe405eaff06731a19e3861200490466fe8 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Wed, 15 Nov 2023 17:27:42 +0100 Subject: [PATCH 004/280] test --- tests/anotherTest | Bin 0 -> 19488 bytes tests/anotherTest.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100755 tests/anotherTest create mode 100644 tests/anotherTest.c diff --git a/tests/anotherTest b/tests/anotherTest new file mode 100755 index 0000000000000000000000000000000000000000..bba09376367d8310b585d24b540ccebbcd87ab7e GIT binary patch literal 19488 zcmeHPYiu0Xb-uH^LoQ8<4~ddUNtTw@BXVReNtt?E(o*C~%S10zlx+o#M@#OIyVi1- z*`2jSLva--cC9KxDIjrE)Jf$Qg`K)F+%^TAwr*)CmaV{P?X(5_Xll8L3JSGTnXU^t zwzU1uoqLAE$uhBv6zGq=z|1-4JCA$s*_pX_9(R8;x_hTj(*zg4*e0aZU?UNwxU_>U zgoucZVh;8z#R^dYIDjpp)L#0U&sH!EDmyBBZrG?O@7!r@jz5qlDz%q5Wil!qn|YxfySjYB9cbCOx6@a!pb8Ba!^hDZg{d zkLguaA5+dJ<%G74svln34jmPyUcaRB^U@yMO)#Z8s2sR#CH^<{?N@#)LNbi-Qm%(7 z*LNrUC@-J1@Xy?*>YMF%7*+Xas?sak(y4*kC#rbuRs*jbD9nYTZ)Z_w!q(exGcp z-Q+_yR45)FA|w-Ejg4&Ve0&9*jS+p-{QD82vy~_mb4w_{11wYm9|Bg&{^%_D3&2Cz zyk$9nQgQB{1^*}DA#C2V5kRRpl($m22K*Lp5RwgJ@L1d!N@e2d)B~m%wURkAo-jrV zmU+xbX9tgXph9K_cwaV^K@1!vgGSzp=PY9+4ur!DBA8{u0J^~=#$fV@F%(aw#b_>- zv4#Y>(Xq5%?AqPCquW>;UK?KL@SP5Kd#5mZ`}Y_LGiMH`@|KzF-_xDWX3YNhK$^l2 zk7P5de#15`X-LP7hGQv!eL(U)P&(cSeGrt-m%^e{6lWkZ6dsI7H^Df|uWh$+I;QD2<8sM`yU&A37f$COm#Z$E z>mc1V7f$C7mnRiY!v>$bq%#5x&(=)1a5V%;#Y-+6iFL{=F8n43Ei>f| zlrvDyKsf{D43sla&cOeD2Hvk*`t#VycY?9g`s-~%#2%Wke8pE{Cw~w;FDoyu%K@G$ zuFQkfEfZv)B+b;7w~NK%xRhzaHg)AyN2ZC|)RpHQnI=|KSN_bAX+kx1<#9)*3Eb3` zvn->wFUs0_gsczp^)S^wEq~yV-}T6sJ@QXH@;|%fj(2-cy?Sr#)W5|}{`Bhp{%FTU z$Hmy0tq-DG#rb~$%l{e**Dd=h8i@p*+4?$=*ctuDL^fTs<|B2F5|7kX6))8-J3)5m z6@~qGr2V=-By#JG*r}_r=YO_6_WU(}OuGIJBW>)x@^_NPH)`_Ve;aQVsSEodTWa zMn9%@{znJ8ayQ&!r(T?V3z1Iiw?aBOp`Fr01fS72DeYNkPewo1zVhXd|KMRwJb2+* z3UKlKFN=H~k1;6nLo)I|Ksq^z$QKAcqrauJ8$LngMC^26O5w$)l!^t{00?US;LIZsos{@^p04vHpn-Ty=n#0i4!Xp<$;kpoMSO$o9*g zPM&~HaVzIRoVklrvDyKsf{D z43sla&cOeV3}|?lvy)zeh4RUG&P*7IxD^lGA5Ry|&7qYE`D#L2?B5bWbLqXt$NyO@ zTA=Bx#o}Y2$3edb`Y)ir0s6vw#p27L%chFOcROR(d2 zSY%GPg>3@(VNfnq*VnMUhx)!iV6?XWVPE&03g4Y*k&xdT{3+ly=*MjOm!UVIe|m=h zA4`9N%B7rvat6v7C}*IYfpP}Q87OC{47ay-6r|Yk;M%p}_Rd=sEN(+g`{%Qh zdqsWXIl#H|)cG88rVC9PH|C+dd5y9t<*8}G{RpJ_DvuHgRIW!|TClQ%h*r@jb^38g z^8-3Qs)bHJ2x&nuIE1h;IRulNM!@UzAONnnL0$bO(0bqP_1^{NdlZOo-E!iE?;lB6 zFNL7c9|BbQErK@qiLb7P<7)qcU?-@r0ENG)@*e3pLPB%>9S{P(o1ykElA56JITDsg zO^t6Q2_dPe_jQxcEme2Ir_uL5X_iZ2f$uretZ4jmXqrSNEzJllUfd07;SZqM7}!{u z09Mh9-R7WV?*+c4#(;^aq5fJ>%^{BsqLO?9+m=QE-9&iP>OgnZGhiwX!63SXUVO_~ zJA)FXSi4-YCOFn-MCHR2E9QR)5R$zgQgda1dR@`3%_ZG$Ln8uoW@>@Q!TSOP`vVsw z-XigPB~GowGo7E3mrhx%@<$>B0i>PYKowW{4@XD>Hv~&Pp^@nUu>LVHi$vWWuzHig zG=cQf_=yub9RqbwgZ&|CzfRiUqun3T?n&AufeW2nf~3|@5ch53zKh*r3h*~%M~tYt z4sAuZpMw3EP}&JyHu5mIibYcOuA^G=d9Yfu?Ay20hw1Wp5sbCRK8sVK{x*cHqxyYk z@2Mx+WDAGI&2Nx%eI-@ht(@hqK=+ke~`_+7hsBaZ)XCxgzBoq+?ZB+ukfg>YG|>KGWPg?~Y~--Z!n1 z>gsu#zqY#Cu95H>P3xUU%$yb&w4uh@PKw*2+4XF1_BA2BDr!pwP?kXmc^-%!4`WYHeL(gSN7EUbTPO;$>u0kAZ5w$`7t^3($SEY2iXo zvzr!b?p`zpv~AVZXux#G7NIF(@qCR^K>b_dBu3_%>WVZSm$|vwts;B3a_MYJgnt{G zj`43F9T}STQSBTeI06MWFw4|nFGK>^Z(;Lk%Y1c}caa6{8JqP=v_ZWTA?t^^?dhd!Fd-u<#5~&PU zZ;Y52Qf4~$M0a)_+})2=7xawWS?b|hF@i-qq3(_iYJd#2j#!0^xpmmgn7Py-#CUEn zxz%1t6lxs~wGPGeW0}EZE}O{~@~wr;(NreUYSGe;Jd|?jNb6`WYng*qHrJZBU`SrJ zVlW-gCt01!W}p}xa>-5uEwq@|rPz(l1IZpmt3Z#SPGK*8=}>g7;*koO_ary39wuxWG8{_W+q<#6gl&LR}ZM+#X0FXL z^H%;-n8Gt_C9&)Xe&IpUR>`(=eLS^@E1(Q8BZ0kqcXYZxr4gybGqa?n!t zbf4hEO;x)Z0hPJ*BmA%jzg^k$eHz>Mg3&ND2}Nv#+DC;}^J!v%;QKBKVs{)H#pnAz z#vcKriTTd@D#o7xQ>y*5_3JNXoJO&~g(FPUQgO~p`-Q@LA42cED0?eHl5uu#fzvQi z<@-5Sd;mtxN*I!KE)o)&f!M-Xi8l)onW+o|Z$Lf|D}1$5(^RQmG>dwV{TASJq2VEj zXF5?8C!*|mgu9dMeGP)|pH+Bqw~WKmG|j2_-f7wKS?qrYIJMK;ug8Fgro%eiQ@|U1 z&4Ta8Sn(|3lu_lx?SBz?sXR{tuSeb3 znXyS~^4ab8##sS8^o!Ia_l-CR8*zl4eRUpu=1tP7aD2Opd!5?7E<2hsCWTh-7f z`Tvb)p4@(ZF3gncuTcKp_Q!$aM%d{;KR0F?qU54b8;NCqx(lX~!bVd#uAiSnGv(*b zWJ^VbvjyzGgpHo*vOhn^Wy*9opDo*pZtD6g^_J#p6FxP-2Nfu z$8^+f3|^@n`h>@STp2KBdk(`g(`P*X=aho!VRlrM{g~3@33vN>-oun0)42=#G4^+0 z-2OaIWIBfp74@D!FDQSfnvgP7v_*+by#7Ceg6d~~o_{m#VMj&1{%?BxlOfxX={w4g z2_HW!Uj{=@WYukyb3elWr{c9|ndy7*t#kSF{m(aB0iv*-EltaeUx$K@U9O++OZfkB zu2OPP)al^FwUr9fMathsr}tY_KyO_3W6N7-@js#*pXZJ# z2e1FSS^RJ7v>h&b96X}vp2h!zb+$hppYG!I->3Y&{d|K4R+OuJV%egTH;(g#S^R&v z$#!@{`7vQXH}zB@O`qIsdu&uE>~e^7AH>HGk4sg+JSmorm$peF=na;;aJ`JtZC)w= O`@3w5`#cUFQTz{knr6@d literal 0 HcmV?d00001 diff --git a/tests/anotherTest.c b/tests/anotherTest.c new file mode 100644 index 0000000000..3e5eb95058 --- /dev/null +++ b/tests/anotherTest.c @@ -0,0 +1,37 @@ +#include +#include + +int shared_data = 0; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +void *increment(void *arg) { + for (int i = 0; i < 100000; ++i) { + pthread_mutex_lock(&mutex); + shared_data++; + pthread_mutex_unlock(&mutex); + } + return NULL; +} + +void *decrement(void *arg) { + for (int i = 0; i < 100000; ++i) { + pthread_mutex_lock(&mutex); + shared_data--; + pthread_mutex_unlock(&mutex); + } + return NULL; +} + +int main() { + pthread_t thread1, thread2; + + pthread_create(&thread1, NULL, increment, NULL); + pthread_create(&thread2, NULL, decrement, NULL); + + pthread_join(thread1, NULL); + pthread_join(thread2, NULL); + + printf("Final shared_data value: %d\n", shared_data); + + return 0; +} \ No newline at end of file From c7efe2d28609b1cc72d9753014511b175581291d Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 16 Nov 2023 14:24:29 +0100 Subject: [PATCH 005/280] changed datastructure to int*Z array --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index f1cac89406..51067155cb 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -27,7 +27,7 @@ end module Array = struct include Array - let zero = (0, Mpqf.zero) + let zero = (0, Z.zero) let hash : 'a array -> int = (fun x -> 31 + x.(0)) (* TODO **) let add_element m n = let num_vars = Array.length m in @@ -83,7 +83,7 @@ struct module Vector = Array type t = { - mutable d : (int * Mpqf.t) Array.t option; + mutable d : (int * Z.t) Array.t option; mutable env : Environment.t } [@@deriving eq, ord] (*TODO add hash**) From f60eb1213357e8e15fa181a22c095db316912a9d Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Mon, 20 Nov 2023 15:45:23 +0100 Subject: [PATCH 006/280] 3 test cases --- tests/anotherTest.c | 6 +++++- tests/relational-domain/01 | Bin 0 -> 18240 bytes tests/relational-domain/01.c | 36 ++++++++++++++++++++++++++++++++ tests/relational-domain/02 | Bin 0 -> 18240 bytes tests/relational-domain/02.c | 37 +++++++++++++++++++++++++++++++++ tests/relational-domain/03 | Bin 0 -> 18360 bytes tests/relational-domain/03.c | 39 +++++++++++++++++++++++++++++++++++ 7 files changed, 117 insertions(+), 1 deletion(-) create mode 100755 tests/relational-domain/01 create mode 100644 tests/relational-domain/01.c create mode 100755 tests/relational-domain/02 create mode 100644 tests/relational-domain/02.c create mode 100755 tests/relational-domain/03 create mode 100644 tests/relational-domain/03.c diff --git a/tests/anotherTest.c b/tests/anotherTest.c index 3e5eb95058..8429228d1c 100644 --- a/tests/anotherTest.c +++ b/tests/anotherTest.c @@ -34,4 +34,8 @@ int main() { printf("Final shared_data value: %d\n", shared_data); return 0; -} \ No newline at end of file +} + +//two threads modify a shared variable (shared_data) using a mutex for synchronization. + +//a race condition on the shared_data varaible due to simultaneous reads and writes from differenet threads without proper synchronization. \ No newline at end of file diff --git a/tests/relational-domain/01 b/tests/relational-domain/01 new file mode 100755 index 0000000000000000000000000000000000000000..8b5fb341de74d119f57b04b1d2c683a28a12352f GIT binary patch literal 18240 zcmeHPdvF`Y8Q+sFTgLAfu@k^DBs@wj*~STZ;6#bzL*g=+wb>1cJJH0+q=`Q?&XCXypk&uXg#z><6BbOlg+$42&QvNTOyzlyots3Gx6?OdJDIXP zSD%-Rk-SEpqj!=55>u-k#pY0(Ar^k9i^md~@wV}$OWKxn`O-;Wr_lszmlp?(sr5H*6>u{} zm<*$E?J{}xN3sSfC;g9Kec-`goqY6Y^!T1%zZkwLGJpEbd89+_CL7YBME-b)5KrYY z9He8$<7+6H7|~6}g& znz{xpD1^7G;pldCI24NuGNK+|BG&i!t?5xaeODwfhnIFS& zC=yZ0KXc&Q7#1#}lQI(DcldfgRBPf-$omT8gA(@yOgZDsA75qSH0G!eIGwQI)^U2u zf*bc;X)t5K`4&y0vlg7jH>YPMPP%+;k!;F>({;n?bqj7@AExAO3(n`pHV$+2>d@q;KS-m{ubPQ+7sOCeizUB!LLv0uI$^v-LH6#7-7gRz8CPB?4sQu z)h&Ybqa-;q{hMqyyWimHf$+@qu^dkif@h{*%JKBzc4qp=%){GVhPSoC@CUxl#xJt* z7u)zoE8p?QzR6?Tf|DnMyMH^gX`r`bs^g8|zV0S8E8F;?Pl%6(ebp`Zp%FO2ecjEl z4eoQ-5m|OtZ^Ws;10_23vh0cKmR+QGL{jKalW^bSlSHmM7o404zVxS6!I#b!1(nx= z$Ij^uaPR}^z>_^OT-}26Vtb0uu5J>G%+*_iySwkDVsP?(y&|}`dk>gX)913;QxOE` zHTPZMm0Mw(XFt6YLUObfcEQQJ&K`AdB}F(o>fR_z&w>-T9?4hhgpfMpsC%I-+0}NR z_C|txix*u9t>9$u*^a5;zTQ*Mk+a~w#&4kFpBUxIqo;PDoe<8j@PJh~K*GJfr*hg` zOkgGltO2mMk>11ix*tWp?Bz@?x?wk7XM*tl;b+dTisi{tKBH%>8iGULUCjw3coCr7(a3bJDz=?npfkF|WUzsQk z>`b=Z9!kaN7b~x}BZFVj^a*c7)3va!MOx>OCz5(j$0w8pMJodEMZeYj^3T~U{RXvg zCYwD7+6DRys1EuS&|5#qX5R$85A-zXQ=k=iqisN$l5)ppp^Vok3o1%I`z20m0cv1> zIVr3yHGbkzI>j{Hr?ABOy8(44*NInx1QPI4mP zM8JuF69FdzP6V6?I1z9n;6%WQfF%OFUXR!3(T{$V223In0FUrM}&64MJ zfD0tgYvATfp4S7?dkrO)|K(gZN&I2C_>R}|y+{UFQ6UDU0==J6;IYNs{07I zTPTKd`BMXM^vku7yCEqxNSWDm0C9;3Je#T6C9$VK%ivgdKs zkj1saO*ShpA>~?vQ3rBSIxdP;(6XY20P#R3gxf@#0tO&F6QJ)AR)r?GzKVj2N}`C) z$1{ZNXCz%h(iN2TQg%IMD}jq5GASl;(W_JnP-!EwI&!d$^r#@}FG8l&8`Xa-d*Wph zASnkQfnM1@6swIE9JskG0A_*742p)Oq*Yl;p5B(m##ax55B?m+7lcwHqLSpU15we~ zPJ&WHpF3|>-_SK=q$`w#qP%=%c@cWfi>m6&i&_@6)GBqQH5e`xXcul+m9oJYsa5loqN?)p<>k~e@?EVcef3nR zX+_KZ(5}7QIEA`Kg<3$Odf70;sIQ*92b%6Qn$}6`;T? z%BNM8M9?v`3wNyu0iM(2Czd*L5Lk#Txe!uaVJ}mr0~! zBMB|y4M#&MVJ@?mtMEg*o>FyTavhR(GF4*{G3zSlhJ5le{@fCMe3u--^8NhP{b^e9 zFP)RKbTpaLy?KtXpTgVIajm>}dD}+yOhW4((GprJ76uzig`?f(CJ%4hh_`Jxl%7b0 zqp4&fnMt>05<6pwNSjVyN7E1*8wJ|NQc3zenoPB&btsaRDG0|y=_re1$pi%9VGA!C zXgxt)_@l{D&3~JgNW?S*P>cJ=Qlag7YXCj&(fl!xp$*nIViHv_JhW!#&748CkVoXc>qRgW7`H4ns zvG^cs*E09C6pQ=iMCz3axSR^=(>(6NL(hzDz9T!rz49&w)=1YNC!C+7BrN(+P{e#u z6EG2KA0?V}6mg;8=OP0{b|(( zTF8$v9f^6u{+w{cFssk)uVOFnprDAR-18RWr%@=h-=+)!@v@SIMhzTKmjMfi;v(RM z{LlneBbsw@W~=om)FS>ogUZKB(M^6_wSu2xS)V5ILjGwYtV0caTjSYiSk`NAXBxPd zom%L}B zwuDo9hcAg;P@$ois)t4dfV7?&9>yJOcE^;ekE&tXGnIyBB&m+XlS84n8o}K)t%fq= zBAgr@i{t00h_Ah{2JQWdsi9OVG@)tc0ORo87^ zy`fj_y>YFIeOha8xpDP|z8;uj@=$w&QW{*lS*Yv#H?CRTuWnqoZcFcgINS0sx<`be9>vrwe36L+sxYaZG9|ZbsWc|lJVu3#W?M3lrLnj!d`2^UDEdZ{V5yMS z!osI(<6vndLOW@6qECy;u@{ZN8*`=)a}b&qTt-!)(HNYW=I9*Z!{{Hypa!N5Wk#SG zN{rxAk!&n6oaAz7Xeg!K&Kx$cYFtJHxkxa2Jazu{{qG_RV1Qc%=0r2u_jA)*i8Ya~ zHC+a#6bIYSwQrei@JybUmVmR`+xIUo%+sd1I6u++Z?)&=52oIH_4Dk%1o>a_4S?Iv z-$|Hq{Uy?mz5OBJc!|iJKYvGIIwS3_ZMzI>oL6>_Vmq!dH!y~G$3oXw_n17-btxX*#3n4uEKOmDsuhy^M6>{ zuaSEEU5M$R#hzkjxBo6MiV6GA&0lgJqw#G`_VIHNLaV)3+A-Z^HAY#AbM$eW{g_l> z%KGevd8UVL_PeBjX@CtSWjm(N+3csJfaxc;0mF9e_NO*`eqLkB-y>Py9zXh~K~-@7 z3$N6av|qAJxc`{{1qxRCey=HEx|S6swb%b1aI5`(uc^qCo{O!?UjO?L(kChT+LC+z z=l>h9>od>v1K3tu?D>7;i8g>ptY>nwJma51Kx3Ed=l1~q{|Nq{2)h0#u^sp0Q^4r_ z*`B|{9`cj65Q9cOo3TC95*S*wdwnmO$4{Z}jNm+I_R zu>I9J#4J!-DKVWd?M-wxUm+dZ{jwcvUNwjPc4>IlW?*B*;yLWYon{U6HPD)Dtf2QH zJ4X6X& +#include + +int x=0; +int y=0; + +void *thread1(void *arg){ + x=1; + y=1; + return NULL; +} + +void *thread2(void *arg){ + y=2; + x=2; + return NULL; +} + +int main(){ + pthread_t thread1_id, thread2_id; + + pthread_create(&thread1_id, NULL, thread1, NULL); + pthread_create(&thread2_id, NULL, thread2, NULL); + + pthread_join(thread1_id, NULL); + pthread_join(thread2_id, NULL); + + if(x==y){ + printf("Two-variable equality detected!\n"); + } else{ + printf("Two-variable equality not detected.\n"); + } + return 0; +} + +//this test case verifies whether the analyzer can correctly represent and maintain the relationship between x and y even when they are modfied concurrectly. diff --git a/tests/relational-domain/02 b/tests/relational-domain/02 new file mode 100755 index 0000000000000000000000000000000000000000..79cfb4c9dc61aaf39351a19e28b5fbecd8c3e8c2 GIT binary patch literal 18240 zcmeHPdvH|M89#TEn`}tPJ4!&!rUGhhc9R&8hk=C<*k}-nVXB?N&CTvjb|u-3yLTl~ zQ7UT7U|RUYK1QZRI~}cdDxL8U#Xf>qAFXv@rsLT0A8K`k7B!-FXf14i-@WHsZZ4bJ z*MGfd=AQ5UopZi(?w)(kJ!kLf?p^Cs6v3%TtPn&^R@__OO2~FZlHEaRcTn0foRZ}+4vS4QDW$}i%UBF|4DfprCo2pawFW3%VEgneGzsPmwz>JH*b>V z&D0P5GX4x@ctvP9F|fEJG#qOgP9(EqEn`a-w=C`mW>Ud+s|r-F04^F+>u%gE;AWC| zGK|8v)27)U$?Bz?^!2y@bawCl9bfxN@ROsD9$MHsw&U>oq(k*48`7af{&KmmRTyh8e2Gw>e0F8=LzrwZe`FE zheG-a_@67FFElMWrfY+Vq&}Ru!w{opJZWN`NM%3er#k$^})m>V9up^JP2N&h>YdVD1)3;uW8EIoEkui;Q z-}3!eAup7DFPo=UG}%gu2cTV9`*Nl){opjY5HlUva9o6LLi=|0mo8JXxl@hrKBOf>96alf$e-7lTE6SaeIiiHn5gC0VMS#9CQR>^C zYPnrcC+Le+z_=}ouW06YAZD0G)HGtvGsu%EGp`dA%DkfGVfdnNHNX2Hm!oe`^QUsT z{eT^SPXn5OUjf|mVJ`P7;C+Cn03QLYzxa7xM@n}jk}t<0+^_3xE@+6zz(`-P;i zrquexJ0EBVuA5QbM&egiudSSOeO38(|4y-T?y}EcGvE4LNPel@ozSxkNf(LuCay8? z!+@NqtOs%JMtN5dv%9kT0bkeblC7u^NRs~;*Ivl!n`&O(cV7M~*b|UH@09zVwH1ix zB`*SA1iT1%5%415MZk-I7XdE}|Ced?psX9H0oA@3i?XEZAvl$$3T zCC&Q)=SiCPz|EC3?+2v!8cHnx3cm?-jh*CdC1{xsUnBWyQGOlTwfO5_0*MNj~d+nzP%7 zxLPcHuC&^FK>EYiPK)Na<8{uWAC&UHN}Agt-T%)3@B4N;Y?UvuVF@=%*zbI~bsPN? zn)i9G>*`t-xN`GAHfd%9ZEeAhU~5ZT)*{>PZf_5^c3dUd;tOzRcb6T^AIUAGB^)bP zaUX-P3dK?`e-c9Ly?Hf|LY9iZbR$GcaVc$tuJ9LMN0j2PB;YIkjzyJFYZafm6s~6% ztBIru|0;EjqLk23ftMQq)GjE@n}@IAqOuv;65o>2Cjd(xK(@?pp|=5DUNMMVRE;1z zgsYk?t`#cTthkJnYlw|9kc-kWQLF)$71f7{24`HTHfK)QVhH~@;G4vw?Fha*f%pg$ zMbsYu0J2|`bTLVnQ`Sw{^^~muUlfr^F^P+QMOc`y4ajQA!B)~EMATh^OsTVqe^2(r z^CUo0_CE~0vI(G7Rt@&uTowi~&nEgs{SwluEG17zrLomJ#NPiK7|+tvL!y%8zI}1g z&`N?*OP@Q>S|zdcEnQ1Sx-j#g37w`qNaIGHA-!1HHJ$C>V+Sy*|NeIsg)NiMU~~{%gU){R(5_i%HKDdap&F2=PFBn^>Zv2|;W>9%RjYT3NZ%ITVF?>%5+bS(;ZpGv z6{ZL)%BPjH37}(W^9PWoV!Dy&x(C;zxO_^Jud28cm_nI&ig>gyqeyKOWj^kjt&rfU4Z0HnyrIvPC%nA%kgC4xf6o6if1O(9uUAVCsbx0+R=f+b?;@0p zeeOd6E2FzFo6IDJl13~Ljq7P)Z?%^@@paQoYo@TNHVNAqYKfRQ?<((xeDYTQ{5E~8 zO%7oT|MVUH8QS$Pos+XnJe4*B(;VSUMWCzgTKW4GXc;lHNuzVfNE+!x6r`Sx#yjnU z9)XsjK+B+>8Ba#z=~Ob6&9r2b+Y`xHi%Bb_83?Vz0xhHI6fKda(k&SiiezOAqQiP7 z&f-KW2|;wwLCXp{8)%A9JT+p3ZZ(q0gnccIu6a_le+8&JlR~|$tn@NWf$>?x4W`qz3ZE=S@{xX!zh6WNiGvQR>E-=SO zjZ84kB(Rwijo4!GVa}~#pJ^!;56X!&AQf;s71XDB+=qvrDc5{QcEk_JUontYx&}G% z`8i6mMGp`~%oWvPn?v=ZM3aspE)x7)WHFK5g^T>~a~bo$4nz?R`STXce+)>W_RiEU zT2ofADm~WB9?ru;eyr(8TrAwr2}dlmy8QVn_VPLqMa;=RZ!!NAkV5Be$`FtMD>-1? z;^XNu>|g?20Dd7qG=Wu%#(bRFY8{Xo#GhwS`B*7B$&ar_@N+Ed(?nj#KTU+SD1m?0 zcs3f8<+|&c0YAV_9q?Z8YkZA@pNClVP2y9@(unK%J@Bhh|3H{EZ8!`*)n`(U&xKNK zJwrgBpAXp252T-Jm!GGkANQ30w8fuZCQ+|RefKQ>I{4HsxBs`m4_xR*{N3_%p`hMZ z`21A}{8}I0MqKx!xG9=8+kz>ag3<>PnyC*7FfwL#a1g&%=TA&&=7<)hGgBF8#!}kQ zaB4sw)?)Z&&1iadOhi*7qr>NKen(1*dnAS%OEtVY_8Hb63 z(x7QhQ?-6OHEr#tRqMO8?i<%=IH$GdmK#^C@9BamCJwDTBBhZvn}oKmcf;ycz1oJg zYj5iA)B0Ae?(K$!`N zAf}%;(_)#F7T1$8I*Zn`0a~#{Qp;wH7|KZ^(*`mbX*+GkwkOCp&cS_)zY9yQYD zxPvv2P2k`r9s+EwRjYe2Z4U`cJ&tKv1Y_e#6k$U%ZA)%9(iu#s(+CYRnrX>EVxz;R z2wK$)0u2tOKoXKMq9SM-V<2fHLOW%3V$g`ou@{fQ8&kFqdl1?djH4)hBmrl(IXXuK zG5SX^sKGM^vP00+lS8;wB$-GKrWn@;2GYjuOyTgV!8jtwD6!Gw=?m}gf0r{42KcGK zoMKE953G%;S4S?&<>m&@h{1WNMU4I>XyhP;N&+8}*r=%U#kz&X8c&D*a0y#Z0arwM1 z!|(-LEuXvNPj9`n#=-WyuECJkU%39P$MA00)0zp>yl%oUEK7FRU$O@^W7;bbL z1DE1Fe9UD(Dis*AKD%L>;X#-EPAOm*W#zqxq%B0hm7mYpo*^yvIqgMQ>bBaD%V7^}$29HvAs&~{&!^Qh z*t`3$MwTzt*{xvvtMeQip}JCHI9J-+-1GTz>Co+$?O5}w8SJ-7!!s@e7bzCaU>|L_ zOQ6+2XL6B(-iO>g(tn&@c#*zqgM9ARrIkeDx!bv_!!}v(GGLP7mw^!Ow*ST=+x}%6 z2bpswdmOy6x%WPzk%Q=_Jx-~!w**`=} Mok+L33|yr62SeQ!b^rhX literal 0 HcmV?d00001 diff --git a/tests/relational-domain/02.c b/tests/relational-domain/02.c new file mode 100644 index 0000000000..11c69e6718 --- /dev/null +++ b/tests/relational-domain/02.c @@ -0,0 +1,37 @@ +#include +#include + +int x=0; +int y=0; + +void *thread1(void *arg){ + if(x==0){ + y=1; + } + return NULL; +} + +void *thread2(void *arg){ + x=1; + return NULL; +} + +int main(){ + pthread_t thread1_id, thread2_id; + + pthread_create(&thread1_id, NULL, thread1, NULL); + pthread_create(&thread2_id, NULL, thread2, NULL); + + pthread_join(thread1_id, NULL); + pthread_join(thread2_id, NULL); + + if(x==y){ + printf("Two-variable equality detected!\n"); + } else { + printf("Two-variable equality not detected.\n"); + } + + return 0; +} + +/* this test case evaluates whether analyzer can correctly analyze the potential equality between x and y considering the different paths taken by the threads. */ diff --git a/tests/relational-domain/03 b/tests/relational-domain/03 new file mode 100755 index 0000000000000000000000000000000000000000..413888ba11031e81e6f850a241a08032dbab7e9a GIT binary patch literal 18360 zcmeHPdu$uWnV%)K5@qWB@FPlN`eM7zgQR3Dj$=nQZJ9QeII)}9Xi_v;Q{+mdO_3_O zY$UgSBx;iwih;N_dIsu0bO#)g%jIzHp#_>8a8a6vo%DgYD2lZGqfXoOWF*%r&RyZQ zY1rSlGatR&QfSe~U*!P1-+YhXH#6VN?(7bCcr?0ehey){C$HEnh?{Mbl891FY*rbN zh}a;Sab6`>ihA&MwoK|Hl0Yldo1RMQQ*tjT;g(XS3cXf}1yimeQNk^l8kB~qY6szT zQYqd|-%|Bt%KCD70Rkfh>~op+lK~P_Cyrt>6SUQ0W}uRetMZO1$~a;PcUIxfDjd^! zRUT80C&h%04Jw{)+6x^erfytH;oNk)gbAip2Bmx7wvvBNd3zLYO~7^|-1K%S7EHOk zj{!$<`BM{j^F6A(h5BK?ia%2oUJ=fuhu8OqGs$2koh?oTCpN4PuI~>Oa-p?$6{ubT z95kkO-n~!2%`C}O7=`DUbwteXAc0`f0I9>C#F z{SZ|1^9$Q0vNDsRS<#(`Wq3mtkhVHB)* z-ZIAGkc=0tLfIw(OUr~63=#*8MCzb15>ICYfvCsVi=DfMwhbCq<_xd~K0#)*+|shSPb6TwWhi z^M*stZDUN0)*?`gKrI5b2-G4_i$E;`e@_v3zxBG`#HQZ$#isQi;8{QR`B}?TdLuUV zvhO8345eEhf%tN1)f7nUazXYf(piIgX9`3aF5 zK8ziokG=SdEwLA`cw^eDu?rtsU2yO(%7L$Rain!Q+QoQ^&#?hgi{efDVp9W;P&Rh_ zZL29ZJ@6=)xl13GN^?mB=T-eMcdf|4BV11@&_@Z7oR2UN#*I;ZtLKupbQ z$Mv0*iGH9xeE5Ubj|y?*XJ03i2VN@UrrsS-#-{65t${dpJbI;X_FVK7*}M~so!B=s z9X%C09eeR2TJ^?rB#xcvejMfgx1B$JZZ3~%PDM{?Q-@DA9{KjU=xI_=6)s?@!mh%3 z%0y423a4ci&PC7IMm^KfGjKk={gs)ryLGm^GjMmN@yL&-yB`C1TK_!Gw|%47j{X^G zrh1;6`{D0OrFU8(FqPmn2<*niruSV*#%}7en`G&8vrxQ-VM|T6$8>oVQ!Q$#7J*s> zY7wYKpca8z1ZokeMW7aeS_Enl`1_6k{mL{ENEZU;r;71Rz{&+CC7;b%PMOx@-9*31 z(r+qPepxDwgU04dC3*|I5A-bPY0ªYY?IuH6B=o~1eE=h!T=pLa>v}rv}4ZbJw zf==>laclr~HQ8)$pr8KmWdVLu$8ij0?IA%++m4o{ceMH+@*NXfuGx6wx|R0NfQ0`f zj;CQ~1IZSNY7wYKpca9@K?L}FJ^ntAzrEY5%4*^=)yoflBeT=@ z)W?&{6wluQ_9&j-btqjUiQw-6>HUWi>;M15QjYku>f<~9mhXHWN%LH_MN%wY zR@ATPQboHJWujnWMtz^h-w^Is2J{|BiN6(GB3Ut`KJH`rMO87b_pGwxZwa~l>y(`B zep{05LtJeUewDP_JEQ#J7f_q$xZ}0Z=ATgdUn`#5q1^w~2!HqOcG#=>JEG_wMfWS} zw*O;1f9JV#aByQ_&A#Dc)+z@2`a=Do-e6zRX8Vq;T^s7{zd?z`&G0F^O9t~_)hDDk zaI9Qq-U(w}Xtr_72`K4XWU&oLHKgWipifk_x(2dn_35`0r}5JM;O(Qr9 z-xyBM<7gzrt)iYVo3DEm=MEC144lQH3DIDHHhQ}v#6vPEd{VMBVheu+-{sY$-#qUM;Xy|Elygy zPR`y^Jz+0kkoe5|uaVClqyQ(Mc}6CqH*oG;L6KkyVgT z`ku5{A}t<2Dw-cq7B@-*!uLq+6Gst9s=OEdZU?eaEM5f%+M)(3kvrX9g#oXfSrX5W zLfb`J&&m6mzozQPrNg(Me0xr2$bO~p`#1Z&7!V#^40WQQR^eaaqg-9T@cK5c&^GyR zThTn!>A}^n-)h^`FBaaGjVnCR_{p-Nxsvy3+EAwkMdJ$ChjIH_x@@TP_GJWX@@wr{M+0S>Wfnc(uB~e6^m~{0EGJzX24*X&3g2z24Y(%S zmuTJ=zkj2jYDVym)pmtDyEXDok}g%KZ8FqJz9LIMYZu#%O14O>Z0=&GEjf_ZhcF*3 z(J45}2{lIi4-OsozX*j@(|+61Oaxs;pL!#+ypFCxI?$)$pK*A!<(}3$`vRqt_z#lN zw;f)6sc7_Y-+CK0z4^a%-O?8cPn|ccuOjkOy%R*cLvQ+`uHCQud!N^ve@;e?(&&03 zo*UZPtoeY$!|DnT&8Hl=m89~SdKW0XHy+R%p4S`i^mXY?KSx2H#b^sY%O4HcC%Rb_ zvxW3%)=UNxsd!$+%8eYdI`Y9Kgf6|?5RsF^kM=>*t#K9L%bJ5K_^(Lit{UYN`#Qu$mqS1bgJ z*@x2EWYD5b1_dbX-3!6-e9kfxRxTebSTH1%R3tL-LWmRYb0VJPDk+{ZJ)WR4;l?p+s3 z{6!u_xLC-C)7eC(m^8zPhq1n2ZOa+W7Q@47?B#HpaK)|3akCIgF^k;7i3WR}c(3HP zkb7W4k3ifc7R`X*| zlVXW*Kfk57AO$c=Sz^cp`L*VTS}UQyaSq9H4axRH9f;nyZHHp{d`&Z;qk`ao>G3?j}*_^ za<;lkUxl3N@AmUu$OBi+%DH!KKW%^TG(Zn2)%wftslNYfHRK(hCJIJnx;K%x`a(JE z!io>44J$q>AShVHkrCXLuI|_}tT7`&d$tO&Oy-QyOl~-yF_O5E7L0gtLL_oy;~CR3 zlcC<~7PR*)ZN&5W_@rTGt^A}I$;Zb`BUv09n*_wc8L+e}OznH5VeGhP>s?VJdiQn% z`^2{2fA`kAh6Vw}JYhs*N*mjLk1%%b+P!V-E@StO9rs4}8hf{H+Z6@Ez0EDc`_}%* z4mH{hSGJ{{ZHAeQTXA7G32rO*#g#3w?ad%NxwQ>UnI9J+{48pQFzbXc&yJgUYtkVaE~c?xG@VqUt=on$Igbk4JcY?u zgp!k46d|dVmzqzT`2yz43dVqrCQ<^Z+<3+kA-kF(WJ9Alu#_p72@$f)39vK(VV$%4 zE@Y`LLuE*iTyZ246bl6gs@3SnX`Q3@<=L@C*HdNG&|2X8o!rBAZpVw!Ya{2WN@2-Cw za$1{VJg?s{omV)jBgKyK^twPPje{PE7|-iLOkXA=M{>viQ_#`c3gdY_g(t+>XE)3Fr3Ma{}GfDEBOB@==!6?IPU*{gN)kGcwYaV36r%D`|b196XTiI z1MI|$h_daKl*?fcjAOnDV3Kk9{Jh$>0N&ky?W%lb&Ta+cZz@YirV`jwI!?+m==^ zN#<_nyu#CanKLoV^b^QX-i=>P6Eo6zNy=x&IjJi|w(Y#a&#QrPJ*4y=!sCbMzedP- i?ASncy0}qhZgx56a@m%a0jlAf?Bz5e4!8g=R{S0b2L@pP literal 0 HcmV?d00001 diff --git a/tests/relational-domain/03.c b/tests/relational-domain/03.c new file mode 100644 index 0000000000..66dbb0025c --- /dev/null +++ b/tests/relational-domain/03.c @@ -0,0 +1,39 @@ +#include +#include + +#define NUM_THREADS 10 +#define NUM_ITERATIONS 10000 + +int x=0; +int y=0; + +void *thread(void *arg){ + for(int i=0; i Date: Mon, 20 Nov 2023 15:50:59 +0100 Subject: [PATCH 007/280] a test deleted --- tests/anotherTest | Bin 19488 -> 0 bytes tests/anotherTest.c | 41 ----------------------------------------- 2 files changed, 41 deletions(-) delete mode 100755 tests/anotherTest delete mode 100644 tests/anotherTest.c diff --git a/tests/anotherTest b/tests/anotherTest deleted file mode 100755 index bba09376367d8310b585d24b540ccebbcd87ab7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19488 zcmeHPYiu0Xb-uH^LoQ8<4~ddUNtTw@BXVReNtt?E(o*C~%S10zlx+o#M@#OIyVi1- z*`2jSLva--cC9KxDIjrE)Jf$Qg`K)F+%^TAwr*)CmaV{P?X(5_Xll8L3JSGTnXU^t zwzU1uoqLAE$uhBv6zGq=z|1-4JCA$s*_pX_9(R8;x_hTj(*zg4*e0aZU?UNwxU_>U zgoucZVh;8z#R^dYIDjpp)L#0U&sH!EDmyBBZrG?O@7!r@jz5qlDz%q5Wil!qn|YxfySjYB9cbCOx6@a!pb8Ba!^hDZg{d zkLguaA5+dJ<%G74svln34jmPyUcaRB^U@yMO)#Z8s2sR#CH^<{?N@#)LNbi-Qm%(7 z*LNrUC@-J1@Xy?*>YMF%7*+Xas?sak(y4*kC#rbuRs*jbD9nYTZ)Z_w!q(exGcp z-Q+_yR45)FA|w-Ejg4&Ve0&9*jS+p-{QD82vy~_mb4w_{11wYm9|Bg&{^%_D3&2Cz zyk$9nQgQB{1^*}DA#C2V5kRRpl($m22K*Lp5RwgJ@L1d!N@e2d)B~m%wURkAo-jrV zmU+xbX9tgXph9K_cwaV^K@1!vgGSzp=PY9+4ur!DBA8{u0J^~=#$fV@F%(aw#b_>- zv4#Y>(Xq5%?AqPCquW>;UK?KL@SP5Kd#5mZ`}Y_LGiMH`@|KzF-_xDWX3YNhK$^l2 zk7P5de#15`X-LP7hGQv!eL(U)P&(cSeGrt-m%^e{6lWkZ6dsI7H^Df|uWh$+I;QD2<8sM`yU&A37f$COm#Z$E z>mc1V7f$C7mnRiY!v>$bq%#5x&(=)1a5V%;#Y-+6iFL{=F8n43Ei>f| zlrvDyKsf{D43sla&cOeD2Hvk*`t#VycY?9g`s-~%#2%Wke8pE{Cw~w;FDoyu%K@G$ zuFQkfEfZv)B+b;7w~NK%xRhzaHg)AyN2ZC|)RpHQnI=|KSN_bAX+kx1<#9)*3Eb3` zvn->wFUs0_gsczp^)S^wEq~yV-}T6sJ@QXH@;|%fj(2-cy?Sr#)W5|}{`Bhp{%FTU z$Hmy0tq-DG#rb~$%l{e**Dd=h8i@p*+4?$=*ctuDL^fTs<|B2F5|7kX6))8-J3)5m z6@~qGr2V=-By#JG*r}_r=YO_6_WU(}OuGIJBW>)x@^_NPH)`_Ve;aQVsSEodTWa zMn9%@{znJ8ayQ&!r(T?V3z1Iiw?aBOp`Fr01fS72DeYNkPewo1zVhXd|KMRwJb2+* z3UKlKFN=H~k1;6nLo)I|Ksq^z$QKAcqrauJ8$LngMC^26O5w$)l!^t{00?US;LIZsos{@^p04vHpn-Ty=n#0i4!Xp<$;kpoMSO$o9*g zPM&~HaVzIRoVklrvDyKsf{D z43sla&cOeV3}|?lvy)zeh4RUG&P*7IxD^lGA5Ry|&7qYE`D#L2?B5bWbLqXt$NyO@ zTA=Bx#o}Y2$3edb`Y)ir0s6vw#p27L%chFOcROR(d2 zSY%GPg>3@(VNfnq*VnMUhx)!iV6?XWVPE&03g4Y*k&xdT{3+ly=*MjOm!UVIe|m=h zA4`9N%B7rvat6v7C}*IYfpP}Q87OC{47ay-6r|Yk;M%p}_Rd=sEN(+g`{%Qh zdqsWXIl#H|)cG88rVC9PH|C+dd5y9t<*8}G{RpJ_DvuHgRIW!|TClQ%h*r@jb^38g z^8-3Qs)bHJ2x&nuIE1h;IRulNM!@UzAONnnL0$bO(0bqP_1^{NdlZOo-E!iE?;lB6 zFNL7c9|BbQErK@qiLb7P<7)qcU?-@r0ENG)@*e3pLPB%>9S{P(o1ykElA56JITDsg zO^t6Q2_dPe_jQxcEme2Ir_uL5X_iZ2f$uretZ4jmXqrSNEzJllUfd07;SZqM7}!{u z09Mh9-R7WV?*+c4#(;^aq5fJ>%^{BsqLO?9+m=QE-9&iP>OgnZGhiwX!63SXUVO_~ zJA)FXSi4-YCOFn-MCHR2E9QR)5R$zgQgda1dR@`3%_ZG$Ln8uoW@>@Q!TSOP`vVsw z-XigPB~GowGo7E3mrhx%@<$>B0i>PYKowW{4@XD>Hv~&Pp^@nUu>LVHi$vWWuzHig zG=cQf_=yub9RqbwgZ&|CzfRiUqun3T?n&AufeW2nf~3|@5ch53zKh*r3h*~%M~tYt z4sAuZpMw3EP}&JyHu5mIibYcOuA^G=d9Yfu?Ay20hw1Wp5sbCRK8sVK{x*cHqxyYk z@2Mx+WDAGI&2Nx%eI-@ht(@hqK=+ke~`_+7hsBaZ)XCxgzBoq+?ZB+ukfg>YG|>KGWPg?~Y~--Z!n1 z>gsu#zqY#Cu95H>P3xUU%$yb&w4uh@PKw*2+4XF1_BA2BDr!pwP?kXmc^-%!4`WYHeL(gSN7EUbTPO;$>u0kAZ5w$`7t^3($SEY2iXo zvzr!b?p`zpv~AVZXux#G7NIF(@qCR^K>b_dBu3_%>WVZSm$|vwts;B3a_MYJgnt{G zj`43F9T}STQSBTeI06MWFw4|nFGK>^Z(;Lk%Y1c}caa6{8JqP=v_ZWTA?t^^?dhd!Fd-u<#5~&PU zZ;Y52Qf4~$M0a)_+})2=7xawWS?b|hF@i-qq3(_iYJd#2j#!0^xpmmgn7Py-#CUEn zxz%1t6lxs~wGPGeW0}EZE}O{~@~wr;(NreUYSGe;Jd|?jNb6`WYng*qHrJZBU`SrJ zVlW-gCt01!W}p}xa>-5uEwq@|rPz(l1IZpmt3Z#SPGK*8=}>g7;*koO_ary39wuxWG8{_W+q<#6gl&LR}ZM+#X0FXL z^H%;-n8Gt_C9&)Xe&IpUR>`(=eLS^@E1(Q8BZ0kqcXYZxr4gybGqa?n!t zbf4hEO;x)Z0hPJ*BmA%jzg^k$eHz>Mg3&ND2}Nv#+DC;}^J!v%;QKBKVs{)H#pnAz z#vcKriTTd@D#o7xQ>y*5_3JNXoJO&~g(FPUQgO~p`-Q@LA42cED0?eHl5uu#fzvQi z<@-5Sd;mtxN*I!KE)o)&f!M-Xi8l)onW+o|Z$Lf|D}1$5(^RQmG>dwV{TASJq2VEj zXF5?8C!*|mgu9dMeGP)|pH+Bqw~WKmG|j2_-f7wKS?qrYIJMK;ug8Fgro%eiQ@|U1 z&4Ta8Sn(|3lu_lx?SBz?sXR{tuSeb3 znXyS~^4ab8##sS8^o!Ia_l-CR8*zl4eRUpu=1tP7aD2Opd!5?7E<2hsCWTh-7f z`Tvb)p4@(ZF3gncuTcKp_Q!$aM%d{;KR0F?qU54b8;NCqx(lX~!bVd#uAiSnGv(*b zWJ^VbvjyzGgpHo*vOhn^Wy*9opDo*pZtD6g^_J#p6FxP-2Nfu z$8^+f3|^@n`h>@STp2KBdk(`g(`P*X=aho!VRlrM{g~3@33vN>-oun0)42=#G4^+0 z-2OaIWIBfp74@D!FDQSfnvgP7v_*+by#7Ceg6d~~o_{m#VMj&1{%?BxlOfxX={w4g z2_HW!Uj{=@WYukyb3elWr{c9|ndy7*t#kSF{m(aB0iv*-EltaeUx$K@U9O++OZfkB zu2OPP)al^FwUr9fMathsr}tY_KyO_3W6N7-@js#*pXZJ# z2e1FSS^RJ7v>h&b96X}vp2h!zb+$hppYG!I->3Y&{d|K4R+OuJV%egTH;(g#S^R&v z$#!@{`7vQXH}zB@O`qIsdu&uE>~e^7AH>HGk4sg+JSmorm$peF=na;;aJ`JtZC)w= O`@3w5`#cUFQTz{knr6@d diff --git a/tests/anotherTest.c b/tests/anotherTest.c deleted file mode 100644 index 8429228d1c..0000000000 --- a/tests/anotherTest.c +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include - -int shared_data = 0; -pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - -void *increment(void *arg) { - for (int i = 0; i < 100000; ++i) { - pthread_mutex_lock(&mutex); - shared_data++; - pthread_mutex_unlock(&mutex); - } - return NULL; -} - -void *decrement(void *arg) { - for (int i = 0; i < 100000; ++i) { - pthread_mutex_lock(&mutex); - shared_data--; - pthread_mutex_unlock(&mutex); - } - return NULL; -} - -int main() { - pthread_t thread1, thread2; - - pthread_create(&thread1, NULL, increment, NULL); - pthread_create(&thread2, NULL, decrement, NULL); - - pthread_join(thread1, NULL); - pthread_join(thread2, NULL); - - printf("Final shared_data value: %d\n", shared_data); - - return 0; -} - -//two threads modify a shared variable (shared_data) using a mutex for synchronization. - -//a race condition on the shared_data varaible due to simultaneous reads and writes from differenet threads without proper synchronization. \ No newline at end of file From 55356de7cd2da30d55af435321bebe05a0ef92a4 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 20 Nov 2023 16:55:04 +0100 Subject: [PATCH 008/280] implemented VarManagement --- .../apron/linearTwoVarEqualityDomain.apron.ml | 173 +++++++++--------- 1 file changed, 90 insertions(+), 83 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 51067155cb..fe596158bb 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -25,24 +25,33 @@ module Mpqf = struct (* multi-precision rational numbers *) let hash x = 31 * (Z.hash (get_den x)) + Z.hash (get_num x) end -module Array = struct + +module Equality = struct + type t = (int option * Z.t) [@@deriving eq, ord, hash] + let zero = (None, Z.zero) + let to_int x = Z.to_int @@ snd x +end + +module EqualitiesArray = struct include Array - let zero = (0, Z.zero) - let hash : 'a array -> int = (fun x -> 31 + x.(0)) (* TODO **) + type t = Equality.t Array.t [@@deriving eq, ord] + + let hash : t -> int = (fun x -> 31 + Equality.to_int x.(0)) (* TODO **) + let add_element m n = - let num_vars = Array.length m in + let num_vars = length m in if num_vars = 0 then m else if n > num_vars then failwith "n too large" else - let new_array = Array.make (num_vars + 1) zero in - if n = 0 then Array.blit m 0 new_array 1 (num_vars - 1) else - Array.blit m 0 new_array 0 n; if n <> num_vars then Array.blit m n new_array (n + 1) (num_vars - n); + let new_array = make (num_vars + 1) Equality.zero in + if n = 0 then blit m 0 new_array 1 (num_vars - 1) else + blit m 0 new_array 0 n; if n <> num_vars then blit m n new_array (n + 1) (num_vars - n); new_array let add_elements m indexes = (** same as add_empty_columns for Matrix (see vectorMatrix.ml)*) - let nnc = Array.length indexes in - if Array.length m = 0 || nnc = 0 then m else - let nc = Array.length m in - let m' = Array.make (nc + nnc) zero in + let nnc = length indexes in + if length m = 0 || nnc = 0 then m else + let nc = length m in + let m' = make (nc + nnc) Equality.zero in let offset = ref 0 in for j = 0 to nc - 1 do while !offset < nnc && !offset + j = indexes.(!offset) do incr offset done; @@ -51,12 +60,12 @@ module Array = struct m' let del_cols m cols = - let n_c = Array.length cols in - if n_c = 0 || Array.length m = 0 then m + let n_c = length cols in + if n_c = 0 || length m = 0 then m else - let m_c = Array.length m in + let m_c = length m in if m_c = n_c then [||] else - let m' = Array.make (m_c - n_c) zero in + let m' = make (m_c - n_c) Equality.zero in let offset = ref 0 in for j = 0 to (m_c - n_c) - 1 do while !offset < n_c && !offset + j = cols.(!offset) do incr offset done; @@ -67,7 +76,7 @@ module Array = struct let del_cols m cols = timing_wrap "del_cols" (del_cols m) cols let remove_zero_elements m = - Array.filter (fun x -> x = 0) m + filter (fun x -> x = 0) m end @@ -80,13 +89,13 @@ module V = RelationDomain.V(Var) module VarManagement = struct include SharedFunctions.EnvOps - module Vector = Array + module EArray = EqualitiesArray type t = { - mutable d : (int * Z.t) Array.t option; + mutable d : EArray.t option; mutable env : Environment.t } - [@@deriving eq, ord] (*TODO add hash**) + [@@deriving eq, ord, hash] (*TODO add hash**) let empty_env = Environment.make [||] [||] @@ -97,19 +106,19 @@ struct let is_bot_env t = t.d = None - let copy t = {t with d = Option.map Vector.copy t.d} + let copy t = {t with d = Option.map EArray.copy t.d} let dim_add (ch: Apron.Dim.change) m = - Array.iteri (fun i x-> ch.dim.(i) <- x + i) ch.dim; (* ?? *) - Array.add_elements m ch.dim + EArray.iteri (fun i x -> ch.dim.(i) <- x + i) ch.dim; (* ?? *) + EArray.add_elements m ch.dim let dim_add ch m = timing_wrap "dim add" (dim_add ch) m (*?*) let dim_remove (ch: Apron.Dim.change) m del = - if Array.length ch.dim = 0 || (Vector.length m = 0) then m else ( - Array.iteri (fun i x-> ch.dim.(i) <- x + i) ch.dim;(* ?? *) - let m' = if not del then let m = Vector.copy m in Array.add_elements m ch.dim else m in - Array.del_cols m' ch.dim) + if EArray.length ch.dim = 0 || (EArray.length m = 0) then m else ( + EArray.iteri (fun i x -> ch.dim.(i) <- x + i) ch.dim;(* ?? *) + let m' = if not del then let m = EArray.copy m in EArray.add_elements m ch.dim else m in + EArray.del_cols m' ch.dim) let dim_remove ch m del = timing_wrap "dim remove" (dim_remove ch m) del @@ -175,23 +184,19 @@ struct let mem_var t var = Environment.mem_var t.env var - include ConvenienceOps(Mpqf) -(* - let get_c v = match Vector.findi (fun x -> x <>: Mpqf.zero) v with - | exception Not_found -> Some Mpqf.zero - | i when Int.compare (Array.length v) (i + 1) = 0 -> Some (v.(i)) + let get_constant (var, off) = match var with + | None -> Some off | _ -> None - let get_coeff_vec (t: t) texp = - (*Parses a Texpr to obtain a coefficient + const (last entry) vector to repr. an affine relation. - Returns None if the expression is not affine*) + let get_coeff (t: t) texp = + (*Parses a Texpr to obtain a (variable, offset) pair to repr. a sum of a variable and an offset. + Returns None if the expression is not a sum between a variable (without coefficient) and a constant. *) let open Apron.Texpr1 in - let exception NotLinear in - let zero_vec = Vector.zero_vec @@ Environment.size t.env + 1 in - let neg v = Vector.map_with (fun x -> Mpqf.mone *: x) v; v in - let is_const_vec v = Vector.compare_length_with (Vector.filteri (fun i x -> (*Inefficient*) - Vector.compare_length_with v (i + 1) > 0 && x <>: Mpqf.zero) v) 1 = 0 - in + let exception NotLinear2Var in + let exception NotIntegerOffset in + let mpqf_to_int x = + if not(Z.equal (Mpqf.get_den x) Z.one) then raise NotIntegerOffset + else Mpqf.get_num x in let rec convert_texpr texp = begin match texp with (*If x is a constant, replace it with its const. val. immediately*) @@ -199,45 +204,45 @@ struct let open Coeff in match union with | Interval _ -> failwith "Not a constant" - | Scalar x -> (match x with - | Float x -> Mpqf.of_float x - | Mpqf x -> x - | Mpfrf x -> Mpfr.to_mpq x) in Vector.set_val zero_vec ((Vector.length zero_vec) - 1) (of_union x) - | Var x -> - let zero_vec_cp = Vector.copy zero_vec in - let entry_only v = Vector.set_val_with v (Environment.dim_of_var t.env x) Mpqf.one; v in - begin match t.d with - | Some m -> let row = Vector.nth m (Environment.dim_of_var t.env x) in - begin match row with - | exception _ -> entry_only zero_vec_cp - | v -> - Vector.set_val_with zero_vec_cp ((Vector.length zero_vec) - 1) v; zero_vec_cp - | _ -> entry_only zero_vec_cp end - | None -> entry_only zero_vec_cp end + | Scalar x -> begin match x with + | Float x -> raise NotIntegerOffset + | Mpqf x -> (None, mpqf_to_int x) + | Mpfrf x -> raise NotIntegerOffset end in of_union x + | Var x -> + let var_dim = Environment.dim_of_var t.env x in + begin match t.d with + | Some m -> m.(var_dim) + | None -> (Some var_dim, Z.zero) end | Unop (u, e, _, _) -> begin match u with - | Neg -> neg @@ convert_texpr e + | Neg -> raise NotLinear2Var | Cast -> convert_texpr e (*Ignore since casts in apron are used for floating point nums and rounding in contrast to CIL casts*) - | Sqrt -> raise NotLinear end + | Sqrt -> raise NotLinear2Var end | Binop (b, e1, e2, _, _) -> begin match b with - | Add -> let v1 = convert_texpr e1 in Vector.map2_with (+:) v1 (convert_texpr e2); v1 - | Sub -> let v1 = convert_texpr e1 in Vector.map2_with (+:) v1 (neg @@ convert_texpr e2); v1 + | Add -> begin match convert_texpr e1, convert_texpr e2 with + | (None, off1), (var2, off2) -> (var2, Z.(off1 + off2)) + | (var1, off1), (None, off2) -> (var1, Z.(off1 + off2)) + | (_, _), (_, _) -> raise NotLinear2Var end + | Sub -> begin match convert_texpr e1, convert_texpr e2 with + | (None, off1), (var2, off2) -> raise NotLinear2Var + | (var1, off1), (None, off2) -> (var1, Z.(off1 - off2)) + | (var1, off1), (var2, off2) -> if var1 = var2 then (None, Z.(off1 - off2)) else raise NotLinear2Var end | Mul -> let x1, x2 = convert_texpr e1, convert_texpr e2 in - begin match get_c x1, get_c x2 with - | _, Some c -> Vector.apply_with_c_with ( *:) c x1; x1 - | Some c, _ -> Vector.apply_with_c_with ( *:) c x2; x2 - | _, _ -> raise NotLinear end - | _ -> raise NotLinear end + begin match get_constant x1, get_constant x2 with + | Some c1, Some c2 -> (None, Z.(c1 * c2)) + | _, _ -> raise NotLinear2Var end + | _ -> raise NotLinear2Var end end in match convert_texpr texp with - | exception NotLinear -> None + | exception NotLinear2Var -> None + | exception NotIntegerOffset -> None | x -> Some(x) - let get_coeff_vec t texp = timing_wrap "coeff_vec" (get_coeff_vec t) texp + let get_coeff t texp = timing_wrap "coeff_vec" (get_coeff t) texp end - +(* (** As it is specifically used for the new affine equality domain, it can only provide bounds if the expression contains known constants only and in that case, min and max are the same. *) module ExpressionBounds (Vc: AbstractVector) (Mx: AbstractMatrix): (SharedFunctions.ConvBounds with type t = VarManagement(Vc).t) = struct @@ -278,16 +283,16 @@ struct let show t = let conv_to_ints row = let module BI = IntOps.BigIntOps in - let row = Array.copy @@ Vector.to_array row + let row = EqualitiesArray.copy @@ Vector.to_EqualitiesArray row in - for i = 0 to Array.length row -1 do + for i = 0 to EqualitiesArray.length row -1 do let val_i = Mpqf.of_mpz @@ Z_mlgmpidl.mpzf_of_z @@ Mpqf.get_den row.(i) - in Array.iteri(fun j x -> row.(j) <- val_i *: x) row + in EqualitiesArray.iteri(fun j x -> row.(j) <- val_i *: x) row done; - let int_arr = Array.init (Array.length row) (fun i -> Mpqf.get_num row.(i)) - in let div = Mpqf.of_mpz @@ Z_mlgmpidl.mpzf_of_z @@ Array.fold_left BI.gcd int_arr.(0) int_arr - in Array.iteri (fun i x -> row.(i) <- x /: div) row; - Vector.of_array @@ row + let int_arr = EqualitiesArray.init (EqualitiesArray.length row) (fun i -> Mpqf.get_num row.(i)) + in let div = Mpqf.of_mpz @@ Z_mlgmpidl.mpzf_of_z @@ EqualitiesArray.fold_left BI.gcd int_arr.(0) int_arr + in EqualitiesArray.iteri (fun i x -> row.(i) <- x /: div) row; + Vector.of_EqualitiesArray @@ row in let vec_to_constraint vec env = let vars, _ = Environment.vars env @@ -305,7 +310,7 @@ struct else if vl <: Mpqf.zero then "+" ^ Mpqf.to_string vl else "" in - let res = (String.concat "" @@ Array.to_list @@ Array.map dim_to_str vars) + let res = (String.concat "" @@ EqualitiesArray.to_list @@ EqualitiesArray.map dim_to_str vars) ^ (c_to_str @@ Vector.nth vec (Vector.length vec - 1)) ^ "=0" in if String.starts_with res "+" then String.sub res 1 (String.length res - 1) else res in @@ -319,7 +324,7 @@ struct let pretty () (x:t) = text (show x) let printXml f x = BatPrintf.fprintf f "\n\n\nmatrix\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) - let name () = "affeq" + let name () = "lin2vareq" let to_yojson _ = failwith "ToDo Implement in future" @@ -664,17 +669,17 @@ struct match t.d with | None -> [] | Some m -> - let earray = Lincons1.array_make t.env (Matrix.num_rows m) in - for i = 0 to Lincons1.array_length earray do + let eEqualitiesArray = Lincons1.EqualitiesArray_make t.env (Matrix.num_rows m) in + for i = 0 to Lincons1.EqualitiesArray_length eEqualitiesArray do let row = Matrix.get_row m i in let coeff_vars = List.map (fun x -> Coeff.s_of_mpqf @@ Vector.nth row (Environment.dim_of_var t.env x), x) (vars t) in let cst = Coeff.s_of_mpqf @@ Vector.nth row (Vector.length row - 1) in - Lincons1.set_list (Lincons1.array_get earray i) coeff_vars (Some cst) + Lincons1.set_list (Lincons1.EqualitiesArray_get eEqualitiesArray i) coeff_vars (Some cst) done; - let {lincons0_array; array_env}: Lincons1.earray = earray in - Array.enum lincons0_array + let {lincons0_EqualitiesArray; EqualitiesArray_env}: Lincons1.eEqualitiesArray = eEqualitiesArray in + EqualitiesArray.enum lincons0_EqualitiesArray |> Enum.map (fun (lincons0: Lincons0.t) -> - Lincons1.{lincons0; env = array_env} + Lincons1.{lincons0; env = EqualitiesArray_env} ) |> List.of_enum @@ -693,5 +698,7 @@ module D2(Vc: AbstractVector) (Mx: AbstractMatrix): RelationDomain.S3 with type struct module D = D (Vc) (Mx) include SharedFunctions.AssertionModule (V) (D) - include D*) + include D + end +*) From 8248b99f5bbc7e443cba17f16f1b978094dfb4b9 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 22 Nov 2023 14:04:13 +0100 Subject: [PATCH 009/280] Added print functions for debugging --- .../apron/linearTwoVarEqualityDomain.apron.ml | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index fe596158bb..587c505054 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -10,8 +10,10 @@ open Pretty module M = Messages open Apron open VectorMatrix +open Printf (** TODO: modify code *) + module Mpqf = struct (* multi-precision rational numbers *) include Mpqf let compare = cmp @@ -40,7 +42,6 @@ module EqualitiesArray = struct let add_element m n = let num_vars = length m in - if num_vars = 0 then m else if n > num_vars then failwith "n too large" else let new_array = make (num_vars + 1) Equality.zero in if n = 0 then blit m 0 new_array 1 (num_vars - 1) else @@ -49,7 +50,7 @@ module EqualitiesArray = struct let add_elements m indexes = (** same as add_empty_columns for Matrix (see vectorMatrix.ml)*) let nnc = length indexes in - if length m = 0 || nnc = 0 then m else + if nnc = 0 then m else let nc = length m in let m' = make (nc + nnc) Equality.zero in let offset = ref 0 in @@ -98,6 +99,16 @@ struct [@@deriving eq, ord, hash] (*TODO add hash**) let empty_env = Environment.make [||] [||] + + (* For debugging *) + let print_env = Environment.print (Format.std_formatter) + let print_opt x = match x with + | Some x -> printf "%d " x + | None -> printf "None " + let print_d = Array.iter (fun (var, off) -> print_opt var; Z.print off) + let print_t t = match t.d with + | Some x -> print_d x + | None -> printf "None "; print_env t.env let bot () = {d = Some [||]; env = empty_env} @@ -128,7 +139,7 @@ struct else Environment.dimchange new_env t.env in match t.d with | None -> bot_env - | Some m -> {d = Some (dim_add dim_change m); env = new_env} + | Some m -> {d = Some (if add then dim_add dim_change m else dim_remove dim_change m del); env = new_env} let change_d t new_env add del = timing_wrap "dimension change" (change_d t new_env add) del @@ -242,6 +253,8 @@ struct let get_coeff t texp = timing_wrap "coeff_vec" (get_coeff t) texp end +(*end*) + (* (** As it is specifically used for the new affine equality domain, it can only provide bounds if the expression contains known constants only and in that case, min and max are the same. *) module ExpressionBounds (Vc: AbstractVector) (Mx: AbstractMatrix): (SharedFunctions.ConvBounds with type t = VarManagement(Vc).t) = @@ -267,16 +280,16 @@ struct let bound_texpr d texpr1 = timing_wrap "bounds calculation" (bound_texpr d) texpr1 end - +*) module D(Vc: AbstractVector) (Mx: AbstractMatrix) = struct include Printable.Std include ConvenienceOps (Mpqf) - include VarManagement (Vc) + include VarManagement - module Bounds = ExpressionBounds (Vc) (Mx) + (* module Bounds = ExpressionBounds (Vc) (Mx) - module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (SharedFunctions.Tracked) + module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (SharedFunctions.Tracked) *) type var = V.t @@ -701,4 +714,4 @@ struct include D end -*) + From 2590df0bedf07cf741b1e8c6a57e8bd8f4d1f3b0 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 22 Nov 2023 16:40:03 +0100 Subject: [PATCH 010/280] Added some helper functions for assign_texpr --- .../apron/linearTwoVarEqualityDomain.apron.ml | 93 +++++++++++-------- 1 file changed, 52 insertions(+), 41 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 587c505054..ca4dbe7cbb 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -76,8 +76,6 @@ module EqualitiesArray = struct let del_cols m cols = timing_wrap "del_cols" (del_cols m) cols - let remove_zero_elements m = - filter (fun x -> x = 0) m end @@ -99,16 +97,16 @@ struct [@@deriving eq, ord, hash] (*TODO add hash**) let empty_env = Environment.make [||] [||] - + (* For debugging *) let print_env = Environment.print (Format.std_formatter) let print_opt x = match x with - | Some x -> printf "%d " x - | None -> printf "None " + | Some x -> printf "%d " x + | None -> printf "None " let print_d = Array.iter (fun (var, off) -> print_opt var; Z.print off) let print_t t = match t.d with - | Some x -> print_d x - | None -> printf "None "; print_env t.env + | Some x -> print_d x + | None -> printf "None "; print_env t.env let bot () = {d = Some [||]; env = empty_env} @@ -252,6 +250,40 @@ struct | x -> Some(x) let get_coeff t texp = timing_wrap "coeff_vec" (get_coeff t) texp + + let find_reference_variable d env var = fst d.(Environment.dim_of_var env var) + + let find_vars_in_the_connected_component d env ref_var = + EArray.filter (fun i -> let (var, _) = d.(i) in var = ref_var) (EArray.mapi const d) + + let find_var_in_the_connected_component_with_least_index d env ref_var = + EArray.fold_left (fun curr_min (var, _) -> if var = ref_var then match curr_min with + | None -> var + | Some curr_min -> + match var with + |Some i -> if i < curr_min then Some i else Some curr_min + | None -> Some curr_min else curr_min) None d + + let abstract_exists var t = match t.d with + | Some d -> + (* let ref_var = find_reference_variable d t.env var in *) + if Environment.mem_var t.env var then + (* let connected_component = find_vars_in_the_connected_component d t.env ref_var in *) + remove_vars t [var] + else + (* the connected component is either empty, or x_i is the reference variable *) + let dim_of_var = Some (Environment.dim_of_var t.env var) in + let connected_component = find_vars_in_the_connected_component d t.env dim_of_var in + if connected_component = [||] then t else + (* TODO: x_i is the reference variable *) + begin match find_var_in_the_connected_component_with_least_index d t.env dim_of_var with + | Some var_least_index -> let (_, off) = d.(var_least_index) in + EArray.iteri (fun _ x -> let (_, off2) = d.(x) in d.(x) <- (Some var_least_index, Z.(off2 - off))) connected_component; + {d = Some d; + env = t.env} + | None -> t + end + | None -> t end (*end*) @@ -289,7 +321,7 @@ struct (* module Bounds = ExpressionBounds (Vc) (Mx) - module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (SharedFunctions.Tracked) *) + module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (SharedFunctions.Tracked) *) type var = V.t @@ -488,39 +520,18 @@ struct let forget_vars t vars = timing_wrap "forget_vars" (forget_vars t) vars - let assign_texpr (t: VarManagement(Vc).t) var texp = - let assign_invertible_rels x var b env = - let j0 = Environment.dim_of_var env var in - let a_j0 = Matrix.get_col x j0 in (*Corresponds to Axj0*) - let b0 = Vector.nth b j0 in - Vector.apply_with_c_with (/:) b0 a_j0; (*Corresponds to Axj0/Bj0*) - let recalc_entries m rd_a = Matrix.map2_with (fun x y -> Vector.map2i_with (fun j z d -> - if j = j0 then y - else if Vector.compare_length_with b (j + 1) > 0 then z -: y *: d - else z +: y *: d) x b; x) m rd_a - in - recalc_entries x a_j0; - if Matrix.normalize_with x then {d = Some x; env = env} else bot () - in - let assign_invertible_rels x var b env = timing_wrap "assign_invertible" (assign_invertible_rels x var b) env in - let assign_uninvertible_rel x var b env = - let b_length = Vector.length b in - Vector.mapi_with (fun i z -> if i < b_length - 1 then Mpqf.mone *: z else z) b; - Vector.set_val_with b (Environment.dim_of_var env var) Mpqf.one; - let opt_m = Matrix.rref_vec_with x b in - if Option.is_none opt_m then bot () else - {d = opt_m; env = env} - in - (* let assign_uninvertible_rel x var b env = timing_wrap "assign_uninvertible" (assign_uninvertible_rel x var b) env in *) - let is_invertible v = Vector.nth v @@ Environment.dim_of_var t.env var <>: Mpqf.zero - in let affineEq_vec = get_coeff_vec t texp - in if is_bot t then t else let m = Option.get t.d in - match affineEq_vec with - | Some v when is_top_env t -> if is_invertible v then t else assign_uninvertible_rel m var v t.env - | Some v -> if is_invertible v then let t' = assign_invertible_rels (Matrix.copy m) var v t.env in {d = t'.d; env = t'.env} - else let new_m = Matrix.remove_zero_rows @@ remove_rels_with_var m var t.env false - in assign_uninvertible_rel new_m var v t.env - | None -> {d = Some (Matrix.remove_zero_rows @@ remove_rels_with_var m var t.env false); env = t.env} + + + let assign_texpr (t: VarManagement.t) var texp = + + in + let dim_var = Environment.dim_of_var texp.env var in + match texp.d with + | Some d -> let rhs = d.(dim_var) in (*Current equality with var on the left hand side*) + bot () + | None -> bot () + + let assign_texpr t var texp = timing_wrap "assign_texpr" (assign_texpr t var) texp From afffd4930fe6e21858fcce58af50a19c04102fae Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 22 Nov 2023 18:18:39 +0100 Subject: [PATCH 011/280] implemented show() --- .../apron/linearTwoVarEqualityDomain.apron.ml | 50 ++++--------------- 1 file changed, 10 insertions(+), 40 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index ca4dbe7cbb..97cea9ee95 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -324,47 +324,17 @@ struct module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (SharedFunctions.Tracked) *) type var = V.t + (* prints the current variable equalities with resolved variable names *) + let show varM = + let lookup i = Var.to_string (Environment.var_of_dim varM.env i) in + let show_var i tuple = + match tuple with + | (None, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ Z.to_string offset + | (Some index, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ lookup index ^ " + " ^ Z.to_string offset + in match varM.d with + | None -> "No equalities available" + | Some arr -> Array.fold_left (fun acc elem -> acc ^ elem ) "" (Array.mapi show_var arr) - let show t = - let conv_to_ints row = - let module BI = IntOps.BigIntOps in - let row = EqualitiesArray.copy @@ Vector.to_EqualitiesArray row - in - for i = 0 to EqualitiesArray.length row -1 do - let val_i = Mpqf.of_mpz @@ Z_mlgmpidl.mpzf_of_z @@ Mpqf.get_den row.(i) - in EqualitiesArray.iteri(fun j x -> row.(j) <- val_i *: x) row - done; - let int_arr = EqualitiesArray.init (EqualitiesArray.length row) (fun i -> Mpqf.get_num row.(i)) - in let div = Mpqf.of_mpz @@ Z_mlgmpidl.mpzf_of_z @@ EqualitiesArray.fold_left BI.gcd int_arr.(0) int_arr - in EqualitiesArray.iteri (fun i x -> row.(i) <- x /: div) row; - Vector.of_EqualitiesArray @@ row - in - let vec_to_constraint vec env = - let vars, _ = Environment.vars env - in let dim_to_str var = - let vl = Vector.nth vec (Environment.dim_of_var env var) - in let var_str = Var.to_string var - in if vl =: Mpqf.one then "+" ^ var_str - else if vl =: Mpqf.mone then "-" ^ var_str - else if vl <: Mpqf.mone then Mpqf.to_string vl ^ var_str - else if vl >: Mpqf.one then Format.asprintf "+%s" (Mpqf.to_string vl) ^ var_str - else "" - in - let c_to_str vl = - if vl >: Mpqf.zero then "-" ^ Mpqf.to_string vl - else if vl <: Mpqf.zero then "+" ^ Mpqf.to_string vl - else "" - in - let res = (String.concat "" @@ EqualitiesArray.to_list @@ EqualitiesArray.map dim_to_str vars) - ^ (c_to_str @@ Vector.nth vec (Vector.length vec - 1)) ^ "=0" - in if String.starts_with res "+" then String.sub res 1 (String.length res - 1) else res - in - match t.d with - | None -> "Bottom Env" - | Some m when Vector.length m = 0 -> "⊤" - | Some m -> - let constraint_list = List.init (Vector.length m) (fun i -> vec_to_constraint (conv_to_ints @@ m) t.env) - in Format.asprintf "%s" ("[|"^ (String.concat "; " constraint_list) ^"|]") let pretty () (x:t) = text (show x) let printXml f x = BatPrintf.fprintf f "\n\n\nmatrix\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) From e59ea837ee61a00c3f1855c33775f13cb59be332 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Tue, 28 Nov 2023 20:07:48 +0100 Subject: [PATCH 012/280] implemented assign_texpr --- .../apron/linearTwoVarEqualityDomain.apron.ml | 194 ++++++++++++------ 1 file changed, 128 insertions(+), 66 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 97cea9ee95..4acdfe76b7 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -29,9 +29,15 @@ end module Equality = struct + (* (Some i, k) represents a sum of a variable with index i and the number k. + (None, k) represents the number k. *) type t = (int option * Z.t) [@@deriving eq, ord, hash] let zero = (None, Z.zero) + let var_zero i = (Some i, Z.zero) let to_int x = Z.to_int @@ snd x + let print : t -> unit = fun (a, b) -> match a with + | None -> print_endline @@ "(None , " ^ Z.to_string b ^ ")" + | Some x -> print_endline @@ "(Some " ^ string_of_int x ^ ", " ^ Z.to_string b ^ ")" end module EqualitiesArray = struct @@ -40,19 +46,21 @@ module EqualitiesArray = struct let hash : t -> int = (fun x -> 31 + Equality.to_int x.(0)) (* TODO **) - let add_element m n = - let num_vars = length m in - if n > num_vars then failwith "n too large" else - let new_array = make (num_vars + 1) Equality.zero in - if n = 0 then blit m 0 new_array 1 (num_vars - 1) else - blit m 0 new_array 0 n; if n <> num_vars then blit m n new_array (n + 1) (num_vars - n); + let make_empty_array len = Array.mapi (fun i (x, y) -> (Some i, Z.zero)) (make len Equality.zero) + + let add_element arr index = + let num_vars = length arr in + if index > num_vars then failwith "n too large" else + let new_array = make (num_vars + 1) (Equality.var_zero index) in + if index = 0 then blit arr 0 new_array 1 (num_vars - 1) else + blit arr 0 new_array 0 index; if index <> num_vars then blit arr index new_array (index + 1) (num_vars - index); new_array let add_elements m indexes = (** same as add_empty_columns for Matrix (see vectorMatrix.ml)*) let nnc = length indexes in if nnc = 0 then m else let nc = length m in - let m' = make (nc + nnc) Equality.zero in + let m' = make_empty_array (nc + nnc) in let offset = ref 0 in for j = 0 to nc - 1 do while !offset < nnc && !offset + j = indexes.(!offset) do incr offset done; @@ -60,13 +68,13 @@ module EqualitiesArray = struct done; m' - let del_cols m cols = + let del_cols : ('a option * Z.t) mappable ->int mappable ->('a option * Z.t) mappable = fun m cols -> let n_c = length cols in if n_c = 0 || length m = 0 then m else let m_c = length m in if m_c = n_c then [||] else - let m' = make (m_c - n_c) Equality.zero in + let m' = make_empty_array (m_c - n_c) in let offset = ref 0 in for j = 0 to (m_c - n_c) - 1 do while !offset < n_c && !offset + j = cols.(!offset) do incr offset done; @@ -94,7 +102,7 @@ struct mutable d : EArray.t option; mutable env : Environment.t } - [@@deriving eq, ord, hash] (*TODO add hash**) + [@@deriving eq, ord, hash] let empty_env = Environment.make [||] [||] @@ -103,10 +111,10 @@ struct let print_opt x = match x with | Some x -> printf "%d " x | None -> printf "None " - let print_d = Array.iter (fun (var, off) -> print_opt var; Z.print off) - let print_t t = match t.d with - | Some x -> print_d x - | None -> printf "None "; print_env t.env + let print_d = Array.iter (fun (var, off) -> print_opt var; Z.print off; printf "; ") + let print_t t = begin match t.d with + | Some x -> (print_d x; print_endline "") + | None -> printf "None " end; print_env t.env; print_endline "" let bot () = {d = Some [||]; env = empty_env} @@ -117,6 +125,10 @@ struct let copy t = {t with d = Option.map EArray.copy t.d} + let size t = match t.d with + | None -> 0 + | Some d -> EArray.length d + let dim_add (ch: Apron.Dim.change) m = EArray.iteri (fun i x -> ch.dim.(i) <- x + i) ch.dim; (* ?? *) EArray.add_elements m ch.dim @@ -189,17 +201,47 @@ struct let keep_vars t vs = timing_wrap "keep_vars" (keep_vars t) vs + let forget_var t var_index = + match t.d with + | None -> t + | Some d -> d.(var_index) <- Equality.var_zero var_index; + {t with d = Some d} + + + let forget_var t var = timing_wrap "forget_var" (forget_var t) var + + (*let forget_vars t vars = + if is_bot t || is_top_env t then t + else + let m = Option.get t.d in + if List.is_empty vars then t else + (*let rec rem_vars m vars' = + begin match vars' with + | [] -> m + | x :: xs -> rem_vars (remove_rels_with_var m x t.env true) xs end + in *){d = Some m; env = t.env} + + let forget_vars t vars = + let res = forget_vars t vars in + if M.tracing then M.tracel "ops" "forget_vars %s -> %s\n" (show t) (show res); + res + + let forget_vars t vars = timing_wrap "forget_vars" (forget_vars t) vars*) + let vars t = vars t.env let mem_var t var = Environment.mem_var t.env var + (* Returns the constant represented by an equality, if the equality represents a constant without a variable *) let get_constant (var, off) = match var with | None -> Some off | _ -> None let get_coeff (t: t) texp = - (*Parses a Texpr to obtain a (variable, offset) pair to repr. a sum of a variable and an offset. - Returns None if the expression is not a sum between a variable (without coefficient) and a constant. *) + (*Parses a Texpr to obtain a (variable, offset) pair to repr. a sum of a variable and an offset, + where the variable is a reference variable in the current state t. + Returns None if the expression is not a sum between a variable (without coefficient) and a constant. + *) let open Apron.Texpr1 in let exception NotLinear2Var in let exception NotIntegerOffset in @@ -218,7 +260,7 @@ struct | Mpqf x -> (None, mpqf_to_int x) | Mpfrf x -> raise NotIntegerOffset end in of_union x | Var x -> - let var_dim = Environment.dim_of_var t.env x in + let var_dim = print Format.std_formatter (Texpr1.of_expr t.env (Var x)); Environment.dim_of_var t.env x in begin match t.d with | Some m -> m.(var_dim) | None -> (Some var_dim, Z.zero) end @@ -266,25 +308,50 @@ struct let abstract_exists var t = match t.d with | Some d -> - (* let ref_var = find_reference_variable d t.env var in *) - if Environment.mem_var t.env var then - (* let connected_component = find_vars_in_the_connected_component d t.env ref_var in *) - remove_vars t [var] - else - (* the connected component is either empty, or x_i is the reference variable *) - let dim_of_var = Some (Environment.dim_of_var t.env var) in - let connected_component = find_vars_in_the_connected_component d t.env dim_of_var in - if connected_component = [||] then t else - (* TODO: x_i is the reference variable *) - begin match find_var_in_the_connected_component_with_least_index d t.env dim_of_var with - | Some var_least_index -> let (_, off) = d.(var_least_index) in + let var_to_remove = Environment.dim_of_var t.env var in + begin match find_reference_variable d t.env var with + | None -> (* the variable is equal to a constant *) t + | Some ref_var -> + if ref_var <> var_to_remove then forget_var t var_to_remove + else + (* x_i is the reference variable of its connected component *) + let dim_of_var = Some (Environment.dim_of_var t.env var) in + let connected_component = find_vars_in_the_connected_component d t.env dim_of_var in + if EArray.length connected_component = 1 + then t (* x_i is the only element of its connected component *) + else + (* x_i is the reference variable -> we need to find a new reference variable *) + let var_least_index = Option.get @@ find_var_in_the_connected_component_with_least_index d t.env dim_of_var in + let (_, off) = d.(var_least_index) in EArray.iteri (fun _ x -> let (_, off2) = d.(x) in d.(x) <- (Some var_least_index, Z.(off2 - off))) connected_component; - {d = Some d; - env = t.env} - | None -> t - end + {d = Some d; env = t.env} + end + | None -> t (* there are no variables in the current environment *) + + let assign_const t var const = match t.d with + | None -> t + | Some d -> d.(var) <- (None, const); t + + let subtract_const_from_var t var const = + match t.d with | None -> t + | Some d -> + let subtract_const_from_var_for_single_equality const index element = + let (eq_var_opt, off2) = d.(index) in + if index = var then + match eq_var_opt with + | None -> d.(index) <- (None, Z.(off2 + const)) + | Some eq_var -> begin if eq_var <> index then d.(index) <- (None, Z.(off2 + const)) end + else + begin if Option.is_some eq_var_opt + then let eq_var = Option.get eq_var_opt + in begin if eq_var = var then d.(index) <- (Some eq_var, Z.(off2 - const)) end + end + in + EArray.iteri (subtract_const_from_var_for_single_equality const) d; {d = Some d; env = t.env} end + + (*end*) (* @@ -466,40 +533,35 @@ struct let pretty_diff () (x, y) = dprintf "%s: %a not leq %a" (name ()) pretty x pretty y - let remove_rels_with_var x var env imp = (* - let j0 = Environment.dim_of_var env var in*) - if imp then x else x - - let remove_rels_with_var x var env imp = timing_wrap "remove_rels_with_var" (remove_rels_with_var x var env) imp - - let forget_vars t vars = - if is_bot t || is_top_env t then t - else - let m = Option.get t.d in - if List.is_empty vars then t else - (*let rec rem_vars m vars' = - begin match vars' with - | [] -> m - | x :: xs -> rem_vars (remove_rels_with_var m x t.env true) xs end - in *){d = Some m; env = t.env} - - let forget_vars t vars = - let res = forget_vars t vars in - if M.tracing then M.tracel "ops" "forget_vars %s -> %s\n" (show t) (show res); - res - - let forget_vars t vars = timing_wrap "forget_vars" (forget_vars t) vars - - - + (* implemented as described on page 10 in the paper about Fast Interprocedural Linear Two-Variable Equalities in the Section "Abstract Effect of Statements" *) let assign_texpr (t: VarManagement.t) var texp = - - in - let dim_var = Environment.dim_of_var texp.env var in - match texp.d with - | Some d -> let rhs = d.(dim_var) in (*Current equality with var on the left hand side*) - bot () - | None -> bot () + let assigned_var = Environment.dim_of_var t.env var (* this is the variable we are assigning to *) in + begin match t.d with + | Some d -> + begin match d.(assigned_var) with + | exception Failure _ -> bot() (* we don't have any information about the variable assigned_var yet*) + | rhs -> (* rhs is the current equality with assigned_var on the left hand side *) + let abstract_exists_var = abstract_exists var t in + begin match get_coeff t texp with + | None -> (* Statement "assigned_var = ?" (non-linear assignment) *) abstract_exists_var + | Some (exp_var_opt, off) -> + begin match exp_var_opt with + | None -> (* Statement "assigned_var = off" (constant assignment) *) + assign_const abstract_exists_var assigned_var off + | Some exp_var (* Statement "assigned_var = exp_var + off" (linear assignment) *) + -> begin if assigned_var = exp_var then + (* Statement "assigned_var = assigned_var + off" *) + subtract_const_from_var t assigned_var off + else + let empty_array = EqualitiesArray.make_empty_array (VarManagement.size t) in + let added_equality = empty_array.(exp_var) <- (Some assigned_var, off); empty_array in + meet abstract_exists_var {d = Some added_equality; env = t.env} (* change reference variable*) + end + end + end + + end + | None -> bot () end From 8dd161db1fb0fc684956066b4ffce96a1f4c04a8 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 29 Nov 2023 11:08:22 +0100 Subject: [PATCH 013/280] Bug fixes in get_coeff and assign_texpr --- .../apron/linearTwoVarEqualityDomain.apron.ml | 48 +++++++------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 4acdfe76b7..a650e8a38e 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -238,8 +238,7 @@ struct | _ -> None let get_coeff (t: t) texp = - (*Parses a Texpr to obtain a (variable, offset) pair to repr. a sum of a variable and an offset, - where the variable is a reference variable in the current state t. + (*Parses a Texpr to obtain a (variable, offset) pair to repr. a sum of a variable and an offset. Returns None if the expression is not a sum between a variable (without coefficient) and a constant. *) let open Apron.Texpr1 in @@ -261,9 +260,7 @@ struct | Mpfrf x -> raise NotIntegerOffset end in of_union x | Var x -> let var_dim = print Format.std_formatter (Texpr1.of_expr t.env (Var x)); Environment.dim_of_var t.env x in - begin match t.d with - | Some m -> m.(var_dim) - | None -> (Some var_dim, Z.zero) end + (Some var_dim, Z.zero) | Unop (u, e, _, _) -> begin match u with | Neg -> raise NotLinear2Var @@ -354,21 +351,13 @@ end (*end*) -(* -(** As it is specifically used for the new affine equality domain, it can only provide bounds if the expression contains known constants only and in that case, min and max are the same. *) -module ExpressionBounds (Vc: AbstractVector) (Mx: AbstractMatrix): (SharedFunctions.ConvBounds with type t = VarManagement(Vc).t) = + +(** TODO: overflow checking *) +module ExpressionBounds: (SharedFunctions.ConvBounds with type t = VarManagement.t) = struct - include VarManagement (Vc) + include VarManagement - let bound_texpr t texpr = - let texpr = Texpr1.to_expr texpr in - match get_coeff_vec t texpr with - | Some v -> begin match get_c v with - | Some c when Mpqf.get_den c = IntOps.BigIntOps.one -> - let int_val = Mpqf.get_num c - in Some int_val, Some int_val - | _ -> None, None end - | _ -> None, None + let bound_texpr t texpr = Some Z.zero, Some Z.zero (*TODO*) let bound_texpr d texpr1 = @@ -379,16 +368,16 @@ struct let bound_texpr d texpr1 = timing_wrap "bounds calculation" (bound_texpr d) texpr1 end -*) -module D(Vc: AbstractVector) (Mx: AbstractMatrix) = + +module D = struct include Printable.Std include ConvenienceOps (Mpqf) include VarManagement - (* module Bounds = ExpressionBounds (Vc) (Mx) + module Bounds = ExpressionBounds - module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (SharedFunctions.Tracked) *) + module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (SharedFunctions.Tracked) type var = V.t (* prints the current variable equalities with resolved variable names *) @@ -539,7 +528,6 @@ struct begin match t.d with | Some d -> begin match d.(assigned_var) with - | exception Failure _ -> bot() (* we don't have any information about the variable assigned_var yet*) | rhs -> (* rhs is the current equality with assigned_var on the left hand side *) let abstract_exists_var = abstract_exists var t in begin match get_coeff t texp with @@ -555,7 +543,7 @@ struct else let empty_array = EqualitiesArray.make_empty_array (VarManagement.size t) in let added_equality = empty_array.(exp_var) <- (Some assigned_var, off); empty_array in - meet abstract_exists_var {d = Some added_equality; env = t.env} (* change reference variable*) + meet abstract_exists_var {d = Some added_equality; env = t.env} end end end @@ -567,19 +555,19 @@ struct let assign_texpr t var texp = timing_wrap "assign_texpr" (assign_texpr t var) texp - let assign_exp (t: VarManagement(Vc)(Mx).t) var exp (no_ov: bool Lazy.t) = + let assign_exp (t: VarManagement.t) var exp (no_ov: bool Lazy.t) = let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in match Convert.texpr1_expr_of_cil_exp t t.env exp (Lazy.force no_ov) with | exp -> assign_texpr t var exp | exception Convert.Unsupported_CilExp _ -> - if is_bot t then t else forget_vars t [var] + if is_bot t then t else forget_var t (Environment.dim_of_var t.env var) let assign_exp t var exp no_ov = let res = assign_exp t var exp no_ov in if M.tracing then M.tracel "ops" "assign_exp t:\n %s \n var: %s \n exp: %a\n no_ov: %b -> \n %s\n" (show t) (Var.to_string var) d_exp exp (Lazy.force no_ov) (show res) ; res - let assign_var (t: VarManagement(Vc)(Mx).t) v v' = + let assign_var (t: VarManagement.t) v v' = let t = add_vars t [v; v'] in let texpr1 = Texpr1.of_expr (t.env) (Var v') in assign_texpr t v (Apron.Texpr1.to_expr texpr1) @@ -588,7 +576,7 @@ struct let res = assign_var t v v' in if M.tracing then M.tracel "ops" "assign_var t:\n %s \n v: %s \n v': %s\n -> %s\n" (show t) (Var.to_string v) (Var.to_string v') (show res) ; res - +(* let assign_var_parallel t vv's = let assigned_vars = List.map (function (v, _) -> v) vv's in let t = add_vars t assigned_vars in @@ -634,7 +622,7 @@ struct let substitute_exp t var exp no_ov = let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in let res = assign_exp t var exp no_ov in - forget_vars res [var] + forget_var res (Environment.dim_of_var t.env var) let substitute_exp t var exp ov = let res = substitute_exp t var exp ov @@ -755,6 +743,6 @@ struct module D = D (Vc) (Mx) include SharedFunctions.AssertionModule (V) (D) include D - +*) end From a34cc7753f344c63cdc20cd3fea3d295dde0a0f7 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Wed, 29 Nov 2023 19:26:05 +0100 Subject: [PATCH 014/280] test cases --- tests/relational-domain/00.c | 18 ++++++++++++++++++ tests/relational-domain/01 | Bin 18240 -> 0 bytes tests/relational-domain/01.c | 10 +++++----- tests/relational-domain/02 | Bin 18240 -> 0 bytes tests/relational-domain/02.c | 6 +----- tests/relational-domain/03 | Bin 18360 -> 0 bytes tests/relational-domain/03.c | 6 +----- tests/relational-domain/04.c | 33 +++++++++++++++++++++++++++++++++ tests/relational-domain/05.c | 33 +++++++++++++++++++++++++++++++++ tests/relational-domain/06.c | 14 ++++++++++++++ tests/relational-domain/07.c | 23 +++++++++++++++++++++++ 11 files changed, 128 insertions(+), 15 deletions(-) create mode 100644 tests/relational-domain/00.c delete mode 100755 tests/relational-domain/01 delete mode 100755 tests/relational-domain/02 delete mode 100755 tests/relational-domain/03 create mode 100644 tests/relational-domain/04.c create mode 100644 tests/relational-domain/05.c create mode 100644 tests/relational-domain/06.c create mode 100644 tests/relational-domain/07.c diff --git a/tests/relational-domain/00.c b/tests/relational-domain/00.c new file mode 100644 index 0000000000..5ec1114f31 --- /dev/null +++ b/tests/relational-domain/00.c @@ -0,0 +1,18 @@ +#include +#include + +int main() { + int x = 0; + int y = 0; + + x = 1; + y = 1; + + __goblint_check(x == y); + + x = 2; + + __goblint_check(x != y); + + return 0; +} \ No newline at end of file diff --git a/tests/relational-domain/01 b/tests/relational-domain/01 deleted file mode 100755 index 8b5fb341de74d119f57b04b1d2c683a28a12352f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18240 zcmeHPdvF`Y8Q+sFTgLAfu@k^DBs@wj*~STZ;6#bzL*g=+wb>1cJJH0+q=`Q?&XCXypk&uXg#z><6BbOlg+$42&QvNTOyzlyots3Gx6?OdJDIXP zSD%-Rk-SEpqj!=55>u-k#pY0(Ar^k9i^md~@wV}$OWKxn`O-;Wr_lszmlp?(sr5H*6>u{} zm<*$E?J{}xN3sSfC;g9Kec-`goqY6Y^!T1%zZkwLGJpEbd89+_CL7YBME-b)5KrYY z9He8$<7+6H7|~6}g& znz{xpD1^7G;pldCI24NuGNK+|BG&i!t?5xaeODwfhnIFS& zC=yZ0KXc&Q7#1#}lQI(DcldfgRBPf-$omT8gA(@yOgZDsA75qSH0G!eIGwQI)^U2u zf*bc;X)t5K`4&y0vlg7jH>YPMPP%+;k!;F>({;n?bqj7@AExAO3(n`pHV$+2>d@q;KS-m{ubPQ+7sOCeizUB!LLv0uI$^v-LH6#7-7gRz8CPB?4sQu z)h&Ybqa-;q{hMqyyWimHf$+@qu^dkif@h{*%JKBzc4qp=%){GVhPSoC@CUxl#xJt* z7u)zoE8p?QzR6?Tf|DnMyMH^gX`r`bs^g8|zV0S8E8F;?Pl%6(ebp`Zp%FO2ecjEl z4eoQ-5m|OtZ^Ws;10_23vh0cKmR+QGL{jKalW^bSlSHmM7o404zVxS6!I#b!1(nx= z$Ij^uaPR}^z>_^OT-}26Vtb0uu5J>G%+*_iySwkDVsP?(y&|}`dk>gX)913;QxOE` zHTPZMm0Mw(XFt6YLUObfcEQQJ&K`AdB}F(o>fR_z&w>-T9?4hhgpfMpsC%I-+0}NR z_C|txix*u9t>9$u*^a5;zTQ*Mk+a~w#&4kFpBUxIqo;PDoe<8j@PJh~K*GJfr*hg` zOkgGltO2mMk>11ix*tWp?Bz@?x?wk7XM*tl;b+dTisi{tKBH%>8iGULUCjw3coCr7(a3bJDz=?npfkF|WUzsQk z>`b=Z9!kaN7b~x}BZFVj^a*c7)3va!MOx>OCz5(j$0w8pMJodEMZeYj^3T~U{RXvg zCYwD7+6DRys1EuS&|5#qX5R$85A-zXQ=k=iqisN$l5)ppp^Vok3o1%I`z20m0cv1> zIVr3yHGbkzI>j{Hr?ABOy8(44*NInx1QPI4mP zM8JuF69FdzP6V6?I1z9n;6%WQfF%OFUXR!3(T{$V223In0FUrM}&64MJ zfD0tgYvATfp4S7?dkrO)|K(gZN&I2C_>R}|y+{UFQ6UDU0==J6;IYNs{07I zTPTKd`BMXM^vku7yCEqxNSWDm0C9;3Je#T6C9$VK%ivgdKs zkj1saO*ShpA>~?vQ3rBSIxdP;(6XY20P#R3gxf@#0tO&F6QJ)AR)r?GzKVj2N}`C) z$1{ZNXCz%h(iN2TQg%IMD}jq5GASl;(W_JnP-!EwI&!d$^r#@}FG8l&8`Xa-d*Wph zASnkQfnM1@6swIE9JskG0A_*742p)Oq*Yl;p5B(m##ax55B?m+7lcwHqLSpU15we~ zPJ&WHpF3|>-_SK=q$`w#qP%=%c@cWfi>m6&i&_@6)GBqQH5e`xXcul+m9oJYsa5loqN?)p<>k~e@?EVcef3nR zX+_KZ(5}7QIEA`Kg<3$Odf70;sIQ*92b%6Qn$}6`;T? z%BNM8M9?v`3wNyu0iM(2Czd*L5Lk#Txe!uaVJ}mr0~! zBMB|y4M#&MVJ@?mtMEg*o>FyTavhR(GF4*{G3zSlhJ5le{@fCMe3u--^8NhP{b^e9 zFP)RKbTpaLy?KtXpTgVIajm>}dD}+yOhW4((GprJ76uzig`?f(CJ%4hh_`Jxl%7b0 zqp4&fnMt>05<6pwNSjVyN7E1*8wJ|NQc3zenoPB&btsaRDG0|y=_re1$pi%9VGA!C zXgxt)_@l{D&3~JgNW?S*P>cJ=Qlag7YXCj&(fl!xp$*nIViHv_JhW!#&748CkVoXc>qRgW7`H4ns zvG^cs*E09C6pQ=iMCz3axSR^=(>(6NL(hzDz9T!rz49&w)=1YNC!C+7BrN(+P{e#u z6EG2KA0?V}6mg;8=OP0{b|(( zTF8$v9f^6u{+w{cFssk)uVOFnprDAR-18RWr%@=h-=+)!@v@SIMhzTKmjMfi;v(RM z{LlneBbsw@W~=om)FS>ogUZKB(M^6_wSu2xS)V5ILjGwYtV0caTjSYiSk`NAXBxPd zom%L}B zwuDo9hcAg;P@$ois)t4dfV7?&9>yJOcE^;ekE&tXGnIyBB&m+XlS84n8o}K)t%fq= zBAgr@i{t00h_Ah{2JQWdsi9OVG@)tc0ORo87^ zy`fj_y>YFIeOha8xpDP|z8;uj@=$w&QW{*lS*Yv#H?CRTuWnqoZcFcgINS0sx<`be9>vrwe36L+sxYaZG9|ZbsWc|lJVu3#W?M3lrLnj!d`2^UDEdZ{V5yMS z!osI(<6vndLOW@6qECy;u@{ZN8*`=)a}b&qTt-!)(HNYW=I9*Z!{{Hypa!N5Wk#SG zN{rxAk!&n6oaAz7Xeg!K&Kx$cYFtJHxkxa2Jazu{{qG_RV1Qc%=0r2u_jA)*i8Ya~ zHC+a#6bIYSwQrei@JybUmVmR`+xIUo%+sd1I6u++Z?)&=52oIH_4Dk%1o>a_4S?Iv z-$|Hq{Uy?mz5OBJc!|iJKYvGIIwS3_ZMzI>oL6>_Vmq!dH!y~G$3oXw_n17-btxX*#3n4uEKOmDsuhy^M6>{ zuaSEEU5M$R#hzkjxBo6MiV6GA&0lgJqw#G`_VIHNLaV)3+A-Z^HAY#AbM$eW{g_l> z%KGevd8UVL_PeBjX@CtSWjm(N+3csJfaxc;0mF9e_NO*`eqLkB-y>Py9zXh~K~-@7 z3$N6av|qAJxc`{{1qxRCey=HEx|S6swb%b1aI5`(uc^qCo{O!?UjO?L(kChT+LC+z z=l>h9>od>v1K3tu?D>7;i8g>ptY>nwJma51Kx3Ed=l1~q{|Nq{2)h0#u^sp0Q^4r_ z*`B|{9`cj65Q9cOo3TC95*S*wdwnmO$4{Z}jNm+I_R zu>I9J#4J!-DKVWd?M-wxUm+dZ{jwcvUNwjPc4>IlW?*B*;yLWYon{U6HPD)Dtf2QH zJ4X6X& #include +//#include int x=0; int y=0; @@ -25,12 +26,11 @@ int main(){ pthread_join(thread1_id, NULL); pthread_join(thread2_id, NULL); - if(x==y){ - printf("Two-variable equality detected!\n"); - } else{ - printf("Two-variable equality not detected.\n"); - } + __goblint_check(x==y); + __goblint_check(x!=y); + return 0; } //this test case verifies whether the analyzer can correctly represent and maintain the relationship between x and y even when they are modfied concurrectly. + diff --git a/tests/relational-domain/02 b/tests/relational-domain/02 deleted file mode 100755 index 79cfb4c9dc61aaf39351a19e28b5fbecd8c3e8c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18240 zcmeHPdvH|M89#TEn`}tPJ4!&!rUGhhc9R&8hk=C<*k}-nVXB?N&CTvjb|u-3yLTl~ zQ7UT7U|RUYK1QZRI~}cdDxL8U#Xf>qAFXv@rsLT0A8K`k7B!-FXf14i-@WHsZZ4bJ z*MGfd=AQ5UopZi(?w)(kJ!kLf?p^Cs6v3%TtPn&^R@__OO2~FZlHEaRcTn0foRZ}+4vS4QDW$}i%UBF|4DfprCo2pawFW3%VEgneGzsPmwz>JH*b>V z&D0P5GX4x@ctvP9F|fEJG#qOgP9(EqEn`a-w=C`mW>Ud+s|r-F04^F+>u%gE;AWC| zGK|8v)27)U$?Bz?^!2y@bawCl9bfxN@ROsD9$MHsw&U>oq(k*48`7af{&KmmRTyh8e2Gw>e0F8=LzrwZe`FE zheG-a_@67FFElMWrfY+Vq&}Ru!w{opJZWN`NM%3er#k$^})m>V9up^JP2N&h>YdVD1)3;uW8EIoEkui;Q z-}3!eAup7DFPo=UG}%gu2cTV9`*Nl){opjY5HlUva9o6LLi=|0mo8JXxl@hrKBOf>96alf$e-7lTE6SaeIiiHn5gC0VMS#9CQR>^C zYPnrcC+Le+z_=}ouW06YAZD0G)HGtvGsu%EGp`dA%DkfGVfdnNHNX2Hm!oe`^QUsT z{eT^SPXn5OUjf|mVJ`P7;C+Cn03QLYzxa7xM@n}jk}t<0+^_3xE@+6zz(`-P;i zrquexJ0EBVuA5QbM&egiudSSOeO38(|4y-T?y}EcGvE4LNPel@ozSxkNf(LuCay8? z!+@NqtOs%JMtN5dv%9kT0bkeblC7u^NRs~;*Ivl!n`&O(cV7M~*b|UH@09zVwH1ix zB`*SA1iT1%5%415MZk-I7XdE}|Ced?psX9H0oA@3i?XEZAvl$$3T zCC&Q)=SiCPz|EC3?+2v!8cHnx3cm?-jh*CdC1{xsUnBWyQGOlTwfO5_0*MNj~d+nzP%7 zxLPcHuC&^FK>EYiPK)Na<8{uWAC&UHN}Agt-T%)3@B4N;Y?UvuVF@=%*zbI~bsPN? zn)i9G>*`t-xN`GAHfd%9ZEeAhU~5ZT)*{>PZf_5^c3dUd;tOzRcb6T^AIUAGB^)bP zaUX-P3dK?`e-c9Ly?Hf|LY9iZbR$GcaVc$tuJ9LMN0j2PB;YIkjzyJFYZafm6s~6% ztBIru|0;EjqLk23ftMQq)GjE@n}@IAqOuv;65o>2Cjd(xK(@?pp|=5DUNMMVRE;1z zgsYk?t`#cTthkJnYlw|9kc-kWQLF)$71f7{24`HTHfK)QVhH~@;G4vw?Fha*f%pg$ zMbsYu0J2|`bTLVnQ`Sw{^^~muUlfr^F^P+QMOc`y4ajQA!B)~EMATh^OsTVqe^2(r z^CUo0_CE~0vI(G7Rt@&uTowi~&nEgs{SwluEG17zrLomJ#NPiK7|+tvL!y%8zI}1g z&`N?*OP@Q>S|zdcEnQ1Sx-j#g37w`qNaIGHA-!1HHJ$C>V+Sy*|NeIsg)NiMU~~{%gU){R(5_i%HKDdap&F2=PFBn^>Zv2|;W>9%RjYT3NZ%ITVF?>%5+bS(;ZpGv z6{ZL)%BPjH37}(W^9PWoV!Dy&x(C;zxO_^Jud28cm_nI&ig>gyqeyKOWj^kjt&rfU4Z0HnyrIvPC%nA%kgC4xf6o6if1O(9uUAVCsbx0+R=f+b?;@0p zeeOd6E2FzFo6IDJl13~Ljq7P)Z?%^@@paQoYo@TNHVNAqYKfRQ?<((xeDYTQ{5E~8 zO%7oT|MVUH8QS$Pos+XnJe4*B(;VSUMWCzgTKW4GXc;lHNuzVfNE+!x6r`Sx#yjnU z9)XsjK+B+>8Ba#z=~Ob6&9r2b+Y`xHi%Bb_83?Vz0xhHI6fKda(k&SiiezOAqQiP7 z&f-KW2|;wwLCXp{8)%A9JT+p3ZZ(q0gnccIu6a_le+8&JlR~|$tn@NWf$>?x4W`qz3ZE=S@{xX!zh6WNiGvQR>E-=SO zjZ84kB(Rwijo4!GVa}~#pJ^!;56X!&AQf;s71XDB+=qvrDc5{QcEk_JUontYx&}G% z`8i6mMGp`~%oWvPn?v=ZM3aspE)x7)WHFK5g^T>~a~bo$4nz?R`STXce+)>W_RiEU zT2ofADm~WB9?ru;eyr(8TrAwr2}dlmy8QVn_VPLqMa;=RZ!!NAkV5Be$`FtMD>-1? z;^XNu>|g?20Dd7qG=Wu%#(bRFY8{Xo#GhwS`B*7B$&ar_@N+Ed(?nj#KTU+SD1m?0 zcs3f8<+|&c0YAV_9q?Z8YkZA@pNClVP2y9@(unK%J@Bhh|3H{EZ8!`*)n`(U&xKNK zJwrgBpAXp252T-Jm!GGkANQ30w8fuZCQ+|RefKQ>I{4HsxBs`m4_xR*{N3_%p`hMZ z`21A}{8}I0MqKx!xG9=8+kz>ag3<>PnyC*7FfwL#a1g&%=TA&&=7<)hGgBF8#!}kQ zaB4sw)?)Z&&1iadOhi*7qr>NKen(1*dnAS%OEtVY_8Hb63 z(x7QhQ?-6OHEr#tRqMO8?i<%=IH$GdmK#^C@9BamCJwDTBBhZvn}oKmcf;ycz1oJg zYj5iA)B0Ae?(K$!`N zAf}%;(_)#F7T1$8I*Zn`0a~#{Qp;wH7|KZ^(*`mbX*+GkwkOCp&cS_)zY9yQYD zxPvv2P2k`r9s+EwRjYe2Z4U`cJ&tKv1Y_e#6k$U%ZA)%9(iu#s(+CYRnrX>EVxz;R z2wK$)0u2tOKoXKMq9SM-V<2fHLOW%3V$g`ou@{fQ8&kFqdl1?djH4)hBmrl(IXXuK zG5SX^sKGM^vP00+lS8;wB$-GKrWn@;2GYjuOyTgV!8jtwD6!Gw=?m}gf0r{42KcGK zoMKE953G%;S4S?&<>m&@h{1WNMU4I>XyhP;N&+8}*r=%U#kz&X8c&D*a0y#Z0arwM1 z!|(-LEuXvNPj9`n#=-WyuECJkU%39P$MA00)0zp>yl%oUEK7FRU$O@^W7;bbL z1DE1Fe9UD(Dis*AKD%L>;X#-EPAOm*W#zqxq%B0hm7mYpo*^yvIqgMQ>bBaD%V7^}$29HvAs&~{&!^Qh z*t`3$MwTzt*{xvvtMeQip}JCHI9J-+-1GTz>Co+$?O5}w8SJ-7!!s@e7bzCaU>|L_ zOQ6+2XL6B(-iO>g(tn&@c#*zqgM9ARrIkeDx!bv_!!}v(GGLP7mw^!Ow*ST=+x}%6 z2bpswdmOy6x%WPzk%Q=_Jx-~!w**`=} Mok+L33|yr62SeQ!b^rhX diff --git a/tests/relational-domain/02.c b/tests/relational-domain/02.c index 11c69e6718..e7227a75cd 100644 --- a/tests/relational-domain/02.c +++ b/tests/relational-domain/02.c @@ -25,11 +25,7 @@ int main(){ pthread_join(thread1_id, NULL); pthread_join(thread2_id, NULL); - if(x==y){ - printf("Two-variable equality detected!\n"); - } else { - printf("Two-variable equality not detected.\n"); - } + __goblint_check(x!=y); return 0; } diff --git a/tests/relational-domain/03 b/tests/relational-domain/03 deleted file mode 100755 index 413888ba11031e81e6f850a241a08032dbab7e9a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18360 zcmeHPdu$uWnV%)K5@qWB@FPlN`eM7zgQR3Dj$=nQZJ9QeII)}9Xi_v;Q{+mdO_3_O zY$UgSBx;iwih;N_dIsu0bO#)g%jIzHp#_>8a8a6vo%DgYD2lZGqfXoOWF*%r&RyZQ zY1rSlGatR&QfSe~U*!P1-+YhXH#6VN?(7bCcr?0ehey){C$HEnh?{Mbl891FY*rbN zh}a;Sab6`>ihA&MwoK|Hl0Yldo1RMQQ*tjT;g(XS3cXf}1yimeQNk^l8kB~qY6szT zQYqd|-%|Bt%KCD70Rkfh>~op+lK~P_Cyrt>6SUQ0W}uRetMZO1$~a;PcUIxfDjd^! zRUT80C&h%04Jw{)+6x^erfytH;oNk)gbAip2Bmx7wvvBNd3zLYO~7^|-1K%S7EHOk zj{!$<`BM{j^F6A(h5BK?ia%2oUJ=fuhu8OqGs$2koh?oTCpN4PuI~>Oa-p?$6{ubT z95kkO-n~!2%`C}O7=`DUbwteXAc0`f0I9>C#F z{SZ|1^9$Q0vNDsRS<#(`Wq3mtkhVHB)* z-ZIAGkc=0tLfIw(OUr~63=#*8MCzb15>ICYfvCsVi=DfMwhbCq<_xd~K0#)*+|shSPb6TwWhi z^M*stZDUN0)*?`gKrI5b2-G4_i$E;`e@_v3zxBG`#HQZ$#isQi;8{QR`B}?TdLuUV zvhO8345eEhf%tN1)f7nUazXYf(piIgX9`3aF5 zK8ziokG=SdEwLA`cw^eDu?rtsU2yO(%7L$Rain!Q+QoQ^&#?hgi{efDVp9W;P&Rh_ zZL29ZJ@6=)xl13GN^?mB=T-eMcdf|4BV11@&_@Z7oR2UN#*I;ZtLKupbQ z$Mv0*iGH9xeE5Ubj|y?*XJ03i2VN@UrrsS-#-{65t${dpJbI;X_FVK7*}M~so!B=s z9X%C09eeR2TJ^?rB#xcvejMfgx1B$JZZ3~%PDM{?Q-@DA9{KjU=xI_=6)s?@!mh%3 z%0y423a4ci&PC7IMm^KfGjKk={gs)ryLGm^GjMmN@yL&-yB`C1TK_!Gw|%47j{X^G zrh1;6`{D0OrFU8(FqPmn2<*niruSV*#%}7en`G&8vrxQ-VM|T6$8>oVQ!Q$#7J*s> zY7wYKpca8z1ZokeMW7aeS_Enl`1_6k{mL{ENEZU;r;71Rz{&+CC7;b%PMOx@-9*31 z(r+qPepxDwgU04dC3*|I5A-bPY0ªYY?IuH6B=o~1eE=h!T=pLa>v}rv}4ZbJw zf==>laclr~HQ8)$pr8KmWdVLu$8ij0?IA%++m4o{ceMH+@*NXfuGx6wx|R0NfQ0`f zj;CQ~1IZSNY7wYKpca9@K?L}FJ^ntAzrEY5%4*^=)yoflBeT=@ z)W?&{6wluQ_9&j-btqjUiQw-6>HUWi>;M15QjYku>f<~9mhXHWN%LH_MN%wY zR@ATPQboHJWujnWMtz^h-w^Is2J{|BiN6(GB3Ut`KJH`rMO87b_pGwxZwa~l>y(`B zep{05LtJeUewDP_JEQ#J7f_q$xZ}0Z=ATgdUn`#5q1^w~2!HqOcG#=>JEG_wMfWS} zw*O;1f9JV#aByQ_&A#Dc)+z@2`a=Do-e6zRX8Vq;T^s7{zd?z`&G0F^O9t~_)hDDk zaI9Qq-U(w}Xtr_72`K4XWU&oLHKgWipifk_x(2dn_35`0r}5JM;O(Qr9 z-xyBM<7gzrt)iYVo3DEm=MEC144lQH3DIDHHhQ}v#6vPEd{VMBVheu+-{sY$-#qUM;Xy|Elygy zPR`y^Jz+0kkoe5|uaVClqyQ(Mc}6CqH*oG;L6KkyVgT z`ku5{A}t<2Dw-cq7B@-*!uLq+6Gst9s=OEdZU?eaEM5f%+M)(3kvrX9g#oXfSrX5W zLfb`J&&m6mzozQPrNg(Me0xr2$bO~p`#1Z&7!V#^40WQQR^eaaqg-9T@cK5c&^GyR zThTn!>A}^n-)h^`FBaaGjVnCR_{p-Nxsvy3+EAwkMdJ$ChjIH_x@@TP_GJWX@@wr{M+0S>Wfnc(uB~e6^m~{0EGJzX24*X&3g2z24Y(%S zmuTJ=zkj2jYDVym)pmtDyEXDok}g%KZ8FqJz9LIMYZu#%O14O>Z0=&GEjf_ZhcF*3 z(J45}2{lIi4-OsozX*j@(|+61Oaxs;pL!#+ypFCxI?$)$pK*A!<(}3$`vRqt_z#lN zw;f)6sc7_Y-+CK0z4^a%-O?8cPn|ccuOjkOy%R*cLvQ+`uHCQud!N^ve@;e?(&&03 zo*UZPtoeY$!|DnT&8Hl=m89~SdKW0XHy+R%p4S`i^mXY?KSx2H#b^sY%O4HcC%Rb_ zvxW3%)=UNxsd!$+%8eYdI`Y9Kgf6|?5RsF^kM=>*t#K9L%bJ5K_^(Lit{UYN`#Qu$mqS1bgJ z*@x2EWYD5b1_dbX-3!6-e9kfxRxTebSTH1%R3tL-LWmRYb0VJPDk+{ZJ)WR4;l?p+s3 z{6!u_xLC-C)7eC(m^8zPhq1n2ZOa+W7Q@47?B#HpaK)|3akCIgF^k;7i3WR}c(3HP zkb7W4k3ifc7R`X*| zlVXW*Kfk57AO$c=Sz^cp`L*VTS}UQyaSq9H4axRH9f;nyZHHp{d`&Z;qk`ao>G3?j}*_^ za<;lkUxl3N@AmUu$OBi+%DH!KKW%^TG(Zn2)%wftslNYfHRK(hCJIJnx;K%x`a(JE z!io>44J$q>AShVHkrCXLuI|_}tT7`&d$tO&Oy-QyOl~-yF_O5E7L0gtLL_oy;~CR3 zlcC<~7PR*)ZN&5W_@rTGt^A}I$;Zb`BUv09n*_wc8L+e}OznH5VeGhP>s?VJdiQn% z`^2{2fA`kAh6Vw}JYhs*N*mjLk1%%b+P!V-E@StO9rs4}8hf{H+Z6@Ez0EDc`_}%* z4mH{hSGJ{{ZHAeQTXA7G32rO*#g#3w?ad%NxwQ>UnI9J+{48pQFzbXc&yJgUYtkVaE~c?xG@VqUt=on$Igbk4JcY?u zgp!k46d|dVmzqzT`2yz43dVqrCQ<^Z+<3+kA-kF(WJ9Alu#_p72@$f)39vK(VV$%4 zE@Y`LLuE*iTyZ246bl6gs@3SnX`Q3@<=L@C*HdNG&|2X8o!rBAZpVw!Ya{2WN@2-Cw za$1{VJg?s{omV)jBgKyK^twPPje{PE7|-iLOkXA=M{>viQ_#`c3gdY_g(t+>XE)3Fr3Ma{}GfDEBOB@==!6?IPU*{gN)kGcwYaV36r%D`|b196XTiI z1MI|$h_daKl*?fcjAOnDV3Kk9{Jh$>0N&ky?W%lb&Ta+cZz@YirV`jwI!?+m==^ zN#<_nyu#CanKLoV^b^QX-i=>P6Eo6zNy=x&IjJi|w(Y#a&#QrPJ*4y=!sCbMzedP- i?ASncy0}qhZgx56a@m%a0jlAf?Bz5e4!8g=R{S0b2L@pP diff --git a/tests/relational-domain/03.c b/tests/relational-domain/03.c index 66dbb0025c..bca76828b1 100644 --- a/tests/relational-domain/03.c +++ b/tests/relational-domain/03.c @@ -28,11 +28,7 @@ int main(){ pthread_join(threads[i], NULL); } - if(x==y){ - printf("x is equal to y\n"); - } else { - printf("x is not equal to y\n"); - } + __goblint_check(x!=y); return 0; } diff --git a/tests/relational-domain/04.c b/tests/relational-domain/04.c new file mode 100644 index 0000000000..9098a74cf1 --- /dev/null +++ b/tests/relational-domain/04.c @@ -0,0 +1,33 @@ +#include +#include + +int x = 0; +float y = 0.0; + +void *thread1(void *arg){ + if(x==0){ + y=1.0; + } + return NULL; +} + +void *thread2(void *arg){ + x=1; + return NULL; +} + +int main(){ + pthread_t thread1_id, thread2_id; + + pthread_create(&thread1_id, NULL, thread1, NULL); + pthread_create(&thread2_id, NULL, thread2, NULL); + + pthread_join(thread1_id, NULL); + pthread_join(thread2_id, NULL); + + __goblint_check(x!=y); + + return 0; +} + +/* This test case ensures that analyzer can track two variable equalities even when the variables have different types. It uses an integer and a float.*/ \ No newline at end of file diff --git a/tests/relational-domain/05.c b/tests/relational-domain/05.c new file mode 100644 index 0000000000..03b46b2863 --- /dev/null +++ b/tests/relational-domain/05.c @@ -0,0 +1,33 @@ +#include +#include + +int x =0; +char y='a'; + +void *thread1(void *arg){ + if(x==0){ + y='b'; + } + return NULL; +} + +void *thread2(void *arg){ + x =1; + return NULL; +} + +int main(){ + pthread_t thread1_id, thread2_id; + + pthread_create(&thread1_id, NULL, thread1, NULL); + pthread_create(&thread2_id, NULL, thread2, NULL); + + pthread_join(thread1_id, NULL); + pthread_join(thread2_id, NULL); + + __goblint_check(x!=y); + + return 0; +} + +/* This test case uses 2 different types of variable, an integer and a character. It ensures that analyzer can track 2 variable equalities even when the variables have different types and sizes. */ \ No newline at end of file diff --git a/tests/relational-domain/06.c b/tests/relational-domain/06.c new file mode 100644 index 0000000000..2c334a247f --- /dev/null +++ b/tests/relational-domain/06.c @@ -0,0 +1,14 @@ +#include +#include + +int main() { + int arr[2] = {10, 20}; + int x = arr[0]; + int y = arr[1]; + + arr[0] = 20; + + __goblint_check(x != y); + + return 0; +} \ No newline at end of file diff --git a/tests/relational-domain/07.c b/tests/relational-domain/07.c new file mode 100644 index 0000000000..5ab277c5d7 --- /dev/null +++ b/tests/relational-domain/07.c @@ -0,0 +1,23 @@ +#include + +int main() { + int a = 2; + int b = 3; + int c = 1; + int d = 0; + + int x = a * a + 2 * c; + __goblint_check(x == 5); // Expect: x == 5 + + int y = 100 * b + 4 * c + 400 - 100; + __goblint_check(y == 7 * c + 300); // Expect: y == 7 * c + 300 + + int z = a * 2 - c * (1 * (5 - d)) + 3; + __goblint_check(z == 4); // Expect: z == 4 + + int p = 2 * a + 3 * b + c + d; + __goblint_check(2 * p == 4 * a + 6 * b + 2 * c + 2 * d); // Expect: 2 * p == 4 * a + 6 * b + 2 * c + 2 * d + + return 0; +} + From fbc3ef2ac687315d35830cd7e57908101813bc4a Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Thu, 30 Nov 2023 11:06:19 +0100 Subject: [PATCH 015/280] Pipeline seems to be working --- ... => linearTwoVarEqualityAnalysis.apron.ml} | 0 ... linearTwoVarEqualityAnalysis.no.apron.ml} | 0 .../apron/linearTwoVarEqualityDomain.apron.ml | 20 ++++++++++--------- 3 files changed, 11 insertions(+), 9 deletions(-) rename src/analyses/apron/{linearTwoVarEquality.apron.ml => linearTwoVarEqualityAnalysis.apron.ml} (100%) rename src/analyses/apron/{linearTwoVarEquality.no.apron.ml => linearTwoVarEqualityAnalysis.no.apron.ml} (100%) diff --git a/src/analyses/apron/linearTwoVarEquality.apron.ml b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml similarity index 100% rename from src/analyses/apron/linearTwoVarEquality.apron.ml rename to src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml diff --git a/src/analyses/apron/linearTwoVarEquality.no.apron.ml b/src/analyses/apron/linearTwoVarEqualityAnalysis.no.apron.ml similarity index 100% rename from src/analyses/apron/linearTwoVarEquality.no.apron.ml rename to src/analyses/apron/linearTwoVarEqualityAnalysis.no.apron.ml diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index a650e8a38e..525b01b082 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -210,7 +210,7 @@ struct let forget_var t var = timing_wrap "forget_var" (forget_var t) var - (*let forget_vars t vars = + let forget_vars t vars = t (*TODO if is_bot t || is_top_env t then t else let m = Option.get t.d in @@ -576,8 +576,8 @@ struct let res = assign_var t v v' in if M.tracing then M.tracel "ops" "assign_var t:\n %s \n v: %s \n v': %s\n -> %s\n" (show t) (Var.to_string v) (Var.to_string v') (show res) ; res -(* - let assign_var_parallel t vv's = +(* from here on TODO till end of module*) + let assign_var_parallel t vv's = t (*TODO let assigned_vars = List.map (function (v, _) -> v) vv's in let t = add_vars t assigned_vars in let primed_vars = List.init (List.length assigned_vars) (fun i -> Var.of_string (Int.to_string i ^"'")) in (* TODO: we use primed vars in analysis, conflict? *) @@ -593,7 +593,7 @@ struct let x = Option.get res.d in if Matrix.normalize_with x then {d = Some x; env = res.env} else bot () | _ -> t - + *) let assign_var_parallel t vv's = let res = assign_var_parallel t vv's in if M.tracing then M.tracel "ops" "assign_var parallel: %s -> %s \n" (show t) (show res); @@ -655,7 +655,7 @@ struct | exception Convert.Unsupported_CilExp _ | _, _ -> overflow_res res - let meet_tcons t tcons expr = + let meet_tcons t tcons expr = t (*TODO let check_const cmp c = if cmp c Mpqf.zero then bot_env else t in let meet_vec e = @@ -687,6 +687,7 @@ struct | _, _ -> t end | None -> t + *) let meet_tcons t tcons expr = timing_wrap "meet_tcons" (meet_tcons t tcons) expr @@ -709,7 +710,7 @@ struct let relift t = t - let invariant t = + let invariant t = [] (*TODO match t.d with | None -> [] | Some m -> @@ -726,6 +727,7 @@ struct Lincons1.{lincons0; env = EqualitiesArray_env} ) |> List.of_enum + *) let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 @@ -736,13 +738,13 @@ struct let marshal t = t let unmarshal t = t + end -module D2(Vc: AbstractVector) (Mx: AbstractMatrix): RelationDomain.S3 with type var = Var.t = +module D2: RelationDomain.S3 with type var = Var.t = struct - module D = D (Vc) (Mx) + module D = D include SharedFunctions.AssertionModule (V) (D) include D -*) end From c56e10ff023629e24bc4ef13cd843f7737a06ecb Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Thu, 30 Nov 2023 11:16:08 +0100 Subject: [PATCH 016/280] dune angepasst --- src/dune | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dune b/src/dune index 4feb4fa3cc..7a0e4f4749 100644 --- a/src/dune +++ b/src/dune @@ -31,9 +31,9 @@ (apron -> affineEqualityDomain.apron.ml) (-> affineEqualityDomain.no-apron.ml) ) - (select linearTwoVarEquality.ml from - (apron -> linearTwoVarEquality.apron.ml) - (-> linearTwoVarEquality.no-apron.ml) + (select linearTwoVarEqualityAnalysis.ml from + (apron -> linearTwoVarEqualityAnalysis.apron.ml) + (-> linearTwoVarEqualityAnalysis.no-apron.ml) ) (select linearTwoVarEqualityDomain.ml from (apron -> linearTwoVarEqualityDomain.apron.ml) From 0535fce7d43d3660bf250a24cabb46a4b41926ac Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Thu, 30 Nov 2023 12:58:05 +0100 Subject: [PATCH 017/280] File for pipeline --- src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml index 32e4b7305f..26b016e042 100644 --- a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml +++ b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml @@ -9,11 +9,11 @@ include RelationAnalysis (** TODO: modify code *) let spec_module: (module MCPSpec) Lazy.t = lazy ( - let module AD = AffineEqualityDomain.D2 (VectorMatrix.ArrayVector) (VectorMatrix.ArrayMatrix) in + let module AD = LinearTwoVarEqualityDomain.D2 in let module RD: RelationDomain.RD = struct - module Var = AffineEqualityDomain.Var - module V = AffineEqualityDomain.V + module Var = LinearTwoVarEqualityDomain.Var + module V = LinearTwoVarEqualityDomain.V include AD end in From cf89aa5920037fcdb7c1eaf10e5652d89ebddace Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 30 Nov 2023 14:33:20 +0100 Subject: [PATCH 018/280] Added explanatory comments and TODOs --- .../apron/affineEqualityAnalysis.apron.ml | 4 +- .../apron/linearTwoVarEqualityDomain.apron.ml | 89 +++++++++++++++---- src/goblint.ml | 38 +++++++- 3 files changed, 113 insertions(+), 18 deletions(-) diff --git a/src/analyses/apron/affineEqualityAnalysis.apron.ml b/src/analyses/apron/affineEqualityAnalysis.apron.ml index 03a9ecdb57..6d7d117882 100644 --- a/src/analyses/apron/affineEqualityAnalysis.apron.ml +++ b/src/analyses/apron/affineEqualityAnalysis.apron.ml @@ -5,7 +5,8 @@ open Analyses include RelationAnalysis - +include LinearTwoVarEqualityDomain +(* let spec_module: (module MCPSpec) Lazy.t = lazy ( let module AD = AffineEqualityDomain.D2 (VectorMatrix.ArrayVector) (VectorMatrix.ArrayMatrix) in @@ -36,3 +37,4 @@ let after_config () = let _ = AfterConfig.register after_config +*) \ No newline at end of file diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 525b01b082..f9dd1166a2 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -2,7 +2,55 @@ @see A. Flexeder, M. Petter, and H. Seidl Fast Interprocedural Linear Two-Variable Equalities. *) -(** TODO: description *) +(** TODO: description + + APRON: + To get the index of a variable if you have a variable, use: + Environment.dim_of_var env variable + + Function naming: + _with -> in place changes + no _with -> make a copy + + TODO while developing: + assert that the output doesnt have the same address as the input + (but it may return an unchanged version without making a copy) + in order to check if the function that don't have "with" really create a copy + Hot o check address equality in OCaml: + == compares address equality + != for unequal addresses + + TODO for next week: + minimal working product + things to implement: + - leq + - join + - assignment + - meet_tcons + + HOW TO RUN THE REGRESSION TESTS: + Method 1: regression test ./rectest.sh numberofdirectory numberoftest + Method 2: make test -> run entire test suite + -> the two methods have a different behaviour w.r.t. unreachable code + script update suite.rb argumentgroupname ???? No idea + - test with different flags + - gobview doesnt work with apron + -Visualize test: + ./regtest.sh 63 01 + see result/ folder + index.xml -> (printxml uses show) + click on program points + orange nodes: dead code + state at the beginning of the line + multiple paths-> line was divided in two parts by the analysis + + TODO: + 12. January or earlier pull request -> all features implemented + -> run on svcomp benchmarks -> to check runtime and unsoundness and crashes + + Maybe TODO: + Abstract Vector in order to have less code duplication (e.g. VectorBase und Vector) +*) open Batteries open GoblintCil @@ -137,7 +185,7 @@ struct let dim_remove (ch: Apron.Dim.change) m del = if EArray.length ch.dim = 0 || (EArray.length m = 0) then m else ( - EArray.iteri (fun i x -> ch.dim.(i) <- x + i) ch.dim;(* ?? *) + EArray.iteri (fun i x -> ch.dim.(i) <- x + i) ch.dim; let m' = if not del then let m = EArray.copy m in EArray.add_elements m ch.dim else m in EArray.del_cols m' ch.dim) @@ -166,7 +214,7 @@ struct change_d t env' false del let drop_vars t vars = timing_wrap "drop_vars" (drop_vars t) vars - + (*TODO used by relational domain*) let remove_vars t vars = drop_vars t vars false let remove_vars t vars = timing_wrap "remove_vars" (remove_vars t) vars @@ -210,7 +258,8 @@ struct let forget_var t var = timing_wrap "forget_var" (forget_var t) var - let forget_vars t vars = t (*TODO + let forget_vars t vars = t + (*TODO if is_bot t || is_top_env t then t else let m = Option.get t.d in @@ -384,9 +433,9 @@ struct let show varM = let lookup i = Var.to_string (Environment.var_of_dim varM.env i) in let show_var i tuple = - match tuple with - | (None, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ Z.to_string offset - | (Some index, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ lookup index ^ " + " ^ Z.to_string offset + match tuple with + | (None, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ Z.to_string offset + | (Some index, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ lookup index ^ " + " ^ Z.to_string offset in match varM.d with | None -> "No equalities available" | Some arr -> Array.fold_left (fun acc elem -> acc ^ elem ) "" (Array.mapi show_var arr) @@ -522,7 +571,8 @@ struct let pretty_diff () (x, y) = dprintf "%s: %a not leq %a" (name ()) pretty x pretty y - (* implemented as described on page 10 in the paper about Fast Interprocedural Linear Two-Variable Equalities in the Section "Abstract Effect of Statements" *) + (* implemented as described on page 10 in the paper about Fast Interprocedural Linear Two-Variable Equalities in the Section "Abstract Effect of Statements" + TODO make a copy of the data structure*) let assign_texpr (t: VarManagement.t) var texp = let assigned_var = Environment.dim_of_var t.env var (* this is the variable we are assigning to *) in begin match t.d with @@ -576,8 +626,9 @@ struct let res = assign_var t v v' in if M.tracing then M.tracel "ops" "assign_var t:\n %s \n v: %s \n v': %s\n -> %s\n" (show t) (Var.to_string v) (Var.to_string v') (show res) ; res -(* from here on TODO till end of module*) - let assign_var_parallel t vv's = t (*TODO + (* from here on TODO till end of module*) + let assign_var_parallel t vv's = t + (*TODO let assigned_vars = List.map (function (v, _) -> v) vv's in let t = add_vars t assigned_vars in let primed_vars = List.init (List.length assigned_vars) (fun i -> Var.of_string (Int.to_string i ^"'")) in (* TODO: we use primed vars in analysis, conflict? *) @@ -593,7 +644,7 @@ struct let x = Option.get res.d in if Matrix.normalize_with x then {d = Some x; env = res.env} else bot () | _ -> t - *) + *) let assign_var_parallel t vv's = let res = assign_var_parallel t vv's in if M.tracing then M.tracel "ops" "assign_var parallel: %s -> %s \n" (show t) (show res); @@ -655,7 +706,14 @@ struct | exception Convert.Unsupported_CilExp _ | _, _ -> overflow_res res - let meet_tcons t tcons expr = t (*TODO + (*meet_tcons -> meet with guard in if statement + texpr -> tree expr (right hand side of equality) + -> expression used to derive tcons -> used to check for overflow + tcons -> tree constraint (x+y does not have types (overflow is type dependent) + *) + let meet_tcons t tcons expr = t + (*TODO let check_const cmp c = if cmp c Mpqf.zero then bot_env else t in let meet_vec e = @@ -687,7 +745,7 @@ struct | _, _ -> t end | None -> t - *) + *) let meet_tcons t tcons expr = timing_wrap "meet_tcons" (meet_tcons t tcons) expr @@ -710,7 +768,8 @@ struct let relift t = t - let invariant t = [] (*TODO + let invariant t = [] + (*TODO match t.d with | None -> [] | Some m -> @@ -727,7 +786,7 @@ struct Lincons1.{lincons0; env = EqualitiesArray_env} ) |> List.of_enum - *) + *) let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 diff --git a/src/goblint.ml b/src/goblint.ml index 4ea3a3d242..2b876f4f6c 100644 --- a/src/goblint.ml +++ b/src/goblint.ml @@ -2,13 +2,47 @@ open Goblint_lib open GobConfig open Maingoblint open Printf - +open SharedFunctions +open AffineEqualityAnalysis +open VarManagement +open Apron +(* open VarManagement *) +let var1 = Var.of_string "test1" +let var2 = Var.of_string "test2" +let env_test = add_vars (bot()) [var1; var2] +let print_opt opt print endl = match opt with | None -> print_endline "None" + | Some x -> print x; if endl then print_endline "" +let const_expr i = Texpr1.Cst (Scalar (Mpqf (Mpqf.of_int i))) +let env_with_information = D.assign_texpr env_test var1 (const_expr 3) (** the main function *) -let main () = +let main () = try Cilfacade.init (); Maingoblint.parse_arguments (); + (* test test + print_endline "Test 1"; + let env_test = add_vars (bot()) [(Var.of_string "test1")] in + print_t env_test; + print_endline ""; print_endline "";*) + + (* test get_coeff + print_endline "Test get_coeff1"; + print_opt (get_coeff env_test (Var var1)) Equality.print false; + print_env env_test.env; + print_endline ""; + print_endline "Test get_coeff2"; + print_opt (get_coeff env_test (const_expr 3)) Equality.print false; + print_endline ""; + print_endline "Test get_coeff2"; + print_opt (get_coeff env_test (const_expr 3)) Equality.print false; + print_endline ""; + print_t env_test;*) + + (* test assign_texpr + *) + print_t env_with_information; + (* Timing. *) Maingoblint.reset_stats (); if get_bool "dbg.timing.enabled" then ( From b036bc892585d8d2e67a47ea771debc42a51d486 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Fri, 1 Dec 2023 18:27:00 +0100 Subject: [PATCH 019/280] implemented assign_var_parallel --- .../apron/linearTwoVarEqualityDomain.apron.ml | 64 ++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index f9dd1166a2..9680e264fa 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -258,25 +258,6 @@ struct let forget_var t var = timing_wrap "forget_var" (forget_var t) var - let forget_vars t vars = t - (*TODO - if is_bot t || is_top_env t then t - else - let m = Option.get t.d in - if List.is_empty vars then t else - (*let rec rem_vars m vars' = - begin match vars' with - | [] -> m - | x :: xs -> rem_vars (remove_rels_with_var m x t.env true) xs end - in *){d = Some m; env = t.env} - - let forget_vars t vars = - let res = forget_vars t vars in - if M.tracing then M.tracel "ops" "forget_vars %s -> %s\n" (show t) (show res); - res - - let forget_vars t vars = timing_wrap "forget_vars" (forget_vars t) vars*) - let vars t = vars t.env let mem_var t var = Environment.mem_var t.env var @@ -454,7 +435,8 @@ struct let bot_env = {d = None; env = Environment.make [||] [||]} let is_bot_env t = t.d = None - +(*Would the top not be the identity matrix in affineEq? + i.e. the array where each variable is assigned itself with no other coeffcients? *) let top () = failwith "D.top ()" let is_top _ = false @@ -571,6 +553,23 @@ struct let pretty_diff () (x, y) = dprintf "%s: %a not leq %a" (name ()) pretty x pretty y + let forget_vars t vars = + if is_bot t || is_top_env t then t + else + let m = Option.get t.d in + if List.is_empty vars then t else + (*let rec rem_vars m vars' = + begin match vars' with + | [] -> m + | x :: xs -> rem_vars (remove_rels_with_var m x t.env true) xs end + in *){d = Some m; env = t.env} + + let forget_vars t vars = + let res = forget_vars t vars in + if M.tracing then M.tracel "ops" "forget_vars %s -> %s\n" (show t) (show res); + res + + let forget_vars t vars = timing_wrap "forget_vars" (forget_vars t) vars (* implemented as described on page 10 in the paper about Fast Interprocedural Linear Two-Variable Equalities in the Section "Abstract Effect of Statements" TODO make a copy of the data structure*) let assign_texpr (t: VarManagement.t) var texp = @@ -627,24 +626,29 @@ struct if M.tracing then M.tracel "ops" "assign_var t:\n %s \n v: %s \n v': %s\n -> %s\n" (show t) (Var.to_string v) (Var.to_string v') (show res) ; res (* from here on TODO till end of module*) - let assign_var_parallel t vv's = t - (*TODO + let assign_var_parallel t vv's = + List.fold_left (fun t (v1,v2) -> assign_var t v1 v2) t vv's + (* + **This implementation automatically assigns the variables to keep the invariant of the matrix, which is irrelvant for us + let assigned_vars = List.map (function (v, _) -> v) vv's in let t = add_vars t assigned_vars in let primed_vars = List.init (List.length assigned_vars) (fun i -> Var.of_string (Int.to_string i ^"'")) in (* TODO: we use primed vars in analysis, conflict? *) let t_primed = add_vars t primed_vars in let multi_t = List.fold_left2 (fun t' v_prime (_,v') -> assign_var t' v_prime v') t_primed primed_vars vv's in match multi_t.d with - | Some m when not @@ is_top_env multi_t -> let replace_col m x y = let dim_x, dim_y = Environment.dim_of_var multi_t.env x, Environment.dim_of_var multi_t.env y in - let col_x = Matrix.get_col m dim_x in - Matrix.set_col_with m col_x dim_y in - let m_cp = Matrix.copy m in - let switched_m = List.fold_left2 (fun m' x y -> replace_col m' x y) m_cp primed_vars assigned_vars in - let res = drop_vars {d = Some switched_m; env = multi_t.env} primed_vars true in + | Some arr when not @@ is_top_env multi_t -> + let replace_entry arr x y = let dim_x, dim_y = Environment.dim_of_var multi_t.env x, Environment.dim_of_var multi_t.env y in + let entry_x = EArray.get arr dim_x in + EArray.set arr entry_x dim_y in + let arr_cp = EArray.copy arr in + let switched_arr = List.fold_left2 (fun arr' x y -> replace_entry arr' x y) arr_cp primed_vars assigned_vars in + let res = drop_vars {d = Some switched_arr; env = multi_t.env} primed_vars true in let x = Option.get res.d in - if Matrix.normalize_with x then {d = Some x; env = res.env} else bot () + {d = Some x; env = res.env} | _ -> t - *) + *) + let assign_var_parallel t vv's = let res = assign_var_parallel t vv's in if M.tracing then M.tracel "ops" "assign_var parallel: %s -> %s \n" (show t) (show res); From b2970b675cb0c03256d0cb8a71216f78479a3c88 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 3 Dec 2023 11:53:01 +0100 Subject: [PATCH 020/280] Restore affineEqualitiesAnalysis and main function --- .../apron/affineEqualityAnalysis.apron.ml | 6 +-- src/goblint.ml | 40 ++----------------- 2 files changed, 5 insertions(+), 41 deletions(-) diff --git a/src/analyses/apron/affineEqualityAnalysis.apron.ml b/src/analyses/apron/affineEqualityAnalysis.apron.ml index 6d7d117882..62576476b6 100644 --- a/src/analyses/apron/affineEqualityAnalysis.apron.ml +++ b/src/analyses/apron/affineEqualityAnalysis.apron.ml @@ -5,8 +5,7 @@ open Analyses include RelationAnalysis -include LinearTwoVarEqualityDomain -(* + let spec_module: (module MCPSpec) Lazy.t = lazy ( let module AD = AffineEqualityDomain.D2 (VectorMatrix.ArrayVector) (VectorMatrix.ArrayMatrix) in @@ -36,5 +35,4 @@ let after_config () = GobConfig.set_string "ana.path_sens[+]" (Spec.name ()) let _ = - AfterConfig.register after_config -*) \ No newline at end of file + AfterConfig.register after_config \ No newline at end of file diff --git a/src/goblint.ml b/src/goblint.ml index 2b876f4f6c..f0c9a47111 100644 --- a/src/goblint.ml +++ b/src/goblint.ml @@ -2,47 +2,13 @@ open Goblint_lib open GobConfig open Maingoblint open Printf -open SharedFunctions -open AffineEqualityAnalysis -open VarManagement -open Apron -(* open VarManagement *) -let var1 = Var.of_string "test1" -let var2 = Var.of_string "test2" -let env_test = add_vars (bot()) [var1; var2] -let print_opt opt print endl = match opt with | None -> print_endline "None" - | Some x -> print x; if endl then print_endline "" -let const_expr i = Texpr1.Cst (Scalar (Mpqf (Mpqf.of_int i))) -let env_with_information = D.assign_texpr env_test var1 (const_expr 3) + (** the main function *) -let main () = +let main () = try Cilfacade.init (); Maingoblint.parse_arguments (); - (* test test - print_endline "Test 1"; - let env_test = add_vars (bot()) [(Var.of_string "test1")] in - print_t env_test; - print_endline ""; print_endline "";*) - - (* test get_coeff - print_endline "Test get_coeff1"; - print_opt (get_coeff env_test (Var var1)) Equality.print false; - print_env env_test.env; - print_endline ""; - print_endline "Test get_coeff2"; - print_opt (get_coeff env_test (const_expr 3)) Equality.print false; - print_endline ""; - print_endline "Test get_coeff2"; - print_opt (get_coeff env_test (const_expr 3)) Equality.print false; - print_endline ""; - print_t env_test;*) - - (* test assign_texpr - *) - print_t env_with_information; - (* Timing. *) Maingoblint.reset_stats (); if get_bool "dbg.timing.enabled" then ( @@ -118,4 +84,4 @@ let main () = exit 124 (* We do this since the evaluation order of top-level bindings is not defined, but we want `main` to run after all the other side-effects (e.g. registering analyses/solvers) have happened. *) -let () = at_exit main +let () = at_exit main \ No newline at end of file From 884d75002d2cb388e4009700a729a58df84d73c9 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 3 Dec 2023 15:44:46 +0100 Subject: [PATCH 021/280] Refactored linearTwoVarEqualityDomain and affineEqualityDomain s.t. there is less code duplication. Moved the module Mpqf and many functions of VaManagement to SharedFunctions, s.t. the two domains can use them. The functions of VarManagement are now in the module VarManagementOps in SharedFunctions. --- .../apron/affineEqualityDomain.apron.ml | 110 +------- .../apron/linearTwoVarEqualityDomain.apron.ml | 259 ++++++------------ src/cdomains/apron/sharedFunctions.apron.ml | 138 ++++++++++ 3 files changed, 225 insertions(+), 282 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index a6f00fdba0..e4cf37fc83 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -13,19 +13,7 @@ module M = Messages open Apron open VectorMatrix -module Mpqf = struct - include Mpqf - let compare = cmp - let zero = of_int 0 - let one = of_int 1 - let mone = of_int (-1) - - let get_den x = Z_mlgmpidl.z_of_mpzf @@ Mpqf.get_den x - - let get_num x = Z_mlgmpidl.z_of_mpzf @@ Mpqf.get_num x - let hash x = 31 * (Z.hash (get_den x)) + Z.hash (get_num x) -end - +module Mpqf = SharedFunctions.Mpqf module Var = SharedFunctions.Var module V = RelationDomain.V(Var) @@ -33,103 +21,9 @@ module V = RelationDomain.V(Var) Furthermore, it provides the function get_coeff_vec that parses an apron expression into a vector of coefficients if the apron expression has an affine form. *) module VarManagement (Vec: AbstractVector) (Mx: AbstractMatrix)= struct - include SharedFunctions.EnvOps module Vector = Vec (Mpqf) module Matrix = Mx(Mpqf) (Vec) - - type t = { - mutable d : Matrix.t option; - mutable env : Environment.t - } - [@@deriving eq, ord, hash] - - let empty_env = Environment.make [||] [||] - - let bot () = - {d = Some (Matrix.empty ()); env = empty_env} - - let bot_env = {d = None; env = empty_env} - - let is_bot_env t = t.d = None - - let copy t = {t with d = Option.map Matrix.copy t.d} - - let dim_add (ch: Apron.Dim.change) m = - Array.iteri (fun i x -> ch.dim.(i) <- x + i) ch.dim; - Matrix.add_empty_columns m ch.dim - - let dim_add ch m = timing_wrap "dim add" (dim_add ch) m - - let dim_remove (ch: Apron.Dim.change) m del = - if Array.length ch.dim = 0 || Matrix.is_empty m then m else ( - Array.iteri (fun i x-> ch.dim.(i) <- x + i) ch.dim; - let m' = if not del then let m = Matrix.copy m in Array.fold_left (fun y x -> Matrix.reduce_col_with y x; y) m ch.dim else m in - Matrix.remove_zero_rows @@ Matrix.del_cols m' ch.dim) - - let dim_remove ch m del = timing_wrap "dim remove" (dim_remove ch m) del - - let change_d t new_env add del = - if Environment.equal t.env new_env then t else - let dim_change = if add then Environment.dimchange t.env new_env - else Environment.dimchange new_env t.env - in match t.d with - | None -> bot_env - | Some m -> {d = Some (if add then dim_add dim_change m else dim_remove dim_change m del); env = new_env} - - let change_d t new_env add del = timing_wrap "dimension change" (change_d t new_env add) del - - let add_vars t vars = - let t = copy t in - let env' = add_vars t.env vars in - change_d t env' true false - - let add_vars t vars = timing_wrap "add_vars" (add_vars t) vars - - let drop_vars t vars del = - let t = copy t in - let env' = remove_vars t.env vars in - change_d t env' false del - - let drop_vars t vars = timing_wrap "drop_vars" (drop_vars t) vars - - let remove_vars t vars = drop_vars t vars false - - let remove_vars t vars = timing_wrap "remove_vars" (remove_vars t) vars - - let remove_vars_with t vars = - let t' = remove_vars t vars in - t.d <- t'.d; - t.env <- t'.env - - let remove_filter t f = - let env' = remove_filter t.env f in - change_d t env' false false - - let remove_filter t f = timing_wrap "remove_filter" (remove_filter t) f - - let remove_filter_with t f = - let t' = remove_filter t f in - t.d <- t'.d; - t.env <- t'.env - - let keep_filter t f = - let t = copy t in - let env' = keep_filter t.env f in - change_d t env' false false - - let keep_filter t f = timing_wrap "keep_filter" (keep_filter t) f - - let keep_vars t vs = - let t = copy t in - let env' = keep_vars t.env vs in - change_d t env' false false - - let keep_vars t vs = timing_wrap "keep_vars" (keep_vars t) vs - - let vars t = vars t.env - - let mem_var t var = Environment.mem_var t.env var - + include SharedFunctions.VarManagementOps (Mx(Mpqf) (Vec)) include ConvenienceOps(Mpqf) let get_c v = match Vector.findi (fun x -> x <>: Mpqf.zero) v with diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 9680e264fa..19e6f3f732 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -29,7 +29,7 @@ - meet_tcons HOW TO RUN THE REGRESSION TESTS: - Method 1: regression test ./rectest.sh numberofdirectory numberoftest + Method 1: regression test ./regtest.sh numberofdirectory numberoftest Method 2: make test -> run entire test suite -> the two methods have a different behaviour w.r.t. unreachable code script update suite.rb argumentgroupname ???? No idea @@ -62,19 +62,8 @@ open Printf (** TODO: modify code *) -module Mpqf = struct (* multi-precision rational numbers *) - include Mpqf - let compare = cmp - let zero = of_int 0 - let one = of_int 1 - let mone = of_int (-1) - - let get_den x = Z_mlgmpidl.z_of_mpzf @@ Mpqf.get_den x - - let get_num x = Z_mlgmpidl.z_of_mpzf @@ Mpqf.get_num x - let hash x = 31 * (Z.hash (get_den x)) + Z.hash (get_num x) -end +module Mpqf = SharedFunctions.Mpqf module Equality = struct (* (Some i, k) represents a sum of a variable with index i and the number k. @@ -94,9 +83,11 @@ module EqualitiesArray = struct let hash : t -> int = (fun x -> 31 + Equality.to_int x.(0)) (* TODO **) + let empty () = [||] + let make_empty_array len = Array.mapi (fun i (x, y) -> (Some i, Z.zero)) (make len Equality.zero) - let add_element arr index = + let add_empty_column arr index = let num_vars = length arr in if index > num_vars then failwith "n too large" else let new_array = make (num_vars + 1) (Equality.var_zero index) in @@ -104,7 +95,7 @@ module EqualitiesArray = struct blit arr 0 new_array 0 index; if index <> num_vars then blit arr index new_array (index + 1) (num_vars - index); new_array - let add_elements m indexes = (** same as add_empty_columns for Matrix (see vectorMatrix.ml)*) + let add_empty_columns m indexes = (** same as add_empty_columns for Matrix (see vectorMatrix.ml)*) let nnc = length indexes in if nnc = 0 then m else let nc = length m in @@ -116,7 +107,7 @@ module EqualitiesArray = struct done; m' - let del_cols : ('a option * Z.t) mappable ->int mappable ->('a option * Z.t) mappable = fun m cols -> + let del_cols m cols = let n_c = length cols in if n_c = 0 || length m = 0 then m else @@ -132,6 +123,54 @@ module EqualitiesArray = struct let del_cols m cols = timing_wrap "del_cols" (del_cols m) cols + let is_empty m = length m = 0 + + let find_reference_variable d var_index = fst d.(var_index) + + + let find_vars_in_the_connected_component d ref_var = + filter (fun i -> let (var, _) = d.(i) in var = ref_var) (mapi const d) + + let find_var_in_the_connected_component_with_least_index d ref_var = + fold_left (fun curr_min (var, _) -> if var = ref_var then match curr_min with + | None -> var + | Some curr_min -> + match var with + |Some i -> if i < curr_min then Some i else Some curr_min + | None -> Some curr_min else curr_min) None d + + (* Forget information about variable var in-place. + The name reduce_col_with is because the affineEqualitiesDomain also defines this function, + and it represents the equalities with a matrix, not like in this case with an array. + We could think about changing this name, then we would need to change it also in + shared_Functions.apron.ml and ectorMatrix.ml and affineEqualitiesDomain.ml *) + let reduce_col_with d var = + let ref_var_opt = find_reference_variable d var in + d.(var) <- Equality.var_zero var; + begin match ref_var_opt with + | None -> (* the variable is equal to a constant *) () + | Some ref_var -> + if ref_var <> var then () + else + (* x_i is the reference variable of its connected component *) + let dim_of_var = Some var in + let connected_component = find_vars_in_the_connected_component d dim_of_var in + if length connected_component = 1 + then () (* x_i is the only element of its connected component *) + else + (* x_i is the reference variable -> we need to find a new reference variable *) + let var_least_index = Option.get @@ find_var_in_the_connected_component_with_least_index d dim_of_var in + let (_, off) = d.(var_least_index) in + iteri (fun _ x -> let (_, off2) = d.(x) in d.(x) <- (Some var_least_index, Z.(off2 - off))) connected_component; + end + + (* Forget information about variable i but notin-place *) + let reduce_col m j = + let copy = copy m in + reduce_col_with copy j; + copy + + let remove_zero_rows t = t end @@ -143,16 +182,8 @@ module V = RelationDomain.V(Var) Furthermore, it provides the function get_coeff_vec that parses an apron expression into a vector of coefficients if the apron expression has an affine form. *) module VarManagement = struct - include SharedFunctions.EnvOps module EArray = EqualitiesArray - - type t = { - mutable d : EArray.t option; - mutable env : Environment.t - } - [@@deriving eq, ord, hash] - - let empty_env = Environment.make [||] [||] + include SharedFunctions.VarManagementOps (EArray) (* For debugging *) let print_env = Environment.print (Format.std_formatter) @@ -164,105 +195,13 @@ struct | Some x -> (print_d x; print_endline "") | None -> printf "None " end; print_env t.env; print_endline "" - let bot () = - {d = Some [||]; env = empty_env} - - let bot_env = {d = None; env = empty_env} - - let is_bot_env t = t.d = None - - let copy t = {t with d = Option.map EArray.copy t.d} let size t = match t.d with | None -> 0 | Some d -> EArray.length d - let dim_add (ch: Apron.Dim.change) m = - EArray.iteri (fun i x -> ch.dim.(i) <- x + i) ch.dim; (* ?? *) - EArray.add_elements m ch.dim - - let dim_add ch m = timing_wrap "dim add" (dim_add ch) m (*?*) - - let dim_remove (ch: Apron.Dim.change) m del = - if EArray.length ch.dim = 0 || (EArray.length m = 0) then m else ( - EArray.iteri (fun i x -> ch.dim.(i) <- x + i) ch.dim; - let m' = if not del then let m = EArray.copy m in EArray.add_elements m ch.dim else m in - EArray.del_cols m' ch.dim) - - let dim_remove ch m del = timing_wrap "dim remove" (dim_remove ch m) del - - let change_d t new_env add del = - if Environment.equal t.env new_env then t else - let dim_change = if add then Environment.dimchange t.env new_env - else Environment.dimchange new_env t.env - in match t.d with - | None -> bot_env - | Some m -> {d = Some (if add then dim_add dim_change m else dim_remove dim_change m del); env = new_env} - - let change_d t new_env add del = timing_wrap "dimension change" (change_d t new_env add) del - - let add_vars t vars = - let t = copy t in - let env' = add_vars t.env vars in - change_d t env' true false - - let add_vars t vars = timing_wrap "add_vars" (add_vars t) vars - - let drop_vars t vars del = - let t = copy t in - let env' = remove_vars t.env vars in - change_d t env' false del - - let drop_vars t vars = timing_wrap "drop_vars" (drop_vars t) vars - (*TODO used by relational domain*) - let remove_vars t vars = drop_vars t vars false - - let remove_vars t vars = timing_wrap "remove_vars" (remove_vars t) vars - - let remove_vars_with t vars = - let t' = remove_vars t vars in - t.d <- t'.d; - t.env <- t'.env - - let remove_filter t f = - let env' = remove_filter t.env f in - change_d t env' false false - - let remove_filter t f = timing_wrap "remove_filter" (remove_filter t) f - - let remove_filter_with t f = - let t' = remove_filter t f in - t.d <- t'.d; - t.env <- t'.env - - let keep_filter t f = - let t = copy t in - let env' = keep_filter t.env f in - change_d t env' false false - - let keep_filter t f = timing_wrap "keep_filter" (keep_filter t) f - - let keep_vars t vs = - let t = copy t in - let env' = keep_vars t.env vs in - change_d t env' false false - - let keep_vars t vs = timing_wrap "keep_vars" (keep_vars t) vs - - let forget_var t var_index = - match t.d with - | None -> t - | Some d -> d.(var_index) <- Equality.var_zero var_index; - {t with d = Some d} - - - let forget_var t var = timing_wrap "forget_var" (forget_var t) var - - let vars t = vars t.env - - let mem_var t var = Environment.mem_var t.env var - - (* Returns the constant represented by an equality, if the equality represents a constant without a variable *) + (* Returns the constant represented by an equality, if the equality represents a constant without a variable. + Else it returns None. *) let get_constant (var, off) = match var with | None -> Some off | _ -> None @@ -320,49 +259,18 @@ struct let get_coeff t texp = timing_wrap "coeff_vec" (get_coeff t) texp - let find_reference_variable d env var = fst d.(Environment.dim_of_var env var) - - let find_vars_in_the_connected_component d env ref_var = - EArray.filter (fun i -> let (var, _) = d.(i) in var = ref_var) (EArray.mapi const d) - - let find_var_in_the_connected_component_with_least_index d env ref_var = - EArray.fold_left (fun curr_min (var, _) -> if var = ref_var then match curr_min with - | None -> var - | Some curr_min -> - match var with - |Some i -> if i < curr_min then Some i else Some curr_min - | None -> Some curr_min else curr_min) None d - let abstract_exists var t = match t.d with - | Some d -> - let var_to_remove = Environment.dim_of_var t.env var in - begin match find_reference_variable d t.env var with - | None -> (* the variable is equal to a constant *) t - | Some ref_var -> - if ref_var <> var_to_remove then forget_var t var_to_remove - else - (* x_i is the reference variable of its connected component *) - let dim_of_var = Some (Environment.dim_of_var t.env var) in - let connected_component = find_vars_in_the_connected_component d t.env dim_of_var in - if EArray.length connected_component = 1 - then t (* x_i is the only element of its connected component *) - else - (* x_i is the reference variable -> we need to find a new reference variable *) - let var_least_index = Option.get @@ find_var_in_the_connected_component_with_least_index d t.env dim_of_var in - let (_, off) = d.(var_least_index) in - EArray.iteri (fun _ x -> let (_, off2) = d.(x) in d.(x) <- (Some var_least_index, Z.(off2 - off))) connected_component; - {d = Some d; env = t.env} - end + | Some d -> {t with d = Some (EArray.reduce_col d (Environment.dim_of_var t.env var))} | None -> t (* there are no variables in the current environment *) let assign_const t var const = match t.d with | None -> t - | Some d -> d.(var) <- (None, const); t + | Some t_d -> let d = EArray.copy t_d in d.(var) <- (None, const); {d = Some d; env = t.env} let subtract_const_from_var t var const = match t.d with | None -> t - | Some d -> + | Some t_d -> let d = EArray.copy t_d in let subtract_const_from_var_for_single_equality const index element = let (eq_var_opt, off2) = d.(index) in if index = var then @@ -379,9 +287,6 @@ struct end -(*end*) - - (** TODO: overflow checking *) module ExpressionBounds: (SharedFunctions.ConvBounds with type t = VarManagement.t) = struct @@ -435,13 +340,13 @@ struct let bot_env = {d = None; env = Environment.make [||] [||]} let is_bot_env t = t.d = None -(*Would the top not be the identity matrix in affineEq? - i.e. the array where each variable is assigned itself with no other coeffcients? *) + (*Would the top not be the identity matrix in affineEq? + i.e. the array where each variable is assigned itself with no other coeffcients? *) let top () = failwith "D.top ()" let is_top _ = false - let is_top_env t = (not @@ Environment.equal empty_env t.env) (*&& GobOption.exists Matrix.is_empty t.d*) + let is_top_env t = (not @@ Environment.equal empty_env t.env) && GobOption.exists EArray.is_empty t.d let meet t1 t2 = let sup_env = Environment.lce t1.env t2.env in @@ -549,29 +454,35 @@ struct join a b else b + let remove_rels_with_var x var env imp = + let j0 = Environment.dim_of_var env var in + if imp then (EArray.reduce_col_with x j0; x) else EArray.reduce_col x j0 + let narrow a b = a let pretty_diff () (x, y) = dprintf "%s: %a not leq %a" (name ()) pretty x pretty y + (* TODO: I'm not sure if forget_vars should remove the variable from the data structure, + or just forget the information we currently have about the variable. Until now, the second possibility is implemented.*) let forget_vars t vars = if is_bot t || is_top_env t then t else let m = Option.get t.d in if List.is_empty vars then t else - (*let rec rem_vars m vars' = - begin match vars' with - | [] -> m - | x :: xs -> rem_vars (remove_rels_with_var m x t.env true) xs end - in *){d = Some m; env = t.env} + let rec rem_vars m vars' = + begin match vars' with + | [] -> m + | x :: xs -> rem_vars (remove_rels_with_var m x t.env true) xs end + in {d = Some (EArray.remove_zero_rows @@ rem_vars (EArray.copy m) vars); env = t.env} let forget_vars t vars = - let res = forget_vars t vars in - if M.tracing then M.tracel "ops" "forget_vars %s -> %s\n" (show t) (show res); - res + let res = forget_vars t vars in + if M.tracing then M.tracel "ops" "forget_vars %s -> %s\n" (show t) (show res); + res let forget_vars t vars = timing_wrap "forget_vars" (forget_vars t) vars (* implemented as described on page 10 in the paper about Fast Interprocedural Linear Two-Variable Equalities in the Section "Abstract Effect of Statements" - TODO make a copy of the data structure*) + This makes a copy of the data structure, it doesn't change t in-place. *) let assign_texpr (t: VarManagement.t) var texp = let assigned_var = Environment.dim_of_var t.env var (* this is the variable we are assigning to *) in begin match t.d with @@ -609,7 +520,7 @@ struct match Convert.texpr1_expr_of_cil_exp t t.env exp (Lazy.force no_ov) with | exp -> assign_texpr t var exp | exception Convert.Unsupported_CilExp _ -> - if is_bot t then t else forget_var t (Environment.dim_of_var t.env var) + if is_bot t then t else forget_vars t [var] let assign_exp t var exp no_ov = let res = assign_exp t var exp no_ov in @@ -627,10 +538,10 @@ struct res (* from here on TODO till end of module*) let assign_var_parallel t vv's = - List.fold_left (fun t (v1,v2) -> assign_var t v1 v2) t vv's - (* + List.fold_left (fun t (v1,v2) -> assign_var t v1 v2) t vv's + (* **This implementation automatically assigns the variables to keep the invariant of the matrix, which is irrelvant for us - + let assigned_vars = List.map (function (v, _) -> v) vv's in let t = add_vars t assigned_vars in let primed_vars = List.init (List.length assigned_vars) (fun i -> Var.of_string (Int.to_string i ^"'")) in (* TODO: we use primed vars in analysis, conflict? *) @@ -648,7 +559,7 @@ struct {d = Some x; env = res.env} | _ -> t *) - + let assign_var_parallel t vv's = let res = assign_var_parallel t vv's in if M.tracing then M.tracel "ops" "assign_var parallel: %s -> %s \n" (show t) (show res); @@ -677,7 +588,7 @@ struct let substitute_exp t var exp no_ov = let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in let res = assign_exp t var exp no_ov in - forget_var res (Environment.dim_of_var t.env var) + forget_vars res [var] let substitute_exp t var exp ov = let res = substitute_exp t var exp ov diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 059a7f8264..f06bb66165 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -351,6 +351,129 @@ struct end +(* Abstraction for the domain representations of the relational analyses: + affineEqualityDomain (uses a matrix) and linearTwoVarEqualityDomain (uses an Array)*) +module type AbstractRelationalDomainRepresentation = +sig + type t + val hash: t -> int + val equal : t -> t -> bool + val compare : t -> t -> int + val hash : t -> int + val empty : unit -> t + val copy : t -> t + val add_empty_columns : t -> int array -> t + val is_empty : t -> bool + val reduce_col_with : t -> int -> unit + + val remove_zero_rows : t -> t + + val del_cols : t -> int array -> t + +end + +(* Shared operations for the management of the Matrix or Array representations of the domains, + used by affineEqualityDomain and linearTwoVarEqualityDomain *) +module VarManagementOps (RelDomain : AbstractRelationalDomainRepresentation) = +struct + include EnvOps + type t = { + mutable d : RelDomain.t option; + mutable env : Environment.t + } + [@@deriving eq, ord, hash] + + let empty_env = Environment.make [||] [||] + + let bot () = + {d = Some (RelDomain.empty ()); env = empty_env} + + let bot_env = {d = None; env = empty_env} + + let is_bot_env t = t.d = None + + let copy t = {t with d = Option.map RelDomain.copy t.d} + + let dim_add (ch: Apron.Dim.change) m = + Array.iteri (fun i x -> ch.dim.(i) <- x + i) ch.dim; + RelDomain.add_empty_columns m ch.dim + + let dim_add ch m = VectorMatrix.timing_wrap "dim add" (dim_add ch) m + + let dim_remove (ch: Apron.Dim.change) m del = + if Array.length ch.dim = 0 || RelDomain.is_empty m then m else ( + Array.iteri (fun i x-> ch.dim.(i) <- x + i) ch.dim; + let m' = if not del then let m = RelDomain.copy m in Array.fold_left (fun y x -> RelDomain.reduce_col_with y x; y) m ch.dim else m in + RelDomain.remove_zero_rows @@ RelDomain.del_cols m' ch.dim) + + let dim_remove ch m del = VectorMatrix.timing_wrap "dim remove" (dim_remove ch m) del + + + let change_d t new_env add del = + if Environment.equal t.env new_env then t else + let dim_change = if add then Environment.dimchange t.env new_env + else Environment.dimchange new_env t.env + in match t.d with + | None -> bot_env + | Some m -> {d = Some (if add then dim_add dim_change m else dim_remove dim_change m del); env = new_env} + + let change_d t new_env add del = VectorMatrix.timing_wrap "dimension change" (change_d t new_env add) del + + + let add_vars t vars = + let t = copy t in + let env' = add_vars t.env vars in + change_d t env' true false + + let add_vars t vars = VectorMatrix.timing_wrap "add_vars" (add_vars t) vars + + let drop_vars t vars del = + let t = copy t in + let env' = remove_vars t.env vars in + change_d t env' false del + + let drop_vars t vars = VectorMatrix.timing_wrap "drop_vars" (drop_vars t) vars + + let remove_vars t vars = drop_vars t vars false + + let remove_vars t vars = VectorMatrix.timing_wrap "remove_vars" (remove_vars t) vars + + let remove_vars_with t vars = + let t' = remove_vars t vars in + t.d <- t'.d; + t.env <- t'.env + + let remove_filter t f = + let env' = remove_filter t.env f in + change_d t env' false false + + let remove_filter t f = VectorMatrix.timing_wrap "remove_filter" (remove_filter t) f + + let remove_filter_with t f = + let t' = remove_filter t f in + t.d <- t'.d; + t.env <- t'.env + + let keep_filter t f = + let t = copy t in + let env' = keep_filter t.env f in + change_d t env' false false + + let keep_filter t f = VectorMatrix.timing_wrap "keep_filter" (keep_filter t) f + + let keep_vars t vs = + let t = copy t in + let env' = keep_vars t.env vs in + change_d t env' false false + + let keep_vars t vs = VectorMatrix.timing_wrap "keep_vars" (keep_vars t) vs + + let vars t = vars t.env + + let mem_var t var = Environment.mem_var t.env var + +end + (** A more specific module type for RelationDomain.RelD2 with ConvBounds integrated and various apron elements. It is designed to be the interface for the D2 modules in affineEqualityDomain and apronDomain and serves as a functor argument for AssertionModule. *) module type AssertionRelS = @@ -447,3 +570,18 @@ struct | (None, None) -> ID.top_of ik end + +(* Multi-precision rational numbers, defined by Apron. + Used by affineEqualityDomain and linearTwoVarEqualityDomain *) +module Mpqf = struct + include Mpqf + let compare = cmp + let zero = of_int 0 + let one = of_int 1 + let mone = of_int (-1) + + let get_den x = Z_mlgmpidl.z_of_mpzf @@ Mpqf.get_den x + + let get_num x = Z_mlgmpidl.z_of_mpzf @@ Mpqf.get_num x + let hash x = 31 * (Z.hash (get_den x)) + Z.hash (get_num x) +end From 030f30a3d9ed94bdc4ceb96a3abce14ac2441ce6 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 4 Dec 2023 16:34:25 +0100 Subject: [PATCH 022/280] Added hash function for EqualitiesArray and added many explanatory comments --- .../apron/linearTwoVarEqualityDomain.apron.ml | 63 +++++++++++-------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 19e6f3f732..8ce656c4d0 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -37,8 +37,10 @@ - gobview doesnt work with apron -Visualize test: ./regtest.sh 63 01 - see result/ folder - index.xml -> (printxml uses show) + python3 -m http.server + open http://localhost:8000/ on a browser + go to /result folder + index.xml -> main (printxml uses show) click on program points orange nodes: dead code state at the beginning of the line @@ -50,6 +52,18 @@ Maybe TODO: Abstract Vector in order to have less code duplication (e.g. VectorBase und Vector) + + DEBUG: + 1. print stack trace while executing ./goblint: + -v option for goblint -> prints stack trace + 2. Print the debug information defined with M.tracel: + https://goblint.readthedocs.io/en/latest/developer-guide/debugging/#tracing + ./script/trace_on + --trace name1 --trace name2 + 3. Debug OCaml + gdb debug for OCaml + or with EarlyBird (apparently it will maybe not work) + or with ocamldebug *) open Batteries @@ -81,7 +95,7 @@ module EqualitiesArray = struct include Array type t = Equality.t Array.t [@@deriving eq, ord] - let hash : t -> int = (fun x -> 31 + Equality.to_int x.(0)) (* TODO **) + let hash : t -> int = Array.fold_left (fun acc a -> 31 * acc + Equality.hash a) 0 let empty () = [||] @@ -369,14 +383,12 @@ struct let meet t1 t2 = timing_wrap "meet" (meet t1) t2 + (* TODO: implement less equal *) let leq t1 t2 = let env_comp = Environment.compare t1.env t2.env in (* Apron's Environment.compare has defined return values. *) if env_comp = -2 || env_comp > 0 then false else if is_bot t1 || is_top_env t2 then true else - if is_bot t2 || is_top_env t1 then false else ( - let m1, m2 = Option.get t1.d, Option.get t2.d in - let m1' = if env_comp = 0 then m1 else dim_add (Environment.dimchange t1.env t2.env) m1 in - true) + if is_bot t2 || is_top_env t1 then false else (true) let leq a b = timing_wrap "leq" (leq a) b @@ -604,27 +616,12 @@ struct exception NotRefinable - let meet_tcons_one_var_eq res expr = - let overflow_res res = if IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp expr) then res else raise NotRefinable in - match Convert.find_one_var expr with - | None -> overflow_res res - | Some v -> - let ik = Cilfacade.get_ikind v.vtype in - match Bounds.bound_texpr res (Convert.texpr1_of_cil_exp res res.env (Lval (Cil.var v)) true) with - | Some _, Some _ when not (Cil.isSigned ik) -> raise NotRefinable (* TODO: unsigned w/o bounds handled differently? *) - | Some min, Some max -> - assert (Z.equal min max); (* other bounds impossible in affeq *) - let (min_ik, max_ik) = IntDomain.Size.range ik in - if Z.compare min min_ik < 0 || Z.compare max max_ik > 0 then - if IntDomain.should_ignore_overflow ik then bot () else raise NotRefinable - else res - | exception Convert.Unsupported_CilExp _ - | _, _ -> overflow_res res + let meet_tcons_one_var_eq res expr = res (*meet_tcons -> meet with guard in if statement texpr -> tree expr (right hand side of equality) -> expression used to derive tcons -> used to check for overflow - tcons -> tree constraint (x+y tree constraint (expression < 0) -> does not have types (overflow is type dependent) *) let meet_tcons t tcons expr = t @@ -672,6 +669,17 @@ struct if M.tracing then M.tracel "ops" "unify: %s %s -> %s\n" (show a) (show b) (show res); res + (* Assert a constraint expression. Defined in apronDomain.apron.ml + + If the constraint is never fulfilled, then return bottom. + Else the domain can be modified with the new information given by the constraint. + + It basically just calls the function meet_tcons. + + It is called by eval (defined in sharedFunctions), but also when a guard in + e.g. an if statement is encountered in the C code. + + *) let assert_cons d e negate no_ov = let no_ov = Lazy.force no_ov in if M.tracing then M.tracel "assert_cons" "assert_cons with expr: %a %b" d_exp e no_ov; @@ -683,6 +691,11 @@ struct let relift t = t + (* representation as C expression + + This function returns all the equalities that are saved in our datastructure t. + + Lincons -> linear constraint *) let invariant t = [] (*TODO match t.d with @@ -708,7 +721,7 @@ struct let env (t: Bounds.t) = t.env type marshal = Bounds.t - + (* marshal is not compatible with apron, therefore we don't have to implement it *) let marshal t = t let unmarshal t = t From 2b776403f358d66f95ca418a6447d8f2425329e8 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Tue, 5 Dec 2023 16:21:37 +0100 Subject: [PATCH 023/280] Reimplemented get_coeff s.t. it parses more than one variable (this is required for me et_tcons) --- .../apron/linearTwoVarEqualityDomain.apron.ml | 83 ++++++++++++------- 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 8ce656c4d0..4d80a8fbb8 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -220,14 +220,28 @@ struct | None -> Some off | _ -> None - let get_coeff (t: t) texp = - (*Parses a Texpr to obtain a (variable, offset) pair to repr. a sum of a variable and an offset. - Returns None if the expression is not a sum between a variable (without coefficient) and a constant. + let get_coeff_vec (t: t) texp = + (*Parses a Texpr to obtain a (coefficient, variable) pair list to repr. a sum of a variables that have a coefficient. If variable is None, the coefficient represents a constant offset. *) let open Apron.Texpr1 in - let exception NotLinear2Var in + let exception NotLinearExpr in let exception NotIntegerOffset in - let mpqf_to_int x = + let negate coeff_var_list = List.map (fun (coeff, var) -> (Z.(-coeff), var)) coeff_var_list in + let multiply_with_Z number coeff_var_list = + List.map (fun (coeff, var) -> (Z.(number * coeff, var))) coeff_var_list in + let multiply a b = + (* if one of them is a constant, then multiply. Otherwise, the expression is not linear*) + if List.length a = 1 then + match List.nth a 0 with + | (a_coeff, None) -> multiply_with_Z a_coeff b + | _ -> raise NotLinearExpr + else + if List.length b = 1 then + match List.nth b 0 with + | (b_coeff, None) -> multiply_with_Z b_coeff a + | _ -> raise NotLinearExpr + else raise NotLinearExpr in + let mpqf_to_Z x = if not(Z.equal (Mpqf.get_den x) Z.one) then raise NotIntegerOffset else Mpqf.get_num x in let rec convert_texpr texp = @@ -239,37 +253,50 @@ struct | Interval _ -> failwith "Not a constant" | Scalar x -> begin match x with | Float x -> raise NotIntegerOffset - | Mpqf x -> (None, mpqf_to_int x) + | Mpqf x -> [(mpqf_to_Z x, None)] | Mpfrf x -> raise NotIntegerOffset end in of_union x | Var x -> - let var_dim = print Format.std_formatter (Texpr1.of_expr t.env (Var x)); Environment.dim_of_var t.env x in - (Some var_dim, Z.zero) + let var_dim = Environment.dim_of_var t.env x in [(Z.one, Some var_dim)] | Unop (u, e, _, _) -> begin match u with - | Neg -> raise NotLinear2Var + | Neg -> negate (convert_texpr e) | Cast -> convert_texpr e (*Ignore since casts in apron are used for floating point nums and rounding in contrast to CIL casts*) - | Sqrt -> raise NotLinear2Var end + | Sqrt -> raise NotLinearExpr end | Binop (b, e1, e2, _, _) -> begin match b with - | Add -> begin match convert_texpr e1, convert_texpr e2 with - | (None, off1), (var2, off2) -> (var2, Z.(off1 + off2)) - | (var1, off1), (None, off2) -> (var1, Z.(off1 + off2)) - | (_, _), (_, _) -> raise NotLinear2Var end - | Sub -> begin match convert_texpr e1, convert_texpr e2 with - | (None, off1), (var2, off2) -> raise NotLinear2Var - | (var1, off1), (None, off2) -> (var1, Z.(off1 - off2)) - | (var1, off1), (var2, off2) -> if var1 = var2 then (None, Z.(off1 - off2)) else raise NotLinear2Var end - | Mul -> - let x1, x2 = convert_texpr e1, convert_texpr e2 in - begin match get_constant x1, get_constant x2 with - | Some c1, Some c2 -> (None, Z.(c1 * c2)) - | _, _ -> raise NotLinear2Var end - | _ -> raise NotLinear2Var end + | Add -> List.concat [convert_texpr e1; convert_texpr e2] + | Sub -> List.concat [convert_texpr e1; negate (convert_texpr e2)] + | Mul -> multiply (convert_texpr e1) (convert_texpr e2) + | _ -> raise NotLinearExpr end end - in match convert_texpr texp with - | exception NotLinear2Var -> None - | exception NotIntegerOffset -> None - | x -> Some(x) + in convert_texpr texp + + let number_vars x = 0(*TODO*) + + let get_coeff (t: t) texp = + (*Parses a Texpr to obtain a (variable, offset) pair to repr. a sum of a variable and an offset. + Returns None if the expression is not a sum between a variable (without coefficient) and a constant. + *) + let sum_coefficients summands_list = + List.fold_left (fun (var, current_var_offset, curr_offset) (next_coeff, next_var) -> + begin match next_var with + | None -> (* this element represents a constant offset *) + (var, current_var_offset, Z.(curr_offset + next_coeff)) + | Some same_var -> (* this element represents a variable with a coefficient + -> it must be always the same variable because we only call this function if number_vars summands_list < 2*) + (Some same_var, Z.(current_var_offset + next_coeff), curr_offset) end) + (None, Z.zero, Z.zero) summands_list + in + match get_coeff_vec t texp with + | exception _ -> None + | summands_list -> if number_vars summands_list < 2 then + let (var, var_coeff, offset) = sum_coefficients summands_list in + if var = None then Some (None, offset) + else if var_coeff = Z.one then Some (var, offset) + else None + else + None + let get_coeff t texp = timing_wrap "coeff_vec" (get_coeff t) texp From 2a96d1305809a0cdb12c6cc571ffd1569449c451 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Wed, 6 Dec 2023 00:14:15 +0100 Subject: [PATCH 024/280] test cases --- tests/regression/77-lin2vareq/00.c | 19 +++++++++++++++ .../77-lin2vareq}/01.c | 5 ++-- .../77-lin2vareq}/02.c | 2 +- .../77-lin2vareq}/03.c | 2 +- .../77-lin2vareq}/04.c | 2 +- .../77-lin2vareq}/05.c | 2 +- .../77-lin2vareq}/06.c | 7 +++++- tests/regression/77-lin2vareq/07.c | 21 +++++++++++++++++ tests/relational-domain/00.c | 18 --------------- tests/relational-domain/07.c | 23 ------------------- 10 files changed, 52 insertions(+), 49 deletions(-) create mode 100644 tests/regression/77-lin2vareq/00.c rename tests/{relational-domain => regression/77-lin2vareq}/01.c (88%) rename tests/{relational-domain => regression/77-lin2vareq}/02.c (94%) rename tests/{relational-domain => regression/77-lin2vareq}/03.c (95%) rename tests/{relational-domain => regression/77-lin2vareq}/04.c (94%) rename tests/{relational-domain => regression/77-lin2vareq}/05.c (94%) rename tests/{relational-domain => regression/77-lin2vareq}/06.c (53%) create mode 100644 tests/regression/77-lin2vareq/07.c delete mode 100644 tests/relational-domain/00.c delete mode 100644 tests/relational-domain/07.c diff --git a/tests/regression/77-lin2vareq/00.c b/tests/regression/77-lin2vareq/00.c new file mode 100644 index 0000000000..4ef01866fc --- /dev/null +++ b/tests/regression/77-lin2vareq/00.c @@ -0,0 +1,19 @@ +#include +#include + +int main() { + int x = 0; + int y = 0; + + x = 1; + y = 1; + + __goblint_check(x == y); //SUCCESS + + x = 10; + + __goblint_check(x != y); //SUCCESS + __goblint_check(x == y); //FAIL + + return 0; +} \ No newline at end of file diff --git a/tests/relational-domain/01.c b/tests/regression/77-lin2vareq/01.c similarity index 88% rename from tests/relational-domain/01.c rename to tests/regression/77-lin2vareq/01.c index df5fc9b15a..5715b0ac21 100644 --- a/tests/relational-domain/01.c +++ b/tests/regression/77-lin2vareq/01.c @@ -1,6 +1,5 @@ #include #include -//#include int x=0; int y=0; @@ -26,8 +25,8 @@ int main(){ pthread_join(thread1_id, NULL); pthread_join(thread2_id, NULL); - __goblint_check(x==y); - __goblint_check(x!=y); + __goblint_check(x==y); //UNKNOWN! + __goblint_check(x!=y); //UNKNOWN! return 0; } diff --git a/tests/relational-domain/02.c b/tests/regression/77-lin2vareq/02.c similarity index 94% rename from tests/relational-domain/02.c rename to tests/regression/77-lin2vareq/02.c index e7227a75cd..c8b66498a3 100644 --- a/tests/relational-domain/02.c +++ b/tests/regression/77-lin2vareq/02.c @@ -25,7 +25,7 @@ int main(){ pthread_join(thread1_id, NULL); pthread_join(thread2_id, NULL); - __goblint_check(x!=y); + __goblint_check(x!=y); //RACE! return 0; } diff --git a/tests/relational-domain/03.c b/tests/regression/77-lin2vareq/03.c similarity index 95% rename from tests/relational-domain/03.c rename to tests/regression/77-lin2vareq/03.c index bca76828b1..b182203c1c 100644 --- a/tests/relational-domain/03.c +++ b/tests/regression/77-lin2vareq/03.c @@ -28,7 +28,7 @@ int main(){ pthread_join(threads[i], NULL); } - __goblint_check(x!=y); + __goblint_check(x!=y); //UNKNOWN! return 0; } diff --git a/tests/relational-domain/04.c b/tests/regression/77-lin2vareq/04.c similarity index 94% rename from tests/relational-domain/04.c rename to tests/regression/77-lin2vareq/04.c index 9098a74cf1..da83ea985c 100644 --- a/tests/relational-domain/04.c +++ b/tests/regression/77-lin2vareq/04.c @@ -25,7 +25,7 @@ int main(){ pthread_join(thread1_id, NULL); pthread_join(thread2_id, NULL); - __goblint_check(x!=y); + __goblint_check(x!=y); //UNKNOWN! return 0; } diff --git a/tests/relational-domain/05.c b/tests/regression/77-lin2vareq/05.c similarity index 94% rename from tests/relational-domain/05.c rename to tests/regression/77-lin2vareq/05.c index 03b46b2863..a38c1a6a06 100644 --- a/tests/relational-domain/05.c +++ b/tests/regression/77-lin2vareq/05.c @@ -25,7 +25,7 @@ int main(){ pthread_join(thread1_id, NULL); pthread_join(thread2_id, NULL); - __goblint_check(x!=y); + __goblint_check(x!=y); //UNKNOWN! return 0; } diff --git a/tests/relational-domain/06.c b/tests/regression/77-lin2vareq/06.c similarity index 53% rename from tests/relational-domain/06.c rename to tests/regression/77-lin2vareq/06.c index 2c334a247f..f74556de7b 100644 --- a/tests/relational-domain/06.c +++ b/tests/regression/77-lin2vareq/06.c @@ -6,9 +6,14 @@ int main() { int x = arr[0]; int y = arr[1]; + printf(x); + + __goblint_check(x!=y); //UNKNOWN! + arr[0] = 20; - __goblint_check(x != y); + __goblint_check(x != y); //UNKNOWN! + __goblint_check(x==y); //UNKNOWN! return 0; } \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/07.c b/tests/regression/77-lin2vareq/07.c new file mode 100644 index 0000000000..18f6a34702 --- /dev/null +++ b/tests/regression/77-lin2vareq/07.c @@ -0,0 +1,21 @@ +#include + +int main() { + int a = 2; + int b = 3; + int c = 1; + int d = 0; + + int x = a * a + 2 * c; + __goblint_check(x == 5); //FAIL + + int y = 100 * b + 4 * c + 400 - 100; + __goblint_check(y == 7 * c + 300); //FAIL + int z = a * 2 - c * (1 * (5 - d)) + 3; + __goblint_check(z == 4); //FAIL + + int p = 2 * a + 3 * b + c + d; + __goblint_check(2 * p == 4 * a + 6 * b + 2 * c + 2 * d); //SUCCESS + return 0; +} + diff --git a/tests/relational-domain/00.c b/tests/relational-domain/00.c deleted file mode 100644 index 5ec1114f31..0000000000 --- a/tests/relational-domain/00.c +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include - -int main() { - int x = 0; - int y = 0; - - x = 1; - y = 1; - - __goblint_check(x == y); - - x = 2; - - __goblint_check(x != y); - - return 0; -} \ No newline at end of file diff --git a/tests/relational-domain/07.c b/tests/relational-domain/07.c deleted file mode 100644 index 5ab277c5d7..0000000000 --- a/tests/relational-domain/07.c +++ /dev/null @@ -1,23 +0,0 @@ -#include - -int main() { - int a = 2; - int b = 3; - int c = 1; - int d = 0; - - int x = a * a + 2 * c; - __goblint_check(x == 5); // Expect: x == 5 - - int y = 100 * b + 4 * c + 400 - 100; - __goblint_check(y == 7 * c + 300); // Expect: y == 7 * c + 300 - - int z = a * 2 - c * (1 * (5 - d)) + 3; - __goblint_check(z == 4); // Expect: z == 4 - - int p = 2 * a + 3 * b + c + d; - __goblint_check(2 * p == 4 * a + 6 * b + 2 * c + 2 * d); // Expect: 2 * p == 4 * a + 6 * b + 2 * c + 2 * d - - return 0; -} - From 63b602c8bd3870224f1d923bafd21c98ac4be280 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Wed, 6 Dec 2023 08:55:06 +0100 Subject: [PATCH 025/280] test cases --- tests/regression/77-lin2vareq/08.c | 12 ++++++++++++ tests/regression/77-lin2vareq/09.c | 14 ++++++++++++++ tests/regression/77-lin2vareq/10.c | 16 ++++++++++++++++ tests/regression/77-lin2vareq/11.c | 11 +++++++++++ tests/regression/77-lin2vareq/12.c | 22 ++++++++++++++++++++++ tests/regression/77-lin2vareq/13.c | 21 +++++++++++++++++++++ 6 files changed, 96 insertions(+) create mode 100644 tests/regression/77-lin2vareq/08.c create mode 100644 tests/regression/77-lin2vareq/09.c create mode 100644 tests/regression/77-lin2vareq/10.c create mode 100644 tests/regression/77-lin2vareq/11.c create mode 100644 tests/regression/77-lin2vareq/12.c create mode 100644 tests/regression/77-lin2vareq/13.c diff --git a/tests/regression/77-lin2vareq/08.c b/tests/regression/77-lin2vareq/08.c new file mode 100644 index 0000000000..57c4f86b52 --- /dev/null +++ b/tests/regression/77-lin2vareq/08.c @@ -0,0 +1,12 @@ +#include + +int main() { + int arr1[] = {1, 2, 3}; + int arr2[] = {1, 2, 3}; + int ptr1 = arr1; + int ptr2 = arr2; + + __goblint_check(ptr1 ==ptr2); //SUCCESS + + return 0; +} diff --git a/tests/regression/77-lin2vareq/09.c b/tests/regression/77-lin2vareq/09.c new file mode 100644 index 0000000000..84908323b4 --- /dev/null +++ b/tests/regression/77-lin2vareq/09.c @@ -0,0 +1,14 @@ +#include + +int main() { + int i, j; + int size = 5; + + for (i = 0; i < size; ++i) { + j = i; + + __goblint_check(i == j); //UNKNOWN! + } + + return 0; +} diff --git a/tests/regression/77-lin2vareq/10.c b/tests/regression/77-lin2vareq/10.c new file mode 100644 index 0000000000..a4b283243f --- /dev/null +++ b/tests/regression/77-lin2vareq/10.c @@ -0,0 +1,16 @@ +#include + +int main() { + int vector1[] = {1, 2, 3}; + int vector2[] = {4, 5, 6}; + int result[3]; + + + for (int i = 0; i < 3; ++i) { + result[i] = vector1[i] + vector2[i]; + } + + __goblint_check(result[0] == 5 && result [1] == 7 && result [2] == 9); //SUCCESS + + return 0; +} diff --git a/tests/regression/77-lin2vareq/11.c b/tests/regression/77-lin2vareq/11.c new file mode 100644 index 0000000000..7f1c4d14fa --- /dev/null +++ b/tests/regression/77-lin2vareq/11.c @@ -0,0 +1,11 @@ +#include + +int main() { + int vector1[] = {1, 2, 3}; + int vector2[] = {1, 2, 3}; + + __goblint_check(vector1[0] == vector2[0] && vector1[1] == vector2[1] && vector1[2] == vector2[2]); + + return 0; +} + diff --git a/tests/regression/77-lin2vareq/12.c b/tests/regression/77-lin2vareq/12.c new file mode 100644 index 0000000000..b598e63591 --- /dev/null +++ b/tests/regression/77-lin2vareq/12.c @@ -0,0 +1,22 @@ +#include + +int main(){ + int matrix1[2][3] = {{1, 2, 3}, {4, 5, 6}}; + int matrix2[2][3] = {{1, 2, 3}, {4, 5, 6}}; + int matrix3[2][3] = {{7, 8, 9}, {10, 11, 12}}; + + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 3; ++j) { + __goblint_check(matrix1[i][j] == matrix2[i][j]); //SUCCESS + } + } + + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 3; ++j) { + __goblint_check(matrix1[i][j] == matrix3[i][j]); //FAIL + } + } + + return 0; + +} \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/13.c b/tests/regression/77-lin2vareq/13.c new file mode 100644 index 0000000000..bb0989042c --- /dev/null +++ b/tests/regression/77-lin2vareq/13.c @@ -0,0 +1,21 @@ +#include + +int main() { + int matrix1[2][2] = {{1, 2}, {3, 4}}; + int matrix2[2][2] = {{5, 6}, {7, 8}}; + int result[2][2]; + + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + result[i][j] = 0; + for (int k = 0; k < 2; ++k) { + result[i][j] += matrix1[i][k] * matrix2[k][j]; + } + } + } + + __goblint_check(result[0][0] == 19 && result[0][1] == 22 && + result[1][0] == 43 && result[1][1] == 50); //SUCCESS + + return 0; +} From 2f7ec0364980a91ebd00b8f127c0f8b288c1843f Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 6 Dec 2023 10:57:41 +0100 Subject: [PATCH 026/280] meet_tcons first draft --- .../apron/linearTwoVarEqualityDomain.apron.ml | 67 +++++++++++++++---- 1 file changed, 53 insertions(+), 14 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 4d80a8fbb8..dbc8c4bc60 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -221,7 +221,7 @@ struct | _ -> None let get_coeff_vec (t: t) texp = - (*Parses a Texpr to obtain a (coefficient, variable) pair list to repr. a sum of a variables that have a coefficient. If variable is None, the coefficient represents a constant offset. + (*Parses a Texpr to obtain a (coefficient, variable) pair list to repr. a sum of a variables that have a coefficient. If variable is None, the coefficient represents a constant offset. *) let open Apron.Texpr1 in let exception NotLinearExpr in @@ -380,6 +380,8 @@ struct let bot_env = {d = None; env = Environment.make [||] [||]} + let top_env env = {d = Some (Array.init (Environment.size env) (fun i -> (Some i, Z.zero))); env = env} + let is_bot_env t = t.d = None (*Would the top not be the identity matrix in affineEq? i.e. the array where each variable is assigned itself with no other coeffcients? *) @@ -576,11 +578,13 @@ struct if M.tracing then M.tracel "ops" "assign_var t:\n %s \n v: %s \n v': %s\n -> %s\n" (show t) (Var.to_string v) (Var.to_string v') (show res) ; res (* from here on TODO till end of module*) + (* This functionality is not common to C and is used for assignments of the form: x = y, y=x; which is not legitimate C grammar + x and y should be assigned to the value of x and y before the assignment respectively. + ==> x = y_old , y = x_old; + Therefore first apply the assignments to temporary variables x' and y' to keep the old dependencies of x and y + and in a second round assign x' to x and y' to y + *) let assign_var_parallel t vv's = - List.fold_left (fun t (v1,v2) -> assign_var t v1 v2) t vv's - (* - **This implementation automatically assigns the variables to keep the invariant of the matrix, which is irrelvant for us - let assigned_vars = List.map (function (v, _) -> v) vv's in let t = add_vars t assigned_vars in let primed_vars = List.init (List.length assigned_vars) (fun i -> Var.of_string (Int.to_string i ^"'")) in (* TODO: we use primed vars in analysis, conflict? *) @@ -588,16 +592,11 @@ struct let multi_t = List.fold_left2 (fun t' v_prime (_,v') -> assign_var t' v_prime v') t_primed primed_vars vv's in match multi_t.d with | Some arr when not @@ is_top_env multi_t -> - let replace_entry arr x y = let dim_x, dim_y = Environment.dim_of_var multi_t.env x, Environment.dim_of_var multi_t.env y in - let entry_x = EArray.get arr dim_x in - EArray.set arr entry_x dim_y in - let arr_cp = EArray.copy arr in - let switched_arr = List.fold_left2 (fun arr' x y -> replace_entry arr' x y) arr_cp primed_vars assigned_vars in - let res = drop_vars {d = Some switched_arr; env = multi_t.env} primed_vars true in + let switched_arr = List.fold_left2 (fun multi_t assigned_var primed_var-> assign_var multi_t assigned_var primed_var) multi_t assigned_vars primed_vars in + let res = drop_vars switched_arr primed_vars true in let x = Option.get res.d in {d = Some x; env = res.env} | _ -> t - *) let assign_var_parallel t vv's = let res = assign_var_parallel t vv's in @@ -645,13 +644,53 @@ struct let meet_tcons_one_var_eq res expr = res - (*meet_tcons -> meet with guard in if statement + (* meet_tcons -> meet with guard in if statement texpr -> tree expr (right hand side of equality) -> expression used to derive tcons -> used to check for overflow tcons -> tree constraint (expression < 0) -> does not have types (overflow is type dependent) *) - let meet_tcons t tcons expr = t + + let number_vars cv's = List.count_matching (fun (_, v)-> match v with | None -> false | Some x -> true) cv's + let meet_tcons t tcons expr = + (* The expression is evaluated using an array of coefficients. The first element of the array belongs to the constant followed by the coefficients of all variables + depending on the result in the array after the evaluating including resolving the constraints in t.d the tcons can be evaluated and additional constraints can be added to t.d *) + let expr_init = Array.init ((Environment.size t.env) +1) (fun _ -> Z.zero) in + match t.d with + | None -> t + | Some d -> + let cv's = get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) in + let update (expr : Z.t Array.t)( c , v) = match v with + | None -> Array.set expr 0 (Z.add expr.(0) c) ; expr + | Some idx -> match d.(idx) with + | (Some idx_i,c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; Array.set expr (idx_i + 1) (Z.add expr.(idx_i + 1) c_i) ; expr + | (None, c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; expr + in + let convert_scalar scalar = 0. in (* TODO just dummy implementation. this Scalar type is weired*) + let final_expr = List.fold_left (fun expr cv -> update expr cv ) expr_init cv's in + let is_constant = List.fold_left (fun b a -> if Z.equal a Z.zero then b else false) true @@ List.tl @@ Array.to_list final_expr in + let is_two_var = if List.count_matching (fun a -> if Z.equal a Z.zero then false else true) @@ List.tl @@ Array.to_list final_expr == 2 then true else false + in if is_constant then + match Tcons1.get_typ tcons with + | EQ -> if Z.equal final_expr.(0) Z.zero then t else {d = None; env = t.env} + | SUPEQ -> if Z.geq final_expr.(0) Z.zero then t else {d = None; env = t.env} + | SUP -> if Z.gt final_expr.(0) Z.zero then t else {d = None; env = t.env} + | DISEQ -> if Z.equal final_expr.(0) Z.zero then {d = None; env = t.env} else t + | EQMOD scalar -> if Float.equal ( Float.modulo (Z.to_float final_expr.(0)) (convert_scalar scalar )) 0. then t else {d = None; env = t.env} + else if is_two_var then + let v12 = List.fold_righti (fun i a l -> if Z.equal a Z.zero then l else i::l) (List.tl @@ Array.to_list final_expr) [] in + let v1 = Environment.var_of_dim t.env (List.hd v12) in let v2 = Environment.var_of_dim t.env (List.hd @@ List.tl v12) in + match Tcons1.get_typ tcons with + | EQ -> meet t (assign_var (top_env t.env) v1 v2) + | SUPEQ -> t (*TODO*) + | SUP -> t (*TODO*) + | DISEQ -> t (*TODO*) + | EQMOD scalar -> t (*Not supported right now*) + else + t (*For any other case we don't know if the (in-) equality is true or false or even possible therefore we just return t *) + + + (*TODO let check_const cmp c = if cmp c Mpqf.zero then bot_env else t in From 63b45a52da82f2c579c541c02751ef2ae9ebd6c8 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 6 Dec 2023 13:56:41 +0100 Subject: [PATCH 027/280] adjustments in meet_tcons and incorporated meet but not tested yet --- .../apron/linearTwoVarEqualityDomain.apron.ml | 131 ++++++++++++------ 1 file changed, 92 insertions(+), 39 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index dbc8c4bc60..4c58f53f81 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -393,13 +393,63 @@ struct let meet t1 t2 = let sup_env = Environment.lce t1.env t2.env in - let t1, t2 = change_d t1 sup_env true false, change_d t2 sup_env true false - in if is_bot t1 || is_bot t2 then bot() else + let t1, t2 = change_d t1 sup_env true false, change_d t2 sup_env true false in + let subst_var ts x t = + match !ts with + | None -> () + | Some ts' -> + if Array.length ts' <> 0 then + for i = 0 to Array.length ts' - 1 do + match ts'.(i) with + | (None, _) -> () + | (Some x', b') -> if x = x' then + (match t with + | (None, bt) -> ts'.(i) <- (None, Z.(b' + bt)) + | (Some xt, bt) -> ts'.(i) <- (Some xt, Z.(b' + bt))) + done + in + let add_conj ts t i = + match !ts with + | None -> () + | Some ts' -> + (match t with + | (None, b) -> + (match ts'.(i) with + | (None, b') -> if b <> b' then ts := None; + | (Some j, b') -> subst_var ts j (None, Z.(b - b'))) + | (Some j, b) -> + (match ts'.(i) with + | (None, b1) -> subst_var ts j (None, Z.(b1 - b)) + | (Some h1, b1) -> + (match ts'.(j) with + | (None, b2) -> subst_var ts i (None, Z.(b2 + b)) + | (Some h2, b2) -> + if h1 = h2 then + (if Z.(b1 <> (b2 + b)) then ts := None) + else if h1 < h2 then subst_var ts h2 (Some h1, Z.(b1 - (b + b2))) + else subst_var ts h1 (Some h2, Z.(b + (b2 - b1)))))) + in + match t1.d, t2.d with + | None, _ -> { d = None; env = sup_env} + | _, None -> { d = None; env = sup_env} + | Some d1', Some d2' -> + let ds = ref (Some (Array.copy d1')) in + if Array.length d2' <> 0 then + for j = 0 to Array.length d2' - 1 do + add_conj ds d2'.(j) j + done; + {d = !ds; env = sup_env} + + (* + let sup_env = Environment.lce t1.env t2.env in + let t1, t2 = change_d t1 sup_env true false, change_d t2 sup_env true false in + if is_bot t1 || is_bot t2 then bot() else let m1, m2 = Option.get t1.d, Option.get t2.d in match m1, m2 with | x, y when is_top_env t1-> {d = Some (dim_add (Environment.dimchange t2.env sup_env) y); env = sup_env} | x, y when is_top_env t2 -> {d = Some (dim_add (Environment.dimchange t1.env sup_env) x); env = sup_env} | x, y -> bot() + *) (*let rref_matr = Matrix.rref_matrix_with (Matrix.copy x) (Matrix.copy y) in if Option.is_none rref_matr then bot () else {d = rref_matr; env = sup_env}*) @@ -591,12 +641,12 @@ struct let t_primed = add_vars t primed_vars in let multi_t = List.fold_left2 (fun t' v_prime (_,v') -> assign_var t' v_prime v') t_primed primed_vars vv's in match multi_t.d with - | Some arr when not @@ is_top_env multi_t -> - let switched_arr = List.fold_left2 (fun multi_t assigned_var primed_var-> assign_var multi_t assigned_var primed_var) multi_t assigned_vars primed_vars in - let res = drop_vars switched_arr primed_vars true in - let x = Option.get res.d in - {d = Some x; env = res.env} - | _ -> t + | Some arr when not @@ is_top_env multi_t -> + let switched_arr = List.fold_left2 (fun multi_t assigned_var primed_var-> assign_var multi_t assigned_var primed_var) multi_t assigned_vars primed_vars in + let res = drop_vars switched_arr primed_vars true in + let x = Option.get res.d in + {d = Some x; env = res.env} + | _ -> t let assign_var_parallel t vv's = let res = assign_var_parallel t vv's in @@ -657,37 +707,40 @@ struct depending on the result in the array after the evaluating including resolving the constraints in t.d the tcons can be evaluated and additional constraints can be added to t.d *) let expr_init = Array.init ((Environment.size t.env) +1) (fun _ -> Z.zero) in match t.d with - | None -> t - | Some d -> - let cv's = get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) in - let update (expr : Z.t Array.t)( c , v) = match v with - | None -> Array.set expr 0 (Z.add expr.(0) c) ; expr - | Some idx -> match d.(idx) with - | (Some idx_i,c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; Array.set expr (idx_i + 1) (Z.add expr.(idx_i + 1) c_i) ; expr - | (None, c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; expr - in - let convert_scalar scalar = 0. in (* TODO just dummy implementation. this Scalar type is weired*) - let final_expr = List.fold_left (fun expr cv -> update expr cv ) expr_init cv's in - let is_constant = List.fold_left (fun b a -> if Z.equal a Z.zero then b else false) true @@ List.tl @@ Array.to_list final_expr in - let is_two_var = if List.count_matching (fun a -> if Z.equal a Z.zero then false else true) @@ List.tl @@ Array.to_list final_expr == 2 then true else false - in if is_constant then - match Tcons1.get_typ tcons with - | EQ -> if Z.equal final_expr.(0) Z.zero then t else {d = None; env = t.env} - | SUPEQ -> if Z.geq final_expr.(0) Z.zero then t else {d = None; env = t.env} - | SUP -> if Z.gt final_expr.(0) Z.zero then t else {d = None; env = t.env} - | DISEQ -> if Z.equal final_expr.(0) Z.zero then {d = None; env = t.env} else t - | EQMOD scalar -> if Float.equal ( Float.modulo (Z.to_float final_expr.(0)) (convert_scalar scalar )) 0. then t else {d = None; env = t.env} - else if is_two_var then - let v12 = List.fold_righti (fun i a l -> if Z.equal a Z.zero then l else i::l) (List.tl @@ Array.to_list final_expr) [] in - let v1 = Environment.var_of_dim t.env (List.hd v12) in let v2 = Environment.var_of_dim t.env (List.hd @@ List.tl v12) in - match Tcons1.get_typ tcons with - | EQ -> meet t (assign_var (top_env t.env) v1 v2) - | SUPEQ -> t (*TODO*) - | SUP -> t (*TODO*) - | DISEQ -> t (*TODO*) - | EQMOD scalar -> t (*Not supported right now*) - else - t (*For any other case we don't know if the (in-) equality is true or false or even possible therefore we just return t *) + | None -> t + | Some d -> + let cv's = get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) in + let update (expr : Z.t Array.t)( c , v) = + match v with + | None -> Array.set expr 0 (Z.add expr.(0) c) ; expr + | Some idx -> match d.(idx) with + | (Some idx_i,c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; Array.set expr (idx_i + 1) (Z.add expr.(idx_i + 1) c_i) ; expr + | (None, c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; expr + in + let final_expr = List.fold_left (fun expr cv -> update expr cv ) expr_init cv's in + let is_constant = List.fold_left (fun b a -> if Z.equal a Z.zero then b else false) true @@ List.tl @@ Array.to_list final_expr in + let var_count = List.count_matching (fun a -> if Z.equal a Z.zero then false else true) @@ List.tl @@ Array.to_list final_expr + in if is_constant then + match Tcons1.get_typ tcons with + | EQ -> if Z.equal final_expr.(0) Z.zero then t else {d = None; env = t.env} + | SUPEQ -> if Z.geq final_expr.(0) Z.zero then t else {d = None; env = t.env} + | SUP -> if Z.gt final_expr.(0) Z.zero then t else {d = None; env = t.env} + | DISEQ -> if Z.equal final_expr.(0) Z.zero then {d = None; env = t.env} else t + | EQMOD scalar -> t (*if Float.equal ( Float.modulo (Z.to_float final_expr.(0)) (convert_scalar scalar )) 0. then t else {d = None; env = t.env}*) + else if var_count == 2 then + let v12 = List.fold_righti (fun i a l -> if Z.equal a Z.zero then l else (i,a)::l) (List.tl @@ Array.to_list final_expr) [] in + let a1 = Tuple2.second (List.hd v12) in + let a2 = Tuple2.second (List.hd v12) in + let var1 = Environment.var_of_dim t.env (Tuple2.first (List.hd v12)) in + let var2 = Environment.var_of_dim t.env (Tuple2.first (List.hd @@ List.tl v12)) in + match Tcons1.get_typ tcons with + | EQ -> if Z.equal a1 Z.one && Z.equal a2 Z.one then meet t (assign_var (top_env t.env) var1 var2) else t + | SUPEQ -> t (*TODO*) + | SUP -> t (*TODO*) + | DISEQ -> t (*TODO*) + | EQMOD scalar -> t (*Not supported right now*) + else + t (*For any other case we don't know if the (in-) equality is true or false or even possible therefore we just return t *) From 953599f2531206e931148dbd94925576266b33bb Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 6 Dec 2023 14:12:24 +0100 Subject: [PATCH 028/280] moved number_vars to get_coeff --- .../apron/linearTwoVarEqualityDomain.apron.ml | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 4c58f53f81..b1c79765a1 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -25,8 +25,8 @@ things to implement: - leq - join - - assignment - - meet_tcons + done: assignment + done: meet_tcons HOW TO RUN THE REGRESSION TESTS: Method 1: regression test ./regtest.sh numberofdirectory numberoftest @@ -50,9 +50,6 @@ 12. January or earlier pull request -> all features implemented -> run on svcomp benchmarks -> to check runtime and unsoundness and crashes - Maybe TODO: - Abstract Vector in order to have less code duplication (e.g. VectorBase und Vector) - DEBUG: 1. print stack trace while executing ./goblint: -v option for goblint -> prints stack trace @@ -74,7 +71,6 @@ open Apron open VectorMatrix open Printf -(** TODO: modify code *) module Mpqf = SharedFunctions.Mpqf @@ -157,7 +153,7 @@ module EqualitiesArray = struct The name reduce_col_with is because the affineEqualitiesDomain also defines this function, and it represents the equalities with a matrix, not like in this case with an array. We could think about changing this name, then we would need to change it also in - shared_Functions.apron.ml and ectorMatrix.ml and affineEqualitiesDomain.ml *) + shared_Functions.apron.ml and vectorMatrix.ml and affineEqualitiesDomain.ml *) let reduce_col_with d var = let ref_var_opt = find_reference_variable d var in d.(var) <- Equality.var_zero var; @@ -178,7 +174,7 @@ module EqualitiesArray = struct iteri (fun _ x -> let (_, off2) = d.(x) in d.(x) <- (Some var_least_index, Z.(off2 - off))) connected_component; end - (* Forget information about variable i but notin-place *) + (* Forget information about variable i but not in-place *) let reduce_col m j = let copy = copy m in reduce_col_with copy j; @@ -271,12 +267,11 @@ struct end in convert_texpr texp - let number_vars x = 0(*TODO*) - let get_coeff (t: t) texp = (*Parses a Texpr to obtain a (variable, offset) pair to repr. a sum of a variable and an offset. Returns None if the expression is not a sum between a variable (without coefficient) and a constant. *) + let number_vars cv's = List.count_matching (fun (_, v)-> match v with | None -> false | Some x -> true) cv's in let sum_coefficients summands_list = List.fold_left (fun (var, current_var_offset, curr_offset) (next_coeff, next_var) -> begin match next_var with @@ -629,10 +624,10 @@ struct res (* from here on TODO till end of module*) (* This functionality is not common to C and is used for assignments of the form: x = y, y=x; which is not legitimate C grammar - x and y should be assigned to the value of x and y before the assignment respectively. - ==> x = y_old , y = x_old; - Therefore first apply the assignments to temporary variables x' and y' to keep the old dependencies of x and y - and in a second round assign x' to x and y' to y + x and y should be assigned to the value of x and y before the assignment respectively. + ==> x = y_old , y = x_old; + Therefore first apply the assignments to temporary variables x' and y' to keep the old dependencies of x and y + and in a second round assign x' to x and y' to y *) let assign_var_parallel t vv's = let assigned_vars = List.map (function (v, _) -> v) vv's in @@ -695,13 +690,13 @@ struct let meet_tcons_one_var_eq res expr = res (* meet_tcons -> meet with guard in if statement - texpr -> tree expr (right hand side of equality) + texpr -> tree expr (right hand side of equality) -> expression used to derive tcons -> used to check for overflow - tcons -> tree constraint (expression < 0) + tcons -> tree constraint (expression < 0) -> does not have types (overflow is type dependent) *) - let number_vars cv's = List.count_matching (fun (_, v)-> match v with | None -> false | Some x -> true) cv's + let meet_tcons t tcons expr = (* The expression is evaluated using an array of coefficients. The first element of the array belongs to the constant followed by the coefficients of all variables depending on the result in the array after the evaluating including resolving the constraints in t.d the tcons can be evaluated and additional constraints can be added to t.d *) From 14670a7c8d7095c0767f31f073a92cff4aadfe2e Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 6 Dec 2023 15:01:10 +0100 Subject: [PATCH 029/280] meet_tcons finished --- .../apron/linearTwoVarEqualityDomain.apron.ml | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index b1c79765a1..b5dfd7f485 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -717,11 +717,23 @@ struct let var_count = List.count_matching (fun a -> if Z.equal a Z.zero then false else true) @@ List.tl @@ Array.to_list final_expr in if is_constant then match Tcons1.get_typ tcons with - | EQ -> if Z.equal final_expr.(0) Z.zero then t else {d = None; env = t.env} - | SUPEQ -> if Z.geq final_expr.(0) Z.zero then t else {d = None; env = t.env} - | SUP -> if Z.gt final_expr.(0) Z.zero then t else {d = None; env = t.env} - | DISEQ -> if Z.equal final_expr.(0) Z.zero then {d = None; env = t.env} else t - | EQMOD scalar -> t (*if Float.equal ( Float.modulo (Z.to_float final_expr.(0)) (convert_scalar scalar )) 0. then t else {d = None; env = t.env}*) + | EQ -> if Z.equal final_expr.(0) Z.zero then t else bot() + | SUPEQ -> if Z.geq final_expr.(0) Z.zero then t else bot() + | SUP -> if Z.gt final_expr.(0) Z.zero then t else bot() + | DISEQ -> if Z.equal final_expr.(0) Z.zero then bot() else t + | EQMOD scalar -> t (*Not supported right now + if Float.equal ( Float.modulo (Z.to_float final_expr.(0)) (convert_scalar scalar )) 0. then t else {d = None; env = t.env}*) + else if var_count == 1 then + let var = List.findi (fun i a -> if Z.equal a Z.zero then false else true) @@ Array.to_list final_expr in + let c = if Z.divisible final_expr.(0) @@ Tuple2.second var then Some (Z.(- final_expr.(0) / (Tuple2.second var))) else None in + match Tcons1.get_typ tcons with + | EQ -> if Option.is_none c then t else + let expr = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int (Option.get c)) in + meet t (assign_texpr (top_env t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expr) + | SUPEQ -> t (*We don't know*) + | SUP -> t (*We don't know*) + | DISEQ -> t (*We don't know*) + | EQMOD scalar -> t (*Not supported right now*) else if var_count == 2 then let v12 = List.fold_righti (fun i a l -> if Z.equal a Z.zero then l else (i,a)::l) (List.tl @@ Array.to_list final_expr) [] in let a1 = Tuple2.second (List.hd v12) in @@ -730,9 +742,9 @@ struct let var2 = Environment.var_of_dim t.env (Tuple2.first (List.hd @@ List.tl v12)) in match Tcons1.get_typ tcons with | EQ -> if Z.equal a1 Z.one && Z.equal a2 Z.one then meet t (assign_var (top_env t.env) var1 var2) else t - | SUPEQ -> t (*TODO*) - | SUP -> t (*TODO*) - | DISEQ -> t (*TODO*) + | SUPEQ -> t (*We don't know*) + | SUP -> t (*We don't know*) + | DISEQ -> t (*We don't know*) | EQMOD scalar -> t (*Not supported right now*) else t (*For any other case we don't know if the (in-) equality is true or false or even possible therefore we just return t *) From ab43a2bc67d2a19e751b6e093370ecd357c67a9c Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 6 Dec 2023 15:02:20 +0100 Subject: [PATCH 030/280] meet_tcons small refinement --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index b5dfd7f485..f4de073433 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -730,10 +730,7 @@ struct | EQ -> if Option.is_none c then t else let expr = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int (Option.get c)) in meet t (assign_texpr (top_env t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expr) - | SUPEQ -> t (*We don't know*) - | SUP -> t (*We don't know*) - | DISEQ -> t (*We don't know*) - | EQMOD scalar -> t (*Not supported right now*) + | _ -> t (*Not supported right now*) else if var_count == 2 then let v12 = List.fold_righti (fun i a l -> if Z.equal a Z.zero then l else (i,a)::l) (List.tl @@ Array.to_list final_expr) [] in let a1 = Tuple2.second (List.hd v12) in @@ -742,10 +739,7 @@ struct let var2 = Environment.var_of_dim t.env (Tuple2.first (List.hd @@ List.tl v12)) in match Tcons1.get_typ tcons with | EQ -> if Z.equal a1 Z.one && Z.equal a2 Z.one then meet t (assign_var (top_env t.env) var1 var2) else t - | SUPEQ -> t (*We don't know*) - | SUP -> t (*We don't know*) - | DISEQ -> t (*We don't know*) - | EQMOD scalar -> t (*Not supported right now*) + | _-> t (*Not supported right now*) else t (*For any other case we don't know if the (in-) equality is true or false or even possible therefore we just return t *) From 6752716ed527c6a332bb65df5d81c39459bb0511 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 6 Dec 2023 16:23:07 +0100 Subject: [PATCH 031/280] First quick test --- .../apron/linearTwoVarEqualityAnalysis.apron.ml | 15 ++++++++++++++- .../apron/linearTwoVarEqualityDomain.apron.ml | 4 ++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml index 26b016e042..b9fce95aa9 100644 --- a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml +++ b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml @@ -3,6 +3,8 @@ @see A. Flexeder, M. Petter, and H. Seidl Fast Interprocedural Linear Two-Variable Equalities. *) open Analyses +open Apron +open LinearTwoVarEqualityDomain include RelationAnalysis @@ -31,9 +33,20 @@ let get_spec (): (module MCPSpec) = Lazy.force spec_module let after_config () = + let x1 = Apron.Var.of_string "x1" in + let x2 = Apron.Var.of_string "x2" in + let x3 = Apron.Var.of_string "x3" in + let x4 = Apron.Var.of_string "x4" in + let x2' = Apron.Var.of_string "x2'" in + let x1' = Apron.Var.of_string "x1'" in + let env_test = Apron.Environment.make (Array.of_list [x1; x2; x3; x4]) @@ Array.of_list [] in + let varM_test = D.top_env env_test in + print_string @@ D.show varM_test ; + print_string @@ D.show (D.assign_texpr varM_test x1 (Texpr1.Cst (Coeff.s_of_int 5))); + failwith "Test completed"; let module Spec = (val get_spec ()) in MCP.register_analysis (module Spec : MCPSpec); GobConfig.set_string "ana.path_sens[+]" (Spec.name ()) -let _ = +let _ = AfterConfig.register after_config diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index f4de073433..16a1c45ba8 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -356,8 +356,8 @@ struct let lookup i = Var.to_string (Environment.var_of_dim varM.env i) in let show_var i tuple = match tuple with - | (None, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ Z.to_string offset - | (Some index, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ lookup index ^ " + " ^ Z.to_string offset + | (None, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ Z.to_string offset ^ "\n" + | (Some index, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ lookup index ^ " + " ^ Z.to_string offset ^ "\n" in match varM.d with | None -> "No equalities available" | Some arr -> Array.fold_left (fun acc elem -> acc ^ elem ) "" (Array.mapi show_var arr) From f69a922d90ab7d57979ffab01ab24370e4c4786e Mon Sep 17 00:00:00 2001 From: Nicolai Frech Date: Wed, 6 Dec 2023 16:28:41 +0100 Subject: [PATCH 032/280] added preliminary implementation for leq --- .../apron/linearTwoVarEqualityDomain.apron.ml | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 16a1c45ba8..33e99f5daa 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -457,12 +457,33 @@ struct let meet t1 t2 = timing_wrap "meet" (meet t1) t2 - (* TODO: implement less equal *) + (* TODO: check implementation for less equal *) let leq t1 t2 = let env_comp = Environment.compare t1.env t2.env in (* Apron's Environment.compare has defined return values. *) + let implies ts t i : bool = + match t with + | (None, b) -> + (match ts.(i) with + | (None, b') -> Z.equal b b' + | _ -> false) + | (Some j, b) -> + (match ts.(i), ts.(j) with + | (None, _), (_, _) -> false + | (_, _), (None, _) -> false + | (Some h1, b1), (Some h2, b2) -> + h1 = h2 && Z.equal b1 (Z.add b2 b)) + in if env_comp = -2 || env_comp > 0 then false else if is_bot t1 || is_top_env t2 then true else - if is_bot t2 || is_top_env t1 then false else (true) + if is_bot t2 || is_top_env t1 then false else ( + let m1, m2 = Option.get t1.d, Option.get t2.d in + let m1' = if env_comp = 0 then m1 else dim_add (Environment.dimchange t1.env t2.env) m1 in + let result : bool ref = ref true in + for i = 0 to Array.length m2 - 1 do + let t = m2.(i) in + if not (implies m1' t i) then result := false; + done; + !result) let leq a b = timing_wrap "leq" (leq a) b From a3755094fe5508b99075eca6732db51231a12937 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 6 Dec 2023 16:44:18 +0100 Subject: [PATCH 033/280] fuer rebecca --- src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml index b9fce95aa9..ce4f7ba3fa 100644 --- a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml +++ b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml @@ -41,8 +41,16 @@ let after_config () = let x1' = Apron.Var.of_string "x1'" in let env_test = Apron.Environment.make (Array.of_list [x1; x2; x3; x4]) @@ Array.of_list [] in let varM_test = D.top_env env_test in + let test = D.assign_texpr varM_test x1 (Texpr1.Cst (Coeff.s_of_int 5)) in + let test = D.assign_var test x3 x2 in + let test = D.assign_var test x4 x2 in + let test = D.assign_texpr test x2 (Texpr1.Cst (Coeff.s_of_int 5)) in + print_string "Original variable setup:\n"; print_string @@ D.show varM_test ; + print_string "After x1 = 5:\n"; print_string @@ D.show (D.assign_texpr varM_test x1 (Texpr1.Cst (Coeff.s_of_int 5))); + print_string "After even more assignments:\n"; + print_string @@ D.show test; failwith "Test completed"; let module Spec = (val get_spec ()) in MCP.register_analysis (module Spec : MCPSpec); From d672d451b45b9d4fa91768a439063dc383ad3c88 Mon Sep 17 00:00:00 2001 From: Nicolai Frech Date: Wed, 6 Dec 2023 17:08:41 +0100 Subject: [PATCH 034/280] added preliminary version of join --- .../apron/linearTwoVarEqualityDomain.apron.ml | 185 +++++++++++++----- 1 file changed, 134 insertions(+), 51 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 33e99f5daa..c4ade6592b 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -492,62 +492,145 @@ struct if M.tracing then M.tracel "leq" "leq a: %s b: %s -> %b \n" (show t1) (show t2) res ; res - let join a b = a - (*let rec lin_disjunc r s a b = - if s >= Vector.length a then a else - let case_two a r col_b = - let a_r = Matrix.get_row a r in - Matrix.map2i_with (fun i x y -> if i < r then - Vector.map2_with (fun u j -> u +: y *: j) x a_r; x) a col_b; - Matrix.remove_row a r + let join a b = + let ts_zip t1 t2 = + if Array.length t1 <> Array.length t2 then None else + let zts = Array.init (Array.length t1) (fun (i : int) -> (i, t1.(i), t2.(i))) in + Some zts + in + let const_offset t = match t with + | (_, b) -> b + in + let diff t1 t2 = Z.((const_offset t1) - (const_offset t2)) + in + let cmp_z x y = + let cmp_z_ref x y: int = + match x, y with + | (None, _), (None, _) -> 0 + | (None, _), (Some _, _) -> -1 + | (Some _, _), (None, _) -> 1 + | (Some ii, _), (Some ij, _) -> ii - ij + in + match x, y with + | (_, t1i, t2i), (_, t1j, t2j) -> + let diff_e1 = cmp_z_ref t1i t1j in + if diff_e1 <> 0 then diff_e1 else + let diff_e2 = cmp_z_ref t2i t2j in + if diff_e2 <> 0 then diff_e2 else + Z.to_int (Z.((diff t1i t2i) - (diff t1j t2j))) + in + let sort_z_by_expr zts = + match zts with + | None -> () + | Some zts' -> Array.stable_sort cmp_z zts' + in + let sort_annotated ats = + let cmp_annotated x y : int = + match x, y with + | (i, _), (j, _) -> i - j in - let case_three a b col_a col_b max = - let col_a, col_b = Vector.copy col_a, Vector.copy col_b in - let col_a, col_b = Vector.keep_vals col_a max, Vector.keep_vals col_b max in - if Vector.equal col_a col_b then (a, b, max) else - let a_rev, b_rev = (Vector.rev_with col_a; col_a), (Vector.rev_with col_b; col_b) in - let i = Vector.find2i (fun x y -> x <>: y) a_rev b_rev in - let (x, y) = Vector.nth a_rev i, Vector.nth b_rev i in - let r, diff = Vector.length a_rev - (i + 1), x -: y in - let a_r, b_r = Matrix.get_row a r, Matrix.get_row b r in - let sub_col = - Vector.map2_with (fun x y -> x -: y) a_rev b_rev; - Vector.rev_with a_rev; - a_rev - in - let multiply_by_t m t = - Matrix.map2i_with (fun i' x c -> if i' <= max then (let beta = c /: diff in - Vector.map2_with (fun u j -> u -: (beta *: j)) x t); x) m sub_col; - m - in - Matrix.remove_row (multiply_by_t a a_r) r, Matrix.remove_row (multiply_by_t b b_r) r, (max - 1) + match ats with + | None -> () + | Some ats' -> Array.stable_sort cmp_annotated ats' + in + let process_eq_classes zts = + let is_const x = + match x with + | (_, (None, _), (None, _)) -> true + | _ -> false in - let col_a, col_b = Matrix.get_col a s, Matrix.get_col b s in - let nth_zero v i = match Vector.nth v i with - | exception Invalid_argument _ -> Mpqf.zero - | x -> x + let size_of_eq_class zts (start : int) : int = + let ref_elem = zts.(start) in + let remaining = (Array.length zts) - start - 1 in + let result = ref 0 in + for i = 0 to remaining do + let current_elem = zts.(start + i) in + if cmp_z ref_elem current_elem = 0 then result := !result + 1 + done; + !result + in + let least_index_var_in_eq_class zts start size : int * Z.t = + let result = ref (0, Z.of_int 0) in + match zts.(start) with + | (i, (_, b), (_, _)) -> result := (i, b); + for i = start + 1 to start + size - 1 do + match zts.(i) with + | (j, (_, b), (_, _)) -> + if j < fst !result then result := (j, b) + done; + !result in - let a_rs, b_rs = nth_zero col_a r, nth_zero col_b r in - if not (Z.equal (Mpqf.get_den a_rs) Z.one) || not (Z.equal (Mpqf.get_den b_rs) Z.one) then failwith "Matrix not in rref form" else - begin match Int.of_float @@ Mpqf.to_float @@ a_rs, Int.of_float @@ Mpqf.to_float @@ b_rs with (* TODO: is it safe to go through floats? *) - | 1, 1 -> lin_disjunc (r + 1) (s + 1) a b - | 1, 0 -> lin_disjunc r (s + 1) (case_two a r col_b) b - | 0, 1 -> lin_disjunc r (s + 1) a (case_two b r col_a) - | 0, 0 -> let new_a, new_b, new_r = case_three a b col_a col_b r in - lin_disjunc new_r (s + 1) new_a new_b - | _ -> failwith "Matrix not in rref form" end + let all_are_const_in_eq_class zts start size : bool = + let result = ref true in + for i = start to start + size - 1 do + if not (is_const zts.(i)) then result := false; + done; + !result + in + let assign_vars_in_const_eq_class ats zts start size least_i least_b = + for i = start to start + size - 1 do + match zts.(i) with + | (ai, t1, t2) -> if Z.equal (diff t1 t2) (Z.of_int 0) then ats.(i) <- (ai, t1) + else + match t1 with + | (_, bj) -> ats.(i) <- (ai, (Some least_i, Z.sub bj least_b)) + done + in + let assign_vars_in_non_const_eq_class ats zts start size least_i least_b = + for i = start to start + size - 1 do + match zts.(i) with + | (ai, t1, _) -> + let bj = const_offset t1 in + ats.(i) <- (ai, (Some least_i, Z.sub bj least_b)) + done + in + match zts with + | None -> None + | Some zts' -> + let result = Array.make (Array.length zts') (0, (None, Z.of_int 0)) in + let i = ref 0 in + while !i < Array.length zts' do + let n = size_of_eq_class zts' !i in + (if n = 1 then + let ztsi = zts'.(!i) in + match ztsi with + | (i', t1, t2) -> if is_const ztsi && Z.equal (diff t1 t2) (Z.of_int 0) then + result.(!i) <- (i', (None, const_offset t1)) + else result.(!i) <- (i', (Some i', Z.of_int 0)) + else + let (least_i, least_b) = least_index_var_in_eq_class zts' !i n in + (if all_are_const_in_eq_class zts' !i n then + assign_vars_in_const_eq_class result zts' !i n least_i least_b + else assign_vars_in_non_const_eq_class result zts' !i n least_i least_b); + ); + i := !i + n; + done; + Some result + in + let strip_annotation ats = + match ats with + | None -> None + | Some ats' -> Some (Array.map snd ats') + in + let join_d t1 t2 = + let zipped = ts_zip t1' t2' in + sort_z_by_expr zipped; + let annotated = process_eq_classes zipped in + sort_annotated annotated; + let result = strip_annotation annotated in + result in if is_bot a then b else if is_bot b then a else - match Option.get a.d, Option.get b.d with - | x, y when is_top_env a || is_top_env b -> {d = Some (Matrix.empty ()); env = Environment.lce a.env b.env} - | x, y when (Environment.compare a.env b.env <> 0) -> - let sup_env = Environment.lce a.env b.env in - let mod_x = dim_add (Environment.dimchange a.env sup_env) x in - let mod_y = dim_add (Environment.dimchange b.env sup_env) y in - {d = Some (lin_disjunc 0 0 (Matrix.copy mod_x) (Matrix.copy mod_y)); env = sup_env} - | x, y when Matrix.equal x y -> {d = Some x; env = a.env} - | x, y -> {d = Some(lin_disjunc 0 0 (Matrix.copy x) (Matrix.copy y)); env = a.env} - *) + match Option.get a.d, Option.get b.d with + | x, y when is_top_env a || is_top_env b -> {d = Some (EArray.empty ()); env = Environment.lce a.env b.env} + | x, y when (Environment.compare a.env b.env <> 0) -> + let sup_env = Environment.lce a.env b.env in + let mod_x = dim_add (Environment.dimchange a.env sup_env) x in + let mod_y = dim_add (Environment.dimchange b.env sup_env) y in + {d = Some (join_d mod_x mod_y); env = sup_env} + | x, y when EArray.equal x y -> {d = Some x; env = a.env} + | x, y -> {d = Some(join_d x y); env = a.env} + let join a b = timing_wrap "join" (join a) b let join a b = From eed4dfe41300e3aa541a1bcc4308b78facc0ea3f Mon Sep 17 00:00:00 2001 From: Nicolai Frech Date: Wed, 6 Dec 2023 17:11:51 +0100 Subject: [PATCH 035/280] changed returned value in join --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index c4ade6592b..3e4012735d 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -627,9 +627,9 @@ struct let sup_env = Environment.lce a.env b.env in let mod_x = dim_add (Environment.dimchange a.env sup_env) x in let mod_y = dim_add (Environment.dimchange b.env sup_env) y in - {d = Some (join_d mod_x mod_y); env = sup_env} + {d = join_d mod_x mod_y; env = sup_env} | x, y when EArray.equal x y -> {d = Some x; env = a.env} - | x, y -> {d = Some(join_d x y); env = a.env} + | x, y -> {d = join_d x y; env = a.env} let join a b = timing_wrap "join" (join a) b From 20cd10e331a706e54c737882a2f4c438c9f18874 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 6 Dec 2023 17:17:57 +0100 Subject: [PATCH 036/280] Fixed bug in abstract_exists --- .../apron/linearTwoVarEqualityDomain.apron.ml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 33e99f5daa..f744f32a1d 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -141,13 +141,11 @@ module EqualitiesArray = struct let find_vars_in_the_connected_component d ref_var = filter (fun i -> let (var, _) = d.(i) in var = ref_var) (mapi const d) - let find_var_in_the_connected_component_with_least_index d ref_var = - fold_left (fun curr_min (var, _) -> if var = ref_var then match curr_min with - | None -> var - | Some curr_min -> - match var with - |Some i -> if i < curr_min then Some i else Some curr_min - | None -> Some curr_min else curr_min) None d +(* find a variable in the connected component with the least index, but not the reference variable. *) + let find_var_in_the_connected_component_with_least_index connected_component ref_var = + fold_left (fun curr_min i -> match curr_min with + | None -> if i <> ref_var then Some i else None + | Some curr_min -> if i < curr_min && i <> ref_var then Some i else Some curr_min) None connected_component (* Forget information about variable var in-place. The name reduce_col_with is because the affineEqualitiesDomain also defines this function, @@ -169,7 +167,7 @@ module EqualitiesArray = struct then () (* x_i is the only element of its connected component *) else (* x_i is the reference variable -> we need to find a new reference variable *) - let var_least_index = Option.get @@ find_var_in_the_connected_component_with_least_index d dim_of_var in + let var_least_index = Option.get @@ find_var_in_the_connected_component_with_least_index connected_component ref_var in let (_, off) = d.(var_least_index) in iteri (fun _ x -> let (_, off2) = d.(x) in d.(x) <- (Some var_least_index, Z.(off2 - off))) connected_component; end From 384223eb4c6196447b278bc0a16898587cbd0a18 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 6 Dec 2023 18:07:50 +0100 Subject: [PATCH 037/280] testing in analysis file, test2 shows error in meet, but likely caused by change_d --- .../linearTwoVarEqualityAnalysis.apron.ml | 49 ++++++++++++++++--- .../apron/linearTwoVarEqualityDomain.apron.ml | 49 ++++++++++--------- 2 files changed, 70 insertions(+), 28 deletions(-) diff --git a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml index ce4f7ba3fa..811974db14 100644 --- a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml +++ b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml @@ -32,7 +32,7 @@ let spec_module: (module MCPSpec) Lazy.t = let get_spec (): (module MCPSpec) = Lazy.force spec_module -let after_config () = +let test1 () = let x1 = Apron.Var.of_string "x1" in let x2 = Apron.Var.of_string "x2" in let x3 = Apron.Var.of_string "x3" in @@ -41,17 +41,54 @@ let after_config () = let x1' = Apron.Var.of_string "x1'" in let env_test = Apron.Environment.make (Array.of_list [x1; x2; x3; x4]) @@ Array.of_list [] in let varM_test = D.top_env env_test in - let test = D.assign_texpr varM_test x1 (Texpr1.Cst (Coeff.s_of_int 5)) in - let test = D.assign_var test x3 x2 in - let test = D.assign_var test x4 x2 in - let test = D.assign_texpr test x2 (Texpr1.Cst (Coeff.s_of_int 5)) in + let test = D.assign_texpr varM_test x1 (Texpr1.Cst (Coeff.s_of_int 5)) in (*x1 = 5*) + let test = D.assign_var test x3 x2 in (*x3 = x2*) + let test = D.assign_var test x4 x2 in (*x4 = x2*) + let test = D.assign_texpr test x2 (Texpr1.Cst (Coeff.s_of_int 3)) in (*x1 = 5*) + (*let test = D.assign_texpr test x2 (Texpr1.Cst (Coeff.s_of_int 5)) in (*x2 = 5*)*) + print_string "Test1:\n"; print_string "Original variable setup:\n"; print_string @@ D.show varM_test ; print_string "After x1 = 5:\n"; print_string @@ D.show (D.assign_texpr varM_test x1 (Texpr1.Cst (Coeff.s_of_int 5))); print_string "After even more assignments:\n"; print_string @@ D.show test; - failwith "Test completed"; + print_string "Test1 completed\n" + +let test2 () = + let x1 = Apron.Var.of_string "x1" in + let x2 = Apron.Var.of_string "x2" in + let x3 = Apron.Var.of_string "x3" in + let x4 = Apron.Var.of_string "x4" in + let x2' = Apron.Var.of_string "x2'" in + let x1' = Apron.Var.of_string "x1'" in + let env_test = Apron.Environment.make (Array.of_list [x1; x2; x3; x4]) @@ Array.of_list [] in + let env_test' = Apron.Environment.make (Array.of_list [x1'; x2';x2 ]) @@ Array.of_list [] in + let varM_test = D.top_env env_test in + let varM_test' = D.top_env env_test' in + let test = D.assign_texpr varM_test x1 (Texpr1.Cst (Coeff.s_of_int 5)) in (*x1 = 5*) + let test = D.assign_var test x3 x2 in (*x3 = x2*) + let test = D.assign_var test x4 x2 in (*x4 = x2*) + let test' = D.assign_texpr varM_test' x1' (Texpr1.Cst (Coeff.s_of_int 3)) in (*x1' = 5*) + let test' = D.assign_var test' x2 x1' in (*x2' = x1'*) + (*let test = D.assign_texpr test x2 (Texpr1.Cst (Coeff.s_of_int 5)) in (*x2 = 5*)*) + print_string "Test2:\n"; + print_string "Original variable setup:\n"; + print_string @@ D.show varM_test ; + print_string "After x1 = 5:\n"; + print_string @@ D.show (D.assign_texpr varM_test x1 (Texpr1.Cst (Coeff.s_of_int 5))); + print_string "After even more assignments:\n"; + print_string @@ D.show test; + print_string "Other environments:\n"; + print_string @@ D.show test'; + print_string "Meet environments:\n"; + print_string @@ D.show (D.meet test test'); + print_string "Test2 completed\n" + +let after_config () =(* + test2(); + failwith "No error Test completed"; + *) let module Spec = (val get_spec ()) in MCP.register_analysis (module Spec : MCPSpec); GobConfig.set_string "ana.path_sens[+]" (Spec.name ()) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 0c8bf292ed..e1482dab4d 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -141,7 +141,7 @@ module EqualitiesArray = struct let find_vars_in_the_connected_component d ref_var = filter (fun i -> let (var, _) = d.(i) in var = ref_var) (mapi const d) -(* find a variable in the connected component with the least index, but not the reference variable. *) + (* find a variable in the connected component with the least index, but not the reference variable. *) let find_var_in_the_connected_component_with_least_index connected_component ref_var = fold_left (fun curr_min i -> match curr_min with | None -> if i <> ref_var then Some i else None @@ -387,6 +387,11 @@ struct let meet t1 t2 = let sup_env = Environment.lce t1.env t2.env in let t1, t2 = change_d t1 sup_env true false, change_d t2 sup_env true false in + print_string "t1 after change_d\n"; + print_string @@ show t1; + print_string "t2 after change_d\n"; + print_string @@ show t2; + print_string "end\n"; let subst_var ts x t = match !ts with | None -> () @@ -458,7 +463,7 @@ struct (* TODO: check implementation for less equal *) let leq t1 t2 = let env_comp = Environment.compare t1.env t2.env in (* Apron's Environment.compare has defined return values. *) - let implies ts t i : bool = +let implies ts t i : bool = match t with | (None, b) -> (match ts.(i) with @@ -492,7 +497,7 @@ struct let join a b = let ts_zip t1 t2 = - if Array.length t1 <> Array.length t2 then None else + if Array.length t1 <> Array.length t2 then None else let zts = Array.init (Array.length t1) (fun (i : int) -> (i, t1.(i), t2.(i))) in Some zts in @@ -501,7 +506,7 @@ struct in let diff t1 t2 = Z.((const_offset t1) - (const_offset t2)) in - let cmp_z x y = + let cmp_z x y = let cmp_z_ref x y: int = match x, y with | (None, _), (None, _) -> 0 @@ -513,7 +518,7 @@ struct | (_, t1i, t2i), (_, t1j, t2j) -> let diff_e1 = cmp_z_ref t1i t1j in if diff_e1 <> 0 then diff_e1 else - let diff_e2 = cmp_z_ref t2i t2j in + let diff_e2 = cmp_z_ref t2i t2j in if diff_e2 <> 0 then diff_e2 else Z.to_int (Z.((diff t1i t2i) - (diff t1j t2j))) in @@ -522,7 +527,7 @@ struct | None -> () | Some zts' -> Array.stable_sort cmp_z zts' in - let sort_annotated ats = + let sort_annotated ats = let cmp_annotated x y : int = match x, y with | (i, _), (j, _) -> i - j @@ -531,23 +536,23 @@ struct | None -> () | Some ats' -> Array.stable_sort cmp_annotated ats' in - let process_eq_classes zts = + let process_eq_classes zts = let is_const x = match x with | (_, (None, _), (None, _)) -> true | _ -> false in - let size_of_eq_class zts (start : int) : int = - let ref_elem = zts.(start) in + let size_of_eq_class zts (start : int) : int = + let ref_elem = zts.(start) in let remaining = (Array.length zts) - start - 1 in - let result = ref 0 in + let result = ref 0 in for i = 0 to remaining do let current_elem = zts.(start + i) in if cmp_z ref_elem current_elem = 0 then result := !result + 1 done; !result in - let least_index_var_in_eq_class zts start size : int * Z.t = + let least_index_var_in_eq_class zts start size : int * Z.t = let result = ref (0, Z.of_int 0) in match zts.(start) with | (i, (_, b), (_, _)) -> result := (i, b); @@ -611,7 +616,7 @@ struct | Some ats' -> Some (Array.map snd ats') in let join_d t1 t2 = - let zipped = ts_zip t1' t2' in + let zipped = ts_zip t1 t2 in sort_z_by_expr zipped; let annotated = process_eq_classes zipped in sort_annotated annotated; @@ -619,16 +624,16 @@ struct result in if is_bot a then b else if is_bot b then a else - match Option.get a.d, Option.get b.d with - | x, y when is_top_env a || is_top_env b -> {d = Some (EArray.empty ()); env = Environment.lce a.env b.env} - | x, y when (Environment.compare a.env b.env <> 0) -> - let sup_env = Environment.lce a.env b.env in - let mod_x = dim_add (Environment.dimchange a.env sup_env) x in - let mod_y = dim_add (Environment.dimchange b.env sup_env) y in - {d = join_d mod_x mod_y; env = sup_env} - | x, y when EArray.equal x y -> {d = Some x; env = a.env} - | x, y -> {d = join_d x y; env = a.env} - + match Option.get a.d, Option.get b.d with + | x, y when is_top_env a || is_top_env b -> {d = Some (EArray.empty ()); env = Environment.lce a.env b.env} + | x, y when (Environment.compare a.env b.env <> 0) -> + let sup_env = Environment.lce a.env b.env in + let mod_x = dim_add (Environment.dimchange a.env sup_env) x in + let mod_y = dim_add (Environment.dimchange b.env sup_env) y in + {d = join_d mod_x mod_y; env = sup_env} + | x, y when EArray.equal x y -> {d = Some x; env = a.env} + | x, y -> {d = join_d x y; env = a.env} + let join a b = timing_wrap "join" (join a) b let join a b = From af3fd6a36e628220fe26980e633c0e2611554a13 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Fri, 8 Dec 2023 14:58:57 +0100 Subject: [PATCH 038/280] Added comment explaining overflow flag in assign_exp and fixed indentation --- .../apron/linearTwoVarEqualityDomain.apron.ml | 209 +++++++++--------- 1 file changed, 106 insertions(+), 103 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index e1482dab4d..04ee49049d 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -25,8 +25,8 @@ things to implement: - leq - join - done: assignment - done: meet_tcons + done: assignment + done: meet_tcons HOW TO RUN THE REGRESSION TESTS: Method 1: regression test ./regtest.sh numberofdirectory numberoftest @@ -394,50 +394,50 @@ struct print_string "end\n"; let subst_var ts x t = match !ts with - | None -> () - | Some ts' -> - if Array.length ts' <> 0 then + | None -> () + | Some ts' -> + if Array.length ts' <> 0 then for i = 0 to Array.length ts' - 1 do match ts'.(i) with - | (None, _) -> () - | (Some x', b') -> if x = x' then + | (None, _) -> () + | (Some x', b') -> if x = x' then (match t with - | (None, bt) -> ts'.(i) <- (None, Z.(b' + bt)) - | (Some xt, bt) -> ts'.(i) <- (Some xt, Z.(b' + bt))) + | (None, bt) -> ts'.(i) <- (None, Z.(b' + bt)) + | (Some xt, bt) -> ts'.(i) <- (Some xt, Z.(b' + bt))) done in let add_conj ts t i = match !ts with - | None -> () - | Some ts' -> - (match t with - | (None, b) -> - (match ts'.(i) with - | (None, b') -> if b <> b' then ts := None; - | (Some j, b') -> subst_var ts j (None, Z.(b - b'))) - | (Some j, b) -> - (match ts'.(i) with - | (None, b1) -> subst_var ts j (None, Z.(b1 - b)) - | (Some h1, b1) -> - (match ts'.(j) with - | (None, b2) -> subst_var ts i (None, Z.(b2 + b)) - | (Some h2, b2) -> - if h1 = h2 then - (if Z.(b1 <> (b2 + b)) then ts := None) - else if h1 < h2 then subst_var ts h2 (Some h1, Z.(b1 - (b + b2))) - else subst_var ts h1 (Some h2, Z.(b + (b2 - b1)))))) + | None -> () + | Some ts' -> + (match t with + | (None, b) -> + (match ts'.(i) with + | (None, b') -> if b <> b' then ts := None; + | (Some j, b') -> subst_var ts j (None, Z.(b - b'))) + | (Some j, b) -> + (match ts'.(i) with + | (None, b1) -> subst_var ts j (None, Z.(b1 - b)) + | (Some h1, b1) -> + (match ts'.(j) with + | (None, b2) -> subst_var ts i (None, Z.(b2 + b)) + | (Some h2, b2) -> + if h1 = h2 then + (if Z.(b1 <> (b2 + b)) then ts := None) + else if h1 < h2 then subst_var ts h2 (Some h1, Z.(b1 - (b + b2))) + else subst_var ts h1 (Some h2, Z.(b + (b2 - b1)))))) in match t1.d, t2.d with - | None, _ -> { d = None; env = sup_env} - | _, None -> { d = None; env = sup_env} - | Some d1', Some d2' -> - let ds = ref (Some (Array.copy d1')) in - if Array.length d2' <> 0 then + | None, _ -> { d = None; env = sup_env} + | _, None -> { d = None; env = sup_env} + | Some d1', Some d2' -> + let ds = ref (Some (Array.copy d1')) in + if Array.length d2' <> 0 then for j = 0 to Array.length d2' - 1 do add_conj ds d2'.(j) j done; - {d = !ds; env = sup_env} - + {d = !ds; env = sup_env} + (* let sup_env = Environment.lce t1.env t2.env in let t1, t2 = change_d t1 sup_env true false, change_d t2 sup_env true false in @@ -463,18 +463,18 @@ struct (* TODO: check implementation for less equal *) let leq t1 t2 = let env_comp = Environment.compare t1.env t2.env in (* Apron's Environment.compare has defined return values. *) -let implies ts t i : bool = + let implies ts t i : bool = match t with | (None, b) -> (match ts.(i) with - | (None, b') -> Z.equal b b' - | _ -> false) + | (None, b') -> Z.equal b b' + | _ -> false) | (Some j, b) -> (match ts.(i), ts.(j) with - | (None, _), (_, _) -> false - | (_, _), (None, _) -> false - | (Some h1, b1), (Some h2, b2) -> - h1 = h2 && Z.equal b1 (Z.add b2 b)) + | (None, _), (_, _) -> false + | (_, _), (None, _) -> false + | (Some h1, b1), (Some h2, b2) -> + h1 = h2 && Z.equal b1 (Z.add b2 b)) in if env_comp = -2 || env_comp > 0 then false else if is_bot t1 || is_top_env t2 then true else @@ -497,16 +497,16 @@ let implies ts t i : bool = let join a b = let ts_zip t1 t2 = - if Array.length t1 <> Array.length t2 then None else - let zts = Array.init (Array.length t1) (fun (i : int) -> (i, t1.(i), t2.(i))) in - Some zts + if Array.length t1 <> Array.length t2 then None else + let zts = Array.init (Array.length t1) (fun (i : int) -> (i, t1.(i), t2.(i))) in + Some zts in let const_offset t = match t with | (_, b) -> b in let diff t1 t2 = Z.((const_offset t1) - (const_offset t2)) in - let cmp_z x y = + let cmp_z x y = let cmp_z_ref x y: int = match x, y with | (None, _), (None, _) -> 0 @@ -519,15 +519,15 @@ let implies ts t i : bool = let diff_e1 = cmp_z_ref t1i t1j in if diff_e1 <> 0 then diff_e1 else let diff_e2 = cmp_z_ref t2i t2j in - if diff_e2 <> 0 then diff_e2 else - Z.to_int (Z.((diff t1i t2i) - (diff t1j t2j))) + if diff_e2 <> 0 then diff_e2 else + Z.to_int (Z.((diff t1i t2i) - (diff t1j t2j))) in let sort_z_by_expr zts = match zts with | None -> () | Some zts' -> Array.stable_sort cmp_z zts' in - let sort_annotated ats = + let sort_annotated ats = let cmp_annotated x y : int = match x, y with | (i, _), (j, _) -> i - j @@ -536,32 +536,32 @@ let implies ts t i : bool = | None -> () | Some ats' -> Array.stable_sort cmp_annotated ats' in - let process_eq_classes zts = + let process_eq_classes zts = let is_const x = match x with | (_, (None, _), (None, _)) -> true | _ -> false in - let size_of_eq_class zts (start : int) : int = - let ref_elem = zts.(start) in + let size_of_eq_class zts (start : int) : int = + let ref_elem = zts.(start) in let remaining = (Array.length zts) - start - 1 in - let result = ref 0 in + let result = ref 0 in for i = 0 to remaining do let current_elem = zts.(start + i) in if cmp_z ref_elem current_elem = 0 then result := !result + 1 done; !result in - let least_index_var_in_eq_class zts start size : int * Z.t = + let least_index_var_in_eq_class zts start size : int * Z.t = let result = ref (0, Z.of_int 0) in match zts.(start) with - | (i, (_, b), (_, _)) -> result := (i, b); - for i = start + 1 to start + size - 1 do - match zts.(i) with - | (j, (_, b), (_, _)) -> - if j < fst !result then result := (j, b) - done; - !result + | (i, (_, b), (_, _)) -> result := (i, b); + for i = start + 1 to start + size - 1 do + match zts.(i) with + | (j, (_, b), (_, _)) -> + if j < fst !result then result := (j, b) + done; + !result in let all_are_const_in_eq_class zts start size : bool = let result = ref true in @@ -601,10 +601,10 @@ let implies ts t i : bool = result.(!i) <- (i', (None, const_offset t1)) else result.(!i) <- (i', (Some i', Z.of_int 0)) else - let (least_i, least_b) = least_index_var_in_eq_class zts' !i n in - (if all_are_const_in_eq_class zts' !i n then - assign_vars_in_const_eq_class result zts' !i n least_i least_b - else assign_vars_in_non_const_eq_class result zts' !i n least_i least_b); + let (least_i, least_b) = least_index_var_in_eq_class zts' !i n in + (if all_are_const_in_eq_class zts' !i n then + assign_vars_in_const_eq_class result zts' !i n least_i least_b + else assign_vars_in_non_const_eq_class result zts' !i n least_i least_b); ); i := !i + n; done; @@ -612,8 +612,8 @@ let implies ts t i : bool = in let strip_annotation ats = match ats with - | None -> None - | Some ats' -> Some (Array.map snd ats') + | None -> None + | Some ats' -> Some (Array.map snd ats') in let join_d t1 t2 = let zipped = ts_zip t1 t2 in @@ -624,16 +624,16 @@ let implies ts t i : bool = result in if is_bot a then b else if is_bot b then a else - match Option.get a.d, Option.get b.d with - | x, y when is_top_env a || is_top_env b -> {d = Some (EArray.empty ()); env = Environment.lce a.env b.env} - | x, y when (Environment.compare a.env b.env <> 0) -> - let sup_env = Environment.lce a.env b.env in - let mod_x = dim_add (Environment.dimchange a.env sup_env) x in - let mod_y = dim_add (Environment.dimchange b.env sup_env) y in - {d = join_d mod_x mod_y; env = sup_env} - | x, y when EArray.equal x y -> {d = Some x; env = a.env} - | x, y -> {d = join_d x y; env = a.env} - + match Option.get a.d, Option.get b.d with + | x, y when is_top_env a || is_top_env b -> {d = Some (EArray.empty ()); env = Environment.lce a.env b.env} + | x, y when (Environment.compare a.env b.env <> 0) -> + let sup_env = Environment.lce a.env b.env in + let mod_x = dim_add (Environment.dimchange a.env sup_env) x in + let mod_y = dim_add (Environment.dimchange b.env sup_env) y in + {d = join_d mod_x mod_y; env = sup_env} + | x, y when EArray.equal x y -> {d = Some x; env = a.env} + | x, y -> {d = join_d x y; env = a.env} + let join a b = timing_wrap "join" (join a) b let join a b = @@ -708,6 +708,9 @@ let implies ts t i : bool = let assign_texpr t var texp = timing_wrap "assign_texpr" (assign_texpr t var) texp + (* no_ov -> no overflow + if it's true then there is no overflow + -> Convert.texpr1_expr_of_cil_exp handles overflow (TODO: test)*) let assign_exp (t: VarManagement.t) var exp (no_ov: bool Lazy.t) = let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in match Convert.texpr1_expr_of_cil_exp t t.env exp (Lazy.force no_ov) with @@ -743,12 +746,12 @@ let implies ts t i : bool = let t_primed = add_vars t primed_vars in let multi_t = List.fold_left2 (fun t' v_prime (_,v') -> assign_var t' v_prime v') t_primed primed_vars vv's in match multi_t.d with - | Some arr when not @@ is_top_env multi_t -> - let switched_arr = List.fold_left2 (fun multi_t assigned_var primed_var-> assign_var multi_t assigned_var primed_var) multi_t assigned_vars primed_vars in - let res = drop_vars switched_arr primed_vars true in - let x = Option.get res.d in - {d = Some x; env = res.env} - | _ -> t + | Some arr when not @@ is_top_env multi_t -> + let switched_arr = List.fold_left2 (fun multi_t assigned_var primed_var-> assign_var multi_t assigned_var primed_var) multi_t assigned_vars primed_vars in + let res = drop_vars switched_arr primed_vars true in + let x = Option.get res.d in + {d = Some x; env = res.env} + | _ -> t let assign_var_parallel t vv's = let res = assign_var_parallel t vv's in @@ -809,27 +812,27 @@ let implies ts t i : bool = depending on the result in the array after the evaluating including resolving the constraints in t.d the tcons can be evaluated and additional constraints can be added to t.d *) let expr_init = Array.init ((Environment.size t.env) +1) (fun _ -> Z.zero) in match t.d with - | None -> t - | Some d -> - let cv's = get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) in - let update (expr : Z.t Array.t)( c , v) = - match v with - | None -> Array.set expr 0 (Z.add expr.(0) c) ; expr - | Some idx -> match d.(idx) with - | (Some idx_i,c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; Array.set expr (idx_i + 1) (Z.add expr.(idx_i + 1) c_i) ; expr - | (None, c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; expr - in - let final_expr = List.fold_left (fun expr cv -> update expr cv ) expr_init cv's in - let is_constant = List.fold_left (fun b a -> if Z.equal a Z.zero then b else false) true @@ List.tl @@ Array.to_list final_expr in - let var_count = List.count_matching (fun a -> if Z.equal a Z.zero then false else true) @@ List.tl @@ Array.to_list final_expr + | None -> t + | Some d -> + let cv's = get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) in + let update (expr : Z.t Array.t)( c , v) = + match v with + | None -> Array.set expr 0 (Z.add expr.(0) c) ; expr + | Some idx -> match d.(idx) with + | (Some idx_i,c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; Array.set expr (idx_i + 1) (Z.add expr.(idx_i + 1) c_i) ; expr + | (None, c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; expr + in + let final_expr = List.fold_left (fun expr cv -> update expr cv ) expr_init cv's in + let is_constant = List.fold_left (fun b a -> if Z.equal a Z.zero then b else false) true @@ List.tl @@ Array.to_list final_expr in + let var_count = List.count_matching (fun a -> if Z.equal a Z.zero then false else true) @@ List.tl @@ Array.to_list final_expr in if is_constant then match Tcons1.get_typ tcons with - | EQ -> if Z.equal final_expr.(0) Z.zero then t else bot() - | SUPEQ -> if Z.geq final_expr.(0) Z.zero then t else bot() - | SUP -> if Z.gt final_expr.(0) Z.zero then t else bot() - | DISEQ -> if Z.equal final_expr.(0) Z.zero then bot() else t - | EQMOD scalar -> t (*Not supported right now - if Float.equal ( Float.modulo (Z.to_float final_expr.(0)) (convert_scalar scalar )) 0. then t else {d = None; env = t.env}*) + | EQ -> if Z.equal final_expr.(0) Z.zero then t else bot() + | SUPEQ -> if Z.geq final_expr.(0) Z.zero then t else bot() + | SUP -> if Z.gt final_expr.(0) Z.zero then t else bot() + | DISEQ -> if Z.equal final_expr.(0) Z.zero then bot() else t + | EQMOD scalar -> t (*Not supported right now + if Float.equal ( Float.modulo (Z.to_float final_expr.(0)) (convert_scalar scalar )) 0. then t else {d = None; env = t.env}*) else if var_count == 1 then let var = List.findi (fun i a -> if Z.equal a Z.zero then false else true) @@ Array.to_list final_expr in let c = if Z.divisible final_expr.(0) @@ Tuple2.second var then Some (Z.(- final_expr.(0) / (Tuple2.second var))) else None in @@ -845,8 +848,8 @@ let implies ts t i : bool = let var1 = Environment.var_of_dim t.env (Tuple2.first (List.hd v12)) in let var2 = Environment.var_of_dim t.env (Tuple2.first (List.hd @@ List.tl v12)) in match Tcons1.get_typ tcons with - | EQ -> if Z.equal a1 Z.one && Z.equal a2 Z.one then meet t (assign_var (top_env t.env) var1 var2) else t - | _-> t (*Not supported right now*) + | EQ -> if Z.equal a1 Z.one && Z.equal a2 Z.one then meet t (assign_var (top_env t.env) var1 var2) else t + | _-> t (*Not supported right now*) else t (*For any other case we don't know if the (in-) equality is true or false or even possible therefore we just return t *) From 6e501e4abb98db139a5f13f37f913c1a93dc8cb6 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Fri, 8 Dec 2023 15:38:23 +0100 Subject: [PATCH 039/280] Merge goblint main branch into relational-domains branch --- .github/workflows/coverage.yml | 3 + .github/workflows/docs.yml | 4 +- .github/workflows/locked.yml | 3 + .github/workflows/metadata.yml | 3 + .github/workflows/options.yml | 6 +- .github/workflows/unlocked.yml | 7 +- .gitignore | 1 - .mailmap | 4 + .readthedocs.yaml | 2 +- .zenodo.json | 15 +- CHANGELOG.md | 12 + CITATION.cff | 4 + conf/examples/very-precise.json | 4 +- conf/svcomp.json | 47 +- conf/svcomp24-validate.json | 140 +++++ conf/svcomp24.json | 146 ++++++ docs/developer-guide/debugging.md | 5 +- docs/developer-guide/firstanalysis.md | 2 +- docs/developer-guide/messaging.md | 13 - docs/developer-guide/releasing.md | 28 +- docs/user-guide/configuring.md | 2 +- dune-project | 8 +- goblint.opam | 9 +- goblint.opam.locked | 22 +- goblint.opam.template | 3 +- gobview | 2 +- lib/goblint/runtime/include/goblint.h | 2 + lib/goblint/runtime/src/goblint.c | 4 + scripts/goblint-lib-modules.py | 4 +- scripts/regression2sv-benchmarks.py | 1 - scripts/spec/check.sh | 27 - scripts/spec/regression.py | 61 --- scripts/spec/regression.sh | 18 - scripts/spec/spec.sh | 10 - scripts/update_suite.rb | 49 +- src/analyses/accessAnalysis.ml | 2 +- .../apron/affineEqualityAnalysis.apron.ml | 1 - src/analyses/apron/apronAnalysis.apron.ml | 3 +- .../linearTwoVarEqualityAnalysis.apron.ml | 1 - src/analyses/apron/relationAnalysis.apron.ml | 15 +- src/analyses/apron/relationPriv.apron.ml | 3 +- src/analyses/base.ml | 37 +- src/analyses/baseInvariant.ml | 52 +- src/analyses/basePriv.ml | 31 +- src/analyses/commonPriv.ml | 7 +- src/analyses/extractPthread.ml | 2 +- src/analyses/fileUse.ml | 296 ----------- src/analyses/loopTermination.ml | 87 +++ src/analyses/mCPRegistry.ml | 2 +- src/analyses/memLeak.ml | 226 ++++++-- src/analyses/memOutOfBounds.ml | 6 +- ...SinceLongjmp.ml => modifiedSinceSetjmp.ml} | 6 +- src/analyses/region.ml | 10 +- src/analyses/spec.ml | 496 ------------------ src/analyses/stackTrace.ml | 4 +- src/analyses/termination.ml | 239 --------- src/analyses/threadAnalysis.ml | 12 +- src/analyses/threadFlag.ml | 4 + src/analyses/unassumeAnalysis.ml | 44 +- src/autoTune.ml | 59 ++- src/build-info/dune | 3 + src/cdomains/addressDomain.ml | 85 +-- src/cdomains/addressDomain_intf.ml | 6 +- .../apron/affineEqualityDomain.apron.ml | 6 +- src/cdomains/apron/apronDomain.apron.ml | 49 +- src/cdomains/apron/gobApron.apron.ml | 98 ++++ src/cdomains/apron/gobApron.no-apron.ml | 0 .../apron/linearTwoVarEqualityDomain.apron.ml | 3 +- src/cdomains/apron/relationDomain.apron.ml | 77 ++- src/cdomains/apron/sharedFunctions.apron.ml | 114 +--- src/cdomains/fileDomain.ml | 81 --- src/cdomains/floatDomain.ml | 14 + src/cdomains/floatDomain.mli | 3 + src/cdomains/floatOps/floatOps.ml | 3 + src/cdomains/floatOps/floatOps.mli | 1 + src/cdomains/floatOps/stubs.c | 14 + src/cdomains/intDomain.ml | 56 +- src/cdomains/mvalMapDomain.ml | 299 ----------- src/cdomains/offset.ml | 2 +- src/cdomains/regionDomain.ml | 23 +- src/cdomains/specDomain.ml | 34 -- src/cdomains/stringDomain.ml | 114 ++++ src/cdomains/stringDomain.mli | 40 ++ src/cdomains/valueDomain.ml | 2 +- src/common/cdomains/basetype.ml | 14 - src/common/common.mld | 14 +- src/common/domains/printable.ml | 42 +- src/common/dune | 10 +- src/common/framework/analysisState.ml | 2 + src/{ => common}/framework/cfgTools.ml | 10 +- src/common/util/cilfacade.ml | 36 +- src/common/util/messageCategory.ml | 5 + src/common/util/messages.ml | 21 +- src/{common/util => config}/afterConfig.ml | 0 src/config/config.mld | 14 + src/config/dune | 23 + src/{common/util => config}/gobConfig.ml | 9 +- src/{common/util => config}/jsonSchema.ml | 0 src/{common/util => config}/options.ml | 2 +- .../util => config}/options.schema.json | 149 ++++-- src/{domains => domain}/boolDomain.ml | 16 +- src/{domains => domain}/disjointDomain.ml | 0 src/domain/domain.mld | 21 + src/domain/dune | 19 + src/{domains => domain}/flagHelper.ml | 0 src/{domains => domain}/hoareDomain.ml | 22 +- src/{common/domains => domain}/lattice.ml | 10 +- src/{domains => domain}/mapDomain.ml | 27 +- src/{domains => domain}/partitionDomain.ml | 0 src/{domains => domain}/setDomain.ml | 6 +- src/{domains => domain}/trieDomain.ml | 0 src/domains/access.ml | 2 + src/domains/queries.ml | 27 +- src/dune | 14 +- src/framework/analyses.ml | 22 + src/framework/constraints.ml | 186 ++++++- src/framework/control.ml | 26 +- src/goblint.ml | 2 + src/goblint_lib.ml | 22 +- src/{util => incremental}/cilMaps.ml | 0 src/incremental/compareCFG.ml | 2 +- src/incremental/dune | 22 + src/incremental/incremental.mld | 16 + src/index.mld | 15 + src/main.camldoc | 2 - src/maingoblint.ml | 25 +- src/mainspec.ml | 13 - src/solvers/postSolver.ml | 8 +- src/spec/dune | 2 - src/spec/file.dot | 37 -- src/spec/render.sh | 31 -- src/spec/specCore.ml | 152 ------ src/spec/specLexer.mll | 67 --- src/spec/specParser.mly | 116 ---- src/spec/specUtil.ml | 52 -- src/util/cilCfg.ml | 6 +- src/{domains => util/library}/accessKind.ml | 0 src/util/library/dune | 18 + src/util/library/library.mld | 14 + src/{analyses => util/library}/libraryDesc.ml | 7 +- src/{analyses => util/library}/libraryDsl.ml | 0 src/{analyses => util/library}/libraryDsl.mli | 0 .../library}/libraryFunctions.ml | 205 ++++---- .../library}/libraryFunctions.mli | 0 src/util/loopUnrolling.ml | 1 + src/util/server.ml | 1 + src/util/std/dune | 3 +- src/util/std/gobHashtbl.ml | 4 - .../myCheck.ml => util/std/gobQCheck.ml} | 3 - src/util/std/gobYaml.ml | 11 + src/util/std/goblint_std.ml | 1 + src/util/terminationPreprocessing.ml | 76 +++ src/util/tracing/dune | 9 + .../tracing/goblint_tracing.ml} | 18 +- src/witness/myARG.ml | 47 +- src/witness/svcomp.ml | 6 +- src/witness/svcompSpec.ml | 42 +- src/witness/witness.ml | 45 +- src/witness/yamlWitness.ml | 211 +++++++- src/witness/yamlWitnessType.ml | 103 ++++ sv-comp/archive.sh | 7 +- .../00-sanity/51-base-special-lval.c | 13 + .../02-base/88-string-ptrs-limited.c | 2 +- .../02-base/89-string-ptrs-not-limited.c | 2 +- .../regression/04-mutex/49-type-invariants.t | 4 - .../04-mutex/77-type-nested-fields.t | 4 - .../04-mutex/79-type-nested-fields-deep1.t | 4 - .../04-mutex/80-type-nested-fields-deep2.t | 4 - .../04-mutex/90-distribute-fields-type-1.t | 4 - .../04-mutex/91-distribute-fields-type-2.t | 4 - .../04-mutex/92-distribute-fields-type-deep.t | 4 - .../93-distribute-fields-type-global.t | 2 - .../regression/09-regions/38-escape_malloc.c | 4 +- .../41-per-thread-array-init-race.c | 40 ++ tests/regression/18-file/01-ok.c | 12 - tests/regression/18-file/02-function.c | 17 - tests/regression/18-file/03-if-close.c | 15 - tests/regression/18-file/04-no-open.c | 10 - tests/regression/18-file/05-open-mode.c | 11 - tests/regression/18-file/06-2open.c | 12 - tests/regression/18-file/07-2close.c | 11 - tests/regression/18-file/08-var-reuse.c | 15 - .../regression/18-file/09-inf-loop-no-close.c | 17 - tests/regression/18-file/10-inf-loop-ok.c | 19 - tests/regression/18-file/11-2if.c | 18 - tests/regression/18-file/12-2close-if.c | 15 - tests/regression/18-file/13-ptr-arith-ok.c | 16 - tests/regression/18-file/14-ptr-arith-close.c | 13 - tests/regression/18-file/15-var-switch.c | 18 - tests/regression/18-file/16-var-reuse-close.c | 14 - tests/regression/18-file/17-myfopen.c | 21 - tests/regression/18-file/18-myfopen-arg.c | 20 - tests/regression/18-file/19-if-close-else.c | 17 - tests/regression/18-file/20-loop-close.c | 18 - tests/regression/18-file/21-for-i.c | 26 - tests/regression/18-file/22-f_int.c | 13 - tests/regression/18-file/23-f_str.c | 13 - tests/regression/18-file/24-f_wstr.c | 14 - tests/regression/18-file/25-mem-ok.c | 29 - tests/regression/18-file/26-open-error-ok.c | 15 - tests/regression/18-file/27-open-error.c | 13 - tests/regression/18-file/28-multiple-exits.c | 14 - tests/regression/18-file/29-alias-global.c | 22 - tests/regression/18-file/30-ptr-of-ptr.c | 14 - tests/regression/18-file/31-var-reuse-fun.c | 16 - tests/regression/18-file/32-multi-ptr-close.c | 25 - tests/regression/18-file/33-multi-ptr-open.c | 23 - .../regression/18-file/34-multi-alias-close.c | 25 - .../regression/18-file/35-multi-alias-open.c | 23 - tests/regression/18-file/36-fun-ptr.c | 14 - .../regression/18-file/37-var-switch-alias.c | 18 - tests/regression/18-file/README.md | 2 + tests/regression/18-file/file.c | 44 -- tests/regression/18-file/file.optimistic.spec | 34 -- tests/regression/18-file/file.spec | 57 -- tests/regression/19-spec/01-malloc-free.c | 19 - tests/regression/19-spec/02-mutex_rc.c | 23 - tests/regression/19-spec/README.md | 2 + .../regression/19-spec/malloc.optimistic.spec | 23 - tests/regression/19-spec/malloc.spec | 26 - tests/regression/19-spec/mutex-lock.spec | 31 -- tests/regression/29-svcomp/32-no-ov.c | 7 + tests/regression/29-svcomp/32-no-ov.t | 16 + tests/regression/29-svcomp/dune | 2 + tests/regression/39-signed-overflows/06-abs.c | 29 + .../39-signed-overflows/07-abs-sqrt.c | 10 + .../regression/39-signed-overflows/08-labs.c | 22 + .../39-signed-overflows/09-labs-sqrt.c | 10 + .../39-signed-overflows/10-shiftleft.c | 31 ++ .../56-witness/52-witness-lifter-ps2.c | 35 ++ .../56-witness/53-witness-lifter-ps3.c | 39 ++ .../73-strings/01-string_literals.c | 8 +- .../73-strings/02-string_literals_with_null.c | 2 +- .../regression/73-strings/03-string_basics.c | 2 +- .../73-strings/05-string-unit-domain.c | 15 + tests/regression/74-invalid_deref/30-calloc.c | 9 + .../74-invalid_deref/31-multithreaded.c | 21 + .../76-memleak/08-unreachable-mem.c | 12 + .../09-unreachable-with-local-var.c | 15 + .../76-memleak/10-global-struct-no-ptr.c | 16 + .../76-memleak/11-global-struct-ptr.c | 19 + .../76-memleak/12-global-nested-struct-ptr.c | 25 + .../13-global-nested-struct-ptr-reachable.c | 29 + ...4-global-nested-struct-non-ptr-reachable.c | 25 + .../20-invalid-memcleanup-multi-threaded.c | 36 ++ ...-invalid-memcleanup-multi-threaded-abort.c | 35 ++ tests/regression/76-memleak/22-leak-later.c | 25 + .../76-memleak/23-leak-later-nested.c | 34 ++ .../76-memleak/24-multi-threaded-assert.c | 34 ++ .../25-assert-unknown-multi-threaded.c | 20 + ...alid-memcleanup-multi-threaded-betterpiv.c | 36 ++ .../27-mem-leak-not-joined-thread.c | 19 + .../28-no-mem-leak-thread-exit-main.c | 22 + .../76-memleak/29-mem-leak-thread-return.c | 26 + .../76-memleak/30-mem-leak-thread-exit.c | 27 + .../76-memleak/31-no-mem-leak-return.c | 32 ++ .../01-simple-loop-terminating.c | 15 + .../02-simple-loop-nonterminating.c | 12 + .../03-nested-loop-terminating.c | 27 + .../04-nested-loop-nonterminating.c | 23 + .../78-termination/05-for-loop-terminating.c | 14 + .../06-for-loop-nonterminating.c | 12 + .../07-nested-for-loop-terminating.c | 20 + .../08-nested-for-loop-nonterminating.c | 19 + .../09-complex-for-loop-terminating.c | 98 ++++ .../10-complex-loop-terminating.c | 218 ++++++++ .../78-termination/11-loopless-termination.c | 8 + .../12-do-while-instant-terminating.c | 15 + .../78-termination/13-do-while-terminating.c | 16 + .../14-do-while-nonterminating.c | 16 + .../15-complex-loop-combination-terminating.c | 126 +++++ ...16-nested-loop-nontrivial-nonterminating.c | 23 + .../78-termination/17-goto-terminating.c | 21 + .../78-termination/18-goto-nonterminating.c | 15 + .../78-termination/19-rand-terminating.c | 31 ++ .../78-termination/20-rand-nonterminating.c | 30 ++ .../21-no-exit-on-rand-unproofable.c | 20 + .../22-exit-on-rand-unproofable.c | 16 + .../23-exit-on-rand-terminating.c | 17 + .../24-upjumping-goto-loopless-terminating.c | 21 + .../25-leave-loop-goto-terminating.c | 28 + .../26-enter-loop-goto-terminating.c | 31 ++ .../27-upjumping-goto-nonterminating.c | 21 + .../28-do-while-continue-terminating.c | 99 ++++ .../29-do-while-continue-nonterminating.c | 22 + .../30-goto-out-of-inner-loop-terminating.c | 36 ++ ...31-goto-out-of-inner-loop-nonterminating.c | 27 + .../32-multithread-terminating.c | 30 ++ .../33-multithread-nonterminating.c | 40 ++ .../34-nested-for-loop-nonterminating.c | 19 + ...out-of-inner-loop-with-print-terminating.c | 42 ++ .../78-termination/36-recursion-terminating.c | 25 + .../37-recursion-nonterminating.c | 25 + .../38-recursion-nested-terminating.c | 41 ++ .../39-recursion-nested-nonterminating.c | 29 + ...-multi-expression-conditions-terminating.c | 44 ++ .../41-for-continue-terminating.c | 27 + ...42-downjumping-goto-loopless-terminating.c | 19 + .../43-return-from-endless-loop-terminating.c | 14 + ...recursion-multiple-functions-terminating.c | 40 ++ ...ursion-multiple-functions-nonterminating.c | 40 ++ ...-recursion-different-context-terminating.c | 32 ++ ...cursion-different-context-nonterminating.c | 32 ++ .../48-dynamic-recursion-nonterminating.c | 10 + tests/regression/78-termination/49-longjmp.c | 11 + .../78-termination/50-decreasing-signed-int.c | 13 + tests/regression/78-termination/51-modulo.c | 14 + unittest/dune | 2 +- unittest/util/intOpsTest.ml | 5 +- 309 files changed, 5156 insertions(+), 3832 deletions(-) create mode 100644 conf/svcomp24-validate.json create mode 100644 conf/svcomp24.json delete mode 100755 scripts/spec/check.sh delete mode 100755 scripts/spec/regression.py delete mode 100755 scripts/spec/regression.sh delete mode 100755 scripts/spec/spec.sh delete mode 100644 src/analyses/fileUse.ml create mode 100644 src/analyses/loopTermination.ml rename src/analyses/{modifiedSinceLongjmp.ml => modifiedSinceSetjmp.ml} (96%) delete mode 100644 src/analyses/spec.ml delete mode 100644 src/analyses/termination.ml create mode 100644 src/cdomains/apron/gobApron.apron.ml create mode 100644 src/cdomains/apron/gobApron.no-apron.ml delete mode 100644 src/cdomains/fileDomain.ml delete mode 100644 src/cdomains/mvalMapDomain.ml delete mode 100644 src/cdomains/specDomain.ml create mode 100644 src/cdomains/stringDomain.ml create mode 100644 src/cdomains/stringDomain.mli rename src/{ => common}/framework/cfgTools.ml (98%) rename src/{common/util => config}/afterConfig.ml (100%) create mode 100644 src/config/config.mld create mode 100644 src/config/dune rename src/{common/util => config}/gobConfig.ml (96%) rename src/{common/util => config}/jsonSchema.ml (100%) rename src/{common/util => config}/options.ml (98%) rename src/{common/util => config}/options.schema.json (96%) rename src/{domains => domain}/boolDomain.ml (72%) rename src/{domains => domain}/disjointDomain.ml (100%) create mode 100644 src/domain/domain.mld create mode 100644 src/domain/dune rename src/{domains => domain}/flagHelper.ml (100%) rename src/{domains => domain}/hoareDomain.ml (97%) rename src/{common/domains => domain}/lattice.ml (96%) rename src/{domains => domain}/mapDomain.ml (98%) rename src/{domains => domain}/partitionDomain.ml (100%) rename src/{domains => domain}/setDomain.ml (98%) rename src/{domains => domain}/trieDomain.ml (100%) rename src/{util => incremental}/cilMaps.ml (100%) create mode 100644 src/incremental/dune create mode 100644 src/incremental/incremental.mld delete mode 100644 src/mainspec.ml delete mode 100644 src/spec/dune delete mode 100644 src/spec/file.dot delete mode 100755 src/spec/render.sh delete mode 100644 src/spec/specCore.ml delete mode 100644 src/spec/specLexer.mll delete mode 100644 src/spec/specParser.mly delete mode 100644 src/spec/specUtil.ml rename src/{domains => util/library}/accessKind.ml (100%) create mode 100644 src/util/library/dune create mode 100644 src/util/library/library.mld rename src/{analyses => util/library}/libraryDesc.ml (95%) rename src/{analyses => util/library}/libraryDsl.ml (100%) rename src/{analyses => util/library}/libraryDsl.mli (100%) rename src/{analyses => util/library}/libraryFunctions.ml (93%) rename src/{analyses => util/library}/libraryFunctions.mli (100%) rename src/{common/domains/myCheck.ml => util/std/gobQCheck.ml} (91%) create mode 100644 src/util/terminationPreprocessing.ml create mode 100644 src/util/tracing/dune rename src/{common/util/tracing.ml => util/tracing/goblint_tracing.ml} (84%) create mode 100644 tests/regression/00-sanity/51-base-special-lval.c create mode 100644 tests/regression/09-regions/41-per-thread-array-init-race.c delete mode 100644 tests/regression/18-file/01-ok.c delete mode 100644 tests/regression/18-file/02-function.c delete mode 100644 tests/regression/18-file/03-if-close.c delete mode 100644 tests/regression/18-file/04-no-open.c delete mode 100644 tests/regression/18-file/05-open-mode.c delete mode 100644 tests/regression/18-file/06-2open.c delete mode 100644 tests/regression/18-file/07-2close.c delete mode 100644 tests/regression/18-file/08-var-reuse.c delete mode 100644 tests/regression/18-file/09-inf-loop-no-close.c delete mode 100644 tests/regression/18-file/10-inf-loop-ok.c delete mode 100644 tests/regression/18-file/11-2if.c delete mode 100644 tests/regression/18-file/12-2close-if.c delete mode 100644 tests/regression/18-file/13-ptr-arith-ok.c delete mode 100644 tests/regression/18-file/14-ptr-arith-close.c delete mode 100644 tests/regression/18-file/15-var-switch.c delete mode 100644 tests/regression/18-file/16-var-reuse-close.c delete mode 100644 tests/regression/18-file/17-myfopen.c delete mode 100644 tests/regression/18-file/18-myfopen-arg.c delete mode 100644 tests/regression/18-file/19-if-close-else.c delete mode 100644 tests/regression/18-file/20-loop-close.c delete mode 100644 tests/regression/18-file/21-for-i.c delete mode 100644 tests/regression/18-file/22-f_int.c delete mode 100644 tests/regression/18-file/23-f_str.c delete mode 100644 tests/regression/18-file/24-f_wstr.c delete mode 100644 tests/regression/18-file/25-mem-ok.c delete mode 100644 tests/regression/18-file/26-open-error-ok.c delete mode 100644 tests/regression/18-file/27-open-error.c delete mode 100644 tests/regression/18-file/28-multiple-exits.c delete mode 100644 tests/regression/18-file/29-alias-global.c delete mode 100644 tests/regression/18-file/30-ptr-of-ptr.c delete mode 100644 tests/regression/18-file/31-var-reuse-fun.c delete mode 100644 tests/regression/18-file/32-multi-ptr-close.c delete mode 100644 tests/regression/18-file/33-multi-ptr-open.c delete mode 100644 tests/regression/18-file/34-multi-alias-close.c delete mode 100644 tests/regression/18-file/35-multi-alias-open.c delete mode 100644 tests/regression/18-file/36-fun-ptr.c delete mode 100644 tests/regression/18-file/37-var-switch-alias.c create mode 100644 tests/regression/18-file/README.md delete mode 100644 tests/regression/18-file/file.c delete mode 100644 tests/regression/18-file/file.optimistic.spec delete mode 100644 tests/regression/18-file/file.spec delete mode 100644 tests/regression/19-spec/01-malloc-free.c delete mode 100644 tests/regression/19-spec/02-mutex_rc.c create mode 100644 tests/regression/19-spec/README.md delete mode 100644 tests/regression/19-spec/malloc.optimistic.spec delete mode 100644 tests/regression/19-spec/malloc.spec delete mode 100644 tests/regression/19-spec/mutex-lock.spec create mode 100644 tests/regression/29-svcomp/32-no-ov.c create mode 100644 tests/regression/29-svcomp/32-no-ov.t create mode 100644 tests/regression/29-svcomp/dune create mode 100644 tests/regression/39-signed-overflows/06-abs.c create mode 100644 tests/regression/39-signed-overflows/07-abs-sqrt.c create mode 100644 tests/regression/39-signed-overflows/08-labs.c create mode 100644 tests/regression/39-signed-overflows/09-labs-sqrt.c create mode 100644 tests/regression/39-signed-overflows/10-shiftleft.c create mode 100644 tests/regression/56-witness/52-witness-lifter-ps2.c create mode 100644 tests/regression/56-witness/53-witness-lifter-ps3.c create mode 100644 tests/regression/73-strings/05-string-unit-domain.c create mode 100644 tests/regression/74-invalid_deref/30-calloc.c create mode 100644 tests/regression/74-invalid_deref/31-multithreaded.c create mode 100644 tests/regression/76-memleak/08-unreachable-mem.c create mode 100644 tests/regression/76-memleak/09-unreachable-with-local-var.c create mode 100644 tests/regression/76-memleak/10-global-struct-no-ptr.c create mode 100644 tests/regression/76-memleak/11-global-struct-ptr.c create mode 100644 tests/regression/76-memleak/12-global-nested-struct-ptr.c create mode 100644 tests/regression/76-memleak/13-global-nested-struct-ptr-reachable.c create mode 100644 tests/regression/76-memleak/14-global-nested-struct-non-ptr-reachable.c create mode 100644 tests/regression/76-memleak/20-invalid-memcleanup-multi-threaded.c create mode 100644 tests/regression/76-memleak/21-invalid-memcleanup-multi-threaded-abort.c create mode 100644 tests/regression/76-memleak/22-leak-later.c create mode 100644 tests/regression/76-memleak/23-leak-later-nested.c create mode 100644 tests/regression/76-memleak/24-multi-threaded-assert.c create mode 100644 tests/regression/76-memleak/25-assert-unknown-multi-threaded.c create mode 100644 tests/regression/76-memleak/26-invalid-memcleanup-multi-threaded-betterpiv.c create mode 100644 tests/regression/76-memleak/27-mem-leak-not-joined-thread.c create mode 100644 tests/regression/76-memleak/28-no-mem-leak-thread-exit-main.c create mode 100644 tests/regression/76-memleak/29-mem-leak-thread-return.c create mode 100644 tests/regression/76-memleak/30-mem-leak-thread-exit.c create mode 100644 tests/regression/76-memleak/31-no-mem-leak-return.c create mode 100644 tests/regression/78-termination/01-simple-loop-terminating.c create mode 100644 tests/regression/78-termination/02-simple-loop-nonterminating.c create mode 100644 tests/regression/78-termination/03-nested-loop-terminating.c create mode 100644 tests/regression/78-termination/04-nested-loop-nonterminating.c create mode 100644 tests/regression/78-termination/05-for-loop-terminating.c create mode 100644 tests/regression/78-termination/06-for-loop-nonterminating.c create mode 100644 tests/regression/78-termination/07-nested-for-loop-terminating.c create mode 100644 tests/regression/78-termination/08-nested-for-loop-nonterminating.c create mode 100644 tests/regression/78-termination/09-complex-for-loop-terminating.c create mode 100644 tests/regression/78-termination/10-complex-loop-terminating.c create mode 100644 tests/regression/78-termination/11-loopless-termination.c create mode 100644 tests/regression/78-termination/12-do-while-instant-terminating.c create mode 100644 tests/regression/78-termination/13-do-while-terminating.c create mode 100644 tests/regression/78-termination/14-do-while-nonterminating.c create mode 100644 tests/regression/78-termination/15-complex-loop-combination-terminating.c create mode 100644 tests/regression/78-termination/16-nested-loop-nontrivial-nonterminating.c create mode 100644 tests/regression/78-termination/17-goto-terminating.c create mode 100644 tests/regression/78-termination/18-goto-nonterminating.c create mode 100644 tests/regression/78-termination/19-rand-terminating.c create mode 100644 tests/regression/78-termination/20-rand-nonterminating.c create mode 100644 tests/regression/78-termination/21-no-exit-on-rand-unproofable.c create mode 100644 tests/regression/78-termination/22-exit-on-rand-unproofable.c create mode 100644 tests/regression/78-termination/23-exit-on-rand-terminating.c create mode 100644 tests/regression/78-termination/24-upjumping-goto-loopless-terminating.c create mode 100644 tests/regression/78-termination/25-leave-loop-goto-terminating.c create mode 100644 tests/regression/78-termination/26-enter-loop-goto-terminating.c create mode 100644 tests/regression/78-termination/27-upjumping-goto-nonterminating.c create mode 100644 tests/regression/78-termination/28-do-while-continue-terminating.c create mode 100644 tests/regression/78-termination/29-do-while-continue-nonterminating.c create mode 100644 tests/regression/78-termination/30-goto-out-of-inner-loop-terminating.c create mode 100644 tests/regression/78-termination/31-goto-out-of-inner-loop-nonterminating.c create mode 100644 tests/regression/78-termination/32-multithread-terminating.c create mode 100644 tests/regression/78-termination/33-multithread-nonterminating.c create mode 100644 tests/regression/78-termination/34-nested-for-loop-nonterminating.c create mode 100644 tests/regression/78-termination/35-goto-out-of-inner-loop-with-print-terminating.c create mode 100644 tests/regression/78-termination/36-recursion-terminating.c create mode 100644 tests/regression/78-termination/37-recursion-nonterminating.c create mode 100644 tests/regression/78-termination/38-recursion-nested-terminating.c create mode 100644 tests/regression/78-termination/39-recursion-nested-nonterminating.c create mode 100644 tests/regression/78-termination/40-multi-expression-conditions-terminating.c create mode 100644 tests/regression/78-termination/41-for-continue-terminating.c create mode 100644 tests/regression/78-termination/42-downjumping-goto-loopless-terminating.c create mode 100644 tests/regression/78-termination/43-return-from-endless-loop-terminating.c create mode 100644 tests/regression/78-termination/44-recursion-multiple-functions-terminating.c create mode 100644 tests/regression/78-termination/45-recursion-multiple-functions-nonterminating.c create mode 100644 tests/regression/78-termination/46-recursion-different-context-terminating.c create mode 100644 tests/regression/78-termination/47-recursion-different-context-nonterminating.c create mode 100644 tests/regression/78-termination/48-dynamic-recursion-nonterminating.c create mode 100644 tests/regression/78-termination/49-longjmp.c create mode 100644 tests/regression/78-termination/50-decreasing-signed-int.c create mode 100644 tests/regression/78-termination/51-modulo.c diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 5635ebbeea..0208af7c7a 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -65,6 +65,9 @@ jobs: - name: Test apron regression (Mukherjee et. al SAS '17 paper') # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) run: ruby scripts/update_suite.rb group apron-mukherjee -s + - name: Test apron termination regression # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) + run: ruby scripts/update_suite.rb group termination -s + - name: Test regression cram run: opam exec -- dune runtest tests/regression diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e1648904c3..1d73e037f4 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -46,7 +46,7 @@ jobs: - name: Setup Pages id: pages - uses: actions/configure-pages@v3 + uses: actions/configure-pages@v4 - name: Install dependencies run: opam install . --deps-only --locked --with-doc @@ -68,4 +68,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v2 + uses: actions/deploy-pages@v3 diff --git a/.github/workflows/locked.yml b/.github/workflows/locked.yml index 65dfbe7bac..8604e7f52c 100644 --- a/.github/workflows/locked.yml +++ b/.github/workflows/locked.yml @@ -64,6 +64,9 @@ jobs: - name: Test apron regression (Mukherjee et. al SAS '17 paper') # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) run: ruby scripts/update_suite.rb group apron-mukherjee -s + - name: Test apron termination regression # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) + run: ruby scripts/update_suite.rb group termination -s + - name: Test regression cram run: opam exec -- dune runtest tests/regression diff --git a/.github/workflows/metadata.yml b/.github/workflows/metadata.yml index 6c7360f9e3..3a48d52fa0 100644 --- a/.github/workflows/metadata.yml +++ b/.github/workflows/metadata.yml @@ -27,6 +27,9 @@ jobs: args: --validate zenodo-validate: + # Zenodo schema URL is dead + if: ${{ false }} + strategy: matrix: node-version: diff --git a/.github/workflows/options.yml b/.github/workflows/options.yml index 94c49e4bf6..7ef8b6929e 100644 --- a/.github/workflows/options.yml +++ b/.github/workflows/options.yml @@ -26,10 +26,10 @@ jobs: run: npm install -g ajv-cli - name: Migrate schema # https://github.com/ajv-validator/ajv-cli/issues/199 - run: ajv migrate -s src/common/util/options.schema.json + run: ajv migrate -s src/config/options.schema.json - name: Validate conf - run: ajv validate -s src/common/util/options.schema.json -d "conf/**/*.json" + run: ajv validate -s src/config/options.schema.json -d "conf/**/*.json" - name: Validate incremental tests - run: ajv validate -s src/common/util/options.schema.json -d "tests/incremental/*/*.json" + run: ajv validate -s src/config/options.schema.json -d "tests/incremental/*/*.json" diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 6c23c7cdd4..57fa0cb6b5 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -92,6 +92,10 @@ jobs: if: ${{ matrix.apron }} run: ruby scripts/update_suite.rb group apron-mukherjee -s + - name: Test apron termination regression # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) + if: ${{ matrix.apron }} + run: ruby scripts/update_suite.rb group termination -s + - name: Test regression cram run: opam exec -- dune runtest tests/regression @@ -156,7 +160,8 @@ jobs: - name: Downgrade dependencies # must specify ocaml-base-compiler again to prevent it from being downgraded - run: opam install $(opam exec -- opam-0install --prefer-oldest goblint ocaml-variants.4.14.0+options ocaml-option-flambda) + # prevent num downgrade to avoid dune/jbuilder error: https://github.com/ocaml/dune/issues/5280 + run: opam install $(opam exec -- opam-0install --prefer-oldest goblint ocaml-variants.4.14.0+options ocaml-option-flambda num.1.4) - name: Build run: ./make.sh nat diff --git a/.gitignore b/.gitignore index 75bd23d36b..faf1513653 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,6 @@ linux-headers .goblint*/ goblint_temp_*/ -src/spec/graph .vagrant g2html.jar diff --git a/.mailmap b/.mailmap index 9153d55765..9aa2d0cc02 100644 --- a/.mailmap +++ b/.mailmap @@ -23,6 +23,7 @@ Kerem Çakırer Sarah Tilscher <66023521+stilscher@users.noreply.github.com> Karoliine Holter <44437975+karoliineh@users.noreply.github.com> + Elias Brandstetter <15275491+superbr4in@users.noreply.github.com> wherekonshade <80516286+Wherekonshade@users.noreply.github.com> @@ -37,3 +38,6 @@ Mireia Cano Pujol Felix Krayer Felix Krayer <91671586+FelixKrayer@users.noreply.github.com> Manuel Pietsch +Tim Ortel <100865202+TimOrtel@users.noreply.github.com> +Tomáš Dacík + <43824605+TDacik@users.noreply.github.com> diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 08044d195c..22f9c86121 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -20,4 +20,4 @@ build: - pip install json-schema-for-humans post_build: - mkdir _readthedocs/html/jsfh/ - - generate-schema-doc --config-file jsfh.yml src/common/util/options.schema.json _readthedocs/html/jsfh/ + - generate-schema-doc --config-file jsfh.yml src/config/options.schema.json _readthedocs/html/jsfh/ diff --git a/.zenodo.json b/.zenodo.json index 5557622f9e..22705c2d9c 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -10,15 +10,18 @@ }, { "name": "Schwarz, Michael", - "affiliation": "Technische Universität München" + "affiliation": "Technische Universität München", + "orcid": "0000-0002-9828-0308" }, { "name": "Erhard, Julian", - "affiliation": "Technische Universität München" + "affiliation": "Technische Universität München", + "orcid": "0000-0002-1729-3925" }, { "name": "Tilscher, Sarah", - "affiliation": "Technische Universität München" + "affiliation": "Technische Universität München", + "orcid": "0009-0009-9644-7475" }, { "name": "Vogler, Ralf", @@ -30,14 +33,16 @@ }, { "name": "Vojdani, Vesal", - "affiliation": "University of Tartu" + "affiliation": "University of Tartu", + "orcid": "0000-0003-4336-7980" } ], "contributors": [ { "name": "Seidl, Helmut", "type": "ProjectLeader", - "affiliation": "Technische Universität München" + "affiliation": "Technische Universität München", + "orcid": "0000-0002-2135-1593" }, { "name": "Schwarz, Martin D.", diff --git a/CHANGELOG.md b/CHANGELOG.md index 97cc399133..d285480259 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## v2.3.0 +Functionally equivalent to Goblint in SV-COMP 2024. + +* Add termination analysis for loops (#1093). +* Add memory out-of-bounds analysis (#1094, #1197). +* Add memory leak analysis (#1127, #1241, #1246). +* Add SV-COMP `termination`, `valid-memsafety` and `valid-memcleanup` properties support (#1220, #1228, #1201, #1199, #1259, #1262). +* Add YAML witness version 2.0 support (#1238, #1240, #1217, #1226, #1225, #1248). +* Add final warnings about unsound results (#1190, #1191). +* Add many library function specifications (#1167, #1174, #1203, #1205, #1212, #1220, #1239, #1242, #1244, #1254, #1269). +* Adapt automatic configuration tuning (#912, #921, #987, #1168, #1214, #1234). + ## v2.2.1 * Bump batteries lower bound to 3.5.0. * Fix flaky dead code elimination transformation test. diff --git a/CITATION.cff b/CITATION.cff index 7a2dcf188d..25d46cf762 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -12,12 +12,15 @@ authors: # same authors as in .zenodo.json and dune-project - given-names: Michael family-names: Schwarz affiliation: "Technische Universität München" + orcid: "https://orcid.org/0000-0002-9828-0308" - given-names: Julian family-names: Erhard affiliation: "Technische Universität München" + orcid: "https://orcid.org/0000-0002-1729-3925" - given-names: Sarah family-names: Tilscher affiliation: "Technische Universität München" + orcid: "https://orcid.org/0009-0009-9644-7475" - given-names: Ralf family-names: Vogler affiliation: "Technische Universität München" @@ -27,6 +30,7 @@ authors: # same authors as in .zenodo.json and dune-project - given-names: Vesal family-names: Vojdani affiliation: "University of Tartu" + orcid: "https://orcid.org/0000-0003-4336-7980" license: MIT repository-code: "https://github.com/goblint/analyzer" diff --git a/conf/examples/very-precise.json b/conf/examples/very-precise.json index 84cbf53585..2197335eaf 100644 --- a/conf/examples/very-precise.json +++ b/conf/examples/very-precise.json @@ -61,7 +61,9 @@ "structs" : { "domain" : "combined-sk" }, - "limit-string-addresses": false + "strings": { + "domain": "disjoint" + } } }, "exp": { diff --git a/conf/svcomp.json b/conf/svcomp.json index 73f99500b9..7e30554ceb 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -32,6 +32,15 @@ "thread", "threadJoins" ], + "path_sens": [ + "mutex", + "malloc_null", + "uninit", + "expsplit", + "activeSetjmp", + "memLeak", + "threadflag" + ], "context": { "widen": false }, @@ -52,7 +61,8 @@ "ldv_xmalloc", "ldv_xzalloc", - "ldv_calloc" + "ldv_calloc", + "ldv_kzalloc" ] }, "base": { @@ -60,6 +70,10 @@ "domain": "partitioned" } }, + "race": { + "free": false, + "call": false + }, "autotune": { "enabled": true, "activated": [ @@ -71,7 +85,9 @@ "octagon", "wideningThresholds", "loopUnrollHeuristic", - "memsafetySpecification" + "memsafetySpecification", + "termination", + "tmpSpecialAnalysis" ] } }, @@ -95,6 +111,33 @@ "enabled": true, "id": "enumerate", "unknown": false + }, + "yaml": { + "enabled": true, + "format-version": "2.0", + "entry-types": [ + "invariant_set" + ], + "invariant-types": [ + "loop_invariant" + ] + }, + "invariant": { + "loop-head": true, + "after-lock": false, + "other": false, + "accessed": false, + "exact": true, + "exclude-vars": [ + "tmp\\(___[0-9]+\\)?", + "cond", + "RETURN", + "__\\(cil_\\)?tmp_?[0-9]*\\(_[0-9]+\\)?", + ".*____CPAchecker_TMP_[0-9]+", + "__VERIFIER_assert__cond", + "__ksymtab_.*", + "\\(ldv_state_variable\\|ldv_timer_state\\|ldv_timer_list\\|ldv_irq_\\(line_\\|data_\\)?[0-9]+\\|ldv_retval\\)_[0-9]+" + ] } }, "pre": { diff --git a/conf/svcomp24-validate.json b/conf/svcomp24-validate.json new file mode 100644 index 0000000000..7832ffa6af --- /dev/null +++ b/conf/svcomp24-validate.json @@ -0,0 +1,140 @@ +{ + "ana": { + "sv-comp": { + "enabled": true, + "functions": true + }, + "int": { + "def_exc": true, + "enums": false, + "interval": true + }, + "float": { + "interval": true + }, + "activated": [ + "base", + "threadid", + "threadflag", + "threadreturn", + "mallocWrapper", + "mutexEvents", + "mutex", + "access", + "race", + "escape", + "expRelation", + "mhp", + "assert", + "var_eq", + "symb_locks", + "region", + "thread", + "threadJoins", + "unassume" + ], + "path_sens": [ + "mutex", + "malloc_null", + "uninit", + "expsplit", + "activeSetjmp", + "memLeak", + "threadflag" + ], + "context": { + "widen": false + }, + "malloc": { + "wrappers": [ + "kmalloc", + "__kmalloc", + "usb_alloc_urb", + "__builtin_alloca", + "kzalloc", + + "ldv_malloc", + + "kzalloc_node", + "ldv_zalloc", + "kmalloc_array", + "kcalloc", + + "ldv_xmalloc", + "ldv_xzalloc", + "ldv_calloc", + "ldv_kzalloc" + ] + }, + "base": { + "arrays": { + "domain": "partitioned" + } + }, + "race": { + "free": false, + "call": false + }, + "autotune": { + "enabled": true, + "activated": [ + "singleThreaded", + "mallocWrappers", + "noRecursiveIntervals", + "enums", + "congruence", + "octagon", + "wideningThresholds", + "loopUnrollHeuristic", + "memsafetySpecification", + "termination", + "tmpSpecialAnalysis" + ] + }, + "widen": { + "tokens": true + } + }, + "exp": { + "region-offsets": true + }, + "solver": "td3", + "sem": { + "unknown_function": { + "spawn": false + }, + "int": { + "signed_overflow": "assume_none" + }, + "null-pointer": { + "dereference": "assume_none" + } + }, + "witness": { + "graphml": { + "enabled": false + }, + "yaml": { + "enabled": false, + "strict": true, + "format-version": "2.0", + "entry-types": [ + "location_invariant", + "loop_invariant", + "invariant_set" + ], + "invariant-types": [ + "location_invariant", + "loop_invariant" + ] + }, + "invariant": { + "loop-head": true, + "after-lock": true, + "other": true + } + }, + "pre": { + "enabled": false + } +} diff --git a/conf/svcomp24.json b/conf/svcomp24.json new file mode 100644 index 0000000000..7e30554ceb --- /dev/null +++ b/conf/svcomp24.json @@ -0,0 +1,146 @@ +{ + "ana": { + "sv-comp": { + "enabled": true, + "functions": true + }, + "int": { + "def_exc": true, + "enums": false, + "interval": true + }, + "float": { + "interval": true + }, + "activated": [ + "base", + "threadid", + "threadflag", + "threadreturn", + "mallocWrapper", + "mutexEvents", + "mutex", + "access", + "race", + "escape", + "expRelation", + "mhp", + "assert", + "var_eq", + "symb_locks", + "region", + "thread", + "threadJoins" + ], + "path_sens": [ + "mutex", + "malloc_null", + "uninit", + "expsplit", + "activeSetjmp", + "memLeak", + "threadflag" + ], + "context": { + "widen": false + }, + "malloc": { + "wrappers": [ + "kmalloc", + "__kmalloc", + "usb_alloc_urb", + "__builtin_alloca", + "kzalloc", + + "ldv_malloc", + + "kzalloc_node", + "ldv_zalloc", + "kmalloc_array", + "kcalloc", + + "ldv_xmalloc", + "ldv_xzalloc", + "ldv_calloc", + "ldv_kzalloc" + ] + }, + "base": { + "arrays": { + "domain": "partitioned" + } + }, + "race": { + "free": false, + "call": false + }, + "autotune": { + "enabled": true, + "activated": [ + "singleThreaded", + "mallocWrappers", + "noRecursiveIntervals", + "enums", + "congruence", + "octagon", + "wideningThresholds", + "loopUnrollHeuristic", + "memsafetySpecification", + "termination", + "tmpSpecialAnalysis" + ] + } + }, + "exp": { + "region-offsets": true + }, + "solver": "td3", + "sem": { + "unknown_function": { + "spawn": false + }, + "int": { + "signed_overflow": "assume_none" + }, + "null-pointer": { + "dereference": "assume_none" + } + }, + "witness": { + "graphml": { + "enabled": true, + "id": "enumerate", + "unknown": false + }, + "yaml": { + "enabled": true, + "format-version": "2.0", + "entry-types": [ + "invariant_set" + ], + "invariant-types": [ + "loop_invariant" + ] + }, + "invariant": { + "loop-head": true, + "after-lock": false, + "other": false, + "accessed": false, + "exact": true, + "exclude-vars": [ + "tmp\\(___[0-9]+\\)?", + "cond", + "RETURN", + "__\\(cil_\\)?tmp_?[0-9]*\\(_[0-9]+\\)?", + ".*____CPAchecker_TMP_[0-9]+", + "__VERIFIER_assert__cond", + "__ksymtab_.*", + "\\(ldv_state_variable\\|ldv_timer_state\\|ldv_timer_list\\|ldv_irq_\\(line_\\|data_\\)?[0-9]+\\|ldv_retval\\)_[0-9]+" + ] + } + }, + "pre": { + "enabled": false + } +} diff --git a/docs/developer-guide/debugging.md b/docs/developer-guide/debugging.md index 7875a9b01e..5c1db12227 100644 --- a/docs/developer-guide/debugging.md +++ b/docs/developer-guide/debugging.md @@ -85,7 +85,7 @@ The configuration file has to be named `launch.json` and must reside in the `./. "configurations": [ { "name": "Goblint", - "type": "ocamlearlybird", + "type": "ocaml.earlybird", "request": "launch", "program": "${workspaceFolder}/goblint.byte", "arguments": [ @@ -97,7 +97,8 @@ The configuration file has to be named `launch.json` and must reside in the `./. ] } ``` -Note that the individual arguments to Goblint should be passed here as separate strings that do not contain spaces. +Note that the individual arguments to Goblint should be passed here as separate strings that do not contain spaces. Finally, to enable breakpoints uncomment `(map_workspace_root false)` in the dune-project file. + ### Running Goblint in the VS Code Debugger diff --git a/docs/developer-guide/firstanalysis.md b/docs/developer-guide/firstanalysis.md index 0923e792cd..4eb35e7f5d 100644 --- a/docs/developer-guide/firstanalysis.md +++ b/docs/developer-guide/firstanalysis.md @@ -67,7 +67,7 @@ The key part now is to define transfer functions for assignment. We only handle There is no need to implement the transfer functions for branching for this example; it only relies on lattice join operations to correctly take both paths into account. The assignment relies on the function `eval`, which is almost there. It just needs you to fix the evaluation of constants! Unless you jumped straight to this line, it should not be too complicated to fix this. -With this in place, we should have sufficient information to tell Goblint that the assertion does hold. +With this in place, we should have sufficient information to tell Goblint that the assertion does hold (run `make` to compile the updated analysis in Goblint). For more information on the signature of the individual transfer functions, please check out `module type Spec` documentation in [`src/framework/analyses.ml`](https://github.com/goblint/analyzer/blob/master/src/framework/analyses.ml). diff --git a/docs/developer-guide/messaging.md b/docs/developer-guide/messaging.md index 28f24bc49c..0028d51f87 100644 --- a/docs/developer-guide/messaging.md +++ b/docs/developer-guide/messaging.md @@ -47,16 +47,3 @@ The `~loc` argument is optional and defaults to the current location, but allows The `_noloc` suffixed functions allow general messages without any location (not even current). By convention, may-warnings (the usual case) should use warning severity and must-warnings should use error severity. - -### Spec analysis - -Warnings inside `.spec` files are converted to warnings. -They parsed from string warnings: the first space-delimited substring determines the category and the rest determines the text. - -For example: -``` -w1 "behavior.undefined.use_after_free" -w2 "integer.overflow" -w3 "unknown my message" -w4 "integer.overflow some text describing the warning" -``` diff --git a/docs/developer-guide/releasing.md b/docs/developer-guide/releasing.md index 69ffcb2461..fcf69ea533 100644 --- a/docs/developer-guide/releasing.md +++ b/docs/developer-guide/releasing.md @@ -37,13 +37,11 @@ 2. Extract distribution archive. 3. Run Docker container in extracted directory: `docker run -it --rm -v $(pwd):/goblint ocaml/opam:ubuntu-22.04-ocaml-4.14` (or newer). 4. Navigate to distribution archive inside Docker container: `cd /goblint`. - 5. Pin package from distribution archive: `opam pin add --no-action .`. - 6. Install depexts: `opam depext --with-test goblint`. - 7. Install and test package: `opam install --with-test goblint`. - 8. Activate opam environment: `eval $(opam env)`. - 9. Check version: `goblint --version`. - 10. Check that analysis works: `goblint -v tests/regression/04-mutex/01-simple_rc.c`. - 11. Exit Docker container. + 5. Install and test package from distribution archive: `opam-2.1 install --with-test .`. + 6. Activate opam environment: `eval $(opam env)`. + 7. Check version: `goblint --version`. + 8. Check that analysis works: `goblint -v tests/regression/04-mutex/01-simple_rc.c`. + 9. Exit Docker container. 12. Temporarily enable Zenodo GitHub webhook. @@ -59,6 +57,7 @@ 15. Create an opam package: `dune-release opam pkg`. 16. Submit the opam package to opam-repository: `dune-release opam submit`. +17. Revert temporary removal of opam pins. ## SV-COMP @@ -97,16 +96,17 @@ This ensures that the environment and the archive have all the correct system libraries. -6. Commit and push the archive to an SV-COMP archives repository branch (but don't open a MR yet): (SV-COMP 2023). -7. Check pushed archive via CoveriTeam-Remote: . +6. Create (or add new version) Zenodo artifact and upload the archive. - 1. Clone coveriteam repository. - 2. Locally modify `actors/goblint.yml` archive location to the raw URL of the pushed archive. - 3. Run Goblint on some sv-benchmarks and properties via CoveriTeam. +7. Open MR with Zenodo version DOI to the [fm-tools](https://gitlab.com/sosy-lab/benchmarking/fm-tools) repository. - This ensures that Goblint runs on SoSy-Lab servers. + ### After all preruns diff --git a/docs/user-guide/configuring.md b/docs/user-guide/configuring.md index 9a32a14a4c..cae57fc8cd 100644 --- a/docs/user-guide/configuring.md +++ b/docs/user-guide/configuring.md @@ -24,7 +24,7 @@ In `.vscode/settings.json` add the following: "/conf/*.json", "/tests/incremental/*/*.json" ], - "url": "/src/common/util/options.schema.json" + "url": "/src/config/options.schema.json" } ] } diff --git a/dune-project b/dune-project index 4a9cd8e3c1..37e81f4199 100644 --- a/dune-project +++ b/dune-project @@ -1,4 +1,4 @@ -(lang dune 3.6) +(lang dune 3.7) (using dune_site 0.1) (cram enable) (name goblint) @@ -24,8 +24,8 @@ (synopsis "Static analysis framework for C") (depends (ocaml (>= 4.10)) - (goblint-cil (>= 2.0.2)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. - (batteries (>= 3.5.0)) + (goblint-cil (>= 2.0.3)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. + (batteries (>= 3.5.1)) (zarith (>= 1.8)) (yojson (>= 2.0.0)) (qcheck-core (>= 0.19)) @@ -64,3 +64,5 @@ (share lib) (share conf)) ) + +; (map_workspace_root false) ;uncomment to enable breakpoints diff --git a/goblint.opam b/goblint.opam index bf51924626..b5f1f360dc 100644 --- a/goblint.opam +++ b/goblint.opam @@ -19,10 +19,10 @@ homepage: "https://goblint.in.tum.de" doc: "https://goblint.readthedocs.io/en/latest/" bug-reports: "https://github.com/goblint/analyzer/issues" depends: [ - "dune" {>= "3.6"} + "dune" {>= "3.7"} "ocaml" {>= "4.10"} - "goblint-cil" {>= "2.0.2"} - "batteries" {>= "3.5.0"} + "goblint-cil" {>= "2.0.3"} + "batteries" {>= "3.5.1"} "zarith" {>= "1.8"} "yojson" {>= "2.0.0"} "qcheck-core" {>= "0.19"} @@ -75,7 +75,8 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ - [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#c7ffc37ad83216a84d90fdbf427cc02a68ea5331" ] + # published goblint-cil 2.0.3 is currently up-to-date, so no pin needed + # [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#d2760bacfbfdb25a374254de44f2ff1cb5f42abd" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] ] diff --git a/goblint.opam.locked b/goblint.opam.locked index 2744d2fe92..02eac0bb75 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -50,16 +50,16 @@ depends: [ "cpu" {= "2.0.0"} "csexp" {= "1.5.1"} "ctypes" {= "0.20.1"} - "dune" {= "3.6.1"} - "dune-build-info" {= "3.6.1"} - "dune-configurator" {= "3.6.1"} - "dune-private-libs" {= "3.6.1"} - "dune-site" {= "3.6.1"} - "dyn" {= "3.6.1"} + "dune" {= "3.7.1"} + "dune-build-info" {= "3.7.1"} + "dune-configurator" {= "3.7.1"} + "dune-private-libs" {= "3.7.1"} + "dune-site" {= "3.7.1"} + "dyn" {= "3.7.1"} "fileutils" {= "0.6.4"} "fmt" {= "0.9.0"} "fpath" {= "0.7.3"} - "goblint-cil" {= "2.0.2"} + "goblint-cil" {= "2.0.3"} "integers" {= "0.7.0"} "json-data-encoding" {= "0.12.1"} "jsonrpc" {= "1.15.0~5.0preview1"} @@ -76,7 +76,7 @@ depends: [ "ocamlfind" {= "1.9.5"} "odoc" {= "2.2.0" & with-doc} "odoc-parser" {= "2.0.0" & with-doc} - "ordering" {= "3.6.1"} + "ordering" {= "3.7.1"} "ounit2" {= "2.2.6" & with-test} "pp" {= "1.1.2"} "ppx_derivers" {= "1.2.1"} @@ -93,7 +93,7 @@ depends: [ "sexplib0" {= "v0.15.1"} "sha" {= "1.15.2"} "stdlib-shims" {= "0.3.0"} - "stdune" {= "3.6.1"} + "stdune" {= "3.7.1"} "stringext" {= "1.6.0"} "topkg" {= "1.0.6"} "tyxml" {= "4.5.0" & with-doc} @@ -130,10 +130,6 @@ post-messages: [ ] # TODO: manually reordered to avoid opam pin crash: https://github.com/ocaml/opam/issues/4936 pin-depends: [ - [ - "goblint-cil.2.0.2" - "git+https://github.com/goblint/cil.git#c7ffc37ad83216a84d90fdbf427cc02a68ea5331" - ] [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" diff --git a/goblint.opam.template b/goblint.opam.template index d8e25cde38..ca2796b3c7 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -2,7 +2,8 @@ # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ - [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#c7ffc37ad83216a84d90fdbf427cc02a68ea5331" ] + # published goblint-cil 2.0.3 is currently up-to-date, so no pin needed + # [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#d2760bacfbfdb25a374254de44f2ff1cb5f42abd" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] ] diff --git a/gobview b/gobview index 42b07f8253..3de13d7412 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit 42b07f825316052ec030370daf0d00ebe28ec092 +Subproject commit 3de13d74124ab7bc30d8be299f02570d8f498b84 diff --git a/lib/goblint/runtime/include/goblint.h b/lib/goblint/runtime/include/goblint.h index b0af41616e..af87035d33 100644 --- a/lib/goblint/runtime/include/goblint.h +++ b/lib/goblint/runtime/include/goblint.h @@ -6,3 +6,5 @@ void __goblint_assume_join(/* pthread_t thread */); // undeclared argument to av void __goblint_split_begin(int exp); void __goblint_split_end(int exp); + +void __goblint_bounded(unsigned long long exp); \ No newline at end of file diff --git a/lib/goblint/runtime/src/goblint.c b/lib/goblint/runtime/src/goblint.c index bc176f93a6..cbcb7cf505 100644 --- a/lib/goblint/runtime/src/goblint.c +++ b/lib/goblint/runtime/src/goblint.c @@ -27,4 +27,8 @@ void __goblint_split_begin(int exp) { void __goblint_split_end(int exp) { +} + +void __goblint_bounded(unsigned long long exp) { + } \ No newline at end of file diff --git a/scripts/goblint-lib-modules.py b/scripts/goblint-lib-modules.py index 5f02271616..ec0e78e440 100755 --- a/scripts/goblint-lib-modules.py +++ b/scripts/goblint-lib-modules.py @@ -30,20 +30,18 @@ "MessagesCompare", "PrivPrecCompare", "ApronPrecCompare", - "Mainspec", # libraries "Goblint_std", "Goblint_timing", "Goblint_backtrace", + "Goblint_tracing", "Goblint_sites", "Goblint_build_info", "Dune_build_info", "MessageCategory", # included in Messages "PreValueDomain", # included in ValueDomain - "SpecCore", # spec stuff - "SpecUtil", # spec stuff "ConfigVersion", "ConfigProfile", diff --git a/scripts/regression2sv-benchmarks.py b/scripts/regression2sv-benchmarks.py index 8f74a70f52..7bcc1c7ea3 100755 --- a/scripts/regression2sv-benchmarks.py +++ b/scripts/regression2sv-benchmarks.py @@ -31,7 +31,6 @@ "09-regions_34-escape_rc", # duplicate of 04/45 "09-regions_35-list2_rc-offsets-thread", # duplicate of 09/03 "10-synch_17-glob_fld_nr", # duplicate of 05/08 - "19-spec_02-mutex_rc", # duplicate of 04/01 "29-svcomp_01-race-2_3b-container_of", # duplicate sv-benchmarks "29-svcomp_01-race-2_4b-container_of", # duplicate sv-benchmarks diff --git a/scripts/spec/check.sh b/scripts/spec/check.sh deleted file mode 100755 index 57b63edfd2..0000000000 --- a/scripts/spec/check.sh +++ /dev/null @@ -1,27 +0,0 @@ -export OCAMLRUNPARAM=b -# file to analyze -file=${1-"tests/file.c"} -# analysis to run or spec file -ana=${2-"tests/regression/18-file/file.optimistic.spec"} -debug=${debug-"true"} -if [ $ana == "file" ]; then - ana="file" - opt="--set ana.file.optimistic true" -else - spec=$ana - ana="spec" - opt="--set ana.spec.file $spec" -fi -cmd="./goblint --set ana.activated[0][+] $ana $opt --html --set warn.debug $debug $file" -echo -e "$(tput setaf 6)$cmd$(tput sgr 0)" -$cmd - - -# # focuses Firefox and reloads current tab -# if false && command -v xdotool >/dev/null 2>&1; then -# WID=`xdotool search --name "Mozilla Firefox" | head -1` -# xdotool windowactivate $WID -# #xdotool key F5 -# # reload is done by add-on Auto Reload (reload result/* on change of report.html) -# # https://addons.mozilla.org/en-US/firefox/addon/auto-reload/?src=api -# fi diff --git a/scripts/spec/regression.py b/scripts/spec/regression.py deleted file mode 100755 index dc9f9fa276..0000000000 --- a/scripts/spec/regression.py +++ /dev/null @@ -1,61 +0,0 @@ -# import fileinput -# for line in fileinput.input(): -# pass - -import sys, os -import re - -if len(sys.argv) != 2: - print("Stdin: output from goblint, 1. argument: C source-file") - sys.exit(1) -path = sys.argv[1] - -goblint = {} -for line in sys.stdin.readlines(): - line = re.sub(r"\033.*?m", "", line) - m = re.match(r"(.+) \("+re.escape(path)+":(.+)\)", line) - if m: goblint[int(m.group(2))] = m.group(1) - -source = {} -lines = open(path).readlines() -for i,line in zip(range(1, len(lines)+1), lines): - m = re.match(r".+ // WARN: (.+)", line) - if m: source[i] = m.group(1) - -diff = {}; -for k,v in sorted(set.union(set(goblint.items()), set(source.items()))): - if k in diff: continue - if k in goblint and k in source and goblint[k]!=source[k]: - diff[k] = ('D', [goblint[k], source[k]]) - elif (k,v) in goblint.items() and (k,v) not in source.items(): - diff[k] = ('G', [goblint[k]]) - elif (k,v) not in goblint.items() and (k,v) in source.items(): - diff[k] = ('S', [source[k]]) - -if not len(diff): - sys.exit(0) - -print("#"*50) -print(path) -print("file://"+os.getcwd()+"/result/"+os.path.basename(path)+".html") - -if len(goblint): - print("## Goblint warnings:") - for k,v in sorted(goblint.items()): - print("{} \t {}".format(k, v)) - print - -if len(source): - print("## Source warnings:") - for k,v in source.items(): - print("{} \t {}".format(k, v)) - print - -if len(diff): - print("## Diff (G..only goblint, S..only source, D..different):") - for k,(s,v) in sorted(diff.items()): - print("{} {} \t {}".format(s, k, v[0])) - for v in v[1:]: print("\t {}".format(v)) - -print -sys.exit(1) \ No newline at end of file diff --git a/scripts/spec/regression.sh b/scripts/spec/regression.sh deleted file mode 100755 index 6dc740ca75..0000000000 --- a/scripts/spec/regression.sh +++ /dev/null @@ -1,18 +0,0 @@ -debug_tmp=$debug -export debug=false # temporarily disable debug output -n=0 -c=0 -dir=${2-"tests/regression/18-file"} -for f in $dir/*.c; do - ./scripts/spec/check.sh $f ${1-"file"} 2>/dev/null | python scripts/spec/regression.py $f && ((c++)) - ((n++)) -done -debug=$debug_tmp -msg="passed $c/$n tests" -echo $msg -if [ $c -eq $n ]; then - exit 0 -else - notify-send -i stop "$msg" - exit 1 -fi diff --git a/scripts/spec/spec.sh b/scripts/spec/spec.sh deleted file mode 100755 index 03abe9a0c7..0000000000 --- a/scripts/spec/spec.sh +++ /dev/null @@ -1,10 +0,0 @@ -# print all states the parser goes through -#export OCAMLRUNPARAM='p' -bin=src/mainspec.native -spec=${1-"tests/regression/18-file/file.spec"} -ocamlbuild -yaccflag -v -X webapp -no-links -use-ocamlfind $bin \ - && (./_build/$bin $spec \ - || (echo "$spec failed, running interactive now..."; - rlwrap ./_build/$bin - ) - ) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index ca408a513a..2722b3ddb5 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -145,10 +145,11 @@ def collect_warnings @vars = $1 @evals = $2 end + if l =~ /\[Termination\]/ then warnings[-1] = "nonterm" end # Get Termination warning next unless l =~ /(.*)\(.*?\:(\d+)(?:\:\d+)?(?:-(?:\d+)(?:\:\d+)?)?\)/ obj,i = $1,$2.to_i - ranking = ["other", "warn", "race", "norace", "deadlock", "nodeadlock", "success", "fail", "unknown"] + ranking = ["other", "warn", "goto", "fundec", "loop", "term", "nonterm", "race", "norace", "deadlock", "nodeadlock", "success", "fail", "unknown"] thiswarn = case obj when /\(conf\. \d+\)/ then "race" when /Deadlock/ then "deadlock" @@ -159,6 +160,9 @@ def collect_warnings when /invariant confirmed/ then "success" when /invariant unconfirmed/ then "unknown" when /invariant refuted/ then "fail" + when /(Upjumping Goto)/ then "goto" + when /(Fundec \w+ is contained in a call graph cycle)/ then "fundec" + when /(Loop analysis)/ then "loop" when /^\[Warning\]/ then "warn" when /^\[Error\]/ then "warn" when /^\[Info\]/ then "warn" @@ -183,19 +187,33 @@ def compare_warnings if cond then @correct += 1 # full p.path is too long and p.name does not allow click to open in terminal - if todo.include? idx then puts "Excellent: ignored check on #{relpath(p.path).to_s.cyan}:#{idx.to_s.blue} is now passing!" end + if todo.include? idx + if idx < 0 + puts "Excellent: ignored check on #{relpath(p.path).to_s.cyan} for #{type.yellow} is now passing!" + else + puts "Excellent: ignored check on #{relpath(p.path).to_s.cyan}:#{idx.to_s.blue} is now passing!" + end + end else - if todo.include? idx then @ignored += 1 else - puts "Expected #{type.yellow}, but registered #{(warnings[idx] or "nothing").yellow} on #{p.name.cyan}:#{idx.to_s.blue}" - puts tests_line[idx].rstrip.gray - ferr = idx if ferr.nil? or idx < ferr + if todo.include? idx + @ignored += 1 + else + if idx < 0 # When non line specific keywords were used don't print a line + puts "Expected #{type.yellow}, but registered #{(warnings[idx] or "nothing").yellow} on #{p.name.cyan}" + else + puts "Expected #{type.yellow}, but registered #{(warnings[idx] or "nothing").yellow} on #{p.name.cyan}:#{idx.to_s.blue}" + puts tests_line[idx].rstrip.gray + ferr = idx if ferr.nil? or idx < ferr + end end end } case type - when "deadlock", "race", "fail", "unknown", "warn" + when "goto", "fundec", "loop", "deadlock", "race", "fail", "unknown", "warn" + check.call warnings[idx] == type + when "nonterm" check.call warnings[idx] == type - when "nowarn" + when "nowarn", "term" check.call warnings[idx].nil? when "assert", "success" check.call warnings[idx] == "success" @@ -294,6 +312,12 @@ def parse_tests (lines) tests[i] = "success" elsif obj =~ /FAIL/ then tests[i] = "fail" + elsif obj =~ /NONTERMLOOP/ then + tests[i] = "loop" + elsif obj =~ /NONTERMGOTO/ then + tests[i] = "goto" + elsif obj =~ /NONTERMFUNDEC/ then + tests[i] = "fundec" elsif obj =~ /UNKNOWN/ then tests[i] = "unknown" elsif obj =~ /(assert|__goblint_check).*\(/ then @@ -306,6 +330,15 @@ def parse_tests (lines) end end end + case lines[0] + when /NONTERM/ + tests[-1] = "nonterm" + when /TERM/ + tests[-1] = "term" + end + if lines[0] =~ /TODO/ then + todo << -1 + end Tests.new(self, tests, tests_line, todo) end diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index b181a1c70e..efad8b4c2e 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -29,7 +29,7 @@ struct let init _ = collect_local := get_bool "witness.yaml.enabled" && get_bool "witness.invariant.accessed"; let activated = get_string_list "ana.activated" in - emit_single_threaded := List.mem (ModifiedSinceLongjmp.Spec.name ()) activated || List.mem (PoisonVariables.Spec.name ()) activated + emit_single_threaded := List.mem (ModifiedSinceSetjmp.Spec.name ()) activated || List.mem (PoisonVariables.Spec.name ()) activated let do_access (ctx: (D.t, G.t, C.t, V.t) ctx) (kind:AccessKind.t) (reach:bool) (e:exp) = if M.tracing then M.trace "access" "do_access %a %a %B\n" d_exp e AccessKind.pretty kind reach; diff --git a/src/analyses/apron/affineEqualityAnalysis.apron.ml b/src/analyses/apron/affineEqualityAnalysis.apron.ml index 62576476b6..007bd91625 100644 --- a/src/analyses/apron/affineEqualityAnalysis.apron.ml +++ b/src/analyses/apron/affineEqualityAnalysis.apron.ml @@ -11,7 +11,6 @@ let spec_module: (module MCPSpec) Lazy.t = let module AD = AffineEqualityDomain.D2 (VectorMatrix.ArrayVector) (VectorMatrix.ArrayMatrix) in let module RD: RelationDomain.RD = struct - module Var = AffineEqualityDomain.Var module V = AffineEqualityDomain.V include AD end diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 29e295a662..0ba17cdb35 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -12,10 +12,9 @@ let spec_module: (module MCPSpec) Lazy.t = let module AD = (val if diff_box then (module ApronDomain.BoxProd (AD): ApronDomain.S3) else (module AD)) in let module RD: RelationDomain.RD = struct - module Var = ApronDomain.Var module V = ApronDomain.V include AD - type var = ApronDomain.Var.t + type var = Apron.Var.t end in let module Priv = (val RelationPriv.get_priv ()) in diff --git a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml index 811974db14..c79ed2898d 100644 --- a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml +++ b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml @@ -14,7 +14,6 @@ let spec_module: (module MCPSpec) Lazy.t = let module AD = LinearTwoVarEqualityDomain.D2 in let module RD: RelationDomain.RD = struct - module Var = LinearTwoVarEqualityDomain.Var module V = LinearTwoVarEqualityDomain.V include AD end diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 13f549fc44..5e128ffc30 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -70,7 +70,7 @@ struct let visitor = object inherit nopCilVisitor method! vlval = function - | (Var v, NoOffset) when v.vglob || ThreadEscape.has_escaped ask v -> + | (Var v, NoOffset) when (v.vglob || ThreadEscape.has_escaped ask v) && RD.Tracked.varinfo_tracked v -> let v_in = if VH.mem v_ins v then VH.find v_ins v @@ -196,7 +196,7 @@ struct let assert_type_bounds ask rel x = assert (RD.Tracked.varinfo_tracked x); match Cilfacade.get_ikind x.vtype with - | ik when not (IntDomain.should_ignore_overflow ik) -> (* don't add type bounds for signed when assume_none *) + | ik -> let (type_min, type_max) = IntDomain.Size.range ik in (* TODO: don't go through CIL exp? *) let e1 = BinOp (Le, Lval (Cil.var x), (Cil.kintegerCilint ik type_max), intType) in @@ -204,7 +204,6 @@ struct let rel = RD.assert_inv rel e1 false (no_overflow ask e1) in (* TODO: how can be overflow when asserting type bounds? *) let rel = RD.assert_inv rel e2 false (no_overflow ask e2) in rel - | _ | exception Invalid_argument _ -> rel @@ -284,8 +283,9 @@ struct let pass_to_callee fundec any_local_reachable var = (* TODO: currently, we pass all locals of the caller to the callee, provided one of them is reachbale to preserve relationality *) (* there should be smarter ways to do this, e.g. by keeping track of which values are written etc. ... *) + (* See, e.g, Beckschulze E, Kowalewski S, Brauer J (2012) Access-based localization for octagons. Electron Notes Theor Comput Sci 287:29–40 *) (* Also, a local *) - let vname = RD.Var.to_string var in + let vname = Apron.Var.to_string var in let locals = fundec.sformals @ fundec.slocals in match List.find_opt (fun v -> VM.var_name (Local v) = vname) locals with (* TODO: optimize *) | None -> true @@ -296,8 +296,7 @@ struct let st = ctx.local in let arg_assigns = GobList.combine_short f.sformals args (* TODO: is it right to ignore missing formals/args? *) - |> List.filter (fun (x, _) -> RD.Tracked.varinfo_tracked x) - |> List.map (Tuple2.map1 RV.arg) + |> List.filter_map (fun (x, e) -> if RD.Tracked.varinfo_tracked x then Some (RV.arg x, e) else None) in let arg_vars = List.map fst arg_assigns in let new_rel = RD.add_vars st.rel arg_vars in @@ -319,7 +318,7 @@ struct RD.remove_filter_with new_rel (fun var -> match RV.find_metadata var with | Some (Local _) when not (pass_to_callee fundec any_local_reachable var) -> true (* remove caller locals provided they are unreachable *) - | Some (Arg _) when not (List.mem_cmp RD.Var.compare var arg_vars) -> true (* remove caller args, but keep just added args *) + | Some (Arg _) when not (List.mem_cmp Apron.Var.compare var arg_vars) -> true (* remove caller args, but keep just added args *) | _ -> false (* keep everything else (just added args, globals, global privs) *) ); if M.tracing then M.tracel "combine" "relation enter newd: %a\n" RD.pretty new_rel; @@ -405,7 +404,7 @@ struct in let any_local_reachable = any_local_reachable fundec reachable_from_args in let arg_vars = f.sformals |> List.filter (RD.Tracked.varinfo_tracked) |> List.map RV.arg in - if M.tracing then M.tracel "combine" "relation remove vars: %a\n" (docList (fun v -> Pretty.text (RD.Var.to_string v))) arg_vars; + if M.tracing then M.tracel "combine" "relation remove vars: %a\n" (docList (fun v -> Pretty.text (Apron.Var.to_string v))) arg_vars; RD.remove_vars_with new_fun_rel arg_vars; (* fine to remove arg vars that also exist in caller because unify from new_rel adds them back with proper constraints *) let tainted = f_ask.f Queries.MayBeTainted in let tainted_vars = TaintPartialContexts.conv_varset tainted in diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index b386af162b..a51fc3545f 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -195,8 +195,7 @@ struct end module AV = struct - include RelationDomain.VarMetadataTbl (VM) (RD.Var) - + include RelationDomain.VarMetadataTbl (VM) let local g = make_var (Local g) let unprot g = make_var (Unprot g) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a8ad9af95b..7c741e227e 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1139,6 +1139,9 @@ struct (* interpreter end *) + let is_not_alloc_var ctx v = + not (ctx.ask (Queries.IsAllocVar v)) + let is_not_heap_alloc_var ctx v = let is_alloc = ctx.ask (Queries.IsAllocVar v) in not is_alloc || (is_alloc && not (ctx.ask (Queries.IsHeapVar v))) @@ -1277,7 +1280,7 @@ struct (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) (* If we're asking for the BlobSize from the base address, then don't check for offsets => we want to avoid getting bot *) if AD.exists (function - | Addr (v,o) -> is_not_heap_alloc_var ctx v || (if not from_base_addr then o <> `NoOffset else false) + | Addr (v,o) -> is_not_alloc_var ctx v || (if not from_base_addr then o <> `NoOffset else false) | _ -> false) a then Queries.Result.bot q else ( @@ -1289,9 +1292,15 @@ struct else a in - let r = get ~full:true (Analyses.ask_of_ctx ctx) ctx.global ctx.local a None in + let r = get ~full:true (Analyses.ask_of_ctx ctx) ctx.global ctx.local a None in (* ignore @@ printf "BlobSize %a = %a\n" d_plainexp e VD.pretty r; *) (match r with + | Array a -> + (* unroll into array for Calloc calls *) + (match ValueDomain.CArrays.get (Queries.to_value_domain_ask (Analyses.ask_of_ctx ctx)) a (None, (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.zero)) with + | Blob (_,s,_) -> `Lifted s + | _ -> Queries.Result.top q + ) | Blob (_,s,_) -> `Lifted s | _ -> Queries.Result.top q) ) @@ -1477,7 +1486,7 @@ struct Priv.read_global a priv_getg st x in let new_value = update_offset old_value in - M.tracel "hgh" "update_offset %a -> %a\n" VD.pretty old_value VD.pretty new_value; + if M.tracing then M.tracel "set" "update_offset %a -> %a\n" VD.pretty old_value VD.pretty new_value; let r = Priv.write_global ~invariant a priv_getg (priv_sideg ctx.sideg) st x new_value in if M.tracing then M.tracel "set" ~var:x.vname "update_one_addr: updated a global var '%s' \nstate:%a\n\n" x.vname D.pretty r; r @@ -2123,7 +2132,7 @@ struct let invalidate_ret_lv st = match lv with | Some lv -> if M.tracing then M.tracel "invalidate" "Invalidating lhs %a for function call %s\n" d_plainlval lv f.vname; - invalidate ~ctx (Analyses.ask_of_ctx ctx) ctx.global st [Cil.mkAddrOrStartOf lv] + invalidate ~deep:false ~ctx (Analyses.ask_of_ctx ctx) ctx.global st [Cil.mkAddrOrStartOf lv] | None -> st in let addr_type_of_exp exp = @@ -2328,6 +2337,24 @@ struct | _ -> failwith ("non-floating-point argument in call to function "^f.vname) end in + let apply_abs ik x = + let eval_x = eval_rv (Analyses.ask_of_ctx ctx) gs st x in + begin match eval_x with + | Int int_x -> + let xcast = ID.cast_to ik int_x in + (* the absolute value of the most-negative value is out of range for 2'complement types *) + (match (ID.minimal xcast), (ID.minimal (ID.top_of ik)) with + | _, None + | None, _ -> ID.top_of ik + | Some mx, Some mm when Z.equal mx mm -> ID.top_of ik + | _, _ -> + let x1 = ID.neg (ID.meet (ID.ending ik Z.zero) xcast) in + let x2 = ID.meet (ID.starting ik Z.zero) xcast in + ID.join x1 x2 + ) + | _ -> failwith ("non-integer argument in call to function "^f.vname) + end + in let result:value = begin match fun_args with | Nan (fk, str) when Cil.isPointerType (Cilfacade.typeOf str) -> Float (FD.nan_of fk) @@ -2356,6 +2383,8 @@ struct | Isunordered (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.unordered x y)) | Fmax (fd, x ,y) -> Float (apply_binary fd FD.fmax x y) | Fmin (fd, x ,y) -> Float (apply_binary fd FD.fmin x y) + | Sqrt (fk, x) -> Float (apply_unary fk FD.sqrt x) + | Abs (ik, x) -> Int (ID.cast_to ik (apply_abs ik x)) end in begin match lv with diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 72e00efbb1..304d3e55ad 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -709,18 +709,22 @@ struct | _ -> Int c in (* handle special calls *) - 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))); - let tv_opt = ID.to_bool c in - begin match tv_opt with + begin match x, t with + | (Var v, offs), TInt (ik, _) -> + let tmpSpecial = ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) in + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty tmpSpecial; + begin match tmpSpecial with + | `Lifted (Abs (ik, xInt)) -> + let c' = ID.cast_to ik c in (* different ik! *) + inv_exp (Int (ID.join c' (ID.neg c'))) xInt st + | tmpSpecial -> + begin match ID.to_bool c with | Some tv -> - begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with + begin match tmpSpecial with | `Lifted (Isfinite xFloat) when tv -> inv_exp (Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st | `Lifted (Isnan xFloat) when tv -> inv_exp (Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st (* should be correct according to C99 standard*) + (* The following do to_bool and of_bool to convert Not{0} into 1 for downstream float inversions *) | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st @@ -730,9 +734,8 @@ struct end | None -> update_lval c x c' ID.pretty end - | _ -> update_lval c x c' ID.pretty end - | _ -> update_lval c x c' ID.pretty + | _, _ -> update_lval c x c' ID.pretty end | Float c -> let c' = match t with @@ -744,22 +747,19 @@ struct | _ -> Float c in (* handle special calls *) - 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))); - 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)) -> - let inv = FD.inv_fabs (FD.cast_to ret_fk c) in - if FD.is_bot inv then - raise Analyses.Deadcode - else - inv_exp (Float inv) xFloat st - | _ -> update_lval c x c' FD.pretty - end + begin match x, t with + | (Var v, offs), TFloat (fk, _) -> + let tmpSpecial = ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) in + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty tmpSpecial; + begin match tmpSpecial 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)) -> + let inv = FD.inv_fabs (FD.cast_to ret_fk c) in + if FD.is_bot inv then + raise Analyses.Deadcode + else + inv_exp (Float inv) xFloat st | _ -> update_lval c x c' FD.pretty end | _ -> update_lval c x c' FD.pretty diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 3843dda300..f9a4a22f44 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -211,12 +211,12 @@ struct let thread_join ?(force=false) ask get e st = st let thread_return ask get set tid st = st - let invariant_global getg g = - match g with - | `Left _ -> (* mutex *) - Invariant.none + let invariant_global getg = function | `Right g' -> (* global *) ValueDomain.invariant_global (read_unprotected_global getg) g' + | _ -> (* mutex *) + Invariant.none + end module PerMutexOplusPriv: S = @@ -230,7 +230,7 @@ struct CPA.find x st.cpa (* let read_global ask getg cpa x = let (cpa', v) as r = read_global ask getg cpa x in - ignore (Pretty.printf "READ GLOBAL %a (%a, %B) = %a\n" CilType.Varinfo.pretty x CilType.Location.pretty !Tracing.current_loc (is_unprotected ask x) VD.pretty v); + ignore (Pretty.printf "READ GLOBAL %a (%a, %B) = %a\n" CilType.Varinfo.pretty x CilType.Location.pretty !Goblint_tracing.current_loc (is_unprotected ask x) VD.pretty v); r *) let write_global ?(invariant=false) ask getg sideg (st: BaseComponents (D).t) x v = let cpa' = CPA.add x v st.cpa in @@ -625,13 +625,11 @@ struct let get_mutex_inits' = CPA.find x get_mutex_inits in VD.join get_mutex_global_x' get_mutex_inits' - let invariant_global getg g = - match g with - | `Left (`Left _) -> (* mutex *) - Invariant.none - | `Left (`Right g') -> (* global *) - ValueDomain.invariant_global (read_unprotected_global getg) g' - | `Right _ -> (* thread *) + let invariant_global getg = function + | `Middle g -> (* global *) + ValueDomain.invariant_global (read_unprotected_global getg) g + | `Left _ + | `Right _ -> (* mutex or thread *) Invariant.none end @@ -847,16 +845,15 @@ struct open Locksets - let invariant_global getg g = - match g with - | `Left _ -> (* mutex *) - Invariant.none + let invariant_global getg = function | `Right g' -> (* global *) ValueDomain.invariant_global (fun x -> GWeak.fold (fun s' tm acc -> WeakRange.fold_weak VD.join tm acc ) (G.weak (getg (V.global x))) (VD.bot ()) ) g' + | _ -> (* mutex *) + Invariant.none let invariant_vars ask getg st = let module VS = Set.Make (CilType.Varinfo) in @@ -1668,7 +1665,7 @@ struct let read_global ask getg st x = let v = Priv.read_global ask getg st x in if !AnalysisState.postsolving && !is_dumping then - LVH.modify_def (VD.bot ()) (!Tracing.current_loc, x) (VD.join v) lvh; + LVH.modify_def (VD.bot ()) (!Goblint_tracing.current_loc, x) (VD.join v) lvh; v let dump () = diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 88181000b9..73a2e75de1 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -85,11 +85,10 @@ struct end module V = struct - (* TODO: Either3? *) - include Printable.Either (struct include Printable.Either (VMutex) (VMutexInits) let name () = "mutex" end) (VGlobal) + include Printable.Either3 (VMutex) (VMutexInits) (VGlobal) let name () = "MutexGlobals" - let mutex x: t = `Left (`Left x) - let mutex_inits: t = `Left (`Right ()) + let mutex x: t = `Left x + let mutex_inits: t = `Middle () let global x: t = `Right x end diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index f084a21edb..8412a65683 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -220,7 +220,7 @@ module Tbls = struct let make_new_val table k = (* TODO: all same key occurrences instead *) let line = -5 - all_keys_count table in - let loc = { !Tracing.current_loc with line } in + let loc = { !Goblint_tracing.current_loc with line } in MyCFG.Statement { (mkStmtOneInstr @@ Set (var dummyFunDec.svar, zero, loc, loc)) with sid = new_sid () diff --git a/src/analyses/fileUse.ml b/src/analyses/fileUse.ml deleted file mode 100644 index 58257b7843..0000000000 --- a/src/analyses/fileUse.ml +++ /dev/null @@ -1,296 +0,0 @@ -(** Analysis of correct file handle usage ([file]). - - @see Vogler, R. Verifying Regular Safety Properties of C Programs Using the Static Analyzer Goblint. Section 3.*) - -open Batteries -open GoblintCil -open Analyses - -module Spec = -struct - include Analyses.DefaultSpec - - let name () = "file" - module D = FileDomain.Dom - module C = FileDomain.Dom - - (* special variables *) - let return_var = Cilfacade.create_var @@ Cil.makeVarinfo false "@return" Cil.voidType, `NoOffset - let unclosed_var = Cilfacade.create_var @@ Cil.makeVarinfo false "@unclosed" Cil.voidType, `NoOffset - - (* keys that were already warned about; needed for multiple returns (i.e. can't be kept in D) *) - let warned_unclosed = ref Set.empty - - (* queries *) - let query ctx (type a) (q: a Queries.t) = - match q with - | Queries.MayPointTo exp -> if M.tracing then M.tracel "file" "query MayPointTo: %a" d_plainexp exp; Queries.Result.top q - | _ -> Queries.Result.top q - - let query_ad (ask: Queries.ask) exp = - match ask.f (Queries.MayPointTo exp) with - | ad when not (Queries.AD.is_top ad) -> Queries.AD.elements ad - | _ -> [] - let print_query_lv ?msg:(msg="") ask exp = - let addrs = query_ad ask exp in (* MayPointTo -> LValSet *) - let pretty_key = function - | Queries.AD.Addr.Addr (v,o) -> Pretty.text (D.string_of_key (v, ValueDomain.Addr.Offs.to_exp o)) - | _ -> Pretty.text "" in - if M.tracing then M.tracel "file" "%s MayPointTo %a = [%a]" msg d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) addrs - - let eval_fv ask exp: varinfo option = - match query_ad ask exp with - | [addr] -> Queries.AD.Addr.to_var_may addr - | _ -> None - - - (* transfer functions *) - let assign ctx (lval:lval) (rval:exp) : D.t = - let m = ctx.local in - (* ignore(printf "%a = %a\n" d_plainlval lval d_plainexp rval); *) - let saveOpened ?unknown:(unknown=false) k m = (* save maybe opened files in the domain to warn about maybe unclosed files at the end *) - if D.may k D.opened m && not (D.is_unknown k m) then (* if unknown we don't have any location for the warning and have handled it already anyway *) - let mustOpen, mayOpen = D.filter_records k D.opened m in - let mustOpen, mayOpen = if unknown then Set.empty, mayOpen else mustOpen, Set.diff mayOpen mustOpen in - D.extend_value unclosed_var (mustOpen, mayOpen) m - else m - in - let key_from_exp = function - | Lval x -> Some (D.key_from_lval x) - | _ -> None - in - match key_from_exp (Lval lval), key_from_exp (stripCasts rval) with (* we just care about Lval assignments *) - | Some k1, Some k2 when k1=k2 -> m (* do nothing on self-assignment *) - | Some k1, Some k2 when D.mem k1 m && D.mem k2 m -> (* both in D *) - if M.tracing then M.tracel "file" "assign (both in D): %s = %s" (D.string_of_key k1) (D.string_of_key k2); - saveOpened k1 m |> D.remove' k1 |> D.alias k1 k2 - | Some k1, Some k2 when D.mem k1 m -> (* only k1 in D *) - if M.tracing then M.tracel "file" "assign (only k1 in D): %s = %s" (D.string_of_key k1) (D.string_of_key k2); - saveOpened k1 m |> D.remove' k1 - | Some k1, Some k2 when D.mem k2 m -> (* only k2 in D *) - if M.tracing then M.tracel "file" "assign (only k2 in D): %s = %s" (D.string_of_key k1) (D.string_of_key k2); - D.alias k1 k2 m - | Some k1, _ when D.mem k1 m -> (* k1 in D and assign something unknown *) - if M.tracing then M.tracel "file" "assign (only k1 in D): %s = %a" (D.string_of_key k1) d_exp rval; - D.warn @@ "[Unsound]changed pointer "^D.string_of_key k1^" (no longer safe)"; - saveOpened ~unknown:true k1 m |> D.unknown k1 - | _ -> (* no change in D for other things *) - if M.tracing then M.tracel "file" "assign (none in D): %a = %a [%a]" d_lval lval d_exp rval d_plainexp rval; - m - - let branch ctx (exp:exp) (tv:bool) : D.t = - let m = ctx.local in - (* ignore(printf "if %a = %B (line %i)\n" d_plainexp exp tv (!Tracing.current_loc).line); *) - let check a b tv = - (* ignore(printf "check: %a = %a, %B\n" d_plainexp a d_plainexp b tv); *) - match a, b with - | Const (CInt(i, kind, str)), Lval lval - | Lval lval, Const (CInt(i, kind, str)) -> - (* ignore(printf "branch(%s==%i, %B)\n" v.vname (Int64.to_int i) tv); *) - let k = D.key_from_lval lval in - if Z.compare i Z.zero = 0 && tv then ( - (* ignore(printf "error-branch\n"); *) - D.error k m - )else - D.success k m - | _ -> M.debug ~category:Analyzer "nothing matched the given BinOp: %a = %a" d_plainexp a d_plainexp b; m - in - match stripCasts (constFold true exp) with - (* somehow there are a lot of casts inside the BinOp which stripCasts only removes when called on the subparts - -> matching as in flagMode didn't work *) - (* | BinOp (Eq, Const (CInt64(i, kind, str)), Lval (Var v, NoOffset), _) - | BinOp (Eq, Lval (Var v, NoOffset), Const (CInt64(i, kind, str)), _) -> - ignore(printf "%s %i\n" v.vname (Int64.to_int i)); m *) - | BinOp (Eq, a, b, _) -> check (stripCasts a) (stripCasts b) tv - | BinOp (Ne, a, b, _) -> check (stripCasts a) (stripCasts b) (not tv) - | e -> M.debug ~category:Analyzer "branch: nothing matched the given exp: %a" d_plainexp e; m - - let body ctx (f:fundec) : D.t = - ctx.local - - let return ctx (exp:exp option) (f:fundec) : D.t = - (* TODO check One Return transformation: oneret.ml *) - let m = ctx.local in - (* if f.svar.vname <> "main" && BatList.is_empty (callstack m) then M.write ("\n\t!!! call stack is empty for function "^f.svar.vname^" !!!"); *) - if f.svar.vname = "main" then ( - let mustOpen, mayOpen = D.union (D.filter_values D.opened m) (D.get_value unclosed_var m) in - if Set.cardinal mustOpen > 0 then ( - D.warn @@ "unclosed files: "^D.string_of_keys mustOpen; - Set.iter (fun v -> D.warn ~loc:(D.V.loc v) "file is never closed") mustOpen; - (* add warnings about currently open files (don't include overwritten or changed file handles!) *) - warned_unclosed := Set.union !warned_unclosed (fst (D.filter_values D.opened m)) (* can't save in domain b/c it wouldn't reach the other return *) - ); - (* go through files "never closed" and recheck for current return *) - Set.iter (fun v -> if D.must (D.V.key v) D.closed m then D.warn ~may:true ~loc:(D.V.loc v) "file is never closed") !warned_unclosed; - (* let mustOpenVars = List.map (fun x -> x.key) mustOpen in *) - (* let mayOpen = List.filter (fun x -> not (List.mem x.key mustOpenVars)) mayOpen in (* ignore values that are already in mustOpen *) *) - let mayOpen = Set.diff mayOpen mustOpen in - if Set.cardinal mayOpen > 0 then - D.warn ~may:true @@ "unclosed files: "^D.string_of_keys mayOpen; - Set.iter (fun v -> D.warn ~may:true ~loc:(D.V.loc v) "file is never closed") mayOpen - ); - (* take care of return value *) - let au = match exp with - | Some(Lval lval) when D.mem (D.key_from_lval lval) m -> (* we return a var in D *) - let k = D.key_from_lval lval in - let varinfo,offset = k in - if varinfo.vglob then - D.alias return_var k m (* if var is global, we alias it *) - else - D.add return_var (D.find' k m) m (* if var is local, we make a copy *) - | _ -> m - in - (* remove formals and locals *) - (* this is not a good approach, what if we added a key foo.fp? -> just keep the globals *) - List.fold_left (fun m var -> D.remove' (var, `NoOffset) m) au (f.sformals @ f.slocals) - (* D.only_globals au *) - - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - let m = if f.svar.vname <> "main" then - (* push current location onto stack *) - D.edit_callstack (BatList.cons (Option.get !Node.current_node)) ctx.local - else ctx.local in - (* we need to remove all variables that are neither globals nor special variables from the domain for f *) - (* problem: we need to be able to check aliases of globals in check_overwrite_open -> keep those in too :/ *) - (* TODO see Base.make_entry, reachable vars > globals? *) - (* [m, D.only_globals m] *) - [m, m] (* this is [caller, callee] *) - - let check_overwrite_open k m = (* used in combine and special *) - if List.is_empty (D.get_aliases k m) then ( - (* there are no other variables pointing to the file handle - and it is opened again without being closed before *) - D.report k D.opened ("overwriting still opened file handle "^D.string_of_key k) m; - let mustOpen, mayOpen = D.filter_records k D.opened m in - let mayOpen = Set.diff mayOpen mustOpen in - (* save opened files in the domain to warn about unclosed files at the end *) - D.extend_value unclosed_var (mustOpen, mayOpen) m - ) else m - - let combine_env ctx lval fexp f args fc au f_ask = - let m = ctx.local in - (* pop the last location off the stack *) - let m = D.edit_callstack List.tl m in (* TODO could it be problematic to keep this in the caller instead of callee domain? if we only add the stack for the callee in enter, then there would be no need to pop a location anymore... *) - (* TODO add all globals from au to m (since we remove formals and locals on return, we can just add everything except special vars?) *) - D.without_special_vars au |> D.add_all m - - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = - let m = ctx.local in - let return_val = D.find_option return_var au in - match lval, return_val with - | Some lval, Some v -> - let k = D.key_from_lval lval in - (* handle potential overwrites *) - let m = check_overwrite_open k m in - (* if v.key is still in D, then it must be a global and we need to alias instead of rebind *) - (* TODO what if there is a local with the same name as the global? *) - if D.V.is_top v then (* returned a local that was top -> just add k as top *) - D.add' k v m - else (* v is now a local which is not top or a global which is aliased *) - let vvar = D.V.get_alias v in (* this is also ok if v is not an alias since it chooses an element from the May-Set which is never empty (global top gets aliased) *) - if D.mem vvar au then (* returned variable was a global TODO what if local had the same name? -> seems to work *) - D.alias k vvar m - else (* returned variable was a local *) - let v = D.V.set_key k v in (* adjust var-field to lval *) - D.add' k v m - | _ -> m - - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = - (* is f a pointer to a function we look out for? *) - let f = eval_fv (Analyses.ask_of_ctx ctx) (Lval (Var f, NoOffset)) |? f in - let m = ctx.local in - let loc = (Option.get !Node.current_node)::(D.callstack m) in - let arglist = List.map (Cil.stripCasts) arglist in (* remove casts, TODO safe? *) - let split_err_branch lval dom = - (* type? NULL = 0 = 0-ptr? Cil.intType, Cil.intPtrType, Cil.voidPtrType -> no difference *) - if not (GobConfig.get_bool "ana.file.optimistic") then - ctx.split dom [Events.SplitBranch ((Cil.BinOp (Cil.Eq, Cil.Lval lval, Cil.integer 0, Cil.intType)), true)]; - dom - in - (* fold possible keys on domain *) - let ret_all f lval = - let xs = D.keys_from_lval lval (Analyses.ask_of_ctx ctx) in (* get all possible keys for a given lval *) - if xs = [] then (D.warn @@ GobPretty.sprintf "could not resolve %a" CilType.Lval.pretty lval; m) - else if List.compare_length_with xs 1 = 0 then f (List.hd xs) m true - (* else List.fold_left (fun m k -> D.join m (f k m)) m xs *) - else - (* if there is more than one key, join all values and do warnings on the result *) - let v = List.fold_left (fun v k -> match v, D.find_option k m with - | None, None -> None - | Some a, None - | None, Some a -> Some a - | Some a, Some b -> Some (D.V.join a b)) None xs in - (* set all of the keys to the computed joined value *) - (* let m' = Option.map_default (fun v -> List.fold_left (fun m k -> D.add' k v m) m xs) m v in *) - (* then check each key *) - (* List.iter (fun k -> ignore(f k m')) xs; *) - (* get Mval.Exp from lval *) - let k' = D.key_from_lval lval in - (* add joined value for that key *) - let m' = Option.map_default (fun v -> D.add' k' v m) m v in - (* check for warnings *) - ignore(f k' m' true); - (* and join the old domain without issuing warnings *) - List.fold_left (fun m k -> D.join m (f k m false)) m xs - in - match lval, f.vname, arglist with - | None, "fopen", _ -> - D.warn "file handle is not saved!"; m - | Some lval, "fopen", _ -> - let f k m w = - let m = check_overwrite_open k m in - (match arglist with - | Const(CStr(filename,_))::Const(CStr(mode,_))::[] -> - (* M.debug ~category:Analyzer @@ "fopen(\""^filename^"\", \""^mode^"\")"; *) - D.fopen k loc filename mode m |> split_err_branch lval (* TODO k instead of lval? *) - | e::Const(CStr(mode,_))::[] -> - (* ignore(printf "CIL: %a\n" d_plainexp e); *) - (match ctx.ask (Queries.EvalStr e) with - | `Lifted filename -> D.fopen k loc filename mode m - | _ -> D.warn "[Unsound]unknown filename"; D.fopen k loc "???" mode m - ) - | xs -> - let args = (String.concat ", " (List.map CilType.Exp.show xs)) in - M.debug ~category:Analyzer "fopen args: %s" args; - (* List.iter (fun exp -> ignore(printf "%a\n" d_plainexp exp)) xs; *) - D.warn @@ "[Program]fopen needs two strings as arguments, given: "^args; m - ) - in ret_all f lval - - | _, "fclose", [Lval fp] -> - let f k m w = - if w then D.reports k [ - false, D.closed, "closeing already closed file handle "^D.string_of_key k; - true, D.opened, "closeing unopened file handle "^D.string_of_key k - ] m; - D.fclose k loc m - in ret_all f fp - | _, "fclose", _ -> - D.warn "fclose needs exactly one argument"; m - - | _, "fprintf", (Lval fp)::_::_ -> - let f k m w = - if w then D.reports k [ - false, D.closed, "writing to closed file handle "^D.string_of_key k; - true, D.opened, "writing to unopened file handle "^D.string_of_key k; - true, D.writable, "writing to read-only file handle "^D.string_of_key k; - ] m; - m - in ret_all f fp - | _, "fprintf", fp::_::_ -> - (* List.iter (fun exp -> ignore(printf "%a\n" d_plainexp exp)) arglist; *) - print_query_lv ~msg:"fprintf(?, ...): " (Analyses.ask_of_ctx ctx) fp; - D.warn "[Program]first argument to printf must be a Lval"; m - | _, "fprintf", _ -> - D.warn "[Program]fprintf needs at least two arguments"; m - - | _ -> m - - let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = [D.bot ()] - let threadspawn ctx ~multiple lval f args fctx = ctx.local - let exitstate v = D.bot () -end - -let _ = - MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml new file mode 100644 index 0000000000..10e0f5c5f4 --- /dev/null +++ b/src/analyses/loopTermination.ml @@ -0,0 +1,87 @@ +(** Termination analysis for loops and [goto] statements ([termination]). *) + +open Analyses +open GoblintCil +open TerminationPreprocessing + +(** Contains all loop counter variables (varinfo) and maps them to their corresponding loop statement. *) +let loop_counters : stmt VarToStmt.t ref = ref VarToStmt.empty + +(** Checks whether a variable can be bounded. *) +let check_bounded ctx varinfo = + let open IntDomain.IntDomTuple in + let exp = Lval (Var varinfo, NoOffset) in + match ctx.ask (EvalInt exp) with + | `Top -> false + | `Lifted v -> not (is_top_of (ikind v) v) + | `Bot -> failwith "Loop counter variable is Bot." + +(** We want to record termination information of loops and use the loop + * statements for that. We use this lifting because we need to have a + * lattice. *) +module Statements = Lattice.Flat (CilType.Stmt) (Printable.DefaultNames) + +(** The termination analysis considering loops and gotos *) +module Spec : Analyses.MCPSpec = +struct + + include Analyses.IdentitySpec + + let name () = "termination" + + module D = Lattice.Unit + module C = D + module V = struct + include UnitV + let is_write_only _ = true + end + module G = MapDomain.MapBot (Statements) (BoolDomain.MustBool) + + let startstate _ = () + let exitstate = startstate + + let find_loop ~loop_counter = + VarToStmt.find loop_counter !loop_counters + + (** Recognizes a call of [__goblint_bounded] to check the EvalInt of the + * respective loop counter variable at that position. *) + let special ctx (lval : lval option) (f : varinfo) (arglist : exp list) = + if !AnalysisState.postsolving then + match f.vname, arglist with + "__goblint_bounded", [Lval (Var loop_counter, NoOffset)] -> + (try + let loop_statement = find_loop ~loop_counter in + let is_bounded = check_bounded ctx loop_counter in + ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); + (* In case the loop is not bounded, a warning is created. *) + if not (is_bounded) then ( + M.warn ~loc:(M.Location.CilLocation (Cilfacade.get_stmtLoc loop_statement)) ~category:Termination "The program might not terminate! (Loop analysis)" + ); + () + with Not_found -> + failwith "Encountered a call to __goblint_bounded with an unknown loop counter variable.") + | _ -> () + else () + + let query ctx (type a) (q: a Queries.t): a Queries.result = + match q with + | Queries.MustTermLoop loop_statement -> + let multithreaded = ctx.ask Queries.IsEverMultiThreaded in + (not multithreaded) + && (match G.find_opt (`Lifted loop_statement) (ctx.global ()) with + Some b -> b + | None -> false) + | Queries.MustTermAllLoops -> + let multithreaded = ctx.ask Queries.IsEverMultiThreaded in + if multithreaded then ( + M.warn ~category:Termination "The program might not terminate! (Multithreaded)\n"; + false) + else + G.for_all (fun _ term_info -> term_info) (ctx.global ()) + | _ -> Queries.Result.top q + +end + +let () = + Cilfacade.register_preprocess (Spec.name ()) (new loopCounterVisitor loop_counters); + MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/analyses/mCPRegistry.ml b/src/analyses/mCPRegistry.ml index 810da827ff..5d0174d44c 100644 --- a/src/analyses/mCPRegistry.ml +++ b/src/analyses/mCPRegistry.ml @@ -215,7 +215,7 @@ struct let arbitrary () = let arbs = map (fun (n, (module D: Printable.S)) -> QCheck.map ~rev:(fun (_, o) -> obj o) (fun x -> (n, repr x)) @@ D.arbitrary ()) @@ domain_list () in - MyCheck.Arbitrary.sequence arbs + GobQCheck.Arbitrary.sequence arbs let relift = unop_map (fun (module S: Printable.S) x -> Obj.repr (S.relift (Obj.obj x))) end diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index dbaa2d69fc..456d434be7 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -6,7 +6,7 @@ open MessageCategory open AnalysisStateUtil module ToppedVarInfoSet = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) - +module WasMallocCalled = BoolDomain.MayBool module Spec : Analyses.MCPSpec = struct include Analyses.IdentitySpec @@ -14,35 +14,188 @@ struct let name () = "memLeak" module D = ToppedVarInfoSet - module C = Lattice.Unit + module C = D + module P = IdentityP (D) + + module V = UnitV + module G = WasMallocCalled + + let context _ d = d + + let must_be_single_threaded ~since_start ctx = + ctx.ask (Queries.MustBeSingleThreaded { since_start }) - let context _ _ = () + let was_malloc_called ctx = + ctx.global () (* HELPER FUNCTIONS *) - let warn_for_multi_threaded ctx = - if not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true })) then ( + let get_global_vars () = + List.filter_map (function GVar (v, _, _) | GVarDecl (v, _) -> Some v | _ -> None) !Cilfacade.current_file.globals + + let get_global_struct_ptr_vars () = + get_global_vars () + |> List.filter (fun v -> + match unrollType v.vtype with + | TPtr (TComp (ci,_), _) + | TPtr ((TNamed ({ttype = TComp (ci, _); _}, _)), _) -> ci.cstruct + | TComp (_, _) + | (TNamed ({ttype = TComp _; _}, _)) -> false + | _ -> false) + + let get_global_struct_non_ptr_vars () = + get_global_vars () + |> List.filter (fun v -> + match unrollType v.vtype with + | TComp (ci, _) + | (TNamed ({ttype = TComp (ci,_); _}, _)) -> ci.cstruct + | _ -> false) + + let get_reachable_mem_from_globals (global_vars:varinfo list) ctx = + global_vars + |> List.map (fun v -> Lval (Var v, NoOffset)) + |> List.filter_map (fun exp -> + match ctx.ask (Queries.MayPointTo exp) with + | a when not (Queries.AD.is_top a) && Queries.AD.cardinal a = 1 -> + begin match List.hd @@ Queries.AD.elements a with + | Queries.AD.Addr.Addr (v, _) when (ctx.ask (Queries.IsHeapVar v)) && not (ctx.ask (Queries.IsMultiple v)) -> Some v + | _ -> None + end + | _ -> None) + + let rec get_reachable_mem_from_str_ptr_globals (global_struct_ptr_vars:varinfo list) ctx = + let eval_value_of_heap_var heap_var = + match ctx.ask (Queries.EvalValue (Lval (Var heap_var, NoOffset))) with + | a when not (Queries.VD.is_top a) -> + begin match a with + | Struct s -> + List.fold_left (fun acc f -> + if isPointerType f.ftype then + begin match ValueDomain.Structs.get s f with + | Queries.VD.Address a when not (Queries.AD.is_top a) && Queries.AD.cardinal a = 1 -> + let reachable_from_addr_set = + Queries.AD.fold (fun addr acc -> + match addr with + | Queries.AD.Addr.Addr (v, _) -> (v :: get_reachable_mem_from_str_ptr_globals [v] ctx) @ acc + | _ -> acc + ) a [] + in + reachable_from_addr_set @ acc + | _ -> acc + end + else acc + ) [] (ValueDomain.Structs.keys s) + | _ -> [] + end + | _ -> [] + in + let get_pts_of_non_heap_ptr_var var = + match ctx.ask (Queries.MayPointTo (Lval (Var var, NoOffset))) with + | a when not (Queries.AD.is_top a) && Queries.AD.cardinal a = 1 -> + begin match List.hd @@ Queries.AD.elements a with + | Queries.AD.Addr.Addr (v, _) when (ctx.ask (Queries.IsHeapVar v)) && not (ctx.ask (Queries.IsMultiple v)) -> v :: (eval_value_of_heap_var v) + | Queries.AD.Addr.Addr (v, _) when not (ctx.ask (Queries.IsAllocVar v)) && isPointerType v.vtype -> get_reachable_mem_from_str_ptr_globals [v] ctx + | _ -> [] + end + | _ -> [] + in + global_struct_ptr_vars + |> List.fold_left (fun acc var -> + if ctx.ask (Queries.IsHeapVar var) then (eval_value_of_heap_var var) @ acc + else if not (ctx.ask (Queries.IsAllocVar var)) && isPointerType var.vtype then (get_pts_of_non_heap_ptr_var var) @ acc + else acc + ) [] + + let get_reachable_mem_from_str_non_ptr_globals (global_struct_non_ptr_vars:varinfo list) ctx = + global_struct_non_ptr_vars + (* Filter out global struct vars that don't have pointer fields *) + |> List.filter_map (fun v -> + match ctx.ask (Queries.EvalValue (Lval (Var v, NoOffset))) with + | a when not (Queries.VD.is_top a) -> + begin match a with + | Queries.VD.Struct s -> + let struct_fields = ValueDomain.Structs.keys s in + let ptr_struct_fields = List.filter (fun f -> isPointerType f.ftype) struct_fields in + if ptr_struct_fields = [] then None else Some (s, ptr_struct_fields) + | _ -> None + end + | _ -> None + ) + |> List.fold_left (fun acc_struct (s, fields) -> + let reachable_from_fields = + List.fold_left (fun acc_field field -> + match ValueDomain.Structs.get s field with + | Queries.VD.Address a -> + let reachable_from_addr_set = + Queries.AD.fold (fun addr acc_addr -> + match addr with + | Queries.AD.Addr.Addr (v, _) -> + let reachable_from_v = Queries.AD.of_list (List.map (fun v -> Queries.AD.Addr.Addr (v, `NoOffset)) (get_reachable_mem_from_str_ptr_globals [v] ctx)) in + Queries.AD.join (Queries.AD.add addr reachable_from_v) acc_addr + | _ -> acc_addr + ) a (Queries.AD.empty ()) + in (Queries.AD.to_var_may reachable_from_addr_set) @ acc_field + | _ -> acc_field + ) [] fields + in + reachable_from_fields @ acc_struct + ) [] + + let warn_for_multi_threaded_due_to_abort ctx = + let malloc_called = was_malloc_called ctx in + if not (must_be_single_threaded ctx ~since_start:true) && malloc_called then ( set_mem_safety_flag InvalidMemTrack; set_mem_safety_flag InvalidMemcleanup; - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Program isn't running in single-threaded mode. A memory leak might occur due to multi-threading" + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Program aborted while running in multi-threaded mode. A memory leak might occur" + ) + + (* If [is_return] is set to [true], then a thread return occurred, else a thread exit *) + let warn_for_thread_return_or_exit ctx is_return = + if not (ToppedVarInfoSet.is_empty ctx.local) then ( + set_mem_safety_flag InvalidMemTrack; + set_mem_safety_flag InvalidMemcleanup; + let current_thread = ctx.ask (Queries.CurrentThreadId) in + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory may be leaked at thread %s for thread %a" (if is_return then "return" else "exit") ThreadIdDomain.ThreadLifted.pretty current_thread ) let check_for_mem_leak ?(assert_exp_imprecise = false) ?(exp = None) ctx = - let state = ctx.local in - if not @@ D.is_empty state then + let allocated_mem = ctx.local in + if not (D.is_empty allocated_mem) then + let reachable_mem_from_non_struct_globals = D.of_list (get_reachable_mem_from_globals (get_global_vars ()) ctx) in + let reachable_mem_from_struct_ptr_globals = D.of_list (get_reachable_mem_from_str_ptr_globals (get_global_struct_ptr_vars ()) ctx) in + let reachable_mem_from_struct_non_ptr_globals = D.of_list (get_reachable_mem_from_str_non_ptr_globals (get_global_struct_non_ptr_vars ()) ctx) in + let reachable_mem_from_struct_globals = D.join reachable_mem_from_struct_ptr_globals reachable_mem_from_struct_non_ptr_globals in + let reachable_mem = D.join reachable_mem_from_non_struct_globals reachable_mem_from_struct_globals in + (* Check and warn if there's unreachable allocated memory at program exit *) + let allocated_and_unreachable_mem = D.diff allocated_mem reachable_mem in + if not (D.is_empty allocated_and_unreachable_mem) then ( + set_mem_safety_flag InvalidMemTrack; + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "There is unreachable allocated heap memory at program exit. A memory leak might occur for the alloc vars %a\n" (Pretty.d_list ", " CilType.Varinfo.pretty) (D.elements allocated_and_unreachable_mem) + ); + (* Check and warn if some of the allocated memory is not deallocated at program exit *) match assert_exp_imprecise, exp with | true, Some exp -> - set_mem_safety_flag InvalidMemTrack; set_mem_safety_flag InvalidMemcleanup; - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp exp D.pretty state + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp exp D.pretty allocated_mem | _ -> - set_mem_safety_flag InvalidMemTrack; set_mem_safety_flag InvalidMemcleanup; - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty state + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables" (* TRANSFER FUNCTIONS *) let return ctx (exp:exp option) (f:fundec) : D.t = + (* Check for a valid-memcleanup and memtrack violation in a multi-threaded setting *) + (* The check for multi-threadedness is to ensure that valid-memtrack and valid-memclenaup are treated separately for single-threaded programs *) + if (ctx.ask (Queries.MayBeThreadReturn) && not (must_be_single_threaded ctx ~since_start:true)) then ( + warn_for_thread_return_or_exit ctx true + ); (* Returning from "main" is one possible program exit => need to check for memory leaks *) - if f.svar.vname = "main" then check_for_mem_leak ctx; + if f.svar.vname = "main" then ( + check_for_mem_leak ctx; + if not (must_be_single_threaded ctx ~since_start:false) && was_malloc_called ctx then begin + set_mem_safety_flag InvalidMemTrack; + set_mem_safety_flag InvalidMemcleanup; + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Possible memory leak: Memory was allocated in a multithreaded program, but not all threads are joined." + end + ); ctx.local let special ctx (lval:lval option) (f:varinfo) (arglist:exp list) : D.t = @@ -52,47 +205,58 @@ struct | Malloc _ | Calloc _ | Realloc _ -> - (* Warn about multi-threaded programs as soon as we encounter a dynamic memory allocation function *) - warn_for_multi_threaded ctx; + ctx.sideg () true; begin match ctx.ask (Queries.AllocVar {on_stack = false}) with - | `Lifted var -> D.add var state + | `Lifted var -> + ToppedVarInfoSet.add var state | _ -> state end | Free ptr -> begin match ctx.ask (Queries.MayPointTo ptr) with - | ad when not (Queries.AD.is_top ad) && Queries.AD.cardinal ad = 1 -> + | ad when (not (Queries.AD.is_top ad)) && Queries.AD.cardinal ad = 1 -> (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) begin match Queries.AD.choose ad with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsAllocVar v) && ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.remove v state (* Unique pointed to heap vars *) - | _ -> state + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsAllocVar v) && ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> + ToppedVarInfoSet.remove v ctx.local + | _ -> ctx.local end - | _ -> state + | _ -> ctx.local end | Abort -> - (* An "Abort" special function indicates program exit => need to check for memory leaks *) check_for_mem_leak ctx; + (* Upon a call to the "Abort" special function in the multi-threaded case, we give up and conservatively warn *) + warn_for_multi_threaded_due_to_abort ctx; state | Assert { exp; _ } -> - let warn_for_assert_exp = - match ctx.ask (Queries.EvalInt exp) with + begin match ctx.ask (Queries.EvalInt exp) with | a when Queries.ID.is_bot a -> M.warn ~category:Assert "assert expression %a is bottom" d_exp exp | a -> begin match Queries.ID.to_bool a with - | Some b -> + | Some true -> () + | Some false -> (* If we know for sure that the expression in "assert" is false => need to check for memory leaks *) - if b = false then - check_for_mem_leak ctx - else () - | None -> check_for_mem_leak ctx ~assert_exp_imprecise:true ~exp:(Some exp) + warn_for_multi_threaded_due_to_abort ctx; + check_for_mem_leak ctx + | None -> + warn_for_multi_threaded_due_to_abort ctx; + check_for_mem_leak ctx ~assert_exp_imprecise:true ~exp:(Some exp) end - in - warn_for_assert_exp; + end; + state + | ThreadExit _ -> + begin match ctx.ask (Queries.CurrentThreadId) with + | `Lifted tid -> + warn_for_thread_return_or_exit ctx false + | _ -> () + end; state | _ -> state let startstate v = D.bot () let exitstate v = D.top () + + let threadenter ctx ~multiple lval f args = [D.bot ()] end let _ = - MCP.register_analysis (module Spec : MCPSpec) \ No newline at end of file + MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index fc60352298..9dccf77ff9 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -69,17 +69,17 @@ struct in host_contains_a_ptr host || offset_contains_a_ptr offset - let points_to_heap_only ctx ptr = + let points_to_alloc_only ctx ptr = match ctx.ask (Queries.MayPointTo ptr) with | a when not (Queries.AD.is_top a)-> Queries.AD.for_all (function - | Addr (v, o) -> ctx.ask (Queries.IsHeapVar v) + | Addr (v, o) -> ctx.ask (Queries.IsAllocVar v) | _ -> false ) a | _ -> false let get_size_of_ptr_target ctx ptr = - if points_to_heap_only ctx ptr then + if points_to_alloc_only ctx ptr then (* Ask for BlobSize from the base address (the second component being set to true) in order to avoid BlobSize giving us bot *) ctx.ask (Queries.BlobSize {exp = ptr; base_address = true}) else diff --git a/src/analyses/modifiedSinceLongjmp.ml b/src/analyses/modifiedSinceSetjmp.ml similarity index 96% rename from src/analyses/modifiedSinceLongjmp.ml rename to src/analyses/modifiedSinceSetjmp.ml index a129c9f92c..93e55b2a17 100644 --- a/src/analyses/modifiedSinceLongjmp.ml +++ b/src/analyses/modifiedSinceSetjmp.ml @@ -1,6 +1,4 @@ -(** Analysis of variables modified since [setjmp] ([modifiedSinceLongjmp]). *) - -(* TODO: this name is wrong *) +(** Analysis of variables modified since [setjmp] ([modifiedSinceSetjmp]). *) open GoblintCil open Analyses @@ -9,7 +7,7 @@ module Spec = struct include Analyses.IdentitySpec - let name () = "modifiedSinceLongjmp" + let name () = "modifiedSinceSetjmp" module D = JmpBufDomain.LocallyModifiedMap module VS = D.VarSet module C = Lattice.Unit diff --git a/src/analyses/region.ml b/src/analyses/region.ml index 652526543c..5b10586aba 100644 --- a/src/analyses/region.ml +++ b/src/analyses/region.ml @@ -177,7 +177,15 @@ struct let threadenter ctx ~multiple lval f args = [`Lifted (RegMap.bot ())] - let threadspawn ctx ~multiple lval f args fctx = ctx.local + let threadspawn ctx ~multiple lval f args fctx = + match ctx.local with + | `Lifted reg -> + let old_regpart = ctx.global () in + let regpart, reg = List.fold_right Reg.assign_escape args (old_regpart, reg) in + if not (RegPart.leq regpart old_regpart) then + ctx.sideg () regpart; + `Lifted reg + | x -> x let exitstate v = `Lifted (RegMap.bot ()) diff --git a/src/analyses/spec.ml b/src/analyses/spec.ml deleted file mode 100644 index 2f754f6160..0000000000 --- a/src/analyses/spec.ml +++ /dev/null @@ -1,496 +0,0 @@ -(** Analysis using finite automaton specification file ([spec]). - - @author Ralf Vogler - - @see Vogler, R. Verifying Regular Safety Properties of C Programs Using the Static Analyzer Goblint. Section 4. *) - -open Batteries -open GoblintCil -open Analyses - -module SC = SpecCore - -module Spec = -struct - include Analyses.DefaultSpec - - let name() = "spec" - module D = SpecDomain.Dom - module C = SpecDomain.Dom - - (* special variables *) - let return_var = Cilfacade.create_var @@ Cil.makeVarinfo false "@return" Cil.voidType, `NoOffset - let global_var = Cilfacade.create_var @@ Cil.makeVarinfo false "@global" Cil.voidType, `NoOffset - - (* spec data *) - let nodes = ref [] - let edges = ref [] - - let load_specfile () = - let specfile = GobConfig.get_string "ana.spec.file" in - if String.length specfile < 1 then failwith "You need to specify a specification file using --set ana.spec.file path/to/file.spec when using the spec analysis!"; - if not (Sys.file_exists specfile) then failwith @@ "The given spec-file ("^specfile^") doesn't exist (CWD is "^Sys.getcwd ()^")."; - let _nodes, _edges = SpecUtil.parseFile specfile in - nodes := _nodes; edges := _edges (* don't change -> no need to save them in domain *) - - (* module for encapsulating general spec checking functions used in multiple transfer functions (assign, special) *) - (* - .spec-format: - - The file contains two types of definitions: nodes and edges. The labels of nodes are output. The labels of edges are the constraints. - - The given nodes are warnings, which have an implicit back edge to the previous node if used as a target. - - Alternatively warnings can be specified like this: "node1 -w1,w2,w3> node2 ...1" (w1, w2 and w3 will be output when the transition is taken). - - The start node of the first transition is the start node of the automaton. - - End nodes are specified by "node -> end _". - - "_end" is the local warning for nodes that are not in an end state, _END is the warning at return ($ is the list of keys). - - An edge with '_' matches everything. - - Edges with "->>" (or "-w1,w2>>" etc.) are forwarding edges, which will continue matching the same statement for the target node. - *) - module SpecCheck = - struct - (* custom goto (D.goto is just for modifying) that checks if the target state is a warning and acts accordingly *) - let goto ?may:(may=false) ?change_state:(change_state=true) key state m ws = - let loc = (Option.get !Node.current_node)::(D.callstack m) in - let warn key m msg = - Str.global_replace (Str.regexp_string "$") (D.string_of_key key) msg - |> D.warn ~may:(D.is_may key m || D.is_unknown key m) - in - (* do transition warnings *) - List.iter (fun state -> match SC.warning state !nodes with Some msg -> warn key m msg | _ -> ()) ws; - match SC.warning state !nodes with - | Some msg -> - warn key m msg; - m (* no goto == implicit back edge *) - | None -> - M.debug ~category:Analyzer "GOTO %s: %s -> %s" (D.string_of_key key) (D.string_of_state key m) state; - if not change_state then m - else if may then D.may_goto key loc state m else D.goto key loc state m - - let equal_exp ctx spec_exp cil_exp = match spec_exp, cil_exp with - (* TODO match constants right away to avoid queries? *) - | `String a, Const(CStr (b,_)) -> a=b - (* | `String a, Const(CWStr xs as c) -> failwith "not implemented" *) - (* CWStr is done in base.ml, query only returns `Str if it's safe *) - | `String a, e -> (match ctx.ask (Queries.EvalStr e) with - | `Lifted b -> a = b - | _ -> M.debug ~category:Analyzer "EQUAL String Query: no result!"; false - ) - | `Regex a, e -> (match ctx.ask (Queries.EvalStr e) with - | `Lifted b -> Str.string_match (Str.regexp a) b 0 - | _ -> M.debug ~category:Analyzer "EQUAL Regex String Query: no result!"; false - ) - | `Bool a, e -> (match ctx.ask (Queries.EvalInt e) with - | b -> (match Queries.ID.to_bool b with Some b -> a=b | None -> false) - ) - | `Int a, e -> (match ctx.ask (Queries.EvalInt e) with - | b -> (match Queries.ID.to_int b with Some b -> (Int64.of_int a)=(IntOps.BigIntOps.to_int64 b) | None -> false) - ) - | `Float a, Const(CReal (b, fkind, str_opt)) -> a=b - | `Float a, _ -> M.debug ~category:Analyzer "EQUAL Float: unsupported!"; false - (* arg is a key. currently there can only be one key per constraint, so we already used it for lookup. TODO multiple keys? *) - | `Var a, b -> true - (* arg is a identifier we use for matching constraints. TODO save in domain *) - | `Ident a, b -> true - | `Error s, b -> failwith @@ "Spec error: "^s - (* wildcard matches anything *) - | `Free, b -> true - | a,b -> M.info ~category:Unsound "EQUAL? Unmatched case - assume true..."; true - - let check_constraint ctx get_key matches m new_a old_key (a,ws,fwd,b,c as edge) = - (* If we have come to a wildcard, we match it instantly, but since there is no way of determining a key - this only makes sense if fwd is true (TODO wildcard for global. TODO use old_key). We pass a state replacement as 'new_a', - which will be applied in the following checks. - Multiple forwarding wildcards are not allowed, i.e. new_a must be None, otherwise we end up in a loop. *) - if SC.is_wildcard c && fwd && new_a=None then Some (m,fwd,Some (b,a),old_key) (* replace b with a in the following checks *) - else - (* save original start state of the constraint (needed to detect reflexive edges) *) - let old_a = a in - (* Assume new_a *) - let a = match new_a with - | Some (x,y) when a=x -> y - | _ -> a - in - (* if we forward, we have to replace the starting state for the following constraints *) - let new_a = if fwd then Some (b,a) else None in - (* TODO how to detect the key?? use "$foo" as key, "foo" as var in constraint and "_" for anything we're not interested in. - What to do for multiple keys (e.g. $foo, $bar)? -> Only allow one key & one map per spec-file (e.g. only $ as a key) or implement multiple maps? *) - (* look inside the constraint if there is a key and if yes, return what it corresponds to *) - (* if we can't find a matching key, we use the global key *) - let key = get_key c |? Cil.var (fst global_var) in - (* ignore(printf "KEY: %a\n" d_plainlval key); *) - (* get possible keys that &lval may point to *) - let keys = D.keys_from_lval key (Analyses.ask_of_ctx ctx) in (* does MayPointTo query *) - let check_key (m,n) var = - (* M.debug ~category:Analyzer @@ "check_key: "^f.vname^"(...): "^D.string_of_entry var m; *) - let wildcard = SC.is_wildcard c && fwd && b<>"end" in - (* skip transitions we can't take b/c we're not in the right state *) - (* i.e. if not in map, we must be at the start node or otherwise we must be in one of the possible saved states *) - if not (D.mem var m) && a<>SC.startnode !edges || D.mem var m && not (D.may_in_state var a m) then ( - (* ignore(printf "SKIP %s: state: %s, a: %s at %i\n" f.vname (D.string_of_state var m) a (!Tracing.current_loc.line)); *) - (m,n) (* not in map -> initial state. TODO save initial state? *) - ) - (* edge must match the current state or be a wildcard transition (except those for end) *) - else if not (matches edge) && not wildcard then (m,n) - (* everything matches the constraint -> go to new state and increase counter *) - else - (* TODO if #Queries.MayPointTo > 1: each result is May, but all combined are Must *) - let may = (List.compare_length_with keys 1 > 0) in - (* do not change state for reflexive edges where the key is not assigned to (e.g. *$p = _) *) - let change_state = not (old_a=b && SC.get_lval c <> Some `Var) in - M.debug ~category:Analyzer "GOTO ~may:%B ~change_state:%B. %s -> %s: %s" may change_state a b (SC.stmt_to_string c); - let new_m = goto ~may:may ~change_state:change_state var b m ws in - (new_m,n+1) - in - (* do check for each varinfo and return the resulting domain if there has been at least one matching constraint *) - let new_m,n = List.fold_left check_key (m,0) keys in (* start with original domain and #transitions=0 *) - if n==0 then None (* no constraint matched the current state *) - else Some (new_m,fwd,new_a,Some key) (* return new domain and forwarding info *) - - let check ctx get_key matches = - let m = ctx.local in - (* go through constraints and return resulting domain for the first match *) - (* if no constraint matches, the unchanged domain is returned *) - (* repeat for target node if it is a forwarding edge *) - (* TODO what should be done if multiple constraints would match? *) - (* TODO ^^ for May-Sets multiple constraints could match and should be taken! *) - try - let rec check_fwd_loop m new_a old_key = (* TODO cycle detection? *) - let new_m,fwd,new_a,key = List.find_map (check_constraint ctx get_key matches m new_a old_key) !edges in - (* List.iter (fun x -> M.debug ~category:Analyzer (x^"\n")) (D.string_of_map new_m); *) - if fwd then M.debug ~category:Analyzer "FWD: %B, new_a: %s, old_key: %s" fwd (dump new_a) (dump old_key); - if fwd then check_fwd_loop new_m new_a key else new_m,key - in - (* now we get the new domain and the latest key that was used *) - let new_m,key = check_fwd_loop m None None in - (* List.iter (fun x -> M.debug ~category:Analyzer (x^"\n")) (D.string_of_map new_m); *) - (* next we have to check if there is a branch() transition we could take *) - let branch_edges = List.filter (fun (a,ws,fwd,b,c) -> SC.is_branch c) !edges in - (* just for the compiler: key is initialized with None, but changes once some constaint matches. If none match, we wouldn't be here but at catch Not_found. *) - match key with - | Some key -> - (* we need to pass the key to the branch function. There is no scheme for getting the key from the constraint, but we should have been forwarded and can use the old key. *) - let check_branch branches var = - (* only keep those branch_edges for which our key might be in the right state *) - let branch_edges = List.filter (fun (a,ws,fwd,b,c) -> D.may_in_state var a new_m) branch_edges in - (* M.debug ~category:Analyzer @@ D.string_of_entry var new_m^" -> branch_edges: "^String.concat "\n " @@ List.map (fun x -> SC.def_to_string (SC.Edge x)) branch_edges; *) - (* count should be a multiple of 2 (true/false), otherwise the spec is malformed *) - if List.length branch_edges mod 2 <> 0 then failwith "Spec is malformed: branch-transitions always need a true and a false case!" else - (* if nothing matches, just return new_m without branching *) - (* if List.is_empty branch_edges then Set.of_list new_m else *) - if List.is_empty branch_edges then Set.of_list ([new_m, Cil.integer 1, true]) else (* XX *) - (* unique set of (dom,exp,tv) used in branch *) - let do_branch branches (a,ws,fwd,b,c) = - let c_str = match SC.branch_exp c with Some (exp,tv) -> SC.exp_to_string exp | _ -> "" in - let c_str = Str.global_replace (Str.regexp_string "$key") "%e:key" c_str in (* TODO what should be used to specify the key? *) - (* TODO this somehow also prints the expression!? why?? *) - let c_exp = Formatcil.cExp c_str [("key", Fe (D.K.to_cil_exp var))] in (* use Fl for Lval instead? *) - (* TODO encode key in exp somehow *) - (* ignore(printf "BRANCH %a\n" d_plainexp c_exp); *) - ctx.split new_m [Events.SplitBranch (c_exp, true)]; - Set.add (new_m,c_exp,true) (Set.add (new_m,c_exp,false) branches) - in - List.fold_left do_branch branches branch_edges - in - let keys = D.keys_from_lval key (Analyses.ask_of_ctx ctx) in - let new_set = List.fold_left check_branch Set.empty keys in ignore(new_set); (* TODO refactor *) - (* List.of_enum (Set.enum new_set) *) - new_m (* XX *) - | None -> new_m - with Not_found -> m (* nothing matched -> no change *) - end - - (* queries *) - let query ctx (type a) (q: a Queries.t) = - match q with - | _ -> Queries.Result.top q - - let query_addrs ask exp = - match ask (Queries.MayPointTo exp) with - | ad when not (Queries.AD.is_top ad) -> Queries.AD.elements ad - | _ -> [] - - let eval_fv ask exp: varinfo option = - match query_addrs ask exp with - | [addr] -> Queries.AD.Addr.to_var_may addr - | _ -> None - - - (* transfer functions *) - let assign ctx (lval:lval) (rval:exp) : D.t = - (* ignore(printf "%a = %a\n" d_plainlval lval d_plainexp rval); *) - let get_key c = match SC.get_key_variant c with - | `Lval s -> - M.debug ~category:Analyzer "Key variant assign `Lval %s; %s" s (SC.stmt_to_string c); - (match SC.get_lval c, lval with - | Some `Var, _ -> Some lval - | Some `Ptr, (Mem Lval x, o) -> Some x (* TODO offset? *) - | _ -> None) - | _ -> None - in - let matches (a,ws,fwd,b,c) = - SC.equal_form (Some lval) c && - (* check for constraints *p = _ where p is the key *) - match lval, SC.get_lval c with - | (Mem Lval x, o), Some `Ptr when SpecCheck.equal_exp ctx (SC.get_rval c) rval -> - let keys = D.keys_from_lval x (Analyses.ask_of_ctx ctx) in - if List.compare_length_with keys 1 <> 0 then failwith "not implemented" - else true - | _ -> false (* nothing to do *) - in - let m = SpecCheck.check ctx get_key matches in - let key_from_exp = function - | Lval (Var v,o) -> Some (v, Offset.Exp.of_cil o) - | _ -> None - in - match key_from_exp (Lval lval), key_from_exp (stripCasts rval) with (* TODO for now we just care about Lval assignments -> should use Queries.MayPointTo *) - | Some k1, Some k2 when k1=k2 -> m (* do nothing on self-assignment *) - | Some k1, Some k2 when D.mem k1 m && D.mem k2 m -> (* both in D *) - M.debug ~category:Analyzer "assign (both in D): %s = %s" (D.string_of_key k1) (D.string_of_key k2); - (* saveOpened k1 *) m |> D.remove' k1 |> D.alias k1 k2 - | Some k1, Some k2 when D.mem k1 m -> (* only k1 in D *) - M.debug ~category:Analyzer "assign (only k1 in D): %s = %s" (D.string_of_key k1) (D.string_of_key k2); - (* saveOpened k1 *) m |> D.remove' k1 - | Some k1, Some k2 when D.mem k2 m -> (* only k2 in D *) - M.debug ~category:Analyzer "assign (only k2 in D): %s = %s" (D.string_of_key k1) (D.string_of_key k2); - let m = D.alias k1 k2 m in (* point k1 to k2 *) - if Basetype.Variables.to_group (fst k2) = Temp (* check if k2 is a temporary Lval introduced by CIL *) - then D.remove' k2 m (* if yes we need to remove it from our map *) - else m (* otherwise no change *) - | Some k1, _ when D.mem k1 m -> (* k1 in D and assign something unknown *) - M.debug ~category:Analyzer "assign (only k1 in D): %s = %a" (D.string_of_key k1) d_exp rval; - D.warn @@ "changed pointer "^D.string_of_key k1^" (no longer safe)"; - (* saveOpened ~unknown:true k1 *) m |> D.unknown k1 - | _ -> (* no change in D for other things *) - M.debug ~category:Analyzer "assign (none in D): %a = %a [%a]" d_lval lval d_exp rval d_plainexp rval; - m - - (* - - branch-transitions in the spec-file come in pairs: e.g. true-branch goes to node a, false-branch to node b - - branch is called for both possibilities - - TODO query the exp and take/don't take the transition - - in case of `Top we take the transition - - both branches get joined after (e.g. for fopen: May [open; error]) - - if there is a branch in the code, branch is also called - -> get the key from exp and backtrack to the corresponding branch-transitions - -> reevaluate with current exp and meet domain with result - *) - (* - - get key from exp - - ask EvalInt - - if result is `Top and we are in a state that is the starting node of a branch edge, we have to: - - go to target node and modify the state in specDomain - - find out which value of key makes exp equal to tv - - save this value and answer queries for EvalInt with it - - if not, compare it with tv and take the corresponding branch - *) - let branch ctx (exp:exp) (tv:bool) : D.t = - let m = ctx.local in - (* ignore(printf "if %a = %B (line %i)\n" d_plainexp exp tv (!Tracing.current_loc).line); *) - let check a b tv = - (* ignore(printf "check: %a = %a\n" d_plainexp a d_plainexp b); *) - match a, b with - | Const (CInt(i, kind, str)), Lval lval - | Lval lval, Const (CInt(i, kind, str)) -> - (* let binop = BinOp (Eq, a, b, Cil.intType) in *) - (* standardize the format of the expression to 'lval==i'. -> spec needs to follow that format, the code is mapped to it. *) - let binop = BinOp (Eq, Lval lval, Const (CInt(i, kind, str)), Cil.intType) in - let key = D.key_from_lval lval in - let value = D.find key m in - if Z.equal i Z.zero && tv then ( - M.debug ~category:Analyzer "error-branch"; - (* D.remove key m *) - )else( - M.debug ~category:Analyzer "success-branch"; - (* m *) - ); - (* there should always be an entry in our domain for key *) - if not (D.mem key m) then m else - (* TODO for now we just assume that a Binop is used and Lval is the key *) - (* get the state(s) that key is/might be in *) - let states = D.get_states key m in - (* compare SC.exp with Cil.exp and tv *) - let branch_exp_eq c exp tv = - (* let c_str = match SC.branch_exp c with Some (exp,tv) -> SC.exp_to_string exp | _ -> "" in - let c_str = Str.global_replace (Str.regexp_string "$key") "%e:key" c_str in - let c_exp = Formatcil.cExp c_str [("key", Fe (D.K.to_exp key))] in *) - (* c_exp=exp *) (* leads to Out_of_memory *) - match SC.branch_exp c with - | Some (c_exp,c_tv) -> - (* let exp_str = CilType.Exp.show exp in *) (* contains too many casts, so that matching fails *) - let exp_str = CilType.Exp.show binop in - let c_str = SC.exp_to_string c_exp in - let c_str = Str.global_replace (Str.regexp_string "$key") (D.string_of_key key) c_str in - (* ignore(printf "branch_exp_eq: '%s' '%s' -> %B\n" c_str exp_str (c_str=exp_str)); *) - c_str=exp_str && c_tv=tv - | _ -> false - in - (* filter those edges that are branches, start with a state from states and have the same branch expression and the same tv *) - let branch_edges = List.filter (fun (a,ws,fwd,b,c) -> SC.is_branch c && List.mem a states && branch_exp_eq c exp tv) !edges in - (* there should be only one such edge or none *) - if List.compare_length_with branch_edges 1 <> 0 then ( (* call of branch for an actual branch *) - M.debug ~category:Analyzer "branch: branch_edges length is not 1! -> actual branch"; - M.debug ~category:Analyzer "%s -> branch_edges1: %a" (D.string_of_entry key m) (Pretty.d_list "\n " (fun () x -> Pretty.text (SC.def_to_string (SC.Edge x)))) branch_edges; - (* filter those edges that are branches, end with a state from states have the same branch expression and the same tv *) - (* TODO they should end with any predecessor of the current state, not only the direct predecessor *) - let branch_edges = List.filter (fun (a,ws,fwd,b,c) -> SC.is_branch c && List.mem b states && branch_exp_eq c exp tv) !edges in - M.debug ~category:Analyzer "%s -> branch_edges2: %a" (D.string_of_entry key m) (Pretty.d_list "\n " (fun () x -> Pretty.text (SC.def_to_string (SC.Edge x)))) branch_edges; - if List.compare_length_with branch_edges 1 <> 0 then m else - (* meet current value with the target state. this is tricky: we can not simply take the target state, since there might have been more than one element already before the branching. - -> find out what the alternative branch target was and remove it *) - let (a,ws,fwd,b,c) = List.hd branch_edges in - (* the alternative branch has the same start node, the same branch expression and the negated tv *) - let (a,ws,fwd,b,c) = List.find (fun (a2,ws,fwd,b,c) -> SC.is_branch c && a2=a && branch_exp_eq c exp (not tv)) !edges in - (* now b is the state the alternative branch goes to -> remove it *) - (* TODO may etc. *) - (* being explicit: check how many records there are. if the value is Must b, then we're sure that it is so and we don't remove anything. *) - if D.V.length value = (1,1) then m else (* XX *) - (* there are multiple possible states -> remove b *) - let v2 = D.V.remove_state b value in - (* M.debug ~category:Analyzer @@ "branch: changed state from "^D.V.string_of value^" to "^D.V.string_of v2; *) - D.add key v2 m - ) else (* call of branch directly after splitting *) - let (a,ws,fwd,b,c) = List.hd branch_edges in - (* TODO may etc. *) - let v2 = D.V.set_state b value in - (* M.debug ~category:Analyzer @@ "branch: changed state from "^D.V.string_of value^" to "^D.V.string_of v2; *) - D.add key v2 m - | _ -> M.debug ~category:Analyzer "nothing matched the given BinOp: %a = %a" d_plainexp a d_plainexp b; m - in - match stripCasts (constFold true exp) with - (* somehow there are a lot of casts inside the BinOp which stripCasts only removes when called on the subparts - -> matching as in flagMode didn't work *) - | BinOp (Eq, a, b, _) -> check (stripCasts a) (stripCasts b) tv - | BinOp (Ne, a, b, _) -> check (stripCasts a) (stripCasts b) (not tv) - | UnOp (LNot, a, _) -> check (stripCasts a) (integer 0) tv - (* TODO makes 2 tests fail. probably check changes something it shouldn't *) - (* | Lval _ as a -> check (stripCasts a) (integer 0) (not tv) *) - | e -> M.debug ~category:Analyzer "branch: nothing matched the given exp: %a" d_plainexp e; m - - let body ctx (f:fundec) : D.t = - ctx.local - - let return ctx (exp:exp option) (f:fundec) : D.t = - let m = ctx.local in - (* M.debug ~category:Analyzer @@ "return: ctx.local="^D.short 50 m^D.string_of_callstack m; *) - (* if f.svar.vname <> "main" && BatList.is_empty (D.callstack m) then M.debug ~category:Analyzer @@ "\n\t!!! call stack is empty for function "^f.svar.vname^" !!!"; *) - if f.svar.vname = "main" then ( - let warn_main msg_loc msg_end = (* there is an end warning for local, return or both *) - (* find edges that have 'end' as a target *) - (* we ignore the constraint, TODO maybe find a better syntax for declaring end states *) - let end_states = BatList.filter_map (fun (a,ws,fwd,b,c) -> if b="end" then Some a else None) !edges in - let must_not, may_not = D.filter_values (fun r -> not @@ List.exists (fun end_state -> D.V.in_state end_state r) end_states) m in - let may_not = Set.diff may_not must_not in - (match msg_loc with (* local warnings for entries that must/may not be in an end state *) - | Some msg -> - Set.iter (fun r -> D.warn ~loc:(D.V.loc r) msg) must_not; - Set.iter (fun r -> D.warn ~may:true ~loc:(D.V.loc r) msg) may_not - | None -> ()); - (match msg_end with - | Some msg -> (* warnings at return for entries that must/may not be in an end state *) - let f msg rs = Str.global_replace (Str.regexp_string "$") (D.string_of_keys rs) msg in - if Set.cardinal must_not > 0 then D.warn (f msg must_not); - if Set.cardinal may_not > 0 then D.warn ~may:true (f msg may_not) - | _ -> ()) - in - (* check if there is a warning for entries that are not in an end state *) - match SC.warning "_end" !nodes, SC.warning "_END" !nodes with - | None, None -> () (* nothing to do here *) - | msg_loc,msg_end -> warn_main msg_loc msg_end - ); - (* take care of return value *) - let au = match exp with - | Some(Lval lval) when D.mem (D.key_from_lval lval) m -> (* we return a var in D *) - let k = D.key_from_lval lval in - let varinfo,offset = k in - if varinfo.vglob then - D.alias return_var k m (* if var is global, we alias it *) - else - D.add return_var (D.find' k m) m (* if var is local, we make a copy *) - | _ -> m - in - (* remove formals and locals *) - (* TODO only keep globals like in fileUse *) - List.fold_left (fun m var -> D.remove' (var, `NoOffset) m) au (f.sformals @ f.slocals) - - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - (* M.debug ~category:Analyzer @@ "entering function "^f.vname^D.string_of_callstack ctx.local; *) - if f.svar.vname = "main" then load_specfile (); - let m = if f.svar.vname <> "main" then - D.edit_callstack (BatList.cons (Option.get !Node.current_node)) ctx.local - else ctx.local in [m, m] - - let combine_env ctx lval fexp f args fc au f_ask = - (* M.debug ~category:Analyzer @@ "leaving function "^f.vname^D.string_of_callstack au; *) - let au = D.edit_callstack List.tl au in - (* remove special return var *) - D.remove' return_var au - - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = - let return_val = D.find_option return_var au in - match lval, return_val with - | Some lval, Some v -> - let k = D.key_from_lval lval in - (* handle potential overwrites *) - (* |> check_overwrite_open k *) - (* if v.key is still in D, then it must be a global and we need to alias instead of rebind *) - (* TODO what if there is a local with the same name as the global? *) - if D.V.is_top v then (* returned a local that was top -> just add k as top *) - D.add' k v ctx.local - else (* v is now a local which is not top or a global which is aliased *) - let vvar = D.V.get_alias v in (* this is also ok if v is not an alias since it chooses an element from the May-Set which is never empty (global top gets aliased) *) - if D.mem vvar au then (* returned variable was a global TODO what if local had the same name? -> seems to work *) - (* let _ = M.debug ~category:Analyzer @@ vvar.vname^" was a global -> alias" in *) - D.alias k vvar ctx.local - else (* returned variable was a local *) - let v = D.V.set_key k v in (* adjust var-field to lval *) - (* M.debug ~category:Analyzer @@ vvar.vname^" was a local -> rebind"; *) - D.add' k v ctx.local - | _ -> ctx.local - - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = - let arglist = List.map (Cil.stripCasts) arglist in (* remove casts, TODO safe? *) - let get_key c = match SC.get_key_variant c with - | `Lval s -> - M.debug ~category:Analyzer "Key variant special `Lval %s; %s" s (SC.stmt_to_string c); - lval - | `Arg(s, i) -> - M.debug ~category:Analyzer "Key variant special `Arg(%s, %d). %s" s i (SC.stmt_to_string c); - (try - let arg = List.at arglist i in - match arg with - | Lval x -> Some x (* TODO enough to just assume the arg is already there as a Lval? *) - | AddrOf x -> Some x - | _ -> None - with Invalid_argument s -> - M.debug ~category:Analyzer "Key out of bounds! Msg: %s" s; (* TODO what to do if spec says that there should be more args... *) - None - ) - | _ -> None (* `Rval or `None *) - in - let matches (a,ws,fwd,b,c) = - let equal_args spec_args cil_args = - if List.compare_length_with spec_args 1 = 0 && List.hd spec_args = `Free then - true (* wildcard as an argument matches everything *) - else if List.compare_lengths arglist spec_args <> 0 then ( - M.debug ~category:Analyzer "SKIP the number of arguments doesn't match the specification!"; - false - )else - List.for_all2 (SpecCheck.equal_exp ctx) spec_args cil_args (* TODO Cil.constFold true arg. Test: Spec and c-file: 1+1 *) - in - (* function name must fit the constraint *) - SC.fname_is f.vname c && - (* right form (assignment or not) *) - SC.equal_form lval c && - (* function arguments match those of the constraint *) - equal_args (SC.get_fun_args c) arglist - in - SpecCheck.check ctx get_key matches - - - let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = [D.bot ()] - let threadspawn ctx ~multiple lval f args fctx = ctx.local - let exitstate v = D.bot () -end - -let _ = - MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/analyses/stackTrace.ml b/src/analyses/stackTrace.ml index 3c3bd56640..dd2cedf871 100644 --- a/src/analyses/stackTrace.ml +++ b/src/analyses/stackTrace.ml @@ -36,7 +36,7 @@ struct (* transfer functions *) let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - [ctx.local, D.push !Tracing.current_loc ctx.local] + [ctx.local, D.push !Goblint_tracing.current_loc ctx.local] let combine_env ctx lval fexp f args fc au f_ask = ctx.local (* keep local as opposed to IdentitySpec *) @@ -46,7 +46,7 @@ struct let exitstate v = D.top () let threadenter ctx ~multiple lval f args = - [D.push !Tracing.current_loc ctx.local] + [D.push !Goblint_tracing.current_loc ctx.local] end diff --git a/src/analyses/termination.ml b/src/analyses/termination.ml deleted file mode 100644 index 0563730fb2..0000000000 --- a/src/analyses/termination.ml +++ /dev/null @@ -1,239 +0,0 @@ -(** Termination analysis of loops using counter variables ([term]). *) - -open Batteries -open GoblintCil -open Analyses - -module M = Messages -let (||?) a b = match a,b with Some x,_ | _, Some x -> Some x | _ -> None - -module TermDomain = struct - include SetDomain.ToppedSet (Basetype.Variables) (struct let topname = "All Variables" end) -end - -(* some kind of location string suitable for variable names? *) -let show_location_id l = - string_of_int l.line ^ "_" ^ string_of_int l.column - -class loopCounterVisitor (fd : fundec) = object(self) - inherit nopCilVisitor - method! vstmt s = - let action s = match s.skind with - | Loop (b, loc, eloc, _, _) -> - (* insert loop counter variable *) - let name = "term"^show_location_id loc in - let typ = intType in (* TODO the type should be the same as the one of the original loop counter *) - let v = Cilfacade.create_var (makeLocalVar fd name ~init:(SingleInit zero) typ) in - (* make an init stmt since the init above is apparently ignored *) - let init_stmt = mkStmtOneInstr @@ Set (var v, zero, loc, eloc) in - (* increment it every iteration *) - let inc_stmt = mkStmtOneInstr @@ Set (var v, increm (Lval (var v)) 1, loc, eloc) in - b.bstmts <- inc_stmt :: b.bstmts; - let nb = mkBlock [init_stmt; mkStmt s.skind] in - s.skind <- Block nb; - s - | _ -> s - in ChangeDoChildrenPost (s, action) -end - -let loopBreaks : (int, location) Hashtbl.t = Hashtbl.create 13 (* break stmt sid -> corresponding loop *) -class loopBreaksVisitor (fd : fundec) = object(self) - inherit nopCilVisitor - method! vstmt s = - (match s.skind with - | Loop (b, loc, eloc, Some continue, Some break) -> Hashtbl.add loopBreaks break.sid loc (* TODO: use eloc? *) - | Loop _ -> failwith "Termination.preprocess: every loop should have a break and continue stmt after prepareCFG" - | _ -> ()); - DoChildren -end - -(* if the given block contains a goto while_break.* we have the termination condition for a loop *) -let exits = function - | { bstmts = [{ skind = Goto (stmt, loc); _ }]; _ } -> Hashtbl.find_option loopBreaks !stmt.sid - | _ -> None (* TODO handle return (need to find out what loop we are in) *) - -let lvals_of_expr = - let rec f a = function - | Const _ | SizeOf _ | SizeOfStr _ | AlignOf _ | AddrOfLabel _ -> a - | Lval l | AddrOf l | StartOf l -> l :: a - | SizeOfE e | AlignOfE e | UnOp (_,e,_) | CastE (_,e) | Imag e | Real e -> f a e - | BinOp (_,e1,e2,_) -> f a e1 @ f a e2 - | Question (c,t,e,_) -> f a c @ f a t @ f a e - in f [] - -let loopVars : (location, lval) Hashtbl.t = Hashtbl.create 13 (* loop location -> lval used for exit *) -class loopVarsVisitor (fd : fundec) = object - inherit nopCilVisitor - method! vstmt s = - let add_exit_cond e loc = - match lvals_of_expr e with - | [lval] when Cilfacade.typeOf e |> isArithmeticType -> Hashtbl.add loopVars loc lval - | _ -> () - in - (match s.skind with - | If (e, tb, fb, loc, eloc) -> Option.map_default (add_exit_cond e) () (exits tb ||? exits fb) - | _ -> ()); - DoChildren -end - -let stripCastsDeep e = - let v = object - inherit nopCilVisitor - method! vexpr e = ChangeTo (stripCasts e) - end - in visitCilExpr v e - -(* keep the enclosing loop for statements *) -let cur_loop = ref None (* current loop *) -let cur_loop' = ref None (* for nested loops *) -let makeVar fd loc name = - let id = name ^ "__" ^ show_location_id loc in - try List.find (fun v -> v.vname = id) fd.slocals - with Not_found -> - let typ = intType in (* TODO the type should be the same as the one of the original loop counter *) - Cilfacade.create_var (makeLocalVar fd id ~init:(SingleInit zero) typ) -let f_assume = Lval (var (emptyFunction "__goblint_assume").svar) -let f_check = Lval (var (emptyFunction "__goblint_check").svar) -class loopInstrVisitor (fd : fundec) = object(self) - inherit nopCilVisitor - method! vstmt s = - (* TODO: use Loop eloc? *) - (match s.skind with - | Loop (_, loc, eloc, _, _) -> - cur_loop' := !cur_loop; - cur_loop := Some loc - | _ -> ()); - let action s = - (* first, restore old cur_loop *) - (match s.skind with - | Loop (_, loc, eloc, _, _) -> - cur_loop := !cur_loop'; - | _ -> ()); - let in_loop () = Option.is_some !cur_loop && Hashtbl.mem loopVars (Option.get !cur_loop) in - match s.skind with - | Loop (b, loc, eloc, Some continue, Some break) when Hashtbl.mem loopVars loc -> - (* find loop var for current loop *) - let x = Hashtbl.find loopVars loc in - (* insert loop counter and diff to loop var *) - let t = var @@ makeVar fd loc "t" in - let d1 = var @@ makeVar fd loc "d1" in - let d2 = var @@ makeVar fd loc "d2" in - (* make init stmts *) - let t_init = mkStmtOneInstr @@ Set (t, zero, loc, eloc) in - let d1_init = mkStmtOneInstr @@ Set (d1, Lval x, loc, eloc) in - let d2_init = mkStmtOneInstr @@ Set (d2, Lval x, loc, eloc) in - (* increment/decrement in every iteration *) - let t_inc = mkStmtOneInstr @@ Set (t, increm (Lval t) 1, loc, eloc) in - let d1_inc = mkStmtOneInstr @@ Set (d1, increm (Lval d1) (-1), loc, eloc) in - let d2_inc = mkStmtOneInstr @@ Set (d2, increm (Lval d2) 1 , loc, eloc) in - let typ = intType in - let e1 = BinOp (Eq, Lval t, BinOp (MinusA, Lval x, Lval d1, typ), typ) in - let e2 = BinOp (Eq, Lval t, BinOp (MinusA, Lval d2, Lval x, typ), typ) in - let inv1 = mkStmtOneInstr @@ Call (None, f_assume, [e1], loc, eloc) in - let inv2 = mkStmtOneInstr @@ Call (None, f_assume, [e2], loc, eloc) in - (match b.bstmts with - | cont :: cond :: ss -> - (* changing succs/preds directly doesn't work -> need to replace whole stmts *) - b.bstmts <- cont :: cond :: inv1 :: inv2 :: d1_inc :: d2_inc :: t_inc :: ss; - let nb = mkBlock [t_init; d1_init; d2_init; mkStmt s.skind] in - s.skind <- Block nb; - | _ -> ()); - s - | Loop (b, loc, eloc, Some continue, Some break) -> - print_endline @@ "WARN: Could not determine loop variable for loop at " ^ CilType.Location.show loc; - s - | _ when Hashtbl.mem loopBreaks s.sid -> (* after a loop, we check that t is bounded/positive (no overflow happened) *) - let loc = Hashtbl.find loopBreaks s.sid in - let t = var @@ makeVar fd loc "t" in - let e3 = BinOp (Ge, Lval t, zero, intType) in - let inv3 = mkStmtOneInstr @@ Call (None, f_check, [e3], loc, locUnknown) in - let nb = mkBlock [mkStmt s.skind; inv3] in - s.skind <- Block nb; - s - | Instr [Set (lval, e, loc, eloc)] when in_loop () -> - (* find loop var for current loop *) - let cur_loop = Option.get !cur_loop in - let x = Hashtbl.find loopVars cur_loop in - if x <> lval then - s - else (* we only care about the loop var *) - let d1 = makeVar fd cur_loop "d1" in - let d2 = makeVar fd cur_loop "d2" in - (match stripCastsDeep e with - | BinOp (op, Lval x', e2, typ) when (op = PlusA || op = MinusA) && x' = x && isArithmeticType typ -> (* TODO x = 1 + x, MinusA! *) - (* increase diff by same expr *) - let d1_inc = mkStmtOneInstr @@ Set (var d1, BinOp (PlusA, Lval (var d1), e2, typ), loc, eloc) in - let d2_inc = mkStmtOneInstr @@ Set (var d2, BinOp (PlusA, Lval (var d2), e2, typ), loc, eloc) in - let nb = mkBlock [d1_inc; d2_inc; mkStmt s.skind] in - s.skind <- Block nb; - s - | _ -> - (* otherwise diff is e - counter *) - let t = makeVar fd cur_loop "t" in - let te = Cilfacade.typeOf e in - let dt1 = mkStmtOneInstr @@ Set (var d1, BinOp (MinusA, Lval x, Lval (var t), te), loc, eloc) in - let dt2 = mkStmtOneInstr @@ Set (var d2, BinOp (MinusA, Lval x, Lval (var t), te), loc, eloc) in - let nb = mkBlock [mkStmt s.skind; dt1; dt2] in - s.skind <- Block nb; - s - ) - | _ -> s - in - ChangeDoChildrenPost (s, action) -end - - -module Spec = -struct - include Analyses.IdentitySpec - - let name () = "term" - module D = TermDomain - module C = TermDomain - - (* queries *) - (*let query ctx (q:Queries.t) : Queries.Result.t =*) - (*match q with*) - (*| Queries.MustTerm loc -> `Bool (D.mem v ctx.local)*) - (*| _ -> Queries.Result.top ()*) - - (* transfer functions *) - - let branch ctx (exp:exp) (tv:bool) : D.t = - ctx.local - (* if the then-block contains a goto while_break.* we have the termination condition for a loop *) - (* match !MyCFG.current_node with *) - (* | Some (MyCFG.Statement({ skind = If (e, tb, fb, loc) })) -> *) - (* let str_exit b = match exits b with Some loc -> string_of_int loc.line | None -> "None" in *) - (* M.debug @@ *) - (* "\nCil-exp: " ^ sprint d_exp e *) - (* (*^ "; Goblint-exp: " ^ sprint d_exp exp*) *) - (* ^ "; Goblint: " ^ sprint Queries.Result.pretty (ctx.ask (Queries.EvalInt exp)) *) - (* ^ "\nCurrent block: " ^ (if tv then "Then" else "Else") *) - (* ^ "\nThen block (exits " ^ str_exit tb ^ "): " ^ sprint d_block tb *) - (* ^ "\nElse block (exits " ^ str_exit fb ^ "): " ^ sprint d_block fb *) - (* ; *) - (* ctx.local *) - (* | _ -> ctx.local *) - - let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = [D.bot ()] - let exitstate v = D.bot () -end - -class recomputeVisitor (fd : fundec) = object(self) - inherit nopCilVisitor - method! vfunc fd = - computeCFGInfo fd true; - SkipChildren -end - -let _ = - (* Cilfacade.register_preprocess Spec.name (new loopCounterVisitor); *) - Cilfacade.register_preprocess (Spec.name ()) (new loopBreaksVisitor); - Cilfacade.register_preprocess (Spec.name ()) (new loopVarsVisitor); - Cilfacade.register_preprocess (Spec.name ()) (new loopInstrVisitor); - Cilfacade.register_preprocess (Spec.name ()) (new recomputeVisitor); - Hashtbl.clear loopBreaks; (* because the sids are now different *) - Cilfacade.register_preprocess (Spec.name ()) (new loopBreaksVisitor); - MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/analyses/threadAnalysis.ml b/src/analyses/threadAnalysis.ml index 0264f4b700..01c5dd87fa 100644 --- a/src/analyses/threadAnalysis.ml +++ b/src/analyses/threadAnalysis.ml @@ -22,12 +22,15 @@ struct module P = IdentityP (D) (* transfer functions *) - let return ctx (exp:exp option) (f:fundec) : D.t = + let handle_thread_return ctx (exp: exp option) = let tid = ThreadId.get_current (Analyses.ask_of_ctx ctx) in - begin match tid with + match tid with | `Lifted tid -> ctx.sideg tid (false, TS.bot (), not (D.is_empty ctx.local)) | _ -> () - end; + + let return ctx (exp:exp option) _ : D.t = + if ctx.ask Queries.MayBeThreadReturn then + handle_thread_return ctx exp; ctx.local let rec is_not_unique ctx tid = @@ -64,6 +67,9 @@ struct | [t] -> join_thread ctx.local t (* single thread *) | _ -> ctx.local (* if several possible threads are may-joined, none are must-joined *) | exception SetDomain.Unsupported _ -> ctx.local) + | ThreadExit { ret_val } -> + handle_thread_return ctx (Some ret_val); + ctx.local | _ -> ctx.local let query ctx (type a) (q: a Queries.t): a Queries.result = diff --git a/src/analyses/threadFlag.ml b/src/analyses/threadFlag.ml index 6bd466caef..a751ae074a 100644 --- a/src/analyses/threadFlag.ml +++ b/src/analyses/threadFlag.ml @@ -21,6 +21,8 @@ struct module D = Flag module C = Flag module P = IdentityP (D) + module V = UnitV + module G = BoolDomain.MayBool let name () = "threadflag" @@ -44,6 +46,7 @@ struct match x with | Queries.MustBeSingleThreaded _ -> not (Flag.is_multi ctx.local) (* If this analysis can tell, it is the case since the start *) | Queries.MustBeUniqueThread -> not (Flag.is_not_main ctx.local) + | Queries.IsEverMultiThreaded -> (ctx.global () : bool) (* requires annotation to compile *) (* This used to be in base but also commented out. *) (* | Queries.MayBePublic _ -> Flag.is_multi ctx.local *) | _ -> Queries.Result.top x @@ -64,6 +67,7 @@ struct [create_tid f] let threadspawn ctx ~multiple lval f args fctx = + ctx.sideg () true; if not (has_ever_been_multi (Analyses.ask_of_ctx ctx)) then ctx.emit Events.EnterMultiThreaded; D.join ctx.local (Flag.get_main ()) diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 43707acd1e..5895f242c9 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -200,6 +200,46 @@ struct M.warn ~category:Witness ~loc:msgLoc "couldn't locate invariant: %s" inv in + let unassume_invariant_set (invariant_set: YamlWitnessType.InvariantSet.t) = + + let unassume_location_invariant (location_invariant: YamlWitnessType.InvariantSet.LocationInvariant.t) = + let loc = loc_of_location location_invariant.location in + let inv = location_invariant.value in + let msgLoc: M.Location.t = CilLocation loc in + + match Locator.find_opt locator loc with + | Some nodes -> + unassume_nodes_invariant ~loc ~nodes inv + | None -> + M.warn ~category:Witness ~loc:msgLoc "couldn't locate invariant: %s" inv + in + + let unassume_loop_invariant (loop_invariant: YamlWitnessType.InvariantSet.LoopInvariant.t) = + let loc = loc_of_location loop_invariant.location in + let inv = loop_invariant.value in + let msgLoc: M.Location.t = CilLocation loc in + + match Locator.find_opt loop_locator loc with + | Some nodes -> + unassume_nodes_invariant ~loc ~nodes inv + | None -> + M.warn ~category:Witness ~loc:msgLoc "couldn't locate invariant: %s" inv + in + + let validate_invariant (invariant: YamlWitnessType.InvariantSet.Invariant.t) = + let target_type = YamlWitnessType.InvariantSet.InvariantType.invariant_type invariant.invariant_type in + match YamlWitness.invariant_type_enabled target_type, invariant.invariant_type with + | true, LocationInvariant x -> + unassume_location_invariant x + | true, LoopInvariant x -> + unassume_loop_invariant x + | false, (LocationInvariant _ | LoopInvariant _) -> + M.info_noloc ~category:Witness "disabled invariant of type %s" target_type + in + + List.iter validate_invariant invariant_set.content + in + match YamlWitness.entry_type_enabled target_type, entry.entry_type with | true, LocationInvariant x -> unassume_location_invariant x @@ -207,7 +247,9 @@ struct unassume_loop_invariant x | true, PreconditionLoopInvariant x -> unassume_precondition_loop_invariant x - | false, (LocationInvariant _ | LoopInvariant _ | PreconditionLoopInvariant _) -> + | true, InvariantSet x -> + unassume_invariant_set x + | false, (LocationInvariant _ | LoopInvariant _ | PreconditionLoopInvariant _ | InvariantSet _) -> M.info_noloc ~category:Witness "disabled entry of type %s" target_type | _ -> M.info_noloc ~category:Witness "cannot unassume entry of type %s" target_type diff --git a/src/autoTune.ml b/src/autoTune.ml index 06347f3190..3cda36a302 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -99,7 +99,9 @@ let rec setCongruenceRecursive fd depth neigbourFunction = FunctionSet.iter (fun vinfo -> print_endline (" " ^ vinfo.vname); - setCongruenceRecursive (Cilfacade.find_varinfo_fundec vinfo) (depth -1) neigbourFunction + match Cilfacade.find_varinfo_fundec vinfo with + | fd -> setCongruenceRecursive fd (depth -1) neigbourFunction + | exception Not_found -> () (* Happens for __goblint_bounded *) ) (FunctionSet.filter (*for extern and builtin functions there is no function definition in CIL*) (fun x -> not (isExtern x.vstorage || BatString.starts_with x.vname "__builtin")) @@ -180,11 +182,11 @@ let enableAnalyses anas = List.iter (GobConfig.set_auto "ana.activated[+]") anas (*If only one thread is used in the program, we can disable most thread analyses*) -(*The exceptions are analyses that are depended on by others: base -> mutex -> mutexEvents, access*) +(*The exceptions are analyses that are depended on by others: base -> mutex -> mutexEvents, access; termination -> threadflag *) (*escape is also still enabled, because otherwise we get a warning*) (*does not consider dynamic calls!*) -let notNeccessaryThreadAnalyses = ["race"; "deadlock"; "maylocks"; "symb_locks"; "thread"; "threadid"; "threadJoins"; "threadreturn"] +let notNeccessaryThreadAnalyses = ["race"; "deadlock"; "maylocks"; "symb_locks"; "thread"; "threadid"; "threadJoins"; "threadreturn"; "mhp"; "region"] let reduceThreadAnalyses () = let isThreadCreate = function | LibraryDesc.ThreadCreate _ -> true @@ -198,7 +200,7 @@ let reduceThreadAnalyses () = (* This is run independent of the autotuner being enabled or not to be sound in the presence of setjmp/longjmp *) (* It is done this way around to allow enabling some of these analyses also for programs without longjmp *) -let longjmpAnalyses = ["activeLongjmp"; "activeSetjmp"; "taintPartialContexts"; "modifiedSinceLongjmp"; "poisonVariables"; "expsplit"; "vla"] +let longjmpAnalyses = ["activeLongjmp"; "activeSetjmp"; "taintPartialContexts"; "modifiedSinceSetjmp"; "poisonVariables"; "expsplit"; "vla"] let activateLongjmpAnalysesWhenRequired () = let isLongjmp = function @@ -222,13 +224,13 @@ let focusOnMemSafetySpecification (spec: Svcomp.Specification.t) = print_endline "Setting \"cil.addNestedScopeAttr\" to true"; set_bool "cil.addNestedScopeAttr" true; print_endline @@ "Specification: ValidDeref -> enabling memOutOfBounds analysis \"" ^ (String.concat ", " memOobAna) ^ "\""; - enableAnalyses memOobAna + enableAnalyses memOobAna; | ValidMemtrack | ValidMemcleanup -> (* Enable the memLeak analysis *) let memLeakAna = ["memLeak"] in if (get_int "ana.malloc.unique_address_count") < 1 then ( - print_endline "Setting \"ana.malloc.unique_address_count\" to 1"; - set_int "ana.malloc.unique_address_count" 1; + print_endline "Setting \"ana.malloc.unique_address_count\" to 5"; + set_int "ana.malloc.unique_address_count" 5; ); print_endline @@ "Specification: ValidMemtrack and ValidMemcleanup -> enabling memLeak analysis \"" ^ (String.concat ", " memLeakAna) ^ "\""; enableAnalyses memLeakAna @@ -237,6 +239,21 @@ let focusOnMemSafetySpecification (spec: Svcomp.Specification.t) = let focusOnMemSafetySpecification () = List.iter focusOnMemSafetySpecification (Svcomp.Specification.of_option ()) +let focusOnTermination (spec: Svcomp.Specification.t) = + match spec with + | Termination -> + let terminationAnas = ["termination"; "threadflag"; "apron"] in + print_endline @@ "Specification: Termination -> enabling termination analyses \"" ^ (String.concat ", " terminationAnas) ^ "\""; + enableAnalyses terminationAnas; + set_string "sem.int.signed_overflow" "assume_none"; + set_bool "ana.int.interval" true; + set_string "ana.apron.domain" "polyhedra"; (* TODO: Needed? *) + () + | _ -> () + +let focusOnTermination () = + List.iter focusOnTermination (Svcomp.Specification.of_option ()) + let focusOnSpecification (spec: Svcomp.Specification.t) = match spec with | UnreachCall s -> () @@ -458,6 +475,17 @@ let wideningOption factors file = print_endline "Enabled widening thresholds"; } +let activateTmpSpecialAnalysis () = + let isMathFun = function + | LibraryDesc.Math _ -> true + | _ -> false + in + let hasMathFunctions = hasFunction isMathFun in + if hasMathFunctions then ( + print_endline @@ "math function -> enabling tmpSpecial analysis and floating-point domain"; + enableAnalyses ["tmpSpecial"]; + set_bool "ana.float.interval" true; + ) let estimateComplexity factors file = let pathsEstimate = factors.loops + factors.controlFlowStatements / 90 in @@ -487,6 +515,14 @@ let chooseFromOptions costTarget options = let isActivated a = get_bool "ana.autotune.enabled" && List.mem a @@ get_string_list "ana.autotune.activated" +let isTerminationTask () = List.mem Svcomp.Specification.Termination (Svcomp.Specification.of_option ()) + +let specificationIsActivated () = + isActivated "specification" && get_string "ana.specification" <> "" + +let specificationTerminationIsActivated () = + isActivated "termination" + let chooseConfig file = let factors = collectFactors visitCilFileSameGlobals file in let fileCompplexity = estimateComplexity factors file in @@ -506,7 +542,7 @@ let chooseConfig file = if isActivated "mallocWrappers" then findMallocWrappers (); - if isActivated "specification" && get_string "ana.specification" <> "" then + if specificationIsActivated () then focusOnSpecification (); if isActivated "enums" && hasEnums file then @@ -518,12 +554,15 @@ let chooseConfig file = if isActivated "arrayDomain" then selectArrayDomains file; + if isActivated "tmpSpecialAnalysis" then + activateTmpSpecialAnalysis (); + let options = [] in let options = if isActivated "congruence" then (congruenceOption factors file)::options else options in - let options = if isActivated "octagon" then (apronOctagonOption factors file)::options else options in + (* Termination analysis uses apron in a different configuration. *) + let options = if isActivated "octagon" && not (isTerminationTask ()) then (apronOctagonOption factors file)::options else options in let options = if isActivated "wideningThresholds" then (wideningOption factors file)::options else options in List.iter (fun o -> o.activate ()) @@ chooseFromOptions (totalTarget - fileCompplexity) options - let reset_lazy () = ResettableLazy.reset functionCallMaps diff --git a/src/build-info/dune b/src/build-info/dune index c1de250263..ff8d68671b 100644 --- a/src/build-info/dune +++ b/src/build-info/dune @@ -27,3 +27,6 @@ (mode (promote (until-clean) (only configOcaml.ml))) ; replace existing file in source tree, even if releasing (only overrides) (action (write-file %{target} "(* Automatically regenerated, changes do not persist! *)\nlet flambda = \"%{ocaml-config:flambda}\""))) +(env + (_ + (flags (:standard -w -no-cmx-file)))) ; suppress warning from flambda compiler bug: https://github.com/ocaml/dune/issues/3277 diff --git a/src/cdomains/addressDomain.ml b/src/cdomains/addressDomain.ml index 5981caf9ea..55b1aceefc 100644 --- a/src/cdomains/addressDomain.ml +++ b/src/cdomains/addressDomain.ml @@ -5,6 +5,7 @@ open IntOps module M = Messages module Mval_outer = Mval +module SD = StringDomain module AddressBase (Mval: Printable.S) = @@ -14,23 +15,14 @@ struct | Addr of Mval.t | NullPtr | UnknownPtr - | StrPtr of string option + | StrPtr of SD.t [@@deriving eq, ord, hash] (* TODO: StrPtr equal problematic if the same literal appears more than once *) let name () = Format.sprintf "address (%s)" (Mval.name ()) - let hash x = match x with - | StrPtr _ -> - if GobConfig.get_bool "ana.base.limit-string-addresses" then - 13859 - else - hash x - | _ -> hash x - let show = function | Addr m -> Mval.show m - | StrPtr (Some x) -> "\"" ^ x ^ "\"" - | StrPtr None -> "(unknown string)" + | StrPtr s -> StringDomain.show s | UnknownPtr -> "?" | NullPtr -> "NULL" @@ -42,31 +34,18 @@ struct ) (* strings *) - let of_string x = StrPtr (Some x) + let of_string x = StrPtr (SD.of_string x) let to_string = function - | StrPtr (Some x) -> Some x + | StrPtr s -> SD.to_string s | _ -> None - (* only keep part before first null byte *) let to_c_string = function - | StrPtr (Some x) -> - begin match String.split_on_char '\x00' x with - | s::_ -> Some s - | [] -> None - end + | StrPtr s -> SD.to_c_string s | _ -> None - let to_n_c_string n x = - match to_c_string x with - | Some x -> - if n > String.length x then - Some x - else if n < 0 then - None - else - Some (String.sub x 0 n) + let to_n_c_string n = function + | StrPtr s -> SD.to_n_c_string n s | _ -> None - let to_string_length x = - match to_c_string x with - | Some x -> Some (String.length x) + let to_string_length = function + | StrPtr s -> SD.to_string_length s | _ -> None let arbitrary () = QCheck.always UnknownPtr (* S TODO: non-unknown *) @@ -101,8 +80,7 @@ struct (* TODO: seems to be unused *) let to_exp = function | Addr m -> AddrOf (Mval.to_cil m) - | StrPtr (Some x) -> mkString x - | StrPtr None -> raise (Lattice.Unsupported "Cannot express unknown string pointer as expression.") + | StrPtr s -> SD.to_exp s | NullPtr -> integer 0 | UnknownPtr -> raise Lattice.TopValue (* TODO: unused *) @@ -123,9 +101,7 @@ struct let semantic_equal x y = match x, y with | Addr x, Addr y -> Mval.semantic_equal x y - | StrPtr None, StrPtr _ - | StrPtr _, StrPtr None -> Some true - | StrPtr (Some a), StrPtr (Some b) -> if a = b then None else Some false + | StrPtr s1, StrPtr s2 -> SD.semantic_equal s1 s2 | NullPtr, NullPtr -> Some true | UnknownPtr, UnknownPtr | UnknownPtr, Addr _ @@ -135,8 +111,7 @@ struct | _, _ -> Some false let leq x y = match x, y with - | StrPtr _, StrPtr None -> true - | StrPtr a, StrPtr b -> a = b + | StrPtr s1, StrPtr s2 -> SD.leq s1 s2 | Addr x, Addr y -> Mval.leq x y | _ -> x = y @@ -144,26 +119,6 @@ struct | Addr x -> Addr (Mval.top_indices x) | x -> x - let join_string_ptr x y = match x, y with - | None, _ - | _, None -> None - | Some a, Some b when a = b -> Some a - | Some a, Some b (* when a <> b *) -> - if GobConfig.get_bool "ana.base.limit-string-addresses" then - None - else - raise Lattice.Uncomparable - - let meet_string_ptr x y = match x, y with - | None, a - | a, None -> a - | Some a, Some b when a = b -> Some a - | Some a, Some b (* when a <> b *) -> - if GobConfig.get_bool "ana.base.limit-string-addresses" then - raise Lattice.BotValue - else - raise Lattice.Uncomparable - let merge mop sop x y = match x, y with | UnknownPtr, UnknownPtr -> UnknownPtr @@ -172,10 +127,10 @@ struct | Addr x, Addr y -> Addr (mop x y) | _ -> raise Lattice.Uncomparable - let join = merge Mval.join join_string_ptr - let widen = merge Mval.widen join_string_ptr - let meet = merge Mval.meet meet_string_ptr - let narrow = merge Mval.narrow meet_string_ptr + let join = merge Mval.join SD.join + let widen = merge Mval.widen SD.join + let meet = merge Mval.meet SD.meet + let narrow = merge Mval.narrow SD.meet include Lattice.NoBotTop @@ -194,8 +149,7 @@ struct let of_elt (x: elt): t = match x with | Addr (v, o) -> Addr v - | StrPtr _ when GobConfig.get_bool "ana.base.limit-string-addresses" -> StrPtr None (* all strings together if limited *) - | StrPtr x -> StrPtr x (* everything else is kept separate, including strings if not limited *) + | StrPtr s -> StrPtr (SD.repr s) | NullPtr -> NullPtr | UnknownPtr -> UnknownPtr end @@ -211,8 +165,7 @@ struct let of_elt (x: elt): t = match x with | Addr (v, o) -> Addr (v, Offset.Unit.of_offs o) (* addrs grouped by var and part of offset *) - | StrPtr _ when GobConfig.get_bool "ana.base.limit-string-addresses" -> StrPtr None (* all strings together if limited *) - | StrPtr x -> StrPtr x (* everything else is kept separate, including strings if not limited *) + | StrPtr s -> StrPtr (SD.repr s) | NullPtr -> NullPtr | UnknownPtr -> UnknownPtr end diff --git a/src/cdomains/addressDomain_intf.ml b/src/cdomains/addressDomain_intf.ml index 0ef3d6dd8d..f65b2977c4 100644 --- a/src/cdomains/addressDomain_intf.ml +++ b/src/cdomains/addressDomain_intf.ml @@ -7,7 +7,7 @@ sig | Addr of Mval.t (** Pointer to mvalue. *) | NullPtr (** NULL pointer. *) | UnknownPtr (** Unknown pointer. Could point to globals, heap and escaped variables. *) - | StrPtr of string option (** String literal pointer. [StrPtr None] abstracts any string pointer *) + | StrPtr of StringDomain.t (** String literal pointer. [StrPtr None] abstracts any string pointer *) include Printable.S with type t := t (** @closed *) val of_string: string -> t @@ -16,8 +16,6 @@ sig val to_string: t -> string option (** Convert {!StrPtr} to string if possible. *) - (** C strings are different from OCaml strings as they are not processed after the first [NUL] byte, even though the OCaml string (and a C string literal) may be longer. *) - val to_c_string: t -> string option (** Convert {!StrPtr} to C string if possible. *) @@ -71,7 +69,7 @@ sig - Each {!Addr}, modulo precise index expressions in the offset, is a sublattice with ordering induced by {!Mval}. - {!NullPtr} is a singleton sublattice. - {!UnknownPtr} is a singleton sublattice. - - If [ana.base.limit-string-addresses] is enabled, then all {!StrPtr} are together in one sublattice with flat ordering. If [ana.base.limit-string-addresses] is disabled, then each {!StrPtr} is a singleton sublattice. *) + - If [ana.base.strings.domain] is disjoint, then each {!StrPtr} is a singleton sublattice. Otherwise, all {!StrPtr} are together in one sublattice with flat ordering. *) module AddressLattice (Mval: Mval.Lattice): sig include module type of AddressPrintable (Mval) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index e4cf37fc83..110d55ee8e 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -10,12 +10,11 @@ open Batteries open GoblintCil open Pretty module M = Messages -open Apron +open GobApron open VectorMatrix module Mpqf = SharedFunctions.Mpqf -module Var = SharedFunctions.Var -module V = RelationDomain.V(Var) +module V = RelationDomain.V (** It defines the type t of the affine equality domain (a struct that contains an optional matrix and an apron environment) and provides the functions needed for handling variables (which are defined by RelationDomain.D2) such as add_vars remove_vars. Furthermore, it provides the function get_coeff_vec that parses an apron expression into a vector of coefficients if the apron expression has an affine form. *) @@ -356,6 +355,7 @@ struct let assign_exp (t: VarManagement(Vc)(Mx).t) var exp (no_ov: bool Lazy.t) = let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in + (* TODO: Do we need to do a constant folding here? It happens for texpr1_of_cil_exp *) match Convert.texpr1_expr_of_cil_exp t t.env exp (Lazy.force no_ov) with | exp -> assign_texpr t var exp | exception Convert.Unsupported_CilExp _ -> diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index 7dffafe967..03b9558621 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -4,7 +4,7 @@ open Batteries open GoblintCil open Pretty (* A binding to a selection of Apron-Domains *) -open Apron +open GobApron open RelationDomain open SharedFunctions @@ -29,8 +29,7 @@ let widening_thresholds_apron = ResettableLazy.from_fun (fun () -> let reset_lazy () = ResettableLazy.reset widening_thresholds_apron -module Var = SharedFunctions.Var -module V = RelationDomain.V(Var) +module V = RelationDomain.V module type Manager = @@ -209,7 +208,6 @@ module type AOpsExtra = sig type t val copy : t -> t - val vars_as_array : t -> Var.t array val vars : t -> Var.t list type marshal val unmarshal : marshal -> t @@ -248,15 +246,6 @@ struct let copy = A.copy Man.mgr - let vars_as_array d = - let ivs, fvs = Environment.vars (A.env d) in - assert (Array.length fvs = 0); (* shouldn't ever contain floats *) - ivs - - let vars d = - let ivs = vars_as_array d in - List.of_enum (Array.enum ivs) - (* marshal type: Abstract0.t and an array of var names *) type marshal = Man.mt Abstract0.t * string array @@ -266,31 +255,24 @@ struct let env = Environment.make vars [||] in {abstract0; env} + let vars x = Environment.ivars_only @@ A.env x + let marshal (x: t): marshal = - let vars = Array.map Var.to_string (vars_as_array x) in + let vars = Array.map Var.to_string (Array.of_list (vars x)) in x.abstract0, vars let mem_var d v = Environment.mem_var (A.env d) v - let add_vars_with nd vs = - let env' = EnvOps.add_vars (A.env nd) vs in + let envop f nd a = + let env' = f (A.env nd) a in A.change_environment_with Man.mgr nd env' false - let remove_vars_with nd vs = - let env' = EnvOps.remove_vars (A.env nd) vs in - A.change_environment_with Man.mgr nd env' false + let add_vars_with = envop Environment.add_vars + let remove_vars_with = envop Environment.remove_vars + let remove_filter_with = envop Environment.remove_filter + let keep_vars_with = envop Environment.keep_vars + let keep_filter_with = envop Environment.keep_filter - let remove_filter_with nd f = - let env' = EnvOps.remove_filter (A.env nd) f in - A.change_environment_with Man.mgr nd env' false - - let keep_vars_with nd vs = - let env' = EnvOps.keep_vars (A.env nd) vs in - A.change_environment_with Man.mgr nd env' false - - let keep_filter_with nd f = - let env' = EnvOps.keep_filter (A.env nd) f in - A.change_environment_with Man.mgr nd env' false let forget_vars_with nd vs = (* Unlike keep_vars_with, this doesn't check mem_var, but assumes valid vars, like assigns *) @@ -497,9 +479,9 @@ struct let to_yojson (x: t) = let constraints = A.to_lincons_array Man.mgr x - |> SharedFunctions.Lincons1Set.of_earray - |> SharedFunctions.Lincons1Set.elements - |> List.map (fun lincons1 -> `String (SharedFunctions.Lincons1.show lincons1)) + |> Lincons1Set.of_earray + |> Lincons1Set.elements + |> List.map (fun lincons1 -> `String (Lincons1.show lincons1)) in let env = `String (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (A.env x)) in @@ -886,7 +868,6 @@ struct let unmarshal (b, d) = (BoxD.unmarshal b, D.unmarshal d) let mem_var (_, d) v = D.mem_var d v - let vars_as_array (_, d) = D.vars_as_array d let vars (_, d) = D.vars d let pretty_diff () ((_, d1), (_, d2)) = D.pretty_diff () (d1, d2) diff --git a/src/cdomains/apron/gobApron.apron.ml b/src/cdomains/apron/gobApron.apron.ml new file mode 100644 index 0000000000..c39a3e42db --- /dev/null +++ b/src/cdomains/apron/gobApron.apron.ml @@ -0,0 +1,98 @@ +open Batteries +include Apron + +module Var = +struct + include Var + let equal x y = Var.compare x y = 0 +end + +module Lincons1 = +struct + include Lincons1 + + let show = Format.asprintf "%a" print + let compare x y = String.compare (show x) (show y) (* HACK *) + + let num_vars x = + (* Apron.Linexpr0.get_size returns some internal nonsense, so we count ourselves. *) + let size = ref 0 in + Lincons1.iter (fun coeff var -> + if not (Apron.Coeff.is_zero coeff) then + incr size + ) x; + !size +end + +module Lincons1Set = +struct + include Set.Make (Lincons1) + + let of_earray ({lincons0_array; array_env}: Lincons1.earray): t = + Array.enum lincons0_array + |> Enum.map (fun (lincons0: Lincons0.t) -> + Lincons1.{lincons0; env = array_env} + ) + |> of_enum +end + +(** A few code elements for environment changes from functions as remove_vars etc. have been moved to sharedFunctions as they are needed in a similar way inside affineEqualityDomain. + A module that includes various methods used by variable handling operations such as add_vars, remove_vars etc. in apronDomain and affineEqualityDomain. *) +module Environment = +struct + include Environment + + let ivars_only env = + let ivs, fvs = Environment.vars env in + assert (Array.length fvs = 0); (* shouldn't ever contain floats *) + List.of_enum (Array.enum ivs) + + let add_vars env vs = + let vs' = + vs + |> List.enum + |> Enum.filter (fun v -> not (Environment.mem_var env v)) + |> Array.of_enum + in + Environment.add env vs' [||] + + let remove_vars env vs = + let vs' = + vs + |> List.enum + |> Enum.filter (fun v -> Environment.mem_var env v) + |> Array.of_enum + in + Environment.remove env vs' + + let remove_filter env f = + let vs' = + ivars_only env + |> List.enum + |> Enum.filter f + |> Array.of_enum + in + Environment.remove env vs' + + let keep_vars env vs = + (* Instead of iterating over all vars in env and doing a linear lookup in vs just to remove them, + make a new env with just the desired vs. *) + let vs' = + vs + |> List.enum + |> Enum.filter (fun v -> Environment.mem_var env v) + |> Array.of_enum + in + Environment.make vs' [||] + + let keep_filter env f = + (* Instead of removing undesired vars, + make a new env with just the desired vars. *) + let vs' = + ivars_only env + |> List.enum + |> Enum.filter f + |> Array.of_enum + in + Environment.make vs' [||] +end diff --git a/src/cdomains/apron/gobApron.no-apron.ml b/src/cdomains/apron/gobApron.no-apron.ml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 04ee49049d..9c0e846b32 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -183,8 +183,7 @@ module EqualitiesArray = struct end -module Var = SharedFunctions.Var -module V = RelationDomain.V(Var) +module V = RelationDomain.V (** It defines the type t of the affine equality domain (a struct that contains an optional matrix and an apron environment) and provides the functions needed for handling variables (which are defined by RelationDomain.D2) such as add_vars remove_vars. Furthermore, it provides the function get_coeff_vec that parses an apron expression into a vector of coefficients if the apron expression has an affine form. *) diff --git a/src/cdomains/apron/relationDomain.apron.ml b/src/cdomains/apron/relationDomain.apron.ml index c5b6a0a89b..48720b0382 100644 --- a/src/cdomains/apron/relationDomain.apron.ml +++ b/src/cdomains/apron/relationDomain.apron.ml @@ -2,42 +2,16 @@ See {!ApronDomain} and {!AffineEqualityDomain}. *) +open GobApron open Batteries open GoblintCil -(** Abstracts the extended apron Var. *) -module type Var = -sig - type t - val compare : t -> t -> int - val of_string : string -> t - val to_string : t -> string - val hash : t -> int - val equal : t -> t -> bool -end - module type VarMetadata = sig type t val var_name: t -> string end -module VarMetadataTbl (VM: VarMetadata) (Var: Var) = -struct - module VH = Hashtbl.Make (Var) - - let vh = VH.create 113 - - let make_var ?name metadata = - let name = Option.default_delayed (fun () -> VM.var_name metadata) name in - let var = Var.of_string name in - VH.replace vh var metadata; - var - - let find_metadata (var: Var.t) = - VH.find_option vh var -end - module VM = struct type t = @@ -55,10 +29,26 @@ struct | Global g -> g.vname end +module VarMetadataTbl (VM: VarMetadata) = +struct + module VH = Hashtbl.Make (Var) + + let vh = VH.create 113 + + let make_var ?name metadata = + let name = Option.default_delayed (fun () -> VM.var_name metadata) name in + let var = Var.of_string name in + VH.replace vh var metadata; + var + + let find_metadata (var: Var.t) = + VH.find_option vh var +end + module type RV = sig - type t - type vartable + type t = Var.t + type vartable = VM.t VarMetadataTbl (VM).VH.t val vh: vartable val make_var: ?name:string -> VM.t -> t @@ -70,12 +60,13 @@ sig val to_cil_varinfo: t -> varinfo Option.t end -module V (Var: Var): (RV with type t = Var.t and type vartable = VM.t VarMetadataTbl (VM) (Var).VH.t) = +module V: RV = struct + open VM + type t = Var.t - module VMT = VarMetadataTbl (VM) (Var) + module VMT = VarMetadataTbl (VM) include VMT - open VM type vartable = VM.t VMT.VH.t @@ -90,12 +81,6 @@ struct | _ -> None end -module type LinCons = -sig - type t - val num_vars: t -> int -end - module type Tracked = sig val type_tracked: typ -> bool @@ -105,7 +90,7 @@ end module type S2 = sig type t - type var + type var = Var.t type marshal module Tracked: Tracked @@ -144,8 +129,8 @@ module type S3 = sig include S2 - val cil_exp_of_lincons1: Apron.Lincons1.t -> exp option - val invariant: t -> Apron.Lincons1.t list + val cil_exp_of_lincons1: Lincons1.t -> exp option + val invariant: t -> Lincons1.t list end type ('a, 'b) relcomponents_t = { @@ -184,10 +169,9 @@ struct let name () = RD.name () ^ " * " ^ PrivD.name () - let of_tuple(rel, priv):t = {rel; priv} - let to_tuple r = (r.rel, r.priv) - let arbitrary () = + let to_tuple r = (r.rel, r.priv) in + let of_tuple (rel, priv) = {rel; priv} in let tr = QCheck.pair (RD.arbitrary ()) (PrivD.arbitrary ()) in QCheck.map ~rev:to_tuple of_tuple tr @@ -216,7 +200,6 @@ end module type RD = sig - module Var : Var - module V : module type of struct include V(Var) end - include S3 with type var = Var.t + module V : RV + include S3 end diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index f06bb66165..4a66ab0381 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -2,48 +2,12 @@ open GoblintCil open Batteries -open Apron +open GobApron module M = Messages module BI = IntOps.BigIntOps -module Var = -struct - include Var - - let equal x y = Var.compare x y = 0 -end - -module Lincons1 = -struct - include Lincons1 - - let show = Format.asprintf "%a" print - let compare x y = String.compare (show x) (show y) (* HACK *) - - let num_vars x = - (* Apron.Linexpr0.get_size returns some internal nonsense, so we count ourselves. *) - let size = ref 0 in - Lincons1.iter (fun coeff var -> - if not (Apron.Coeff.is_zero coeff) then - incr size - ) x; - !size -end - -module Lincons1Set = -struct - include Set.Make (Lincons1) - - let of_earray ({lincons0_array; array_env}: Lincons1.earray): t = - Array.enum lincons0_array - |> Enum.map (fun (lincons0: Lincons0.t) -> - Lincons1.{lincons0; env = array_env} - ) - |> of_enum -end - let int_of_scalar ?round (scalar: Scalar.t) = if Scalar.is_infty scalar <> 0 then (* infinity means unbounded *) None @@ -291,66 +255,6 @@ struct include CilOfApron (V) end -(** A few code elements for environment changes from functions as remove_vars etc. have been moved to sharedFunctions as they are needed in a similar way inside affineEqualityDomain. - A module that includes various methods used by variable handling operations such as add_vars, remove_vars etc. in apronDomain and affineEqualityDomain. *) -module EnvOps = -struct - let vars env = - let ivs, fvs = Environment.vars env in - assert (Array.length fvs = 0); (* shouldn't ever contain floats *) - List.of_enum (Array.enum ivs) - - let add_vars env vs = - let vs' = - vs - |> List.enum - |> Enum.filter (fun v -> not (Environment.mem_var env v)) - |> Array.of_enum - in - Environment.add env vs' [||] - - let remove_vars env vs = - let vs' = - vs - |> List.enum - |> Enum.filter (fun v -> Environment.mem_var env v) - |> Array.of_enum - in - Environment.remove env vs' - - let remove_filter env f = - let vs' = - vars env - |> List.enum - |> Enum.filter f - |> Array.of_enum - in - Environment.remove env vs' - - let keep_vars env vs = - (* Instead of iterating over all vars in env and doing a linear lookup in vs just to remove them, - make a new env with just the desired vs. *) - let vs' = - vs - |> List.enum - |> Enum.filter (fun v -> Environment.mem_var env v) - |> Array.of_enum - in - Environment.make vs' [||] - - let keep_filter env f = - (* Instead of removing undesired vars, - make a new env with just the desired vars. *) - let vs' = - vars env - |> List.enum - |> Enum.filter f - |> Array.of_enum - in - Environment.make vs' [||] - -end - (* Abstraction for the domain representations of the relational analyses: affineEqualityDomain (uses a matrix) and linearTwoVarEqualityDomain (uses an Array)*) module type AbstractRelationalDomainRepresentation = @@ -376,7 +280,6 @@ end used by affineEqualityDomain and linearTwoVarEqualityDomain *) module VarManagementOps (RelDomain : AbstractRelationalDomainRepresentation) = struct - include EnvOps type t = { mutable d : RelDomain.t option; mutable env : Environment.t @@ -419,17 +322,17 @@ struct let change_d t new_env add del = VectorMatrix.timing_wrap "dimension change" (change_d t new_env add) del - + let vars x = Environment.ivars_only x.env let add_vars t vars = let t = copy t in - let env' = add_vars t.env vars in + let env' = Environment.add_vars t.env vars in change_d t env' true false let add_vars t vars = VectorMatrix.timing_wrap "add_vars" (add_vars t) vars let drop_vars t vars del = let t = copy t in - let env' = remove_vars t.env vars in + let env' = Environment.remove_vars t.env vars in change_d t env' false del let drop_vars t vars = VectorMatrix.timing_wrap "drop_vars" (drop_vars t) vars @@ -444,7 +347,7 @@ struct t.env <- t'.env let remove_filter t f = - let env' = remove_filter t.env f in + let env' = Environment.remove_filter t.env f in change_d t env' false false let remove_filter t f = VectorMatrix.timing_wrap "remove_filter" (remove_filter t) f @@ -456,24 +359,23 @@ struct let keep_filter t f = let t = copy t in - let env' = keep_filter t.env f in + let env' = Environment.keep_filter t.env f in change_d t env' false false let keep_filter t f = VectorMatrix.timing_wrap "keep_filter" (keep_filter t) f let keep_vars t vs = let t = copy t in - let env' = keep_vars t.env vs in + let env' = Environment.keep_vars t.env vs in change_d t env' false false let keep_vars t vs = VectorMatrix.timing_wrap "keep_vars" (keep_vars t) vs - let vars t = vars t.env - let mem_var t var = Environment.mem_var t.env var end + (** A more specific module type for RelationDomain.RelD2 with ConvBounds integrated and various apron elements. It is designed to be the interface for the D2 modules in affineEqualityDomain and apronDomain and serves as a functor argument for AssertionModule. *) module type AssertionRelS = diff --git a/src/cdomains/fileDomain.ml b/src/cdomains/fileDomain.ml deleted file mode 100644 index ca585b8bce..0000000000 --- a/src/cdomains/fileDomain.ml +++ /dev/null @@ -1,81 +0,0 @@ -(** Domains for file handles. *) - -open Batteries - -module D = MvalMapDomain - - -module Val = -struct - type mode = Read | Write [@@deriving eq, ord, hash] - type s = Open of string*mode | Closed | Error [@@deriving eq, ord, hash] - let name = "File handles" - let var_state = Closed - let string_of_mode = function Read -> "Read" | Write -> "Write" - let string_of_state = function - | Open(filename, m) -> "open("^filename^", "^string_of_mode m^")" - | Closed -> "closed" - | Error -> "error" - - (* properties of records (e.g. used by Dom.warn_each) *) - let opened s = s <> Closed && s <> Error - let closed s = s = Closed - let writable s = match s with Open((_,Write)) -> true | _ -> false -end - - -module Dom = -struct - include D.Domain (D.Value (Val)) - - (* returns a tuple (thunk, result) *) - let report_ ?(neg=false) k p msg m = - let f ?(may=false) msg = - let f () = warn ~may msg in - f, if may then `May true else `Must true in - let mf = (fun () -> ()), `Must false in - if mem k m then - let p = if neg then not % p else p in - let v = find' k m in - if V.must p v then f msg (* must *) - else if V.may p v then f ~may:true msg (* may *) - else mf (* none *) - else if neg then f msg else mf - - let report ?(neg=false) k p msg m = (fst (report_ ~neg k p msg m)) () (* evaluate thunk *) - - let reports k xs m = - let uncurry (neg, p, msg) = report_ ~neg:neg k p msg m in - let f result x = if snd (uncurry x) = result then Some (fst (uncurry x)) else None in - let must_true = BatList.filter_map (f (`Must true)) xs in - let may_true = BatList.filter_map (f (`May true)) xs in - (* output first must and first may *) - if must_true <> [] then (List.hd must_true) (); - if may_true <> [] then (List.hd may_true) () - - (* handling state *) - let opened r = V.state r |> Val.opened - let closed r = V.state r |> Val.closed - let writable r = V.state r |> Val.writable - - let fopen k loc filename mode m = - if is_unknown k m then m else - let mode = match String.lowercase_ascii mode with "r" -> Val.Read | _ -> Val.Write in - let v = V.make k loc (Val.Open(filename, mode)) in - add' k v m - let fclose k loc m = - if is_unknown k m then m else - let v = V.make k loc Val.Closed in - change k v m - let error k m = - if is_unknown k m then m else - let loc = if mem k m then find' k m |> V.split |> snd |> Set.choose |> V.loc else [] in - let v = V.make k loc Val.Error in - change k v m - let success k m = - if is_unknown k m then m else - match find_option k m with - | Some v when V.may (Val.opened%V.state) v && V.may (V.in_state Val.Error) v -> - change k (V.filter (Val.opened%V.state) v) m (* TODO what about must-set? *) - | _ -> m -end diff --git a/src/cdomains/floatDomain.ml b/src/cdomains/floatDomain.ml index f52c849111..39d3744401 100644 --- a/src/cdomains/floatDomain.ml +++ b/src/cdomains/floatDomain.ml @@ -40,6 +40,8 @@ module type FloatArith = sig (** sin(x) *) val tan : t -> t (** tan(x) *) + val sqrt : t -> t + (** sqrt(x) *) (** {inversions of unary functions}*) val inv_ceil : ?asPreciseAsConcrete:bool -> t -> t @@ -670,6 +672,14 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | (l, h) when l = h && l = Float_t.zero -> of_const 0. (*tan(0) = 0*) | _ -> top () (**could be exact for intervals where l=h, or even for some intervals *) + let eval_sqrt = function + | (l, h) when l = Float_t.zero && h = Float_t.zero -> of_const 0. + | (l, h) when l >= Float_t.zero -> + let low = Float_t.sqrt Down l in + let high = Float_t.sqrt Up h in + Interval (low, high) + | _ -> top () + let eval_inv_ceil ?(asPreciseAsConcrete=false) = function | (l, h) -> if (Float_t.sub Up (Float_t.ceil l) (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0)) = (Float_t.of_float Nearest 1.0)) then ( @@ -784,6 +794,7 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct let cos = eval_unop eval_cos let sin = eval_unop eval_sin let tan = eval_unop eval_tan + let sqrt = eval_unop eval_sqrt let inv_ceil ?(asPreciseAsConcrete=false) = eval_unop ~warn:false (eval_inv_ceil ~asPreciseAsConcrete:asPreciseAsConcrete) let inv_floor ?(asPreciseAsConcrete=false) = eval_unop ~warn:false (eval_inv_floor ~asPreciseAsConcrete:asPreciseAsConcrete) @@ -899,6 +910,7 @@ module FloatIntervalImplLifted = struct let cos = lift (F1.cos, F2.cos) let sin = lift (F1.sin, F2.sin) let tan = lift (F1.tan, F2.tan) + let sqrt = lift (F1.sqrt, F2.sqrt) let inv_ceil ?(asPreciseAsConcrete=BoolDomain.MustBool.top ()) = function | F32 a -> F32 (F1.inv_ceil ~asPreciseAsConcrete:true a) @@ -1159,6 +1171,8 @@ module FloatDomTupleImpl = struct map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.sin); } let tan = map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.tan); } + let sqrt = + map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.sqrt); } (*"asPreciseAsConcrete" has no meaning here*) let inv_ceil ?(asPreciseAsConcrete=BoolDomain.MustBool.top ()) = diff --git a/src/cdomains/floatDomain.mli b/src/cdomains/floatDomain.mli index 06bca69aca..d958e1ee81 100644 --- a/src/cdomains/floatDomain.mli +++ b/src/cdomains/floatDomain.mli @@ -57,6 +57,9 @@ module type FloatArith = sig val tan : t -> t (** tan(x) *) + val sqrt : t -> t + (** sqrt(x) *) + (** {inversions of unary functions}*) val inv_ceil : ?asPreciseAsConcrete:bool -> t -> t (** (inv_ceil z -> x) if (z = ceil(x)) *) diff --git a/src/cdomains/floatOps/floatOps.ml b/src/cdomains/floatOps/floatOps.ml index a951ec08fe..a4e39d930e 100644 --- a/src/cdomains/floatOps/floatOps.ml +++ b/src/cdomains/floatOps/floatOps.ml @@ -35,6 +35,7 @@ module type CFloatType = sig val sub: round_mode -> t -> t -> t val mul: round_mode -> t -> t -> t val div: round_mode -> t -> t -> t + val sqrt: round_mode -> t -> t val atof: round_mode -> string -> t end @@ -74,6 +75,7 @@ module CDouble = struct external sub: round_mode -> t -> t -> t = "sub_double" external mul: round_mode -> t -> t -> t = "mul_double" external div: round_mode -> t -> t -> t = "div_double" + external sqrt: round_mode -> t -> t = "sqrt_double" external atof: round_mode -> string -> t = "atof_double" end @@ -107,6 +109,7 @@ module CFloat = struct external sub: round_mode -> t -> t -> t = "sub_float" external mul: round_mode -> t -> t -> t = "mul_float" external div: round_mode -> t -> t -> t = "div_float" + external sqrt: round_mode -> t -> t = "sqrt_float" external atof: round_mode -> string -> t = "atof_float" diff --git a/src/cdomains/floatOps/floatOps.mli b/src/cdomains/floatOps/floatOps.mli index 05bf363872..cf24f75ed5 100644 --- a/src/cdomains/floatOps/floatOps.mli +++ b/src/cdomains/floatOps/floatOps.mli @@ -38,6 +38,7 @@ module type CFloatType = sig val sub: round_mode -> t -> t -> t val mul: round_mode -> t -> t -> t val div: round_mode -> t -> t -> t + val sqrt: round_mode -> t -> t val atof: round_mode -> string -> t end diff --git a/src/cdomains/floatOps/stubs.c b/src/cdomains/floatOps/stubs.c index e0485883dd..50e4a2fb31 100644 --- a/src/cdomains/floatOps/stubs.c +++ b/src/cdomains/floatOps/stubs.c @@ -36,6 +36,20 @@ static void change_round_mode(int mode) } } +#define UNARY_OP(name, type, op) \ + CAMLprim value name##_##type(value mode, value x) \ + { \ + int old_roundingmode = fegetround(); \ + change_round_mode(Int_val(mode)); \ + volatile type r, x1 = Double_val(x); \ + r = op(x1); \ + fesetround(old_roundingmode); \ + return caml_copy_double(r); \ + } + +UNARY_OP(sqrt, double, sqrt); +UNARY_OP(sqrt, float, sqrtf); + #define BINARY_OP(name, type, op) \ CAMLprim value name##_##type(value mode, value x, value y) \ { \ diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 3bc84ae676..5d5174744f 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -610,6 +610,11 @@ module IntervalArith(Ints_t : IntOps.IntOps) = struct let x2y2 = (Ints_t.mul x2 y2) in (min4 x1y1 x1y2 x2y1 x2y2, max4 x1y1 x1y2 x2y1 x2y2) + let shift_left (x1,x2) (y1,y2) = + let y1p = Ints_t.shift_left Ints_t.one y1 in + let y2p = Ints_t.shift_left Ints_t.one y2 in + mul (x1, x2) (y1p, y2p) + let div (x1, x2) (y1, y2) = let x1y1n = (Ints_t.div x1 y1) in let x1y2n = (Ints_t.div x1 y2) in @@ -851,7 +856,6 @@ struct let bitnot = bit1 (fun _ik -> Ints_t.bitnot) let shift_right = bitcomp (fun _ik x y -> Ints_t.shift_right x (Ints_t.to_int y)) - let shift_left = bitcomp (fun _ik x y -> Ints_t.shift_left x (Ints_t.to_int y)) let neg ?no_ov ik = function None -> (None,{underflow=false; overflow=false}) | Some x -> norm ik @@ Some (IArith.neg x) @@ -864,6 +868,20 @@ struct let mul ?no_ov = binary_op_with_norm IArith.mul let sub ?no_ov = binary_op_with_norm IArith.sub + let shift_left ik a b = + match is_bot a, is_bot b with + | true, true -> (bot_of ik,{underflow=false; overflow=false}) + | true, _ + | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show a) (show b))) + | _ -> + match a, minimal b, maximal b with + | Some a, Some bl, Some bu when (Ints_t.compare bl Ints_t.zero >= 0) -> + (try + let r = IArith.shift_left a (Ints_t.to_int bl, Ints_t.to_int bu) in + norm ik @@ Some r + with Z.Overflow -> (top_of ik,{underflow=false; overflow=true})) + | _ -> (top_of ik,{underflow=true; overflow=true}) + let rem ik x y = match x, y with | None, None -> None | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) @@ -978,12 +996,12 @@ struct let arbitrary ik = let open QCheck.Iter in - (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint MyCheck.Arbitrary.big_int in *) + (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint GobQCheck.Arbitrary.big_int in *) (* TODO: apparently bigints are really slow compared to int64 for domaintest *) - let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 MyCheck.Arbitrary.int64 in + let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 in let pair_arb = QCheck.pair int_arb int_arb in let shrink = function - | Some (l, u) -> (return None) <+> (MyCheck.shrink pair_arb (l, u) >|= of_interval ik >|= fst) + | Some (l, u) -> (return None) <+> (GobQCheck.shrink pair_arb (l, u) >|= of_interval ik >|= fst) | None -> empty in QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) (fun x -> of_interval ik x |> fst ) pair_arb) @@ -1583,13 +1601,13 @@ struct let arbitrary ik = let open QCheck.Iter in - (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint MyCheck.Arbitrary.big_int in *) + (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint GobQCheck.Arbitrary.big_int in *) (* TODO: apparently bigints are really slow compared to int64 for domaintest *) - let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 MyCheck.Arbitrary.int64 in + let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 in let pair_arb = QCheck.pair int_arb int_arb in let list_pair_arb = QCheck.small_list pair_arb in let canonize_randomly_generated_list = (fun x -> norm_intvs ik x |> fst) in - let shrink xs = MyCheck.shrink list_pair_arb xs >|= canonize_randomly_generated_list + let shrink xs = GobQCheck.shrink list_pair_arb xs >|= canonize_randomly_generated_list in QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) canonize_randomly_generated_list list_pair_arb) end @@ -1677,7 +1695,7 @@ struct let logand n1 n2 = of_bool ((to_bool' n1) && (to_bool' n2)) let logor n1 n2 = of_bool ((to_bool' n1) || (to_bool' n2)) let cast_to ?torg t x = failwith @@ "Cast_to not implemented for " ^ (name ()) ^ "." - let arbitrary ik = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 MyCheck.Arbitrary.int64 (* TODO: use ikind *) + let arbitrary ik = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 (* TODO: use ikind *) let invariant _ _ = Invariant.none (* TODO *) end @@ -2384,8 +2402,8 @@ struct let excluded s = from_excl ik s in let definite x = of_int ik x in let shrink = function - | `Excluded (s, _) -> MyCheck.shrink (S.arbitrary ()) s >|= excluded (* S TODO: possibly shrink excluded to definite *) - | `Definite x -> (return `Bot) <+> (MyCheck.shrink (BigInt.arbitrary ()) x >|= definite) + | `Excluded (s, _) -> GobQCheck.shrink (S.arbitrary ()) s >|= excluded (* S TODO: possibly shrink excluded to definite *) + | `Definite x -> (return `Bot) <+> (GobQCheck.shrink (BigInt.arbitrary ()) x >|= definite) | `Bot -> empty in QCheck.frequency ~shrink ~print:show [ @@ -2798,8 +2816,8 @@ module Enums : S with type int_t = BigInt.t = struct let neg s = of_excl_list ik (BISet.elements s) in let pos s = norm ik (Inc s) in let shrink = function - | Exc (s, _) -> MyCheck.shrink (BISet.arbitrary ()) s >|= neg (* S TODO: possibly shrink neg to pos *) - | Inc s -> MyCheck.shrink (BISet.arbitrary ()) s >|= pos + | Exc (s, _) -> GobQCheck.shrink (BISet.arbitrary ()) s >|= neg (* S TODO: possibly shrink neg to pos *) + | Inc s -> GobQCheck.shrink (BISet.arbitrary ()) s >|= pos in QCheck.frequency ~shrink ~print:show [ 20, QCheck.map neg (BISet.arbitrary ()); @@ -3289,7 +3307,7 @@ struct let arbitrary ik = let open QCheck in - let int_arb = map ~rev:Ints_t.to_int64 Ints_t.of_int64 MyCheck.Arbitrary.int64 in + let int_arb = map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 in let cong_arb = pair int_arb int_arb in let of_pair ik p = normalize ik (Some p) in let to_pair = Option.get in @@ -3410,14 +3428,14 @@ module IntDomTupleImpl = struct | Some(_, {underflow; overflow}) -> not (underflow || overflow) | _ -> false - let check_ov ik intv intv_set = + let check_ov ~cast ik intv intv_set = let no_ov = (no_overflow ik intv) || (no_overflow ik intv_set) in if not no_ov && ( BatOption.is_some intv || BatOption.is_some intv_set) then ( let (_,{underflow=underflow_intv; overflow=overflow_intv}) = match intv with None -> (I2.bot (), {underflow= true; overflow = true}) | Some x -> x in let (_,{underflow=underflow_intv_set; overflow=overflow_intv_set}) = match intv_set with None -> (I5.bot (), {underflow= true; overflow = true}) | Some x -> x in let underflow = underflow_intv && underflow_intv_set in let overflow = overflow_intv && overflow_intv_set in - set_overflow_flag ~cast:false ~underflow ~overflow ik; + set_overflow_flag ~cast ~underflow ~overflow ik; ); no_ov @@ -3426,7 +3444,7 @@ module IntDomTupleImpl = struct let map x = Option.map fst x in let intv = f p2 @@ r.fi2_ovc (module I2) in let intv_set = f p5 @@ r.fi2_ovc (module I5) in - ignore (check_ov ik intv intv_set); + ignore (check_ov ~cast:false ik intv intv_set); map @@ f p1 @@ r.fi2_ovc (module I1), map @@ f p2 @@ r.fi2_ovc (module I2), map @@ f p3 @@ r.fi2_ovc (module I3), map @@ f p4 @@ r.fi2_ovc (module I4), map @@ f p5 @@ r.fi2_ovc (module I5) let create2_ovc ik r x = (* use where values are introduced *) @@ -3607,7 +3625,7 @@ module IntDomTupleImpl = struct let map f ?no_ov = function Some x -> Some (f ?no_ov x) | _ -> None in let intv = map (r.f1_ovc (module I2)) b in let intv_set = map (r.f1_ovc (module I5)) e in - let no_ov = check_ov ik intv intv_set in + let no_ov = check_ov ~cast ik intv intv_set in let no_ov = no_ov || should_ignore_overflow ik in refine ik ( map (fun ?no_ov x -> r.f1_ovc ?no_ov (module I1) x |> fst) a @@ -3617,10 +3635,10 @@ module IntDomTupleImpl = struct , BatOption.map fst intv_set ) (* map2 with overflow check *) - let map2ovc ik r (xa, xb, xc, xd, xe) (ya, yb, yc, yd, ye) = + let map2ovc ?(cast=false) ik r (xa, xb, xc, xd, xe) (ya, yb, yc, yd, ye) = let intv = opt_map2 (r.f2_ovc (module I2)) xb yb in let intv_set = opt_map2 (r.f2_ovc (module I5)) xe ye in - let no_ov = check_ov ik intv intv_set in + let no_ov = check_ov ~cast ik intv intv_set in let no_ov = no_ov || should_ignore_overflow ik in refine ik ( opt_map2 (fun ?no_ov x y -> r.f2_ovc ?no_ov (module I1) x y |> fst) xa ya diff --git a/src/cdomains/mvalMapDomain.ml b/src/cdomains/mvalMapDomain.ml deleted file mode 100644 index d0d2f8da85..0000000000 --- a/src/cdomains/mvalMapDomain.ml +++ /dev/null @@ -1,299 +0,0 @@ -(** Domains for {{!Mval} mvalue} maps. *) - -open Batteries -open GoblintCil - -module M = Messages - - -exception Unknown -exception Error - -(* signature for map entries *) -module type S = -sig - include Lattice.S - type k = Mval.Exp.t (* key *) - type s (* state is defined by Impl *) - type r (* record *) - - (* printing *) - val string_of: t -> string - val string_of_key: k -> string - val string_of_record: r -> string - - (* constructing *) - val make: k -> Node.t list -> s -> t - - (* manipulation *) - val map: (r -> r) -> t -> t - val filter: (r -> bool) -> t -> t - val union: t -> t -> t - val set_key: k -> t -> t - val set_state: s -> t -> t - val remove_state: s -> t -> t - - (* deconstructing *) - val split: t -> r Set.t * r Set.t - val map': (r -> 'a) -> t -> 'a Set.t * 'a Set.t - val filter': (r -> bool) -> t -> r Set.t * r Set.t - val length: t -> int * int - - (* predicates *) - val must: (r -> bool) -> t -> bool - val may: (r -> bool) -> t -> bool - (* properties of records *) - val key: r -> k - val loc: r -> Node.t list - val edit_loc: (Node.t list -> Node.t list) -> r -> r - val state: r -> s - val in_state: s -> r -> bool - - (* special variables *) - val get_record: t -> r option - (* val make_record: k -> location list -> s -> r *) - val make_var: k -> t - val from_tuple: r Set.t * r Set.t -> t - - (* aliasing *) - val is_alias: t -> bool - val get_alias: t -> k - val make_alias: k -> t -end - -module Value (Impl: sig - type s (* state *) [@@deriving eq, ord, hash] - val name: string - val var_state: s - val string_of_state: s -> string - end) : S with type s = Impl.s = -struct - type k = Mval.Exp.t [@@deriving eq, ord, hash] - type s = Impl.s [@@deriving eq, ord, hash] - module R = struct - include Printable.StdLeaf - type t = { key: k; loc: Node.t list; state: s } [@@deriving eq, ord, hash] - let name () = "MValMapDomainValue" - - let pretty () {key; loc; state} = - Pretty.dprintf "{key=%a; loc=%a; state=%s}" Mval.Exp.pretty key (Pretty.d_list ", " Node.pretty) loc (Impl.string_of_state state) - - include Printable.SimplePretty ( - struct - type nonrec t = t - let pretty = pretty - end - ) - end - type r = R.t - open R - (* TODO: use SetDomain.Reverse? *) - module Must' = SetDomain.ToppedSet (R) (struct let topname = "top" end) - module Must = Lattice.Reverse (Must') - module May = SetDomain.ToppedSet (R) (struct let topname = "top" end) - include Lattice.Prod (Must) (May) - let name () = Impl.name - - (* converts to polymorphic sets *) - let split (x,y) = try Must'.elements x |> Set.of_list, May.elements y |> Set.of_list with SetDomain.Unsupported _ -> Set.empty, Set.empty - - (* special variable used for indirection *) - let alias_var = Cilfacade.create_var @@ Cil.makeVarinfo false "@alias" Cil.voidType, `NoOffset - (* alias structure: x[0].key=alias_var, y[0].key=linked_var *) - let is_alias (x,y) = neg Must'.is_empty x && (Must'.choose x).key=alias_var - let get_alias (x,y) = (May.choose y).key - - (* Printing *) - let string_of_key k = Mval.Exp.show k - let string_of_loc xs = String.concat ", " (List.map (CilType.Location.show % Node.location) xs) - let string_of_record r = Impl.string_of_state r.state^" ("^string_of_loc r.loc^")" - let string_of (x,y) = - if is_alias (x,y) then - "alias for "^string_of_key @@ get_alias (x,y) - else - let x, y = split (x,y) in - let z = Set.diff y x in - "{ "^String.concat ", " (List.map string_of_record (Set.elements x))^" }, "^ - "{ "^String.concat ", " (List.map string_of_record (Set.elements z))^" }" - let show x = string_of x - include Printable.SimpleShow (struct - type nonrec t = t - let show = show - end) - (* constructing & manipulation *) - let make_record k l s = { key=k; loc=l; state=s } - let make k l s = let v = make_record k l s in Must'.singleton v, May.singleton v - let map f (x,y) = Must'.map f x, May.map f y - let filter p (x,y) = Must'.filter p x, May.filter p y (* retains top *) - let union (a,b) (c,d) = Must'.union a c, May.union b d - let set_key k v = map (fun x -> {x with key=k}) v (* changes key for all elements *) - let set_state s v = map (fun x -> {x with state=s}) v - let remove_state s v = filter (fun x -> x.state<>s) v - - (* deconstructing *) - let length = split %> Tuple2.mapn Set.cardinal - let map' f = split %> Tuple2.mapn (Set.map f) - let filter' f = split %> Tuple2.mapn (Set.filter f) - - (* predicates *) - let must p (x,y) = Must'.exists p x || May.for_all p y - let may p (x,y) = May.exists p y - - (* properties of records *) - let key r = r.key - let loc r = r.loc - let edit_loc f r = {r with loc=f r.loc} - let state r = r.state - let in_state s r = r.state = s - - (* special variables *) - let get_record (x,y) = if Must'.is_empty x then None else Some (Must'.choose x) - let make_var_record k = make_record k [] Impl.var_state - let make_var k = Must'.singleton (make_var_record k), May.singleton (make_var_record k) - let make_alias k = Must'.singleton (make_var_record alias_var), May.singleton (make_var_record k) - let from_tuple (x,y) = Set.to_list x |> Must'.of_list, Set.to_list y |> May.of_list -end - - -module Domain (V: S) = -struct - module K = Mval.Exp - module V = V - module MD = MapDomain.MapBot (Mval.Exp) (V) - include MD - - (* Map functions *) - (* find that resolves aliases *) - let find' k m = let v = find k m in if V.is_alias v then find (V.get_alias v) m else v - let find_option k m = if mem k m then Some(find' k m) else None - let get_alias k m = (* target: returns Some k' if k links to k' *) - if mem k m && V.is_alias (find k m) then Some (V.get_alias (find k m)) else None - let get_aliased k m = (* sources: get list of keys that link to k *) - (* iter (fun k' (x,y) -> if V.is_alias (x,y) then print_endline ("alias "^V.string_of_key k'^" -> "^V.string_of_key (Set.choose y).key)) m; *) - (* TODO V.get_alias v=k somehow leads to Out_of_memory... *) - filter (fun k' v -> V.is_alias v && V.string_of_key (V.get_alias v)=V.string_of_key k) m |> bindings |> List.map fst - let get_aliases k m = (* get list of all other keys that have the same pointee *) - match get_alias k m with - | Some k' -> [k] (* k links to k' *) - | None -> get_aliased k m (* k' that link to k *) - let alias a b m = (* link a to b *) - (* if b is already an alias, follow it... *) - let b' = get_alias b m |? b in - (* add an entry for key a, that points to b' *) - add a (V.make_alias b') m - let remove' k m = (* fixes keys that link to k before removing it *) - if mem k m && not (V.is_alias (find k m)) then (* k might be aliased *) - let v = find k m in - match get_aliased k m with - | [] -> remove k m (* nothing links to k *) - | k'::xs -> let m = add k' v m in (* set k' to v, link xs to k', finally remove k *) - (* List.map (fun x -> x.vname) (k'::xs) |> String.concat ", " |> print_endline; *) - List.fold_left (fun m x -> alias x k' m) m xs |> remove k - else remove k m (* k not in m or an alias *) - let add' k v m = - remove' k m (* fixes keys that might have linked to k *) - |> add k v (* set new value *) - let change k v m = (* if k is an alias, replace its pointee *) - add (get_alias k m |? k) v m - - (* special variables *) - let get_record k m = Option.bind (find_option k m) V.get_record - let edit_record k f m = - let v = find_option k m |? V.make_var k in - add k (V.map f v) m - let get_value k m = find_option k m |> Option.map_default V.split (Set.empty,Set.empty) - let extend_value k v' m = - let v = V.from_tuple v' in - if mem k m then - add k (V.union (find k m) v) m - else - add k v m - let union (a,b) (c,d) = Set.union a c, Set.union b d - let is_special_var k = String.get (V.string_of_key k) 0 = '@' - let without_special_vars m = filter (fun k v -> not @@ is_special_var k) m - - (* functions needed for enter & combine *) - (* only keep globals, aliases to them and special variables *) - let only_globals m = filter (fun k v -> (fst k).vglob || V.is_alias v && (fst (V.get_alias v)).vglob || is_special_var k) m - (* adds all the bindings from m2 to m1 (overwrites!) *) - let add_all m1 m2 = add_list (bindings m2) m1 - - (* callstack for locations *) - let callstack_var = Cilfacade.create_var @@ Cil.makeVarinfo false "@callstack" Cil.voidType, `NoOffset - let callstack m = get_record callstack_var m |> Option.map_default V.loc [] - let string_of_callstack m = " [call stack: "^String.concat ", " (List.map (CilType.Location.show % Node.location) (callstack m))^"]" - let edit_callstack f m = edit_record callstack_var (V.edit_loc f) m - - - (* predicates *) - let must k p m = mem k m && V.must p (find' k m) - let may k p m = mem k m && V.may p (find' k m) - let is_may k m = mem k m && let x,y = V.length (find' k m) in x=0 && y>0 - - let filter_values p m = (* filters all values in the map and flattens result *) - let flatten_sets = List.fold_left Set.union Set.empty in - without_special_vars m - |> filter (fun k v -> V.may p v && not (V.is_alias v)) - |> bindings |> List.map (fun (k,v) -> V.filter' p v) - |> List.split |> (fun (x,y) -> flatten_sets x, flatten_sets y) - let filter_records k p m = (* filters both sets of k *) - if mem k m then V.filter' p (find' k m) else Set.empty, Set.empty - - let unknown k m = add' k (V.top ()) m - let is_unknown k m = if mem k m then V.is_top (find' k m) else false - - (* printing *) - let string_of_state k m = if not (mem k m) then "?" else V.string_of (find' k m) - let string_of_key k = V.string_of_key k - let string_of_keys rs = Set.map (V.string_of_key % V.key) rs |> Set.elements |> String.concat ", " - let string_of_entry k m = string_of_key k ^ ": " ^ string_of_state k m - let string_of_map m = List.map (fun (k,v) -> string_of_entry k m) (bindings m) - - let warn ?may:(may=false) ?loc:(loc=[Option.get !Node.current_node]) msg = - let split_category s = - if Str.string_partial_match (Str.regexp {|\[\([^]]*\)\]|}) s 0 then - (Some (Str.matched_group 1 s), Str.string_after s (Str.match_end ())) - else - (None, s) - in - let rec split_categories s = - match split_category s with - | (Some category, s') -> - let (categories, s'') = split_categories s' in - (category :: categories, s'') - | (None, s') -> ([], s') - in - match split_categories msg with - | ([], msg) -> (if may then Messages.warn else Messages.error) ~loc:(Node (List.last loc)) "%s" msg - | (category :: categories, msg) -> - let category_of_string s = Messages.Category.from_string_list [String.lowercase_ascii s] in (* TODO: doesn't split subcategories, not used and no defined syntax even *) - let category = category_of_string category in - let tags = List.map (fun category -> Messages.Tag.Category (category_of_string category)) categories in - (if may then Messages.warn else Messages.error) ~loc:(Node (List.last loc)) ~category ~tags "%s" msg - - (* getting keys from Cil Lvals *) - - let key_from_lval lval = match lval with (* TODO try to get a Mval.Exp from Cil.Lval *) - | Var v1, o1 -> v1, Offset.Exp.of_cil o1 - | Mem Lval(Var v1, o1), o2 -> v1, Offset.Exp.of_cil (addOffset o1 o2) - (* | Mem exp, o1 -> failwith "not implemented yet" (* TODO use query_lv *) *) - | _ -> Cilfacade.create_var @@ Cil.makeVarinfo false ("?"^CilType.Lval.show lval) Cil.voidType, `NoOffset (* TODO *) - - let keys_from_lval lval (ask: Queries.ask) = (* use MayPointTo query to get all possible pointees of &lval *) - (* print_query_lv ctx.ask (AddrOf lval); *) - let query_addrs (ask: Queries.ask) exp = match ask.f (Queries.MayPointTo exp) with - | ad when not (Queries.AD.is_top ad) -> Queries.AD.elements ad - | _ -> [] - in - let exp = AddrOf lval in - let addrs = query_addrs ask exp in (* MayPointTo -> LValSet *) - let keys = List.fold (fun vs addr -> - match addr with - | Queries.AD.Addr.Addr (v,o) -> (v, ValueDomain.Offs.to_exp o) :: vs - | _ -> vs - ) [] addrs - in - let pretty_key k = Pretty.text (string_of_key k) in - Messages.debug ~category:Analyzer "MayPointTo %a = [%a]" d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) keys; - keys -end diff --git a/src/cdomains/offset.ml b/src/cdomains/offset.ml index eca85e08a4..52cfe9eb41 100644 --- a/src/cdomains/offset.ml +++ b/src/cdomains/offset.ml @@ -22,7 +22,7 @@ struct include CilType.Exp let name () = "exp index" - let any = CastE (TInt (Cilfacade.ptrdiff_ikind (), []), mkString "any_index") + let any = Cilfacade.any_index_exp let all = CastE (TInt (Cilfacade.ptrdiff_ikind (), []), mkString "all_index") (* Override output *) diff --git a/src/cdomains/regionDomain.ml b/src/cdomains/regionDomain.ml index b577e3499f..681eb79007 100644 --- a/src/cdomains/regionDomain.ml +++ b/src/cdomains/regionDomain.ml @@ -157,13 +157,13 @@ struct (* This is the main logic for dealing with the bullet and finding it an * owner... *) - let add_set (s:set) llist (p,m:t): t = + let add_set ?(escape=false) (s:set) llist (p,m:t): t = if RS.has_bullet s then let f key value (ys, x) = if RS.has_bullet value then key::ys, RS.join value x else ys,x in let ys,x = RegMap.fold f m (llist, RS.remove_bullet s) in let x = RS.remove_bullet x in - if RS.is_empty x then + if not escape && RS.is_empty x then p, RegMap.add_list_set llist RS.single_bullet m else RegPart.add x p, RegMap.add_list_set ys x m @@ -215,6 +215,25 @@ struct | Some (_,x,_) -> p, RegMap.add x RS.single_bullet m | _ -> p,m + (* Copied & modified from assign. *) + let assign_escape (rval: exp) (st: t): t = + (* let _ = printf "%a = %a\n" (printLval plainCilPrinter) lval (printExp plainCilPrinter) rval in *) + let t = Cilfacade.typeOf rval in + if isPointerType t then begin (* TODO: this currently allows function pointers, e.g. in iowarrior, but should it? *) + match eval_exp rval with + (* TODO: should offs_x matter? *) + | Some (deref_y,y,offs_y) -> + let (p,m) = st in begin + match is_global y with + | true -> + add_set ~escape:true (RS.single_vf y) [] st + | false -> + add_set ~escape:true (RegMap.find y m) [y] st + end + | _ -> st + end else + st + let related_globals (deref_vfd: eval_t) (p,m: t): elt list = let add_o o2 (v,o) = (v, F.add_offset o o2) in match deref_vfd with diff --git a/src/cdomains/specDomain.ml b/src/cdomains/specDomain.ml deleted file mode 100644 index 75a9d8edc5..0000000000 --- a/src/cdomains/specDomain.ml +++ /dev/null @@ -1,34 +0,0 @@ -(** Domains for finite automaton specification file analysis. *) - -open Batteries - -module D = MvalMapDomain - - -module Val = -struct - type s = string [@@deriving eq, ord, hash] - let name = "Spec value" - let var_state = "" - let string_of_state s = s - - (* transforms May-Sets of length 1 to Must. NOTE: this should only be done if the original set had more than one element! *) - (* let maybe_must = function May xs when Set.cardinal xs = 1 -> Must (Set.choose xs) | x -> x *) - (* let may = function Must x -> May (Set.singleton x) | xs -> xs *) - (* let records = function Must x -> (Set.singleton x) | May xs -> xs *) - (* let list_of_records = function Must x -> [x] | May xs -> List.of_enum (Set.enum xs) *) - (* let vnames x = String.concat ", " (List.map (fun r -> string_of_key r.var) (list_of_records x)) *) -end - - -module Dom = -struct - include D.Domain (D.Value (Val)) - - (* handling state *) - let goto k loc state m = add' k (V.make k loc state) m - let may_goto k loc state m = let v = V.join (find' k m) (V.make k loc state) in add' k v m - let in_state k s m = must k (V.in_state s) m - let may_in_state k s m = may k (V.in_state s) m - let get_states k m = if not (mem k m) then [] else find' k m |> V.map' V.state |> snd |> Set.elements -end diff --git a/src/cdomains/stringDomain.ml b/src/cdomains/stringDomain.ml new file mode 100644 index 0000000000..0621f37eb6 --- /dev/null +++ b/src/cdomains/stringDomain.ml @@ -0,0 +1,114 @@ +include Printable.StdLeaf + +let name () = "string" + +type string_domain = Unit | Disjoint | Flat + +let string_domain: string_domain ResettableLazy.t = + ResettableLazy.from_fun (fun () -> + match GobConfig.get_string "ana.base.strings.domain" with + | "unit" -> Unit + | "disjoint" -> Disjoint + | "flat" -> Flat + | _ -> failwith "ana.base.strings.domain: illegal value" + ) + +let get_string_domain () = ResettableLazy.force string_domain + +let reset_lazy () = + ResettableLazy.reset string_domain + + +type t = string option [@@deriving eq, ord, hash] + +let hash x = + if get_string_domain () = Disjoint then + hash x + else + 13859 + +let show = function + | Some x -> "\"" ^ x ^ "\"" + | None -> "(unknown string)" + +include Printable.SimpleShow ( + struct + type nonrec t = t + let show = show + end + ) + +let of_string x = + if get_string_domain () = Unit then + None + else + Some x +let to_string x = x + +(* only keep part before first null byte *) +let to_c_string = function + | Some x -> + begin match String.split_on_char '\x00' x with + | s::_ -> Some s + | [] -> None + end + | None -> None + +let to_n_c_string n x = + match to_c_string x with + | Some x -> + if n > String.length x then + Some x + else if n < 0 then + None + else + Some (String.sub x 0 n) + | None -> None + +let to_string_length x = + match to_c_string x with + | Some x -> Some (String.length x) + | None -> None + +let to_exp = function + | Some x -> GoblintCil.mkString x + | None -> raise (Lattice.Unsupported "Cannot express unknown string pointer as expression.") + +let semantic_equal x y = + match x, y with + | None, _ + | _, None -> Some true + | Some a, Some b -> if a = b then None else Some false + +let leq x y = + match x, y with + | _, None -> true + | a, b -> a = b + +let join x y = + match x, y with + | None, _ + | _, None -> None + | Some a, Some b when a = b -> Some a + | Some a, Some b (* when a <> b *) -> + if get_string_domain () = Disjoint then + raise Lattice.Uncomparable + else + None + +let meet x y = + match x, y with + | None, a + | a, None -> a + | Some a, Some b when a = b -> Some a + | Some a, Some b (* when a <> b *) -> + if get_string_domain () = Disjoint then + raise Lattice.Uncomparable + else + raise Lattice.BotValue + +let repr x = + if get_string_domain () = Disjoint then + x (* everything else is kept separate, including strings if not limited *) + else + None (* all strings together if limited *) diff --git a/src/cdomains/stringDomain.mli b/src/cdomains/stringDomain.mli new file mode 100644 index 0000000000..66423caa0b --- /dev/null +++ b/src/cdomains/stringDomain.mli @@ -0,0 +1,40 @@ +(** String literals domain. *) + +include Printable.S + +val reset_lazy: unit -> unit +(** Reset the cached configuration of the string domain. *) + +val of_string: string -> t +(** Convert from string. *) + +val to_string: t -> string option +(** Convert to string if possible. *) + +(** C strings are different from OCaml strings as they are not processed after the first [NUL] byte, even though the OCaml string (and a C string literal) may be longer. *) + +val to_c_string: t -> string option +(** Convert to C string if possible. *) + +val to_n_c_string: int -> t -> string option +(** Convert to C string of given maximum length if possible. *) + +val to_string_length: t -> int option +(** Find length of C string if possible. *) + +val to_exp: t -> GoblintCil.exp +(** Convert to CIL expression. *) + +val semantic_equal: t -> t -> bool option +(** Check semantic equality of two strings. + + @return [Some true] if definitely equal, [Some false] if definitely not equal, [None] if unknown. *) + +(** Some {!Lattice.S} operations. *) + +val leq: t -> t -> bool +val join: t -> t -> t +val meet: t -> t -> t + +val repr : t -> t +(** Representative for address lattice. *) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index cba4b04c18..e6f3122cb0 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -502,7 +502,7 @@ struct let warn_type op x y = if GobConfig.get_bool "dbg.verbose" then - ignore @@ printf "warn_type %s: incomparable abstr. values %s and %s at %a: %a and %a\n" op (tag_name (x:t)) (tag_name (y:t)) CilType.Location.pretty !Tracing.current_loc pretty x pretty y + ignore @@ printf "warn_type %s: incomparable abstr. values %s and %s at %a: %a and %a\n" op (tag_name (x:t)) (tag_name (y:t)) CilType.Location.pretty !Goblint_tracing.current_loc pretty x pretty y let rec leq x y = match (x,y) with diff --git a/src/common/cdomains/basetype.ml b/src/common/cdomains/basetype.ml index 55b5dbde07..1b846309aa 100644 --- a/src/common/cdomains/basetype.ml +++ b/src/common/cdomains/basetype.ml @@ -20,8 +20,6 @@ struct | _ -> Local let name () = "variables" let printXml f x = BatPrintf.fprintf f "\n\n%s\n\n\n" (XmlUtil.escape (show x)) - - let arbitrary () = MyCheck.Arbitrary.varinfo end module RawStrings: Printable.S with type t = string = @@ -35,12 +33,6 @@ struct let printXml f x = BatPrintf.fprintf f "\n\n%s\n\n\n" (XmlUtil.escape (show x)) end -module Strings: Lattice.S with type t = [`Bot | `Lifted of string | `Top] = - Lattice.Flat (RawStrings) (struct - let top_name = "?" - let bot_name = "-" - end) - module RawBools: Printable.S with type t = bool = struct include Printable.StdLeaf @@ -52,12 +44,6 @@ struct let printXml f x = BatPrintf.fprintf f "\n\n%s\n\n\n" (show x) end -module Bools: Lattice.S with type t = [`Bot | `Lifted of bool | `Top] = - Lattice.Flat (RawBools) (struct - let top_name = "?" - let bot_name = "-" - end) - module CilExp = struct include CilType.Exp diff --git a/src/common/common.mld b/src/common/common.mld index 662c789572..2ad88c3758 100644 --- a/src/common/common.mld +++ b/src/common/common.mld @@ -10,6 +10,7 @@ For better context, see {!Goblint_lib} which also documents these modules. Node Edge MyCFG +CfgTools } {2 Specification} @@ -18,19 +19,10 @@ AnalysisState ControlSpecC } -{2 Configuration} -{!modules: -GobConfig -AfterConfig -JsonSchema -Options -} - {1 Domains} {!modules: Printable -Lattice } {2 Analysis-specific} @@ -42,7 +34,6 @@ Lattice {1 I/O} {!modules: Messages -Tracing } @@ -69,6 +60,3 @@ RichVarinfo {2 Standard library} {!modules:GobFormat} - -{2 Other libraries} -{!modules:MyCheck} diff --git a/src/common/domains/printable.ml b/src/common/domains/printable.ml index b0755fb730..cc01718ee8 100644 --- a/src/common/domains/printable.ml +++ b/src/common/domains/printable.ml @@ -233,9 +233,9 @@ struct let arbitrary () = let open QCheck.Iter in let shrink = function - | `Lifted x -> (return `Bot) <+> (MyCheck.shrink (Base.arbitrary ()) x >|= lift) + | `Lifted x -> (return `Bot) <+> (GobQCheck.shrink (Base.arbitrary ()) x >|= lift) | `Bot -> empty - | `Top -> MyCheck.Iter.of_arbitrary ~n:20 (Base.arbitrary ()) >|= lift + | `Top -> GobQCheck.Iter.of_arbitrary ~n:20 (Base.arbitrary ()) >|= lift in QCheck.frequency ~shrink ~print:show [ 20, QCheck.map lift (Base.arbitrary ()); @@ -273,6 +273,40 @@ struct | `Right x -> `Right (Base2.relift x) end +module Either3 (Base1: S) (Base2: S) (Base3: S) = +struct + type t = [`Left of Base1.t | `Middle of Base2.t | `Right of Base3.t] [@@deriving eq, ord, hash] + include Std + + let pretty () (state:t) = + match state with + | `Left n -> Pretty.dprintf "%s:%a" (Base1.name ()) Base1.pretty n + | `Middle n -> Pretty.dprintf "%s:%a" (Base2.name ()) Base2.pretty n + | `Right n -> Pretty.dprintf "%s:%a" (Base3.name ()) Base3.pretty n + + let show state = + match state with + | `Left n -> (Base1.name ()) ^ ":" ^ Base1.show n + | `Middle n -> (Base2.name ()) ^ ":" ^ Base2.show n + | `Right n -> (Base3.name ()) ^ ":" ^ Base3.show n + + let name () = "either " ^ Base1.name () ^ " or " ^ Base2.name () ^ " or " ^ Base3.name () + let printXml f = function + | `Left x -> BatPrintf.fprintf f "\n\nLeft\n\n%a\n\n" Base1.printXml x + | `Middle x -> BatPrintf.fprintf f "\n\nMiddle\n\n%a\n\n" Base2.printXml x + | `Right x -> BatPrintf.fprintf f "\n\nRight\n\n%a\n\n" Base3.printXml x + + let to_yojson = function + | `Left x -> `Assoc [ Base1.name (), Base1.to_yojson x ] + | `Middle x -> `Assoc [ Base2.name (), Base2.to_yojson x ] + | `Right x -> `Assoc [ Base3.name (), Base3.to_yojson x ] + + let relift = function + | `Left x -> `Left (Base1.relift x) + | `Middle x -> `Middle (Base2.relift x) + | `Right x -> `Right (Base3.relift x) +end + module Option (Base: S) (N: Name) = struct type t = Base.t option [@@deriving eq, ord, hash] @@ -592,8 +626,8 @@ struct let arbitrary () = let open QCheck.Iter in let shrink = function - | `Lifted x -> MyCheck.shrink (Base.arbitrary ()) x >|= lift - | `Top -> MyCheck.Iter.of_arbitrary ~n:20 (Base.arbitrary ()) >|= lift + | `Lifted x -> GobQCheck.shrink (Base.arbitrary ()) x >|= lift + | `Top -> GobQCheck.Iter.of_arbitrary ~n:20 (Base.arbitrary ()) >|= lift in QCheck.frequency ~shrink ~print:show [ 20, QCheck.map lift (Base.arbitrary ()); diff --git a/src/common/dune b/src/common/dune index c8f1564782..7994798579 100644 --- a/src/common/dune +++ b/src/common/dune @@ -8,22 +8,18 @@ batteries.unthreaded zarith goblint_std + goblint_config + goblint_tracing goblint-cil fpath yojson - json-data-encoding - cpu goblint_timing - goblint_build_info - goblint.sites qcheck-core.runner) (flags :standard -open Goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash - ppx_deriving_yojson - ppx_blob)) - (preprocessor_deps (file util/options.schema.json))) + ppx_deriving_yojson))) (documentation) diff --git a/src/common/framework/analysisState.ml b/src/common/framework/analysisState.ml index 05a93741f8..fd76e1bb67 100644 --- a/src/common/framework/analysisState.ml +++ b/src/common/framework/analysisState.ml @@ -7,6 +7,8 @@ let should_warn = ref false (** Whether signed overflow or underflow happened *) let svcomp_may_overflow = ref false +(** Whether the termination analysis detects the program as non-terminating *) +let svcomp_may_not_terminate = ref false (** Whether an invalid free happened *) let svcomp_may_invalid_free = ref false diff --git a/src/framework/cfgTools.ml b/src/common/framework/cfgTools.ml similarity index 98% rename from src/framework/cfgTools.ml rename to src/common/framework/cfgTools.ml index 8f98a48e84..78aba17060 100644 --- a/src/framework/cfgTools.ml +++ b/src/common/framework/cfgTools.ml @@ -122,10 +122,6 @@ let rec pretty_edges () = function | [_,x] -> Edge.pretty_plain () x | (_,x)::xs -> Pretty.dprintf "%a; %a" Edge.pretty_plain x pretty_edges xs -let get_pseudo_return_id fd = - let start_id = 10_000_000_000 in (* TODO get max_sid? *) - let sid = Hashtbl.hash fd.svar.vid in (* Need pure sid instead of Cil.new_sid for incremental, similar to vid in Cilfacade.create_var. We only add one return stmt per loop, so the hash from the functions vid should be unique. *) - if sid < start_id then sid + start_id else sid let node_scc_global = NH.create 113 @@ -260,7 +256,7 @@ let createCFG (file: file) = if Messages.tracing then Messages.trace "cfg" "adding pseudo-return to the function %s.\n" fd.svar.vname; let fd_end_loc = {fd_loc with line = fd_loc.endLine; byte = fd_loc.endByte; column = fd_loc.endColumn} in let newst = mkStmt (Return (None, fd_end_loc)) in - newst.sid <- get_pseudo_return_id fd; + newst.sid <- Cilfacade.get_pseudo_return_id fd; Cilfacade.StmtH.add Cilfacade.pseudo_return_to_fun newst fd; Cilfacade.IntH.replace Cilfacade.pseudo_return_stmt_sids newst.sid newst; let newst_node = Statement newst in @@ -475,7 +471,7 @@ let createCFG (file: file) = ); if Messages.tracing then Messages.trace "cfg" "CFG building finished.\n\n"; if get_bool "dbg.verbose" then - ignore (Pretty.eprintf "cfgF (%a), cfgB (%a)\n" GobHashtbl.pretty_statistics (GobHashtbl.magic_stats cfgF) GobHashtbl.pretty_statistics (GobHashtbl.magic_stats cfgB)); + ignore (Pretty.eprintf "cfgF (%a), cfgB (%a)\n" GobHashtbl.pretty_statistics (NH.stats cfgF) GobHashtbl.pretty_statistics (NH.stats cfgB)); cfgF, cfgB, skippedByEdge let createCFG = Timing.wrap "createCFG" createCFG @@ -685,7 +681,7 @@ let getGlobalInits (file: file) : edges = lval in let rec any_index_offset = function - | Index (e,o) -> Index (Offset.Index.Exp.any, any_index_offset o) + | Index (e,o) -> Index (Cilfacade.any_index_exp, any_index_offset o) | Field (f,o) -> Field (f, any_index_offset o) | NoOffset -> NoOffset in diff --git a/src/common/util/cilfacade.ml b/src/common/util/cilfacade.ml index ba57074e5a..eff97da404 100644 --- a/src/common/util/cilfacade.ml +++ b/src/common/util/cilfacade.ml @@ -74,19 +74,18 @@ let print (fileAST: file) = let rmTemps fileAST = RmUnused.removeUnused fileAST - let visitors = ref [] let register_preprocess name visitor_fun = visitors := !visitors @ [name, visitor_fun] let do_preprocess ast = - let f fd (name, visitor_fun) = - (* this has to be done here, since the settings aren't available when register_preprocess is called *) - if List.mem name (get_string_list "ana.activated") then - ignore @@ visitCilFunction (visitor_fun fd) fd - in - iterGlobals ast (function GFun (fd,_) -> List.iter (f fd) !visitors | _ -> ()) - + (* this has to be done here, since the settings aren't available when register_preprocess is called *) + let active_visitors = List.filter_map (fun (name, visitor_fun) -> if List.mem name (get_string_list "ana.activated") then Some visitor_fun else None) !visitors in + let f fd visitor_fun = ignore @@ visitCilFunction (visitor_fun fd) fd in + if active_visitors <> [] then + iterGlobals ast (function GFun (fd,_) -> List.iter (f fd) active_visitors | _ -> ()) + else + () (** @raise GoblintCil.FrontC.ParseError @raise GoblintCil.Errormsg.Error *) @@ -532,6 +531,12 @@ let stmt_fundecs: fundec StmtH.t ResettableLazy.t = h ) + +let get_pseudo_return_id fd = + let start_id = 10_000_000_000 in (* TODO get max_sid? *) + let sid = Hashtbl.hash fd.svar.vid in (* Need pure sid instead of Cil.new_sid for incremental, similar to vid in Cilfacade.create_var. We only add one return stmt per loop, so the hash from the functions vid should be unique. *) + if sid < start_id then sid + start_id else sid + let pseudo_return_to_fun = StmtH.create 113 (** Find [fundec] which the [stmt] is in. *) @@ -667,9 +672,16 @@ let find_stmt_sid sid = try IntH.find pseudo_return_stmt_sids sid with Not_found -> IntH.find (ResettableLazy.force stmt_sids) sid +module FunLocH = Hashtbl.Make(CilType.Fundec) +module LocSet = Hashtbl.Make(CilType.Location) + +(** Contains the locations of the upjumping gotos and the respective functions + * they are being called in. *) +let funs_with_upjumping_gotos: unit LocSet.t FunLocH.t = FunLocH.create 13 -let reset_lazy () = +let reset_lazy ?(keepupjumpinggotos=false) () = StmtH.clear pseudo_return_to_fun; + if not keepupjumpinggotos then FunLocH.clear funs_with_upjumping_gotos; ResettableLazy.reset stmt_fundecs; ResettableLazy.reset varinfo_fundecs; ResettableLazy.reset name_fundecs; @@ -701,3 +713,9 @@ let add_function_declarations (file: Cil.file): unit = let fun_decls = List.filter_map declaration_from_GFun functions in let globals = upto_last_type @ fun_decls @ non_types @ functions in file.globals <- globals + + +(** Special index expression for some unknown index. + Weakly updates array in assignment. + Used for [exp.fast_global_inits]. *) +let any_index_exp = CastE (TInt (ptrdiff_ikind (), []), mkString "any_index") (* TODO: move back to Offset *) diff --git a/src/common/util/messageCategory.ml b/src/common/util/messageCategory.ml index c70b8faf5f..41c9bc08e1 100644 --- a/src/common/util/messageCategory.ml +++ b/src/common/util/messageCategory.ml @@ -46,6 +46,7 @@ type category = | Imprecise | Witness | Program + | Termination [@@deriving eq, ord, hash] type t = category [@@deriving eq, ord, hash] @@ -204,6 +205,7 @@ let should_warn e = | Imprecise -> "imprecise" | Witness -> "witness" | Program -> "program" + | Termination -> "termination" (* Don't forget to add option to schema! *) in get_bool ("warn." ^ (to_string e)) @@ -224,6 +226,7 @@ let path_show e = | Imprecise -> ["Imprecise"] | Witness -> ["Witness"] | Program -> ["Program"] + | Termination -> ["Termination"] let show x = String.concat " > " (path_show x) @@ -263,6 +266,7 @@ let categoryName = function | Overflow -> "Overflow"; | DivByZero -> "DivByZero") | Float -> "Float" + | Termination -> "Termination" let from_string_list (s: string list) = @@ -283,6 +287,7 @@ let from_string_list (s: string list) = | "imprecise" -> Imprecise | "witness" -> Witness | "program" -> Program + | "termination" -> Termination | _ -> Unknown let to_yojson x = `List (List.map (fun x -> `String x) (path_show x)) diff --git a/src/common/util/messages.ml b/src/common/util/messages.ml index 42a3118978..d7afec43c5 100644 --- a/src/common/util/messages.ml +++ b/src/common/util/messages.ml @@ -339,4 +339,23 @@ let msg_final severity ?(tags=[]) ?(category=Category.Unknown) fmt = else GobPretty.igprintf () fmt -include Tracing + +include Goblint_tracing + +open Pretty + +let tracel sys ?var fmt = + let loc = !current_loc in + let docloc sys doc = + printtrace sys (dprintf "(%a)@?" CilType.Location.pretty loc ++ indent 2 doc); + in + gtrace true docloc sys var ~loc ignore fmt + +let traceli sys ?var ?(subsys=[]) fmt = + let loc = !current_loc in + let g () = activate sys subsys in + let docloc sys doc: unit = + printtrace sys (dprintf "(%a)" CilType.Location.pretty loc ++ indent 2 doc); + traceIndent () + in + gtrace true docloc sys var ~loc g fmt diff --git a/src/common/util/afterConfig.ml b/src/config/afterConfig.ml similarity index 100% rename from src/common/util/afterConfig.ml rename to src/config/afterConfig.ml diff --git a/src/config/config.mld b/src/config/config.mld new file mode 100644 index 0000000000..160eaa9a11 --- /dev/null +++ b/src/config/config.mld @@ -0,0 +1,14 @@ +{0 Library goblint.config} +This library is unwrapped and provides the following top-level modules. +For better context, see {!Goblint_lib} which also documents these modules. + + +{1 Framework} + +{2 Configuration} +{!modules: +GobConfig +AfterConfig +JsonSchema +Options +} diff --git a/src/config/dune b/src/config/dune new file mode 100644 index 0000000000..1508e2553e --- /dev/null +++ b/src/config/dune @@ -0,0 +1,23 @@ +(include_subdirs no) + +(library + (name goblint_config) + (public_name goblint.config) + (wrapped false) ; TODO: wrap + (libraries + batteries.unthreaded + goblint_std + goblint_tracing + fpath + yojson + json-data-encoding + cpu + goblint.sites + qcheck-core.runner) + (flags :standard -open Goblint_std) + (preprocess + (pps + ppx_blob)) + (preprocessor_deps (file options.schema.json))) + +(documentation) diff --git a/src/common/util/gobConfig.ml b/src/config/gobConfig.ml similarity index 96% rename from src/common/util/gobConfig.ml rename to src/config/gobConfig.ml index c517ba150d..24a1701ce6 100644 --- a/src/common/util/gobConfig.ml +++ b/src/config/gobConfig.ml @@ -21,7 +21,6 @@ *) open Batteries -open Tracing open Printf exception ConfigError of string @@ -300,7 +299,7 @@ struct try let st = String.trim st in let x = get_value !json_conf (parse_path st) in - if tracing then trace "conf-reads" "Reading '%s', it is %a.\n" st GobYojson.pretty x; + if Goblint_tracing.tracing then Goblint_tracing.trace "conf-reads" "Reading '%s', it is %a.\n" st GobYojson.pretty x; try f x with Yojson.Safe.Util.Type_error (s, _) -> eprintf "The value for '%s' has the wrong type: %s\n" st s; @@ -332,7 +331,7 @@ struct let wrap_get f x = (* self-observe options, which Spec construction depends on *) - if !building_spec && Tracing.tracing then Tracing.trace "config" "get during building_spec: %s\n" x; + if !building_spec && Goblint_tracing.tracing then Goblint_tracing.trace "config" "get during building_spec: %s\n" x; (* TODO: blacklist such building_spec option from server mode modification since it will have no effect (spec is already built) *) f x @@ -352,7 +351,7 @@ struct (** Helper function for writing values. Handles the tracing. *) let set_path_string st v = - if tracing then trace "conf" "Setting '%s' to %a.\n" st GobYojson.pretty v; + if Goblint_tracing.tracing then Goblint_tracing.trace "conf" "Setting '%s' to %a.\n" st GobYojson.pretty v; set_value v json_conf (parse_path st) let set_json st j = @@ -402,7 +401,7 @@ struct | Some fn -> let v = Yojson.Safe.from_channel % BatIO.to_input_channel |> File.with_file_in (Fpath.to_string fn) in merge v; - if tracing then trace "conf" "Merging with '%a', resulting\n%a.\n" GobFpath.pretty fn GobYojson.pretty !json_conf + if Goblint_tracing.tracing then Goblint_tracing.trace "conf" "Merging with '%a', resulting\n%a.\n" GobFpath.pretty fn GobYojson.pretty !json_conf | None -> raise (Sys_error (Printf.sprintf "%s: No such file or diretory" (Fpath.to_string fn))) end diff --git a/src/common/util/jsonSchema.ml b/src/config/jsonSchema.ml similarity index 100% rename from src/common/util/jsonSchema.ml rename to src/config/jsonSchema.ml diff --git a/src/common/util/options.ml b/src/config/options.ml similarity index 98% rename from src/common/util/options.ml rename to src/config/options.ml index 3046f70809..125da3330b 100644 --- a/src/common/util/options.ml +++ b/src/config/options.ml @@ -1,4 +1,4 @@ -(** [src/common/util/options.schema.json] low-level access. *) +(** [src/config/options.schema.json] low-level access. *) open Json_schema diff --git a/src/common/util/options.schema.json b/src/config/options.schema.json similarity index 96% rename from src/common/util/options.schema.json rename to src/config/options.schema.json index 40669ff729..4d9546a9ca 100644 --- a/src/common/util/options.schema.json +++ b/src/config/options.schema.json @@ -352,7 +352,7 @@ "description": "List of path-sensitive analyses", "type": "array", "items": { "type": "string" }, - "default": [ "mutex", "malloc_null", "uninit", "expsplit","activeSetjmp" ] + "default": [ "mutex", "malloc_null", "uninit", "expsplit","activeSetjmp","memLeak" ] }, "ctx_insens": { "title": "ana.ctx_insens", @@ -467,32 +467,6 @@ }, "additionalProperties": false }, - "file": { - "title": "ana.file", - "type": "object", - "properties": { - "optimistic": { - "title": "ana.file.optimistic", - "description": "Assume fopen never fails.", - "type": "boolean", - "default": false - } - }, - "additionalProperties": false - }, - "spec": { - "title": "ana.spec", - "type": "object", - "properties": { - "file": { - "title": "ana.spec.file", - "description": "Path to the specification file.", - "type": "string", - "default": "" - } - }, - "additionalProperties": false - }, "pml": { "title": "ana.pml", "type": "object", @@ -542,9 +516,39 @@ "title": "ana.autotune.activated", "description": "Lists of activated tuning options.", "type": "array", - "items": { "type": "string" }, + "items": { + "type": "string", + "enum": [ + "congruence", + "singleThreaded", + "specification", + "mallocWrappers", + "noRecursiveIntervals", + "enums", + "loopUnrollHeuristic", + "forceLoopUnrollForFewLoops", + "arrayDomain", + "octagon", + "wideningThresholds", + "memsafetySpecification", + "termination", + "tmpSpecialAnalysis" + ] + }, "default": [ - "congruence", "singleThreaded", "specification", "mallocWrappers", "noRecursiveIntervals", "enums", "loopUnrollHeuristic", "arrayDomain", "octagon", "wideningThresholds", "memsafetySpecification" + "congruence", + "singleThreaded", + "specification", + "mallocWrappers", + "noRecursiveIntervals", + "enums", + "loopUnrollHeuristic", + "arrayDomain", + "octagon", + "wideningThresholds", + "memsafetySpecification", + "termination", + "tmpSpecialAnalysis" ] } }, @@ -625,11 +629,19 @@ }, "additionalProperties": false }, - "limit-string-addresses": { - "title": "ana.base.limit-string-addresses", - "description": "Limit abstract address sets to keep at most one distinct string pointer.", - "type": "boolean", - "default": true + "strings": { + "title": "ana.base.strings", + "type": "object", + "properties": { + "domain": { + "title": "ana.base.strings.domain", + "description": "Domain for string literals.", + "type": "string", + "enum": ["unit", "flat", "disjoint"], + "default": "flat" + } + }, + "additionalProperties": false }, "partition-arrays": { "title": "ana.base.partition-arrays", @@ -1008,6 +1020,12 @@ "type": "boolean", "default": true }, + "call": { + "title": "ana.race.call", + "description": "Report races for thread-unsafe function calls.", + "type": "boolean", + "default": true + }, "direct-arithmetic": { "title": "ana.race.direct-arithmetic", "description": "Collect and distribute direct (i.e. not in a field) accesses to arithmetic types.", @@ -2105,6 +2123,12 @@ "type": "boolean", "default": true }, + "termination": { + "title": "warn.termination", + "description": "Non-Termination warning", + "type": "boolean", + "default": true + }, "unknown": { "title": "warn.unknown", "description": "Unknown (of string) warnings", @@ -2158,6 +2182,25 @@ "description": "Output messages in deterministic order. Useful for cram testing.", "type": "boolean", "default": false + }, + "memleak": { + "title": "warn.memleak", + "type":"object", + "properties": { + "memcleanup": { + "title": "warn.memleak.memcleanup", + "description": "Enable memory leak warnings only for violations of the SV-COMP \"valid-memcleanup\" category", + "type": "boolean", + "default": false + }, + "memtrack": { + "title": "warn.memleak.memtrack", + "description": "Enable memory leak warnings only for violations of the SV-COMP \"valid-memtrack\" category", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2418,6 +2461,16 @@ "type": "boolean", "default": false }, + "format-version": { + "title": "witness.yaml.format-version", + "description": "YAML witness format version", + "type": "string", + "enum": [ + "0.1", + "2.0" + ], + "default": "0.1" + }, "entry-types": { "title": "witness.yaml.entry-types", "description": "YAML witness entry types to output/input.", @@ -2430,7 +2483,8 @@ "flow_insensitive_invariant", "precondition_loop_invariant", "loop_invariant_certificate", - "precondition_loop_invariant_certificate" + "precondition_loop_invariant_certificate", + "invariant_set" ] }, "default": [ @@ -2438,7 +2492,24 @@ "loop_invariant", "flow_insensitive_invariant", "loop_invariant_certificate", - "precondition_loop_invariant_certificate" + "precondition_loop_invariant_certificate", + "invariant_set" + ] + }, + "invariant-types": { + "title": "witness.yaml.invariant-types", + "description": "YAML witness invariant types to output/input.", + "type": "array", + "items": { + "type": "string", + "enum": [ + "location_invariant", + "loop_invariant" + ] + }, + "default": [ + "location_invariant", + "loop_invariant" ] }, "path": { @@ -2453,6 +2524,12 @@ "type": "string", "default": "" }, + "strict": { + "title": "witness.yaml.strict", + "description": "", + "type": "boolean", + "default": false + }, "unassume": { "title": "witness.yaml.unassume", "description": "YAML witness input path", diff --git a/src/domains/boolDomain.ml b/src/domain/boolDomain.ml similarity index 72% rename from src/domains/boolDomain.ml rename to src/domain/boolDomain.ml index e088c3605c..08be66a602 100644 --- a/src/domains/boolDomain.ml +++ b/src/domain/boolDomain.ml @@ -4,10 +4,10 @@ module Bool = struct include Basetype.RawBools (* type t = bool - let equal = Bool.equal - let compare = Bool.compare - let relift x = x - let arbitrary () = QCheck.bool *) + let equal = Bool.equal + let compare = Bool.compare + let relift x = x + let arbitrary () = QCheck.bool *) let pretty_diff () (x,y) = GoblintCil.Pretty.dprintf "%s: %a not leq %a" (name ()) pretty x pretty y end @@ -38,4 +38,10 @@ struct let widen = (&&) let meet = (||) let narrow = (||) -end \ No newline at end of file +end + +module FlatBool: Lattice.S with type t = [`Bot | `Lifted of bool | `Top] = + Lattice.Flat (Bool) (struct + let top_name = "?" + let bot_name = "-" + end) diff --git a/src/domains/disjointDomain.ml b/src/domain/disjointDomain.ml similarity index 100% rename from src/domains/disjointDomain.ml rename to src/domain/disjointDomain.ml diff --git a/src/domain/domain.mld b/src/domain/domain.mld new file mode 100644 index 0000000000..ce7e1a5859 --- /dev/null +++ b/src/domain/domain.mld @@ -0,0 +1,21 @@ +{0 Library goblint.domain} +This library is unwrapped and provides the following top-level modules. +For better context, see {!Goblint_lib} which also documents these modules. + + +{1 Domains} +{!modules: +Lattice +} + +{2 General} +{!modules: +BoolDomain +SetDomain +MapDomain +TrieDomain +DisjointDomain +HoareDomain +PartitionDomain +FlagHelper +} diff --git a/src/domain/dune b/src/domain/dune new file mode 100644 index 0000000000..169f4a1d5c --- /dev/null +++ b/src/domain/dune @@ -0,0 +1,19 @@ +(include_subdirs no) + +(library + (name goblint_domain) + (public_name goblint.domain) + (wrapped false) ; TODO: wrap + (libraries + batteries.unthreaded + goblint_std + goblint_common + goblint-cil) + (flags :standard -open Goblint_std) + (preprocess + (pps + ppx_deriving.std + ppx_deriving_hash + ppx_deriving_yojson))) + +(documentation) diff --git a/src/domains/flagHelper.ml b/src/domain/flagHelper.ml similarity index 100% rename from src/domains/flagHelper.ml rename to src/domain/flagHelper.ml diff --git a/src/domains/hoareDomain.ml b/src/domain/hoareDomain.ml similarity index 97% rename from src/domains/hoareDomain.ml rename to src/domain/hoareDomain.ml index 23b1a92240..37b8231b92 100644 --- a/src/domains/hoareDomain.ml +++ b/src/domain/hoareDomain.ml @@ -134,13 +134,15 @@ struct let equal x y = leq x y && leq y x let hash xs = fold (fun v a -> a + E.hash v) xs 0 let compare x y = - if equal x y - then 0 + if equal x y then + 0 + else ( + let caridnality_comp = compare (cardinal x) (cardinal y) in + if caridnality_comp <> 0 then + caridnality_comp else - let caridnality_comp = compare (cardinal x) (cardinal y) in - if caridnality_comp <> 0 - then caridnality_comp - else Map.compare (List.compare E.compare) x y + Map.compare (List.compare E.compare) x y + ) let show x : string = let all_elems : string list = List.map E.show (elements x) in Printable.get_short_list "{" "}" all_elems @@ -234,8 +236,8 @@ struct ) s2 nil with Not_found -> dprintf "choose failed b/c of empty set s1: %d s2: %d" - (cardinal s1) - (cardinal s2) + (cardinal s1) + (cardinal s2) end end @@ -339,8 +341,8 @@ struct ) s2 nil with Not_found -> dprintf "choose failed b/c of empty set s1: %d s2: %d" - (cardinal s1) - (cardinal s2) + (cardinal s1) + (cardinal s2) end end [@@deprecated] diff --git a/src/common/domains/lattice.ml b/src/domain/lattice.ml similarity index 96% rename from src/common/domains/lattice.ml rename to src/domain/lattice.ml index 51306d637f..9ea3f74635 100644 --- a/src/common/domains/lattice.ml +++ b/src/domain/lattice.ml @@ -148,18 +148,14 @@ struct end (* HAS SIDE-EFFECTS ---- PLEASE INSTANCIATE ONLY ONCE!!! *) -module HConsed (Base:S) = +module HConsed (Base:S) (Arg: sig val assume_idempotent: bool end) = struct include Printable.HConsed (Base) - (* We do refine int values on narrow and meet {!IntDomain.IntDomTupleImpl}, which can lead to fixpoint issues if we assume x op x = x *) - (* see https://github.com/goblint/analyzer/issues/1005 *) - let int_refine_active = GobConfig.get_string "ana.int.refinement" <> "never" - let lift_f2 f x y = f (unlift x) (unlift y) - let narrow x y = if (not int_refine_active) && x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.narrow x y) + let narrow x y = if Arg.assume_idempotent && x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.narrow x y) let widen x y = if x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.widen x y) - let meet x y = if (not int_refine_active) && x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.meet x y) + let meet x y = if Arg.assume_idempotent && x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.meet x y) let join x y = if x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.join x y) let leq x y = (x.BatHashcons.tag == y.BatHashcons.tag) || lift_f2 Base.leq x y let is_top = lift_f Base.is_top diff --git a/src/domains/mapDomain.ml b/src/domain/mapDomain.ml similarity index 98% rename from src/domains/mapDomain.ml rename to src/domain/mapDomain.ml index 76dec6f0d2..901056a5f4 100644 --- a/src/domains/mapDomain.ml +++ b/src/domain/mapDomain.ml @@ -138,7 +138,7 @@ end module PMap (Domain: Printable.S) (Range: Lattice.S) : PS with type key = Domain.t and - type value = Range.t = +type value = Range.t = struct module M = Map.Make (Domain) @@ -212,7 +212,7 @@ end (* TODO: why is HashCached.hash significantly slower as a functor compared to being inlined into PMap? *) module HashCached (M: S) : S with type key = M.key and - type value = M.value = +type value = M.value = struct include Lattice.HashCached (M) @@ -259,11 +259,12 @@ struct end (* TODO: this is very slow because every add/remove in a fold-loop relifts *) +(* TODO: currently hardcoded to assume_idempotent *) module HConsed (M: S) : S with type key = M.key and - type value = M.value = +type value = M.value = struct - include Lattice.HConsed (M) + include Lattice.HConsed (M) (struct let assume_idempotent = false end) type key = M.key type value = M.value @@ -310,7 +311,7 @@ end module Timed (M: S) : S with type key = M.key and - type value = M.value = +type value = M.value = struct let time str f arg = Timing.wrap (M.name ()) (Timing.wrap str f) arg @@ -384,7 +385,7 @@ end module MapBot (Domain: Printable.S) (Range: Lattice.S) : S with type key = Domain.t and - type value = Range.t = +type value = Range.t = struct include PMap (Domain) (Range) @@ -433,7 +434,7 @@ end module MapTop (Domain: Printable.S) (Range: Lattice.S) : S with type key = Domain.t and - type value = Range.t = +type value = Range.t = struct include PMap (Domain) (Range) @@ -485,7 +486,7 @@ exception Fn_over_All of string module LiftTop (Range: Lattice.S) (M: S with type value = Range.t): S with type key = M.key and - type value = Range.t = +type value = Range.t = struct include Lattice.LiftTop (M) @@ -604,7 +605,7 @@ end module MapBot_LiftTop (Domain: Printable.S) (Range: Lattice.S) : S with type key = Domain.t and - type value = Range.t = +type value = Range.t = struct module M = MapBot (Domain) (Range) include LiftTop (Range) (M) @@ -613,7 +614,7 @@ end module LiftBot (Range: Lattice.S) (M: S with type value = Range.t): S with type key = M.key and - type value = Range.t = +type value = Range.t = struct include Lattice.LiftBot (M) @@ -717,8 +718,8 @@ struct let singleton k v = `Lifted (M.singleton k v) let empty () = `Lifted (M.empty ()) let is_empty = function - | `Bot -> false - | `Lifted x -> M.is_empty x + | `Bot -> false + | `Lifted x -> M.is_empty x let exists f = function | `Bot -> raise (Fn_over_All "exists") | `Lifted x -> M.exists f x @@ -732,7 +733,7 @@ end module MapTop_LiftBot (Domain: Printable.S) (Range: Lattice.S): S with type key = Domain.t and - type value = Range.t = +type value = Range.t = struct module M = MapTop (Domain) (Range) include LiftBot (Range) (M) diff --git a/src/domains/partitionDomain.ml b/src/domain/partitionDomain.ml similarity index 100% rename from src/domains/partitionDomain.ml rename to src/domain/partitionDomain.ml diff --git a/src/domains/setDomain.ml b/src/domain/setDomain.ml similarity index 98% rename from src/domains/setDomain.ml rename to src/domain/setDomain.ml index 1b5239de80..a84f9017f6 100644 --- a/src/domains/setDomain.ml +++ b/src/domain/setDomain.ml @@ -159,7 +159,7 @@ end * calling [top ()] will raise an exception *) module Make (Base: Printable.S): S with type elt = Base.t and - type t = BatSet.Make (Base).t = (* TODO: remove, only needed in VarEq for some reason... *) +type t = BatSet.Make (Base).t = (* TODO: remove, only needed in VarEq for some reason... *) struct include Printable.Std include BatSet.Make(Base) @@ -259,7 +259,7 @@ end module LiftTop (S: S) (N: ToppedSetNames): S with type elt = S.elt and - type t = [`Top | `Lifted of S.t] = (* Expose t for HoareDomain.Set_LiftTop *) +type t = [`Top | `Lifted of S.t] = (* Expose t for HoareDomain.Set_LiftTop *) struct include Printable.Std @@ -396,7 +396,7 @@ end (** Functor for creating artificially topped set domains. *) module ToppedSet (Base: Printable.S) (N: ToppedSetNames): S with type elt = Base.t and - type t = [`Top | `Lifted of Make (Base).t] = (* TODO: don't expose t *) +type t = [`Top | `Lifted of Make (Base).t] = (* TODO: don't expose t *) struct module S = Make (Base) include LiftTop (S) (N) diff --git a/src/domains/trieDomain.ml b/src/domain/trieDomain.ml similarity index 100% rename from src/domains/trieDomain.ml rename to src/domain/trieDomain.ml diff --git a/src/domains/access.ml b/src/domains/access.ml index 3ba7aaee74..baa9d34220 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -444,6 +444,8 @@ let may_race A.{kind; acc; _} A.{kind=kind2; acc=acc2; _} = | Read, Read -> false (* two read/read accesses do not race *) | Free, _ | _, Free when not (get_bool "ana.race.free") -> false + | Call, _ + | _, Call when not (get_bool "ana.race.call") -> false | _, _ -> MCPAccess.A.may_race acc acc2 (* analysis-specific information excludes race *) (** Access sets for race detection and warnings. *) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index c706339bf2..228320bef3 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -32,7 +32,11 @@ module FlatYojson = Lattice.Flat (Printable.Yojson) (struct let bot_name = "bot yojson" end) -module SD = Basetype.Strings +module SD: Lattice.S with type t = [`Bot | `Lifted of string | `Top] = + Lattice.Flat (Basetype.RawStrings) (struct + let top_name = "?" + let bot_name = "-" + end) module VD = ValueDomain.Compound module AD = ValueDomain.AD @@ -127,6 +131,9 @@ type _ t = | MayAccessed: AccessDomain.EventSet.t t | MayBeTainted: AD.t t | MayBeModifiedSinceSetjmp: JmpBufDomain.BufferEntry.t -> VS.t t + | MustTermLoop: stmt -> MustBool.t t + | MustTermAllLoops: MustBool.t t + | IsEverMultiThreaded: MayBool.t t | TmpSpecial: Mval.Exp.t -> ML.t t type 'a result = 'a @@ -193,6 +200,9 @@ struct | MayAccessed -> (module AccessDomain.EventSet) | MayBeTainted -> (module AD) | MayBeModifiedSinceSetjmp _ -> (module VS) + | MustTermLoop _ -> (module MustBool) + | MustTermAllLoops -> (module MustBool) + | IsEverMultiThreaded -> (module MayBool) | TmpSpecial _ -> (module ML) (** Get bottom result for query. *) @@ -258,6 +268,9 @@ struct | MayAccessed -> AccessDomain.EventSet.top () | MayBeTainted -> AD.top () | MayBeModifiedSinceSetjmp _ -> VS.top () + | MustTermLoop _ -> MustBool.top () + | MustTermAllLoops -> MustBool.top () + | IsEverMultiThreaded -> MayBool.top () | TmpSpecial _ -> ML.top () end @@ -319,8 +332,11 @@ struct | Any (EvalMutexAttr _ ) -> 50 | Any ThreadCreateIndexedNode -> 51 | Any ThreadsJoinedCleanly -> 52 - | Any (TmpSpecial _) -> 53 - | Any (IsAllocVar _) -> 54 + | Any (MustTermLoop _) -> 53 + | Any MustTermAllLoops -> 54 + | Any IsEverMultiThreaded -> 55 + | Any (TmpSpecial _) -> 56 + | Any (IsAllocVar _) -> 57 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -362,6 +378,7 @@ struct | Any (IsHeapVar v1), Any (IsHeapVar v2) -> CilType.Varinfo.compare v1 v2 | Any (IsAllocVar v1), Any (IsAllocVar v2) -> CilType.Varinfo.compare v1 v2 | Any (IsMultiple v1), Any (IsMultiple v2) -> CilType.Varinfo.compare v1 v2 + | Any (MustTermLoop s1), Any (MustTermLoop s2) -> CilType.Stmt.compare s1 s2 | Any (EvalThread e1), Any (EvalThread e2) -> CilType.Exp.compare e1 e2 | Any (EvalJumpBuf e1), Any (EvalJumpBuf e2) -> CilType.Exp.compare e1 e2 | Any (WarnGlobal vi1), Any (WarnGlobal vi2) -> Stdlib.compare (Hashtbl.hash vi1) (Hashtbl.hash vi2) @@ -401,6 +418,7 @@ struct | Any (IterVars i) -> 0 | Any (PathQuery (i, q)) -> 31 * i + hash (Any q) | Any (IsHeapVar v) -> CilType.Varinfo.hash v + | Any (MustTermLoop s) -> CilType.Stmt.hash s | Any (IsAllocVar v) -> CilType.Varinfo.hash v | Any (IsMultiple v) -> CilType.Varinfo.hash v | Any (EvalThread e) -> CilType.Exp.hash e @@ -471,6 +489,9 @@ struct | Any MayBeTainted -> Pretty.dprintf "MayBeTainted" | Any DYojson -> Pretty.dprintf "DYojson" | Any MayBeModifiedSinceSetjmp buf -> Pretty.dprintf "MayBeModifiedSinceSetjmp %a" JmpBufDomain.BufferEntry.pretty buf + | Any (MustTermLoop s) -> Pretty.dprintf "MustTermLoop %a" CilType.Stmt.pretty s + | Any MustTermAllLoops -> Pretty.dprintf "MustTermAllLoops" + | Any IsEverMultiThreaded -> Pretty.dprintf "IsEverMultiThreaded" | Any (TmpSpecial lv) -> Pretty.dprintf "TmpSpecial %a" Mval.Exp.pretty lv end diff --git a/src/dune b/src/dune index 7a0e4f4749..6528d6b10a 100644 --- a/src/dune +++ b/src/dune @@ -6,11 +6,15 @@ (library (name goblint_lib) (public_name goblint.lib) - (modules :standard \ goblint mainspec privPrecCompare apronPrecCompare messagesCompare) - (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_common + (modules :standard \ goblint privPrecCompare apronPrecCompare messagesCompare) + (libraries goblint.sites goblint.build-info goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace fileutils goblint_std goblint_config goblint_common goblint_domain goblint_library goblint_incremental goblint_tracing ; Conditionally compile based on whether apron optional dependency is installed or not. ; Alternative dependencies seem like the only way to optionally depend on optional dependencies. ; See: https://dune.readthedocs.io/en/stable/concepts.html#alternative-dependencies. + (select gobApron.ml from + (apron -> gobApron.apron.ml) + (-> gobApron.no-apron.ml) + ) (select apronDomain.ml from (apron apron.octD apron.boxD apron.polkaMPQ zarith_mlgmpidl -> apronDomain.apron.ml) (-> apronDomain.no-apron.ml) @@ -81,10 +85,10 @@ (copy_files# witness/z3/*.ml) (executables - (names goblint mainspec) - (public_names goblint -) + (names goblint) + (public_names goblint) (modes byte native) ; https://dune.readthedocs.io/en/stable/dune-files.html#linking-modes - (modules goblint mainspec) + (modules goblint) (libraries goblint.lib goblint.sites.dune goblint.build-info.dune goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall -open Goblint_std) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index eec811afc4..a37a3043c2 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -88,6 +88,22 @@ struct | `Right _ -> true end +module GVarFC (V:SpecSysVar) (C:Printable.S) = +struct + include Printable.Either (V) (Printable.Prod (CilType.Fundec) (C)) + let name () = "FromSpec" + let spec x = `Left x + let call (x, c) = `Right (x, c) + + (* from Basetype.Variables *) + let var_id = show + let node _ = MyCFG.Function Cil.dummyFunDec + let pretty_trace = pretty + let is_write_only = function + | `Left x -> V.is_write_only x + | `Right _ -> true +end + module GVarG (G: Lattice.S) (C: Printable.S) = struct module CSet = @@ -596,6 +612,12 @@ struct let is_write_only _ = false end +module UnitV = +struct + include Printable.Unit + include StdV +end + module VarinfoV = struct include CilType.Varinfo (* TODO: or Basetype.Variables? *) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index cc54c91a5a..77d3a38186 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -12,12 +12,17 @@ module M = Messages (** Lifts a [Spec] so that the domain is [Hashcons]d *) module HashconsLifter (S:Spec) - : Spec with module D = Lattice.HConsed (S.D) - and module G = S.G + : Spec with module G = S.G and module C = S.C = struct - module D = Lattice.HConsed (S.D) + module HConsedArg = + struct + (* We do refine int values on narrow and meet {!IntDomain.IntDomTupleImpl}, which can lead to fixpoint issues if we assume x op x = x *) + (* see https://github.com/goblint/analyzer/issues/1005 *) + let assume_idempotent = GobConfig.get_string "ana.int.refinement" = "never" + end + module D = Lattice.HConsed (S.D) (HConsedArg) module G = S.G module C = S.C module V = S.V @@ -820,13 +825,13 @@ struct ) let tf var getl sidel getg sideg prev_node (_,edge) d (f,t) = - let old_loc = !Tracing.current_loc in - let old_loc2 = !Tracing.next_loc in - Tracing.current_loc := f; - Tracing.next_loc := t; + let old_loc = !Goblint_tracing.current_loc in + let old_loc2 = !Goblint_tracing.next_loc in + Goblint_tracing.current_loc := f; + Goblint_tracing.next_loc := t; Goblint_backtrace.protect ~mark:(fun () -> TfLocation f) ~finally:(fun () -> - Tracing.current_loc := old_loc; - Tracing.next_loc := old_loc2 + Goblint_tracing.current_loc := old_loc; + Goblint_tracing.next_loc := old_loc2 ) (fun () -> let d = tf var getl sidel getg sideg prev_node edge d in d @@ -999,7 +1004,7 @@ struct let dummy_pseudo_return_node f = (* not the same as in CFG, but compares equal because of sid *) - Node.Statement ({Cil.dummyStmt with sid = CfgTools.get_pseudo_return_id f}) + Node.Statement ({Cil.dummyStmt with sid = Cilfacade.get_pseudo_return_id f}) in let add_nodes_of_fun (functions: fundec list) (withEntry: fundec -> bool) = let add_stmts (f: fundec) = @@ -1344,7 +1349,7 @@ struct module EM = struct - include MapDomain.MapBot (Basetype.CilExp) (Basetype.Bools) + include MapDomain.MapBot (Basetype.CilExp) (BoolDomain.FlatBool) let name () = "branches" end @@ -1467,14 +1472,14 @@ struct module V = struct - include Printable.Either (S.V) (Printable.Either (Printable.Prod (Node) (C)) (Printable.Prod (CilType.Fundec) (C))) + include Printable.Either3 (S.V) (Printable.Prod (Node) (C)) (Printable.Prod (CilType.Fundec) (C)) let name () = "longjmp" let s x = `Left x - let longjmpto x = `Right (`Left x) - let longjmpret x = `Right (`Right x) + let longjmpto x = `Middle x + let longjmpret x = `Right x let is_write_only = function | `Left x -> S.V.is_write_only x - | `Right _ -> false + | _ -> false end module G = @@ -1511,7 +1516,7 @@ struct begin match g with | `Left g -> S.query (conv ctx) (WarnGlobal (Obj.repr g)) - | `Right g -> + | _ -> Queries.Result.top q end | InvariantGlobal g -> @@ -1519,7 +1524,7 @@ struct begin match g with | `Left g -> S.query (conv ctx) (InvariantGlobal (Obj.repr g)) - | `Right g -> + | _ -> Queries.Result.top q end | IterSysVars (vq, vf) -> @@ -1687,6 +1692,10 @@ struct ) in List.iter handle_path (S.paths_as_set conv_ctx); + if !AnalysisState.should_warn && List.mem "termination" @@ get_string_list "ana.activated" then ( + AnalysisState.svcomp_may_not_terminate := true; + M.warn ~category:Termination "The program might not terminate! (Longjmp)" + ); S.D.bot () | _ -> S.special conv_ctx lv f args let threadenter ctx = S.threadenter (conv ctx) @@ -1697,6 +1706,149 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx) end + +(** Add cycle detection in the context-sensitive dynamic function call graph to an analysis *) +module RecursionTermLifter (S: Spec) + : Spec with module D = S.D + and module C = S.C += +(* two global invariants: + - S.V -> S.G + Needed to store the previously built global invariants + - fundec * S.C -> (Set (fundec * S.C)) + The second global invariant maps from the callee fundec and context to a set of caller fundecs and contexts. + This structure therefore stores the context-sensitive call graph. + For example: + let the function f in context c call function g in context c'. + In the global invariant structure it would be stored like this: (g,c') -> {(f, c)} +*) + +struct + include S + + (* contains all the callee fundecs and contexts *) + module V = GVarFC(S.V)(S.C) + + (* Tuple containing the fundec and context of a caller *) + module Call = Printable.Prod (CilType.Fundec) (S.C) + + (* Set containing multiple caller tuples *) + module CallerSet = SetDomain.Make (Call) + + module G = + struct + include Lattice.Lift2 (G) (CallerSet) (Printable.DefaultNames) + + let spec = function + | `Bot -> G.bot () + | `Lifted1 x -> x + | _ -> failwith "RecursionTermLifter.spec" + + let callers = function + | `Bot -> CallerSet.bot () + | `Lifted2 x -> x + | _ -> failwith "RecursionTermLifter.callGraph" + + let create_spec spec = `Lifted1 spec + let create_singleton_caller caller = `Lifted2 (CallerSet.singleton caller) + + let printXml f = function + | `Lifted1 x -> G.printXml f x + | `Lifted2 x -> BatPrintf.fprintf f "%a" CallerSet.printXml x + | x -> BatPrintf.fprintf f "%a" printXml x + + end + + let name () = "RecursionTermLifter (" ^ S.name () ^ ")" + + let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = + { ctx with + global = (fun v -> G.spec (ctx.global (V.spec v))); + sideg = (fun v g -> ctx.sideg (V.spec v) (G.create_spec g)); + } + + let cycleDetection ctx call = + let module LH = Hashtbl.Make (Printable.Prod (CilType.Fundec) (S.C)) in + let module LS = Set.Make (Printable.Prod (CilType.Fundec) (S.C)) in + (* find all cycles/SCCs *) + let global_visited_calls = LH.create 100 in + + (* DFS *) + let rec iter_call (path_visited_calls: LS.t) ((fundec, _) as call) = + if LS.mem call path_visited_calls then ( + AnalysisState.svcomp_may_not_terminate := true; (*set the indicator for a non-terminating program for the sv comp*) + (*Cycle found*) + let loc = M.Location.CilLocation fundec.svar.vdecl in + M.warn ~loc ~category:Termination "The program might not terminate! (Fundec %a is contained in a call graph cycle)" CilType.Fundec.pretty fundec) (* output a warning for non-termination*) + else if not (LH.mem global_visited_calls call) then begin + LH.replace global_visited_calls call (); + let new_path_visited_calls = LS.add call path_visited_calls in + let gvar = V.call call in + let callers = G.callers (ctx.global gvar) in + CallerSet.iter (fun to_call -> + iter_call new_path_visited_calls to_call + ) callers; + end + in + iter_call LS.empty call + + let query ctx (type a) (q: a Queries.t): a Queries.result = + match q with + | WarnGlobal v -> + (* check result of loop analysis *) + if not (ctx.ask Queries.MustTermAllLoops) then + AnalysisState.svcomp_may_not_terminate := true; + let v: V.t = Obj.obj v in + begin match v with + | `Left v' -> + S.query (conv ctx) (WarnGlobal (Obj.repr v')) + | `Right call -> cycleDetection ctx call (* Note: to make it more efficient, one could only execute the cycle detection in case the loop analysis returns true, because otherwise the program will probably not terminate anyway*) + end + | InvariantGlobal v -> + let v: V.t = Obj.obj v in + begin match v with + | `Left v -> + S.query (conv ctx) (InvariantGlobal (Obj.repr v)) + | `Right v -> + Queries.Result.top q + end + | _ -> S.query (conv ctx) q + + let branch ctx = S.branch (conv ctx) + let assign ctx = S.assign (conv ctx) + let vdecl ctx = S.vdecl (conv ctx) + + + let record_call sideg callee caller = + sideg (V.call callee) (G.create_singleton_caller caller) + + let enter ctx = S.enter (conv ctx) + let paths_as_set ctx = S.paths_as_set (conv ctx) + let body ctx = S.body (conv ctx) + let return ctx = S.return (conv ctx) + let combine_env ctx r fe f args fc es f_ask = + if !AnalysisState.postsolving then ( + let c_r: S.C.t = ctx.context () in (* Caller context *) + let nodeF = ctx.node in + let fd_r : fundec = Node.find_fundec nodeF in (* Caller fundec *) + let caller: (fundec * S.C.t) = (fd_r, c_r) in + let c_e: S.C.t = Option.get fc in (* Callee context *) + let fd_e : fundec = f in (* Callee fundec *) + let callee = (fd_e, c_e) in + record_call ctx.sideg callee caller + ); + S.combine_env (conv ctx) r fe f args fc es f_ask + + let combine_assign ctx = S.combine_assign (conv ctx) + let special ctx = S.special (conv ctx) + let threadenter ctx = S.threadenter (conv ctx) + let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) + let sync ctx = S.sync (conv ctx) + let skip ctx = S.skip (conv ctx) + let asm ctx = S.asm (conv ctx) + let event ctx e octx = S.event (conv ctx) e (conv octx) +end + module CompareGlobSys (SpecSys: SpecSys) = struct open SpecSys diff --git a/src/framework/control.ml b/src/framework/control.ml index 5c938cfd08..00a6034e27 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -15,6 +15,7 @@ module type S2S = functor (X : Spec) -> Spec let spec_module: (module Spec) Lazy.t = lazy ( GobConfig.building_spec := true; let arg_enabled = get_bool "witness.graphml.enabled" || get_bool "exp.arg" in + let termination_enabled = List.mem "termination" (get_string_list "ana.activated") in (* check if loop termination analysis is enabled*) let open Batteries in (* apply functor F on module X if opt is true *) let lift opt (module F : S2S) (module X : Spec) = (module (val if opt then (module F (X)) else (module X) : Spec) : Spec) in @@ -36,6 +37,7 @@ let spec_module: (module Spec) Lazy.t = lazy ( Also must be outside of deadcode, because deadcode splits (like mutex lock event) don't pass on tokens. *) |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) |> lift true (module LongjmpLifter) + |> lift termination_enabled (module RecursionTermLifter) (* Always activate the recursion termination analysis, when the loop termination analysis is activated*) ) in GobConfig.building_spec := false; ControlSpecC.control_spec_c := (module S1.C); @@ -103,6 +105,8 @@ struct let module StringMap = BatMap.Make (String) in let live_lines = ref StringMap.empty in let dead_lines = ref StringMap.empty in + let module FunSet = Hashtbl.Make (CilType.Fundec) in + let live_funs: unit FunSet.t = FunSet.create 13 in let add_one n v = match n with | Statement s when Cilfacade.(StmtH.mem pseudo_return_to_fun s) -> @@ -113,6 +117,7 @@ struct See: https://github.com/goblint/analyzer/issues/290#issuecomment-881258091. *) let l = UpdateCil.getLoc n in let f = Node.find_fundec n in + FunSet.replace live_funs f (); let add_fun = BatISet.add l.line in let add_file = StringMap.modify_def BatISet.empty f.svar.vname add_fun in let is_dead = LT.for_all (fun (_,x,f) -> Spec.D.is_bot x) v in @@ -134,6 +139,21 @@ struct try StringMap.find fn (StringMap.find file !live_lines) with Not_found -> BatISet.empty in + if List.mem "termination" @@ get_string_list "ana.activated" then ( + (* check if we have upjumping gotos *) + let open Cilfacade in + let warn_for_upjumps fundec gotos = + if FunSet.mem live_funs fundec then ( + (* set nortermiantion flag *) + AnalysisState.svcomp_may_not_terminate := true; + (* iterate through locations to produce warnings *) + LocSet.iter (fun l _ -> + M.warn ~loc:(M.Location.CilLocation l) ~category:Termination "The program might not terminate! (Upjumping Goto)" + ) gotos + ) + in + FunLocH.iter warn_for_upjumps funs_with_upjumping_gotos + ); dead_lines := StringMap.mapi (fun fi -> StringMap.mapi (fun fu ded -> BatISet.diff ded (live fi fu))) !dead_lines; dead_lines := StringMap.map (StringMap.filter (fun _ x -> not (BatISet.is_empty x))) !dead_lines; dead_lines := StringMap.filter (fun _ x -> not (StringMap.is_empty x)) !dead_lines; @@ -293,7 +313,7 @@ struct if M.tracing then M.trace "con" "Initializer %a\n" CilType.Location.pretty loc; (*incr count; if (get_bool "dbg.verbose")&& (!count mod 1000 = 0) then Printf.printf "%d %!" !count; *) - Tracing.current_loc := loc; + Goblint_tracing.current_loc := loc; match edge with | MyCFG.Entry func -> if M.tracing then M.trace "global_inits" "Entry %a\n" d_lval (var func.svar); @@ -315,9 +335,9 @@ struct in let with_externs = do_extern_inits ctx file in (*if (get_bool "dbg.verbose") then Printf.printf "Number of init. edges : %d\nWorking:" (List.length edges); *) - let old_loc = !Tracing.current_loc in + let old_loc = !Goblint_tracing.current_loc in let result : Spec.D.t = List.fold_left transfer_func with_externs edges in - Tracing.current_loc := old_loc; + Goblint_tracing.current_loc := old_loc; if M.tracing then M.trace "global_inits" "startstate: %a\n" Spec.D.pretty result; result, !funs in diff --git a/src/goblint.ml b/src/goblint.ml index f0c9a47111..e3639b2b32 100644 --- a/src/goblint.ml +++ b/src/goblint.ml @@ -38,6 +38,8 @@ let main () = print_endline (GobUnix.localtime ()); print_endline GobSys.command_line; ); + (* When analyzing a termination specification, activate the termination analysis before pre-processing. *) + if get_bool "ana.autotune.enabled" && AutoTune.specificationTerminationIsActivated () then AutoTune.focusOnTermination (); let file = lazy (Fun.protect ~finally:GoblintDir.finalize preprocess_parse_merge) in if get_bool "server.enabled" then ( let file = diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index a71a0c9684..5a2e0d3e0e 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -51,7 +51,7 @@ module VarQuery = VarQuery (** {2 Configuration} Runtime configuration is represented as JSON. - Options are specified and documented by the JSON schema [src/common/util/options.schema.json]. *) + Options are specified and documented by the JSON schema [src/config/options.schema.json]. *) module GobConfig = GobConfig module AfterConfig = AfterConfig @@ -130,7 +130,7 @@ module ExtractPthread = ExtractPthread Analyses related to [longjmp] and [setjmp]. *) module ActiveSetjmp = ActiveSetjmp -module ModifiedSinceLongjmp = ModifiedSinceLongjmp +module ModifiedSinceSetjmp = ModifiedSinceSetjmp module ActiveLongjmp = ActiveLongjmp module PoisonVariables = PoisonVariables module Vla = Vla @@ -147,12 +147,10 @@ module UnitAnalysis = UnitAnalysis (** {2 Other} *) module Assert = Assert -module FileUse = FileUse +module LoopTermination = LoopTermination module Uninit = Uninit -module Termination = Termination module Expsplit = Expsplit module StackTrace = StackTrace -module Spec = Spec (** {2 Helper} @@ -213,6 +211,7 @@ module FloatDomain = FloatDomain module Mval = Mval module Offset = Offset +module StringDomain = StringDomain module AddressDomain = AddressDomain (** {5 Complex} *) @@ -262,12 +261,8 @@ module AccessDomain = AccessDomain module MusteqDomain = MusteqDomain module RegionDomain = RegionDomain -module FileDomain = FileDomain module StackDomain = StackDomain -module MvalMapDomain = MvalMapDomain -module SpecDomain = SpecDomain - (** {2 Testing} Modules related to (property-based) testing of domains. *) @@ -330,7 +325,6 @@ module SolverBox = SolverBox Various input/output interfaces and formats. *) module Messages = Messages -module Tracing = Tracing (** {2 Front-end} @@ -339,6 +333,7 @@ module Tracing = Tracing module Preprocessor = Preprocessor module CompilationDatabase = CompilationDatabase module MakefileUtil = MakefileUtil +module TerminationPreprocessing = TerminationPreprocessing (** {2 Witnesses} @@ -445,6 +440,7 @@ module WideningThresholds = WideningThresholds module VectorMatrix = VectorMatrix module SharedFunctions = SharedFunctions +module GobApron = GobApron (** {2 Precision comparison} *) @@ -465,9 +461,3 @@ module ApronPrecCompareUtil = ApronPrecCompareUtil OCaml standard library extensions which are not provided by {!Batteries}. *) module GobFormat = GobFormat - -(** {2 Other libraries} - - External library extensions. *) - -module MyCheck = MyCheck diff --git a/src/util/cilMaps.ml b/src/incremental/cilMaps.ml similarity index 100% rename from src/util/cilMaps.ml rename to src/incremental/cilMaps.ml diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index 225cbb1c76..55b3fa8fc5 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -17,7 +17,7 @@ let (&&<>) (prev_result: bool * rename_mapping) f : bool * rename_mapping = let eq_node (x, fun1) (y, fun2) ~rename_mapping = let isPseudoReturn f sid = - let pid = CfgTools.get_pseudo_return_id f in + let pid = Cilfacade.get_pseudo_return_id f in sid == pid in match x,y with | Statement s1, Statement s2 -> diff --git a/src/incremental/dune b/src/incremental/dune new file mode 100644 index 0000000000..595dba22f7 --- /dev/null +++ b/src/incremental/dune @@ -0,0 +1,22 @@ +(include_subdirs no) + +(library + (name goblint_incremental) + (public_name goblint.incremental) + (wrapped false) ; TODO: wrap + (libraries + batteries.unthreaded + zarith + goblint_std + goblint_config + goblint_common + goblint-cil + fpath) + (flags :standard -open Goblint_std) + (preprocess + (pps + ppx_deriving.std + ppx_deriving_hash + ppx_deriving_yojson))) + +(documentation) diff --git a/src/incremental/incremental.mld b/src/incremental/incremental.mld new file mode 100644 index 0000000000..bf9b6e6a58 --- /dev/null +++ b/src/incremental/incremental.mld @@ -0,0 +1,16 @@ +{0 Library goblint.incremental} +This library is unwrapped and provides the following top-level modules. +For better context, see {!Goblint_lib} which also documents these modules. + + +{1 Incremental} + +{!modules: +CompareCIL +CompareAST +CompareCFG +UpdateCil +MaxIdUtil +Serialize +CilMaps +} diff --git a/src/index.mld b/src/index.mld index 2afbbc97ae..76b9d230dd 100644 --- a/src/index.mld +++ b/src/index.mld @@ -7,9 +7,21 @@ The following libraries make up Goblint's main codebase. {!modules:Goblint_lib} This library currently contains the majority of Goblint and is in the process of being split into smaller libraries. +{2 Library goblint.config} +This {{!page-config}unwrapped library} contains various configuration modules extracted from {!Goblint_lib}. + {2 Library goblint.common} This {{!page-common}unwrapped library} contains various common modules extracted from {!Goblint_lib}. +{2 Library goblint.domain} +This {{!page-domain}unwrapped library} contains various domain modules extracted from {!Goblint_lib}. + +{2 Library goblint.library} +This {{!page-library}unwrapped library} contains various library specification modules extracted from {!Goblint_lib}. + +{2 Library goblint.incremental} +This {{!page-incremental}unwrapped library} contains various incremental modules extracted from {!Goblint_lib}. + {1 Library extensions} The following libraries provide extensions to other OCaml libraries. @@ -43,6 +55,9 @@ The following libraries provide utilities which are completely independent of Go {2 Library goblint.timing} {!modules:Goblint_timing} +{2 Library goblint.tracing} +{!modules:Goblint_tracing} + {1 Vendored} The following libraries are vendored in Goblint. diff --git a/src/main.camldoc b/src/main.camldoc index ec08a14a7b..0a0e52035f 100644 --- a/src/main.camldoc +++ b/src/main.camldoc @@ -85,7 +85,6 @@ FlagModeDomain LockDomain StackDomain FileDomain -SpecDomain LvalMapDomain } @@ -106,7 +105,6 @@ Glob {!modules: MCP Base -Spec CondVars Contain diff --git a/src/maingoblint.ml b/src/maingoblint.ml index 1512e63b47..2c7d353594 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -53,7 +53,7 @@ let rec option_spec_list: Arg_complete.speclist Lazy.t = lazy ( let add_string l = let f str = l := str :: !l in Arg_complete.String (f, Arg_complete.empty) in let add_int l = let f str = l := str :: !l in Arg_complete.Int (f, Arg_complete.empty) in let set_trace sys = - if Messages.tracing then Tracing.addsystem sys + if Messages.tracing then Goblint_tracing.addsystem sys else (prerr_endline "Goblint has been compiled without tracing, recompile in trace profile (./scripts/trace_on.sh)"; raise Stdlib.Exit) in let configure_html () = @@ -112,8 +112,8 @@ let rec option_spec_list: Arg_complete.speclist Lazy.t = lazy ( ; "--print_options" , Arg_complete.Unit (fun () -> Options.print_options (); exit 0), "" ; "--print_all_options" , Arg_complete.Unit (fun () -> Options.print_all_options (); exit 0), "" ; "--trace" , Arg_complete.String (set_trace, Arg_complete.empty), "" - ; "--tracevars" , add_string Tracing.tracevars, "" - ; "--tracelocs" , add_int Tracing.tracelocs, "" + ; "--tracevars" , add_string Goblint_tracing.tracevars, "" + ; "--tracelocs" , add_int Goblint_tracing.tracelocs, "" ; "--help" , Arg_complete.Unit (fun _ -> print_help stdout),"" ; "--html" , Arg_complete.Unit (fun _ -> configure_html ()),"" ; "--sarif" , Arg_complete.Unit (fun _ -> configure_sarif ()),"" @@ -161,6 +161,12 @@ let check_arguments () = ); if get_bool "solvers.td3.space" && get_bool "solvers.td3.remove-wpoint" then fail "solvers.td3.space is incompatible with solvers.td3.remove-wpoint"; if get_bool "solvers.td3.space" && get_string "solvers.td3.side_widen" = "sides-local" then fail "solvers.td3.space is incompatible with solvers.td3.side_widen = 'sides-local'"; + if List.mem "termination" @@ get_string_list "ana.activated" then ( + if GobConfig.get_bool "incremental.load" || GobConfig.get_bool "incremental.save" then fail "termination analysis is not compatible with incremental analysis"; + set_list "ana.activated" (GobConfig.get_list "ana.activated" @ [`String ("threadflag")]); + set_string "sem.int.signed_overflow" "assume_none"; + warn "termination analysis implicitly activates threadflag analysis and set sem.int.signed_overflow to assume_none"; + ); if not (get_bool "ana.sv-comp.enabled") && get_bool "witness.graphml.enabled" then fail "witness.graphml.enabled: cannot generate GraphML witness without SV-COMP mode (ana.sv-comp.enabled)" (** Initialize some globals in other modules. *) @@ -185,10 +191,10 @@ let handle_flags () = let handle_options () = check_arguments (); - AfterConfig.run (); Sys.set_signal (GobSys.signal_of_string (get_string "dbg.solver-signal")) Signal_ignore; (* Ignore solver-signal before solving (e.g. MyCFG), otherwise exceptions self-signal the default, which crashes instead of printing backtrace. *) if AutoTune.isActivated "memsafetySpecification" && get_string "ana.specification" <> "" then AutoTune.focusOnMemSafetySpecification (); + AfterConfig.run (); Cilfacade.init_options (); handle_flags () @@ -256,6 +262,15 @@ let preprocess_files () = (* Preprocessor flags *) let cppflags = ref (get_string_list "pre.cppflags") in + if get_bool "ana.sv-comp.enabled" then ( + let architecture_flag = match get_string "exp.architecture" with + | "32bit" -> "-m32" + | "64bit" -> "-m64" + | _ -> assert false + in + cppflags := architecture_flag :: !cppflags + ); + (* the base include directory *) (* TODO: any better way? dune executable promotion doesn't add _build sites *) let source_lib_dirs = @@ -487,7 +502,7 @@ let merge_parsed parsed = Cilfacade.current_file := merged_AST; (* Set before createCFG, so Cilfacade maps can be computed for loop unrolling. *) CilCfg.createCFG merged_AST; (* Create CIL CFG from CIL AST. *) - Cilfacade.reset_lazy (); (* Reset Cilfacade maps, which need to be recomputer after loop unrolling. *) + Cilfacade.reset_lazy ~keepupjumpinggotos:true (); (* Reset Cilfacade maps, which need to be recomputer after loop unrolling but keep gotos. *) merged_AST let preprocess_parse_merge () = diff --git a/src/mainspec.ml b/src/mainspec.ml deleted file mode 100644 index 4509645f98..0000000000 --- a/src/mainspec.ml +++ /dev/null @@ -1,13 +0,0 @@ -open Goblint_lib -open Batteries (* otherwise open_in would return wrong type for SpecUtil *) -open SpecUtil - -let _ = - (* no arguments -> run interactively (= reading from stdin) *) - let args = Array.length Sys.argv > 1 in - if args && Sys.argv.(1) = "-" then - ignore(parse ~dot:true stdin) - else - let cin = if args then open_in Sys.argv.(1) else stdin in - ignore(parse ~repl:(not args) ~print:true cin) -(* exit 0 *) diff --git a/src/solvers/postSolver.ml b/src/solvers/postSolver.ml index f96ca832a1..e01560c752 100644 --- a/src/solvers/postSolver.ml +++ b/src/solvers/postSolver.ml @@ -154,13 +154,7 @@ struct module VH = Hashtbl.Make (S.Var) (* starts as Hashtbl for quick lookup *) - let starth = - (* VH.of_list S.starts *) (* TODO: BatHashtbl.Make.of_list is broken, use after new Batteries release *) - let starth = VH.create (List.length S.starts) in - List.iter (fun (x, d) -> - VH.replace starth x d - ) S.starts; - starth + let starth = VH.of_list S.starts let system x = match S.system x, VH.find_option starth x with diff --git a/src/spec/dune b/src/spec/dune deleted file mode 100644 index 47c22a0d46..0000000000 --- a/src/spec/dune +++ /dev/null @@ -1,2 +0,0 @@ -(ocamllex specLexer) -(ocamlyacc specParser) diff --git a/src/spec/file.dot b/src/spec/file.dot deleted file mode 100644 index a78c64d3fc..0000000000 --- a/src/spec/file.dot +++ /dev/null @@ -1,37 +0,0 @@ -digraph file { - // changed file pointer {fp} (no longer safe) - - // file handle is not saved! - // overwriting still opened file handle - // file is never closed - // file may be never closed - // closeing unopened file handle - // closeing already closed file handle - // writing to closed file handle - // writing to unopened file handle - // writing to read-only file handle - - // unclosed files: ... - // maybe unclosed files: ... - - w1 [label="file handle is not saved!"]; - w2 [label="closeing unopened file handle"]; - w3 [label="writing to unopened file handle"]; - w4 [label="writing to read-only file handle"]; - w5 [label="closeing already closed file handle"]; - w6 [label="writing to closed file handle"]; - - 1 -> w1 [label="fopen(_)"]; - 1 -> w2 [label="fclose($fp)"]; - 1 -> w3 [label="fprintf($fp, _)"]; - 1 -> open_read [label="$fp = fopen($path, \"r\")"]; - 1 -> open_write [label="$fp = fopen($path, \"w\")"]; - 1 -> open_write [label="$fp = fopen($path, \"a\")"]; - open_read -> w4 [label="fprintf($fp, _)"]; - open_write -> open_write [label="fprintf($fp, _)"]; - open_read -> closed [label="fclose($fp)"]; - open_write -> closed [label="fclose($fp)"]; - closed -> w5 [label="fclose($fp)"]; - closed -> w6 [label="fprintf($fp, _)"]; - closed -> 1 [label="->"]; -} \ No newline at end of file diff --git a/src/spec/render.sh b/src/spec/render.sh deleted file mode 100755 index 91e486c247..0000000000 --- a/src/spec/render.sh +++ /dev/null @@ -1,31 +0,0 @@ -# command -v ls >&- || {echo >&2 bla; exit 1;} -function check(){ - set -e # needed to exit script from function - hash $1 2>&- || (echo >&2 "$1 is needed but not installed! $2"; exit 1;) - set +e # do not exit shell if some command fails (default) -} -check dot -mode=${1-"png"} -file=${2-"file"} -dst=graph -viewcmd=gpicview - -mkdir -p ${dst} -cp ${file}.dot ${dst} -file=${file##*/} # use basename in case the file was somewhere else -cd ${dst} -trap 'cd ..' EXIT # leave dst again on exit -case "$mode" in - png) dot -Tpng -o${file}.png ${file}.dot; - check ${viewcmd} "Please edit viewcmd accordingly." - pkill ${viewcmd}; - ${viewcmd} ${file}.png & - ;; - pdf) rm -f ${file}.tex; - check dot2tex - dot -Txdot ${file}.dot | dot2tex > ${file}.tex; - check pdflatex - pdflatex ${file}.tex - echo "generated $dst/$file.pdf" - ;; -esac diff --git a/src/spec/specCore.ml b/src/spec/specCore.ml deleted file mode 100644 index 9d0ce35624..0000000000 --- a/src/spec/specCore.ml +++ /dev/null @@ -1,152 +0,0 @@ -(* types used by specParser and functions for handling the constructed types *) - -open Batteries - -exception Endl -exception Eof - -(* type value = String of string | Bool of bool | Int of int | Float of float *) -type lval = Ptr of lval | Var of string | Ident of string -type fcall = {fname: string; args: exp list} -and exp = - Fun of fcall | - Exp_ | - Lval of lval | - Regex of string | - String of string | Bool of bool | Int of int | Float of float | - Binop of string * exp * exp | - Unop of string * exp -type stmt = {lval: lval option; exp: exp} -type def = Node of (string * string) (* node warning *) - | Edge of (string * string list * bool * string * stmt) (* start-node, warning-nodes, forwarding, target-node, constraint *) - -(* let stmts edges = List.map (fun (a,b,c) -> c) edges - let get_fun stmt = match stmt.exp with Fun x -> Some x | _ -> None - let fun_records edges = List.filter_map get_fun (stmts edges) - let fun_names edges = fun_records edges |> List.map (fun x -> x.fname) - let fun_by_fname fname edges = List.filter (fun x -> x.fname=fname) (fun_records edges) *) -let fname_is fname stmt = - match stmt.exp with - | Fun x -> x.fname=fname - | _ -> false - -let is_wildcard stmt = stmt.exp = Exp_ - -let branch_exp stmt = - match stmt.exp with - | Fun { fname="branch"; args=[exp; Bool tv] } -> Some (exp,tv) - | _ -> None - -let is_branch stmt = branch_exp stmt <> None - -let startnode edges = - (* The start node of the first transition is the start node of the automaton. *) - let a,ws,fwd,b,c = List.hd edges in a - -let warning state nodes = - try - Some (snd (List.find (fun x -> fst x = state) nodes)) (* find node for state and return its warning *) - with - | Not_found -> None (* no node for state *) - -let get_lval stmt = - let f = function - | Ptr x -> `Ptr (* TODO recursive *) - | Var s -> `Var - | Ident s -> `Ident - in - Option.map f stmt.lval - -let get_exp = function - | Regex x -> `Regex x - | String x -> `String x - | Bool x -> `Bool x - | Int x -> `Int x - | Float x -> `Float x - | Lval (Var x) -> `Var x - | Lval (Ident x) -> `Ident x - | Fun x -> `Error "Functions aren't allowed to have functions as an argument (put the function as a previous state instead)" - | Exp_ -> `Free - | Unop ("!", Bool x) -> `Bool (not x) - | _ -> `Error "Unsupported operation inside function argument, use a simpler expression instead." - -let get_rval stmt = get_exp stmt.exp - -let get_key_variant stmt = - let rec get_from_exp = function - | Fun f -> get_from_args f.args (* TODO for special we only consider constraints where the root of the exp is Fun (see fname_is) *) - | Lval (Var s) -> `Rval s - | _ -> `None - (* walks over arguments until it finds something or returns `None *) - and get_from_argsi i = function - | [] -> `None - | x::xs -> - match get_from_exp x with - | `Rval s -> `Arg(s, i) - | _ -> get_from_argsi (i+1) xs (* matches `None and `Arg -> `Arg of `Arg not supported *) - and get_from_args args = get_from_argsi 0 args (* maybe better use List.findi *) - in - let rec get_from_lval = function - | Ptr x -> get_from_lval x - | Var s -> Some s - | Ident s -> None - in - match stmt.lval with - | Some lval when Option.is_some (get_from_lval lval) -> `Lval (Option.get (get_from_lval lval)) - | _ -> get_from_exp stmt.exp - -let equal_form lval stmt = - match lval, stmt.lval with - | Some _, Some _ - | None, None -> true - | _ -> false - -(* get function arguments with tags corresponding to the type -> should only be called for functions, returns [] for everything else *) -let get_fun_args stmt = match stmt.exp with - | Fun f -> List.map get_exp f.args - | _ -> [] - -(* functions for output *) -let rec lval_to_string = function - | Ptr x -> "*"^(lval_to_string x) - | Var x -> "$"^x - | Ident x -> x -let rec exp_to_string = function - | Fun x -> x.fname^"("^String.concat ", " (List.map exp_to_string x.args)^")" - | Exp_ -> "_" - | Lval x -> lval_to_string x - | Regex x -> "r\""^x^"\"" - | String x -> "\""^x^"\"" - | Bool x -> string_of_bool x - | Int x -> string_of_int x - | Float x -> string_of_float x - | Binop (op, a, b) -> exp_to_string a ^ " " ^ op ^ " " ^ exp_to_string b - | Unop (op, a) -> op ^ " " ^ exp_to_string a -let stmt_to_string stmt = match stmt.lval, stmt.exp with - | Some lval, exp -> lval_to_string lval^" = "^exp_to_string exp - | None, exp -> exp_to_string exp -let arrow_to_string ws fwd = (String.concat "," ws)^if fwd then ">" else "" -let def_to_string = function - | Node(n, m) -> n^"\t\""^m^"\"" - | Edge(a, ws, fwd, b, s) -> a^" -"^arrow_to_string ws fwd^"> "^b^"\t"^stmt_to_string s - -let to_dot_graph defs = - let no_warnings = true in - let def_to_string = function - | Node(n, m) -> - if no_warnings then "" - else n^"\t[style=filled, fillcolor=orange, label=\""^n^": "^m^"\"];" - | Edge(a, ws, fwd, b, s) -> - let style = if fwd then "style=dotted, " else "" in - let ws = if List.is_empty ws then "" else (String.concat "," ws)^" | " in - a^" -> "^b^"\t["^style^"label=\""^ws^String.escaped (stmt_to_string s)^"\"];" - in - let ends,defs = List.partition (function Edge (a,ws,fwd,b,s) -> b="end" && s.exp=Exp_ | _ -> false) defs in - let endstates = List.filter_map (function Edge (a,ws,fwd,b,s) -> Some a | _ -> None) ends in - (* set the default style for nodes *) - let defaultstyle = "node [shape=box, style=rounded];" in - (* style end nodes and then reset *) - let endstyle = if List.is_empty endstates then "" else "node [peripheries=2]; "^(String.concat " " endstates)^"; node [peripheries=1];" in - let lines = "digraph file {"::defaultstyle::endstyle::(List.map def_to_string defs |> List.filter (fun s -> s<>"")) in - (* List.iter print_endline lines *) - String.concat "\n " lines ^ "\n}" diff --git a/src/spec/specLexer.mll b/src/spec/specLexer.mll deleted file mode 100644 index 64ac69359e..0000000000 --- a/src/spec/specLexer.mll +++ /dev/null @@ -1,67 +0,0 @@ -{ - open SpecParser (* The type token is defined in specParser.mli *) - exception Token of string - let line = ref 1 -} - -let digit = ['0'-'9'] -let alpha = ['a'-'z' 'A'-'Z'] -let nl = '\r'?'\n' (* new line *) -let s = [' ' '\t'] (* whitespace *) -let w = '_' | alpha | digit (* word *) -let endlinecomment = "//" [^'\n']* -let multlinecomment = "/*"([^'*']|('*'+[^'*''/'])|nl)*'*'+'/' -let comments = endlinecomment | multlinecomment -let str = ('\"'(([^'\"']|"\\\"")* as s)'\"') | ('\''(([^'\'']|"\\'")* as s)'\'') - -rule token = parse - | s { token lexbuf } (* skip blanks *) - | comments { token lexbuf } (* skip comments *) - | nl { incr line; EOL } - - (* operators *) - | '(' { LPAREN } - | ')' { RPAREN } - | '[' { LBRACK } - | ']' { RBRACK } - | '{' { LCURL } - | '}' { RCURL } - (*| '.' { DOT } *) - (*| "->" { ARROW } *) - | '+' { PLUS } - | '-' { MINUS } - | '*' { MUL } - | '/' { DIV } - | '%' { MOD } - | '<' { LT } - | '>' { GT } - | "==" { EQEQ } - | "!=" { NE } - | "<=" { LE } - | ">=" { GE } - | "&&" { AND } - | "||" { OR } - | '!' { NOT } - | '=' { EQ } - | ',' { COMMA } - | ';' { SEMICOLON } - - (* literals, identifiers *) - | "true" { BOOL(true) } - | "false" { BOOL(false) } - | "null" { NULL } - | digit+ as x { INT(int_of_string x) } - | str { STRING(s) } - | '_' { UNDERS } (* used for spec, but has to be before Ident! *) - | ('_'|alpha) w* as x { IDENT(x) } - - (* spec *) - | ':' { COLON } - | "$"(w+ as x) { VAR(x) } - | "r" str { REGEX(s) } - | (w+ as n) s+ str - { NODE(n, s) } - | (w+ as a) s* "-" ((w+ ("," w+)*)? as ws) (">"? as fwd) ">" s* (w+ as b) s+ - { EDGE(a, BatString.split_on_string ~by:"," ws, fwd=">", b) } - | eof { EOF } - | _ as x { raise(Token (Char.escaped x^": unknown token in line "^string_of_int !line)) } diff --git a/src/spec/specParser.mly b/src/spec/specParser.mly deleted file mode 100644 index fe8fe90ec8..0000000000 --- a/src/spec/specParser.mly +++ /dev/null @@ -1,116 +0,0 @@ -%{ - (* necessary to open a different compilation unit - because exceptions directly defined here aren't visible outside - (e.g. SpecParser.Eof is raised, but Error: Unbound constructor - if used to catch in a different module) *) - open SpecCore -%} - -%token EOL EOF -/* operators */ -%token LPAREN RPAREN LCURL RCURL LBRACK RBRACK -%token PLUS MINUS MUL DIV MOD -%token LT GT EQEQ NE LE GE AND OR NOT -%token EQ COMMA SEMICOLON -/* literals, identifiers */ -%token BOOL -%token NULL -%token INT -%token STRING -%token IDENT -/* spec */ -%token UNDERS COLON -%token VAR -%token REGEX -%token NODE -%token EDGE - -/* precedence groups from low to high */ -%right EQ -%left OR -%left AND -%left EQEQ NE -%left LT GT LE GE -%left PLUS MINUS -%left MUL DIV MOD -%right NOT UPLUS UMINUS DEREF - -%start file -%type file - -%% - -file: - | def EOL { $1 } - | def EOF { $1 } /* no need for an empty line at the end */ - | EOL { raise Endl } /* empty line */ - | EOF { raise Eof } /* end of file */ -; - -def: - | NODE { Node($1) } - | EDGE stmt { let a, ws, fwd, b = $1 in Edge(a, ws, fwd, b, $2) } -; - -stmt: - | lval EQ expr { {lval = Some $1; exp = $3} } /* TODO expression would be better */ - | expr { {lval = None; exp = $1} } -; - -lval: - | MUL lval %prec DEREF { Ptr $2 } - | IDENT { Ident $1 } /* C identifier, e.g. foo, _foo, _1, but not 1b */ - | VAR { Var $1 } /* spec variable, e.g. $foo, $123, $__ */ -; - -expr: - | LPAREN expr RPAREN { $2 } - | REGEX { Regex $1 } - | STRING { String $1 } - | BOOL { Bool $1 } - | lval { Lval $1 } - | IDENT args { Fun {fname=$1; args=$2} } /* function */ - | UNDERS { Exp_ } - | nexpr { Int $1 } - /* | nexpr LT nexpr { Bool ($1<$3) } - | nexpr GT nexpr { Bool ($1>$3) } - | nexpr EQEQ nexpr { Bool ($1=$3) } - | nexpr NE nexpr { Bool ($1<>$3) } - | nexpr LE nexpr { Bool ($1<=$3) } - | nexpr GE nexpr { Bool ($1>=$3) } */ - | expr OR expr { Binop ("||", $1, $3) } - | expr AND expr { Binop ("&&", $1, $3) } - | expr EQEQ expr { Binop ("==", $1, $3) } - | expr NE expr { Binop ("!=", $1, $3) } - | expr LT expr { Binop ("<", $1, $3) } - | expr GT expr { Binop (">", $1, $3) } - | expr LE expr { Binop ("<=", $1, $3) } - | expr GE expr { Binop (">=", $1, $3) } - | expr PLUS expr { Binop ("+", $1, $3) } - | expr MINUS expr { Binop ("-", $1, $3) } - | expr MUL expr { Binop ("*", $1, $3) } - | expr DIV expr { Binop ("/", $1, $3) } - | expr MOD expr { Binop ("%", $1, $3) } - | NOT expr { Unop ("!", $2) } -; - -nexpr: - | INT { $1 } - | MINUS nexpr %prec UMINUS { - $2 } - | PLUS nexpr %prec UPLUS { $2 } - /* | LPAREN nexpr RPAREN { $2 } - | nexpr PLUS nexpr { $1 + $3 } - | nexpr MINUS nexpr { $1 - $3 } - | nexpr MUL nexpr { $1 * $3 } - | nexpr DIV nexpr { $1 / $3 } */ -; - -args: - | LPAREN RPAREN { [] } - | LPAREN expr_list RPAREN { $2 } -; - -expr_list: - | expr { [$1] } - | expr COMMA expr_list { $1 :: $3 } -; diff --git a/src/spec/specUtil.ml b/src/spec/specUtil.ml deleted file mode 100644 index 55e0b51135..0000000000 --- a/src/spec/specUtil.ml +++ /dev/null @@ -1,52 +0,0 @@ -(* functions for driving specParser *) - -open Batteries - -(* config *) -let save_dot = true - -let line = ref 1 -exception Parse_error of string - -let parse ?repl:(repl=false) ?print:(print=false) ?dot:(dot=false) cin = - let lexbuf = Lexing.from_channel cin in - let defs = ref [] in - (* Printf.printf "\nrepl: %B, print: %B, dot: %B, save_dot: %B\n" repl print dot save_dot; *) - try - while true do (* loop over all lines *) - try - let result = SpecParser.file SpecLexer.token lexbuf in - defs := !defs@[result]; - incr line; - if print then (print_endline (SpecCore.def_to_string result); flush stdout) - with - (* just an empty line -> don't print *) - | SpecCore.Endl -> incr line - (* somehow gets raised in some cases instead of SpecCore.Eof *) - | BatInnerIO.Input_closed -> raise SpecCore.Eof - (* catch and print in repl-mode *) - | e when repl -> print_endline (Printexc.to_string e) - done; - ([], []) (* never happens, but ocaml needs it for type *) - with - (* done *) - | SpecCore.Eof -> - let nodes = List.filter_map (function SpecCore.Node x -> Some x | _ -> None) !defs in - let edges = List.filter_map (function SpecCore.Edge x -> Some x | _ -> None) !defs in - if print then Printf.printf "\n#Definitions: %i, #Nodes: %i, #Edges: %i\n" - (List.length !defs) (List.length nodes) (List.length edges); - if save_dot && not dot then ( - let dotgraph = SpecCore.to_dot_graph !defs in - output_file ~filename:"result/graph.dot" ~text:dotgraph; - print_endline ("saved graph as "^Sys.getcwd ()^"/result/graph.dot"); - ); - if dot then ( - print_endline (SpecCore.to_dot_graph !defs) - ); - (nodes, edges) - (* stop on parsing error if not in REPL and include line number *) - | e -> raise (Parse_error ("Line "^string_of_int !line^": "^Printexc.to_string e)) - -let parseFile filename = parse (open_in filename) - -(* print ~first:"[" ~sep:", " ~last:"]" print_any stdout @@ 5--10 *) diff --git a/src/util/cilCfg.ml b/src/util/cilCfg.ml index 2c8ec646c3..923cf7600b 100644 --- a/src/util/cilCfg.ml +++ b/src/util/cilCfg.ml @@ -42,6 +42,7 @@ let loopCount file = let createCFG (fileAST: file) = + Cilfacade.do_preprocess fileAST; (* The analyzer keeps values only for blocks. So if you want a value for every program point, each instruction *) (* needs to be in its own block. end_basic_blocks does that. *) (* After adding support for VLAs, there are new VarDecl instructions at the point where a variable was declared and *) @@ -49,6 +50,7 @@ let createCFG (fileAST: file) = (* BB causes the output CIL file to no longer compile. *) (* Since we want the output of justcil to compile, we do not run allBB visitor if justcil is enable, regardless of *) (* exp.basic-blocks. This does not matter, as we will not run any analysis anyway, when justcil is enabled. *) + (* the preprocessing must be done here, to add the changes of CIL to the CFG*) if not (get_bool "exp.basic-blocks") && not (get_bool "justcil") then end_basic_blocks fileAST; (* We used to renumber vids but CIL already generates them fresh, so no need. @@ -66,6 +68,4 @@ let createCFG (fileAST: file) = computeCFGInfo fd true | _ -> () ); - if get_bool "dbg.run_cil_check" then assert (Check.checkFile [] fileAST); - - Cilfacade.do_preprocess fileAST + if get_bool "dbg.run_cil_check" then assert (Check.checkFile [] fileAST); \ No newline at end of file diff --git a/src/domains/accessKind.ml b/src/util/library/accessKind.ml similarity index 100% rename from src/domains/accessKind.ml rename to src/util/library/accessKind.ml diff --git a/src/util/library/dune b/src/util/library/dune new file mode 100644 index 0000000000..075c01c35d --- /dev/null +++ b/src/util/library/dune @@ -0,0 +1,18 @@ +(include_subdirs no) + +(library + (name goblint_library) + (public_name goblint.library) + (wrapped false) ; TODO: wrap + (libraries + batteries.unthreaded + goblint_common + goblint_domain + goblint_config + goblint-cil) + (preprocess + (pps + ppx_deriving.std + ppx_deriving_hash))) + +(documentation) diff --git a/src/util/library/library.mld b/src/util/library/library.mld new file mode 100644 index 0000000000..f55db3f2ff --- /dev/null +++ b/src/util/library/library.mld @@ -0,0 +1,14 @@ +{0 Library goblint.library} +This library is unwrapped and provides the following top-level modules. +For better context, see {!Goblint_lib} which also documents these modules. + + +{1 Utilities} + +{2 Library specification} +{!modules: +AccessKind +LibraryDesc +LibraryDsl +LibraryFunctions +} diff --git a/src/analyses/libraryDesc.ml b/src/util/library/libraryDesc.ml similarity index 95% rename from src/analyses/libraryDesc.ml rename to src/util/library/libraryDesc.ml index 72a4261cb5..4997b306a9 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/util/library/libraryDesc.ml @@ -27,6 +27,7 @@ type math = | Islessequal of (Basetype.CilExp.t * Basetype.CilExp.t) | Islessgreater of (Basetype.CilExp.t * Basetype.CilExp.t) | Isunordered of (Basetype.CilExp.t * Basetype.CilExp.t) + | Abs of (CilType.Ikind.t * Basetype.CilExp.t) | Ceil of (CilType.Fkind.t * Basetype.CilExp.t) | Floor of (CilType.Fkind.t * Basetype.CilExp.t) | Fabs of (CilType.Fkind.t * Basetype.CilExp.t) @@ -38,7 +39,8 @@ type math = | Atan2 of (CilType.Fkind.t * Basetype.CilExp.t * Basetype.CilExp.t) | Cos of (CilType.Fkind.t * Basetype.CilExp.t) | Sin of (CilType.Fkind.t * Basetype.CilExp.t) - | Tan of (CilType.Fkind.t * Basetype.CilExp.t) [@@deriving eq, ord, hash] + | Tan of (CilType.Fkind.t * Basetype.CilExp.t) + | Sqrt of (CilType.Fkind.t * Basetype.CilExp.t) [@@deriving eq, ord, hash] (** Type of special function, or {!Unknown}. *) (* Use inline record if not single {!Cil.exp} argument. *) @@ -78,6 +80,7 @@ type special = | Identity of Cil.exp (** Identity function. Some compiler optimization annotation functions map to this. *) | Setjmp of { env: Cil.exp; } | Longjmp of { env: Cil.exp; value: Cil.exp; } + | Bounded of { exp: Cil.exp} (** Used to check for bounds for termination analysis. *) | Rand | Unknown (** Anything not belonging to other types. *) (* TODO: rename to Other? *) @@ -158,6 +161,7 @@ module MathPrintable = struct | Islessequal (exp1, exp2) -> Pretty.dprintf "isLessEqual(%a, %a)" d_exp exp1 d_exp exp2 | Islessgreater (exp1, exp2) -> Pretty.dprintf "isLessGreater(%a, %a)" d_exp exp1 d_exp exp2 | Isunordered (exp1, exp2) -> Pretty.dprintf "isUnordered(%a, %a)" d_exp exp1 d_exp exp2 + | Abs (ik, exp) -> Pretty.dprintf "(%a )abs(%a)" d_ikind ik d_exp exp | Ceil (fk, exp) -> Pretty.dprintf "(%a )ceil(%a)" d_fkind fk d_exp exp | Floor (fk, exp) -> Pretty.dprintf "(%a )floor(%a)" d_fkind fk d_exp exp | Fabs (fk, exp) -> Pretty.dprintf "(%a )fabs(%a)" d_fkind fk d_exp exp @@ -170,6 +174,7 @@ module MathPrintable = struct | Cos (fk, exp) -> Pretty.dprintf "(%a )cos(%a)" d_fkind fk d_exp exp | Sin (fk, exp) -> Pretty.dprintf "(%a )sin(%a)" d_fkind fk d_exp exp | Tan (fk, exp) -> Pretty.dprintf "(%a )tan(%a)" d_fkind fk d_exp exp + | Sqrt (fk, exp) -> Pretty.dprintf "(%a )sqrt(%a)" d_fkind fk d_exp exp include Printable.SimplePretty ( struct diff --git a/src/analyses/libraryDsl.ml b/src/util/library/libraryDsl.ml similarity index 100% rename from src/analyses/libraryDsl.ml rename to src/util/library/libraryDsl.ml diff --git a/src/analyses/libraryDsl.mli b/src/util/library/libraryDsl.mli similarity index 100% rename from src/analyses/libraryDsl.mli rename to src/util/library/libraryDsl.mli diff --git a/src/analyses/libraryFunctions.ml b/src/util/library/libraryFunctions.ml similarity index 93% rename from src/analyses/libraryFunctions.ml rename to src/util/library/libraryFunctions.ml index f84fe1d4e3..2c65f7ae61 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/util/library/libraryFunctions.ml @@ -32,6 +32,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_strncat", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("__builtin___strncat_chk", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []; drop "os" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("memcmp", unknown [drop "s1" [r]; drop "s2" [r]; drop "n" []]); + ("__builtin_memcmp", unknown [drop "s1" [r]; drop "s2" [r]; drop "n" []]); ("memchr", unknown [drop "s" [r]; drop "c" []; drop "n" []]); ("asctime", unknown ~attrs:[ThreadUnsafe] [drop "time_ptr" [r_deep]]); ("fclose", unknown [drop "stream" [r_deep; w_deep; f_deep]]); @@ -62,6 +63,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("localeconv", unknown ~attrs:[ThreadUnsafe] []); ("localtime", unknown ~attrs:[ThreadUnsafe] [drop "time" [r]]); ("strlen", special [__ "s" [r]] @@ fun s -> Strlen s); + ("__builtin_strlen", special [__ "s" [r]] @@ fun s -> Strlen s); ("strstr", special [__ "haystack" [r]; __ "needle" [r]] @@ fun haystack needle -> Strstr { haystack; needle; }); ("strcmp", special [__ "s1" [r]; __ "s2" [r]] @@ fun s1 s2 -> Strcmp { s1; s2; n = None; }); ("strtok", unknown ~attrs:[ThreadUnsafe] [drop "str" [r; w]; drop "delim" [r]]); @@ -87,6 +89,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("iswspace", unknown [drop "wc" []]); ("iswalnum", unknown [drop "wc" []]); ("iswprint", unknown [drop "wc" []]); + ("iswxdigit", unknown [drop "ch" []]); ("rename" , unknown [drop "oldpath" [r]; drop "newpath" [r];]); ("perror", unknown [drop "s" [r]]); ("getchar", unknown []); @@ -116,8 +119,10 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("vsnprintf", unknown [drop "str" [w]; drop "size" []; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("mktime", unknown [drop "tm" [r;w]]); ("ctime", unknown ~attrs:[ThreadUnsafe] [drop "rm" [r]]); - ("clearerr", unknown [drop "stream" [w]]); + ("clearerr", unknown [drop "stream" [w]]); (* TODO: why only w? *) ("setbuf", unknown [drop "stream" [w]; drop "buf" [w]]); + ("wprintf", unknown (drop "fmt" [r] :: VarArgs (drop' [r]))); + ("fwprintf", unknown (drop "stream" [r_deep; w_deep] :: drop "fmt" [r] :: VarArgs (drop' [r]))); ("swprintf", unknown (drop "wcs" [w] :: drop "maxlen" [] :: drop "fmt" [r] :: VarArgs (drop' [r]))); ("assert", special [__ "exp" []] @@ fun exp -> Assert { exp; check = true; refine = get_bool "sem.assert.refine" }); (* only used if assert is used without include, e.g. in transformed files *) ("difftime", unknown [drop "time1" []; drop "time2" []]); @@ -128,7 +133,10 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("wcstombs", unknown ~attrs:[ThreadUnsafe] [drop "dst" [w]; drop "src" [r]; drop "size" []]); ("wcsrtombs", unknown ~attrs:[ThreadUnsafe] [drop "dst" [w]; drop "src" [r_deep; w]; drop "size" []; drop "ps" [r_deep; w_deep]]); ("mbstowcs", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []]); - ("abs", unknown [drop "j" []]); + ("abs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs (IInt, j)) }); + ("labs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs (ILong, j)) }); + ("llabs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs (ILongLong, j)) }); + ("imaxabs", unknown [drop "j" []]); ("localtime_r", unknown [drop "timep" [r]; drop "result" [w]]); ("strpbrk", unknown [drop "s" [r]; drop "accept" [r]]); ("_setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); (* only has one underscore *) @@ -146,6 +154,11 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("atomic_flag_test_and_set_explicit", unknown [drop "obj" [r; w]; drop "order" []]); ("atomic_load", unknown [drop "obj" [r]]); ("atomic_store", unknown [drop "obj" [w]; drop "desired" []]); + ("_Exit", special [drop "status" []] @@ Abort); + ("strcoll", unknown [drop "lhs" [r]; drop "rhs" [r]]); + ("wscanf", unknown (drop "fmt" [r] :: VarArgs (drop' [w]))); + ("fwscanf", unknown (drop "stream" [r_deep; w_deep] :: drop "fmt" [r] :: VarArgs (drop' [w]))); + ("swscanf", unknown (drop "buffer" [r] :: drop "fmt" [r] :: VarArgs (drop' [w]))); ] (** C POSIX library functions. @@ -295,6 +308,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("sendto", unknown [drop "sockfd" []; drop "buf" [r]; drop "len" []; drop "flags" []; drop "dest_addr" [r_deep]; drop "addrlen" []]); ("strdup", unknown [drop "s" [r]]); ("strndup", unknown [drop "s" [r]; drop "n" []]); + ("__strndup", unknown [drop "s" [r]; drop "n" []]); ("syscall", unknown (drop "number" [] :: VarArgs (drop' [r; w]))); ("sysconf", unknown [drop "name" []]); ("syslog", unknown (drop "priority" [] :: drop "format" [r] :: VarArgs (drop' [r]))); (* TODO: is the VarArgs correct here? *) @@ -335,7 +349,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("regexec", unknown [drop "preg" [r_deep]; drop "string" [r]; drop "nmatch" []; drop "pmatch" [w_deep]; drop "eflags" []]); ("regfree", unknown [drop "preg" [f_deep]]); ("ffs", unknown [drop "i" []]); - ("_exit", special [drop "status" []] Abort); + ("_exit", special [drop "status" []] @@ Abort); ("execvp", unknown [drop "file" [r]; drop "argv" [r_deep]]); ("execl", unknown (drop "path" [r] :: drop "arg" [r] :: VarArgs (drop' [r]))); ("statvfs", unknown [drop "path" [r]; drop "buf" [w]]); @@ -403,6 +417,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("srandom", unknown [drop "seed" []]); ("random", special [] Rand); ("posix_memalign", unknown [drop "memptr" [w]; drop "alignment" []; drop "size" []]); (* TODO: Malloc *) + ("stpcpy", unknown [drop "dest" [w]; drop "src" [r]]); ] (** Pthread functions. *) @@ -505,6 +520,7 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_unreachable", special' [] @@ fun () -> if get_bool "sem.builtin_unreachable.dead_code" then Abort else Unknown); (* https://github.com/sosy-lab/sv-benchmarks/issues/1296 *) ("__assert_rtn", special [drop "func" [r]; drop "file" [r]; drop "line" []; drop "exp" [r]] @@ Abort); (* MacOS's built-in assert *) ("__assert_fail", special [drop "assertion" [r]; drop "file" [r]; drop "line" []; drop "function" [r]] @@ Abort); (* gcc's built-in assert *) + ("__assert", special [drop "assertion" [r]; drop "file" [r]; drop "line" []] @@ Abort); (* header says: The following is not at all used here but needed for standard compliance. *) ("__builtin_return_address", unknown [drop "level" []]); ("__builtin___sprintf_chk", unknown (drop "s" [w] :: drop "flag" [] :: drop "os" [] :: drop "fmt" [r] :: VarArgs (drop' [r]))); ("__builtin_add_overflow", unknown [drop "a" []; drop "b" []; drop "c" [w]]); @@ -565,6 +581,10 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fputs_unlocked", unknown [drop "s" [r]; drop "stream" [w]]); + ("feof_unlocked", unknown [drop "stream" [r_deep; w_deep]]); + ("ferror_unlocked", unknown [drop "stream" [r_deep; w_deep]]); + ("fwrite_unlocked", unknown [drop "buffer" [r]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("clearerr_unlocked", unknown [drop "stream" [w]]); (* TODO: why only w? *) ("futimesat", unknown [drop "dirfd" []; drop "pathname" [r]; drop "times" [r]]); ("error", unknown ((drop "status" []) :: (drop "errnum" []) :: (drop "format" [r]) :: (VarArgs (drop' [r])))); ("warn", unknown (drop "format" [r] :: VarArgs (drop' [r]))); @@ -576,6 +596,11 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__fgets_chk", unknown [drop "__s" [w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); ("__fread_alias", unknown [drop "__ptr" [w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); ("__fread_chk", unknown [drop "__ptr" [w]; drop "__ptrlen" []; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fread_chk_warn", unknown [drop "buffer" [w]; drop "os" []; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("fread_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "buffer" [w]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("__fread_unlocked_alias", unknown ~attrs:[ThreadUnsafe] [drop "__ptr" [w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fread_unlocked_chk", unknown ~attrs:[ThreadUnsafe] [drop "__ptr" [w]; drop "__ptrlen" []; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fread_unlocked_chk_warn", unknown ~attrs:[ThreadUnsafe] [drop "__ptr" [w]; drop "__ptrlen" []; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); ("__read_chk", unknown [drop "__fd" []; drop "__buf" [w]; drop "__nbytes" []; drop "__buflen" []]); ("__read_alias", unknown [drop "__fd" []; drop "__buf" [w]; drop "__nbytes" []]); ("__readlink_chk", unknown [drop "path" [r]; drop "buf" [w]; drop "len" []; drop "buflen" []]); @@ -644,6 +669,8 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fstatfs", unknown [drop "fd" []; drop "buf" [w]]); ("cfmakeraw", unknown [drop "termios" [r; w]]); ("process_vm_readv", unknown [drop "pid" []; drop "local_iov" [w_deep]; drop "liovcnt" []; drop "remote_iov" []; drop "riovcnt" []; drop "flags" []]); + ("__libc_current_sigrtmax", unknown []); + ("__libc_current_sigrtmin", unknown []); ] let big_kernel_lock = AddrOf (Cil.var (Cilfacade.create_var (makeGlobalVar "[big kernel lock]" intType))) @@ -718,6 +745,7 @@ let goblint_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__goblint_assert", special [__ "exp" []] @@ fun exp -> Assert { exp; check = true; refine = get_bool "sem.assert.refine" }); ("__goblint_split_begin", unknown [drop "exp" []]); ("__goblint_split_end", unknown [drop "exp" []]); + ("__goblint_bounded", special [__ "exp"[]] @@ fun exp -> Bounded { exp }); ] (** zstd functions. @@ -936,9 +964,9 @@ let math_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("scalbln", unknown [drop "arg" []; drop "exp" []]); ("scalblnf", unknown [drop "arg" []; drop "exp" []]); ("scalblnl", unknown [drop "arg" []; drop "exp" []]); - ("sqrt", unknown [drop "x" []]); - ("sqrtf", unknown [drop "x" []]); - ("sqrtl", unknown [drop "x" []]); + ("sqrt", special [__ "x" []] @@ fun x -> Math { fun_args = (Sqrt (FDouble, x)) }); + ("sqrtf", special [__ "x" []] @@ fun x -> Math { fun_args = (Sqrt (FFloat, x)) }); + ("sqrtl", special [__ "x" []] @@ fun x -> Math { fun_args = (Sqrt (FLongDouble, x)) }); ("tgamma", unknown [drop "x" []]); ("tgammaf", unknown [drop "x" []]); ("tgammal", unknown [drop "x" []]); @@ -972,6 +1000,7 @@ let svcomp_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__VERIFIER_atomic_end", special [] @@ Unlock verifier_atomic); ("__VERIFIER_nondet_loff_t", unknown []); (* cannot give it in sv-comp.c without including stdlib or similar *) ("__VERIFIER_nondet_int", unknown []); (* declare invalidate actions to prevent invalidating globals when extern in regression tests *) + ("__VERIFIER_nondet_size_t", unknown []); (* cannot give it in sv-comp.c without including stdlib or similar *) ] let ncurses_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ @@ -1204,88 +1233,88 @@ open Invalidate * We assume that no known functions that are reachable are executed/spawned. For that we use ThreadCreate above. *) (* WTF: why are argument numbers 1-indexed (in partition)? *) let invalidate_actions = [ - "__printf_chk", readsAll;(*safe*) - "printk", readsAll;(*safe*) - "__mutex_init", readsAll;(*safe*) - "__builtin___snprintf_chk", writes [1];(*keep [1]*) - "__vfprintf_chk", writes [1];(*keep [1]*) - "__builtin_va_arg", readsAll;(*safe*) - "__builtin_va_end", readsAll;(*safe*) - "__builtin_va_start", readsAll;(*safe*) - "__ctype_b_loc", readsAll;(*safe*) - "__errno", readsAll;(*safe*) - "__errno_location", readsAll;(*safe*) - "__strdup", readsAll;(*safe*) - "strtoul__extinline", readsAll;(*safe*) - "readdir_r", writesAll;(*unsafe*) - "atoi__extinline", readsAll;(*safe*) - "_IO_getc", writesAll;(*unsafe*) - "pipe", writesAll;(*unsafe*) - "strerror_r", writesAll;(*unsafe*) - "raise", writesAll;(*unsafe*) - "_strlen", readsAll;(*safe*) - "stat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) - "lstat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) - "waitpid", readsAll;(*safe*) - "__open_alias", readsAll;(*safe*) - "__open_2", readsAll;(*safe*) - "ioctl", writesAll;(*unsafe*) - "fstat__extinline", writesAll;(*unsafe*) - "scandir", writes [1;3;4];(*keep [1;3;4]*) - "bindtextdomain", readsAll;(*safe*) - "textdomain", readsAll;(*safe*) - "dcgettext", readsAll;(*safe*) - "putw", readsAll;(*safe*) - "__getdelim", writes [3];(*keep [3]*) - "__h_errno_location", readsAll;(*safe*) - "__fxstat", readsAll;(*safe*) - "openlog", readsAll;(*safe*) - "umask", readsAll;(*safe*) - "clntudp_create", writesAllButFirst 3 readsAll;(*drop 3*) - "svctcp_create", readsAll;(*safe*) - "clntudp_bufcreate", writesAll;(*unsafe*) - "authunix_create_default", readsAll;(*safe*) - "clnt_broadcast", writesAll;(*unsafe*) - "clnt_sperrno", readsAll;(*safe*) - "pmap_unset", writesAll;(*unsafe*) - "svcudp_create", readsAll;(*safe*) - "svc_register", writesAll;(*unsafe*) - "svc_run", writesAll;(*unsafe*) - "dup", readsAll; (*safe*) - "__builtin___vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) - "__builtin___vsnprintf_chk", writesAllButFirst 3 readsAll; (*drop 3*) - "__error", readsAll; (*safe*) - "__maskrune", writesAll; (*unsafe*) - "times", writesAll; (*unsafe*) - "timespec_get", writes [1]; - "__tolower", readsAll; (*safe*) - "signal", writesAll; (*unsafe*) - "BF_cfb64_encrypt", writes [1;3;4;5]; (*keep [1;3;4,5]*) - "BZ2_bzBuffToBuffDecompress", writes [3;4]; (*keep [3;4]*) - "uncompress", writes [3;4]; (*keep [3;4]*) - "__xstat", writes [3]; (*keep [1]*) - "__lxstat", writes [3]; (*keep [1]*) - "remove", readsAll; - "BZ2_bzBuffToBuffCompress", writes [3;4]; (*keep [3;4]*) - "compress2", writes [3]; (*keep [3]*) - "__toupper", readsAll; (*safe*) - "BF_set_key", writes [3]; (*keep [3]*) - "PL_NewHashTable", readsAll; (*safe*) - "assert_failed", readsAll; (*safe*) - "munmap", readsAll;(*safe*) - "mmap", readsAll;(*safe*) - "__builtin_va_arg_pack_len", readsAll; - "__open_too_many_args", readsAll; - "usb_submit_urb", readsAll; (* first argument is written to but according to specification must not be read from anymore *) - "dev_driver_string", readsAll; - "__spin_lock_init", writes [1]; - "kmem_cache_create", readsAll; - "idr_pre_get", readsAll; - "zil_replay", writes [1;2;3;5]; - (* ddverify *) - "sema_init", readsAll; - "__goblint_assume_join", readsAll; - ] + "__printf_chk", readsAll;(*safe*) + "printk", readsAll;(*safe*) + "__mutex_init", readsAll;(*safe*) + "__builtin___snprintf_chk", writes [1];(*keep [1]*) + "__vfprintf_chk", writes [1];(*keep [1]*) + "__builtin_va_arg", readsAll;(*safe*) + "__builtin_va_end", readsAll;(*safe*) + "__builtin_va_start", readsAll;(*safe*) + "__ctype_b_loc", readsAll;(*safe*) + "__errno", readsAll;(*safe*) + "__errno_location", readsAll;(*safe*) + "__strdup", readsAll;(*safe*) + "strtoul__extinline", readsAll;(*safe*) + "readdir_r", writesAll;(*unsafe*) + "atoi__extinline", readsAll;(*safe*) + "_IO_getc", writesAll;(*unsafe*) + "pipe", writesAll;(*unsafe*) + "strerror_r", writesAll;(*unsafe*) + "raise", writesAll;(*unsafe*) + "_strlen", readsAll;(*safe*) + "stat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) + "lstat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) + "waitpid", readsAll;(*safe*) + "__open_alias", readsAll;(*safe*) + "__open_2", readsAll;(*safe*) + "ioctl", writesAll;(*unsafe*) + "fstat__extinline", writesAll;(*unsafe*) + "scandir", writes [1;3;4];(*keep [1;3;4]*) + "bindtextdomain", readsAll;(*safe*) + "textdomain", readsAll;(*safe*) + "dcgettext", readsAll;(*safe*) + "putw", readsAll;(*safe*) + "__getdelim", writes [3];(*keep [3]*) + "__h_errno_location", readsAll;(*safe*) + "__fxstat", readsAll;(*safe*) + "openlog", readsAll;(*safe*) + "umask", readsAll;(*safe*) + "clntudp_create", writesAllButFirst 3 readsAll;(*drop 3*) + "svctcp_create", readsAll;(*safe*) + "clntudp_bufcreate", writesAll;(*unsafe*) + "authunix_create_default", readsAll;(*safe*) + "clnt_broadcast", writesAll;(*unsafe*) + "clnt_sperrno", readsAll;(*safe*) + "pmap_unset", writesAll;(*unsafe*) + "svcudp_create", readsAll;(*safe*) + "svc_register", writesAll;(*unsafe*) + "svc_run", writesAll;(*unsafe*) + "dup", readsAll; (*safe*) + "__builtin___vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) + "__builtin___vsnprintf_chk", writesAllButFirst 3 readsAll; (*drop 3*) + "__error", readsAll; (*safe*) + "__maskrune", writesAll; (*unsafe*) + "times", writesAll; (*unsafe*) + "timespec_get", writes [1]; + "__tolower", readsAll; (*safe*) + "signal", writesAll; (*unsafe*) + "BF_cfb64_encrypt", writes [1;3;4;5]; (*keep [1;3;4,5]*) + "BZ2_bzBuffToBuffDecompress", writes [3;4]; (*keep [3;4]*) + "uncompress", writes [3;4]; (*keep [3;4]*) + "__xstat", writes [3]; (*keep [1]*) + "__lxstat", writes [3]; (*keep [1]*) + "remove", readsAll; + "BZ2_bzBuffToBuffCompress", writes [3;4]; (*keep [3;4]*) + "compress2", writes [3]; (*keep [3]*) + "__toupper", readsAll; (*safe*) + "BF_set_key", writes [3]; (*keep [3]*) + "PL_NewHashTable", readsAll; (*safe*) + "assert_failed", readsAll; (*safe*) + "munmap", readsAll;(*safe*) + "mmap", readsAll;(*safe*) + "__builtin_va_arg_pack_len", readsAll; + "__open_too_many_args", readsAll; + "usb_submit_urb", readsAll; (* first argument is written to but according to specification must not be read from anymore *) + "dev_driver_string", readsAll; + "__spin_lock_init", writes [1]; + "kmem_cache_create", readsAll; + "idr_pre_get", readsAll; + "zil_replay", writes [1;2;3;5]; + (* ddverify *) + "sema_init", readsAll; + "__goblint_assume_join", readsAll; +] let invalidate_actions = let tbl = Hashtbl.create 113 in diff --git a/src/analyses/libraryFunctions.mli b/src/util/library/libraryFunctions.mli similarity index 100% rename from src/analyses/libraryFunctions.mli rename to src/util/library/libraryFunctions.mli diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 4ce8fc06b4..e1a8ad542b 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -320,6 +320,7 @@ class loopUnrollingCallVisitor = object | Unlock _ | ThreadCreate _ | Assert _ + | Bounded _ | ThreadJoin _ -> raise Found; | _ -> diff --git a/src/util/server.ml b/src/util/server.ml index 22f5a03350..829ee92ee8 100644 --- a/src/util/server.ml +++ b/src/util/server.ml @@ -280,6 +280,7 @@ let analyze ?(reset=false) (s: t) = InvariantCil.reset_lazy (); WideningThresholds.reset_lazy (); IntDomain.reset_lazy (); + StringDomain.reset_lazy (); PrecisionUtil.reset_lazy (); ApronDomain.reset_lazy (); AutoTune.reset_lazy (); diff --git a/src/util/std/dune b/src/util/std/dune index c6961a1725..b074a29937 100644 --- a/src/util/std/dune +++ b/src/util/std/dune @@ -9,7 +9,8 @@ goblint-cil fpath yojson - yaml) + yaml + qcheck-core) (preprocess (pps ppx_deriving.std diff --git a/src/util/std/gobHashtbl.ml b/src/util/std/gobHashtbl.ml index c14bafc0cb..c93244eb47 100644 --- a/src/util/std/gobHashtbl.ml +++ b/src/util/std/gobHashtbl.ml @@ -1,9 +1,5 @@ module Pretty = GoblintCil.Pretty -let magic_stats h = - let h: _ Hashtbl.t = Obj.magic h in (* Batteries Hashtables don't expose stats yet...: https://github.com/ocaml-batteries-team/batteries-included/pull/1079 *) - Hashtbl.stats h - let pretty_statistics () (s: Hashtbl.statistics) = let load_factor = float_of_int s.num_bindings /. float_of_int s.num_buckets in Pretty.dprintf "bindings=%d buckets=%d max_length=%d histo=%a load=%f" s.num_bindings s.num_buckets s.max_bucket_length (Pretty.docList (Pretty.dprintf "%d")) (Array.to_list s.bucket_histogram) load_factor diff --git a/src/common/domains/myCheck.ml b/src/util/std/gobQCheck.ml similarity index 91% rename from src/common/domains/myCheck.ml rename to src/util/std/gobQCheck.ml index 98583cd2c3..12809d5b46 100644 --- a/src/common/domains/myCheck.ml +++ b/src/util/std/gobQCheck.ml @@ -56,7 +56,4 @@ struct let gens = List.map gen arbs in let shrinks = List.map shrink arbs in make ~shrink:(Shrink.sequence shrinks) (Gen.sequence gens) - - open GoblintCil - let varinfo: Cil.varinfo arbitrary = QCheck.always (Cil.makeGlobalVar "arbVar" Cil.voidPtrType) (* S TODO: how to generate this *) end diff --git a/src/util/std/gobYaml.ml b/src/util/std/gobYaml.ml index a4f3e597aa..131daaaebb 100644 --- a/src/util/std/gobYaml.ml +++ b/src/util/std/gobYaml.ml @@ -1,3 +1,14 @@ +let to_string' ?(len=65535 * 4) ?encoding ?scalar_style ?layout_style v = + assert (len >= 1); + let rec aux len = + match Yaml.to_string ~len ?encoding ?scalar_style ?layout_style v with + | Ok _ as o -> o + | Error (`Msg ("scalar failed" | "doc_end failed")) when len < Sys.max_string_length / 2 -> + aux (len * 2) + | Error (`Msg _) as e -> e + in + aux len + include Yaml.Util include GobResult.Syntax diff --git a/src/util/std/goblint_std.ml b/src/util/std/goblint_std.ml index e716d1df5b..0d548cac08 100644 --- a/src/util/std/goblint_std.ml +++ b/src/util/std/goblint_std.ml @@ -19,6 +19,7 @@ module GobUnix = GobUnix module GobFpath = GobFpath module GobPretty = GobPretty +module GobQCheck = GobQCheck module GobYaml = GobYaml module GobYojson = GobYojson module GobZ = GobZ diff --git a/src/util/terminationPreprocessing.ml b/src/util/terminationPreprocessing.ml new file mode 100644 index 0000000000..9023a68f8a --- /dev/null +++ b/src/util/terminationPreprocessing.ml @@ -0,0 +1,76 @@ +open GoblintCil +(* module Z = Big_int_Z *) + +module VarToStmt = Map.Make(CilType.Varinfo) (* maps varinfos (= loop counter variable) to the statement of the corresponding loop*) + +let counter_ikind = IULongLong +let counter_typ = TInt (counter_ikind, []) +let min_int_exp = + (* Currently only tested for IInt type, which is signed *) + if Cil.isSigned counter_ikind then + Const(CInt(Z.shift_left Cilint.mone_cilint ((bytesSizeOfInt counter_ikind)*8-1), IInt, None)) + else + Const(CInt(Z.zero, counter_ikind, None)) + +class loopCounterVisitor lc (fd : fundec) = object(self) + inherit nopCilVisitor + + (* Counter of variables inserted for termination *) + val mutable vcounter = ref 0 + + method! vfunc _ = + vcounter := 0; + DoChildren + + method! vstmt s = + + let specialFunction name = + { svar = makeGlobalVar name (TFun(voidType, Some [("exp", counter_typ, [])], false,[])); + smaxid = 0; + slocals = []; + sformals = []; + sbody = mkBlock []; + smaxstmtid = None; + sallstmts = []; + } in + + let f_bounded = Lval (var (specialFunction "__goblint_bounded").svar) in + + (* Yields increment expression e + 1 where the added "1" that has the same type as the expression [e]. + Using Cil.increm instead does not work for non-[IInt] ikinds. *) + let increment_expression e = + let et = typeOf e in + let bop = PlusA in + let one = Const (CInt (Cilint.one_cilint, counter_ikind, None)) in + constFold false (BinOp(bop, e, one, et)) in + + let action s = match s.skind with + | Loop (b, loc, eloc, _, _) -> + let vname = "term" ^ string_of_int loc.line ^ "_" ^ string_of_int loc.column ^ "_id" ^ (string_of_int !vcounter) in + incr vcounter; + let v = Cil.makeLocalVar fd vname counter_typ in (*Not tested for incremental mode*) + let lval = Lval (Var v, NoOffset) in + let init_stmt = mkStmtOneInstr @@ Set (var v, min_int_exp, loc, eloc) in + let inc_stmt = mkStmtOneInstr @@ Set (var v, increment_expression lval, loc, eloc) in + let exit_stmt = mkStmtOneInstr @@ Call (None, f_bounded, [lval], loc, locUnknown) in + b.bstmts <- exit_stmt :: inc_stmt :: b.bstmts; + lc := VarToStmt.add (v: varinfo) (s: stmt) !lc; + let nb = mkBlock [init_stmt; mkStmt s.skind] in + s.skind <- Block nb; + s + | Goto (sref, l) -> + let goto_jmp_stmt = sref.contents.skind in + let loc_stmt = Cil.get_stmtLoc goto_jmp_stmt in + if CilType.Location.compare l loc_stmt >= 0 then ( + (* is pos if first loc is greater -> below the second loc *) + (* problem: the program might not terminate! *) + let open Cilfacade in + let current = FunLocH.find_opt funs_with_upjumping_gotos fd in + let current = BatOption.default (LocSet.create 13) current in + LocSet.replace current l (); + FunLocH.replace funs_with_upjumping_gotos fd current; + ); + s + | _ -> s + in ChangeDoChildrenPost (s, action); +end diff --git a/src/util/tracing/dune b/src/util/tracing/dune new file mode 100644 index 0000000000..7e37139567 --- /dev/null +++ b/src/util/tracing/dune @@ -0,0 +1,9 @@ +(include_subdirs no) + +(library + (name goblint_tracing) + (public_name goblint.tracing) + (libraries + goblint_std + goblint-cil + goblint_build_info)) diff --git a/src/common/util/tracing.ml b/src/util/tracing/goblint_tracing.ml similarity index 84% rename from src/common/util/tracing.ml rename to src/util/tracing/goblint_tracing.ml index ad8892c396..0e5580b036 100644 --- a/src/common/util/tracing.ml +++ b/src/util/tracing/goblint_tracing.ml @@ -4,6 +4,7 @@ * large domains we output. The original code generated the document object * even when the subsystem is not activated. *) +open Goblint_std open GoblintCil open Pretty @@ -67,13 +68,6 @@ let trace sys ?var fmt = gtrace true printtrace sys var ignore fmt * c: continue/normal print w/o indent-change *) -let tracel sys ?var fmt = - let loc = !current_loc in - let docloc sys doc = - printtrace sys (dprintf "(%a)@?" CilType.Location.pretty loc ++ indent 2 doc); - in - gtrace true docloc sys var ~loc ignore fmt - let tracei (sys:string) ?var ?(subsys=[]) fmt = let f sys d = printtrace sys d; traceIndent () in let g () = activate sys subsys in @@ -85,13 +79,3 @@ let traceu sys fmt = let f sys d = printtrace sys d; traceOutdent () in let g () = deactivate sys in gtrace true f sys None g fmt - - -let traceli sys ?var ?(subsys=[]) fmt = - let loc = !current_loc in - let g () = activate sys subsys in - let docloc sys doc: unit = - printtrace sys (dprintf "(%a)" CilType.Location.pretty loc ++ indent 2 doc); - traceIndent () - in - gtrace true docloc sys var ~loc g fmt diff --git a/src/witness/myARG.ml b/src/witness/myARG.ml index 068aed7a22..373a66d3d6 100644 --- a/src/witness/myARG.ml +++ b/src/witness/myARG.ml @@ -141,7 +141,7 @@ struct let equal_node_context _ _ = failwith "StackNode: equal_node_context" end -module Stack (Cfg:CfgForward) (Arg: S): +module Stack (Arg: S with module Edge = InlineEdge): S with module Node = StackNode (Arg.Node) and module Edge = Arg.Edge = struct module Node = StackNode (Arg.Node) @@ -156,45 +156,30 @@ struct | n :: stack -> let cfgnode = Arg.Node.cfgnode n in match cfgnode with - | Function _ -> (* TODO: can this be done without Cfg? *) + | Function _ -> (* TODO: can this be done without cfgnode? *) begin match stack with (* | [] -> failwith "StackArg.next: return stack empty" *) | [] -> [] (* main return *) | call_n :: call_stack -> - let call_cfgnode = Arg.Node.cfgnode call_n in let call_next = - Cfg.next call_cfgnode + Arg.next call_n (* filter because infinite loops starting with function call will have another Neg(1) edge from the head *) - |> List.filter (fun (locedges, to_node) -> - List.exists (function - | (_, Proc _) -> true - | (_, _) -> false - ) locedges + |> List.filter_map (fun (edge, to_n) -> + match edge with + | InlinedEdge _ -> Some to_n + | _ -> None ) in - begin match call_next with - | [] -> failwith "StackArg.next: call next empty" - | [(_, return_node)] -> - begin match Arg.Node.move_opt call_n return_node with - (* TODO: Is it possible to have a calling node without a returning node? *) - (* | None -> [] *) - | None -> failwith "StackArg.next: no return node" - | Some return_n -> - (* TODO: Instead of next & filter, construct unique return_n directly. Currently edge missing. *) - Arg.next n - |> List.filter (fun (edge, to_n) -> - (* let to_cfgnode = Arg.Node.cfgnode to_n in - MyCFG.Node.equal to_cfgnode return_node *) - Arg.Node.equal_node_context to_n return_n - ) - |> List.map (fun (edge, to_n) -> - let to_n' = to_n :: call_stack in - (edge, to_n') - ) - end - | _ :: _ :: _ -> failwith "StackArg.next: call next ambiguous" - end + Arg.next n + |> List.filter_map (fun (edge, to_n) -> + if BatList.mem_cmp Arg.Node.compare to_n call_next then ( + let to_n' = to_n :: call_stack in + Some (edge, to_n') + ) + else + None + ) end | _ -> let+ (edge, to_n) = Arg.next n in diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index eae97b1199..6d22a51166 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -17,7 +17,6 @@ let task: (module Task) option ref = ref None let is_error_function' f spec = - let module Task = (val (Option.get !task)) in List.exists (function | Specification.UnreachCall f_spec -> f.vname = f_spec | _ -> false @@ -53,6 +52,7 @@ struct | UnreachCall _ -> "unreach-call" | NoOverflow -> "no-overflow" | NoDataRace -> "no-data-race" (* not yet in SV-COMP/Benchexec *) + | Termination -> "termination" | ValidFree -> "valid-free" | ValidDeref -> "valid-deref" | ValidMemtrack -> "valid-memtrack" @@ -76,9 +76,9 @@ sig val is_sink: Arg.Node.t -> bool end -module StackTaskResult (Cfg:MyCFG.CfgForward) (TaskResult: TaskResult) = +module StackTaskResult (TaskResult: TaskResult with module Arg.Edge = MyARG.InlineEdge) = struct - module Arg = MyARG.Stack (Cfg) (TaskResult.Arg) + module Arg = MyARG.Stack (TaskResult.Arg) let result = TaskResult.result diff --git a/src/witness/svcompSpec.ml b/src/witness/svcompSpec.ml index 66b3b83ac8..c7601ef637 100644 --- a/src/witness/svcompSpec.ml +++ b/src/witness/svcompSpec.ml @@ -6,6 +6,7 @@ type t = | UnreachCall of string | NoDataRace | NoOverflow + | Termination | ValidFree | ValidDeref | ValidMemtrack @@ -17,6 +18,7 @@ let of_string s = let s = String.strip s in let regexp_single = Str.regexp "CHECK( init(main()), LTL(G \\(.*\\)) )" in let regexp_negated = Str.regexp "CHECK( init(main()), LTL(G ! \\(.*\\)) )" in + let regexp_finally = Str.regexp "CHECK( init(main()), LTL(F \\(.*\\)) )" in if Str.string_match regexp_negated s 0 then let global_not = Str.matched_group 1 s in if global_not = "data-race" then @@ -42,6 +44,12 @@ let of_string s = ValidMemcleanup else failwith "Svcomp.Specification.of_string: unknown global expression" + else if Str.string_match regexp_finally s 0 then + let finally = Str.matched_group 1 s in + if finally = "end" then + Termination + else + failwith "Svcomp.Specification.of_string: unknown finally expression" else failwith "Svcomp.Specification.of_string: unknown expression" @@ -66,22 +74,32 @@ let of_option () = of_string s let to_string spec = - let print_output spec_str is_neg = + let module Prop = struct + type prop = F | G + let string_of_prop = function + | F -> "F" + | G -> "G" + end + in + let open Prop in + let print_output prop spec_str is_neg = + let prop = string_of_prop prop in if is_neg then - Printf.sprintf "CHECK( init(main()), LTL(G ! %s) )" spec_str + Printf.sprintf "CHECK( init(main()), LTL(%s ! %s) )" prop spec_str else - Printf.sprintf "CHECK( init(main()), LTL(G %s) )" spec_str + Printf.sprintf "CHECK( init(main()), LTL(%s %s) )" prop spec_str in - let spec_str, is_neg = match spec with - | UnreachCall f -> "call(" ^ f ^ "())", true - | NoDataRace -> "data-race", true - | NoOverflow -> "overflow", true - | ValidFree -> "valid-free", false - | ValidDeref -> "valid-deref", false - | ValidMemtrack -> "valid-memtrack", false - | ValidMemcleanup -> "valid-memcleanup", false + let prop, spec_str, is_neg = match spec with + | UnreachCall f -> G, "call(" ^ f ^ "())", true + | NoDataRace -> G, "data-race", true + | NoOverflow -> G, "overflow", true + | ValidFree -> G, "valid-free", false + | ValidDeref -> G, "valid-deref", false + | ValidMemtrack -> G, "valid-memtrack", false + | ValidMemcleanup -> G, "valid-memcleanup", false + | Termination -> F, "end", false in - print_output spec_str is_neg + print_output prop spec_str is_neg let to_string spec = String.concat "\n" (List.map to_string spec) diff --git a/src/witness/witness.ml b/src/witness/witness.ml index d94960d542..29f337a668 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -14,7 +14,7 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) let module TaskResult = (val if get_bool "witness.graphml.stack" then - (module StackTaskResult (Task.Cfg) (TaskResult) : WitnessTaskResult) + (module StackTaskResult (TaskResult) : WitnessTaskResult) else (module TaskResult) ) @@ -475,6 +475,36 @@ struct in (module TaskResult:WitnessTaskResult) ) + | Termination -> + let module TrivialArg = + struct + include Arg + let next _ = [] + end + in + if not !AnalysisState.svcomp_may_not_terminate then + let module TaskResult = + struct + module Arg = TrivialArg + let result = Result.True + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + else ( + let module TaskResult = + struct + module Arg = TrivialArg + let result = Result.Unknown + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + ) | NoOverflow -> let module TrivialArg = struct @@ -665,11 +695,18 @@ struct | Some false -> print_svcomp_result "ERROR (verify)" | _ -> if get_string "witness.yaml.validate" <> "" then ( - if !YamlWitness.cnt_refuted > 0 then + match get_bool "witness.yaml.strict" with + | true when !YamlWitness.cnt_error > 0 -> + print_svcomp_result "ERROR (witness error)" + | true when !YamlWitness.cnt_unsupported > 0 -> + print_svcomp_result "ERROR (witness unsupported)" + | true when !YamlWitness.cnt_disabled > 0 -> + print_svcomp_result "ERROR (witness disabled)" + | _ when !YamlWitness.cnt_refuted > 0 -> print_svcomp_result (Result.to_string (False None)) - else if !YamlWitness.cnt_unconfirmed > 0 then + | _ when !YamlWitness.cnt_unconfirmed > 0 -> print_svcomp_result (Result.to_string Unknown) - else + | _ -> write entrystates ) else diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 9e8ebeff51..253ee5eecd 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -25,7 +25,7 @@ struct let uuid = Uuidm.v4_gen uuid_random_state () in let creation_time = TimeUtil.iso8601_now () in { - format_version = "0.1"; + format_version = GobConfig.get_string "witness.yaml.format-version"; uuid = Uuidm.to_string uuid; creation_time; producer; @@ -91,6 +91,29 @@ struct metadata = metadata ~task (); } + let location_invariant' ~location ~(invariant): InvariantSet.Invariant.t = { + invariant_type = LocationInvariant { + location; + value = invariant; + format = "c_expression"; + }; + } + + let loop_invariant' ~location ~(invariant): InvariantSet.Invariant.t = { + invariant_type = LoopInvariant { + location; + value = invariant; + format = "c_expression"; + }; + } + + let invariant_set ~task ~(invariants): Entry.t = { + entry_type = InvariantSet { + content = invariants; + }; + metadata = metadata ~task (); + } + let target ~uuid ~type_ ~(file_name): Target.t = { uuid; type_; @@ -113,9 +136,9 @@ struct let precondition_loop_invariant_certificate ~target ~(certification): Entry.t = { entry_type = PreconditionLoopInvariantCertificate { - target; - certification; - }; + target; + certification; + }; metadata = metadata (); } end @@ -124,8 +147,7 @@ let yaml_entries_to_file yaml_entries file = let yaml = `A yaml_entries in (* Yaml_unix.to_file_exn file yaml *) (* to_file/to_string uses a fixed-size buffer... *) - (* estimate how big it should be + extra in case empty *) - let text = match Yaml.to_string ~len:(List.length yaml_entries * 4096 + 2048) yaml with + let text = match GobYaml.to_string' yaml with | Ok text -> text | Error (`Msg m) -> failwith ("Yaml.to_string: " ^ m) in @@ -134,6 +156,9 @@ let yaml_entries_to_file yaml_entries file = let entry_type_enabled entry_type = List.mem entry_type (GobConfig.get_string_list "witness.yaml.entry-types") +let invariant_type_enabled invariant_type = + List.mem invariant_type (GobConfig.get_string_list "witness.yaml.invariant-types") + module Make (R: ResultQuery.SpecSysSol2) = struct open R @@ -145,6 +170,16 @@ struct module FCMap = BatHashtbl.Make (Printable.Prod (CilType.Fundec) (Spec.C)) type con_inv = {node: Node.t; context: Spec.C.t; invariant: Invariant.t; state: Spec.D.t} + (* TODO: fix location hack *) + module LH = BatHashtbl.Make (CilType.Location) + let location2nodes: Node.t list LH.t Lazy.t = lazy ( + let lh = LH.create 113 in + NH.iter (fun n _ -> + LH.modify_def [] (Node.location n) (List.cons n) lh + ) (Lazy.force nh); + lh + ) + let write () = let input_files = GobConfig.get_string_list "files" in let data_model = match GobConfig.get_string "exp.architecture" with @@ -208,16 +243,21 @@ struct (* Generate location invariants (without precondition) *) let entries = if entry_type_enabled YamlWitnessType.LocationInvariant.entry_type then ( - NH.fold (fun n local acc -> - let loc = Node.location n in - if is_invariant_node n then ( - let lvals = local_lvals n local in - match R.ask_local_node n ~local (Invariant {Invariant.default_context with lvals}) with + LH.fold (fun loc ns acc -> + if List.exists is_invariant_node ns then ( + let inv = List.fold_left (fun acc n -> + let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in + let lvals = local_lvals n local in + Invariant.(acc || R.ask_local_node n ~local (Invariant {Invariant.default_context with lvals})) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) (Invariant.bot ()) ns + in + match inv with | `Lifted inv -> + let fundec = Node.find_fundec (List.hd ns) in (* TODO: fix location hack *) + let location_function = fundec.svar.vname in + let location = Entry.location ~location:loc ~location_function in let invs = WitnessUtil.InvariantExp.process_exp inv in List.fold_left (fun acc inv -> - let location_function = (Node.find_fundec n).svar.vname in - let location = Entry.location ~location:loc ~location_function in let invariant = Entry.invariant (CilType.Exp.show inv) in let entry = Entry.location_invariant ~task ~location ~invariant in entry :: acc @@ -227,7 +267,7 @@ struct ) else acc - ) (Lazy.force nh) entries + ) (Lazy.force location2nodes) entries ) else entries @@ -236,15 +276,20 @@ struct (* Generate loop invariants (without precondition) *) let entries = if entry_type_enabled YamlWitnessType.LoopInvariant.entry_type then ( - NH.fold (fun n local acc -> - let loc = Node.location n in - if WitnessInvariant.emit_loop_head && WitnessUtil.NH.mem WitnessInvariant.loop_heads n then ( - match R.ask_local_node n ~local (Invariant Invariant.default_context) with + LH.fold (fun loc ns acc -> + if WitnessInvariant.emit_loop_head && List.exists (WitnessUtil.NH.mem WitnessInvariant.loop_heads) ns then ( + let inv = List.fold_left (fun acc n -> + let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in + Invariant.(acc || R.ask_local_node n ~local (Invariant Invariant.default_context)) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) (Invariant.bot ()) ns + in + match inv with | `Lifted inv -> + let fundec = Node.find_fundec (List.hd ns) in (* TODO: fix location hack *) + let location_function = fundec.svar.vname in + let location = Entry.location ~location:loc ~location_function in let invs = WitnessUtil.InvariantExp.process_exp inv in List.fold_left (fun acc inv -> - let location_function = (Node.find_fundec n).svar.vname in - let location = Entry.location ~location:loc ~location_function in let invariant = Entry.invariant (CilType.Exp.show inv) in let entry = Entry.loop_invariant ~task ~location ~invariant in entry :: acc @@ -254,7 +299,7 @@ struct ) else acc - ) (Lazy.force nh) entries + ) (Lazy.force location2nodes) entries ) else entries @@ -385,6 +430,84 @@ struct entries in + (* Generate invariant set *) + let entries = + if entry_type_enabled YamlWitnessType.InvariantSet.entry_type then ( + let invariants = [] in + + (* Generate location invariants *) + let invariants = + if invariant_type_enabled YamlWitnessType.InvariantSet.LocationInvariant.invariant_type then ( + LH.fold (fun loc ns acc -> + if List.exists is_invariant_node ns then ( + let inv = List.fold_left (fun acc n -> + let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in + let lvals = local_lvals n local in + Invariant.(acc || R.ask_local_node n ~local (Invariant {Invariant.default_context with lvals})) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) (Invariant.bot ()) ns + in + match inv with + | `Lifted inv -> + let fundec = Node.find_fundec (List.hd ns) in (* TODO: fix location hack *) + let location_function = fundec.svar.vname in + let location = Entry.location ~location:loc ~location_function in + let invs = WitnessUtil.InvariantExp.process_exp inv in + List.fold_left (fun acc inv -> + let invariant = CilType.Exp.show inv in + let invariant = Entry.location_invariant' ~location ~invariant in + invariant :: acc + ) acc invs + | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) + acc + ) + else + acc + ) (Lazy.force location2nodes) invariants + ) + else + invariants + in + + (* Generate loop invariants *) + let invariants = + if invariant_type_enabled YamlWitnessType.InvariantSet.LoopInvariant.invariant_type then ( + LH.fold (fun loc ns acc -> + if WitnessInvariant.emit_loop_head && List.exists (WitnessUtil.NH.mem WitnessInvariant.loop_heads) ns then ( + let inv = List.fold_left (fun acc n -> + let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in + Invariant.(acc || R.ask_local_node n ~local (Invariant Invariant.default_context)) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) (Invariant.bot ()) ns + in + match inv with + | `Lifted inv -> + let fundec = Node.find_fundec (List.hd ns) in (* TODO: fix location hack *) + let location_function = fundec.svar.vname in + let location = Entry.location ~location:loc ~location_function in + let invs = WitnessUtil.InvariantExp.process_exp inv in + List.fold_left (fun acc inv -> + let invariant = CilType.Exp.show inv in + let invariant = Entry.loop_invariant' ~location ~invariant in + invariant :: acc + ) acc invs + | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) + acc + ) + else + acc + ) (Lazy.force location2nodes) invariants + ) + else + invariants + in + + let invariants = List.rev invariants in + let entry = Entry.invariant_set ~task ~invariants in + entry :: entries + ) + else + entries + in + let yaml_entries = List.rev_map YamlWitnessType.Entry.to_yaml entries in (* reverse to make entries in file in the same order as generation messages *) M.msg_group Info ~category:Witness "witness generation summary" [ @@ -639,6 +762,48 @@ struct None in + let validate_invariant_set (invariant_set: YamlWitnessType.InvariantSet.t) = + + let validate_location_invariant (location_invariant: YamlWitnessType.InvariantSet.LocationInvariant.t) = + let loc = loc_of_location location_invariant.location in + let inv = location_invariant.value in + + match Locator.find_opt locator loc with + | Some lvars -> + ignore (validate_lvars_invariant ~entry_certificate:None ~loc ~lvars inv) + | None -> + incr cnt_error; + M.warn ~category:Witness ~loc:(CilLocation loc) "couldn't locate invariant: %s" inv; + in + + let validate_loop_invariant (loop_invariant: YamlWitnessType.InvariantSet.LoopInvariant.t) = + let loc = loc_of_location loop_invariant.location in + let inv = loop_invariant.value in + + match Locator.find_opt loop_locator loc with + | Some lvars -> + ignore (validate_lvars_invariant ~entry_certificate:None ~loc ~lvars inv) + | None -> + incr cnt_error; + M.warn ~category:Witness ~loc:(CilLocation loc) "couldn't locate invariant: %s" inv; + in + + let validate_invariant (invariant: YamlWitnessType.InvariantSet.Invariant.t) = + let target_type = YamlWitnessType.InvariantSet.InvariantType.invariant_type invariant.invariant_type in + match invariant_type_enabled target_type, invariant.invariant_type with + | true, LocationInvariant x -> + validate_location_invariant x + | true, LoopInvariant x -> + validate_loop_invariant x + | false, (LocationInvariant _ | LoopInvariant _) -> + incr cnt_disabled; + M.info_noloc ~category:Witness "disabled invariant of type %s" target_type; + in + + List.iter validate_invariant invariant_set.content; + None + in + match entry_type_enabled target_type, entry.entry_type with | true, LocationInvariant x -> validate_location_invariant x @@ -646,7 +811,9 @@ struct validate_loop_invariant x | true, PreconditionLoopInvariant x -> validate_precondition_loop_invariant x - | false, (LocationInvariant _ | LoopInvariant _ | PreconditionLoopInvariant _) -> + | true, InvariantSet x -> + validate_invariant_set x + | false, (LocationInvariant _ | LoopInvariant _ | PreconditionLoopInvariant _ | InvariantSet _) -> incr cnt_disabled; M.info_noloc ~category:Witness "disabled entry of type %s" target_type; None diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index 3390c1e3ab..de9fa151d8 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -242,6 +242,103 @@ struct {location; loop_invariant; precondition} end +module InvariantSet = +struct + module LoopInvariant = + struct + type t = { + location: Location.t; + value: string; + format: string; + } + + let invariant_type = "loop_invariant" + + let to_yaml' {location; value; format} = + [ + ("location", Location.to_yaml location); + ("value", `String value); + ("format", `String format); + ] + + let of_yaml y = + let open GobYaml in + let+ location = y |> find "location" >>= Location.of_yaml + and+ value = y |> find "value" >>= to_string + and+ format = y |> find "format" >>= to_string in + {location; value; format} + end + + module LocationInvariant = + struct + include LoopInvariant + + let invariant_type = "location_invariant" + end + + (* TODO: could maybe use GADT, but adds ugly existential layer to entry type pattern matching *) + module InvariantType = + struct + type t = + | LocationInvariant of LocationInvariant.t + | LoopInvariant of LoopInvariant.t + + let invariant_type = function + | LocationInvariant _ -> LocationInvariant.invariant_type + | LoopInvariant _ -> LoopInvariant.invariant_type + + let to_yaml' = function + | LocationInvariant x -> LocationInvariant.to_yaml' x + | LoopInvariant x -> LoopInvariant.to_yaml' x + + let of_yaml y = + let open GobYaml in + let* invariant_type = y |> find "type" >>= to_string in + if invariant_type = LocationInvariant.invariant_type then + let+ x = y |> LocationInvariant.of_yaml in + LocationInvariant x + else if invariant_type = LoopInvariant.invariant_type then + let+ x = y |> LoopInvariant.of_yaml in + LoopInvariant x + else + Error (`Msg "type") + end + + module Invariant = + struct + type t = { + invariant_type: InvariantType.t; + } + + let to_yaml {invariant_type} = + `O [ + ("invariant", `O ([ + ("type", `String (InvariantType.invariant_type invariant_type)); + ] @ InvariantType.to_yaml' invariant_type) + ) + ] + + let of_yaml y = + let open GobYaml in + let+ invariant_type = y |> find "invariant" >>= InvariantType.of_yaml in + {invariant_type} + end + + type t = { + content: Invariant.t list; + } + + let entry_type = "invariant_set" + + let to_yaml' {content} = + [("content", `A (List.map Invariant.to_yaml content))] + + let of_yaml y = + let open GobYaml in + let+ content = y |> find "content" >>= list >>= list_map Invariant.of_yaml in + {content} +end + module Target = struct type t = { @@ -326,6 +423,7 @@ struct | PreconditionLoopInvariant of PreconditionLoopInvariant.t | LoopInvariantCertificate of LoopInvariantCertificate.t | PreconditionLoopInvariantCertificate of PreconditionLoopInvariantCertificate.t + | InvariantSet of InvariantSet.t let entry_type = function | LocationInvariant _ -> LocationInvariant.entry_type @@ -334,6 +432,7 @@ struct | PreconditionLoopInvariant _ -> PreconditionLoopInvariant.entry_type | LoopInvariantCertificate _ -> LoopInvariantCertificate.entry_type | PreconditionLoopInvariantCertificate _ -> PreconditionLoopInvariantCertificate.entry_type + | InvariantSet _ -> InvariantSet.entry_type let to_yaml' = function | LocationInvariant x -> LocationInvariant.to_yaml' x @@ -342,6 +441,7 @@ struct | PreconditionLoopInvariant x -> PreconditionLoopInvariant.to_yaml' x | LoopInvariantCertificate x -> LoopInvariantCertificate.to_yaml' x | PreconditionLoopInvariantCertificate x -> PreconditionLoopInvariantCertificate.to_yaml' x + | InvariantSet x -> InvariantSet.to_yaml' x let of_yaml y = let open GobYaml in @@ -364,6 +464,9 @@ struct else if entry_type = PreconditionLoopInvariantCertificate.entry_type then let+ x = y |> PreconditionLoopInvariantCertificate.of_yaml in PreconditionLoopInvariantCertificate x + else if entry_type = InvariantSet.entry_type then + let+ x = y |> InvariantSet.of_yaml in + InvariantSet x else Error (`Msg "entry_type") end diff --git a/sv-comp/archive.sh b/sv-comp/archive.sh index 9bab49f70d..5d8605dc70 100755 --- a/sv-comp/archive.sh +++ b/sv-comp/archive.sh @@ -4,7 +4,7 @@ make clean -git tag -m "SV-COMP 2023" svcomp23 +git tag -m "SV-COMP 2024" svcomp24 dune build --profile=release src/goblint.exe rm -f goblint @@ -18,7 +18,7 @@ cp _opam/share/apron/lib/libapron.so lib/ cp _opam/share/apron/lib/liboctD.so lib/ cp _opam/share/apron/lib/libboxD.so lib/ cp _opam/share/apron/lib/libpolkaMPQ.so lib/ -cp _opam/.opam-switch/sources/apron/COPYING lib/LICENSE.APRON +wget -O lib/LICENSE.APRON https://raw.githubusercontent.com/antoinemine/apron/master/COPYING # done outside to ensure archive contains goblint/ directory cd .. @@ -32,7 +32,8 @@ zip goblint/sv-comp/goblint.zip \ goblint/lib/libboxD.so \ goblint/lib/libpolkaMPQ.so \ goblint/lib/LICENSE.APRON \ - goblint/conf/svcomp23.json \ + goblint/conf/svcomp24.json \ + goblint/conf/svcomp24-validate.json \ goblint/lib/libc/stub/include/assert.h \ goblint/lib/goblint/runtime/include/goblint.h \ goblint/lib/libc/stub/src/stdlib.c \ diff --git a/tests/regression/00-sanity/51-base-special-lval.c b/tests/regression/00-sanity/51-base-special-lval.c new file mode 100644 index 0000000000..8f74a1babe --- /dev/null +++ b/tests/regression/00-sanity/51-base-special-lval.c @@ -0,0 +1,13 @@ +// Making sure special function lval is not invalidated recursively +#include + +extern int * anIntPlease(); +int main() { + int x = 0; + int *p = &x; + p = anIntPlease(); + + __goblint_check(x == 0); + + return 0; +} diff --git a/tests/regression/02-base/88-string-ptrs-limited.c b/tests/regression/02-base/88-string-ptrs-limited.c index ab8b2fefe8..c4f39dc711 100644 --- a/tests/regression/02-base/88-string-ptrs-limited.c +++ b/tests/regression/02-base/88-string-ptrs-limited.c @@ -1,4 +1,4 @@ -//PARAM: --enable ana.base.limit-string-addresses +//PARAM: --set ana.base.strings.domain flat #include #include diff --git a/tests/regression/02-base/89-string-ptrs-not-limited.c b/tests/regression/02-base/89-string-ptrs-not-limited.c index 96100d230d..ab30e21fd8 100644 --- a/tests/regression/02-base/89-string-ptrs-not-limited.c +++ b/tests/regression/02-base/89-string-ptrs-not-limited.c @@ -1,4 +1,4 @@ -//PARAM: --disable ana.base.limit-string-addresses +//PARAM: --set ana.base.strings.domain disjoint #include #include diff --git a/tests/regression/04-mutex/49-type-invariants.t b/tests/regression/04-mutex/49-type-invariants.t index 4c105d1559..4b8118eec1 100644 --- a/tests/regression/04-mutex/49-type-invariants.t +++ b/tests/regression/04-mutex/49-type-invariants.t @@ -14,8 +14,6 @@ live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (49-type-invariants.c:21:3-21:21) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (49-type-invariants.c:21:3-21:21) [Info][Unsound] Write to unknown address: privatization is unsound. (49-type-invariants.c:21:3-21:21) [Info][Imprecise] INVALIDATING ALL GLOBALS! (49-type-invariants.c:21:3-21:21) [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (49-type-invariants.c:21:3-21:21) @@ -39,8 +37,6 @@ live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (49-type-invariants.c:21:3-21:21) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (49-type-invariants.c:21:3-21:21) [Info][Unsound] Write to unknown address: privatization is unsound. (49-type-invariants.c:21:3-21:21) [Info][Imprecise] INVALIDATING ALL GLOBALS! (49-type-invariants.c:21:3-21:21) [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (49-type-invariants.c:21:3-21:21) diff --git a/tests/regression/04-mutex/77-type-nested-fields.t b/tests/regression/04-mutex/77-type-nested-fields.t index bb935cb0ed..68d9cdb779 100644 --- a/tests/regression/04-mutex/77-type-nested-fields.t +++ b/tests/regression/04-mutex/77-type-nested-fields.t @@ -15,11 +15,7 @@ live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:31:3-31:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:31:3-31:20) [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:31:3-31:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:38:3-38:22) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:38:3-38:22) [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:38:3-38:22) [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:31:3-31:20) [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:31:3-31:20) diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.t b/tests/regression/04-mutex/79-type-nested-fields-deep1.t index ba1399d225..85f7bfb709 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.t +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.t @@ -15,11 +15,7 @@ live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:36:3-36:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:36:3-36:20) [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:36:3-36:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:43:3-43:24) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:43:3-43:24) [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:43:3-43:24) [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:36:3-36:20) [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:36:3-36:20) diff --git a/tests/regression/04-mutex/80-type-nested-fields-deep2.t b/tests/regression/04-mutex/80-type-nested-fields-deep2.t index 71bdcfb2e2..a2e9e2ab15 100644 --- a/tests/regression/04-mutex/80-type-nested-fields-deep2.t +++ b/tests/regression/04-mutex/80-type-nested-fields-deep2.t @@ -15,11 +15,7 @@ live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:36:3-36:22) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:36:3-36:22) [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:36:3-36:22) - [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:43:3-43:24) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:43:3-43:24) [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:43:3-43:24) [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:36:3-36:22) [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:36:3-36:22) diff --git a/tests/regression/04-mutex/90-distribute-fields-type-1.t b/tests/regression/04-mutex/90-distribute-fields-type-1.t index 46435045b9..a3b5faf083 100644 --- a/tests/regression/04-mutex/90-distribute-fields-type-1.t +++ b/tests/regression/04-mutex/90-distribute-fields-type-1.t @@ -17,11 +17,7 @@ live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:31:3-31:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:31:3-31:20) [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:31:3-31:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:39:3-39:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:39:3-39:17) [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:39:3-39:17) [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:31:3-31:20) [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:31:3-31:20) diff --git a/tests/regression/04-mutex/91-distribute-fields-type-2.t b/tests/regression/04-mutex/91-distribute-fields-type-2.t index c7e66c0527..5773245114 100644 --- a/tests/regression/04-mutex/91-distribute-fields-type-2.t +++ b/tests/regression/04-mutex/91-distribute-fields-type-2.t @@ -17,11 +17,7 @@ live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:32:3-32:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:32:3-32:17) [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:32:3-32:17) - [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:40:3-40:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:40:3-40:17) [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:40:3-40:17) [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:32:3-32:17) [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:32:3-32:17) diff --git a/tests/regression/04-mutex/92-distribute-fields-type-deep.t b/tests/regression/04-mutex/92-distribute-fields-type-deep.t index 4fc1c7e101..798374d63c 100644 --- a/tests/regression/04-mutex/92-distribute-fields-type-deep.t +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.t @@ -17,11 +17,7 @@ live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:36:3-36:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:36:3-36:20) [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:36:3-36:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:44:3-44:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:44:3-44:17) [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:44:3-44:17) [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:36:3-36:20) [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:36:3-36:20) diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.t b/tests/regression/04-mutex/93-distribute-fields-type-global.t index bf34d99936..07999854ff 100644 --- a/tests/regression/04-mutex/93-distribute-fields-type-global.t +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.t @@ -16,8 +16,6 @@ live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (93-distribute-fields-type-global.c:13:3-13:29) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (93-distribute-fields-type-global.c:13:3-13:29) [Info][Unsound] Write to unknown address: privatization is unsound. (93-distribute-fields-type-global.c:13:3-13:29) [Info][Imprecise] INVALIDATING ALL GLOBALS! (93-distribute-fields-type-global.c:13:3-13:29) [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (93-distribute-fields-type-global.c:13:3-13:29) diff --git a/tests/regression/09-regions/38-escape_malloc.c b/tests/regression/09-regions/38-escape_malloc.c index 9f5b44531e..c849d5bbe3 100644 --- a/tests/regression/09-regions/38-escape_malloc.c +++ b/tests/regression/09-regions/38-escape_malloc.c @@ -9,7 +9,7 @@ pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; void *t_fun(void *arg) { int *p = (int *) arg; pthread_mutex_lock(&mutex1); - (*p)++; // TODO RACE! + (*p)++; // RACE! pthread_mutex_unlock(&mutex1); return NULL; } @@ -20,7 +20,7 @@ int main(void) { // TODO: q escapes as region owner pthread_create(&id, NULL, t_fun, (void *) q); pthread_mutex_lock(&mutex2); - (*q)++; // TODO RACE! + (*q)++; // RACE! pthread_mutex_unlock(&mutex2); pthread_join (id, NULL); return 0; diff --git a/tests/regression/09-regions/41-per-thread-array-init-race.c b/tests/regression/09-regions/41-per-thread-array-init-race.c new file mode 100644 index 0000000000..f6d267495e --- /dev/null +++ b/tests/regression/09-regions/41-per-thread-array-init-race.c @@ -0,0 +1,40 @@ +// PARAM: --set ana.activated[+] region --enable ana.sv-comp.functions +// Per-thread array pointers passed via argument but initialized before thread create. +// Extracted from silver searcher. +#include +#include +extern void abort(void); +void assume_abort_if_not(int cond) { + if(!cond) {abort();} +} +extern int __VERIFIER_nondet_int(); + +void *thread(void *arg) { + int *p = arg; + int i = *p; // RACE! + return NULL; +} + +int main() { + int threads_total = __VERIFIER_nondet_int(); + assume_abort_if_not(threads_total >= 0); + + pthread_t *tids = malloc(threads_total * sizeof(pthread_t)); + int *is = calloc(threads_total, sizeof(int)); + + // create threads + for (int i = 0; i < threads_total; i++) { + pthread_create(&tids[i], NULL, &thread, &is[i]); // may fail but doesn't matter + is[i] = i; // RACE! + } + + // join threads + for (int i = 0; i < threads_total; i++) { + pthread_join(tids[i], NULL); + } + + free(tids); + free(is); + + return 0; +} diff --git a/tests/regression/18-file/01-ok.c b/tests/regression/18-file/01-ok.c deleted file mode 100644 index 5c1f21ff1c..0000000000 --- a/tests/regression/18-file/01-ok.c +++ /dev/null @@ -1,12 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp; - fp = fopen("test.txt", "a"); - fprintf(fp, "Testing...\n"); - fclose(fp); -} - -// All ok! diff --git a/tests/regression/18-file/02-function.c b/tests/regression/18-file/02-function.c deleted file mode 100644 index fc3157c264..0000000000 --- a/tests/regression/18-file/02-function.c +++ /dev/null @@ -1,17 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -void f(){ - fp = fopen("test.txt", "a"); -} - -int main(){ - f(); - fprintf(fp, "Testing...\n"); - fclose(fp); -} - -// All ok! diff --git a/tests/regression/18-file/03-if-close.c b/tests/regression/18-file/03-if-close.c deleted file mode 100644 index b2bf1ebe97..0000000000 --- a/tests/regression/18-file/03-if-close.c +++ /dev/null @@ -1,15 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - int b; - fp = fopen("test.txt", "a"); // WARN: MAYBE file is never closed - - fprintf(fp, "Testing...\n"); - - if (b) - fclose(fp); -} // WARN: MAYBE unclosed files: fp diff --git a/tests/regression/18-file/04-no-open.c b/tests/regression/18-file/04-no-open.c deleted file mode 100644 index 70683f3852..0000000000 --- a/tests/regression/18-file/04-no-open.c +++ /dev/null @@ -1,10 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - fprintf(fp, "Testing...\n"); // WARN: writing to unopened file handle fp - fclose(fp); // WARN: closeing unopened file handle fp -} diff --git a/tests/regression/18-file/05-open-mode.c b/tests/regression/18-file/05-open-mode.c deleted file mode 100644 index 77326d7a70..0000000000 --- a/tests/regression/18-file/05-open-mode.c +++ /dev/null @@ -1,11 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - fp = fopen("test.txt", "r"); - fprintf(fp, "Testing...\n"); // WARN: writing to read-only file handle fp - fclose(fp); -} diff --git a/tests/regression/18-file/06-2open.c b/tests/regression/18-file/06-2open.c deleted file mode 100644 index 2826c2f1dc..0000000000 --- a/tests/regression/18-file/06-2open.c +++ /dev/null @@ -1,12 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - fp = fopen("test1.txt", "a"); // WARN: file is never closed - fp = fopen("test2.txt", "a"); // WARN: overwriting still opened file handle fp - fprintf(fp, "Testing...\n"); - fclose(fp); -} // WARN: unclosed files: fp diff --git a/tests/regression/18-file/07-2close.c b/tests/regression/18-file/07-2close.c deleted file mode 100644 index 0545bf9814..0000000000 --- a/tests/regression/18-file/07-2close.c +++ /dev/null @@ -1,11 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp; - fp = fopen("test.txt", "a"); - fprintf(fp, "Testing...\n"); - fclose(fp); - fclose(fp); // WARN: closeing already closed file handle fp -} diff --git a/tests/regression/18-file/08-var-reuse.c b/tests/regression/18-file/08-var-reuse.c deleted file mode 100644 index 1caa238517..0000000000 --- a/tests/regression/18-file/08-var-reuse.c +++ /dev/null @@ -1,15 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp; - fp = fopen("test.txt", "a"); - fprintf(fp, "Testing...\n"); - fclose(fp); - fp = fopen("test2.txt", "a"); - fprintf(fp, "Testing...\n"); - fclose(fp); -} - -// All ok! diff --git a/tests/regression/18-file/09-inf-loop-no-close.c b/tests/regression/18-file/09-inf-loop-no-close.c deleted file mode 100644 index e9563ef195..0000000000 --- a/tests/regression/18-file/09-inf-loop-no-close.c +++ /dev/null @@ -1,17 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - int i; - fp = fopen("test.txt", "a"); // WARN: file is never closed - - while (i){ - fprintf(fp, "Testing...\n"); - i++; - } - - //fclose(fp); -} // WARN: unclosed files: fp diff --git a/tests/regression/18-file/10-inf-loop-ok.c b/tests/regression/18-file/10-inf-loop-ok.c deleted file mode 100644 index d88fde272e..0000000000 --- a/tests/regression/18-file/10-inf-loop-ok.c +++ /dev/null @@ -1,19 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - int i; - fp = fopen("test.txt", "a"); - - while (i){ - fprintf(fp, "Testing...\n"); - i++; - } - - fclose(fp); -} - -// All ok. diff --git a/tests/regression/18-file/11-2if.c b/tests/regression/18-file/11-2if.c deleted file mode 100644 index e24fec6e46..0000000000 --- a/tests/regression/18-file/11-2if.c +++ /dev/null @@ -1,18 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - int b; - fp = fopen("test.txt", "a"); // WARN: MAYBE file is never closed - - if (b) - fclose(fp); - - fprintf(fp, "Testing...\n"); // WARN: MAYBE writing to closed file handle fp - - if (!b) - fclose(fp); // WARN: MAYBE closeing already closed file handle fp -} // WARN: MAYBE unclosed files: fp diff --git a/tests/regression/18-file/12-2close-if.c b/tests/regression/18-file/12-2close-if.c deleted file mode 100644 index 4934b33114..0000000000 --- a/tests/regression/18-file/12-2close-if.c +++ /dev/null @@ -1,15 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp; - int b; - fp = fopen("test.txt", "a"); - fprintf(fp, "Testing...\n"); - - if (b) - fclose(fp); - - fclose(fp); // WARN: MAYBE closeing already closed file handle fp -} diff --git a/tests/regression/18-file/13-ptr-arith-ok.c b/tests/regression/18-file/13-ptr-arith-ok.c deleted file mode 100644 index f707110957..0000000000 --- a/tests/regression/18-file/13-ptr-arith-ok.c +++ /dev/null @@ -1,16 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp; - fp = fopen("test.txt", "a"); // WARN: MAYBE file is never closed - fprintf(fp, "Testing...\n"); - - fp++; // WARN: changed pointer fp (no longer safe) - fp--; // WARN: changed pointer fp (no longer safe) - - fclose(fp); // WARN: MAYBE closeing already closed file handle fp -} // WARN: MAYBE unclosed files: fp - -// OPT: All ok! diff --git a/tests/regression/18-file/14-ptr-arith-close.c b/tests/regression/18-file/14-ptr-arith-close.c deleted file mode 100644 index 3f9cd21ee2..0000000000 --- a/tests/regression/18-file/14-ptr-arith-close.c +++ /dev/null @@ -1,13 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp; - fp = fopen("test.txt", "a"); // WARN: MAYBE file is never closed - fprintf(fp, "Testing...\n"); - - fp++; // WARN: changed pointer fp (no longer safe) - - fclose(fp); // WARN: MAYBE closeing already closed file handle fp -} // WARN: MAYBE unclosed files: fp diff --git a/tests/regression/18-file/15-var-switch.c b/tests/regression/18-file/15-var-switch.c deleted file mode 100644 index d7f74b85db..0000000000 --- a/tests/regression/18-file/15-var-switch.c +++ /dev/null @@ -1,18 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp1; - fp1 = fopen("test.txt", "a"); - fprintf(fp1, "Testing...\n"); - - FILE *fp2; - fp2 = fopen("test.txt", "a"); // WARN: file is never closed - fprintf(fp2, "Testing...\n"); - - fp2 = fp1; - - fclose(fp1); - fclose(fp2); // WARN: closeing already closed file handle fp2 -} // WARN: unclosed files: fp2 diff --git a/tests/regression/18-file/16-var-reuse-close.c b/tests/regression/18-file/16-var-reuse-close.c deleted file mode 100644 index cb1fb5fd22..0000000000 --- a/tests/regression/18-file/16-var-reuse-close.c +++ /dev/null @@ -1,14 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp; - fp = fopen("test.txt", "a"); - fprintf(fp, "Testing...\n"); - fclose(fp); - - fp = fopen("test.txt", "a"); // WARN: file is never closed - fprintf(fp, "Testing...\n"); - // fclose(fp); -} // WARN: unclosed files: fp diff --git a/tests/regression/18-file/17-myfopen.c b/tests/regression/18-file/17-myfopen.c deleted file mode 100644 index 3e005c6e70..0000000000 --- a/tests/regression/18-file/17-myfopen.c +++ /dev/null @@ -1,21 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - - -FILE* myfopen(){ - // FILE *fp_tmp = fopen("test.txt", "a"); // local! - return fopen("test.txt", "a"); -} - -int main(){ - FILE *fp1; - FILE *fp2; - fp1 = myfopen(); - fp2 = myfopen(); // WARN: file is never closed - - fprintf(fp1, "Testing...\n"); - fclose(fp1); - fprintf(fp2, "Testing...\n"); - // fclose(fp2); -} // WARN: unclosed files: fp2 diff --git a/tests/regression/18-file/18-myfopen-arg.c b/tests/regression/18-file/18-myfopen-arg.c deleted file mode 100644 index 5d98db4c53..0000000000 --- a/tests/regression/18-file/18-myfopen-arg.c +++ /dev/null @@ -1,20 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - - -FILE* myfopen(char* f){ - return fopen(f, "a"); -} - -int main(){ - FILE *fp1; - FILE *fp2; - fp1 = myfopen("test1.txt"); - fp2 = myfopen("test2.txt"); // WARN: file is never closed - - fprintf(fp1, "Testing...\n"); - fclose(fp1); - fprintf(fp2, "Testing...\n"); - // fclose(fp2); -} // WARN: unclosed files: fp2 diff --git a/tests/regression/18-file/19-if-close-else.c b/tests/regression/18-file/19-if-close-else.c deleted file mode 100644 index 049e8454b4..0000000000 --- a/tests/regression/18-file/19-if-close-else.c +++ /dev/null @@ -1,17 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - int b; - fp = fopen("test.txt", "a"); - - if (b) - fclose(fp); - else - fprintf(fp, "Testing...\n"); - - fclose(fp); // WARN: MAYBE closeing already closed file handle fp -} diff --git a/tests/regression/18-file/20-loop-close.c b/tests/regression/18-file/20-loop-close.c deleted file mode 100644 index 981248c152..0000000000 --- a/tests/regression/18-file/20-loop-close.c +++ /dev/null @@ -1,18 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - int i; - fp = fopen("test.txt", "a"); // WARN: MAYBE file is never closed - - while (i){ // May closed (11, 3), open(test.txt, Write) (7, 3) - fprintf(fp, "Testing...\n"); // WARN: MAYBE writing to closed file handle fp - fclose(fp); // WARN: MAYBE closeing already closed file handle fp - i++; - } - // why: fp -> Must open(test.txt, Write) (7, 3) - // -> because loop wouldn't exit? -} // WARN: MAYBE unclosed files: fp diff --git a/tests/regression/18-file/21-for-i.c b/tests/regression/18-file/21-for-i.c deleted file mode 100644 index e41bb9b005..0000000000 --- a/tests/regression/18-file/21-for-i.c +++ /dev/null @@ -1,26 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE *fp; - -int main(){ - int i; - fp = fopen("test.txt", "w"); // WARN: MAYBE file is never closed - - for(i=1; i<10; i++){ // join - // i -> Unknown int - if(i%2){ - // i -> Unknown int - // fprintf(fp, "Testing...%s\n", i); // Segmentation fault! - // actually shouldn't warn because open and close are always alternating... - fprintf(fp, "Testing...%i\n", i); // WARN: MAYBE writing to closed file handle fp - fclose(fp); // WARN: MAYBE closeing already closed file handle fp - }else{ - fp = fopen("test.txt", "a"); // WARN: MAYBE file is never closed - } - // why no join? - } - // fp opened or closed? (last i=9 -> open) - // widening -> Warn: might be unclosed -} // WARN: MAYBE unclosed files: fp diff --git a/tests/regression/18-file/22-f_int.c b/tests/regression/18-file/22-f_int.c deleted file mode 100644 index f0376fc5a9..0000000000 --- a/tests/regression/18-file/22-f_int.c +++ /dev/null @@ -1,13 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int f(int x){ - return 2*x; -} - -int main(){ - int a = 1; - a = f(2); - return 0; -} diff --git a/tests/regression/18-file/23-f_str.c b/tests/regression/18-file/23-f_str.c deleted file mode 100644 index 81224d2e72..0000000000 --- a/tests/regression/18-file/23-f_str.c +++ /dev/null @@ -1,13 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -char* f(char* x){ - return x; -} - -int main(){ - char* a = "foo"; - a = f("bar"); - return 0; -} diff --git a/tests/regression/18-file/24-f_wstr.c b/tests/regression/18-file/24-f_wstr.c deleted file mode 100644 index 2379c1f718..0000000000 --- a/tests/regression/18-file/24-f_wstr.c +++ /dev/null @@ -1,14 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include -#include - -wchar_t* f(wchar_t* x){ - return x; -} - -int main(){ - wchar_t* a = L"foo"; - a = f(L"bar"); - return 0; -} diff --git a/tests/regression/18-file/25-mem-ok.c b/tests/regression/18-file/25-mem-ok.c deleted file mode 100644 index 00ba189b8d..0000000000 --- a/tests/regression/18-file/25-mem-ok.c +++ /dev/null @@ -1,29 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp[3]; - // Array -> varinfo with index-offset - fp[1] = fopen("test.txt", "a"); - fprintf(fp[1], "Testing...\n"); - fclose(fp[1]); - - - struct foo { - int i; - FILE *fp; - } bar; - // Struct -> varinfo with field-offset - bar.fp = fopen("test.txt", "a"); - fprintf(bar.fp, "Testing...\n"); - fclose(bar.fp); - - - // Pointer -> Mem exp - *(fp+2) = fopen("test.txt", "a"); - fprintf(*(fp+2), "Testing...\n"); - fclose(*(fp+2)); -} - -// All ok! diff --git a/tests/regression/18-file/26-open-error-ok.c b/tests/regression/18-file/26-open-error-ok.c deleted file mode 100644 index 5cf3aaf7bb..0000000000 --- a/tests/regression/18-file/26-open-error-ok.c +++ /dev/null @@ -1,15 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main (){ - FILE *fp; - fp = fopen("test.txt", "w"); - - if(fp!=NULL){ - fprintf(fp, "Testing..."); - fclose(fp); - } -} - -// All ok! diff --git a/tests/regression/18-file/27-open-error.c b/tests/regression/18-file/27-open-error.c deleted file mode 100644 index bd3048208f..0000000000 --- a/tests/regression/18-file/27-open-error.c +++ /dev/null @@ -1,13 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main (){ - FILE *fp; - fp = fopen("test.txt", "w"); // WARN: MAYBE file is never closed - - if(fp==NULL){ - fprintf(fp, "Testing..."); // WARN: writing to unopened file handle fp - fclose(fp); // WARN: closeing unopened file handle fp - } -} // WARN: MAYBE unclosed files: fp diff --git a/tests/regression/18-file/28-multiple-exits.c b/tests/regression/18-file/28-multiple-exits.c deleted file mode 100644 index 04fa5abab0..0000000000 --- a/tests/regression/18-file/28-multiple-exits.c +++ /dev/null @@ -1,14 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp; - fp = fopen("test.txt", "a"); // WARN: MAYBE file is never closed - fprintf(fp, "Testing...\n"); - int b; - if(b) - return 1; // WARN: unclosed files: fp - fclose(fp); - return 0; -} diff --git a/tests/regression/18-file/29-alias-global.c b/tests/regression/18-file/29-alias-global.c deleted file mode 100644 index 17b94748c0..0000000000 --- a/tests/regression/18-file/29-alias-global.c +++ /dev/null @@ -1,22 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE* fp; -FILE* myfopen(char* f){ - fp = fopen(f, "a"); - return fp; -} - -int main(){ - FILE *fp1; - FILE *fp2; - fp1 = myfopen("test1.txt"); - fp2 = myfopen("test2.txt"); - fprintf(fp1, "Testing...\n"); - fclose(fp1); - fprintf(fp2, "Testing...\n"); - fclose(fp2); -} - -// All ok! diff --git a/tests/regression/18-file/30-ptr-of-ptr.c b/tests/regression/18-file/30-ptr-of-ptr.c deleted file mode 100644 index 5a8d1f97a9..0000000000 --- a/tests/regression/18-file/30-ptr-of-ptr.c +++ /dev/null @@ -1,14 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp1; - fp1 = fopen("test.txt", "a"); - FILE **fp2; - - fp2 = &fp1; - - fclose(fp1); - fclose(*fp2); // WARN: closeing already closed file handle fp1 -} diff --git a/tests/regression/18-file/31-var-reuse-fun.c b/tests/regression/18-file/31-var-reuse-fun.c deleted file mode 100644 index 9c0ccb16a2..0000000000 --- a/tests/regression/18-file/31-var-reuse-fun.c +++ /dev/null @@ -1,16 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -FILE* myfopen(char* f){ - FILE* fp; - fp = fopen(f, "a"); - return fp; -} - -int main(){ - FILE *fp; - fp = fopen("test1.txt", "a"); // WARN: file is never closed - fp = myfopen("test2.txt"); // WARN: overwriting still opened file handle fp - fclose(fp); -} // WARN: unclosed files: fp diff --git a/tests/regression/18-file/32-multi-ptr-close.c b/tests/regression/18-file/32-multi-ptr-close.c deleted file mode 100644 index e252d563a5..0000000000 --- a/tests/regression/18-file/32-multi-ptr-close.c +++ /dev/null @@ -1,25 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp1; - fp1 = fopen("test1.txt", "a"); - fprintf(fp1, "Testing...\n"); - - FILE *fp2; - fp2 = fopen("test2.txt", "a"); - fprintf(fp2, "Testing...\n"); - - FILE **fp; - int b; - if(b){ - fp = &fp1; - }else{ - fp = &fp2; - } - - fclose(*fp); - fclose(fp1); // WARN: MAYBE closeing already closed file handle fp1 - fclose(fp2); // WARN: MAYBE closeing already closed file handle fp2 -} diff --git a/tests/regression/18-file/33-multi-ptr-open.c b/tests/regression/18-file/33-multi-ptr-open.c deleted file mode 100644 index b3cfa0ade4..0000000000 --- a/tests/regression/18-file/33-multi-ptr-open.c +++ /dev/null @@ -1,23 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp1; - fp1 = fopen("test1.txt", "a"); // WARN: MAYBE file is never closed - - FILE *fp2; - fp2 = fopen("test2.txt", "r"); // WARN: MAYBE file is never closed - - FILE **fp; - int b; - if(b){ - fp = &fp1; - }else{ - fp = &fp2; - } - - fprintf(*fp, "Testing...\n"); // WARN: MAYBE writing to read-only file handle fp - - fclose(*fp); -} // WARN: MAYBE unclosed files: fp1, fp2 diff --git a/tests/regression/18-file/34-multi-alias-close.c b/tests/regression/18-file/34-multi-alias-close.c deleted file mode 100644 index 0ebb9ddd30..0000000000 --- a/tests/regression/18-file/34-multi-alias-close.c +++ /dev/null @@ -1,25 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp1; - fp1 = fopen("test1.txt", "a"); - fprintf(fp1, "Testing...\n"); - - FILE *fp2; - fp2 = fopen("test2.txt", "a"); - fprintf(fp2, "Testing...\n"); - - FILE *fp; - int b; - if(b){ - fp = fp1; - }else{ - fp = fp2; - } - - fclose(fp); - fclose(fp1); // WARN: MAYBE closeing already closed file handle fp1 - fclose(fp2); // WARN: MAYBE closeing already closed file handle fp2 -} diff --git a/tests/regression/18-file/35-multi-alias-open.c b/tests/regression/18-file/35-multi-alias-open.c deleted file mode 100644 index 21a4a9cca6..0000000000 --- a/tests/regression/18-file/35-multi-alias-open.c +++ /dev/null @@ -1,23 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp1; - fp1 = fopen("test1.txt", "a"); // WARN: MAYBE file is never closed - - FILE *fp2; - fp2 = fopen("test2.txt", "r"); // WARN: MAYBE file is never closed - - FILE *fp; - int b; - if(b){ - fp = fp1; - }else{ - fp = fp2; - } - - fprintf(fp, "Testing...\n"); // WARN: MAYBE writing to read-only file handle fp - - fclose(fp); -} // WARN: MAYBE unclosed files: fp1, fp2 diff --git a/tests/regression/18-file/36-fun-ptr.c b/tests/regression/18-file/36-fun-ptr.c deleted file mode 100644 index 4f70bf7382..0000000000 --- a/tests/regression/18-file/36-fun-ptr.c +++ /dev/null @@ -1,14 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp; - FILE* (*f)(const char *, const char*); - f = fopen; - fp = f("test.txt", "a"); - fprintf(fp, "Testing...\n"); - fclose(fp); -} - -// All ok! diff --git a/tests/regression/18-file/37-var-switch-alias.c b/tests/regression/18-file/37-var-switch-alias.c deleted file mode 100644 index 5dfde5a2d9..0000000000 --- a/tests/regression/18-file/37-var-switch-alias.c +++ /dev/null @@ -1,18 +0,0 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic - -#include - -int main(){ - FILE *fp1; - fp1 = fopen("test.txt", "a"); - fprintf(fp1, "Testing...\n"); - - FILE *fp2; - fp2 = fopen("test.txt", "a"); // WARN: file is never closed - fprintf(fp2, "Testing...\n"); - - fp2 = fp1; - - fclose(fp2); - fclose(fp1); // WARN: closeing already closed file handle fp1 -} // WARN: unclosed files: fp2 diff --git a/tests/regression/18-file/README.md b/tests/regression/18-file/README.md new file mode 100644 index 0000000000..0e93e175c6 --- /dev/null +++ b/tests/regression/18-file/README.md @@ -0,0 +1,2 @@ +The file analysis has been removed from recent Goblint versions, please use Release 2.3.0 +Folder is left in place to avoid renumbering all tests diff --git a/tests/regression/18-file/file.c b/tests/regression/18-file/file.c deleted file mode 100644 index fc2ebe1699..0000000000 --- a/tests/regression/18-file/file.c +++ /dev/null @@ -1,44 +0,0 @@ -#include - -int main(){ - - // no errors - FILE *fp; - fp = fopen("test.txt", "a"); - if(fp!=0) { - fprintf(fp, "Testing...\n"); - fclose(fp); - } - - // missing fopen -> compiles, but leads to Segmentation fault - FILE *fp2; - // fp2 = fopen("test.txt", "a"); - fprintf(fp2, "Testing...\n"); // WARN - fclose(fp2); // WARN - - // writing to a read-only file -> doesn't do anything - FILE *fp3; - fp3 = fopen("test.txt", "r"); - fprintf(fp3, "Testing...\n"); // (WARN) - fclose(fp3); - - // accessing closed file -> write doesn't do anything - FILE *fp4; - fp4 = fopen("test.txt", "a"); - fclose(fp4); - fprintf(fp4, "Testing...\n"); // WARN - - // missing fclose - FILE *fp5; - fp5 = fopen("test.txt", "a"); // WARN - fprintf(fp5, "Testing...\n"); - - // missing assignment to file handle - fopen("test.txt", "a"); // WARN - - - // bad style: - // opening file but not doing anything - - return 0; // WARN about all unclosed files -} \ No newline at end of file diff --git a/tests/regression/18-file/file.optimistic.spec b/tests/regression/18-file/file.optimistic.spec deleted file mode 100644 index d42e2217b7..0000000000 --- a/tests/regression/18-file/file.optimistic.spec +++ /dev/null @@ -1,34 +0,0 @@ -w1 "file handle is not saved!" -w2 "closeing unopened file handle $" -w3 "writing to unopened file handle $" -w4 "writing to read-only file handle $" -w5 "closeing already closed file handle $" -w6 "writing to closed file handle $" -w7 "overwriting still opened file handle $" -w8 "unrecognized file open mode for file handle $" - -1 -> w1 fopen(_, _) -1 -> w2 fclose($fp) -1 -> w3 fprintf($fp, _) -1 -> open_read $fp = fopen(path, "r") -1 -> open_write $fp = fopen(path, r"[wa]") // see OCaml doc for details (e.g. \\| for alternatives) -1 -> w8 $fp = fopen(path, _) - -open_read -> w4 fprintf($fp, _) - -open_read -w7>> 1 $fp = fopen(path, _) -open_write -w7>> 1 $fp = fopen(path, _) - -open_read -> closed fclose($fp) -open_write -> closed fclose($fp) - -closed -> w5 fclose($fp) -closed -> w6 fprintf($fp, _) -closed ->> 1 _ // let state 1 handle the rest - -// setup which states are end states -1 -> end _ -closed -> end _ -// warning for all entries that are not in an end state -_end "file is never closed" -_END "unclosed files: $" \ No newline at end of file diff --git a/tests/regression/18-file/file.spec b/tests/regression/18-file/file.spec deleted file mode 100644 index aeb747abfd..0000000000 --- a/tests/regression/18-file/file.spec +++ /dev/null @@ -1,57 +0,0 @@ -w1 "file handle is not saved!" -w2 "closeing unopened file handle $" -w3 "writing to unopened file handle $" -w4 "writing to read-only file handle $" -w5 "closeing already closed file handle $" -w6 "writing to closed file handle $" -w7 "overwriting still opened file handle $" -w8 "unrecognized file open mode for file handle $" - -// TODO later add fputs and stuff -1 -> w1 fopen(_, _) -1 -> w2 fclose($fp) -1 -> w3 fprintf($fp, _) -//1 -> open_read $fp = fopen(path, "r") -//1 -> open_write $fp = fopen(path, r"[wa]") // see OCaml doc for details (e.g. \\| for alternatives) -//1 -> w8 $fp = fopen(path, _) - -// go to unchecked states first -1 -> u_open_read $fp = fopen(path, "r") -1 -> u_open_write $fp = fopen(path, r"[wa]") -1 -> w8 $fp = fopen(path, _) -// once branch(exp, tv) is matched, return dom with 1. arg (lval = exp) and true/false -// forwarding from branch is not possible (would need an extra map for storing states) -> ignore it -// warnings are also ignored -// then in branch take out lval, check exp and do the transition to a checked state -u_open_read -> 1 branch($key==0, true) -u_open_read -> open_read branch($key==0, false) -u_open_write -> 1 branch($key==0, true) -u_open_write -> open_write branch($key==0, false) - -// alternative: forward everything. Problem: saving arguments of call (special_fn -> branch -> special_fn) -// 1 ->> open_check $fp = fopen(path, _) -// open_check ->> 1 branch($fp==0, true) -// open_check ->> open branch($fp==0, false) -// open -> open_read $fp = fopen(path, "r") -// open -> open_write $fp = fopen(path, "[wa]") -// open -> w8 $fp = fopen(path, _) - -open_read -> w4 fprintf($fp, _) -// open_write -> open_write fprintf($fp, _) // not needed, but changes loc - -open_read -w7>> 1 $fp = fopen(path, _) -open_write -w7>> 1 $fp = fopen(path, _) - -open_read -> closed fclose($fp) -open_write -> closed fclose($fp) - -closed -> w5 fclose($fp) -closed -> w6 fprintf($fp, _) -closed ->> 1 _ // let state 1 handle the rest - -// setup which states are end states -1 -> end _ -closed -> end _ -// warning for all entries that are not in an end state -_end "file is never closed" -_END "unclosed files: $" \ No newline at end of file diff --git a/tests/regression/19-spec/01-malloc-free.c b/tests/regression/19-spec/01-malloc-free.c deleted file mode 100644 index 43ee527dba..0000000000 --- a/tests/regression/19-spec/01-malloc-free.c +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include - -int main(){ - int *ip; - //*ip = 5; // segfault - //printf("%i", *ip); // segfault - ip = malloc(sizeof(int)); // assume malloc never fails - - // do stuff - //*ip = 5; - - free(ip); - //free(ip); // crash: double free or corruption - *ip = 5; // undefined but no crash - printf("%i", *ip); // undefined but printed 5 - ip = NULL; // make sure the pointer is not used anymore - *ip = 5; // segfault -} diff --git a/tests/regression/19-spec/02-mutex_rc.c b/tests/regression/19-spec/02-mutex_rc.c deleted file mode 100644 index 82c1642a93..0000000000 --- a/tests/regression/19-spec/02-mutex_rc.c +++ /dev/null @@ -1,23 +0,0 @@ -#include -#include - -int myglobal; -pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; -pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; - -void *t_fun(void *arg) { - pthread_mutex_lock(&mutex1); - myglobal=myglobal+1; // RACE! - pthread_mutex_unlock(&mutex1); - return NULL; -} - -int main(void) { - pthread_t id; - pthread_create(&id, NULL, t_fun, NULL); - pthread_mutex_lock(&mutex2); - myglobal=myglobal+1; // RACE! - pthread_mutex_unlock(&mutex2); - pthread_join (id, NULL); - return 0; -} diff --git a/tests/regression/19-spec/README.md b/tests/regression/19-spec/README.md new file mode 100644 index 0000000000..d7e3ae3c8e --- /dev/null +++ b/tests/regression/19-spec/README.md @@ -0,0 +1,2 @@ +The spec analysis has been removed from recent Goblint versions, please use Release 2.3.0 +Folder is left in place to avoid renumbering all tests diff --git a/tests/regression/19-spec/malloc.optimistic.spec b/tests/regression/19-spec/malloc.optimistic.spec deleted file mode 100644 index 860c573814..0000000000 --- a/tests/regression/19-spec/malloc.optimistic.spec +++ /dev/null @@ -1,23 +0,0 @@ -w1 "pointer is not saved [leak]" -w2 "freeing unallocated pointer $ [segfault?]" -w3 "writing to unallocated pointer $ [segfault?]" -w4 "overwriting unfreed pointer $ [leak]" -w5 "freeing already freed pointer $ [double free!]" - -1 -w1> 1 malloc(_) -1 -w2> 1 free($p) -1 -w3> 1 *$p = _ -1 -> alloc $p = malloc(_) // TODO does compiler check size? - -alloc -w4> alloc $p = malloc(_) -alloc -> freed free($p) - -freed -w5> freed free($p) -freed ->> 1 _ // let state 1 handle the rest - -// setup which states are end states -1 -> end _ -freed -> end _ -// warning for all entries that are not in an end state -_end "pointer is never freed" -_END "unfreed pointers: $" \ No newline at end of file diff --git a/tests/regression/19-spec/malloc.spec b/tests/regression/19-spec/malloc.spec deleted file mode 100644 index 9f09430051..0000000000 --- a/tests/regression/19-spec/malloc.spec +++ /dev/null @@ -1,26 +0,0 @@ -w1 "pointer is not saved [leak]" -w2 "freeing unallocated pointer $ [segfault?]" -w3 "writing to unallocated pointer $ [segfault?]" -w4 "overwriting unfreed pointer $ [leak]" -w5 "freeing already freed pointer $ [double free!]" - -1 -w1> 1 malloc(_) -1 -w2> 1 free($p) -1 -w3> 1 *$p = _ -1 -> u_alloc $p = malloc(_) - -u_alloc -> 1 branch($key==0, true) -u_alloc -> alloc branch($key==0, false) - -alloc -w4> alloc $p = malloc(_) -alloc -> freed free($p) - -freed -w5> freed free($p) -freed ->> 1 _ // let state 1 handle the rest - -// setup which states are end states -1 -> end _ -freed -> end _ -// warning for all entries that are not in an end state -_end "pointer is never freed" -_END "unfreed pointers: $" \ No newline at end of file diff --git a/tests/regression/19-spec/mutex-lock.spec b/tests/regression/19-spec/mutex-lock.spec deleted file mode 100644 index 1ec8264078..0000000000 --- a/tests/regression/19-spec/mutex-lock.spec +++ /dev/null @@ -1,31 +0,0 @@ -w1 "unlocking not locked mutex" -w2 "locking already locked mutex" - -1 -w1> 1 pthread_mutex_unlock($p) -1 -> lock pthread_mutex_lock($p) - -lock -w2> lock pthread_mutex_lock($p) -lock -> 1 pthread_mutex_unlock($p) - -// setup which states are end states -1 -> end _ -// warning for all entries that are not in an end state -_end "mutex is never unlocked" -_END "locked mutexes: $" - - - -//w1 "joining not created thread" -//w2 "overwriting id of already created thread" -// -//1 -w1> 1 pthread_join ($p, _) -//1 -> created pthread_create($p, _, _, _) -// -//created -w2> created pthread_create($p, _, _, _) -//created -> 1 pthread_join ($p, _) -// -//// setup which states are end states -//1 -> end _ -//// warning for all entries that are not in an end state -//_end "thread is never joined" -//_END "unjoined threads: $" \ No newline at end of file diff --git a/tests/regression/29-svcomp/32-no-ov.c b/tests/regression/29-svcomp/32-no-ov.c new file mode 100644 index 0000000000..0167098c29 --- /dev/null +++ b/tests/regression/29-svcomp/32-no-ov.c @@ -0,0 +1,7 @@ +// PARAM: --enable ana.int.interval --enable ana.sv-comp.enabled --enable ana.sv-comp.functions --set ana.specification "CHECK( init(main()), LTL(G ! overflow) )" + +int main(){ + // This is not an overflow, just implementation defined behavior on a cast + int data = ((int)(rand() & 1 ? (((unsigned)rand()<<30) ^ ((unsigned)rand()<<15) ^ rand()) : -(((unsigned)rand()<<30) ^ ((unsigned)rand()<<15) ^ rand()) - 1)); + return 0; +} \ No newline at end of file diff --git a/tests/regression/29-svcomp/32-no-ov.t b/tests/regression/29-svcomp/32-no-ov.t new file mode 100644 index 0000000000..85eb90c185 --- /dev/null +++ b/tests/regression/29-svcomp/32-no-ov.t @@ -0,0 +1,16 @@ + $ goblint --enable ana.int.interval --enable ana.sv-comp.enabled --enable ana.sv-comp.functions --set ana.specification "CHECK( init(main()), LTL(G ! overflow) )" 32-no-ov.c + SV-COMP specification: CHECK( init(main()), LTL(G ! overflow) ) + [Warning][Integer > Overflow][CWE-190] Unsigned integer overflow (32-no-ov.c:5:6-5:159) + [Warning][Integer > Overflow][CWE-190] Unsigned integer overflow (32-no-ov.c:5:6-5:159) + [Warning][Integer > Overflow][CWE-191] Unsigned integer underflow (32-no-ov.c:5:6-5:159) + [Warning][Integer > Overflow][CWE-190] Signed integer overflow (32-no-ov.c:5:6-5:159) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 3 + dead: 0 + total lines: 3 + SV-COMP result: true + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 diff --git a/tests/regression/29-svcomp/dune b/tests/regression/29-svcomp/dune new file mode 100644 index 0000000000..23c0dd3290 --- /dev/null +++ b/tests/regression/29-svcomp/dune @@ -0,0 +1,2 @@ +(cram + (deps (glob_files *.c))) diff --git a/tests/regression/39-signed-overflows/06-abs.c b/tests/regression/39-signed-overflows/06-abs.c new file mode 100644 index 0000000000..1323434cbc --- /dev/null +++ b/tests/regression/39-signed-overflows/06-abs.c @@ -0,0 +1,29 @@ +//PARAM: --enable ana.int.interval --set ana.activated[+] tmpSpecial +#include +int main() { + int data; + if (data > (-0x7fffffff - 1)) + { + if (abs(data) < 100) + { + __goblint_check(data < 100); + __goblint_check(-100 < data); + int result = data * data; //NOWARN + } + + if(abs(data) <= 100) + { + __goblint_check(data <= 100); + __goblint_check(-100 <= data); + int result = data * data; //NOWARN + } + + if(abs(data) - 1 <= 99) + { + __goblint_check(data <= 100); + __goblint_check(-100 <= data); + int result = data * data; //NOWARN + } + } + return 8; +} \ No newline at end of file diff --git a/tests/regression/39-signed-overflows/07-abs-sqrt.c b/tests/regression/39-signed-overflows/07-abs-sqrt.c new file mode 100644 index 0000000000..13ed863e51 --- /dev/null +++ b/tests/regression/39-signed-overflows/07-abs-sqrt.c @@ -0,0 +1,10 @@ +//PARAM: --enable ana.int.interval --enable ana.float.interval --set ana.activated[+] tmpSpecial +#include +int main() { + int data; + if (data > (-0x7fffffff - 1) && abs(data) < (long)sqrt((double)0x7fffffff)) + { + int result = data * data; //NOWARN + } + return 8; +} \ No newline at end of file diff --git a/tests/regression/39-signed-overflows/08-labs.c b/tests/regression/39-signed-overflows/08-labs.c new file mode 100644 index 0000000000..a9c6773d11 --- /dev/null +++ b/tests/regression/39-signed-overflows/08-labs.c @@ -0,0 +1,22 @@ +//PARAM: --enable ana.int.interval --set ana.activated[+] tmpSpecial +#include +int main() { + long data; + if (data > (-0xffffffff - 1)) + { + if (labs(data) < 100) + { + __goblint_check(data < 100); + __goblint_check(-100 < data); + int result = data * data; //NOWARN + } + + if(labs(data) <= 100) + { + __goblint_check(data <= 100); + __goblint_check(-100 <= data); + int result = data * data; //NOWARN + } + } + return 8; +} diff --git a/tests/regression/39-signed-overflows/09-labs-sqrt.c b/tests/regression/39-signed-overflows/09-labs-sqrt.c new file mode 100644 index 0000000000..3a4b20a82b --- /dev/null +++ b/tests/regression/39-signed-overflows/09-labs-sqrt.c @@ -0,0 +1,10 @@ +//PARAM: --enable ana.int.interval --enable ana.float.interval --set ana.activated[+] tmpSpecial +#include +int main() { + int data; + if (data > (-0x7fffffff - 1) && llabs(data) < (long)sqrt((double)0x7fffffff)) + { + int result = data * data; //NOWARN + } + return 8; +} diff --git a/tests/regression/39-signed-overflows/10-shiftleft.c b/tests/regression/39-signed-overflows/10-shiftleft.c new file mode 100644 index 0000000000..7e790306ca --- /dev/null +++ b/tests/regression/39-signed-overflows/10-shiftleft.c @@ -0,0 +1,31 @@ +// PARAM: --enable ana.int.interval +#include +#include +int main() +{ + int r; + int zero_or_one = 0; + int top; + char c; + r = c << 1U; //NOWARN + + r = c << 128U; //WARN + r = r << 1U; //WARN + r = 8 << -2; //WARN + + if(top) { zero_or_one = 1; } + + r = 8 << zero_or_one; + + __goblint_check(r >= 8); + __goblint_check(r <= 16); + + int regval = INT_MAX; + int shift = regval >> 6; //NOWARN + int blub = 1 << shift; //WARN + + int regval2; + unsigned long bla = (unsigned long )((1 << ((int )regval2 >> 6)) << 20); //WARN + + return 0; +} diff --git a/tests/regression/56-witness/52-witness-lifter-ps2.c b/tests/regression/56-witness/52-witness-lifter-ps2.c new file mode 100644 index 0000000000..bcb7c1410c --- /dev/null +++ b/tests/regression/56-witness/52-witness-lifter-ps2.c @@ -0,0 +1,35 @@ +// PARAM: --enable ana.sv-comp.enabled --enable ana.sv-comp.functions --enable witness.graphml.enabled --set ana.specification 'CHECK( init(main()), LTL(G valid-memtrack) )' --set ana.activated[+] memLeak --set ana.path_sens[+] memLeak --set ana.malloc.unique_address_count 1 +struct _twoIntsStruct { + int intOne ; + int intTwo ; +}; + +typedef struct _twoIntsStruct twoIntsStruct; + +void printStructLine(twoIntsStruct const *structTwoIntsStruct) +{ + return; +} + + +int main(int argc, char **argv) +{ + twoIntsStruct *data; + int tmp_1; + + + if (tmp_1 != 0) { + twoIntsStruct *dataBuffer = malloc(800UL); + data = dataBuffer; + } + else { + + twoIntsStruct *dataBuffer_0 = malloc(800UL); + data = dataBuffer_0; + } + + printStructLine((twoIntsStruct const *)data); + free((void *)data); + + return; +} diff --git a/tests/regression/56-witness/53-witness-lifter-ps3.c b/tests/regression/56-witness/53-witness-lifter-ps3.c new file mode 100644 index 0000000000..06b73b3888 --- /dev/null +++ b/tests/regression/56-witness/53-witness-lifter-ps3.c @@ -0,0 +1,39 @@ +// PARAM: --enable ana.sv-comp.enabled --enable ana.sv-comp.functions --enable witness.graphml.enabled --set ana.specification 'CHECK( init(main()), LTL(G valid-memtrack) )' --set ana.activated[+] memLeak --set ana.path_sens[+] memLeak --set ana.malloc.unique_address_count 1 +struct _twoIntsStruct { + int intOne ; + int intTwo ; +}; + +typedef struct _twoIntsStruct twoIntsStruct; + +void printStructLine(twoIntsStruct const *structTwoIntsStruct) +{ + return; +} + +twoIntsStruct *foo() { + twoIntsStruct *data; + int tmp_1; + + if (tmp_1 != 0) { + twoIntsStruct *dataBuffer = malloc(800UL); + data = dataBuffer; + } + else { + + twoIntsStruct *dataBuffer_0 = malloc(800UL); + data = dataBuffer_0; + } + return data; +} + +int main(int argc, char **argv) +{ + twoIntsStruct *data; + data = foo(); + + printStructLine((twoIntsStruct const *)data); + free((void *)data); + + return; +} diff --git a/tests/regression/73-strings/01-string_literals.c b/tests/regression/73-strings/01-string_literals.c index 36e4ed121c..42086e07b6 100644 --- a/tests/regression/73-strings/01-string_literals.c +++ b/tests/regression/73-strings/01-string_literals.c @@ -1,4 +1,4 @@ -// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval +// PARAM: --set ana.base.strings.domain disjoint --enable ana.int.interval #include #include @@ -21,7 +21,7 @@ int main() { char* s1 = "abcde"; char* s2 = "abcdfg"; char* s3 = hello_world(); - + int i = strlen(s1); __goblint_check(i == 5); @@ -96,10 +96,10 @@ int main() { #define STRNCAT strncat(s1, "hi", 1) #endif STRNCAT; // WARN - + #ifdef __APPLE__ // do nothing => no warning - #else + #else char s4[] = "hello"; strcpy(s4, s2); // NOWARN strncpy(s4, s3, 2); // NOWARN diff --git a/tests/regression/73-strings/02-string_literals_with_null.c b/tests/regression/73-strings/02-string_literals_with_null.c index 75d000bbb8..cc41e9e287 100644 --- a/tests/regression/73-strings/02-string_literals_with_null.c +++ b/tests/regression/73-strings/02-string_literals_with_null.c @@ -1,4 +1,4 @@ -// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval +// PARAM: --set ana.base.strings.domain disjoint --enable ana.int.interval #include #include diff --git a/tests/regression/73-strings/03-string_basics.c b/tests/regression/73-strings/03-string_basics.c index db196c64b4..a9d02d5e8b 100644 --- a/tests/regression/73-strings/03-string_basics.c +++ b/tests/regression/73-strings/03-string_basics.c @@ -1,4 +1,4 @@ -// PARAM: --disable ana.base.limit-string-addresses --enable ana.int.interval +// PARAM: --set ana.base.strings.domain disjoint --enable ana.int.interval #include #include diff --git a/tests/regression/73-strings/05-string-unit-domain.c b/tests/regression/73-strings/05-string-unit-domain.c new file mode 100644 index 0000000000..70e6bed5bf --- /dev/null +++ b/tests/regression/73-strings/05-string-unit-domain.c @@ -0,0 +1,15 @@ +// PARAM: --set ana.base.strings.domain unit +#include +#include + +void foo(char *s) { + int l = strlen(s); + __goblint_check(l == 3 || l == 6); // UNKNOWN +} + +int main() { + foo("foo"); + foo("bar"); + foo("foobar"); + return 0; +} diff --git a/tests/regression/74-invalid_deref/30-calloc.c b/tests/regression/74-invalid_deref/30-calloc.c new file mode 100644 index 0000000000..624e9c212d --- /dev/null +++ b/tests/regression/74-invalid_deref/30-calloc.c @@ -0,0 +1,9 @@ +//PARAM: --set ana.activated[+] useAfterFree --set ana.activated[+] threadJoins --set ana.activated[+] memOutOfBounds --enable ana.int.interval --set ana.base.arrays.domain partitioned +#include +#include + +int main(int argc, char **argv) +{ + int* ptrCalloc = calloc(100UL,8UL); + *ptrCalloc = 8; //NOWARN +} diff --git a/tests/regression/74-invalid_deref/31-multithreaded.c b/tests/regression/74-invalid_deref/31-multithreaded.c new file mode 100644 index 0000000000..8a0c12350b --- /dev/null +++ b/tests/regression/74-invalid_deref/31-multithreaded.c @@ -0,0 +1,21 @@ +//PARAM: --set ana.path_sens[+] threadflag --set ana.activated[+] memOutOfBounds --set ana.base.privatization mutex-meet-tid +#include + +int data; +int *p = &data, *q; +pthread_mutex_t mutex; +void *t_fun(void *arg) { + pthread_mutex_lock(&mutex); + *p = 8; + pthread_mutex_unlock(&mutex); + return ((void *)0); +} +int main() { + pthread_t id; + pthread_create(&id, ((void *)0), t_fun, ((void *)0)); + q = p; + pthread_mutex_lock(&mutex); + *q = 8; //NOWARN + pthread_mutex_unlock(&mutex); + return 0; +} diff --git a/tests/regression/76-memleak/08-unreachable-mem.c b/tests/regression/76-memleak/08-unreachable-mem.c new file mode 100644 index 0000000000..08e7b4e741 --- /dev/null +++ b/tests/regression/76-memleak/08-unreachable-mem.c @@ -0,0 +1,12 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +int *g; + +int main(int argc, char const *argv[]) { + g = malloc(sizeof(int)); + // Reference to g's heap contents is lost here + g = NULL; + + return 0; //WARN +} diff --git a/tests/regression/76-memleak/09-unreachable-with-local-var.c b/tests/regression/76-memleak/09-unreachable-with-local-var.c new file mode 100644 index 0000000000..bc71bb560e --- /dev/null +++ b/tests/regression/76-memleak/09-unreachable-with-local-var.c @@ -0,0 +1,15 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +int *g; + +int main(int argc, char const *argv[]) { + g = malloc(sizeof(int)); + // Reference to g's heap contents is lost here + g = NULL; + + // According to `valid-memtrack`, the memory of p is unreachable and we don't have a false positive + int *p = malloc(sizeof(int)); + + return 0; //WARN +} diff --git a/tests/regression/76-memleak/10-global-struct-no-ptr.c b/tests/regression/76-memleak/10-global-struct-no-ptr.c new file mode 100644 index 0000000000..490b2bb443 --- /dev/null +++ b/tests/regression/76-memleak/10-global-struct-no-ptr.c @@ -0,0 +1,16 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +typedef struct st { + int *a; + int b; +} st; + +st st_nonptr; + +int main(int argc, char const *argv[]) { + st_nonptr.a = malloc(sizeof(int)); + st_nonptr.a = NULL; + + return 0; //WARN +} diff --git a/tests/regression/76-memleak/11-global-struct-ptr.c b/tests/regression/76-memleak/11-global-struct-ptr.c new file mode 100644 index 0000000000..4ebe1c16b8 --- /dev/null +++ b/tests/regression/76-memleak/11-global-struct-ptr.c @@ -0,0 +1,19 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +typedef struct st { + int *a; + int b; +} st; + +st *st_ptr; + +int main(int argc, char const *argv[]) { + st_ptr = malloc(sizeof(st)); + st_ptr->a = malloc(sizeof(int)); + st_ptr->a = NULL; + free(st_ptr); + + // Only st_ptr->a is causing trouble here + return 0; //WARN +} diff --git a/tests/regression/76-memleak/12-global-nested-struct-ptr.c b/tests/regression/76-memleak/12-global-nested-struct-ptr.c new file mode 100644 index 0000000000..e0f5175064 --- /dev/null +++ b/tests/regression/76-memleak/12-global-nested-struct-ptr.c @@ -0,0 +1,25 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +typedef struct st { + int *a; + int b; +} st; + +typedef struct st2 { + st *st_ptr; +} st2; + +st2 *st_var; + +int main(int argc, char const *argv[]) { + st_var = malloc(sizeof(st2)); + st_var->st_ptr = malloc(sizeof(st)); + st_var->st_ptr->a = malloc(sizeof(int)); + st_var->st_ptr->a = NULL; + free(st_var->st_ptr); + free(st_var); + + // Only st_var->st_ptr->a is causing trouble here + return 0; //WARN +} diff --git a/tests/regression/76-memleak/13-global-nested-struct-ptr-reachable.c b/tests/regression/76-memleak/13-global-nested-struct-ptr-reachable.c new file mode 100644 index 0000000000..1726625a59 --- /dev/null +++ b/tests/regression/76-memleak/13-global-nested-struct-ptr-reachable.c @@ -0,0 +1,29 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +typedef struct st { + int *a; + int b; +} st; + +typedef struct st2 { + st *st_ptr; +} st2; + +st2 *st_var; + +int main(int argc, char const *argv[]) { + st_var = malloc(sizeof(st2)); + st_var->st_ptr = malloc(sizeof(st)); + int *local_ptr = malloc(sizeof(int)); + st_var->st_ptr->a = local_ptr; + local_ptr = NULL; + + free(st_var->st_ptr); + free(st_var); + + // local_ptr's memory is reachable through st_var->st_ptr->a + // It's leaked, because we don't call free() on it + // Hence, there should be a single warning for a memory leak, but not for unreachable memory + return 0; //WARN +} diff --git a/tests/regression/76-memleak/14-global-nested-struct-non-ptr-reachable.c b/tests/regression/76-memleak/14-global-nested-struct-non-ptr-reachable.c new file mode 100644 index 0000000000..1153bd81e0 --- /dev/null +++ b/tests/regression/76-memleak/14-global-nested-struct-non-ptr-reachable.c @@ -0,0 +1,25 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +typedef struct st { + int *a; + int b; +} st; + +typedef struct st2 { + st *st_ptr; +} st2; + +st2 st_var; + +int main(int argc, char const *argv[]) { + st_var.st_ptr = malloc(sizeof(st)); + int *local_ptr = malloc(sizeof(int)); + st_var.st_ptr->a = local_ptr; + local_ptr = NULL; + free(st_var.st_ptr); + + // local_ptr's memory is reachable through st_var.st_ptr->a, but it's not freed + // Hence, there should be only a single warning for a memory leak, but not for unreachable memory + return 0; //WARN +} diff --git a/tests/regression/76-memleak/20-invalid-memcleanup-multi-threaded.c b/tests/regression/76-memleak/20-invalid-memcleanup-multi-threaded.c new file mode 100644 index 0000000000..038801f219 --- /dev/null +++ b/tests/regression/76-memleak/20-invalid-memcleanup-multi-threaded.c @@ -0,0 +1,36 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread +#include +#include + +int *g; +int *m1; + +void *f1(void *arg) { + m1 = malloc(sizeof(int)); + // Thread t1 leaks m1 here + pthread_exit(NULL); //WARN +} + +void *f2(void *arg) { + int *m2; + m2 = malloc(sizeof(int)); + free(m2); // No leak for thread t2, since it calls free before exiting + pthread_exit(NULL); //NOWARN +} + +int main(int argc, char const *argv[]) { + g = malloc(sizeof(int)); + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + + pthread_t t2; + pthread_create(&t2, NULL, f2, NULL); + + free(g); + + pthread_join(t1, NULL); + pthread_join(t2, NULL); + + // main thread is not leaking anything + return 0; //NOWARN +} diff --git a/tests/regression/76-memleak/21-invalid-memcleanup-multi-threaded-abort.c b/tests/regression/76-memleak/21-invalid-memcleanup-multi-threaded-abort.c new file mode 100644 index 0000000000..eaba1e91b5 --- /dev/null +++ b/tests/regression/76-memleak/21-invalid-memcleanup-multi-threaded-abort.c @@ -0,0 +1,35 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread +#include + +int *g; +int *m1; + +void *f1(void *arg) { + m1 = malloc(sizeof(int)); + // Thread t1 leaks m1 here + exit(2); //WARN +} + +void *f2(void *arg) { + int *m2; + m2 = malloc(sizeof(int)); + free(m2); // No leak for thread t2, since it calls free before exiting + pthread_exit(NULL); //NOWARN +} + +int main(int argc, char const *argv[]) { + g = malloc(sizeof(int)); + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + + pthread_t t2; + pthread_create(&t2, NULL, f2, NULL); + + free(g); + + pthread_join(t1, NULL); + pthread_join(t2, NULL); + + // main thread is not leaking anything + return 0; //NOWARN +} diff --git a/tests/regression/76-memleak/22-leak-later.c b/tests/regression/76-memleak/22-leak-later.c new file mode 100644 index 0000000000..6e6e51bbdc --- /dev/null +++ b/tests/regression/76-memleak/22-leak-later.c @@ -0,0 +1,25 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include +#include + +int *g; +int *m1; +int *m2; + +void *f1(void *arg) { + int top; + + // Thread t1 leaks m0 here + exit(2); //WARN +} + +int main(int argc, char const *argv[]) { + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + + int* m0 = malloc(sizeof(int)); + free(m0); + + // main thread is not leaking anything + return 0; +} diff --git a/tests/regression/76-memleak/23-leak-later-nested.c b/tests/regression/76-memleak/23-leak-later-nested.c new file mode 100644 index 0000000000..952dc66334 --- /dev/null +++ b/tests/regression/76-memleak/23-leak-later-nested.c @@ -0,0 +1,34 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include +#include + +int *g; +int *m1; +int *m2; + +void *f2(void *arg) { + // Thread t2 leaks m0 and t1_ptr here + quick_exit(2); //WARN +} + +void *f1(void *arg) { + pthread_t t2; + pthread_create(&t2, NULL, f2, NULL); + + int *t1_ptr = malloc(sizeof(int)); + + pthread_join(t2, NULL); + // t1_ptr is leaked, since t2 calls quick_exit() potentially before this program point + free(t1_ptr); +} + +int main(int argc, char const *argv[]) { + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + + int* m0 = malloc(sizeof(int)); + free(m0); + + // main thread is not leaking anything + return 0; +} diff --git a/tests/regression/76-memleak/24-multi-threaded-assert.c b/tests/regression/76-memleak/24-multi-threaded-assert.c new file mode 100644 index 0000000000..309a5dde75 --- /dev/null +++ b/tests/regression/76-memleak/24-multi-threaded-assert.c @@ -0,0 +1,34 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --disable warn.assert +#include +#include +#include + +int *g; +int *m1; +int *m2; + +void *f2(void *arg) { + // Thread t2 leaks m0 and t1_ptr here + assert(0); //WARN +} + +void *f1(void *arg) { + pthread_t t2; + pthread_create(&t2, NULL, f2, NULL); + + int *t1_ptr = malloc(sizeof(int)); + assert(1); //NOWARN + pthread_join(t2, NULL); + free(t1_ptr); +} + +int main(int argc, char const *argv[]) { + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + + int* m0 = malloc(sizeof(int)); + free(m0); + + // main thread is not leaking anything + return 0; +} diff --git a/tests/regression/76-memleak/25-assert-unknown-multi-threaded.c b/tests/regression/76-memleak/25-assert-unknown-multi-threaded.c new file mode 100644 index 0000000000..95eb291887 --- /dev/null +++ b/tests/regression/76-memleak/25-assert-unknown-multi-threaded.c @@ -0,0 +1,20 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --disable warn.assert +#include +#include +#include + +void *f1(void *arg) { + int top; + assert(top); //WARN +} + +int main(int argc, char const *argv[]) { + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + + int* m0 = malloc(sizeof(int)); + free(m0); + + // main thread is not leaking anything + return 0; +} diff --git a/tests/regression/76-memleak/26-invalid-memcleanup-multi-threaded-betterpiv.c b/tests/regression/76-memleak/26-invalid-memcleanup-multi-threaded-betterpiv.c new file mode 100644 index 0000000000..9f636ab587 --- /dev/null +++ b/tests/regression/76-memleak/26-invalid-memcleanup-multi-threaded-betterpiv.c @@ -0,0 +1,36 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.base.privatization mutex-meet-tid --set ana.path_sens[+] threadflag --set ana.activated[+] thread +#include +#include + +int *g; +int *m1; +int *m2; + +void *f1(void *arg) { + m1 = malloc(sizeof(int)); + // Thread t1 leaks m1 here + pthread_exit(NULL); //WARN +} + +void *f2(void *arg) { + m2 = malloc(sizeof(int)); + free(m2); // No leak for thread t2, since it calls free before exiting + pthread_exit(NULL); //NOWARN +} + +int main(int argc, char const *argv[]) { + g = malloc(sizeof(int)); + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + + pthread_t t2; + pthread_create(&t2, NULL, f2, NULL); + + free(g); + + pthread_join(t1, NULL); + pthread_join(t2, NULL); + + // main thread is not leaking anything + return 0; //NOWARN +} diff --git a/tests/regression/76-memleak/27-mem-leak-not-joined-thread.c b/tests/regression/76-memleak/27-mem-leak-not-joined-thread.c new file mode 100644 index 0000000000..15f249ffe1 --- /dev/null +++ b/tests/regression/76-memleak/27-mem-leak-not-joined-thread.c @@ -0,0 +1,19 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread +#include +#include + +int *m1; + +void *f1(void *arg) { + m1 = malloc(sizeof(int)); + while (1); + return NULL; +} + +int main(int argc, char const *argv[]) { + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + + // memory from thread f1 which was not joined into main, is not freed + return 0; //WARN +} \ No newline at end of file diff --git a/tests/regression/76-memleak/28-no-mem-leak-thread-exit-main.c b/tests/regression/76-memleak/28-no-mem-leak-thread-exit-main.c new file mode 100644 index 0000000000..f7340d1d4f --- /dev/null +++ b/tests/regression/76-memleak/28-no-mem-leak-thread-exit-main.c @@ -0,0 +1,22 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread +#include +#include + +int *m1; + +void *f1(void *arg) { + m1 = malloc(sizeof(int)); + while (1); + return NULL; +} + +int main(int argc, char const *argv[]) { + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + + // A pthread_exit called in main will wait for other threads to finish + // Therefore, no memory leak here + pthread_exit(NULL); // NOWARN + + return 0; // NOWARN (unreachable) +} \ No newline at end of file diff --git a/tests/regression/76-memleak/29-mem-leak-thread-return.c b/tests/regression/76-memleak/29-mem-leak-thread-return.c new file mode 100644 index 0000000000..bec64ca22f --- /dev/null +++ b/tests/regression/76-memleak/29-mem-leak-thread-return.c @@ -0,0 +1,26 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread +#include +#include + +int *m1; + +void *f2(void *arg) { + m1 = malloc(sizeof(int)); + while (1); + return NULL; +} + +void *f1(void *arg) { + pthread_t t2; + pthread_create(&t2, NULL, f2, NULL); + + return NULL; +} + +int main(int argc, char const *argv[]) { + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + pthread_join(t1, NULL); + + return 0; // WARN +} \ No newline at end of file diff --git a/tests/regression/76-memleak/30-mem-leak-thread-exit.c b/tests/regression/76-memleak/30-mem-leak-thread-exit.c new file mode 100644 index 0000000000..e98ae3f346 --- /dev/null +++ b/tests/regression/76-memleak/30-mem-leak-thread-exit.c @@ -0,0 +1,27 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread +#include +#include + +int *m1; + +void *f2(void *arg) { + m1 = malloc(sizeof(int)); + while (1); + return NULL; +} + +void *f1(void *arg) { + pthread_t t2; + pthread_create(&t2, NULL, f2, NULL); + + pthread_exit(NULL); + return NULL; +} + +int main(int argc, char const *argv[]) { + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + pthread_join(t1, NULL); + + return 0; // WARN +} \ No newline at end of file diff --git a/tests/regression/76-memleak/31-no-mem-leak-return.c b/tests/regression/76-memleak/31-no-mem-leak-return.c new file mode 100644 index 0000000000..70e0c66216 --- /dev/null +++ b/tests/regression/76-memleak/31-no-mem-leak-return.c @@ -0,0 +1,32 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak --set ana.activated[+] thread +#include +#include + + +void *f2(void *arg) { + int* m1 = malloc(sizeof(int)); + free(m1); + return NULL; +} + +// We check here that the analysis can distinguish between thread returns and normal returns + +void startf2(pthread_t* t){ + pthread_create(t, NULL, f2, NULL); + return; //NOWARN +} + +void *f1(void *arg) { + pthread_t t2; + startf2(&t2); + pthread_join(t2, NULL); + return NULL; // NOWARN +} + +int main(int argc, char const *argv[]) { + pthread_t t1; + pthread_create(&t1, NULL, f1, NULL); + pthread_join(t1, NULL); + + return 0; // NOWARN +} \ No newline at end of file diff --git a/tests/regression/78-termination/01-simple-loop-terminating.c b/tests/regression/78-termination/01-simple-loop-terminating.c new file mode 100644 index 0000000000..8ca4610057 --- /dev/null +++ b/tests/regression/78-termination/01-simple-loop-terminating.c @@ -0,0 +1,15 @@ +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i = 1; + + while (i <= 10) + { + printf("%d\n", i); + i++; + } + + return 0; +} diff --git a/tests/regression/78-termination/02-simple-loop-nonterminating.c b/tests/regression/78-termination/02-simple-loop-nonterminating.c new file mode 100644 index 0000000000..d8847e2b74 --- /dev/null +++ b/tests/regression/78-termination/02-simple-loop-nonterminating.c @@ -0,0 +1,12 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + while (1) // NONTERMLOOP termination analysis shall mark beginning of while as non-terminating loop + { + continue; + } + + return 0; +} diff --git a/tests/regression/78-termination/03-nested-loop-terminating.c b/tests/regression/78-termination/03-nested-loop-terminating.c new file mode 100644 index 0000000000..6b31204567 --- /dev/null +++ b/tests/regression/78-termination/03-nested-loop-terminating.c @@ -0,0 +1,27 @@ +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int rows = 3; + int columns = 4; + int i = 1; + + // Outer while loop for rows + while (i <= rows) + { + int j = 1; + + // Inner while loop for columns + while (j <= columns) + { + printf("(%d, %d) ", i, j); + j++; + } + + printf("\n"); + i++; + } + + return 0; +} diff --git a/tests/regression/78-termination/04-nested-loop-nonterminating.c b/tests/regression/78-termination/04-nested-loop-nonterminating.c new file mode 100644 index 0000000000..21b6014509 --- /dev/null +++ b/tests/regression/78-termination/04-nested-loop-nonterminating.c @@ -0,0 +1,23 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int outerCount = 1; + + while (outerCount <= 3) + { + int innerCount = 1; + + while (1) // NONTERMLOOP termination analysis shall mark beginning of while as non-terminating loop + { + printf("(%d, %d) ", outerCount, innerCount); + innerCount++; + } + + printf("\n"); + outerCount++; + } + + return 0; +} diff --git a/tests/regression/78-termination/05-for-loop-terminating.c b/tests/regression/78-termination/05-for-loop-terminating.c new file mode 100644 index 0000000000..7a2b789496 --- /dev/null +++ b/tests/regression/78-termination/05-for-loop-terminating.c @@ -0,0 +1,14 @@ +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i; + + for (i = 1; i <= 10; i++) + { + printf("%d\n", i); + } + + return 0; +} diff --git a/tests/regression/78-termination/06-for-loop-nonterminating.c b/tests/regression/78-termination/06-for-loop-nonterminating.c new file mode 100644 index 0000000000..6c6123251c --- /dev/null +++ b/tests/regression/78-termination/06-for-loop-nonterminating.c @@ -0,0 +1,12 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + for (;;) // NONTERMLOOP termination analysis shall mark beginning of for as non-terminating loop + { + printf("This loop does not terminate.\n"); + } + + return 0; +} diff --git a/tests/regression/78-termination/07-nested-for-loop-terminating.c b/tests/regression/78-termination/07-nested-for-loop-terminating.c new file mode 100644 index 0000000000..3293a1fa2c --- /dev/null +++ b/tests/regression/78-termination/07-nested-for-loop-terminating.c @@ -0,0 +1,20 @@ +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int rows = 3; + int columns = 4; + + // Nested loop to iterate over rows and columns + for (int i = 1; i <= rows; i++) + { + for (int j = 1; j <= columns; j++) + { + printf("(%d, %d) ", i, j); + } + printf("\n"); + } + + return 0; +} diff --git a/tests/regression/78-termination/08-nested-for-loop-nonterminating.c b/tests/regression/78-termination/08-nested-for-loop-nonterminating.c new file mode 100644 index 0000000000..cb65a0d267 --- /dev/null +++ b/tests/regression/78-termination/08-nested-for-loop-nonterminating.c @@ -0,0 +1,19 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int outerCount, innerCount; + + for (outerCount = 1; outerCount <= 3; outerCount++) + { + for (innerCount = 1;; innerCount++) // NONTERMLOOP termination analysis shall mark beginning of for as non-terminating loop + { + printf("(%d, %d) ", outerCount, innerCount); + } + + printf("\n"); + } + + return 0; +} diff --git a/tests/regression/78-termination/09-complex-for-loop-terminating.c b/tests/regression/78-termination/09-complex-for-loop-terminating.c new file mode 100644 index 0000000000..74ee41eae8 --- /dev/null +++ b/tests/regression/78-termination/09-complex-for-loop-terminating.c @@ -0,0 +1,98 @@ +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none +// Apron is not precise enough for some nested loops +#include + +int loops0(){ + int i, j, k; + + // Outer loop + for (i = 1; i <= 5; i++) + { + // Inner loop 1 + for (j = 1; j <= i; j++) + { + printf("%d ", j); + } + printf("\n"); + + // Inner loop 2 + for (k = i; k >= 1; k--) + { + printf("%d ", k); + } + printf("\n"); + } + + // Additional loop + for (i = 5; i >= 1; i--) + { + for (j = i; j >= 1; j--) + { + printf("%d ", j); + } + printf("\n"); + } + return 0; +} + +int loops1(){ + int i, j, k; + + // Loop with conditions + for (i = 1; i <= 10; i++) + { + if (i % 2 == 0) + { + printf("%d is even\n", i); + } + else + { + printf("%d is odd\n", i); + } + } + + // Loop with nested conditions + for (i = 1; i <= 10; i++) + { + printf("Number: %d - ", i); + if (i < 5) + { + printf("Less than 5\n"); + } + else if (i > 5) + { + printf("Greater than 5\n"); + } + else + { + printf("Equal to 5\n"); + } + } + + // Loop with a break statement + for (i = 1; i <= 10; i++) + { + printf("%d ", i); + if (i == 5) + { + break; + } + } + printf("\n"); + + // Loop with multiple variables + int a, b, c; + for (a = 1, b = 2, c = 3; a <= 10; a++, b += 2, c += 3) + { + printf("%d %d %d\n", a, b, c); + } + return 0; +} + +int main() +{ + loops0(); + loops1(); + + return 0; +} diff --git a/tests/regression/78-termination/10-complex-loop-terminating.c b/tests/regression/78-termination/10-complex-loop-terminating.c new file mode 100644 index 0000000000..96253c445f --- /dev/null +++ b/tests/regression/78-termination/10-complex-loop-terminating.c @@ -0,0 +1,218 @@ +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none +// Apron is not precise enough for some nested loops +#include + +int loops0(){ + int i = 1; + int j = 1; + int k = 5; + + // Outer while loop + while (i <= 5) + { + // Inner while loop 1 + while (j <= i) + { + printf("%d ", j); + j++; + } + printf("\n"); + j = 1; + + // Inner while loop 2 + while (k >= 1) + { + printf("%d ", k); + k--; + } + printf("\n"); + k = 5; + + i++; + } + + // Additional while loop + i = 5; + while (i >= 1) + { + j = i; + while (j >= 1) + { + printf("%d ", j); + j--; + } + printf("\n"); + i--; + } + + // Loop with conditions + i = 1; + while (i <= 10) + { + if (i % 2 == 0) + { + printf("%d is even\n", i); + } + else + { + printf("%d is odd\n", i); + } + i++; + } + + // Loop with nested conditions + i = 1; + while (i <= 10) + { + printf("Number: %d - ", i); + if (i < 5) + { + printf("Less than 5\n"); + } + else if (i > 5) + { + printf("Greater than 5\n"); + } + else + { + printf("Equal to 5\n"); + } + i++; + } + return 0; +} + +int loops1() +{ + int i = 1; + int j = 1; + int k = 5; + + // Outer while loop + while (i <= 5) + { + // Inner while loop 1 + while (j <= i) + { + printf("%d ", j); + j++; + } + printf("\n"); + j = 1; + + // Inner while loop 2 + while (k >= 1) + { + printf("%d ", k); + k--; + } + printf("\n"); + k = 5; + + i++; + } + + // Additional while loop + i = 5; + while (i >= 1) + { + j = i; + while (j >= 1) + { + printf("%d ", j); + j--; + } + printf("\n"); + i--; + } + + // Loop with conditions + i = 1; + while (i <= 10) + { + if (i % 2 == 0) + { + printf("%d is even\n", i); + } + else + { + printf("%d is odd\n", i); + } + i++; + } + + return 0; +} + +int loops2(){ + int i = 1; + int j = 1; + int k = 5; + + // Loop with nested conditions + i = 1; + while (i <= 10) + { + printf("Number: %d - ", i); + if (i < 5) + { + printf("Less than 5\n"); + } + else if (i > 5) + { + printf("Greater than 5\n"); + } + else + { + printf("Equal to 5\n"); + } + i++; + } + + // Loop with a break statement + i = 1; + while (i <= 10) + { + printf("%d ", i); + if (i == 5) + { + break; + } + i++; + } + printf("\n"); + + // Loop with a continue statement + i = 1; + while (i <= 10) + { + if (i % 2 == 0) + { + i++; + continue; + } + printf("%d ", i); + i++; + } + printf("\n"); + + // Loop with multiple variables + int a = 1; + int b = 2; + int c = 3; + while (a <= 10) + { + printf("%d %d %d\n", a, b, c); + a++; + b += 2; + c += 3; + } + return 0; +} + +int main(){ + loops0(); + loops1(); + loops2(); + return 0; +} \ No newline at end of file diff --git a/tests/regression/78-termination/11-loopless-termination.c b/tests/regression/78-termination/11-loopless-termination.c new file mode 100644 index 0000000000..9f1a7e0f13 --- /dev/null +++ b/tests/regression/78-termination/11-loopless-termination.c @@ -0,0 +1,8 @@ +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + printf("Terminating code without a loop\n"); + return 0; +} diff --git a/tests/regression/78-termination/12-do-while-instant-terminating.c b/tests/regression/78-termination/12-do-while-instant-terminating.c new file mode 100644 index 0000000000..5bc18902b3 --- /dev/null +++ b/tests/regression/78-termination/12-do-while-instant-terminating.c @@ -0,0 +1,15 @@ +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i = 0; + + do + { + printf("Inside the do-while loop\n"); + } while (i > 0); + + printf("Exited the loop\n"); + return 0; +} diff --git a/tests/regression/78-termination/13-do-while-terminating.c b/tests/regression/78-termination/13-do-while-terminating.c new file mode 100644 index 0000000000..6ac6946495 --- /dev/null +++ b/tests/regression/78-termination/13-do-while-terminating.c @@ -0,0 +1,16 @@ +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i = 1; + + do + { + printf("Inside the do-while loop\n"); + i++; + } while (i <= 5); + + printf("Exited the loop\n"); + return 0; +} diff --git a/tests/regression/78-termination/14-do-while-nonterminating.c b/tests/regression/78-termination/14-do-while-nonterminating.c new file mode 100644 index 0000000000..0a9df3421f --- /dev/null +++ b/tests/regression/78-termination/14-do-while-nonterminating.c @@ -0,0 +1,16 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i = 1; + + do + { + printf("Inside the do-while loop\n"); + i++; + } while (i >= 2); // NONTERMLOOP termination analysis shall mark while as non-terminating loop + + printf("Exited the loop\n"); + return 0; +} diff --git a/tests/regression/78-termination/15-complex-loop-combination-terminating.c b/tests/regression/78-termination/15-complex-loop-combination-terminating.c new file mode 100644 index 0000000000..4912bbb1f2 --- /dev/null +++ b/tests/regression/78-termination/15-complex-loop-combination-terminating.c @@ -0,0 +1,126 @@ +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none +// Apron is not precise enough for some nested loops +#include + +int non_nested_loops(){ + // Non-nested loops + int i; + + // for loop + for (i = 1; i <= 10; i++) + { + printf("For loop iteration: %d\n", i); + } + + // while loop + int j = 1; + while (j <= 10) + { + printf("While loop iteration: %d\n", j); + j++; + } + + // do-while loop + int k = 1; + do + { + printf("Do-While loop iteration: %d\n", k); + k++; + } while (k <= 10); + return 0; +} + +int nested_loops(){ + // Nested loops + int a, b; + + // Nested for and while loop + for (a = 1; a <= 5; a++) + { + int c = 1; + while (c <= a) + { + printf("Nested For-While loop: %d\n", c); + c++; + } + } + + // Nested while and do-while loop + int x = 1; + while (x <= 5) + { + int y = 1; + do + { + printf("Nested While-Do-While loop: %d\n", y); + y++; + } while (y <= x); + x++; + } + + // Nested do-while and for loop + int p = 1; + do + { + for (int q = 1; q <= p; q++) + { + printf("Nested Do-While-For loop: %d\n", q); + } + p++; + } while (p <= 5); + return 0; +} + +int nested_while_loop_with_break(){ + int m; + + // Nested while loop with a break statement + int n = 1; + while (n <= 5) + { + printf("Outer While loop iteration: %d\n", n); + m = 1; + while (1) + { + printf("Inner While loop iteration: %d\n", m); + m++; + if (m == 4) + { + break; + } + } + n++; + } + return 0; +} + +int nested_loop_with_conditions(){ + // Loop with nested conditions + for (int v = 1; v <= 10; v++) + { + printf("Loop with Nested Conditions: %d - ", v); + if (v < 5) + { + printf("Less than 5\n"); + } + else if (v > 5) + { + printf("Greater than 5\n"); + } + else + { + printf("Equal to 5\n"); + } + } +} + +int main() +{ + non_nested_loops(); + nested_loops(); + // Additional nested loops + nested_while_loop_with_break(); + nested_loop_with_conditions(); + + return 0; +} diff --git a/tests/regression/78-termination/16-nested-loop-nontrivial-nonterminating.c b/tests/regression/78-termination/16-nested-loop-nontrivial-nonterminating.c new file mode 100644 index 0000000000..267a2d2fd8 --- /dev/null +++ b/tests/regression/78-termination/16-nested-loop-nontrivial-nonterminating.c @@ -0,0 +1,23 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int outerCount = 1; + + while (outerCount <= 3) + { + int innerCount = 1; + + while (outerCount < 3 || innerCount > 0) // NONTERMLOOP termination analysis shall mark beginning of while as non-terminating loop + { + printf("(%d, %d) ", outerCount, innerCount); + innerCount++; + } + + printf("\n"); + outerCount++; + } + + return 0; +} diff --git a/tests/regression/78-termination/17-goto-terminating.c b/tests/regression/78-termination/17-goto-terminating.c new file mode 100644 index 0000000000..2f678d294b --- /dev/null +++ b/tests/regression/78-termination/17-goto-terminating.c @@ -0,0 +1,21 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// The program terminates but the analysis is currently only meant to detect up-jumping gotos as potentially NonTerminating, therefore we expect an NonTerm +#include + +int main() +{ + int num = 1; + +loop: + printf("Current number: %d\n", num); + num++; + + if (num <= 10) + { + goto loop; // NONTERMGOTO termination analysis shall mark goto statement up-jumping goto + // We are not able to detect up-jumping gotos as terminating, we + // just warn about them might being nonterminating. + } + + return 0; +} diff --git a/tests/regression/78-termination/18-goto-nonterminating.c b/tests/regression/78-termination/18-goto-nonterminating.c new file mode 100644 index 0000000000..6de80effd7 --- /dev/null +++ b/tests/regression/78-termination/18-goto-nonterminating.c @@ -0,0 +1,15 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int num = 1; + +loop: + printf("Current number: %d\n", num); + num++; + + goto loop; // NONTERMGOTO termination analysis shall mark goto statement up-jumping goto + + return 0; +} diff --git a/tests/regression/78-termination/19-rand-terminating.c b/tests/regression/78-termination/19-rand-terminating.c new file mode 100644 index 0000000000..a5b6c22941 --- /dev/null +++ b/tests/regression/78-termination/19-rand-terminating.c @@ -0,0 +1,31 @@ +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include +#include +#include + +int main() +{ + // Seed the random number generator + srand(time(NULL)); + + if (rand()) + { + // Loop inside the if part + for (int i = 1; i <= 5; i++) + { + printf("Loop inside if part: %d\n", i); + } + } + else + { + // Loop inside the else part + int j = 1; + while (j <= 5) + { + printf("Loop inside else part: %d\n", j); + j++; + } + } + + return 0; +} diff --git a/tests/regression/78-termination/20-rand-nonterminating.c b/tests/regression/78-termination/20-rand-nonterminating.c new file mode 100644 index 0000000000..21b25ed9dd --- /dev/null +++ b/tests/regression/78-termination/20-rand-nonterminating.c @@ -0,0 +1,30 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include +#include +#include + +int main() +{ + // Seed the random number generator + srand(time(NULL)); + + if (rand()) + { + // Loop inside the if part + for (int i = 1; i >= 0; i++) // NONTERMLOOP termination analysis shall mark beginning of while as non-terminating loop + { + printf("Loop inside if part: %d\n", i); + } + } + else + { + // Loop inside the else part + int j = 1; + while (j > 0) // NONTERMLOOP termination analysis shall mark beginning of while as non-terminating loop + { + printf("Loop inside else part: %d\n", j); + } + } + + return 0; +} diff --git a/tests/regression/78-termination/21-no-exit-on-rand-unproofable.c b/tests/regression/78-termination/21-no-exit-on-rand-unproofable.c new file mode 100644 index 0000000000..5f82d91079 --- /dev/null +++ b/tests/regression/78-termination/21-no-exit-on-rand-unproofable.c @@ -0,0 +1,20 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int forever, i = 0; + + // This loop is not provable, therefore it should throw a warning + while (i < 4 || forever == 1) // NONTERMLOOP termination analysis shall mark beginning of while as non-terminating loop + { + i++; + if (i == 4) + { + if (rand()) + { + forever = 1; + } + } + } +} \ No newline at end of file diff --git a/tests/regression/78-termination/22-exit-on-rand-unproofable.c b/tests/regression/78-termination/22-exit-on-rand-unproofable.c new file mode 100644 index 0000000000..33838ca83d --- /dev/null +++ b/tests/regression/78-termination/22-exit-on-rand-unproofable.c @@ -0,0 +1,16 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int forever = 1; + + // This loop is not provable, therefore it should throw a warning + while (forever == 1) // NONTERMLOOP termination analysis shall mark beginning of while as non-terminating loop + { + if (rand()) // May exit, may not + { + forever = 0; + } + } +} \ No newline at end of file diff --git a/tests/regression/78-termination/23-exit-on-rand-terminating.c b/tests/regression/78-termination/23-exit-on-rand-terminating.c new file mode 100644 index 0000000000..e65c064c40 --- /dev/null +++ b/tests/regression/78-termination/23-exit-on-rand-terminating.c @@ -0,0 +1,17 @@ +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include +#include + +int main() +{ + int short_run, i = 0; + // Currently not able to detect this as terminating due to multiple conditions + while (i < 90 && short_run != 1) + { + i++; + if (rand()) + { + short_run = 1; + } + } +} \ No newline at end of file diff --git a/tests/regression/78-termination/24-upjumping-goto-loopless-terminating.c b/tests/regression/78-termination/24-upjumping-goto-loopless-terminating.c new file mode 100644 index 0000000000..ce257d11ef --- /dev/null +++ b/tests/regression/78-termination/24-upjumping-goto-loopless-terminating.c @@ -0,0 +1,21 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// The program terminates but the analysis is currently only meant to detect up-jumping gotos as potentially NonTerminating, therefore we expect an NonTerm +#include + +int main() +{ // Currently not able to detect up-jumping loop free gotos + goto mark2; + +mark1: + printf("This is mark1\n"); + goto mark3; + +mark2: + printf("This is mark2\n"); + goto mark1; // NONTERMGOTO termination analysis shall mark goto statement up-jumping goto + +mark3: + printf("This is mark3\n"); + + return 0; +} diff --git a/tests/regression/78-termination/25-leave-loop-goto-terminating.c b/tests/regression/78-termination/25-leave-loop-goto-terminating.c new file mode 100644 index 0000000000..b882759bff --- /dev/null +++ b/tests/regression/78-termination/25-leave-loop-goto-terminating.c @@ -0,0 +1,28 @@ +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int counter = 0; + + while (1) + { + counter++; + + // Dummy code + printf("Iteration %d\n", counter); + int result = counter * 2; + printf("Result: %d\n", result); + + // Condition to terminate the loop + if (result >= 10) + { // Apron is not able to detect this + goto end; + } + } + +end: + printf("Loop exited. Result is greater than or equal to 10.\n"); + + return 0; +} diff --git a/tests/regression/78-termination/26-enter-loop-goto-terminating.c b/tests/regression/78-termination/26-enter-loop-goto-terminating.c new file mode 100644 index 0000000000..aa85f22b3e --- /dev/null +++ b/tests/regression/78-termination/26-enter-loop-goto-terminating.c @@ -0,0 +1,31 @@ +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int counter = 0; + + goto jump_point; + + while (1) + { + counter++; + + // Dummy code + printf("Iteration %d\n", counter); + int result = counter * 2; + jump_point: + printf("Result: %d\n", result); + + // Condition to terminate the loop + if (result >= 10) + { // Apron is not able to detect this + goto end; + } + } + +end: + printf("Loop exited. Result is greater than or equal to 10.\n"); + + return 0; +} diff --git a/tests/regression/78-termination/27-upjumping-goto-nonterminating.c b/tests/regression/78-termination/27-upjumping-goto-nonterminating.c new file mode 100644 index 0000000000..e0eb633b11 --- /dev/null +++ b/tests/regression/78-termination/27-upjumping-goto-nonterminating.c @@ -0,0 +1,21 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + goto mark2; + +mark1: + printf("This is mark1\n"); + goto mark3; + +mark2: + printf("This is mark2\n"); + goto mark1; // NONTERMGOTO termination analysis shall mark goto statement up-jumping goto + +mark3: + printf("This is mark3\n"); + goto mark1; // NONTERMGOTO termination analysis shall mark goto statement up-jumping goto + + return 0; +} diff --git a/tests/regression/78-termination/28-do-while-continue-terminating.c b/tests/regression/78-termination/28-do-while-continue-terminating.c new file mode 100644 index 0000000000..a61174d295 --- /dev/null +++ b/tests/regression/78-termination/28-do-while-continue-terminating.c @@ -0,0 +1,99 @@ +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i = 1; + + do + { + i++; + printf("Inside the do-while loop\n"); + if (i % 2 == 0) + { + + printf("Skipping %i is even\n", i); + continue; // This is handled as an goto to line 8 and therefore an up-jumping goto + } + } while (i <= 5); + + printf("Exited the loop\n"); + return 0; +} + +/* +NOTE: +Test 28: does not terminate but should terminate (test case +"28-do-while-continue-terminating.c") Reason: upjumping goto + +If one has a look at the generated CIL output (attached at the bottom of this +file), one can see that the "continue" is translated in a "goto" with a +corresponding label "__Cont". This label points to the loop-exit condition. +Since the condition is part of the loop, its location is evaluated to 8-17. The +location of the goto "goto __Cont" is located in line 15. To provide soundness +for the analysis, the preprocessing detects upjumping gotos with the help of its +location. In case such a goto is detected, the program is classified as +non-terminating. Due to this inserted goto (which is a result of the +"continue"), an upjumping goto is located, which makes this program +non-terminating. + +It should be noted that this issue happens when "do while"-loops and "continues" +are combined. If one combines "while"-loops and "continues", the analysis can +still classify the loop as terminating. The reason for that can be seen in the +second CIL output, where the "do while"-loop is replaced by a "while"-loop. +Instead of creating a new label, the "while-continue" label of the loop is +reused. Also, this goto statement is not specified as a goto, but as a Continue +statement. Hence, it is not analyzed for the upjumping gotos, which does not +lead to the problem as with the "do while". + + +------- SHORTENED CIL output for Test 28 (DO WHILE): ------- +int main(void) +{{{{ + #line 8 + while (1) { + while_continue: ; + #line 12 + if (i % 2 == 0) { + #line 15 + goto __Cont; + } + __Cont: + #line 8 + if (! (i <= 5)) { + #line 8 + goto while_break; + } + } + + while_break: + }} + #line 20 + return (0); +}} + + +------- SHORTENED CIL output for Test 28 (WHILE): ------- +Test 28: replacing DO WHILE with WHILE: int main(void) +{{{{ + #line 8 + while (1) { + while_continue: ; + #line 8 + if (! (i <= 5)) { + #line 8 + goto while_break; + } + #line 12 + if (i % 2 == 0) { + #line 15 + goto while_continue; + } + } + while_break: ; + }} + #line 20 + return (0); +}} + +*/ diff --git a/tests/regression/78-termination/29-do-while-continue-nonterminating.c b/tests/regression/78-termination/29-do-while-continue-nonterminating.c new file mode 100644 index 0000000000..dd931c012f --- /dev/null +++ b/tests/regression/78-termination/29-do-while-continue-nonterminating.c @@ -0,0 +1,22 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i = 1; + + do + { + printf("Inside the do-while loop\n"); + i++; + + if (i % 2) + { + printf("Continue as %i is odd\n", i); + continue; + } + } while (i >= 2); // NONTERMLOOP termination analysis shall mark beginning of while as non-terminating loop + + printf("Exited the loop\n"); + return 0; +} diff --git a/tests/regression/78-termination/30-goto-out-of-inner-loop-terminating.c b/tests/regression/78-termination/30-goto-out-of-inner-loop-terminating.c new file mode 100644 index 0000000000..c07b558d07 --- /dev/null +++ b/tests/regression/78-termination/30-goto-out-of-inner-loop-terminating.c @@ -0,0 +1,36 @@ +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int rows = 5; + int columns = 5; + + // Outer loop for rows + for (int i = 1; i <= rows; i++) + { + // Inner loop for columns + for (int j = 1; j <= columns; j++) + { + if (j == 3) + { + goto outer_loop; // Jump to the label "outer_loop" + } + printf("(%d, %d) ", i, j); + } + printf("Not Skipped?\n"); + outer_loop:; // Label for the outer loop + printf("Skipped!\n"); + } + + return 0; +} + +/* +NOTE: In case we do NOT assume no-overflow: +Test 30: terminates (test case "30-goto-out-of-inner-loop-terminating.c") +Test 35: does not terminate (test case +"35-goto-out-of-inner-loop-with-print-terminating.c") + +The reason is explained in "35-goto-out-of-inner-loop-with-print-terminating.c" +*/ diff --git a/tests/regression/78-termination/31-goto-out-of-inner-loop-nonterminating.c b/tests/regression/78-termination/31-goto-out-of-inner-loop-nonterminating.c new file mode 100644 index 0000000000..f9b9275620 --- /dev/null +++ b/tests/regression/78-termination/31-goto-out-of-inner-loop-nonterminating.c @@ -0,0 +1,27 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int rows = 5; + int columns = 5; + + // Outer loop for rows + for (int i = 1; 1; i++) // NONTERMLOOP termination analysis shall mark beginning of for as non-terminating loop + { + // Inner loop for columns + for (int j = 1; j <= columns; j++) + { + if (j == 3) + { + printf("Goto as continue for outer loop\n"); + goto outer_loop; + } + printf("(%d, %d) ", i, j); + } + printf("\n"); + outer_loop:; // Label for the outer loop + } + + return 0; +} diff --git a/tests/regression/78-termination/32-multithread-terminating.c b/tests/regression/78-termination/32-multithread-terminating.c new file mode 100644 index 0000000000..eb8b796a47 --- /dev/null +++ b/tests/regression/78-termination/32-multithread-terminating.c @@ -0,0 +1,30 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +// The program terminates but as the termination analysis is meant to not handle multithreaded programs we expect NonTerm here +#include +#include +#include + +// Thread function +void *printPID(void *arg) +{ + pid_t pid = getpid(); + pthread_t tid = pthread_self(); + printf("Thread ID: %lu, Process ID: %d\n", (unsigned long)tid, pid); + return NULL; +} + +int main() +{ + // Create three threads + pthread_t thread1, thread2, thread3; + pthread_create(&thread1, NULL, printPID, NULL); + pthread_create(&thread2, NULL, printPID, NULL); + pthread_create(&thread3, NULL, printPID, NULL); + + // Wait for all threads to finish + pthread_join(thread1, NULL); + pthread_join(thread2, NULL); + pthread_join(thread3, NULL); + + return 0; +} diff --git a/tests/regression/78-termination/33-multithread-nonterminating.c b/tests/regression/78-termination/33-multithread-nonterminating.c new file mode 100644 index 0000000000..8a6274c7ab --- /dev/null +++ b/tests/regression/78-termination/33-multithread-nonterminating.c @@ -0,0 +1,40 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include +#include +#include +#include +#include + +// Thread function +void *printPID(void *arg) +{ + pid_t pid = getpid(); + pthread_t tid = pthread_self(); + while (1) + { + printf("Thread ID: %lu, Process ID: %d\n", (unsigned long)tid, pid); + struct timespec sleepTime; + sleepTime.tv_sec = 1; // Seconds + sleepTime.tv_nsec = + 100000000 + (rand() % 200000000); // Nanoseconds (0.1 seconds + rand) + printf("Sleep for %ld nsec\n", sleepTime.tv_nsec); + nanosleep(&sleepTime, NULL); + } + return NULL; +} + +int main() +{ + // Create three threads + pthread_t thread1, thread2, thread3; + pthread_create(&thread1, NULL, printPID, NULL); + pthread_create(&thread2, NULL, printPID, NULL); + pthread_create(&thread3, NULL, printPID, NULL); + + // Wait for all threads to finish + pthread_join(thread1, NULL); + pthread_join(thread2, NULL); + pthread_join(thread3, NULL); + + return 0; +} diff --git a/tests/regression/78-termination/34-nested-for-loop-nonterminating.c b/tests/regression/78-termination/34-nested-for-loop-nonterminating.c new file mode 100644 index 0000000000..2f21f9e996 --- /dev/null +++ b/tests/regression/78-termination/34-nested-for-loop-nonterminating.c @@ -0,0 +1,19 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int outerCount, innerCount; + + for (outerCount = 1; outerCount <= 3; outerCount++) + { + for (innerCount = 1; innerCount > 0; innerCount++) // NONTERMLOOP termination analysis shall mark beginning of for as non-terminating loop + { + printf("(%d, %d) ", outerCount, innerCount); + } + + printf("\n"); + } + + return 0; +} diff --git a/tests/regression/78-termination/35-goto-out-of-inner-loop-with-print-terminating.c b/tests/regression/78-termination/35-goto-out-of-inner-loop-with-print-terminating.c new file mode 100644 index 0000000000..4c738e1173 --- /dev/null +++ b/tests/regression/78-termination/35-goto-out-of-inner-loop-with-print-terminating.c @@ -0,0 +1,42 @@ +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set "ana.activated[+]" apron --enable ana.int.interval --set ana.apron.domain polyhedra --set sem.int.signed_overflow assume_none +#include + +int main() +{ + int rows = 5; + int columns = 5; + + // Outer loop for rows + for (int i = 1; i <= rows; i++) + { + // Inner loop for columns + for (int j = 1; j <= columns; j++) + { + if (j == 3) + { + goto outer_loop; // Jump to the label "outer_loop" + } + printf("(%d, %d) ", i, j); + } + outer_loop: // Label for the outer loop + printf("\n"); + } + + return 0; +} + +/* +NOTE: In case we do NOT assume no-overflow: +Test 30: terminates (test case "30-goto-out-of-inner-loop-terminating.c") +Test 35: does not terminate (test case +"35-goto-out-of-inner-loop-with-print-terminating.c") + +The only difference between Test 30 and Test 35 is line 17. Test 30 has an +additional statement, and Test 35 continues already with the label. This +difference in Test 35 leads to an overflow in line 11, and hence to the +non-termination. This overflow is created by a WPoint Issue. By enabling the +no-overflow option this issue can be fixed and, both test cases are correctly +detected as terminating. + +(The overflow also happens without the termination analysis enabled.) +*/ diff --git a/tests/regression/78-termination/36-recursion-terminating.c b/tests/regression/78-termination/36-recursion-terminating.c new file mode 100644 index 0000000000..179efabeea --- /dev/null +++ b/tests/regression/78-termination/36-recursion-terminating.c @@ -0,0 +1,25 @@ +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +void recursiveFunction(int n) +{ + // Base case: When n reaches 0, stop recursion + if (n == 0) + { + printf("Terminating recursion\n"); + return; + } + + printf("Recursive call with n = %d\n", n); + + // Recursive call: Decrement n and call the function again + recursiveFunction(n - 1); +} + +int main() +{ + // Call the recursive function with an initial value + recursiveFunction(5); + + return 0; +} diff --git a/tests/regression/78-termination/37-recursion-nonterminating.c b/tests/regression/78-termination/37-recursion-nonterminating.c new file mode 100644 index 0000000000..c47fbcdd49 --- /dev/null +++ b/tests/regression/78-termination/37-recursion-nonterminating.c @@ -0,0 +1,25 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra --enable ana.context.widen +#include + +void recursiveFunction(int n) // NONTERMFUNDEC termination analysis shall mark fundec of non-terminating function +{ + // Base case: When n reaches 0, stop recursion + if (n == 30) + { + printf("Terminating recursion\n"); + return; + } + + printf("Recursive call with n = %d\n", n); + + // Recursive call: Decrement n and call the function again + recursiveFunction(n - 1); +} + +int main() +{ + // Call the recursive function with an initial value + recursiveFunction(5); + + return 0; +} diff --git a/tests/regression/78-termination/38-recursion-nested-terminating.c b/tests/regression/78-termination/38-recursion-nested-terminating.c new file mode 100644 index 0000000000..a471cfc386 --- /dev/null +++ b/tests/regression/78-termination/38-recursion-nested-terminating.c @@ -0,0 +1,41 @@ +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +void innerRecursiveFunction(int n) +{ + if (n == 0) + { + printf("Terminating inner recursion\n"); + return; + } + + printf("Inner recursive call with n = %d\n", n); + + // Recursive call to the innerRecursiveFunction + innerRecursiveFunction(n - 1); +} + +void outerRecursiveFunction(int n) +{ + if (n == 0) + { + printf("Terminating outer recursion\n"); + return; + } + + printf("Outer recursive call with n = %d\n", n); + + // Recursive call to the outerRecursiveFunction + outerRecursiveFunction(n - 1); + + // Call to the innerRecursiveFunction + innerRecursiveFunction(n); +} + +int main() +{ + // Call the outerRecursiveFunction with an initial value + outerRecursiveFunction(3); + + return 0; +} diff --git a/tests/regression/78-termination/39-recursion-nested-nonterminating.c b/tests/regression/78-termination/39-recursion-nested-nonterminating.c new file mode 100644 index 0000000000..a8d7107442 --- /dev/null +++ b/tests/regression/78-termination/39-recursion-nested-nonterminating.c @@ -0,0 +1,29 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +void innerRecursiveFunction() // TODO NONTERMFUNDEC termination analysis shall mark fundec of non-terminating function but can not as dead code is not analysed +{ + printf("Nested recursive call\n"); + + // Recursive call to the innerRecursiveFunction + innerRecursiveFunction(); +} + +void outerRecursiveFunction() // NONTERMFUNDEC termination analysis shall mark fundec of non-terminating function +{ + printf("Outer recursive call\n"); + + // Recursive call to the outerRecursiveFunction + outerRecursiveFunction(); + + // Call to the innerRecursiveFunction + innerRecursiveFunction(); +} + +int main() +{ + // Call the outerRecursiveFunction + outerRecursiveFunction(); + + return 0; +} diff --git a/tests/regression/78-termination/40-multi-expression-conditions-terminating.c b/tests/regression/78-termination/40-multi-expression-conditions-terminating.c new file mode 100644 index 0000000000..80f8c5a1e8 --- /dev/null +++ b/tests/regression/78-termination/40-multi-expression-conditions-terminating.c @@ -0,0 +1,44 @@ +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + int i; + + // Loop with complex conditions + for (i = 1; i <= 10; i++) + { + if (i > 5 && i % 2 == 0) // CIL defines new jump labels to default location (-1) + { + printf("%d ", i); + } + } + printf("\n"); + + // Loop with complex conditions + i = 1; + while (i <= 10) + { + if (i > 5 && i % 2 == 0) // CIL defines new jump labels to default location (-1) + { + printf("%d ", i); + } + i++; + } + printf("\n"); + + // Loop with multiple conditions + int s = 1; + while (s <= 10 && s % 2 == 0) // CIL defines new jump labels to default location (-1) + { + printf("Loop with Multiple Conditions: %d\n", s); + s++; + } + + // Loop with multiple variables + int t, u; + for (t = 1, u = 10; t <= 5 && u >= 5; t++, u--) // CIL defines new jump labels to default location (-1) + { + printf("Loop with Multiple Variables: %d %d\n", t, u); + } +} \ No newline at end of file diff --git a/tests/regression/78-termination/41-for-continue-terminating.c b/tests/regression/78-termination/41-for-continue-terminating.c new file mode 100644 index 0000000000..d87a705868 --- /dev/null +++ b/tests/regression/78-termination/41-for-continue-terminating.c @@ -0,0 +1,27 @@ +// SKIP TODO TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() +{ + // Loop with a continue statement + for (int i = 1; i <= 10; i++) + { + if (i % 2 == 0) + { + continue; // Converted to an goto to "for" in line 7 + } + printf("%d ", i); + } + printf("\n"); + + + // Loop with a continue statement + for (int r = 1; r <= 10; r++) + { + if (r % 3 == 0) + { + continue; // Converted to an goto to "for" in line 19 + } + printf("Loop with Continue: %d\n", r); + } +} \ No newline at end of file diff --git a/tests/regression/78-termination/42-downjumping-goto-loopless-terminating.c b/tests/regression/78-termination/42-downjumping-goto-loopless-terminating.c new file mode 100644 index 0000000000..48864883f7 --- /dev/null +++ b/tests/regression/78-termination/42-downjumping-goto-loopless-terminating.c @@ -0,0 +1,19 @@ +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() { // Currently not able to detect up-jumping loop free gotos + goto mark2; + +mark1: + printf("This is mark1\n"); + goto mark3; + +mark2: + printf("This is mark2\n"); + goto mark3; + +mark3: + printf("This is mark3\n"); + + return 0; +} diff --git a/tests/regression/78-termination/43-return-from-endless-loop-terminating.c b/tests/regression/78-termination/43-return-from-endless-loop-terminating.c new file mode 100644 index 0000000000..fb48e1cdbe --- /dev/null +++ b/tests/regression/78-termination/43-return-from-endless-loop-terminating.c @@ -0,0 +1,14 @@ +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +int main() { + int i = 1; + + while (i != 0) { + printf("%d\n", i); + i++; + if (i>10) { + return 0; + } + } +} diff --git a/tests/regression/78-termination/44-recursion-multiple-functions-terminating.c b/tests/regression/78-termination/44-recursion-multiple-functions-terminating.c new file mode 100644 index 0000000000..7f9b63527e --- /dev/null +++ b/tests/regression/78-termination/44-recursion-multiple-functions-terminating.c @@ -0,0 +1,40 @@ +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +void functionB(int n); +void functionC(int n); +void functionD(int n); + +void functionA(int n) { + if (n > 0) { + printf("Function A: %d\n", n); + functionB(n - 1); + } +} + +void functionB(int n) { + if (n > 0) { + printf("Function B: %d\n", n); + functionC(n - 1); + } +} + +void functionC(int n) { + if (n > 0) { + printf("Function C: %d\n", n); + functionD(n - 1); + } +} + +void functionD(int n) { + if (n > 0) { + printf("Function D: %d\n", n); + functionA(n - 1); + } +} + +int main() { + int n = 15; + functionA(n); + return 0; +} diff --git a/tests/regression/78-termination/45-recursion-multiple-functions-nonterminating.c b/tests/regression/78-termination/45-recursion-multiple-functions-nonterminating.c new file mode 100644 index 0000000000..be47fde704 --- /dev/null +++ b/tests/regression/78-termination/45-recursion-multiple-functions-nonterminating.c @@ -0,0 +1,40 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +void functionB(int n); +void functionC(int n); +void functionD(int n); + +void functionA(int n) { + if (n > 0) { + printf("Function A: %d\n", n); + functionB(n - 1); + } +} + +void functionB(int n) { + if (n > 0) { + printf("Function B: %d\n", n); + functionC(n - 1); + } +} + +void functionC(int n) { + if (n > 0) { + printf("Function C: %d\n", n); + functionD(n + 1); + } +} + +void functionD(int n) { + if (n > 0) { + printf("Function D: %d\n", n); + functionA(n + 1); + } +} + +int main() { + int n = 15; + functionA(n); + return 0; +} diff --git a/tests/regression/78-termination/46-recursion-different-context-terminating.c b/tests/regression/78-termination/46-recursion-different-context-terminating.c new file mode 100644 index 0000000000..2fa42f58fc --- /dev/null +++ b/tests/regression/78-termination/46-recursion-different-context-terminating.c @@ -0,0 +1,32 @@ +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +void functionC(int n); + +void functionA(int n) { + if (n > 0) { + printf("Function A: %d\n", n); + functionC(n - 1); + } +} + +void functionB(int n) { + if (n > 0) { + printf("Function B: %d\n", n); + functionC(n - 1); + } +} + +void functionC(int n) { + if (n > 0) { + printf("Function C: %d\n", n); + functionC(n - 1); + } +} + +int main() { + int n = 5; + functionA(n + 1); + functionB(n + 7); + return 0; +} diff --git a/tests/regression/78-termination/47-recursion-different-context-nonterminating.c b/tests/regression/78-termination/47-recursion-different-context-nonterminating.c new file mode 100644 index 0000000000..b0e44bce92 --- /dev/null +++ b/tests/regression/78-termination/47-recursion-different-context-nonterminating.c @@ -0,0 +1,32 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include + +void functionC(int n); + +void functionA(int n) { + if (n > 0) { + printf("Function A: %d\n", n); + functionC(n - 1); + } +} + +void functionB(int n) { + if (n > 0) { + printf("Function B: %d\n", n); + functionC(n - 1); + } +} + +void functionC(int n) { + if (n > 0) { + printf("Function C: %d\n", n); + functionC(n); + } +} + +int main() { + int n = 5; + functionA(n + 1); + functionB(n + 7); + return 0; +} diff --git a/tests/regression/78-termination/48-dynamic-recursion-nonterminating.c b/tests/regression/78-termination/48-dynamic-recursion-nonterminating.c new file mode 100644 index 0000000000..d54c49fb43 --- /dev/null +++ b/tests/regression/78-termination/48-dynamic-recursion-nonterminating.c @@ -0,0 +1,10 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +void troll(void (*f) ()) +{ + f(f); +} + +int main() +{ + troll(troll); +} diff --git a/tests/regression/78-termination/49-longjmp.c b/tests/regression/78-termination/49-longjmp.c new file mode 100644 index 0000000000..be13cb286c --- /dev/null +++ b/tests/regression/78-termination/49-longjmp.c @@ -0,0 +1,11 @@ +// SKIP NONTERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain polyhedra +#include +jmp_buf buf; +int main() +{ + if(setjmp(buf)) { + + } + + longjmp(buf, 1); +} diff --git a/tests/regression/78-termination/50-decreasing-signed-int.c b/tests/regression/78-termination/50-decreasing-signed-int.c new file mode 100644 index 0000000000..01daa5ee21 --- /dev/null +++ b/tests/regression/78-termination/50-decreasing-signed-int.c @@ -0,0 +1,13 @@ +// SKIP TERM PARAM: --set "ana.activated[+]" termination --set ana.activated[+] apron --enable ana.int.interval --set ana.apron.domain octagon +int main() +{ + int x; + + if(x <= 0){ + return 0; + } + while (x > 0) { + x = x - 1; + } + return 0; +} diff --git a/tests/regression/78-termination/51-modulo.c b/tests/regression/78-termination/51-modulo.c new file mode 100644 index 0000000000..5f5b8f1924 --- /dev/null +++ b/tests/regression/78-termination/51-modulo.c @@ -0,0 +1,14 @@ +// SKIP TERM PARAM: --enable ana.autotune.enabled --enable ana.sv-comp.functions --enable ana.sv-comp.enabled --set ana.autotune.activated "['congruence']" --set ana.specification "CHECK( init(main()), LTL(F end) )" + +// This task previously crashed due to the autotuner +int main() { + int a; + int odd, count = 0; + while(a > 1) { + odd = a % 2; + if(!odd) a = a / 2; + else a = a - 1; + count++; + } + return count; +} diff --git a/unittest/dune b/unittest/dune index 7313aa964b..a08a4b2323 100644 --- a/unittest/dune +++ b/unittest/dune @@ -2,7 +2,7 @@ (test (name mainTest) - (libraries ounit2 qcheck-ounit goblint.lib goblint.sites.dune goblint.build-info.dune) + (libraries ounit2 qcheck-ounit goblint.std goblint.lib goblint.sites.dune goblint.build-info.dune) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall)) diff --git a/unittest/util/intOpsTest.ml b/unittest/util/intOpsTest.ml index 611f2f546f..307d9e84b0 100644 --- a/unittest/util/intOpsTest.ml +++ b/unittest/util/intOpsTest.ml @@ -1,4 +1,5 @@ open OUnit2 +open Goblint_std open Goblint_lib (* If the first operand of a div is negative, Zarith rounds the result away from zero. @@ -10,13 +11,13 @@ let old_div a b = if Z.lt a Z.zero then Z.neg (Z.ediv (Z.neg a) b) else Z.ediv a let old_rem a b = Z.sub a (Z.mul b (old_div a b)) let test_bigint_div = - QCheck.(Test.make ~name:"div" (pair MyCheck.Arbitrary.big_int MyCheck.Arbitrary.big_int) (fun (x, y) -> + QCheck.(Test.make ~name:"div" (pair GobQCheck.Arbitrary.big_int GobQCheck.Arbitrary.big_int) (fun (x, y) -> assume (Z.compare y Z.zero <> 0); Z.equal (Z.div x y) (old_div x y) )) let test_bigint_rem = - QCheck.(Test.make ~name:"rem" (pair MyCheck.Arbitrary.big_int MyCheck.Arbitrary.big_int) (fun (x, y) -> + QCheck.(Test.make ~name:"rem" (pair GobQCheck.Arbitrary.big_int GobQCheck.Arbitrary.big_int) (fun (x, y) -> assume (Z.compare y Z.zero <> 0); Z.equal (Z.rem x y) (old_rem x y) )) From 8d2f9d567aa39848c27a3f0bee1c920511558671 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 10 Dec 2023 17:38:39 +0100 Subject: [PATCH 040/280] Debugged functions to change dimension of the array --- .../linearTwoVarEqualityAnalysis.apron.ml | 14 ++++++++++-- .../apron/linearTwoVarEqualityDomain.apron.ml | 22 ++++++++++++++----- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml index c79ed2898d..29ba24f829 100644 --- a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml +++ b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml @@ -68,8 +68,8 @@ let test2 () = let test = D.assign_texpr varM_test x1 (Texpr1.Cst (Coeff.s_of_int 5)) in (*x1 = 5*) let test = D.assign_var test x3 x2 in (*x3 = x2*) let test = D.assign_var test x4 x2 in (*x4 = x2*) - let test' = D.assign_texpr varM_test' x1' (Texpr1.Cst (Coeff.s_of_int 3)) in (*x1' = 5*) - let test' = D.assign_var test' x2 x1' in (*x2' = x1'*) + let test' = D.assign_texpr varM_test' x1' (Texpr1.Cst (Coeff.s_of_int 3)) in (*x1' = 3*) + let test' = D.assign_var test' x2 x1' in (*x2 = x1'*) (*let test = D.assign_texpr test x2 (Texpr1.Cst (Coeff.s_of_int 5)) in (*x2 = 5*)*) print_string "Test2:\n"; print_string "Original variable setup:\n"; @@ -82,6 +82,16 @@ let test2 () = print_string @@ D.show test'; print_string "Meet environments:\n"; print_string @@ D.show (D.meet test test'); + print_string "change_d:\n"; + let sup_env = Environment.lce test.env test'.env in + print_string @@ D.show (VarManagement.change_d test sup_env true false); + print_string "reduce_col:\n"; + let sup_env = Environment.lce test.env test'.env in + let after_change_d = VarManagement.change_d test sup_env true false in + print_string @@ D.show ({d = Some ((EqualitiesArray.reduce_col (Option.get after_change_d.d) 3)); env = sup_env}); + print_string "drop_vars:\n"; + let sup_env = Environment.lce test.env test'.env in + print_string @@ D.show (VarManagement.drop_vars (VarManagement.change_d test sup_env true false) [x1'; x2'] true); print_string "Test2 completed\n" let after_config () =(* diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 9c0e846b32..2ec658412e 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -110,10 +110,15 @@ module EqualitiesArray = struct if nnc = 0 then m else let nc = length m in let m' = make_empty_array (nc + nnc) in + let offset_map = Array.make nc 0 in + let add_offset_to_array_entry (var, offs) = match var with + | None -> (var, offs) + | Some var_index -> (Some (var_index + offset_map.(var_index)), offs) in let offset = ref 0 in for j = 0 to nc - 1 do - while !offset < nnc && !offset + j = indexes.(!offset) do incr offset done; - m'.(j + !offset) <- m.(j); + while !offset < nnc && !offset + j = indexes.(!offset) do incr offset; done; + offset_map.(j) <- !offset; + m'.(j + !offset) <- add_offset_to_array_entry m.(j); done; m' @@ -124,10 +129,15 @@ module EqualitiesArray = struct let m_c = length m in if m_c = n_c then [||] else let m' = make_empty_array (m_c - n_c) in + let offset_map = Array.make m_c 0 in + let remove_offset_from_array_entry (var, offs) = match var with + | None -> (var, offs) + | Some var_index -> (Some (var_index - offset_map.(var_index)), offs) in let offset = ref 0 in for j = 0 to (m_c - n_c) - 1 do - while !offset < n_c && !offset + j = cols.(!offset) do incr offset done; - m'.(j) <- m.(j + !offset); + while !offset < n_c && !offset + j = cols.(!offset) do incr offset; done; + offset_map.(j + !offset) <- !offset; + m'.(j) <- remove_offset_from_array_entry m.(j + !offset); done; m' @@ -164,12 +174,12 @@ module EqualitiesArray = struct let dim_of_var = Some var in let connected_component = find_vars_in_the_connected_component d dim_of_var in if length connected_component = 1 - then () (* x_i is the only element of its connected component *) + then () (* x_i is the only element of its connected component *) else (* x_i is the reference variable -> we need to find a new reference variable *) let var_least_index = Option.get @@ find_var_in_the_connected_component_with_least_index connected_component ref_var in let (_, off) = d.(var_least_index) in - iteri (fun _ x -> let (_, off2) = d.(x) in d.(x) <- (Some var_least_index, Z.(off2 - off))) connected_component; + iteri (fun _ x -> let (_, off2) = d.(x) in if x <> ref_var then d.(x) <- (Some var_least_index, Z.(off2 - off))) connected_component; end (* Forget information about variable i but not in-place *) From 0a5eb8e15176a3599e8d208742882e33af3ed85c Mon Sep 17 00:00:00 2001 From: nicolai frech Date: Wed, 13 Dec 2023 03:04:56 +0100 Subject: [PATCH 041/280] changed leq in linearTwoVarEqualityDomain.apron.ml changed the condition for implication of a single equality in the less or equal function. A conjunction of equalities might represent a set of states that is a subset of another set of states and its corresponding representation as a conjunction of equalities. In particular, an assignment to a variable only consisting of a constant offset implies, say, the trivial equality, which is satisfied for any assignment to the same variable. The change correctly classifies this kind of implication. --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 2ec658412e..323f723423 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -480,6 +480,7 @@ struct | _ -> false) | (Some j, b) -> (match ts.(i), ts.(j) with + | (None, b1), (None, b2) -> Z.equal b1 (Z.add b2 b) | (None, _), (_, _) -> false | (_, _), (None, _) -> false | (Some h1, b1), (Some h2, b2) -> From d8111b890258dc9dc55d0bad4d3b31af85f2d0f8 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 13 Dec 2023 11:53:28 +0100 Subject: [PATCH 042/280] Adjustments meet_tcons --- .../linearTwoVarEqualityAnalysis.apron.ml | 21 ++++- .../apron/linearTwoVarEqualityDomain.apron.ml | 84 +++++++++++-------- 2 files changed, 68 insertions(+), 37 deletions(-) diff --git a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml index 29ba24f829..3aa52d8492 100644 --- a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml +++ b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml @@ -94,10 +94,27 @@ let test2 () = print_string @@ D.show (VarManagement.drop_vars (VarManagement.change_d test sup_env true false) [x1'; x2'] true); print_string "Test2 completed\n" + let test3 () = + let x = Apron.Var.of_string "x" in + let y = Apron.Var.of_string "y" in + let env_test = Apron.Environment.make (Array.of_list [x; y]) @@ Array.of_list [] in + let varM_test = D.top_env env_test in + let test = D.assign_texpr varM_test x (Texpr1.Cst (Coeff.s_of_int 1)) in (*x = 1*) + let test = D.assign_texpr test y (Texpr1.Cst (Coeff.s_of_int 1)) in (*y = 1*) + let tcons1 = Apron.Tcons1.make (Texpr1.of_expr env_test (Binop (Sub, Var x, Var y, Int, Near ))) EQ in + print_string "Test1:\n"; + print_string "Original variable setup:\n"; + print_string @@ D.show varM_test ; + print_string "After x = 1 and y=1:\n"; + print_string @@ D.show test; + print_string "Tcons:\n"; + print_string @@ D.show @@ D.meet_tcons test tcons1 " "; + print_string "Test1 completed\n" + let after_config () =(* - test2(); + test3(); failwith "No error Test completed"; - *) +*) let module Spec = (val get_spec ()) in MCP.register_analysis (module Spec : MCPSpec); GobConfig.set_string "ana.path_sens[+]" (Spec.name ()) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 323f723423..8f3a64de1b 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -358,41 +358,41 @@ struct module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (SharedFunctions.Tracked) type var = V.t - (* prints the current variable equalities with resolved variable names *) - let show varM = - let lookup i = Var.to_string (Environment.var_of_dim varM.env i) in - let show_var i tuple = - match tuple with - | (None, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ Z.to_string offset ^ "\n" - | (Some index, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ lookup index ^ " + " ^ Z.to_string offset ^ "\n" - in match varM.d with - | None -> "No equalities available" - | Some arr -> Array.fold_left (fun acc elem -> acc ^ elem ) "" (Array.mapi show_var arr) - - - let pretty () (x:t) = text (show x) - let printXml f x = BatPrintf.fprintf f "\n\n\nmatrix\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) - let name () = "lin2vareq" let to_yojson _ = failwith "ToDo Implement in future" - - + let is_bot t = equal t (bot ()) + let is_bot_env t = t.d = None - let bot_env = {d = None; env = Environment.make [||] [||]} let top_env env = {d = Some (Array.init (Environment.size env) (fun i -> (Some i, Z.zero))); env = env} - let is_bot_env t = t.d = None (*Would the top not be the identity matrix in affineEq? i.e. the array where each variable is assigned itself with no other coeffcients? *) let top () = failwith "D.top ()" - let is_top _ = false + let is_top varM = if Option.is_some varM.d then + EArray.fold_lefti (fun b i (a, e) -> if Z.(e == Z.zero) && Option.is_some a && Option.get a == i then b else false) true (Option.get varM.d) + else false let is_top_env t = (not @@ Environment.equal empty_env t.env) && GobOption.exists EArray.is_empty t.d + (* prints the current variable equalities with resolved variable names *) + let show varM = + let lookup i = Var.to_string (Environment.var_of_dim varM.env i) in + let show_var i tuple = + match tuple with + | (None, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ Z.to_string offset ^ "\n" + | (Some index, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ lookup index ^ " + " ^ Z.to_string offset ^ "\n" + in if is_top varM then "⊤\n" else + match varM.d with + | None -> "Bot Env\n" + | Some arr -> if EArray.is_empty arr then "Bot \n" else Array.fold_left (fun acc elem -> acc ^ elem ) "" (Array.mapi show_var arr) + + let pretty () (x:t) = text (show x) + let printXml f x = BatPrintf.fprintf f "\n\n\nmatrix\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) + let meet t1 t2 = let sup_env = Environment.lce t1.env t2.env in let t1, t2 = change_d t1 sup_env true false, change_d t2 sup_env true false in @@ -815,15 +815,24 @@ struct tcons -> tree constraint (expression < 0) -> does not have types (overflow is type dependent) *) - - - let meet_tcons t tcons expr = + let print_coeff_vec l (env : Environment.t) = + let print_element _ e = match e with + | (a, Some x) -> print_string ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env x)) ^ " + ") + | (a, None) -> print_string ((Z.to_string a) ^ "+") in + List.fold_left print_element () l; print_newline () + + let print_final_expr l (env : Environment.t) = + let print_element _ i a = if i == 0 then print_string ((Z.to_string a) ^ " + ") else + print_string ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env (i-1))) ^ " + ") in + List.fold_lefti print_element () l; print_newline () + + let meet_tcons t tcons _ = (* The expression is evaluated using an array of coefficients. The first element of the array belongs to the constant followed by the coefficients of all variables depending on the result in the array after the evaluating including resolving the constraints in t.d the tcons can be evaluated and additional constraints can be added to t.d *) let expr_init = Array.init ((Environment.size t.env) +1) (fun _ -> Z.zero) in match t.d with - | None -> t - | Some d -> + | None -> bot () + | Some d -> if is_bot t then bot () else let cv's = get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) in let update (expr : Z.t Array.t)( c , v) = match v with @@ -833,14 +842,19 @@ struct | (None, c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; expr in let final_expr = List.fold_left (fun expr cv -> update expr cv ) expr_init cv's in - let is_constant = List.fold_left (fun b a -> if Z.equal a Z.zero then b else false) true @@ List.tl @@ Array.to_list final_expr in - let var_count = List.count_matching (fun a -> if Z.equal a Z.zero then false else true) @@ List.tl @@ Array.to_list final_expr - in if is_constant then + let var_count = List.count_matching (fun a -> if Z.equal a Z.zero then false else true) ( List.tl ( Array.to_list final_expr)) + in (* + print_string "Meet_tcons:\n"; + print_coeff_vec cv's t.env; + print_final_expr (Array.to_list final_expr) t.env; + print_string ("Meet_tcons var_count is " ^ (Int.to_string var_count) ^ " and the type is " ^(Lincons0.string_of_typ (Tcons1.get_typ tcons)) ^"\n"); + *) + if var_count == 0 then match Tcons1.get_typ tcons with - | EQ -> if Z.equal final_expr.(0) Z.zero then t else bot() - | SUPEQ -> if Z.geq final_expr.(0) Z.zero then t else bot() - | SUP -> if Z.gt final_expr.(0) Z.zero then t else bot() - | DISEQ -> if Z.equal final_expr.(0) Z.zero then bot() else t + | EQ -> if Z.equal final_expr.(0) Z.zero then t else bot_env + | SUPEQ -> if Z.geq final_expr.(0) Z.zero then t else bot_env + | SUP -> if Z.gt final_expr.(0) Z.zero then t else bot_env + | DISEQ -> if Z.equal final_expr.(0) Z.zero then bot_env else t | EQMOD scalar -> t (*Not supported right now if Float.equal ( Float.modulo (Z.to_float final_expr.(0)) (convert_scalar scalar )) 0. then t else {d = None; env = t.env}*) else if var_count == 1 then @@ -922,10 +936,10 @@ struct *) let assert_cons d e negate no_ov = let no_ov = Lazy.force no_ov in - if M.tracing then M.tracel "assert_cons" "assert_cons with expr: %a %b" d_exp e no_ov; + if M.tracing then M.tracel "assert_cons" "assert_cons with expr: %a %b\n" d_exp e no_ov; match Convert.tcons1_of_cil_exp d d.env e negate no_ov with - | tcons1 -> meet_tcons d tcons1 e - | exception Convert.Unsupported_CilExp _ -> d + | tcons1 -> let t = meet_tcons d tcons1 e in Pretty.printf "assert_cons with expr: %a\n" d_exp e; print_string @@ show t;t + | exception Convert.Unsupported_CilExp _ -> d let assert_cons d e negate no_ov = timing_wrap "assert_cons" (assert_cons d e negate) no_ov From 504f05bfd7a935c403cb207bc1b331abbb97f77c Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 13 Dec 2023 12:00:14 +0100 Subject: [PATCH 043/280] fixed top and bug in assign_texpr --- .../apron/linearTwoVarEqualityDomain.apron.ml | 104 +++++++++--------- 1 file changed, 54 insertions(+), 50 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 8f3a64de1b..22990e547b 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -145,6 +145,8 @@ module EqualitiesArray = struct let is_empty m = length m = 0 + let is_top_array m = m = make_empty_array (length m) + let find_reference_variable d var_index = fst d.(var_index) @@ -335,7 +337,7 @@ module ExpressionBounds: (SharedFunctions.ConvBounds with type t = VarManagement struct include VarManagement - let bound_texpr t texpr = Some Z.zero, Some Z.zero (*TODO*) + let bound_texpr t texpr = Some (Z.of_int (-1000)), Some (Z.of_int (1000)) (*TODO*) let bound_texpr d texpr1 = @@ -358,10 +360,11 @@ struct module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (SharedFunctions.Tracked) type var = V.t + let name () = "lin2vareq" let to_yojson _ = failwith "ToDo Implement in future" - + let is_bot t = equal t (bot ()) let is_bot_env t = t.d = None @@ -376,7 +379,7 @@ struct EArray.fold_lefti (fun b i (a, e) -> if Z.(e == Z.zero) && Option.is_some a && Option.get a == i then b else false) true (Option.get varM.d) else false - let is_top_env t = (not @@ Environment.equal empty_env t.env) && GobOption.exists EArray.is_empty t.d + let is_top_env t = (not @@ Environment.equal empty_env t.env) && GobOption.exists EArray.is_top_array t.d (* prints the current variable equalities with resolved variable names *) let show varM = @@ -386,12 +389,12 @@ struct | (None, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ Z.to_string offset ^ "\n" | (Some index, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ lookup index ^ " + " ^ Z.to_string offset ^ "\n" in if is_top varM then "⊤\n" else - match varM.d with - | None -> "Bot Env\n" - | Some arr -> if EArray.is_empty arr then "Bot \n" else Array.fold_left (fun acc elem -> acc ^ elem ) "" (Array.mapi show_var arr) + match varM.d with + | None -> "Bot Env\n" + | Some arr -> if EArray.is_empty arr then "Bot \n" else Array.fold_left (fun acc elem -> acc ^ elem ) "" (Array.mapi show_var arr) - let pretty () (x:t) = text (show x) - let printXml f x = BatPrintf.fprintf f "\n\n\nmatrix\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) + let pretty () (x:t) = text (show x) + let printXml f x = BatPrintf.fprintf f "\n\n\nequalities-array\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) let meet t1 t2 = let sup_env = Environment.lce t1.env t2.env in @@ -704,8 +707,9 @@ struct (* Statement "assigned_var = assigned_var + off" *) subtract_const_from_var t assigned_var off else + (* Statement "assigned_var = exp_var + off" (assigned_var is not the same as exp_var) *) let empty_array = EqualitiesArray.make_empty_array (VarManagement.size t) in - let added_equality = empty_array.(exp_var) <- (Some assigned_var, off); empty_array in + let added_equality = empty_array.(assigned_var) <- (Some exp_var, off); empty_array in meet abstract_exists_var {d = Some added_equality; env = t.env} end end @@ -819,11 +823,11 @@ struct let print_element _ e = match e with | (a, Some x) -> print_string ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env x)) ^ " + ") | (a, None) -> print_string ((Z.to_string a) ^ "+") in - List.fold_left print_element () l; print_newline () + List.fold_left print_element () l; print_newline () let print_final_expr l (env : Environment.t) = let print_element _ i a = if i == 0 then print_string ((Z.to_string a) ^ " + ") else - print_string ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env (i-1))) ^ " + ") in + print_string ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env (i-1))) ^ " + ") in List.fold_lefti print_element () l; print_newline () let meet_tcons t tcons _ = @@ -833,49 +837,49 @@ struct match t.d with | None -> bot () | Some d -> if is_bot t then bot () else - let cv's = get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) in - let update (expr : Z.t Array.t)( c , v) = - match v with - | None -> Array.set expr 0 (Z.add expr.(0) c) ; expr - | Some idx -> match d.(idx) with - | (Some idx_i,c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; Array.set expr (idx_i + 1) (Z.add expr.(idx_i + 1) c_i) ; expr - | (None, c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; expr - in - let final_expr = List.fold_left (fun expr cv -> update expr cv ) expr_init cv's in - let var_count = List.count_matching (fun a -> if Z.equal a Z.zero then false else true) ( List.tl ( Array.to_list final_expr)) - in (* + let cv's = get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) in + let update (expr : Z.t Array.t)( c , v) = + match v with + | None -> Array.set expr 0 (Z.add expr.(0) c) ; expr + | Some idx -> match d.(idx) with + | (Some idx_i,c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; Array.set expr (idx_i + 1) (Z.add expr.(idx_i + 1) c_i) ; expr + | (None, c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; expr + in + let final_expr = List.fold_left (fun expr cv -> update expr cv ) expr_init cv's in + let var_count = List.count_matching (fun a -> if Z.equal a Z.zero then false else true) ( List.tl ( Array.to_list final_expr)) + in (* print_string "Meet_tcons:\n"; print_coeff_vec cv's t.env; print_final_expr (Array.to_list final_expr) t.env; print_string ("Meet_tcons var_count is " ^ (Int.to_string var_count) ^ " and the type is " ^(Lincons0.string_of_typ (Tcons1.get_typ tcons)) ^"\n"); *) - if var_count == 0 then - match Tcons1.get_typ tcons with - | EQ -> if Z.equal final_expr.(0) Z.zero then t else bot_env - | SUPEQ -> if Z.geq final_expr.(0) Z.zero then t else bot_env - | SUP -> if Z.gt final_expr.(0) Z.zero then t else bot_env - | DISEQ -> if Z.equal final_expr.(0) Z.zero then bot_env else t - | EQMOD scalar -> t (*Not supported right now - if Float.equal ( Float.modulo (Z.to_float final_expr.(0)) (convert_scalar scalar )) 0. then t else {d = None; env = t.env}*) - else if var_count == 1 then - let var = List.findi (fun i a -> if Z.equal a Z.zero then false else true) @@ Array.to_list final_expr in - let c = if Z.divisible final_expr.(0) @@ Tuple2.second var then Some (Z.(- final_expr.(0) / (Tuple2.second var))) else None in - match Tcons1.get_typ tcons with - | EQ -> if Option.is_none c then t else - let expr = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int (Option.get c)) in - meet t (assign_texpr (top_env t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expr) - | _ -> t (*Not supported right now*) - else if var_count == 2 then - let v12 = List.fold_righti (fun i a l -> if Z.equal a Z.zero then l else (i,a)::l) (List.tl @@ Array.to_list final_expr) [] in - let a1 = Tuple2.second (List.hd v12) in - let a2 = Tuple2.second (List.hd v12) in - let var1 = Environment.var_of_dim t.env (Tuple2.first (List.hd v12)) in - let var2 = Environment.var_of_dim t.env (Tuple2.first (List.hd @@ List.tl v12)) in - match Tcons1.get_typ tcons with - | EQ -> if Z.equal a1 Z.one && Z.equal a2 Z.one then meet t (assign_var (top_env t.env) var1 var2) else t - | _-> t (*Not supported right now*) - else - t (*For any other case we don't know if the (in-) equality is true or false or even possible therefore we just return t *) + if var_count == 0 then + match Tcons1.get_typ tcons with + | EQ -> if Z.equal final_expr.(0) Z.zero then t else bot_env + | SUPEQ -> if Z.geq final_expr.(0) Z.zero then t else bot_env + | SUP -> if Z.gt final_expr.(0) Z.zero then t else bot_env + | DISEQ -> if Z.equal final_expr.(0) Z.zero then bot_env else t + | EQMOD scalar -> t (*Not supported right now + if Float.equal ( Float.modulo (Z.to_float final_expr.(0)) (convert_scalar scalar )) 0. then t else {d = None; env = t.env}*) + else if var_count == 1 then + let var = List.findi (fun i a -> if Z.equal a Z.zero then false else true) @@ Array.to_list final_expr in + let c = if Z.divisible final_expr.(0) @@ Tuple2.second var then Some (Z.(- final_expr.(0) / (Tuple2.second var))) else None in + match Tcons1.get_typ tcons with + | EQ -> if Option.is_none c then t else + let expr = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int (Option.get c)) in + meet t (assign_texpr (top_env t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expr) + | _ -> t (*Not supported right now*) + else if var_count == 2 then + let v12 = List.fold_righti (fun i a l -> if Z.equal a Z.zero then l else (i,a)::l) (List.tl @@ Array.to_list final_expr) [] in + let a1 = Tuple2.second (List.hd v12) in + let a2 = Tuple2.second (List.hd v12) in + let var1 = Environment.var_of_dim t.env (Tuple2.first (List.hd v12)) in + let var2 = Environment.var_of_dim t.env (Tuple2.first (List.hd @@ List.tl v12)) in + match Tcons1.get_typ tcons with + | EQ -> if Z.equal a1 Z.one && Z.equal a2 Z.one then meet t (assign_var (top_env t.env) var1 var2) else t + | _-> t (*Not supported right now*) + else + t (*For any other case we don't know if the (in-) equality is true or false or even possible therefore we just return t *) @@ -939,7 +943,7 @@ struct if M.tracing then M.tracel "assert_cons" "assert_cons with expr: %a %b\n" d_exp e no_ov; match Convert.tcons1_of_cil_exp d d.env e negate no_ov with | tcons1 -> let t = meet_tcons d tcons1 e in Pretty.printf "assert_cons with expr: %a\n" d_exp e; print_string @@ show t;t - | exception Convert.Unsupported_CilExp _ -> d + | exception Convert.Unsupported_CilExp _ -> d let assert_cons d e negate no_ov = timing_wrap "assert_cons" (assert_cons d e negate) no_ov From 4178745f08439267b746a639983cb3c99d138cc2 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 13 Dec 2023 13:16:08 +0100 Subject: [PATCH 044/280] fixed bug in join where it returned an empty array instead of the top element --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 22990e547b..8d4dc03fac 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -638,7 +638,8 @@ struct in if is_bot a then b else if is_bot b then a else match Option.get a.d, Option.get b.d with - | x, y when is_top_env a || is_top_env b -> {d = Some (EArray.empty ()); env = Environment.lce a.env b.env} + | x, y when is_top_env a || is_top_env b -> let new_env = Environment.lce a.env b.env + in {d = Some (EArray.make_empty_array @@ Environment.size new_env); env = new_env} | x, y when (Environment.compare a.env b.env <> 0) -> let sup_env = Environment.lce a.env b.env in let mod_x = dim_add (Environment.dimchange a.env sup_env) x in @@ -942,7 +943,7 @@ struct let no_ov = Lazy.force no_ov in if M.tracing then M.tracel "assert_cons" "assert_cons with expr: %a %b\n" d_exp e no_ov; match Convert.tcons1_of_cil_exp d d.env e negate no_ov with - | tcons1 -> let t = meet_tcons d tcons1 e in Pretty.printf "assert_cons with expr: %a\n" d_exp e; print_string @@ show t;t + | tcons1 -> let t = meet_tcons d tcons1 e in print_string @@ show t;t | exception Convert.Unsupported_CilExp _ -> d let assert_cons d e negate no_ov = timing_wrap "assert_cons" (assert_cons d e negate) no_ov From b2e6515fcc2cbed715ca6a376aa254394c9a3f88 Mon Sep 17 00:00:00 2001 From: nicolai frech Date: Wed, 13 Dec 2023 14:03:10 +0100 Subject: [PATCH 045/280] Update linearTwoVarEqualityDomain.apron.ml --- .../apron/linearTwoVarEqualityDomain.apron.ml | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 8d4dc03fac..0e0579e901 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -476,19 +476,26 @@ struct let leq t1 t2 = let env_comp = Environment.compare t1.env t2.env in (* Apron's Environment.compare has defined return values. *) let implies ts t i : bool = - match t with - | (None, b) -> - (match ts.(i) with - | (None, b') -> Z.equal b b' + match t with + | (None, b) -> + (match ts.(i) with + | (None, b') -> Z.equal b b' + | (Some j, b') -> (match ts.(j) with + | (None, bj) -> Z.equal bj (Z.sub b b') + | _ -> false + )) + | (Some j, b) -> + (match ts.(i), ts.(j) with + | (None, b1), (None, b2) -> Z.equal b1 (Z.add b2 b) + | (Some h1, b1), (None, b2) -> (match ts.(h1) with + | (None, bh1) -> Z.equal (Z.add bh1 b1) (Z.add b2 b) | _ -> false) - | (Some j, b) -> - (match ts.(i), ts.(j) with - | (None, b1), (None, b2) -> Z.equal b1 (Z.add b2 b) - | (None, _), (_, _) -> false - | (_, _), (None, _) -> false - | (Some h1, b1), (Some h2, b2) -> - h1 = h2 && Z.equal b1 (Z.add b2 b)) - in + | (None, b1), (Some h2, b2) -> (match ts.(h2) with + | (None, bh2) -> Z.equal (Z.sub b1 bh2) (Z.add b2 b) + | _ -> false) + | (Some h1, b1), (Some h2, b2) -> + h1 = h2 && Z.equal b1 (Z.add b2 b)) + in if env_comp = -2 || env_comp > 0 then false else if is_bot t1 || is_top_env t2 then true else if is_bot t2 || is_top_env t1 then false else ( From e1a7d4dada0a668e5e9deee8c980195e31b8cddd Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 13 Dec 2023 14:36:12 +0100 Subject: [PATCH 046/280] fixed bug in meet_tcons --- .../apron/linearTwoVarEqualityDomain.apron.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 0e0579e901..99cbc8154d 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -337,7 +337,7 @@ module ExpressionBounds: (SharedFunctions.ConvBounds with type t = VarManagement struct include VarManagement - let bound_texpr t texpr = Some (Z.of_int (-1000)), Some (Z.of_int (1000)) (*TODO*) + let bound_texpr t texpr = None, None(*Some (Z.of_int (-1000)), Some (Z.of_int (1000)) TODO*) let bound_texpr d texpr1 = @@ -388,7 +388,7 @@ struct match tuple with | (None, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ Z.to_string offset ^ "\n" | (Some index, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ lookup index ^ " + " ^ Z.to_string offset ^ "\n" - in if is_top varM then "⊤\n" else + in (*if is_top varM then "⊤\n" else *) match varM.d with | None -> "Bot Env\n" | Some arr -> if EArray.is_empty arr then "Bot \n" else Array.fold_left (fun acc elem -> acc ^ elem ) "" (Array.mapi show_var arr) @@ -399,11 +399,11 @@ struct let meet t1 t2 = let sup_env = Environment.lce t1.env t2.env in let t1, t2 = change_d t1 sup_env true false, change_d t2 sup_env true false in - print_string "t1 after change_d\n"; + (*print_string "t1 after change_d\n"; print_string @@ show t1; print_string "t2 after change_d\n"; print_string @@ show t2; - print_string "end\n"; + print_string "end\n";*) let subst_var ts x t = match !ts with | None -> () @@ -646,7 +646,7 @@ struct if is_bot a then b else if is_bot b then a else match Option.get a.d, Option.get b.d with | x, y when is_top_env a || is_top_env b -> let new_env = Environment.lce a.env b.env - in {d = Some (EArray.make_empty_array @@ Environment.size new_env); env = new_env} + in (top_env new_env) | x, y when (Environment.compare a.env b.env <> 0) -> let sup_env = Environment.lce a.env b.env in let mod_x = dim_add (Environment.dimchange a.env sup_env) x in @@ -850,7 +850,7 @@ struct match v with | None -> Array.set expr 0 (Z.add expr.(0) c) ; expr | Some idx -> match d.(idx) with - | (Some idx_i,c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; Array.set expr (idx_i + 1) (Z.add expr.(idx_i + 1) c_i) ; expr + | (Some idx_i,c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; Array.set expr (idx_i + 1) (Z.add expr.(idx_i + 1) Z.one) ; expr | (None, c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; expr in let final_expr = List.fold_left (fun expr cv -> update expr cv ) expr_init cv's in @@ -950,7 +950,7 @@ struct let no_ov = Lazy.force no_ov in if M.tracing then M.tracel "assert_cons" "assert_cons with expr: %a %b\n" d_exp e no_ov; match Convert.tcons1_of_cil_exp d d.env e negate no_ov with - | tcons1 -> let t = meet_tcons d tcons1 e in print_string @@ show t;t + | tcons1 -> meet_tcons d tcons1 e | exception Convert.Unsupported_CilExp _ -> d let assert_cons d e negate no_ov = timing_wrap "assert_cons" (assert_cons d e negate) no_ov From e2e3dcacf46a51ef56b51fa5af5b5e43b9dce349 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 13 Dec 2023 14:59:04 +0100 Subject: [PATCH 047/280] This time really fixed the bug in meet_tcons --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 99cbc8154d..f1f9db7d52 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -850,7 +850,7 @@ struct match v with | None -> Array.set expr 0 (Z.add expr.(0) c) ; expr | Some idx -> match d.(idx) with - | (Some idx_i,c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; Array.set expr (idx_i + 1) (Z.add expr.(idx_i + 1) Z.one) ; expr + | (Some idx_i,c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; Array.set expr (idx_i + 1) (Z.add expr.(idx_i + 1) c) ; expr | (None, c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; expr in let final_expr = List.fold_left (fun expr cv -> update expr cv ) expr_init cv's in From d0018b9007bea47dfe7ec1065c6f837895d4a51f Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 13 Dec 2023 16:15:55 +0100 Subject: [PATCH 048/280] revert unwanted changes --- src/domain/mapDomain.ml | 20 +++++++++---------- src/domain/setDomain.ml | 6 +++--- .../73-strings/01-string_literals.c | 1 - 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/domain/mapDomain.ml b/src/domain/mapDomain.ml index 901056a5f4..740da9969e 100644 --- a/src/domain/mapDomain.ml +++ b/src/domain/mapDomain.ml @@ -138,7 +138,7 @@ end module PMap (Domain: Printable.S) (Range: Lattice.S) : PS with type key = Domain.t and -type value = Range.t = + type value = Range.t = struct module M = Map.Make (Domain) @@ -212,7 +212,7 @@ end (* TODO: why is HashCached.hash significantly slower as a functor compared to being inlined into PMap? *) module HashCached (M: S) : S with type key = M.key and -type value = M.value = + type value = M.value = struct include Lattice.HashCached (M) @@ -262,7 +262,7 @@ end (* TODO: currently hardcoded to assume_idempotent *) module HConsed (M: S) : S with type key = M.key and -type value = M.value = + type value = M.value = struct include Lattice.HConsed (M) (struct let assume_idempotent = false end) @@ -311,7 +311,7 @@ end module Timed (M: S) : S with type key = M.key and -type value = M.value = + type value = M.value = struct let time str f arg = Timing.wrap (M.name ()) (Timing.wrap str f) arg @@ -385,7 +385,7 @@ end module MapBot (Domain: Printable.S) (Range: Lattice.S) : S with type key = Domain.t and -type value = Range.t = + type value = Range.t = struct include PMap (Domain) (Range) @@ -434,7 +434,7 @@ end module MapTop (Domain: Printable.S) (Range: Lattice.S) : S with type key = Domain.t and -type value = Range.t = + type value = Range.t = struct include PMap (Domain) (Range) @@ -486,7 +486,7 @@ exception Fn_over_All of string module LiftTop (Range: Lattice.S) (M: S with type value = Range.t): S with type key = M.key and -type value = Range.t = + type value = Range.t = struct include Lattice.LiftTop (M) @@ -605,7 +605,7 @@ end module MapBot_LiftTop (Domain: Printable.S) (Range: Lattice.S) : S with type key = Domain.t and -type value = Range.t = + type value = Range.t = struct module M = MapBot (Domain) (Range) include LiftTop (Range) (M) @@ -614,7 +614,7 @@ end module LiftBot (Range: Lattice.S) (M: S with type value = Range.t): S with type key = M.key and -type value = Range.t = + type value = Range.t = struct include Lattice.LiftBot (M) @@ -733,7 +733,7 @@ end module MapTop_LiftBot (Domain: Printable.S) (Range: Lattice.S): S with type key = Domain.t and -type value = Range.t = + type value = Range.t = struct module M = MapTop (Domain) (Range) include LiftBot (Range) (M) diff --git a/src/domain/setDomain.ml b/src/domain/setDomain.ml index a84f9017f6..1b5239de80 100644 --- a/src/domain/setDomain.ml +++ b/src/domain/setDomain.ml @@ -159,7 +159,7 @@ end * calling [top ()] will raise an exception *) module Make (Base: Printable.S): S with type elt = Base.t and -type t = BatSet.Make (Base).t = (* TODO: remove, only needed in VarEq for some reason... *) + type t = BatSet.Make (Base).t = (* TODO: remove, only needed in VarEq for some reason... *) struct include Printable.Std include BatSet.Make(Base) @@ -259,7 +259,7 @@ end module LiftTop (S: S) (N: ToppedSetNames): S with type elt = S.elt and -type t = [`Top | `Lifted of S.t] = (* Expose t for HoareDomain.Set_LiftTop *) + type t = [`Top | `Lifted of S.t] = (* Expose t for HoareDomain.Set_LiftTop *) struct include Printable.Std @@ -396,7 +396,7 @@ end (** Functor for creating artificially topped set domains. *) module ToppedSet (Base: Printable.S) (N: ToppedSetNames): S with type elt = Base.t and -type t = [`Top | `Lifted of Make (Base).t] = (* TODO: don't expose t *) + type t = [`Top | `Lifted of Make (Base).t] = (* TODO: don't expose t *) struct module S = Make (Base) include LiftTop (S) (N) diff --git a/tests/regression/73-strings/01-string_literals.c b/tests/regression/73-strings/01-string_literals.c index 9ece1940c6..4a43590bf5 100644 --- a/tests/regression/73-strings/01-string_literals.c +++ b/tests/regression/73-strings/01-string_literals.c @@ -43,7 +43,6 @@ void example2() { char* s1 = "abcde"; char* s2 = "abcdfg"; char* s3 = hello_world(); - size_t len = strlen(s1); __goblint_check(len == 5); From 22e146cd178eaad98d08de0882b9b2fe100ca14e Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 13 Dec 2023 18:00:58 +0100 Subject: [PATCH 049/280] ### Bot_env vs. Bot AffineEqualities, similar to some other domains, uses `bot()` and `bot_env`. Interestingly, these two are not the same. There exists also a `top_env` and `top` though `top()` is set to fail the program when called. `top()` and `bot()` are both functions required by Goblint and define the bottom and top type of the domain while disregarding the environment. As a result, some domains implement their own bottom and top types which take the environment into account which are then called `top_env` and `bot_env`. The two different versions of bottom and top should never be mixed because they can vary quite a lot. It was attempted to merge these two states but as the insight for this seperation of environment depended types is missing, this attempt failed. So far we clearified our representation of bottom and top in the domain and made sure that only `bot_env` and `top_env` as well as their respective boolean functions are used by the domain. So far, rudementarry tests are working and also guards seem to be evaluated correctly (see test 77/14), but further tests will be necessary to figure out if the current interpretation is correct. The interpretation of `bot` and `top` was copied from previous domain projects. ### Last adjustments We removed some debug output and also wrote test 14 to check if guards are evaluated correctly and also dead code is recognized accurately. --- .../linearTwoVarEqualityAnalysis.apron.ml | 10 +-- .../apron/linearTwoVarEqualityDomain.apron.ml | 80 +++++++++---------- tests/regression/77-lin2vareq/14.c | 17 ++++ 3 files changed, 59 insertions(+), 48 deletions(-) create mode 100644 tests/regression/77-lin2vareq/14.c diff --git a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml index 3aa52d8492..c8fc993762 100644 --- a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml +++ b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml @@ -36,10 +36,8 @@ let test1 () = let x2 = Apron.Var.of_string "x2" in let x3 = Apron.Var.of_string "x3" in let x4 = Apron.Var.of_string "x4" in - let x2' = Apron.Var.of_string "x2'" in - let x1' = Apron.Var.of_string "x1'" in let env_test = Apron.Environment.make (Array.of_list [x1; x2; x3; x4]) @@ Array.of_list [] in - let varM_test = D.top_env env_test in + let varM_test = D.identity env_test in let test = D.assign_texpr varM_test x1 (Texpr1.Cst (Coeff.s_of_int 5)) in (*x1 = 5*) let test = D.assign_var test x3 x2 in (*x3 = x2*) let test = D.assign_var test x4 x2 in (*x4 = x2*) @@ -63,8 +61,8 @@ let test2 () = let x1' = Apron.Var.of_string "x1'" in let env_test = Apron.Environment.make (Array.of_list [x1; x2; x3; x4]) @@ Array.of_list [] in let env_test' = Apron.Environment.make (Array.of_list [x1'; x2';x2 ]) @@ Array.of_list [] in - let varM_test = D.top_env env_test in - let varM_test' = D.top_env env_test' in + let varM_test = D.identity env_test in + let varM_test' = D.identity env_test' in let test = D.assign_texpr varM_test x1 (Texpr1.Cst (Coeff.s_of_int 5)) in (*x1 = 5*) let test = D.assign_var test x3 x2 in (*x3 = x2*) let test = D.assign_var test x4 x2 in (*x4 = x2*) @@ -98,7 +96,7 @@ let test2 () = let x = Apron.Var.of_string "x" in let y = Apron.Var.of_string "y" in let env_test = Apron.Environment.make (Array.of_list [x; y]) @@ Array.of_list [] in - let varM_test = D.top_env env_test in + let varM_test = D.identity env_test in let test = D.assign_texpr varM_test x (Texpr1.Cst (Coeff.s_of_int 1)) in (*x = 1*) let test = D.assign_texpr test y (Texpr1.Cst (Coeff.s_of_int 1)) in (*y = 1*) let tcons1 = Apron.Tcons1.make (Texpr1.of_expr env_test (Binop (Sub, Var x, Var y, Int, Near ))) EQ in diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index f1f9db7d52..bf1f76ae2f 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -145,7 +145,7 @@ module EqualitiesArray = struct let is_empty m = length m = 0 - let is_top_array m = m = make_empty_array (length m) + let is_top_array m = Array.fold_lefti (fun b i (a, e) -> if e == Z.zero && Option.is_some a && Option.get a == i then b else false) true m let find_reference_variable d var_index = fst d.(var_index) @@ -364,22 +364,18 @@ struct let name () = "lin2vareq" let to_yojson _ = failwith "ToDo Implement in future" - + let is_bot t = equal t (bot ()) let is_bot_env t = t.d = None +(*this shows "top" for a specific environment to enable the calculations. It is the identity of all equalities*) + let identity env = {d = Some (Array.init (Environment.size env) (fun i -> (Some i, Z.zero))); env = env} - let top_env env = {d = Some (Array.init (Environment.size env) (fun i -> (Some i, Z.zero))); env = env} - - (*Would the top not be the identity matrix in affineEq? - i.e. the array where each variable is assigned itself with no other coeffcients? *) - let top () = failwith "D.top ()" - - let is_top varM = if Option.is_some varM.d then - EArray.fold_lefti (fun b i (a, e) -> if Z.(e == Z.zero) && Option.is_some a && Option.get a == i then b else false) true (Option.get varM.d) - else false + (*Should never be called but implemented for completeness *) + let top () = {d = Some (EArray.empty()); env = empty_env} - let is_top_env t = (not @@ Environment.equal empty_env t.env) && GobOption.exists EArray.is_top_array t.d +(*is_top returns true for identity array and empty array *) + let is_top t = GobOption.exists EArray.is_top_array t.d (* prints the current variable equalities with resolved variable names *) let show varM = @@ -388,10 +384,10 @@ struct match tuple with | (None, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ Z.to_string offset ^ "\n" | (Some index, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ lookup index ^ " + " ^ Z.to_string offset ^ "\n" - in (*if is_top varM then "⊤\n" else *) + in if is_top varM then "⊤\n" else match varM.d with - | None -> "Bot Env\n" - | Some arr -> if EArray.is_empty arr then "Bot \n" else Array.fold_left (fun acc elem -> acc ^ elem ) "" (Array.mapi show_var arr) + | None -> "⊥\n" + | Some arr -> if is_bot varM then "Bot \n" else Array.fold_left (fun acc elem -> acc ^ elem ) "" (Array.mapi show_var arr) let pretty () (x:t) = text (show x) let printXml f x = BatPrintf.fprintf f "\n\n\nequalities-array\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) @@ -476,29 +472,29 @@ struct let leq t1 t2 = let env_comp = Environment.compare t1.env t2.env in (* Apron's Environment.compare has defined return values. *) let implies ts t i : bool = - match t with - | (None, b) -> - (match ts.(i) with - | (None, b') -> Z.equal b b' - | (Some j, b') -> (match ts.(j) with + match t with + | (None, b) -> + (match ts.(i) with + | (None, b') -> Z.equal b b' + | (Some j, b') -> (match ts.(j) with | (None, bj) -> Z.equal bj (Z.sub b b') | _ -> false )) - | (Some j, b) -> - (match ts.(i), ts.(j) with - | (None, b1), (None, b2) -> Z.equal b1 (Z.add b2 b) - | (Some h1, b1), (None, b2) -> (match ts.(h1) with + | (Some j, b) -> + (match ts.(i), ts.(j) with + | (None, b1), (None, b2) -> Z.equal b1 (Z.add b2 b) + | (Some h1, b1), (None, b2) -> (match ts.(h1) with | (None, bh1) -> Z.equal (Z.add bh1 b1) (Z.add b2 b) | _ -> false) - | (None, b1), (Some h2, b2) -> (match ts.(h2) with + | (None, b1), (Some h2, b2) -> (match ts.(h2) with | (None, bh2) -> Z.equal (Z.sub b1 bh2) (Z.add b2 b) | _ -> false) - | (Some h1, b1), (Some h2, b2) -> - h1 = h2 && Z.equal b1 (Z.add b2 b)) - in + | (Some h1, b1), (Some h2, b2) -> + h1 = h2 && Z.equal b1 (Z.add b2 b)) + in if env_comp = -2 || env_comp > 0 then false else - if is_bot t1 || is_top_env t2 then true else - if is_bot t2 || is_top_env t1 then false else ( + if is_bot_env t1 || is_top t2 then true else + if is_bot_env t2 || is_top t1 then false else ( let m1, m2 = Option.get t1.d, Option.get t2.d in let m1' = if env_comp = 0 then m1 else dim_add (Environment.dimchange t1.env t2.env) m1 in let result : bool ref = ref true in @@ -643,10 +639,10 @@ struct let result = strip_annotation annotated in result in - if is_bot a then b else if is_bot b then a else + if is_bot_env a then b else if is_bot_env b then a else match Option.get a.d, Option.get b.d with - | x, y when is_top_env a || is_top_env b -> let new_env = Environment.lce a.env b.env - in (top_env new_env) + | x, y when is_top a || is_top b -> let new_env = Environment.lce a.env b.env + in (identity new_env) | x, y when (Environment.compare a.env b.env <> 0) -> let sup_env = Environment.lce a.env b.env in let mod_x = dim_add (Environment.dimchange a.env sup_env) x in @@ -679,7 +675,7 @@ struct (* TODO: I'm not sure if forget_vars should remove the variable from the data structure, or just forget the information we currently have about the variable. Until now, the second possibility is implemented.*) let forget_vars t vars = - if is_bot t || is_top_env t then t + if is_bot_env t || is_top t then t else let m = Option.get t.d in if List.is_empty vars then t else @@ -724,7 +720,7 @@ struct end end - | None -> bot () end + | None -> bot_env end @@ -738,7 +734,7 @@ struct match Convert.texpr1_expr_of_cil_exp t t.env exp (Lazy.force no_ov) with | exp -> assign_texpr t var exp | exception Convert.Unsupported_CilExp _ -> - if is_bot t then t else forget_vars t [var] + if is_bot_env t then t else forget_vars t [var] let assign_exp t var exp no_ov = let res = assign_exp t var exp no_ov in @@ -768,7 +764,7 @@ struct let t_primed = add_vars t primed_vars in let multi_t = List.fold_left2 (fun t' v_prime (_,v') -> assign_var t' v_prime v') t_primed primed_vars vv's in match multi_t.d with - | Some arr when not @@ is_top_env multi_t -> + | Some arr when not @@ is_top multi_t -> let switched_arr = List.fold_left2 (fun multi_t assigned_var primed_var-> assign_var multi_t assigned_var primed_var) multi_t assigned_vars primed_vars in let res = drop_vars switched_arr primed_vars true in let x = Option.get res.d in @@ -843,8 +839,8 @@ struct depending on the result in the array after the evaluating including resolving the constraints in t.d the tcons can be evaluated and additional constraints can be added to t.d *) let expr_init = Array.init ((Environment.size t.env) +1) (fun _ -> Z.zero) in match t.d with - | None -> bot () - | Some d -> if is_bot t then bot () else + | None -> bot_env + | Some d -> if is_bot_env t then bot_env else let cv's = get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) in let update (expr : Z.t Array.t)( c , v) = match v with @@ -875,7 +871,7 @@ struct match Tcons1.get_typ tcons with | EQ -> if Option.is_none c then t else let expr = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int (Option.get c)) in - meet t (assign_texpr (top_env t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expr) + meet t (assign_texpr (identity t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expr) | _ -> t (*Not supported right now*) else if var_count == 2 then let v12 = List.fold_righti (fun i a l -> if Z.equal a Z.zero then l else (i,a)::l) (List.tl @@ Array.to_list final_expr) [] in @@ -884,7 +880,7 @@ struct let var1 = Environment.var_of_dim t.env (Tuple2.first (List.hd v12)) in let var2 = Environment.var_of_dim t.env (Tuple2.first (List.hd @@ List.tl v12)) in match Tcons1.get_typ tcons with - | EQ -> if Z.equal a1 Z.one && Z.equal a2 Z.one then meet t (assign_var (top_env t.env) var1 var2) else t + | EQ -> if Z.equal a1 Z.one && Z.equal a2 Z.one then meet t (assign_var (identity t.env) var1 var2) else t | _-> t (*Not supported right now*) else t (*For any other case we don't know if the (in-) equality is true or false or even possible therefore we just return t *) @@ -950,7 +946,7 @@ struct let no_ov = Lazy.force no_ov in if M.tracing then M.tracel "assert_cons" "assert_cons with expr: %a %b\n" d_exp e no_ov; match Convert.tcons1_of_cil_exp d d.env e negate no_ov with - | tcons1 -> meet_tcons d tcons1 e + | tcons1 -> meet_tcons d tcons1 e | exception Convert.Unsupported_CilExp _ -> d let assert_cons d e negate no_ov = timing_wrap "assert_cons" (assert_cons d e negate) no_ov diff --git a/tests/regression/77-lin2vareq/14.c b/tests/regression/77-lin2vareq/14.c new file mode 100644 index 0000000000..00182c38ed --- /dev/null +++ b/tests/regression/77-lin2vareq/14.c @@ -0,0 +1,17 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq +#include +#include + +int main() { + int x = 0; + int y = 0; + + x = 10; + y = 1; + + __goblint_check(x == 10 * y); //SUCCESS + + if(x == 10 * y) + return 0; + __goblint_check(0); // NOWARN (unreachable) +} From 469fd4d0c7cb4cdb93ef4aa2e3d70ed67a4a71ba Mon Sep 17 00:00:00 2001 From: nicolai frech Date: Thu, 14 Dec 2023 00:39:42 +0100 Subject: [PATCH 050/280] Update linearTwoVarEqualityDomain.apron.ml --- .../apron/linearTwoVarEqualityDomain.apron.ml | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index bf1f76ae2f..323ce66863 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -473,24 +473,18 @@ struct let env_comp = Environment.compare t1.env t2.env in (* Apron's Environment.compare has defined return values. *) let implies ts t i : bool = match t with - | (None, b) -> + | (None, b) -> (match ts.(i) with | (None, b') -> Z.equal b b' - | (Some j, b') -> (match ts.(j) with - | (None, bj) -> Z.equal bj (Z.sub b b') - | _ -> false - )) - | (Some j, b) -> + | _ -> false) + | (Some j, b) -> (match ts.(i), ts.(j) with | (None, b1), (None, b2) -> Z.equal b1 (Z.add b2 b) - | (Some h1, b1), (None, b2) -> (match ts.(h1) with - | (None, bh1) -> Z.equal (Z.add bh1 b1) (Z.add b2 b) - | _ -> false) - | (None, b1), (Some h2, b2) -> (match ts.(h2) with - | (None, bh2) -> Z.equal (Z.sub b1 bh2) (Z.add b2 b) - | _ -> false) | (Some h1, b1), (Some h2, b2) -> - h1 = h2 && Z.equal b1 (Z.add b2 b)) + h1 = h2 && Z.equal b1 (Z.add b2 b) + | (Some _, _), (_, _) -> false + | (_, _), (Some _, _) -> false + ) in if env_comp = -2 || env_comp > 0 then false else if is_bot_env t1 || is_top t2 then true else From 9251332a4c8cbd25f93594d74e7dffb14bd28c08 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 14 Dec 2023 14:49:08 +0100 Subject: [PATCH 051/280] Restored previous version --- gobview | 2 +- .../apron/linearTwoVarEqualityDomain.apron.ml | 54 +++++++++++++------ .../77-lin2vareq/{00.c => 00-basic.c} | 5 +- .../regression/77-lin2vareq/01-basic_matrix.c | 19 +++++++ .../77-lin2vareq/{09.c => 02-iteration.c} | 1 + tests/regression/77-lin2vareq/02.c | 33 ------------ .../77-lin2vareq/03-array_pointer.c | 15 ++++++ tests/regression/77-lin2vareq/03.c | 35 ------------ .../{11.c => 04-arrays_equality.c} | 1 + tests/regression/77-lin2vareq/04.c | 33 ------------ tests/regression/77-lin2vareq/05.c | 51 ++++++++---------- tests/regression/77-lin2vareq/06.c | 41 +++++++++----- .../{12.c => 07-array_2d_equality.c} | 1 + tests/regression/77-lin2vareq/07.c | 21 -------- .../{10.c => 08-vector_addition.c} | 1 + tests/regression/77-lin2vareq/08.c | 12 ----- .../{13.c => 09-matrix_2d_multiplication.c} | 1 + .../regression/77-lin2vareq/10-associative.c | 14 +++++ .../77-lin2vareq/11-associative_matrix.c | 42 +++++++++++++++ .../regression/77-lin2vareq/12-commutative.c | 18 +++++++ .../77-lin2vareq/13-commutative_matrix.c | 42 +++++++++++++++ .../regression/77-lin2vareq/14-distributive.c | 15 ++++++ .../77-lin2vareq/15-distributive_matrix.c | 54 +++++++++++++++++++ .../77-lin2vareq/{01.c => 16-thread.c} | 1 + 24 files changed, 319 insertions(+), 193 deletions(-) rename tests/regression/77-lin2vareq/{00.c => 00-basic.c} (82%) create mode 100644 tests/regression/77-lin2vareq/01-basic_matrix.c rename tests/regression/77-lin2vareq/{09.c => 02-iteration.c} (79%) delete mode 100644 tests/regression/77-lin2vareq/02.c create mode 100644 tests/regression/77-lin2vareq/03-array_pointer.c delete mode 100644 tests/regression/77-lin2vareq/03.c rename tests/regression/77-lin2vareq/{11.c => 04-arrays_equality.c} (82%) delete mode 100644 tests/regression/77-lin2vareq/04.c rename tests/regression/77-lin2vareq/{12.c => 07-array_2d_equality.c} (91%) delete mode 100644 tests/regression/77-lin2vareq/07.c rename tests/regression/77-lin2vareq/{10.c => 08-vector_addition.c} (86%) delete mode 100644 tests/regression/77-lin2vareq/08.c rename tests/regression/77-lin2vareq/{13.c => 09-matrix_2d_multiplication.c} (91%) create mode 100644 tests/regression/77-lin2vareq/10-associative.c create mode 100644 tests/regression/77-lin2vareq/11-associative_matrix.c create mode 100644 tests/regression/77-lin2vareq/12-commutative.c create mode 100644 tests/regression/77-lin2vareq/13-commutative_matrix.c create mode 100644 tests/regression/77-lin2vareq/14-distributive.c create mode 100644 tests/regression/77-lin2vareq/15-distributive_matrix.c rename tests/regression/77-lin2vareq/{01.c => 16-thread.c} (93%) diff --git a/gobview b/gobview index 3de13d7412..42b07f8253 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit 3de13d74124ab7bc30d8be299f02570d8f498b84 +Subproject commit 42b07f825316052ec030370daf0d00ebe28ec092 diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 323ce66863..066ea5f8b6 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -952,25 +952,47 @@ struct This function returns all the equalities that are saved in our datastructure t. Lincons -> linear constraint *) - let invariant t = [] - (*TODO + (*TODO*) + let invariant t = [] + (*let invariant t = match t.d with | None -> [] | Some m -> - let eEqualitiesArray = Lincons1.EqualitiesArray_make t.env (Matrix.num_rows m) in - for i = 0 to Lincons1.EqualitiesArray_length eEqualitiesArray do - let row = Matrix.get_row m i in - let coeff_vars = List.map (fun x -> Coeff.s_of_mpqf @@ Vector.nth row (Environment.dim_of_var t.env x), x) (vars t) in - let cst = Coeff.s_of_mpqf @@ Vector.nth row (Vector.length row - 1) in - Lincons1.set_list (Lincons1.EqualitiesArray_get eEqualitiesArray i) coeff_vars (Some cst) - done; - let {lincons0_EqualitiesArray; EqualitiesArray_env}: Lincons1.eEqualitiesArray = eEqualitiesArray in - EqualitiesArray.enum lincons0_EqualitiesArray - |> Enum.map (fun (lincons0: Lincons0.t) -> - Lincons1.{lincons0; env = EqualitiesArray_env} - ) - |> List.of_enum - *) + let linear_constraints = + EArray.fold_left + (fun acc row -> + let coeff_vars = List.map (fun(var,off) -> Coeff.s_of_int off, Some var) row in + let cst = Coeff.s_of_int (snd (List.hd row)) in + Lincons1.make (Linexpr1.make t.env) Lincons1.EQ + |> Lincons1.set_list coeff_vars (Some cst) + |> (fun lc -> Lincons1.{lincons0 = Lincons0.of_lincons1 lc; env = t.env}) + :: acc) + [] m + in + List.rev linear_constraints *) + + (* let invariant t = + match t.d with + | None -> [] + | Some m -> + let linear_constraints = + EArray.fold_left + (fun acc row -> + let lc = + List.fold_left + (fun lc (var, off) -> + let coeff = Coeff.s_of_int off in + let var_opt = Some var in + Lincons1.set_coeff lc var_opt coeff) + (Lincons1.make (Linexpr1.make t.env) Lincons1.EQ) + row + |> fun lc -> Lincons1.set_cst lc (Coeff.s_of_int (snd (List.hd row))) + in + Lincons1.{ lincons0 = Lincons0.of_lincons1 lc; env = t.env } :: acc) + [] m + in + List.rev linear_constraints *) + let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 diff --git a/tests/regression/77-lin2vareq/00.c b/tests/regression/77-lin2vareq/00-basic.c similarity index 82% rename from tests/regression/77-lin2vareq/00.c rename to tests/regression/77-lin2vareq/00-basic.c index 4ef01866fc..22d26ab725 100644 --- a/tests/regression/77-lin2vareq/00.c +++ b/tests/regression/77-lin2vareq/00-basic.c @@ -1,3 +1,5 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq + #include #include @@ -16,4 +18,5 @@ int main() { __goblint_check(x == y); //FAIL return 0; -} \ No newline at end of file +} + diff --git a/tests/regression/77-lin2vareq/01-basic_matrix.c b/tests/regression/77-lin2vareq/01-basic_matrix.c new file mode 100644 index 0000000000..22566b09a2 --- /dev/null +++ b/tests/regression/77-lin2vareq/01-basic_matrix.c @@ -0,0 +1,19 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq + +#include +#include + +int main() { + int arr[2] = {10, 20}; + int x = arr[0]; + int y = arr[1]; + + __goblint_check(x!=y); //SUCCESS + + arr[0] = 20; + + __goblint_check(x != y); //SUCCESS + __goblint_check(x==y); //FAIL + + return 0; +} \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/09.c b/tests/regression/77-lin2vareq/02-iteration.c similarity index 79% rename from tests/regression/77-lin2vareq/09.c rename to tests/regression/77-lin2vareq/02-iteration.c index 84908323b4..1c79d0f84d 100644 --- a/tests/regression/77-lin2vareq/09.c +++ b/tests/regression/77-lin2vareq/02-iteration.c @@ -1,3 +1,4 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq #include int main() { diff --git a/tests/regression/77-lin2vareq/02.c b/tests/regression/77-lin2vareq/02.c deleted file mode 100644 index c8b66498a3..0000000000 --- a/tests/regression/77-lin2vareq/02.c +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include - -int x=0; -int y=0; - -void *thread1(void *arg){ - if(x==0){ - y=1; - } - return NULL; -} - -void *thread2(void *arg){ - x=1; - return NULL; -} - -int main(){ - pthread_t thread1_id, thread2_id; - - pthread_create(&thread1_id, NULL, thread1, NULL); - pthread_create(&thread2_id, NULL, thread2, NULL); - - pthread_join(thread1_id, NULL); - pthread_join(thread2_id, NULL); - - __goblint_check(x!=y); //RACE! - - return 0; -} - -/* this test case evaluates whether analyzer can correctly analyze the potential equality between x and y considering the different paths taken by the threads. */ diff --git a/tests/regression/77-lin2vareq/03-array_pointer.c b/tests/regression/77-lin2vareq/03-array_pointer.c new file mode 100644 index 0000000000..01eca241ac --- /dev/null +++ b/tests/regression/77-lin2vareq/03-array_pointer.c @@ -0,0 +1,15 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +#include + +int main() { + int arr1[] = {1, 2, 3}; + int arr2[] = {1, 2, 3}; + int ptr1[] = arr1; + int ptr2[] = arr2; + + for(int i=0; i< 3 ; i++){ + __goblint_check(ptr1[i] ==ptr2[i]); //SUCCESS + } + + return 0; +} diff --git a/tests/regression/77-lin2vareq/03.c b/tests/regression/77-lin2vareq/03.c deleted file mode 100644 index b182203c1c..0000000000 --- a/tests/regression/77-lin2vareq/03.c +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include - -#define NUM_THREADS 10 -#define NUM_ITERATIONS 10000 - -int x=0; -int y=0; - -void *thread(void *arg){ - for(int i=0; i int main() { diff --git a/tests/regression/77-lin2vareq/04.c b/tests/regression/77-lin2vareq/04.c deleted file mode 100644 index da83ea985c..0000000000 --- a/tests/regression/77-lin2vareq/04.c +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include - -int x = 0; -float y = 0.0; - -void *thread1(void *arg){ - if(x==0){ - y=1.0; - } - return NULL; -} - -void *thread2(void *arg){ - x=1; - return NULL; -} - -int main(){ - pthread_t thread1_id, thread2_id; - - pthread_create(&thread1_id, NULL, thread1, NULL); - pthread_create(&thread2_id, NULL, thread2, NULL); - - pthread_join(thread1_id, NULL); - pthread_join(thread2_id, NULL); - - __goblint_check(x!=y); //UNKNOWN! - - return 0; -} - -/* This test case ensures that analyzer can track two variable equalities even when the variables have different types. It uses an integer and a float.*/ \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/05.c b/tests/regression/77-lin2vareq/05.c index a38c1a6a06..0a5eb1d3ec 100644 --- a/tests/regression/77-lin2vareq/05.c +++ b/tests/regression/77-lin2vareq/05.c @@ -1,33 +1,26 @@ -#include -#include - -int x =0; -char y='a'; - -void *thread1(void *arg){ - if(x==0){ - y='b'; - } - return NULL; -} - -void *thread2(void *arg){ - x =1; - return NULL; -} +//SKIP PARAM: --set ana.activated[+] lin2vareq +// from https://dl.acm.org/doi/10.1145/2049706.2049710 -int main(){ - pthread_t thread1_id, thread2_id; - - pthread_create(&thread1_id, NULL, thread1, NULL); - pthread_create(&thread2_id, NULL, thread2, NULL); - - pthread_join(thread1_id, NULL); - pthread_join(thread2_id, NULL); +#include - __goblint_check(x!=y); //UNKNOWN! +int main() { + int x1 = 5, x2 = 10, x3 = 15, x4, x5, x6, x7, x8, x9, x10, x11, x12; + + x4 = 3 * x2 + 5; + x5 = 3 * x3 + 15; + x6 = x3 + 3; + x7 = x3 + 2; + x8 = 7 * x3 + 15; + x9 = 0; + x10 = 2 * x9 + 2; + x11 = 2 * x1 - 3; + x12 = 4 * x1 - 5; + + __goblint_check(x4 == 3 * x2 + 5); //SUCCESS + __goblint_check(x5 == 3 * x3 + 15); //SUCCESS + __goblint_check(x7 == x6 - 1); //SUCCESS + __goblint_check(x10 == 2 * x9 + 2); //SUCCESS + __goblint_check(x12 == 2 * x11 + 1); //SUCCESS return 0; -} - -/* This test case uses 2 different types of variable, an integer and a character. It ensures that analyzer can track 2 variable equalities even when the variables have different types and sizes. */ \ No newline at end of file +} \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/06.c b/tests/regression/77-lin2vareq/06.c index f74556de7b..e250133918 100644 --- a/tests/regression/77-lin2vareq/06.c +++ b/tests/regression/77-lin2vareq/06.c @@ -1,19 +1,36 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +// from https://dl.acm.org/doi/10.1145/2049706.2049710 + #include -#include -int main() { - int arr[2] = {10, 20}; - int x = arr[0]; - int y = arr[1]; +typedef int dataX_t; +typedef int dataY_t; - printf(x); +dataX_t x_arr[100]; +dataY_t y_arr[100]; +dataX_t *x_ptr; +dataY_t *y_ptr; - __goblint_check(x!=y); //UNKNOWN! +void access() { + *x_ptr = 42; + *y_ptr = *x_ptr + 10; +} + +int main() { + int i; + x_ptr = &x_arr[0]; + y_ptr = &y_arr[0]; - arr[0] = 20; + for (i = 0; i < 100; i++) { + access(); + + __goblint_check(i == 8 * i + 0); //UNKNOWN! + __goblint_check(x_ptr == 8 * i + x_arr); //UNKNOWN! + __goblint_check(y_ptr == 4 * i + y_arr); //UNKNOWN! - __goblint_check(x != y); //UNKNOWN! - __goblint_check(x==y); //UNKNOWN! + x_ptr++; + y_ptr++; + } - return 0; -} \ No newline at end of file + return 0; +} diff --git a/tests/regression/77-lin2vareq/12.c b/tests/regression/77-lin2vareq/07-array_2d_equality.c similarity index 91% rename from tests/regression/77-lin2vareq/12.c rename to tests/regression/77-lin2vareq/07-array_2d_equality.c index b598e63591..84abbff1cf 100644 --- a/tests/regression/77-lin2vareq/12.c +++ b/tests/regression/77-lin2vareq/07-array_2d_equality.c @@ -1,3 +1,4 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq #include int main(){ diff --git a/tests/regression/77-lin2vareq/07.c b/tests/regression/77-lin2vareq/07.c deleted file mode 100644 index 18f6a34702..0000000000 --- a/tests/regression/77-lin2vareq/07.c +++ /dev/null @@ -1,21 +0,0 @@ -#include - -int main() { - int a = 2; - int b = 3; - int c = 1; - int d = 0; - - int x = a * a + 2 * c; - __goblint_check(x == 5); //FAIL - - int y = 100 * b + 4 * c + 400 - 100; - __goblint_check(y == 7 * c + 300); //FAIL - int z = a * 2 - c * (1 * (5 - d)) + 3; - __goblint_check(z == 4); //FAIL - - int p = 2 * a + 3 * b + c + d; - __goblint_check(2 * p == 4 * a + 6 * b + 2 * c + 2 * d); //SUCCESS - return 0; -} - diff --git a/tests/regression/77-lin2vareq/10.c b/tests/regression/77-lin2vareq/08-vector_addition.c similarity index 86% rename from tests/regression/77-lin2vareq/10.c rename to tests/regression/77-lin2vareq/08-vector_addition.c index a4b283243f..a0aa6cde18 100644 --- a/tests/regression/77-lin2vareq/10.c +++ b/tests/regression/77-lin2vareq/08-vector_addition.c @@ -1,3 +1,4 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq #include int main() { diff --git a/tests/regression/77-lin2vareq/08.c b/tests/regression/77-lin2vareq/08.c deleted file mode 100644 index 57c4f86b52..0000000000 --- a/tests/regression/77-lin2vareq/08.c +++ /dev/null @@ -1,12 +0,0 @@ -#include - -int main() { - int arr1[] = {1, 2, 3}; - int arr2[] = {1, 2, 3}; - int ptr1 = arr1; - int ptr2 = arr2; - - __goblint_check(ptr1 ==ptr2); //SUCCESS - - return 0; -} diff --git a/tests/regression/77-lin2vareq/13.c b/tests/regression/77-lin2vareq/09-matrix_2d_multiplication.c similarity index 91% rename from tests/regression/77-lin2vareq/13.c rename to tests/regression/77-lin2vareq/09-matrix_2d_multiplication.c index bb0989042c..c0215f9e93 100644 --- a/tests/regression/77-lin2vareq/13.c +++ b/tests/regression/77-lin2vareq/09-matrix_2d_multiplication.c @@ -1,3 +1,4 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq #include int main() { diff --git a/tests/regression/77-lin2vareq/10-associative.c b/tests/regression/77-lin2vareq/10-associative.c new file mode 100644 index 0000000000..12ff76624e --- /dev/null +++ b/tests/regression/77-lin2vareq/10-associative.c @@ -0,0 +1,14 @@ +#include + +int main() { + int a = 5; + int b = 3; + int c = 2; + + int expression1 = (a + b) * c; + int expression2 = c * (b + a); + + __goblint_check(expression1 == expression2); //SUCCESS + + return 0; +} diff --git a/tests/regression/77-lin2vareq/11-associative_matrix.c b/tests/regression/77-lin2vareq/11-associative_matrix.c new file mode 100644 index 0000000000..bb2d9ecbba --- /dev/null +++ b/tests/regression/77-lin2vareq/11-associative_matrix.c @@ -0,0 +1,42 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +#include + +#define ROWS 3 +#define COLS 3 + +int main() { + int a[ROWS][COLS] = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9} + }; + + int b[ROWS][COLS] = { + {9, 8, 7}, + {6, 5, 4}, + {3, 2, 1} + }; + + int result1[ROWS][COLS]; + int result2[ROWS][COLS]; + + for (int i = 0; i < ROWS; i++) { + for (int j = 0; j < COLS; j++) { + result1[i][j] = (a[i][j] + b[i][j]) + a[i][j]; + } + } + + for (int i = 0; i < ROWS; i++) { + for (int j = 0; j < COLS; j++) { + result2[i][j] = a[i][j] + (b[i][j] + a[i][j]); + } + } + + for(int i=0; i + +int main() { + int a = 5; + int b = 3; + int c = 2; + + int expression1 = (a + b) + c; + + int expression2 = a + (b + c); + + __goblint_check(expression1 == expression2); //SUCCESS + + return 0; +} + + diff --git a/tests/regression/77-lin2vareq/13-commutative_matrix.c b/tests/regression/77-lin2vareq/13-commutative_matrix.c new file mode 100644 index 0000000000..6ec102a617 --- /dev/null +++ b/tests/regression/77-lin2vareq/13-commutative_matrix.c @@ -0,0 +1,42 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +#include + +#define ROWS 3 +#define COLS 3 + +int main() { + int a[ROWS][COLS] = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9} + }; + + int b[ROWS][COLS] = { + {9, 8, 7}, + {6, 5, 4}, + {3, 2, 1} + }; + + int result1[ROWS][COLS]; + int result2[ROWS][COLS]; + + for (int i = 0; i < ROWS; i++) { + for (int j = 0; j < COLS; j++) { + result1[i][j] = a[i][j] + b[i][j]; + } + } + + for (int i = 0; i < ROWS; i++) { + for (int j = 0; j < COLS; j++) { + result2[i][j] = b[i][j] + a[i][j]; + } + } + + for(int i=0; i< ROWS; i++){ + for(int j=0; j< COLS; j++){ + __goblint_check(result1[i][j] == result2[i][j]); //SUCCESS + } + } + + return 0; +} diff --git a/tests/regression/77-lin2vareq/14-distributive.c b/tests/regression/77-lin2vareq/14-distributive.c new file mode 100644 index 0000000000..5e61d8b8a7 --- /dev/null +++ b/tests/regression/77-lin2vareq/14-distributive.c @@ -0,0 +1,15 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +#include + +int main() { + int a = 5; + int b = 3; + int c = 2; + + int expression1 = a * (b + c); + int expression2 = (a * b) + (a * c); + + __goblint_check(expression1 == expression2); //SUCCESS + + return 0; +} diff --git a/tests/regression/77-lin2vareq/15-distributive_matrix.c b/tests/regression/77-lin2vareq/15-distributive_matrix.c new file mode 100644 index 0000000000..264d9c9c9c --- /dev/null +++ b/tests/regression/77-lin2vareq/15-distributive_matrix.c @@ -0,0 +1,54 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +#include + +#define ROWS 3 +#define COLS 3 + +int main() { + int a[ROWS][COLS] = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9} + }; + + int b[ROWS][COLS] = { + {9, 8, 7}, + {6, 5, 4}, + {3, 2, 1} + }; + + int c = 2; + + int result1[ROWS][COLS]; + int result2[ROWS][COLS]; + + for (int i = 0; i < ROWS; i++) { + for (int j = 0; j < COLS; j++) { + result1[i][j] = c * (a[i][j] + b[i][j]); + } + } + + int temp1[ROWS][COLS]; + int temp2[ROWS][COLS]; + + for (int i = 0; i < ROWS; i++) { + for (int j = 0; j < COLS; j++) { + temp1[i][j] = c * a[i][j]; + temp2[i][j] = c * b[i][j]; + } + } + + for (int i = 0; i < ROWS; i++) { + for (int j = 0; j < COLS; j++) { + result2[i][j] = temp1[i][j] + temp2[i][j]; + } + } + + for(int i=0; i #include From 0d099d6fd3aece9f4080d37b9eaa31f76ff79d18 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 14 Dec 2023 15:28:51 +0100 Subject: [PATCH 052/280] REmoved a few comments --- .../linearTwoVarEqualityAnalysis.apron.ml | 87 +----------------- .../apron/linearTwoVarEqualityDomain.apron.ml | 91 ++----------------- 2 files changed, 9 insertions(+), 169 deletions(-) diff --git a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml index c8fc993762..786e38b73c 100644 --- a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml +++ b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml @@ -3,12 +3,8 @@ @see A. Flexeder, M. Petter, and H. Seidl Fast Interprocedural Linear Two-Variable Equalities. *) open Analyses -open Apron -open LinearTwoVarEqualityDomain - include RelationAnalysis -(** TODO: modify code *) let spec_module: (module MCPSpec) Lazy.t = lazy ( let module AD = LinearTwoVarEqualityDomain.D2 in @@ -31,88 +27,7 @@ let spec_module: (module MCPSpec) Lazy.t = let get_spec (): (module MCPSpec) = Lazy.force spec_module -let test1 () = - let x1 = Apron.Var.of_string "x1" in - let x2 = Apron.Var.of_string "x2" in - let x3 = Apron.Var.of_string "x3" in - let x4 = Apron.Var.of_string "x4" in - let env_test = Apron.Environment.make (Array.of_list [x1; x2; x3; x4]) @@ Array.of_list [] in - let varM_test = D.identity env_test in - let test = D.assign_texpr varM_test x1 (Texpr1.Cst (Coeff.s_of_int 5)) in (*x1 = 5*) - let test = D.assign_var test x3 x2 in (*x3 = x2*) - let test = D.assign_var test x4 x2 in (*x4 = x2*) - let test = D.assign_texpr test x2 (Texpr1.Cst (Coeff.s_of_int 3)) in (*x1 = 5*) - (*let test = D.assign_texpr test x2 (Texpr1.Cst (Coeff.s_of_int 5)) in (*x2 = 5*)*) - print_string "Test1:\n"; - print_string "Original variable setup:\n"; - print_string @@ D.show varM_test ; - print_string "After x1 = 5:\n"; - print_string @@ D.show (D.assign_texpr varM_test x1 (Texpr1.Cst (Coeff.s_of_int 5))); - print_string "After even more assignments:\n"; - print_string @@ D.show test; - print_string "Test1 completed\n" - -let test2 () = - let x1 = Apron.Var.of_string "x1" in - let x2 = Apron.Var.of_string "x2" in - let x3 = Apron.Var.of_string "x3" in - let x4 = Apron.Var.of_string "x4" in - let x2' = Apron.Var.of_string "x2'" in - let x1' = Apron.Var.of_string "x1'" in - let env_test = Apron.Environment.make (Array.of_list [x1; x2; x3; x4]) @@ Array.of_list [] in - let env_test' = Apron.Environment.make (Array.of_list [x1'; x2';x2 ]) @@ Array.of_list [] in - let varM_test = D.identity env_test in - let varM_test' = D.identity env_test' in - let test = D.assign_texpr varM_test x1 (Texpr1.Cst (Coeff.s_of_int 5)) in (*x1 = 5*) - let test = D.assign_var test x3 x2 in (*x3 = x2*) - let test = D.assign_var test x4 x2 in (*x4 = x2*) - let test' = D.assign_texpr varM_test' x1' (Texpr1.Cst (Coeff.s_of_int 3)) in (*x1' = 3*) - let test' = D.assign_var test' x2 x1' in (*x2 = x1'*) - (*let test = D.assign_texpr test x2 (Texpr1.Cst (Coeff.s_of_int 5)) in (*x2 = 5*)*) - print_string "Test2:\n"; - print_string "Original variable setup:\n"; - print_string @@ D.show varM_test ; - print_string "After x1 = 5:\n"; - print_string @@ D.show (D.assign_texpr varM_test x1 (Texpr1.Cst (Coeff.s_of_int 5))); - print_string "After even more assignments:\n"; - print_string @@ D.show test; - print_string "Other environments:\n"; - print_string @@ D.show test'; - print_string "Meet environments:\n"; - print_string @@ D.show (D.meet test test'); - print_string "change_d:\n"; - let sup_env = Environment.lce test.env test'.env in - print_string @@ D.show (VarManagement.change_d test sup_env true false); - print_string "reduce_col:\n"; - let sup_env = Environment.lce test.env test'.env in - let after_change_d = VarManagement.change_d test sup_env true false in - print_string @@ D.show ({d = Some ((EqualitiesArray.reduce_col (Option.get after_change_d.d) 3)); env = sup_env}); - print_string "drop_vars:\n"; - let sup_env = Environment.lce test.env test'.env in - print_string @@ D.show (VarManagement.drop_vars (VarManagement.change_d test sup_env true false) [x1'; x2'] true); - print_string "Test2 completed\n" - - let test3 () = - let x = Apron.Var.of_string "x" in - let y = Apron.Var.of_string "y" in - let env_test = Apron.Environment.make (Array.of_list [x; y]) @@ Array.of_list [] in - let varM_test = D.identity env_test in - let test = D.assign_texpr varM_test x (Texpr1.Cst (Coeff.s_of_int 1)) in (*x = 1*) - let test = D.assign_texpr test y (Texpr1.Cst (Coeff.s_of_int 1)) in (*y = 1*) - let tcons1 = Apron.Tcons1.make (Texpr1.of_expr env_test (Binop (Sub, Var x, Var y, Int, Near ))) EQ in - print_string "Test1:\n"; - print_string "Original variable setup:\n"; - print_string @@ D.show varM_test ; - print_string "After x = 1 and y=1:\n"; - print_string @@ D.show test; - print_string "Tcons:\n"; - print_string @@ D.show @@ D.meet_tcons test tcons1 " "; - print_string "Test1 completed\n" - -let after_config () =(* - test3(); - failwith "No error Test completed"; -*) +let after_config () = let module Spec = (val get_spec ()) in MCP.register_analysis (module Spec : MCPSpec); GobConfig.set_string "ana.path_sens[+]" (Spec.name ()) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 066ea5f8b6..b7f2551a9a 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -1,32 +1,22 @@ -(** OCaml implementation of the affine equalities domain. +(** OCaml implementation of the linear two-Variable equalitie domain. @see A. Flexeder, M. Petter, and H. Seidl Fast Interprocedural Linear Two-Variable Equalities. *) -(** TODO: description +(** Abstract states in this domain are represented by structs containing an array and an apron environment. + The arrays are modeled as proposed in the paper: Each variable is assigned to an index and each array element represents a linear relationship that must hold at the corresponding program point. + The apron environment is hereby used to organize the order of columns and variables. APRON: To get the index of a variable if you have a variable, use: Environment.dim_of_var env variable Function naming: - _with -> in place changes + _with -> in-place changes no _with -> make a copy - TODO while developing: - assert that the output doesnt have the same address as the input - (but it may return an unchanged version without making a copy) - in order to check if the function that don't have "with" really create a copy - Hot o check address equality in OCaml: == compares address equality != for unequal addresses - TODO for next week: - minimal working product - things to implement: - - leq - - join - done: assignment - done: meet_tcons HOW TO RUN THE REGRESSION TESTS: Method 1: regression test ./regtest.sh numberofdirectory numberoftest @@ -395,11 +385,6 @@ struct let meet t1 t2 = let sup_env = Environment.lce t1.env t2.env in let t1, t2 = change_d t1 sup_env true false, change_d t2 sup_env true false in - (*print_string "t1 after change_d\n"; - print_string @@ show t1; - print_string "t2 after change_d\n"; - print_string @@ show t2; - print_string "end\n";*) let subst_var ts x t = match !ts with | None -> () @@ -446,21 +431,6 @@ struct done; {d = !ds; env = sup_env} - (* - let sup_env = Environment.lce t1.env t2.env in - let t1, t2 = change_d t1 sup_env true false, change_d t2 sup_env true false in - if is_bot t1 || is_bot t2 then bot() else - let m1, m2 = Option.get t1.d, Option.get t2.d in - match m1, m2 with - | x, y when is_top_env t1-> {d = Some (dim_add (Environment.dimchange t2.env sup_env) y); env = sup_env} - | x, y when is_top_env t2 -> {d = Some (dim_add (Environment.dimchange t1.env sup_env) x); env = sup_env} - | x, y -> bot() - *) - (*let rref_matr = Matrix.rref_matrix_with (Matrix.copy x) (Matrix.copy y) in - if Option.is_none rref_matr then bot () else - {d = rref_matr; env = sup_env}*) - - let meet t1 t2 = let res = meet t1 t2 in if M.tracing then M.tracel "meet" "meet a: %s b: %s -> %s \n" (show t1) (show t2) (show res) ; @@ -468,7 +438,6 @@ struct let meet t1 t2 = timing_wrap "meet" (meet t1) t2 - (* TODO: check implementation for less equal *) let leq t1 t2 = let env_comp = Environment.compare t1.env t2.env in (* Apron's Environment.compare has defined return values. *) let implies ts t i : bool = @@ -666,8 +635,6 @@ struct let pretty_diff () (x, y) = dprintf "%s: %a not leq %a" (name ()) pretty x pretty y - (* TODO: I'm not sure if forget_vars should remove the variable from the data structure, - or just forget the information we currently have about the variable. Until now, the second possibility is implemented.*) let forget_vars t vars = if is_bot_env t || is_top t then t else @@ -686,7 +653,7 @@ struct let forget_vars t vars = timing_wrap "forget_vars" (forget_vars t) vars (* implemented as described on page 10 in the paper about Fast Interprocedural Linear Two-Variable Equalities in the Section "Abstract Effect of Statements" - This makes a copy of the data structure, it doesn't change t in-place. *) + This makes a copy of the data structure, it doesn't change it in-place. *) let assign_texpr (t: VarManagement.t) var texp = let assigned_var = Environment.dim_of_var t.env var (* this is the variable we are assigning to *) in begin match t.d with @@ -722,7 +689,7 @@ struct (* no_ov -> no overflow if it's true then there is no overflow - -> Convert.texpr1_expr_of_cil_exp handles overflow (TODO: test)*) + -> Convert.texpr1_expr_of_cil_exp handles overflow *) let assign_exp (t: VarManagement.t) var exp (no_ov: bool Lazy.t) = let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in match Convert.texpr1_expr_of_cil_exp t t.env exp (Lazy.force no_ov) with @@ -744,7 +711,6 @@ struct let res = assign_var t v v' in if M.tracing then M.tracel "ops" "assign_var t:\n %s \n v: %s \n v': %s\n -> %s\n" (show t) (Var.to_string v) (Var.to_string v') (show res) ; res - (* from here on TODO till end of module*) (* This functionality is not common to C and is used for assignments of the form: x = y, y=x; which is not legitimate C grammar x and y should be assigned to the value of x and y before the assignment respectively. ==> x = y_old , y = x_old; @@ -845,12 +811,7 @@ struct in let final_expr = List.fold_left (fun expr cv -> update expr cv ) expr_init cv's in let var_count = List.count_matching (fun a -> if Z.equal a Z.zero then false else true) ( List.tl ( Array.to_list final_expr)) - in (* - print_string "Meet_tcons:\n"; - print_coeff_vec cv's t.env; - print_final_expr (Array.to_list final_expr) t.env; - print_string ("Meet_tcons var_count is " ^ (Int.to_string var_count) ^ " and the type is " ^(Lincons0.string_of_typ (Tcons1.get_typ tcons)) ^"\n"); - *) + in if var_count == 0 then match Tcons1.get_typ tcons with | EQ -> if Z.equal final_expr.(0) Z.zero then t else bot_env @@ -879,42 +840,6 @@ struct else t (*For any other case we don't know if the (in-) equality is true or false or even possible therefore we just return t *) - - - (*TODO - let check_const cmp c = if cmp c Mpqf.zero then bot_env else t - in - let meet_vec e = - (*Flip the sign of the const. val in coeff vec*) - Vector.mapi_with (fun i x -> if Vector.compare_length_with e (i + 1) = 0 then Mpqf.mone *: x else x) e; - let res = if is_bot t then bot () else - let opt_m = Matrix.rref_vec_with (Matrix.copy @@ Option.get t.d) e - in if Option.is_none opt_m then bot () else {d = opt_m; env = t.env} in - meet_tcons_one_var_eq res expr - in - match get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with - | Some v -> - begin match get_c v, Tcons1.get_typ tcons with - | Some c, DISEQ -> check_const (=:) c - | Some c, SUP -> check_const (<=:) c - | Some c, EQ -> check_const (<>:) c - | Some c, SUPEQ -> check_const (<:) c - | None, DISEQ - | None, SUP -> - begin match meet_vec v with - | exception NotRefinable -> t - | res -> if equal res t then bot_env else t - end - | None, EQ -> - begin match meet_vec v with - | exception NotRefinable -> t - | res -> if is_bot res then bot_env else res - end - | _, _ -> t - end - | None -> t - *) - let meet_tcons t tcons expr = timing_wrap "meet_tcons" (meet_tcons t tcons) expr let unify a b = From 61ae1f73701be8b06b6fce340ea9508d3ac31f6e Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Thu, 14 Dec 2023 06:29:07 +0100 Subject: [PATCH 053/280] test cases adapted --- .../77-lin2vareq/03-array_equality.c | 20 +++++++++++ tests/regression/77-lin2vareq/04.c | 17 +++++++++ .../regression/77-lin2vareq/11-distributive.c | 17 +++++++++ tests/regression/77-lin2vareq/13-thread.c | 36 +++++++++++++++++++ 4 files changed, 90 insertions(+) create mode 100644 tests/regression/77-lin2vareq/03-array_equality.c create mode 100644 tests/regression/77-lin2vareq/04.c create mode 100644 tests/regression/77-lin2vareq/11-distributive.c create mode 100644 tests/regression/77-lin2vareq/13-thread.c diff --git a/tests/regression/77-lin2vareq/03-array_equality.c b/tests/regression/77-lin2vareq/03-array_equality.c new file mode 100644 index 0000000000..9e667242da --- /dev/null +++ b/tests/regression/77-lin2vareq/03-array_equality.c @@ -0,0 +1,20 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +#include + +int main() { + int arr1[] = {1, 2, 3}; + int arr2[] = {1, 2, 3}; + + int x1 = arr1[0]; + int x2 = arr2[0]; + + int y1 = arr1[1]; + int y2 = arr2[1]; + + __goblint_check(x1 == x2); //SUCCESS + __goblint_check(y1 == y2); //SUCCESS + + return 0; +} + +//In this case, variables are introduced to represent the values of array elements at specific indices. The equality checks are performed on those variables. diff --git a/tests/regression/77-lin2vareq/04.c b/tests/regression/77-lin2vareq/04.c new file mode 100644 index 0000000000..00182c38ed --- /dev/null +++ b/tests/regression/77-lin2vareq/04.c @@ -0,0 +1,17 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq +#include +#include + +int main() { + int x = 0; + int y = 0; + + x = 10; + y = 1; + + __goblint_check(x == 10 * y); //SUCCESS + + if(x == 10 * y) + return 0; + __goblint_check(0); // NOWARN (unreachable) +} diff --git a/tests/regression/77-lin2vareq/11-distributive.c b/tests/regression/77-lin2vareq/11-distributive.c new file mode 100644 index 0000000000..43a701733f --- /dev/null +++ b/tests/regression/77-lin2vareq/11-distributive.c @@ -0,0 +1,17 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +#include + +int main() { + int a = 5; + int b = 3; + int c = 2; + + int expression1 = a * (b + c); + int expression2 = (a * b) + (a * c); + + __goblint_check(expression1 == expression2); //SUCCESS + + return 0; +} + +//This test case checks the distributive property of multiplication over addition diff --git a/tests/regression/77-lin2vareq/13-thread.c b/tests/regression/77-lin2vareq/13-thread.c new file mode 100644 index 0000000000..f95f5cf1ed --- /dev/null +++ b/tests/regression/77-lin2vareq/13-thread.c @@ -0,0 +1,36 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +#include +#include + +int x=0; +int y=0; + +void *thread1(void *arg){ + x=1; + y=1; + return NULL; +} + +void *thread2(void *arg){ + y=2; + x=2; + return NULL; +} + +int main(){ + pthread_t thread1_id, thread2_id; + + pthread_create(&thread1_id, NULL, thread1, NULL); + pthread_create(&thread2_id, NULL, thread2, NULL); + + pthread_join(thread1_id, NULL); + pthread_join(thread2_id, NULL); + + __goblint_check(x==y); //UNKNOWN! + __goblint_check(x!=y); //UNKNOWN! + + return 0; +} + +//this test case verifies whether the analyzer can correctly represent and maintain the relationship between x and y even when they are modfied concurrectly. + From 89b47a58486530334ebdb969ade0e3131cafab96 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Thu, 14 Dec 2023 06:45:56 +0100 Subject: [PATCH 054/280] test cases adapted --- tests/regression/77-lin2vareq/02-iteration.c | 2 + .../77-lin2vareq/03-array_pointer.c | 15 ------ .../77-lin2vareq/04-arrays_equality.c | 12 ----- .../77-lin2vareq/07-array_2d_equality.c | 38 ++++++++----- .../77-lin2vareq/08-vector_addition.c | 23 ++++++-- .../09-matrix_2d_multiplication.c | 30 +++++++---- .../regression/77-lin2vareq/10-associative.c | 4 ++ .../77-lin2vareq/11-associative_matrix.c | 42 --------------- .../regression/77-lin2vareq/12-commutative.c | 7 ++- .../77-lin2vareq/13-commutative_matrix.c | 42 --------------- .../regression/77-lin2vareq/14-distributive.c | 15 ------ tests/regression/77-lin2vareq/14.c | 17 ------ .../77-lin2vareq/15-distributive_matrix.c | 54 ------------------- tests/regression/77-lin2vareq/16-thread.c | 36 ------------- 14 files changed, 73 insertions(+), 264 deletions(-) delete mode 100644 tests/regression/77-lin2vareq/03-array_pointer.c delete mode 100644 tests/regression/77-lin2vareq/04-arrays_equality.c delete mode 100644 tests/regression/77-lin2vareq/11-associative_matrix.c delete mode 100644 tests/regression/77-lin2vareq/13-commutative_matrix.c delete mode 100644 tests/regression/77-lin2vareq/14-distributive.c delete mode 100644 tests/regression/77-lin2vareq/14.c delete mode 100644 tests/regression/77-lin2vareq/15-distributive_matrix.c delete mode 100644 tests/regression/77-lin2vareq/16-thread.c diff --git a/tests/regression/77-lin2vareq/02-iteration.c b/tests/regression/77-lin2vareq/02-iteration.c index 1c79d0f84d..46d99f1b17 100644 --- a/tests/regression/77-lin2vareq/02-iteration.c +++ b/tests/regression/77-lin2vareq/02-iteration.c @@ -13,3 +13,5 @@ int main() { return 0; } + +//This test case checks whether the value of variable i is always equal to the value of variable j within the loop. \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/03-array_pointer.c b/tests/regression/77-lin2vareq/03-array_pointer.c deleted file mode 100644 index 01eca241ac..0000000000 --- a/tests/regression/77-lin2vareq/03-array_pointer.c +++ /dev/null @@ -1,15 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -#include - -int main() { - int arr1[] = {1, 2, 3}; - int arr2[] = {1, 2, 3}; - int ptr1[] = arr1; - int ptr2[] = arr2; - - for(int i=0; i< 3 ; i++){ - __goblint_check(ptr1[i] ==ptr2[i]); //SUCCESS - } - - return 0; -} diff --git a/tests/regression/77-lin2vareq/04-arrays_equality.c b/tests/regression/77-lin2vareq/04-arrays_equality.c deleted file mode 100644 index 416a04b73c..0000000000 --- a/tests/regression/77-lin2vareq/04-arrays_equality.c +++ /dev/null @@ -1,12 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -#include - -int main() { - int vector1[] = {1, 2, 3}; - int vector2[] = {1, 2, 3}; - - __goblint_check(vector1[0] == vector2[0] && vector1[1] == vector2[1] && vector1[2] == vector2[2]); - - return 0; -} - diff --git a/tests/regression/77-lin2vareq/07-array_2d_equality.c b/tests/regression/77-lin2vareq/07-array_2d_equality.c index 84abbff1cf..79460a2f17 100644 --- a/tests/regression/77-lin2vareq/07-array_2d_equality.c +++ b/tests/regression/77-lin2vareq/07-array_2d_equality.c @@ -1,23 +1,37 @@ //SKIP PARAM: --set ana.activated[+] lin2vareq #include -int main(){ +#include + +int main() { int matrix1[2][3] = {{1, 2, 3}, {4, 5, 6}}; int matrix2[2][3] = {{1, 2, 3}, {4, 5, 6}}; int matrix3[2][3] = {{7, 8, 9}, {10, 11, 12}}; - for (int i = 0; i < 2; ++i) { - for (int j = 0; j < 3; ++j) { - __goblint_check(matrix1[i][j] == matrix2[i][j]); //SUCCESS - } - } + int x11 = matrix1[0][0]; + int x12 = matrix1[0][1]; + int x13 = matrix1[0][2]; + + int x21 = matrix2[0][0]; + int x22 = matrix2[0][1]; + int x23 = matrix2[0][2]; + + int y11 = matrix1[1][0]; + int y12 = matrix1[1][1]; + int y13 = matrix1[1][2]; + + int y21 = matrix2[1][0]; + int y22 = matrix2[1][1]; + int y23 = matrix2[1][2]; - for (int i = 0; i < 2; ++i) { - for (int j = 0; j < 3; ++j) { - __goblint_check(matrix1[i][j] == matrix3[i][j]); //FAIL - } - } + __goblint_check(x11 == x21); //SUCCESS + __goblint_check(x12 == x22); //SUCCESS + __goblint_check(x13 == x23); //SUCCESS + __goblint_check(y11 == y21); //SUCCESS + __goblint_check(y12 == y22); //SUCCESS + __goblint_check(y13 == y23); //SUCCESS return 0; +} -} \ No newline at end of file +//Individual variables are introduced to represent the elements of the matrices. The equality checks are performed on these individual variables. diff --git a/tests/regression/77-lin2vareq/08-vector_addition.c b/tests/regression/77-lin2vareq/08-vector_addition.c index a0aa6cde18..b6fbbc6fbd 100644 --- a/tests/regression/77-lin2vareq/08-vector_addition.c +++ b/tests/regression/77-lin2vareq/08-vector_addition.c @@ -6,12 +6,25 @@ int main() { int vector2[] = {4, 5, 6}; int result[3]; + int x1 = vector1[0]; + int x2 = vector1[1]; + int x3 = vector1[2]; - for (int i = 0; i < 3; ++i) { - result[i] = vector1[i] + vector2[i]; - } + int y1 = vector2[0]; + int y2 = vector2[1]; + int y3 = vector2[2]; + + result[0] = x1 + y1; + result[1] = x2 + y2; + result[2] = x3 + y3; + + __goblint_check(result[0] == 5); //SUCCESS + __goblint_check(result[1] == 7); //SUCCESS + __goblint_check(result[2] == 9); //SUCCESS - __goblint_check(result[0] == 5 && result [1] == 7 && result [2] == 9); //SUCCESS - return 0; } + +//This test case checks whether the addition of corresponding elements from two vectors results in the expected values in the 'result' vector. + + diff --git a/tests/regression/77-lin2vareq/09-matrix_2d_multiplication.c b/tests/regression/77-lin2vareq/09-matrix_2d_multiplication.c index c0215f9e93..4d69b85149 100644 --- a/tests/regression/77-lin2vareq/09-matrix_2d_multiplication.c +++ b/tests/regression/77-lin2vareq/09-matrix_2d_multiplication.c @@ -6,17 +6,27 @@ int main() { int matrix2[2][2] = {{5, 6}, {7, 8}}; int result[2][2]; - for (int i = 0; i < 2; ++i) { - for (int j = 0; j < 2; ++j) { - result[i][j] = 0; - for (int k = 0; k < 2; ++k) { - result[i][j] += matrix1[i][k] * matrix2[k][j]; - } - } - } + int x11 = matrix1[0][0]; + int x12 = matrix1[0][1]; + int x21 = matrix1[1][0]; + int x22 = matrix1[1][1]; - __goblint_check(result[0][0] == 19 && result[0][1] == 22 && - result[1][0] == 43 && result[1][1] == 50); //SUCCESS + int y11 = matrix2[0][0]; + int y12 = matrix2[0][1]; + int y21 = matrix2[1][0]; + int y22 = matrix2[1][1]; + + result[0][0] = x11 * y11 + x12 * y21; + result[0][1] = x11 * y12 + x12 * y22; + result[1][0] = x21 * y11 + x22 * y21; + result[1][1] = x21 * y12 + x22 * y22; + + __goblint_check(result[0][0] == 19); //SUCCESS + __goblint_check(result[0][1] == 22); //SUCCESS + __goblint_check(result[1][0] == 43); //SUCCESS + __goblint_check(result[1][1] == 50); //SUCCESS return 0; } + +//This test case checks the correctness of matrix multiplication diff --git a/tests/regression/77-lin2vareq/10-associative.c b/tests/regression/77-lin2vareq/10-associative.c index 12ff76624e..e44ee79b5a 100644 --- a/tests/regression/77-lin2vareq/10-associative.c +++ b/tests/regression/77-lin2vareq/10-associative.c @@ -1,3 +1,4 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq #include int main() { @@ -12,3 +13,6 @@ int main() { return 0; } + + +//This test case checks the associative property \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/11-associative_matrix.c b/tests/regression/77-lin2vareq/11-associative_matrix.c deleted file mode 100644 index bb2d9ecbba..0000000000 --- a/tests/regression/77-lin2vareq/11-associative_matrix.c +++ /dev/null @@ -1,42 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -#include - -#define ROWS 3 -#define COLS 3 - -int main() { - int a[ROWS][COLS] = { - {1, 2, 3}, - {4, 5, 6}, - {7, 8, 9} - }; - - int b[ROWS][COLS] = { - {9, 8, 7}, - {6, 5, 4}, - {3, 2, 1} - }; - - int result1[ROWS][COLS]; - int result2[ROWS][COLS]; - - for (int i = 0; i < ROWS; i++) { - for (int j = 0; j < COLS; j++) { - result1[i][j] = (a[i][j] + b[i][j]) + a[i][j]; - } - } - - for (int i = 0; i < ROWS; i++) { - for (int j = 0; j < COLS; j++) { - result2[i][j] = a[i][j] + (b[i][j] + a[i][j]); - } - } - - for(int i=0; i - -#define ROWS 3 -#define COLS 3 - -int main() { - int a[ROWS][COLS] = { - {1, 2, 3}, - {4, 5, 6}, - {7, 8, 9} - }; - - int b[ROWS][COLS] = { - {9, 8, 7}, - {6, 5, 4}, - {3, 2, 1} - }; - - int result1[ROWS][COLS]; - int result2[ROWS][COLS]; - - for (int i = 0; i < ROWS; i++) { - for (int j = 0; j < COLS; j++) { - result1[i][j] = a[i][j] + b[i][j]; - } - } - - for (int i = 0; i < ROWS; i++) { - for (int j = 0; j < COLS; j++) { - result2[i][j] = b[i][j] + a[i][j]; - } - } - - for(int i=0; i< ROWS; i++){ - for(int j=0; j< COLS; j++){ - __goblint_check(result1[i][j] == result2[i][j]); //SUCCESS - } - } - - return 0; -} diff --git a/tests/regression/77-lin2vareq/14-distributive.c b/tests/regression/77-lin2vareq/14-distributive.c deleted file mode 100644 index 5e61d8b8a7..0000000000 --- a/tests/regression/77-lin2vareq/14-distributive.c +++ /dev/null @@ -1,15 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -#include - -int main() { - int a = 5; - int b = 3; - int c = 2; - - int expression1 = a * (b + c); - int expression2 = (a * b) + (a * c); - - __goblint_check(expression1 == expression2); //SUCCESS - - return 0; -} diff --git a/tests/regression/77-lin2vareq/14.c b/tests/regression/77-lin2vareq/14.c deleted file mode 100644 index 00182c38ed..0000000000 --- a/tests/regression/77-lin2vareq/14.c +++ /dev/null @@ -1,17 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq -#include -#include - -int main() { - int x = 0; - int y = 0; - - x = 10; - y = 1; - - __goblint_check(x == 10 * y); //SUCCESS - - if(x == 10 * y) - return 0; - __goblint_check(0); // NOWARN (unreachable) -} diff --git a/tests/regression/77-lin2vareq/15-distributive_matrix.c b/tests/regression/77-lin2vareq/15-distributive_matrix.c deleted file mode 100644 index 264d9c9c9c..0000000000 --- a/tests/regression/77-lin2vareq/15-distributive_matrix.c +++ /dev/null @@ -1,54 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -#include - -#define ROWS 3 -#define COLS 3 - -int main() { - int a[ROWS][COLS] = { - {1, 2, 3}, - {4, 5, 6}, - {7, 8, 9} - }; - - int b[ROWS][COLS] = { - {9, 8, 7}, - {6, 5, 4}, - {3, 2, 1} - }; - - int c = 2; - - int result1[ROWS][COLS]; - int result2[ROWS][COLS]; - - for (int i = 0; i < ROWS; i++) { - for (int j = 0; j < COLS; j++) { - result1[i][j] = c * (a[i][j] + b[i][j]); - } - } - - int temp1[ROWS][COLS]; - int temp2[ROWS][COLS]; - - for (int i = 0; i < ROWS; i++) { - for (int j = 0; j < COLS; j++) { - temp1[i][j] = c * a[i][j]; - temp2[i][j] = c * b[i][j]; - } - } - - for (int i = 0; i < ROWS; i++) { - for (int j = 0; j < COLS; j++) { - result2[i][j] = temp1[i][j] + temp2[i][j]; - } - } - - for(int i=0; i -#include - -int x=0; -int y=0; - -void *thread1(void *arg){ - x=1; - y=1; - return NULL; -} - -void *thread2(void *arg){ - y=2; - x=2; - return NULL; -} - -int main(){ - pthread_t thread1_id, thread2_id; - - pthread_create(&thread1_id, NULL, thread1, NULL); - pthread_create(&thread2_id, NULL, thread2, NULL); - - pthread_join(thread1_id, NULL); - pthread_join(thread2_id, NULL); - - __goblint_check(x==y); //UNKNOWN! - __goblint_check(x!=y); //UNKNOWN! - - return 0; -} - -//this test case verifies whether the analyzer can correctly represent and maintain the relationship between x and y even when they are modfied concurrectly. - From b8374c0551d4af4624fabe996cdeda3c8f2dc033 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Thu, 14 Dec 2023 22:36:43 +0100 Subject: [PATCH 055/280] test cases updated --- .../regression/77-lin2vareq/01-basic_matrix.c | 19 --------- .../{02-iteration.c => 01-iteration.c} | 2 +- tests/regression/77-lin2vareq/02.c | 17 ++++++++ .../77-lin2vareq/03-array_equality.c | 20 ---------- tests/regression/77-lin2vareq/{05.c => 03.c} | 0 tests/regression/77-lin2vareq/04.c | 39 ++++++++++++++----- .../{10-associative.c => 05-associative.c} | 0 .../{11-distributive.c => 06-distributive.c} | 0 tests/regression/77-lin2vareq/06.c | 36 ----------------- .../77-lin2vareq/07-array_2d_equality.c | 37 ------------------ .../{12-commutative.c => 07-commutative.c} | 0 .../77-lin2vareq/08-vector_addition.c | 30 -------------- tests/regression/77-lin2vareq/08.c | 16 ++++++++ .../09-matrix_2d_multiplication.c | 32 --------------- tests/regression/77-lin2vareq/13-thread.c | 36 ----------------- 15 files changed, 63 insertions(+), 221 deletions(-) delete mode 100644 tests/regression/77-lin2vareq/01-basic_matrix.c rename tests/regression/77-lin2vareq/{02-iteration.c => 01-iteration.c} (87%) create mode 100644 tests/regression/77-lin2vareq/02.c delete mode 100644 tests/regression/77-lin2vareq/03-array_equality.c rename tests/regression/77-lin2vareq/{05.c => 03.c} (100%) rename tests/regression/77-lin2vareq/{10-associative.c => 05-associative.c} (100%) rename tests/regression/77-lin2vareq/{11-distributive.c => 06-distributive.c} (100%) delete mode 100644 tests/regression/77-lin2vareq/06.c delete mode 100644 tests/regression/77-lin2vareq/07-array_2d_equality.c rename tests/regression/77-lin2vareq/{12-commutative.c => 07-commutative.c} (100%) delete mode 100644 tests/regression/77-lin2vareq/08-vector_addition.c create mode 100644 tests/regression/77-lin2vareq/08.c delete mode 100644 tests/regression/77-lin2vareq/09-matrix_2d_multiplication.c delete mode 100644 tests/regression/77-lin2vareq/13-thread.c diff --git a/tests/regression/77-lin2vareq/01-basic_matrix.c b/tests/regression/77-lin2vareq/01-basic_matrix.c deleted file mode 100644 index 22566b09a2..0000000000 --- a/tests/regression/77-lin2vareq/01-basic_matrix.c +++ /dev/null @@ -1,19 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq - -#include -#include - -int main() { - int arr[2] = {10, 20}; - int x = arr[0]; - int y = arr[1]; - - __goblint_check(x!=y); //SUCCESS - - arr[0] = 20; - - __goblint_check(x != y); //SUCCESS - __goblint_check(x==y); //FAIL - - return 0; -} \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/02-iteration.c b/tests/regression/77-lin2vareq/01-iteration.c similarity index 87% rename from tests/regression/77-lin2vareq/02-iteration.c rename to tests/regression/77-lin2vareq/01-iteration.c index 46d99f1b17..a028c74c77 100644 --- a/tests/regression/77-lin2vareq/02-iteration.c +++ b/tests/regression/77-lin2vareq/01-iteration.c @@ -8,7 +8,7 @@ int main() { for (i = 0; i < size; ++i) { j = i; - __goblint_check(i == j); //UNKNOWN! + __goblint_check(i == j); //SUCESS } return 0; diff --git a/tests/regression/77-lin2vareq/02.c b/tests/regression/77-lin2vareq/02.c new file mode 100644 index 0000000000..00182c38ed --- /dev/null +++ b/tests/regression/77-lin2vareq/02.c @@ -0,0 +1,17 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq +#include +#include + +int main() { + int x = 0; + int y = 0; + + x = 10; + y = 1; + + __goblint_check(x == 10 * y); //SUCCESS + + if(x == 10 * y) + return 0; + __goblint_check(0); // NOWARN (unreachable) +} diff --git a/tests/regression/77-lin2vareq/03-array_equality.c b/tests/regression/77-lin2vareq/03-array_equality.c deleted file mode 100644 index 9e667242da..0000000000 --- a/tests/regression/77-lin2vareq/03-array_equality.c +++ /dev/null @@ -1,20 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -#include - -int main() { - int arr1[] = {1, 2, 3}; - int arr2[] = {1, 2, 3}; - - int x1 = arr1[0]; - int x2 = arr2[0]; - - int y1 = arr1[1]; - int y2 = arr2[1]; - - __goblint_check(x1 == x2); //SUCCESS - __goblint_check(y1 == y2); //SUCCESS - - return 0; -} - -//In this case, variables are introduced to represent the values of array elements at specific indices. The equality checks are performed on those variables. diff --git a/tests/regression/77-lin2vareq/05.c b/tests/regression/77-lin2vareq/03.c similarity index 100% rename from tests/regression/77-lin2vareq/05.c rename to tests/regression/77-lin2vareq/03.c diff --git a/tests/regression/77-lin2vareq/04.c b/tests/regression/77-lin2vareq/04.c index 00182c38ed..e250133918 100644 --- a/tests/regression/77-lin2vareq/04.c +++ b/tests/regression/77-lin2vareq/04.c @@ -1,17 +1,36 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq +//SKIP PARAM: --set ana.activated[+] lin2vareq +// from https://dl.acm.org/doi/10.1145/2049706.2049710 + #include -#include + +typedef int dataX_t; +typedef int dataY_t; + +dataX_t x_arr[100]; +dataY_t y_arr[100]; +dataX_t *x_ptr; +dataY_t *y_ptr; + +void access() { + *x_ptr = 42; + *y_ptr = *x_ptr + 10; +} int main() { - int x = 0; - int y = 0; + int i; + x_ptr = &x_arr[0]; + y_ptr = &y_arr[0]; + + for (i = 0; i < 100; i++) { + access(); + + __goblint_check(i == 8 * i + 0); //UNKNOWN! + __goblint_check(x_ptr == 8 * i + x_arr); //UNKNOWN! + __goblint_check(y_ptr == 4 * i + y_arr); //UNKNOWN! - x = 10; - y = 1; + x_ptr++; + y_ptr++; + } - __goblint_check(x == 10 * y); //SUCCESS - - if(x == 10 * y) return 0; - __goblint_check(0); // NOWARN (unreachable) } diff --git a/tests/regression/77-lin2vareq/10-associative.c b/tests/regression/77-lin2vareq/05-associative.c similarity index 100% rename from tests/regression/77-lin2vareq/10-associative.c rename to tests/regression/77-lin2vareq/05-associative.c diff --git a/tests/regression/77-lin2vareq/11-distributive.c b/tests/regression/77-lin2vareq/06-distributive.c similarity index 100% rename from tests/regression/77-lin2vareq/11-distributive.c rename to tests/regression/77-lin2vareq/06-distributive.c diff --git a/tests/regression/77-lin2vareq/06.c b/tests/regression/77-lin2vareq/06.c deleted file mode 100644 index e250133918..0000000000 --- a/tests/regression/77-lin2vareq/06.c +++ /dev/null @@ -1,36 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -// from https://dl.acm.org/doi/10.1145/2049706.2049710 - -#include - -typedef int dataX_t; -typedef int dataY_t; - -dataX_t x_arr[100]; -dataY_t y_arr[100]; -dataX_t *x_ptr; -dataY_t *y_ptr; - -void access() { - *x_ptr = 42; - *y_ptr = *x_ptr + 10; -} - -int main() { - int i; - x_ptr = &x_arr[0]; - y_ptr = &y_arr[0]; - - for (i = 0; i < 100; i++) { - access(); - - __goblint_check(i == 8 * i + 0); //UNKNOWN! - __goblint_check(x_ptr == 8 * i + x_arr); //UNKNOWN! - __goblint_check(y_ptr == 4 * i + y_arr); //UNKNOWN! - - x_ptr++; - y_ptr++; - } - - return 0; -} diff --git a/tests/regression/77-lin2vareq/07-array_2d_equality.c b/tests/regression/77-lin2vareq/07-array_2d_equality.c deleted file mode 100644 index 79460a2f17..0000000000 --- a/tests/regression/77-lin2vareq/07-array_2d_equality.c +++ /dev/null @@ -1,37 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -#include - -#include - -int main() { - int matrix1[2][3] = {{1, 2, 3}, {4, 5, 6}}; - int matrix2[2][3] = {{1, 2, 3}, {4, 5, 6}}; - int matrix3[2][3] = {{7, 8, 9}, {10, 11, 12}}; - - int x11 = matrix1[0][0]; - int x12 = matrix1[0][1]; - int x13 = matrix1[0][2]; - - int x21 = matrix2[0][0]; - int x22 = matrix2[0][1]; - int x23 = matrix2[0][2]; - - int y11 = matrix1[1][0]; - int y12 = matrix1[1][1]; - int y13 = matrix1[1][2]; - - int y21 = matrix2[1][0]; - int y22 = matrix2[1][1]; - int y23 = matrix2[1][2]; - - __goblint_check(x11 == x21); //SUCCESS - __goblint_check(x12 == x22); //SUCCESS - __goblint_check(x13 == x23); //SUCCESS - __goblint_check(y11 == y21); //SUCCESS - __goblint_check(y12 == y22); //SUCCESS - __goblint_check(y13 == y23); //SUCCESS - - return 0; -} - -//Individual variables are introduced to represent the elements of the matrices. The equality checks are performed on these individual variables. diff --git a/tests/regression/77-lin2vareq/12-commutative.c b/tests/regression/77-lin2vareq/07-commutative.c similarity index 100% rename from tests/regression/77-lin2vareq/12-commutative.c rename to tests/regression/77-lin2vareq/07-commutative.c diff --git a/tests/regression/77-lin2vareq/08-vector_addition.c b/tests/regression/77-lin2vareq/08-vector_addition.c deleted file mode 100644 index b6fbbc6fbd..0000000000 --- a/tests/regression/77-lin2vareq/08-vector_addition.c +++ /dev/null @@ -1,30 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -#include - -int main() { - int vector1[] = {1, 2, 3}; - int vector2[] = {4, 5, 6}; - int result[3]; - - int x1 = vector1[0]; - int x2 = vector1[1]; - int x3 = vector1[2]; - - int y1 = vector2[0]; - int y2 = vector2[1]; - int y3 = vector2[2]; - - result[0] = x1 + y1; - result[1] = x2 + y2; - result[2] = x3 + y3; - - __goblint_check(result[0] == 5); //SUCCESS - __goblint_check(result[1] == 7); //SUCCESS - __goblint_check(result[2] == 9); //SUCCESS - - return 0; -} - -//This test case checks whether the addition of corresponding elements from two vectors results in the expected values in the 'result' vector. - - diff --git a/tests/regression/77-lin2vareq/08.c b/tests/regression/77-lin2vareq/08.c new file mode 100644 index 0000000000..9e72fac4de --- /dev/null +++ b/tests/regression/77-lin2vareq/08.c @@ -0,0 +1,16 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +#include + +int main() { + int x; + int y = 5; + + int result1 = x + y; + int result2 = y + x; + + __goblint_check(result1 == result2); //SUCCESS + + return 0; +} + +//This test case includes variable with unknown values diff --git a/tests/regression/77-lin2vareq/09-matrix_2d_multiplication.c b/tests/regression/77-lin2vareq/09-matrix_2d_multiplication.c deleted file mode 100644 index 4d69b85149..0000000000 --- a/tests/regression/77-lin2vareq/09-matrix_2d_multiplication.c +++ /dev/null @@ -1,32 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -#include - -int main() { - int matrix1[2][2] = {{1, 2}, {3, 4}}; - int matrix2[2][2] = {{5, 6}, {7, 8}}; - int result[2][2]; - - int x11 = matrix1[0][0]; - int x12 = matrix1[0][1]; - int x21 = matrix1[1][0]; - int x22 = matrix1[1][1]; - - int y11 = matrix2[0][0]; - int y12 = matrix2[0][1]; - int y21 = matrix2[1][0]; - int y22 = matrix2[1][1]; - - result[0][0] = x11 * y11 + x12 * y21; - result[0][1] = x11 * y12 + x12 * y22; - result[1][0] = x21 * y11 + x22 * y21; - result[1][1] = x21 * y12 + x22 * y22; - - __goblint_check(result[0][0] == 19); //SUCCESS - __goblint_check(result[0][1] == 22); //SUCCESS - __goblint_check(result[1][0] == 43); //SUCCESS - __goblint_check(result[1][1] == 50); //SUCCESS - - return 0; -} - -//This test case checks the correctness of matrix multiplication diff --git a/tests/regression/77-lin2vareq/13-thread.c b/tests/regression/77-lin2vareq/13-thread.c deleted file mode 100644 index f95f5cf1ed..0000000000 --- a/tests/regression/77-lin2vareq/13-thread.c +++ /dev/null @@ -1,36 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -#include -#include - -int x=0; -int y=0; - -void *thread1(void *arg){ - x=1; - y=1; - return NULL; -} - -void *thread2(void *arg){ - y=2; - x=2; - return NULL; -} - -int main(){ - pthread_t thread1_id, thread2_id; - - pthread_create(&thread1_id, NULL, thread1, NULL); - pthread_create(&thread2_id, NULL, thread2, NULL); - - pthread_join(thread1_id, NULL); - pthread_join(thread2_id, NULL); - - __goblint_check(x==y); //UNKNOWN! - __goblint_check(x!=y); //UNKNOWN! - - return 0; -} - -//this test case verifies whether the analyzer can correctly represent and maintain the relationship between x and y even when they are modfied concurrectly. - From ee2f7d7d7e7b9f43172b5f6a40bf11b824d68ae3 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Thu, 14 Dec 2023 23:21:26 +0100 Subject: [PATCH 056/280] test cases updated --- tests/regression/77-lin2vareq/08.c | 2 +- tests/regression/77-lin2vareq/09.c | 13 +++++++++++++ tests/regression/77-lin2vareq/10.c | 16 ++++++++++++++++ tests/regression/77-lin2vareq/11.c | 17 +++++++++++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 tests/regression/77-lin2vareq/09.c create mode 100644 tests/regression/77-lin2vareq/10.c create mode 100644 tests/regression/77-lin2vareq/11.c diff --git a/tests/regression/77-lin2vareq/08.c b/tests/regression/77-lin2vareq/08.c index 9e72fac4de..c1729442bb 100644 --- a/tests/regression/77-lin2vareq/08.c +++ b/tests/regression/77-lin2vareq/08.c @@ -13,4 +13,4 @@ int main() { return 0; } -//This test case includes variable with unknown values +//This test case includes variable with unknown value by not initializing the variable diff --git a/tests/regression/77-lin2vareq/09.c b/tests/regression/77-lin2vareq/09.c new file mode 100644 index 0000000000..213a172585 --- /dev/null +++ b/tests/regression/77-lin2vareq/09.c @@ -0,0 +1,13 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +#include + +int main() { + int x; + int y = 5; + + x = y * y; + + __goblint_check(x == y * y); //SUCCESS + + return 0; +} diff --git a/tests/regression/77-lin2vareq/10.c b/tests/regression/77-lin2vareq/10.c new file mode 100644 index 0000000000..6b38ef0405 --- /dev/null +++ b/tests/regression/77-lin2vareq/10.c @@ -0,0 +1,16 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none --enable ana.int.interval +#include + +int undefinedFunction(); + +int main() { + int x; + + x = undefinedFunction(); + + __goblint_check(x == undefinedFunction()); //UNKNOWN! + + return 0; +} + +//This test case sets a variable to a function that was not defined \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/11.c b/tests/regression/77-lin2vareq/11.c new file mode 100644 index 0000000000..1d617aeca4 --- /dev/null +++ b/tests/regression/77-lin2vareq/11.c @@ -0,0 +1,17 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +// Example from https://link.springer.com/content/pdf/10.1007/BF00268497.pdf +#include + +void main(void) { + int i; + int k; + i = 2; + k = 0; + + while (i < 100) { + __goblint_check(3 * i - k == 1); //UNKNOWN! + i = i + 1; + k = k + 3; + } + __goblint_check(3 * i - k == 1); //UNKNOWN! +} From 1cf7115ba78aaf4f8766c0cca5ead10888a2d4c4 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Fri, 15 Dec 2023 02:04:21 +0100 Subject: [PATCH 057/280] test cases updated --- tests/regression/77-lin2vareq/08.c | 25 +++++++++++++------------ tests/regression/77-lin2vareq/11.c | 17 ----------------- 2 files changed, 13 insertions(+), 29 deletions(-) delete mode 100644 tests/regression/77-lin2vareq/11.c diff --git a/tests/regression/77-lin2vareq/08.c b/tests/regression/77-lin2vareq/08.c index c1729442bb..1d617aeca4 100644 --- a/tests/regression/77-lin2vareq/08.c +++ b/tests/regression/77-lin2vareq/08.c @@ -1,16 +1,17 @@ //SKIP PARAM: --set ana.activated[+] lin2vareq -#include +// Example from https://link.springer.com/content/pdf/10.1007/BF00268497.pdf +#include -int main() { - int x; - int y = 5; +void main(void) { + int i; + int k; + i = 2; + k = 0; - int result1 = x + y; - int result2 = y + x; - - __goblint_check(result1 == result2); //SUCCESS - - return 0; + while (i < 100) { + __goblint_check(3 * i - k == 1); //UNKNOWN! + i = i + 1; + k = k + 3; + } + __goblint_check(3 * i - k == 1); //UNKNOWN! } - -//This test case includes variable with unknown value by not initializing the variable diff --git a/tests/regression/77-lin2vareq/11.c b/tests/regression/77-lin2vareq/11.c deleted file mode 100644 index 1d617aeca4..0000000000 --- a/tests/regression/77-lin2vareq/11.c +++ /dev/null @@ -1,17 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -// Example from https://link.springer.com/content/pdf/10.1007/BF00268497.pdf -#include - -void main(void) { - int i; - int k; - i = 2; - k = 0; - - while (i < 100) { - __goblint_check(3 * i - k == 1); //UNKNOWN! - i = i + 1; - k = k + 3; - } - __goblint_check(3 * i - k == 1); //UNKNOWN! -} From 861623775508a60cd5fec7c726a2871c136f1256 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Fri, 15 Dec 2023 14:07:58 +0100 Subject: [PATCH 058/280] Pull request comment Rebecca --- pull_request.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 pull_request.md diff --git a/pull_request.md b/pull_request.md new file mode 100644 index 0000000000..a213f5800f --- /dev/null +++ b/pull_request.md @@ -0,0 +1,12 @@ +This draft PR introduces an analysis for linear two variable equalities (ref: [A. Flexeder, M. Petter, and H. Seidl Fast Interprocedural Linear Two-Variable Equalities](http://doi.acm.org/10.1145/2049706.2049710)). The structure of the analysis is mostly based on the affine equality analysis. + +Abstract states in this domain are represented by structs containing an optional array and an apron environment. +The bottom element is represented by a struct with "None" instead of the array. + +The arrays are modeled as proposed in the paper: Each variable is assigned to an index and each array element represents a linear relationship that must hold at the corresponding program point. +The apron environment is used to organize the order of columns and variables. + +The length of the array always corresponds to the number of variables in the environment. +If for example in the array at index j we store the element (Some i, k), this means that our analysis found out that x_i = x_j + k. If the array entry at index j is (None, k), it means that x_j = k, where k is a constant and x_i and x_j are variables. + +In order to have less code duplication, we moved some functions from affineEqualityDomain to sharedFunctions, such that affineEqualityDomain and our linearTwoVarEqualityDomain can both use them. From 97d57b1c181ca255e607c57530c76b7fef6e4ddd Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Fri, 15 Dec 2023 02:24:55 +0100 Subject: [PATCH 059/280] test cases updated --- tests/regression/77-lin2vareq/08.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/regression/77-lin2vareq/08.c b/tests/regression/77-lin2vareq/08.c index 1d617aeca4..4525a84434 100644 --- a/tests/regression/77-lin2vareq/08.c +++ b/tests/regression/77-lin2vareq/08.c @@ -14,4 +14,5 @@ void main(void) { k = k + 3; } __goblint_check(3 * i - k == 1); //UNKNOWN! + } From f32d554dae0b2796e2703ee5230d8d5ba7420b11 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Fri, 15 Dec 2023 02:29:02 +0100 Subject: [PATCH 060/280] test cases updated --- tests/regression/77-lin2vareq/{08.c => 08-loop.c} | 0 tests/regression/77-lin2vareq/{10.c => 10-undefined_function.c} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/regression/77-lin2vareq/{08.c => 08-loop.c} (100%) rename tests/regression/77-lin2vareq/{10.c => 10-undefined_function.c} (100%) diff --git a/tests/regression/77-lin2vareq/08.c b/tests/regression/77-lin2vareq/08-loop.c similarity index 100% rename from tests/regression/77-lin2vareq/08.c rename to tests/regression/77-lin2vareq/08-loop.c diff --git a/tests/regression/77-lin2vareq/10.c b/tests/regression/77-lin2vareq/10-undefined_function.c similarity index 100% rename from tests/regression/77-lin2vareq/10.c rename to tests/regression/77-lin2vareq/10-undefined_function.c From 44fe307748e1a4970cb14ffff81144030927db01 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Fri, 15 Dec 2023 17:47:26 +0100 Subject: [PATCH 061/280] Added comment about extension to pull request --- pull_request.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pull_request.md b/pull_request.md index a213f5800f..da24749621 100644 --- a/pull_request.md +++ b/pull_request.md @@ -10,3 +10,5 @@ The length of the array always corresponds to the number of variables in the env If for example in the array at index j we store the element (Some i, k), this means that our analysis found out that x_i = x_j + k. If the array entry at index j is (None, k), it means that x_j = k, where k is a constant and x_i and x_j are variables. In order to have less code duplication, we moved some functions from affineEqualityDomain to sharedFunctions, such that affineEqualityDomain and our linearTwoVarEqualityDomain can both use them. + +This draft currently only supports equalities of the form `x = y + x`. We will extend it to support equalities of the form `a * x = b * y + c` where `x` and `y` are arbitrary variables, `a` and `b` are constants and `c` can be linear expression of various constants and variables which are equal to a constant i.e. the linear expression must be equal to a constant. \ No newline at end of file From d4519a13754f74af17d2291b9887daccf3fdb7cd Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Fri, 15 Dec 2023 19:16:32 +0100 Subject: [PATCH 062/280] Adapted the get_coeff and get_coeff_vec functions to recognize more expressions, and also added an according test --- .../apron/linearTwoVarEqualityDomain.apron.ml | 126 ++++++++++-------- .../77-lin2vareq/11-complicated_expression.c | 19 +++ 2 files changed, 88 insertions(+), 57 deletions(-) create mode 100644 tests/regression/77-lin2vareq/11-complicated_expression.c diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index b7f2551a9a..c949ce4ff5 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -215,6 +215,15 @@ struct | None -> Some off | _ -> None + + let get_variable_value_if_it_is_a_constant t var = + match t.d with + | None -> None + | Some d -> match d.(var) with + | (None, constant) -> Some constant + | _ -> None + + let get_coeff_vec (t: t) texp = (*Parses a Texpr to obtain a (coefficient, variable) pair list to repr. a sum of a variables that have a coefficient. If variable is None, the coefficient represents a constant offset. *) @@ -251,7 +260,11 @@ struct | Mpqf x -> [(mpqf_to_Z x, None)] | Mpfrf x -> raise NotIntegerOffset end in of_union x | Var x -> - let var_dim = Environment.dim_of_var t.env x in [(Z.one, Some var_dim)] + let var_dim = Environment.dim_of_var t.env x in + begin match get_variable_value_if_it_is_a_constant t var_dim with + | None -> [(Z.one, Some var_dim)] + | Some constant -> [(constant, None)] + end | Unop (u, e, _, _) -> begin match u with | Neg -> negate (convert_texpr e) @@ -270,26 +283,29 @@ struct (*Parses a Texpr to obtain a (variable, offset) pair to repr. a sum of a variable and an offset. Returns None if the expression is not a sum between a variable (without coefficient) and a constant. *) - let number_vars cv's = List.count_matching (fun (_, v)-> match v with | None -> false | Some x -> true) cv's in + let exception Not2VarExpr in let sum_coefficients summands_list = List.fold_left (fun (var, current_var_offset, curr_offset) (next_coeff, next_var) -> begin match next_var with | None -> (* this element represents a constant offset *) (var, current_var_offset, Z.(curr_offset + next_coeff)) | Some same_var -> (* this element represents a variable with a coefficient - -> it must be always the same variable because we only call this function if number_vars summands_list < 2*) - (Some same_var, Z.(current_var_offset + next_coeff), curr_offset) end) + -> it must be always the same variable, else it's not a two-variable equality*) + begin if Option.is_none var || Some same_var = var then + (Some same_var, Z.(current_var_offset + next_coeff), curr_offset) + else raise Not2VarExpr end + end) (None, Z.zero, Z.zero) summands_list in match get_coeff_vec t texp with | exception _ -> None - | summands_list -> if number_vars summands_list < 2 then - let (var, var_coeff, offset) = sum_coefficients summands_list in + | summands_list -> + match sum_coefficients summands_list with + | exception _ -> None + | (var, var_coeff, offset) -> if var = None then Some (None, offset) else if var_coeff = Z.one then Some (var, offset) else None - else - None let get_coeff t texp = timing_wrap "coeff_vec" (get_coeff t) texp @@ -354,17 +370,17 @@ struct let name () = "lin2vareq" let to_yojson _ = failwith "ToDo Implement in future" - + let is_bot t = equal t (bot ()) let is_bot_env t = t.d = None -(*this shows "top" for a specific environment to enable the calculations. It is the identity of all equalities*) + (*this shows "top" for a specific environment to enable the calculations. It is the identity of all equalities*) let identity env = {d = Some (Array.init (Environment.size env) (fun i -> (Some i, Z.zero))); env = env} (*Should never be called but implemented for completeness *) let top () = {d = Some (EArray.empty()); env = empty_env} -(*is_top returns true for identity array and empty array *) + (*is_top returns true for identity array and empty array *) let is_top t = GobOption.exists EArray.is_top_array t.d (* prints the current variable equalities with resolved variable names *) @@ -453,7 +469,7 @@ struct h1 = h2 && Z.equal b1 (Z.add b2 b) | (Some _, _), (_, _) -> false | (_, _), (Some _, _) -> false - ) + ) in if env_comp = -2 || env_comp > 0 then false else if is_bot_env t1 || is_top t2 then true else @@ -657,29 +673,25 @@ struct let assign_texpr (t: VarManagement.t) var texp = let assigned_var = Environment.dim_of_var t.env var (* this is the variable we are assigning to *) in begin match t.d with - | Some d -> - begin match d.(assigned_var) with - | rhs -> (* rhs is the current equality with assigned_var on the left hand side *) - let abstract_exists_var = abstract_exists var t in - begin match get_coeff t texp with - | None -> (* Statement "assigned_var = ?" (non-linear assignment) *) abstract_exists_var - | Some (exp_var_opt, off) -> - begin match exp_var_opt with - | None -> (* Statement "assigned_var = off" (constant assignment) *) - assign_const abstract_exists_var assigned_var off - | Some exp_var (* Statement "assigned_var = exp_var + off" (linear assignment) *) - -> begin if assigned_var = exp_var then - (* Statement "assigned_var = assigned_var + off" *) - subtract_const_from_var t assigned_var off - else - (* Statement "assigned_var = exp_var + off" (assigned_var is not the same as exp_var) *) - let empty_array = EqualitiesArray.make_empty_array (VarManagement.size t) in - let added_equality = empty_array.(assigned_var) <- (Some exp_var, off); empty_array in - meet abstract_exists_var {d = Some added_equality; env = t.env} - end - end + | Some d -> + let abstract_exists_var = abstract_exists var t in + begin match get_coeff t texp with + | None -> (* Statement "assigned_var = ?" (non-linear assignment) *) abstract_exists_var + | Some (exp_var_opt, off) -> + begin match exp_var_opt with + | None -> (* Statement "assigned_var = off" (constant assignment) *) + assign_const abstract_exists_var assigned_var off + | Some exp_var (* Statement "assigned_var = exp_var + off" (linear assignment) *) + -> begin if assigned_var = exp_var then + (* Statement "assigned_var = assigned_var + off" *) + subtract_const_from_var t assigned_var off + else + (* Statement "assigned_var = exp_var + off" (assigned_var is not the same as exp_var) *) + let empty_array = EqualitiesArray.make_empty_array (VarManagement.size t) in + let added_equality = empty_array.(assigned_var) <- (Some exp_var, off); empty_array in + meet abstract_exists_var {d = Some added_equality; env = t.env} + end end - end | None -> bot_env end @@ -895,29 +907,29 @@ struct [] m in List.rev linear_constraints *) - - (* let invariant t = - match t.d with - | None -> [] - | Some m -> - let linear_constraints = - EArray.fold_left - (fun acc row -> - let lc = - List.fold_left - (fun lc (var, off) -> - let coeff = Coeff.s_of_int off in - let var_opt = Some var in - Lincons1.set_coeff lc var_opt coeff) - (Lincons1.make (Linexpr1.make t.env) Lincons1.EQ) - row - |> fun lc -> Lincons1.set_cst lc (Coeff.s_of_int (snd (List.hd row))) - in - Lincons1.{ lincons0 = Lincons0.of_lincons1 lc; env = t.env } :: acc) - [] m - in - List.rev linear_constraints *) - + + (* let invariant t = + match t.d with + | None -> [] + | Some m -> + let linear_constraints = + EArray.fold_left + (fun acc row -> + let lc = + List.fold_left + (fun lc (var, off) -> + let coeff = Coeff.s_of_int off in + let var_opt = Some var in + Lincons1.set_coeff lc var_opt coeff) + (Lincons1.make (Linexpr1.make t.env) Lincons1.EQ) + row + |> fun lc -> Lincons1.set_cst lc (Coeff.s_of_int (snd (List.hd row))) + in + Lincons1.{ lincons0 = Lincons0.of_lincons1 lc; env = t.env } :: acc) + [] m + in + List.rev linear_constraints *) + let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 diff --git a/tests/regression/77-lin2vareq/11-complicated_expression.c b/tests/regression/77-lin2vareq/11-complicated_expression.c new file mode 100644 index 0000000000..08c0a1d952 --- /dev/null +++ b/tests/regression/77-lin2vareq/11-complicated_expression.c @@ -0,0 +1,19 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none + +#include + +int main() { + int x; + int k; + int y = 5; + + int result1 = 3 * (x + y) - 2 * x + 6; + int result2 = 3 * (x + y) - 2 * k + 6; + + __goblint_check(result1 == x + 21); // SUCCESS + __goblint_check(result2 == x + 21); // UNKNOWN! + + return 0; +} + +// This test case includes variable with unknown values From 1c242addd0f4491ae75863ef9c4355d2a1cfeabf Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 17 Dec 2023 22:29:22 +0100 Subject: [PATCH 063/280] removed a test --- ..._expression.c => 10-complicated_expression.c} | 0 .../77-lin2vareq/10-undefined_function.c | 16 ---------------- 2 files changed, 16 deletions(-) rename tests/regression/77-lin2vareq/{11-complicated_expression.c => 10-complicated_expression.c} (100%) delete mode 100644 tests/regression/77-lin2vareq/10-undefined_function.c diff --git a/tests/regression/77-lin2vareq/11-complicated_expression.c b/tests/regression/77-lin2vareq/10-complicated_expression.c similarity index 100% rename from tests/regression/77-lin2vareq/11-complicated_expression.c rename to tests/regression/77-lin2vareq/10-complicated_expression.c diff --git a/tests/regression/77-lin2vareq/10-undefined_function.c b/tests/regression/77-lin2vareq/10-undefined_function.c deleted file mode 100644 index 6b38ef0405..0000000000 --- a/tests/regression/77-lin2vareq/10-undefined_function.c +++ /dev/null @@ -1,16 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none --enable ana.int.interval -#include - -int undefinedFunction(); - -int main() { - int x; - - x = undefinedFunction(); - - __goblint_check(x == undefinedFunction()); //UNKNOWN! - - return 0; -} - -//This test case sets a variable to a function that was not defined \ No newline at end of file From 60922f3c4ed058c93a6f28c99722737a05d12596 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Sun, 17 Dec 2023 22:43:07 +0100 Subject: [PATCH 064/280] typo --- pull_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pull_request.md b/pull_request.md index da24749621..e12b271495 100644 --- a/pull_request.md +++ b/pull_request.md @@ -11,4 +11,4 @@ If for example in the array at index j we store the element (Some i, k), this me In order to have less code duplication, we moved some functions from affineEqualityDomain to sharedFunctions, such that affineEqualityDomain and our linearTwoVarEqualityDomain can both use them. -This draft currently only supports equalities of the form `x = y + x`. We will extend it to support equalities of the form `a * x = b * y + c` where `x` and `y` are arbitrary variables, `a` and `b` are constants and `c` can be linear expression of various constants and variables which are equal to a constant i.e. the linear expression must be equal to a constant. \ No newline at end of file +This draft currently only supports equalities of the form `x = y + c`. We will extend it to support equalities of the form `a * x = b * y + c` where `x` and `y` are arbitrary variables, `a` and `b` are constants and `c` can be a linear expression of various constants and variables which are equal to a constant i.e. the linear expression must be equal to a constant. \ No newline at end of file From 7371a3463363ae63fe0e5d754d0b6f80b778887d Mon Sep 17 00:00:00 2001 From: Klara Fall <44259717+alina-weber@users.noreply.github.com> Date: Wed, 20 Dec 2023 15:24:00 +0100 Subject: [PATCH 065/280] Update src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml Co-authored-by: Julian Erhard --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index c949ce4ff5..19855d1fe4 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -824,7 +824,7 @@ struct let final_expr = List.fold_left (fun expr cv -> update expr cv ) expr_init cv's in let var_count = List.count_matching (fun a -> if Z.equal a Z.zero then false else true) ( List.tl ( Array.to_list final_expr)) in - if var_count == 0 then + if var_count = 0 then match Tcons1.get_typ tcons with | EQ -> if Z.equal final_expr.(0) Z.zero then t else bot_env | SUPEQ -> if Z.geq final_expr.(0) Z.zero then t else bot_env From 0d42a745960e67b081f06acd81a95cf524076d2e Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 20 Dec 2023 15:29:07 +0100 Subject: [PATCH 066/280] Fixed semgrep warnings --- .../apron/linearTwoVarEqualityDomain.apron.ml | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 19855d1fe4..e9dd6951bf 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -235,12 +235,12 @@ struct List.map (fun (coeff, var) -> (Z.(number * coeff, var))) coeff_var_list in let multiply a b = (* if one of them is a constant, then multiply. Otherwise, the expression is not linear*) - if List.length a = 1 then + if List.compare_length_with a 1 = 0 then match List.nth a 0 with | (a_coeff, None) -> multiply_with_Z a_coeff b | _ -> raise NotLinearExpr else - if List.length b = 1 then + if List.compare_length_with a 1 = 0 then match List.nth b 0 with | (b_coeff, None) -> multiply_with_Z b_coeff a | _ -> raise NotLinearExpr @@ -329,9 +329,10 @@ struct | None -> d.(index) <- (None, Z.(off2 + const)) | Some eq_var -> begin if eq_var <> index then d.(index) <- (None, Z.(off2 + const)) end else - begin if Option.is_some eq_var_opt - then let eq_var = Option.get eq_var_opt - in begin if eq_var = var then d.(index) <- (Some eq_var, Z.(off2 - const)) end + begin match eq_var_opt with + | Some eq_var -> + if eq_var = var then d.(index) <- (Some eq_var, Z.(off2 - const)) + | None -> () end in EArray.iteri (subtract_const_from_var_for_single_equality const) d; {d = Some d; env = t.env} @@ -548,7 +549,7 @@ struct !result in let least_index_var_in_eq_class zts start size : int * Z.t = - let result = ref (0, Z.of_int 0) in + let result = ref (0, Z.zero) in match zts.(start) with | (i, (_, b), (_, _)) -> result := (i, b); for i = start + 1 to start + size - 1 do @@ -568,7 +569,7 @@ struct let assign_vars_in_const_eq_class ats zts start size least_i least_b = for i = start to start + size - 1 do match zts.(i) with - | (ai, t1, t2) -> if Z.equal (diff t1 t2) (Z.of_int 0) then ats.(i) <- (ai, t1) + | (ai, t1, t2) -> if Z.equal (diff t1 t2) (Z.zero) then ats.(i) <- (ai, t1) else match t1 with | (_, bj) -> ats.(i) <- (ai, (Some least_i, Z.sub bj least_b)) @@ -585,16 +586,16 @@ struct match zts with | None -> None | Some zts' -> - let result = Array.make (Array.length zts') (0, (None, Z.of_int 0)) in + let result = Array.make (Array.length zts') (0, (None, Z.zero)) in let i = ref 0 in while !i < Array.length zts' do let n = size_of_eq_class zts' !i in (if n = 1 then let ztsi = zts'.(!i) in match ztsi with - | (i', t1, t2) -> if is_const ztsi && Z.equal (diff t1 t2) (Z.of_int 0) then + | (i', t1, t2) -> if is_const ztsi && Z.equal (diff t1 t2) (Z.zero) then result.(!i) <- (i', (None, const_offset t1)) - else result.(!i) <- (i', (Some i', Z.of_int 0)) + else result.(!i) <- (i', (Some i', Z.zero)) else let (least_i, least_b) = least_index_var_in_eq_class zts' !i n in (if all_are_const_in_eq_class zts' !i n then From 3e9605f8f9f4dd40aeeaa8ac88ed402d8d35483b Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 20 Dec 2023 15:50:14 +0100 Subject: [PATCH 067/280] changed line ending in affineEqualityAnalysis.apron.ml --- .../apron/affineEqualityAnalysis.apron.ml | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/analyses/apron/affineEqualityAnalysis.apron.ml b/src/analyses/apron/affineEqualityAnalysis.apron.ml index 007bd91625..1ce19f0f93 100644 --- a/src/analyses/apron/affineEqualityAnalysis.apron.ml +++ b/src/analyses/apron/affineEqualityAnalysis.apron.ml @@ -1,37 +1,37 @@ -(** {{!RelationAnalysis} Relational integer value analysis} using an OCaml implementation of the affine equalities domain ([affeq]). - - @see Karr, M. Affine relationships among variables of a program. *) - -open Analyses - -include RelationAnalysis - -let spec_module: (module MCPSpec) Lazy.t = - lazy ( - let module AD = AffineEqualityDomain.D2 (VectorMatrix.ArrayVector) (VectorMatrix.ArrayMatrix) in - let module RD: RelationDomain.RD = - struct - module V = AffineEqualityDomain.V - include AD - end - in - let module Priv = (val RelationPriv.get_priv ()) in - let module Spec = - struct - include SpecFunctor (Priv) (RD) (RelationPrecCompareUtil.DummyUtil) - let name () = "affeq" - end - in - (module Spec) - ) - -let get_spec (): (module MCPSpec) = - Lazy.force spec_module - -let after_config () = - let module Spec = (val get_spec ()) in - MCP.register_analysis (module Spec : MCPSpec); - GobConfig.set_string "ana.path_sens[+]" (Spec.name ()) - -let _ = +(** {{!RelationAnalysis} Relational integer value analysis} using an OCaml implementation of the affine equalities domain ([affeq]). + + @see Karr, M. Affine relationships among variables of a program. *) + +open Analyses + +include RelationAnalysis + +let spec_module: (module MCPSpec) Lazy.t = + lazy ( + let module AD = AffineEqualityDomain.D2 (VectorMatrix.ArrayVector) (VectorMatrix.ArrayMatrix) in + let module RD: RelationDomain.RD = + struct + module V = AffineEqualityDomain.V + include AD + end + in + let module Priv = (val RelationPriv.get_priv ()) in + let module Spec = + struct + include SpecFunctor (Priv) (RD) (RelationPrecCompareUtil.DummyUtil) + let name () = "affeq" + end + in + (module Spec) + ) + +let get_spec (): (module MCPSpec) = + Lazy.force spec_module + +let after_config () = + let module Spec = (val get_spec ()) in + MCP.register_analysis (module Spec : MCPSpec); + GobConfig.set_string "ana.path_sens[+]" (Spec.name ()) + +let _ = AfterConfig.register after_config \ No newline at end of file From 7ed2d38af1f738d8d8765ae8758c451176ee031f Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 20 Dec 2023 15:53:02 +0100 Subject: [PATCH 068/280] revert changed line ending in affineEqualityAnalysis.apron.ml --- .../apron/affineEqualityAnalysis.apron.ml | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/analyses/apron/affineEqualityAnalysis.apron.ml b/src/analyses/apron/affineEqualityAnalysis.apron.ml index 1ce19f0f93..007bd91625 100644 --- a/src/analyses/apron/affineEqualityAnalysis.apron.ml +++ b/src/analyses/apron/affineEqualityAnalysis.apron.ml @@ -1,37 +1,37 @@ -(** {{!RelationAnalysis} Relational integer value analysis} using an OCaml implementation of the affine equalities domain ([affeq]). - - @see Karr, M. Affine relationships among variables of a program. *) - -open Analyses - -include RelationAnalysis - -let spec_module: (module MCPSpec) Lazy.t = - lazy ( - let module AD = AffineEqualityDomain.D2 (VectorMatrix.ArrayVector) (VectorMatrix.ArrayMatrix) in - let module RD: RelationDomain.RD = - struct - module V = AffineEqualityDomain.V - include AD - end - in - let module Priv = (val RelationPriv.get_priv ()) in - let module Spec = - struct - include SpecFunctor (Priv) (RD) (RelationPrecCompareUtil.DummyUtil) - let name () = "affeq" - end - in - (module Spec) - ) - -let get_spec (): (module MCPSpec) = - Lazy.force spec_module - -let after_config () = - let module Spec = (val get_spec ()) in - MCP.register_analysis (module Spec : MCPSpec); - GobConfig.set_string "ana.path_sens[+]" (Spec.name ()) - -let _ = +(** {{!RelationAnalysis} Relational integer value analysis} using an OCaml implementation of the affine equalities domain ([affeq]). + + @see Karr, M. Affine relationships among variables of a program. *) + +open Analyses + +include RelationAnalysis + +let spec_module: (module MCPSpec) Lazy.t = + lazy ( + let module AD = AffineEqualityDomain.D2 (VectorMatrix.ArrayVector) (VectorMatrix.ArrayMatrix) in + let module RD: RelationDomain.RD = + struct + module V = AffineEqualityDomain.V + include AD + end + in + let module Priv = (val RelationPriv.get_priv ()) in + let module Spec = + struct + include SpecFunctor (Priv) (RD) (RelationPrecCompareUtil.DummyUtil) + let name () = "affeq" + end + in + (module Spec) + ) + +let get_spec (): (module MCPSpec) = + Lazy.force spec_module + +let after_config () = + let module Spec = (val get_spec ()) in + MCP.register_analysis (module Spec : MCPSpec); + GobConfig.set_string "ana.path_sens[+]" (Spec.name ()) + +let _ = AfterConfig.register after_config \ No newline at end of file From ca8bf917e293a3481c788a5232d87147f142d114 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 20 Dec 2023 16:00:06 +0100 Subject: [PATCH 069/280] added line ending in affineEqualityAnalysis.apron.ml --- src/analyses/apron/affineEqualityAnalysis.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/apron/affineEqualityAnalysis.apron.ml b/src/analyses/apron/affineEqualityAnalysis.apron.ml index 007bd91625..ce859d87b7 100644 --- a/src/analyses/apron/affineEqualityAnalysis.apron.ml +++ b/src/analyses/apron/affineEqualityAnalysis.apron.ml @@ -34,4 +34,4 @@ let after_config () = GobConfig.set_string "ana.path_sens[+]" (Spec.name ()) let _ = - AfterConfig.register after_config \ No newline at end of file + AfterConfig.register after_config From e1841b0839261d0a5946764965634a1e2f9d5ee6 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 20 Dec 2023 16:20:49 +0100 Subject: [PATCH 070/280] Adjusted meet_tcons according to pull_request review --- .../apron/linearTwoVarEqualityDomain.apron.ml | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index e9dd6951bf..5d0cfbd45c 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -810,41 +810,44 @@ struct let meet_tcons t tcons _ = (* The expression is evaluated using an array of coefficients. The first element of the array belongs to the constant followed by the coefficients of all variables depending on the result in the array after the evaluating including resolving the constraints in t.d the tcons can be evaluated and additional constraints can be added to t.d *) - let expr_init = Array.init ((Environment.size t.env) +1) (fun _ -> Z.zero) in + let expr = Array.init ((Environment.size t.env) +1) (fun _ -> Z.zero) in match t.d with | None -> bot_env | Some d -> if is_bot_env t then bot_env else let cv's = get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) in let update (expr : Z.t Array.t)( c , v) = match v with - | None -> Array.set expr 0 (Z.add expr.(0) c) ; expr + | None -> Array.set expr 0 (Z.add expr.(0) c) | Some idx -> match d.(idx) with - | (Some idx_i,c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; Array.set expr (idx_i + 1) (Z.add expr.(idx_i + 1) c) ; expr - | (None, c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; expr + | (Some idx_i,c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; Array.set expr (idx_i + 1) (Z.add expr.(idx_i + 1) c) + | (None, c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) in - let final_expr = List.fold_left (fun expr cv -> update expr cv ) expr_init cv's in - let var_count = List.count_matching (fun a -> if Z.equal a Z.zero then false else true) ( List.tl ( Array.to_list final_expr)) + List.iter (update expr) cv's ; + let counting count i a = if i = 0 || Z.equal a Z.zero then count else count+1 in + let var_count = Array.fold_lefti counting 0 expr in if var_count = 0 then match Tcons1.get_typ tcons with - | EQ -> if Z.equal final_expr.(0) Z.zero then t else bot_env - | SUPEQ -> if Z.geq final_expr.(0) Z.zero then t else bot_env - | SUP -> if Z.gt final_expr.(0) Z.zero then t else bot_env - | DISEQ -> if Z.equal final_expr.(0) Z.zero then bot_env else t - | EQMOD scalar -> t (*Not supported right now - if Float.equal ( Float.modulo (Z.to_float final_expr.(0)) (convert_scalar scalar )) 0. then t else {d = None; env = t.env}*) + | EQ when Z.equal expr.(0) Z.zero -> t + | SUPEQ when Z.geq expr.(0) Z.zero -> t + | SUP when Z.gt expr.(0) Z.zero -> t + | DISEQ when not @@ Z.equal expr.(0) Z.zero -> t + | EQMOD scalar -> t + | _ -> bot_env (*Not supported right now + if Float.equal ( Float.modulo (Z.to_float expr.(0)) (convert_scalar scalar )) 0. then t else {d = None; env = t.env}*) else if var_count == 1 then - let var = List.findi (fun i a -> if Z.equal a Z.zero then false else true) @@ Array.to_list final_expr in - let c = if Z.divisible final_expr.(0) @@ Tuple2.second var then Some (Z.(- final_expr.(0) / (Tuple2.second var))) else None in + let index = Array.findi (fun a -> not @@ Z.equal a Z.zero) expr in + let var = ( index, expr.(index)) in + let c = if Z.divisible expr.(0) @@ Tuple2.second var then Some (Z.(- expr.(0) / (Tuple2.second var))) else None in match Tcons1.get_typ tcons with | EQ -> if Option.is_none c then t else let expr = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int (Option.get c)) in meet t (assign_texpr (identity t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expr) | _ -> t (*Not supported right now*) else if var_count == 2 then - let v12 = List.fold_righti (fun i a l -> if Z.equal a Z.zero then l else (i,a)::l) (List.tl @@ Array.to_list final_expr) [] in + let v12 = Array.fold_righti (fun i a l -> if Z.equal a Z.zero || i = 0 then l else (i,a)::l) expr [] in let a1 = Tuple2.second (List.hd v12) in - let a2 = Tuple2.second (List.hd v12) in + let a2 = Tuple2.second (List.hd @@ List.tl v12) in let var1 = Environment.var_of_dim t.env (Tuple2.first (List.hd v12)) in let var2 = Environment.var_of_dim t.env (Tuple2.first (List.hd @@ List.tl v12)) in match Tcons1.get_typ tcons with From b5c4bf65864f6d460e0dcc98105eb96ba0fa6fc3 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 20 Dec 2023 16:05:58 +0100 Subject: [PATCH 071/280] removed pull request file --- pull_request.md | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 pull_request.md diff --git a/pull_request.md b/pull_request.md deleted file mode 100644 index e12b271495..0000000000 --- a/pull_request.md +++ /dev/null @@ -1,14 +0,0 @@ -This draft PR introduces an analysis for linear two variable equalities (ref: [A. Flexeder, M. Petter, and H. Seidl Fast Interprocedural Linear Two-Variable Equalities](http://doi.acm.org/10.1145/2049706.2049710)). The structure of the analysis is mostly based on the affine equality analysis. - -Abstract states in this domain are represented by structs containing an optional array and an apron environment. -The bottom element is represented by a struct with "None" instead of the array. - -The arrays are modeled as proposed in the paper: Each variable is assigned to an index and each array element represents a linear relationship that must hold at the corresponding program point. -The apron environment is used to organize the order of columns and variables. - -The length of the array always corresponds to the number of variables in the environment. -If for example in the array at index j we store the element (Some i, k), this means that our analysis found out that x_i = x_j + k. If the array entry at index j is (None, k), it means that x_j = k, where k is a constant and x_i and x_j are variables. - -In order to have less code duplication, we moved some functions from affineEqualityDomain to sharedFunctions, such that affineEqualityDomain and our linearTwoVarEqualityDomain can both use them. - -This draft currently only supports equalities of the form `x = y + c`. We will extend it to support equalities of the form `a * x = b * y + c` where `x` and `y` are arbitrary variables, `a` and `b` are constants and `c` can be a linear expression of various constants and variables which are equal to a constant i.e. the linear expression must be equal to a constant. \ No newline at end of file From 561ee06c17e5590fde94aa35b7b48a850d5adb6c Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 20 Dec 2023 16:20:07 +0100 Subject: [PATCH 072/280] Removed notes and added whitespace --- .../apron/linearTwoVarEqualityDomain.apron.ml | 53 ++----------------- src/goblint.ml | 2 +- 2 files changed, 6 insertions(+), 49 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 5d0cfbd45c..6960dbc7b8 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -5,52 +5,6 @@ (** Abstract states in this domain are represented by structs containing an array and an apron environment. The arrays are modeled as proposed in the paper: Each variable is assigned to an index and each array element represents a linear relationship that must hold at the corresponding program point. The apron environment is hereby used to organize the order of columns and variables. - - APRON: - To get the index of a variable if you have a variable, use: - Environment.dim_of_var env variable - - Function naming: - _with -> in-place changes - no _with -> make a copy - - == compares address equality - != for unequal addresses - - - HOW TO RUN THE REGRESSION TESTS: - Method 1: regression test ./regtest.sh numberofdirectory numberoftest - Method 2: make test -> run entire test suite - -> the two methods have a different behaviour w.r.t. unreachable code - script update suite.rb argumentgroupname ???? No idea - - test with different flags - - gobview doesnt work with apron - -Visualize test: - ./regtest.sh 63 01 - python3 -m http.server - open http://localhost:8000/ on a browser - go to /result folder - index.xml -> main (printxml uses show) - click on program points - orange nodes: dead code - state at the beginning of the line - multiple paths-> line was divided in two parts by the analysis - - TODO: - 12. January or earlier pull request -> all features implemented - -> run on svcomp benchmarks -> to check runtime and unsoundness and crashes - - DEBUG: - 1. print stack trace while executing ./goblint: - -v option for goblint -> prints stack trace - 2. Print the debug information defined with M.tracel: - https://goblint.readthedocs.io/en/latest/developer-guide/debugging/#tracing - ./script/trace_on - --trace name1 --trace name2 - 3. Debug OCaml - gdb debug for OCaml - or with EarlyBird (apparently it will maybe not work) - or with ocamldebug *) open Batteries @@ -91,8 +45,11 @@ module EqualitiesArray = struct let num_vars = length arr in if index > num_vars then failwith "n too large" else let new_array = make (num_vars + 1) (Equality.var_zero index) in - if index = 0 then blit arr 0 new_array 1 (num_vars - 1) else - blit arr 0 new_array 0 index; if index <> num_vars then blit arr index new_array (index + 1) (num_vars - index); + if index = 0 + then blit arr 0 new_array 1 (num_vars - 1) + else blit arr 0 new_array 0 index; + if index <> num_vars + then blit arr index new_array (index + 1) (num_vars - index); new_array let add_empty_columns m indexes = (** same as add_empty_columns for Matrix (see vectorMatrix.ml)*) diff --git a/src/goblint.ml b/src/goblint.ml index e3639b2b32..25e809f9e9 100644 --- a/src/goblint.ml +++ b/src/goblint.ml @@ -86,4 +86,4 @@ let main () = exit 124 (* We do this since the evaluation order of top-level bindings is not defined, but we want `main` to run after all the other side-effects (e.g. registering analyses/solvers) have happened. *) -let () = at_exit main \ No newline at end of file +let () = at_exit main From 314137b218040bc1442c81fbd15953ff7b5df86f Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 21 Dec 2023 10:46:36 +0100 Subject: [PATCH 073/280] fixed submodule --- gobview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gobview b/gobview index 42b07f8253..3de13d7412 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit 42b07f825316052ec030370daf0d00ebe28ec092 +Subproject commit 3de13d74124ab7bc30d8be299f02570d8f498b84 From 211ba13630d886c5c08949450e36b6c8f6bc578b Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 21 Dec 2023 11:51:27 +0100 Subject: [PATCH 074/280] added named arguments for add and del --- .../apron/affineEqualityDomain.apron.ml | 4 +-- .../apron/linearTwoVarEqualityDomain.apron.ml | 32 +++++-------------- src/cdomains/apron/sharedFunctions.apron.ml | 25 +++++++-------- 3 files changed, 22 insertions(+), 39 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index c1498bb7e7..1e1a9ef12e 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -187,7 +187,7 @@ struct let meet t1 t2 = let sup_env = Environment.lce t1.env t2.env in - let t1, t2 = change_d t1 sup_env true false, change_d t2 sup_env true false + let t1, t2 = change_d t1 sup_env ~add:true ~del:false, change_d t2 sup_env ~add:true ~del:false in if is_bot t1 || is_bot t2 then bot() else let m1, m2 = Option.get t1.d, Option.get t2.d in match m1, m2 with @@ -390,7 +390,7 @@ struct Matrix.set_col_with m col_x dim_y in let m_cp = Matrix.copy m in let switched_m = List.fold_left2 (fun m' x y -> replace_col m' x y) m_cp primed_vars assigned_vars in - let res = drop_vars {d = Some switched_m; env = multi_t.env} primed_vars true in + let res = drop_vars {d = Some switched_m; env = multi_t.env} primed_vars ~del:true in let x = Option.get res.d in if Matrix.normalize_with x then {d = Some x; env = res.env} else bot () | _ -> t diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 6960dbc7b8..b0fafb714c 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -13,7 +13,6 @@ open Pretty module M = Messages open Apron open VectorMatrix -open Printf @@ -151,35 +150,20 @@ struct module EArray = EqualitiesArray include SharedFunctions.VarManagementOps (EArray) - (* For debugging *) - let print_env = Environment.print (Format.std_formatter) - let print_opt x = match x with - | Some x -> printf "%d " x - | None -> printf "None " - let print_d = Array.iter (fun (var, off) -> print_opt var; Z.print off; printf "; ") - let print_t t = begin match t.d with - | Some x -> (print_d x; print_endline "") - | None -> printf "None " end; print_env t.env; print_endline "" - let size t = match t.d with | None -> 0 | Some d -> EArray.length d - (* Returns the constant represented by an equality, if the equality represents a constant without a variable. + (* Returns the constant value of a variable, + if we know the constant value of this variable. Else it returns None. *) - let get_constant (var, off) = match var with - | None -> Some off - | _ -> None - - let get_variable_value_if_it_is_a_constant t var = - match t.d with - | None -> None - | Some d -> match d.(var) with - | (None, constant) -> Some constant + let get_constant (var, off) = match var with + | None -> Some off | _ -> None - + in + Option.bind t.d (fun d -> get_constant d.(var)) let get_coeff_vec (t: t) texp = (*Parses a Texpr to obtain a (coefficient, variable) pair list to repr. a sum of a variables that have a coefficient. If variable is None, the coefficient represents a constant offset. @@ -358,7 +342,7 @@ struct let meet t1 t2 = let sup_env = Environment.lce t1.env t2.env in - let t1, t2 = change_d t1 sup_env true false, change_d t2 sup_env true false in + let t1, t2 = change_d t1 sup_env ~add:true ~del:false, change_d t2 sup_env ~add:true ~del:false in let subst_var ts x t = match !ts with | None -> () @@ -696,7 +680,7 @@ struct match multi_t.d with | Some arr when not @@ is_top multi_t -> let switched_arr = List.fold_left2 (fun multi_t assigned_var primed_var-> assign_var multi_t assigned_var primed_var) multi_t assigned_vars primed_vars in - let res = drop_vars switched_arr primed_vars true in + let res = drop_vars switched_arr primed_vars ~del:true in let x = Option.get res.d in {d = Some x; env = res.env} | _ -> t diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 61e0c6a8de..4356ace127 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -261,7 +261,6 @@ end module type AbstractRelationalDomainRepresentation = sig type t - val hash: t -> int val equal : t -> t -> bool val compare : t -> t -> int val hash : t -> int @@ -304,41 +303,41 @@ struct let dim_add ch m = VectorMatrix.timing_wrap "dim add" (dim_add ch) m - let dim_remove (ch: Apron.Dim.change) m del = + let dim_remove (ch: Apron.Dim.change) m ~del = if Array.length ch.dim = 0 || RelDomain.is_empty m then m else ( Array.iteri (fun i x-> ch.dim.(i) <- x + i) ch.dim; let m' = if not del then let m = RelDomain.copy m in Array.fold_left (fun y x -> RelDomain.reduce_col_with y x; y) m ch.dim else m in RelDomain.remove_zero_rows @@ RelDomain.del_cols m' ch.dim) - let dim_remove ch m del = VectorMatrix.timing_wrap "dim remove" (dim_remove ch m) del + let dim_remove ch m ~del = VectorMatrix.timing_wrap "dim remove" (fun del -> dim_remove ch m ~del:del) del - let change_d t new_env add del = + let change_d t new_env ~add ~del = if Environment.equal t.env new_env then t else let dim_change = if add then Environment.dimchange t.env new_env else Environment.dimchange new_env t.env in match t.d with | None -> bot_env - | Some m -> {d = Some (if add then dim_add dim_change m else dim_remove dim_change m del); env = new_env} + | Some m -> {d = Some (if add then dim_add dim_change m else dim_remove dim_change m ~del:del); env = new_env} - let change_d t new_env add del = VectorMatrix.timing_wrap "dimension change" (change_d t new_env add) del + let change_d t new_env ~add ~del = VectorMatrix.timing_wrap "dimension change" (fun del -> change_d t new_env ~add:add ~del:del) del let vars x = Environment.ivars_only x.env let add_vars t vars = let t = copy t in let env' = Environment.add_vars t.env vars in - change_d t env' true false + change_d t env' ~add:true ~del:false let add_vars t vars = VectorMatrix.timing_wrap "add_vars" (add_vars t) vars - let drop_vars t vars del = + let drop_vars t vars ~del = let t = copy t in let env' = Environment.remove_vars t.env vars in - change_d t env' false del + change_d t env' ~add:false ~del:del let drop_vars t vars = VectorMatrix.timing_wrap "drop_vars" (drop_vars t) vars - let remove_vars t vars = drop_vars t vars false + let remove_vars t vars = drop_vars t vars ~del:false let remove_vars t vars = VectorMatrix.timing_wrap "remove_vars" (remove_vars t) vars @@ -349,7 +348,7 @@ struct let remove_filter t f = let env' = Environment.remove_filter t.env f in - change_d t env' false false + change_d t env' ~add:false ~del:false let remove_filter t f = VectorMatrix.timing_wrap "remove_filter" (remove_filter t) f @@ -361,14 +360,14 @@ struct let keep_filter t f = let t = copy t in let env' = Environment.keep_filter t.env f in - change_d t env' false false + change_d t env' ~add:false ~del:false let keep_filter t f = VectorMatrix.timing_wrap "keep_filter" (keep_filter t) f let keep_vars t vs = let t = copy t in let env' = Environment.keep_vars t.env vs in - change_d t env' false false + change_d t env' ~add:false ~del:false let keep_vars t vs = VectorMatrix.timing_wrap "keep_vars" (keep_vars t) vs From 976b3385653fdd5e36259b3eca1c330908e837ee Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 21 Dec 2023 11:56:15 +0100 Subject: [PATCH 075/280] removed a match statement --- .../apron/linearTwoVarEqualityDomain.apron.ml | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index b0fafb714c..e258150ab0 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -238,15 +238,12 @@ struct end) (None, Z.zero, Z.zero) summands_list in - match get_coeff_vec t texp with - | exception _ -> None - | summands_list -> - match sum_coefficients summands_list with - | exception _ -> None - | (var, var_coeff, offset) -> - if var = None then Some (None, offset) - else if var_coeff = Z.one then Some (var, offset) - else None + match sum_coefficients (get_coeff_vec t texp) with + | exception _ -> None + | (var, var_coeff, offset) -> + if var = None then Some (None, offset) + else if var_coeff = Z.one then Some (var, offset) + else None let get_coeff t texp = timing_wrap "coeff_vec" (get_coeff t) texp From 4e0ecf0f1eeb131836a6aaf4d81c3b2d70f2daca Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Thu, 21 Dec 2023 14:26:19 +0100 Subject: [PATCH 076/280] Small fix --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index e258150ab0..668c524818 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -181,7 +181,7 @@ struct | (a_coeff, None) -> multiply_with_Z a_coeff b | _ -> raise NotLinearExpr else - if List.compare_length_with a 1 = 0 then + if List.compare_length_with b 1 = 0 then match List.nth b 0 with | (b_coeff, None) -> multiply_with_Z b_coeff a | _ -> raise NotLinearExpr From cab810a15059e42d0e337d4e546f7f78c5683a9c Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 21 Dec 2023 15:46:35 +0100 Subject: [PATCH 077/280] improve code style --- .../apron/linearTwoVarEqualityDomain.apron.ml | 85 ++++++++----------- 1 file changed, 37 insertions(+), 48 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 668c524818..7f55d7d9bc 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -55,43 +55,34 @@ module EqualitiesArray = struct let nnc = length indexes in if nnc = 0 then m else let nc = length m in - let m' = make_empty_array (nc + nnc) in - let offset_map = Array.make nc 0 in - let add_offset_to_array_entry (var, offs) = match var with - | None -> (var, offs) - | Some var_index -> (Some (var_index + offset_map.(var_index)), offs) in let offset = ref 0 in - for j = 0 to nc - 1 do - while !offset < nnc && !offset + j = indexes.(!offset) do incr offset; done; - offset_map.(j) <- !offset; - m'.(j + !offset) <- add_offset_to_array_entry m.(j); - done; + let offset_map = Array.init nc (fun j -> + while !offset < nnc && !offset + j = indexes.(!offset) do incr offset; done; + !offset) + in let add_offset_to_array_entry (var, offs) = + Option.map (fun var_index -> var_index + offset_map.(var_index)) var, offs in + let m' = make_empty_array (nc + nnc) + in Array.iteri (fun j eq -> m'.(j + offset_map.(j)) <- add_offset_to_array_entry eq) m; m' - let del_cols m cols = - let n_c = length cols in - if n_c = 0 || length m = 0 then m + let del_cols m indexes = + let nrc = length indexes in + if nrc = 0 || length m = 0 then m else - let m_c = length m in - if m_c = n_c then [||] else - let m' = make_empty_array (m_c - n_c) in - let offset_map = Array.make m_c 0 in - let remove_offset_from_array_entry (var, offs) = match var with - | None -> (var, offs) - | Some var_index -> (Some (var_index - offset_map.(var_index)), offs) in + let nc = length m in + if nc = nrc then [||] else let offset = ref 0 in - for j = 0 to (m_c - n_c) - 1 do - while !offset < n_c && !offset + j = cols.(!offset) do incr offset; done; - offset_map.(j + !offset) <- !offset; - m'.(j) <- remove_offset_from_array_entry m.(j + !offset); - done; - m' + let offset_map = Array.init nc (fun j -> + if indexes.(!offset) = j then (incr offset; 0) else !offset) + in let remove_offset_from_array_entry (var, offs) = + Option.map (fun var_index -> var_index - offset_map.(var_index)) var, offs in + Array.init (nc - nrc) (fun j -> remove_offset_from_array_entry m.(j + offset_map.(j))) let del_cols m cols = timing_wrap "del_cols" (del_cols m) cols let is_empty m = length m = 0 - let is_top_array m = Array.fold_lefti (fun b i (a, e) -> if e == Z.zero && Option.is_some a && Option.get a == i then b else false) true m + let is_top_array m = Array.fold_lefti (fun b i (a, e) -> if Z.(e = zero) && Option.is_some a && Option.get a = i then b else false) true m let find_reference_variable d var_index = fst d.(var_index) @@ -242,7 +233,7 @@ struct | exception _ -> None | (var, var_coeff, offset) -> if var = None then Some (None, offset) - else if var_coeff = Z.one then Some (var, offset) + else if Z.(var_coeff = one) then Some (var, offset) else None @@ -607,30 +598,28 @@ struct res let forget_vars t vars = timing_wrap "forget_vars" (forget_vars t) vars + (* implemented as described on page 10 in the paper about Fast Interprocedural Linear Two-Variable Equalities in the Section "Abstract Effect of Statements" This makes a copy of the data structure, it doesn't change it in-place. *) let assign_texpr (t: VarManagement.t) var texp = - let assigned_var = Environment.dim_of_var t.env var (* this is the variable we are assigning to *) in + let assigned_var = Environment.dim_of_var t.env var (* this is the variable we are assigning to *) in begin match t.d with | Some d -> let abstract_exists_var = abstract_exists var t in begin match get_coeff t texp with - | None -> (* Statement "assigned_var = ?" (non-linear assignment) *) abstract_exists_var - | Some (exp_var_opt, off) -> - begin match exp_var_opt with - | None -> (* Statement "assigned_var = off" (constant assignment) *) - assign_const abstract_exists_var assigned_var off - | Some exp_var (* Statement "assigned_var = exp_var + off" (linear assignment) *) - -> begin if assigned_var = exp_var then - (* Statement "assigned_var = assigned_var + off" *) - subtract_const_from_var t assigned_var off - else - (* Statement "assigned_var = exp_var + off" (assigned_var is not the same as exp_var) *) - let empty_array = EqualitiesArray.make_empty_array (VarManagement.size t) in - let added_equality = empty_array.(assigned_var) <- (Some exp_var, off); empty_array in - meet abstract_exists_var {d = Some added_equality; env = t.env} - end - end + | None -> (* Statement "assigned_var = ?" (non-linear assignment) *) + abstract_exists_var + | Some (None, off) -> + (* Statement "assigned_var = off" (constant assignment) *) + assign_const abstract_exists_var assigned_var off + | Some (Some exp_var, off) when assigned_var = exp_var -> + (* Statement "assigned_var = assigned_var + off" *) + subtract_const_from_var t assigned_var off + | Some (Some exp_var, off) -> + (* Statement "assigned_var = exp_var + off" (assigned_var is not the same as exp_var) *) + let added_equality = EqualitiesArray.make_empty_array (VarManagement.size t) in + added_equality.(assigned_var) <- (Some exp_var, off); + meet abstract_exists_var {d = Some added_equality; env = t.env} end | None -> bot_env end @@ -741,7 +730,7 @@ struct List.fold_left print_element () l; print_newline () let print_final_expr l (env : Environment.t) = - let print_element _ i a = if i == 0 then print_string ((Z.to_string a) ^ " + ") else + let print_element _ i a = if i = 0 then print_string ((Z.to_string a) ^ " + ") else print_string ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env (i-1))) ^ " + ") in List.fold_lefti print_element () l; print_newline () @@ -773,7 +762,7 @@ struct | EQMOD scalar -> t | _ -> bot_env (*Not supported right now if Float.equal ( Float.modulo (Z.to_float expr.(0)) (convert_scalar scalar )) 0. then t else {d = None; env = t.env}*) - else if var_count == 1 then + else if var_count = 1 then let index = Array.findi (fun a -> not @@ Z.equal a Z.zero) expr in let var = ( index, expr.(index)) in let c = if Z.divisible expr.(0) @@ Tuple2.second var then Some (Z.(- expr.(0) / (Tuple2.second var))) else None in @@ -782,7 +771,7 @@ struct let expr = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int (Option.get c)) in meet t (assign_texpr (identity t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expr) | _ -> t (*Not supported right now*) - else if var_count == 2 then + else if var_count = 2 then let v12 = Array.fold_righti (fun i a l -> if Z.equal a Z.zero || i = 0 then l else (i,a)::l) expr [] in let a1 = Tuple2.second (List.hd v12) in let a2 = Tuple2.second (List.hd @@ List.tl v12) in From 17363799525dbb27581d39ecfdcb47029e9513b5 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Tue, 2 Jan 2024 18:31:34 +0100 Subject: [PATCH 078/280] Incorporated some change requests --- .../apron/linearTwoVarEqualityDomain.apron.ml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 7f55d7d9bc..04b8ca4648 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -406,12 +406,7 @@ struct if is_bot_env t2 || is_top t1 then false else ( let m1, m2 = Option.get t1.d, Option.get t2.d in let m1' = if env_comp = 0 then m1 else dim_add (Environment.dimchange t1.env t2.env) m1 in - let result : bool ref = ref true in - for i = 0 to Array.length m2 - 1 do - let t = m2.(i) in - if not (implies m1' t i) then result := false; - done; - !result) + Array.fold_lefti (fun b i t -> b && implies m1' t i) true m2) let leq a b = timing_wrap "leq" (leq a) b @@ -489,11 +484,7 @@ struct !result in let all_are_const_in_eq_class zts start size : bool = - let result = ref true in - for i = start to start + size - 1 do - if not (is_const zts.(i)) then result := false; - done; - !result + Array.fold_left (fun b e -> b && (is_const e)) true zts in let assign_vars_in_const_eq_class ats zts start size least_i least_b = for i = start to start + size - 1 do @@ -506,8 +497,7 @@ struct in let assign_vars_in_non_const_eq_class ats zts start size least_i least_b = for i = start to start + size - 1 do - match zts.(i) with - | (ai, t1, _) -> + let (ai, t1, _) = zts.(i) in let bj = const_offset t1 in ats.(i) <- (ai, (Some least_i, Z.sub bj least_b)) done From 0b6ca62b35c3341636a3360231504d88c4c6acda Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 3 Jan 2024 12:50:45 +0100 Subject: [PATCH 079/280] suggested changes and bug fix, added corresponding test case --- .../apron/linearTwoVarEqualityDomain.apron.ml | 162 +++++++++--------- .../77-lin2vareq/10-complicated_expression.c | 4 + 2 files changed, 83 insertions(+), 83 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 04b8ca4648..ed65821e94 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -167,16 +167,11 @@ struct List.map (fun (coeff, var) -> (Z.(number * coeff, var))) coeff_var_list in let multiply a b = (* if one of them is a constant, then multiply. Otherwise, the expression is not linear*) - if List.compare_length_with a 1 = 0 then - match List.nth a 0 with - | (a_coeff, None) -> multiply_with_Z a_coeff b - | _ -> raise NotLinearExpr - else - if List.compare_length_with b 1 = 0 then - match List.nth b 0 with - | (b_coeff, None) -> multiply_with_Z b_coeff a - | _ -> raise NotLinearExpr - else raise NotLinearExpr in + match a, b with + | [(a_coeff, None)], b -> multiply_with_Z a_coeff b + | a, [(b_coeff, None)] -> multiply_with_Z b_coeff a + | _ -> raise NotLinearExpr + in let mpqf_to_Z x = if not(Z.equal (Mpqf.get_den x) Z.one) then raise NotIntegerOffset else Mpqf.get_num x in @@ -209,30 +204,33 @@ struct | Mul -> multiply (convert_texpr e1) (convert_texpr e2) | _ -> raise NotLinearExpr end end - in convert_texpr texp + in match convert_texpr texp with + | exception NotLinearExpr -> None + | x -> Some(x) let get_coeff (t: t) texp = (*Parses a Texpr to obtain a (variable, offset) pair to repr. a sum of a variable and an offset. Returns None if the expression is not a sum between a variable (without coefficient) and a constant. *) let exception Not2VarExpr in - let sum_coefficients summands_list = - List.fold_left (fun (var, current_var_offset, curr_offset) (next_coeff, next_var) -> - begin match next_var with - | None -> (* this element represents a constant offset *) - (var, current_var_offset, Z.(curr_offset + next_coeff)) - | Some same_var -> (* this element represents a variable with a coefficient - -> it must be always the same variable, else it's not a two-variable equality*) - begin if Option.is_none var || Some same_var = var then - (Some same_var, Z.(current_var_offset + next_coeff), curr_offset) - else raise Not2VarExpr end - end) - (None, Z.zero, Z.zero) summands_list + let sum_next_coefficient (var, current_var_offset, curr_offset) (next_coeff, next_var) = + begin match next_var with + | None -> (* this element represents a constant offset *) + (var, current_var_offset, Z.(curr_offset + next_coeff)) + | Some _ -> (* this element represents a variable with a coefficient + -> it must be always the same variable, else it's not a two-variable equality*) + begin if Option.is_none var || next_var = var then + (next_var, Z.(current_var_offset + next_coeff), curr_offset) + else raise Not2VarExpr end + end in + let sum_coefficients summands_list_opt = + Option.map (List.fold_left sum_next_coefficient (None, Z.zero, Z.zero)) summands_list_opt in match sum_coefficients (get_coeff_vec t texp) with | exception _ -> None - | (var, var_coeff, offset) -> - if var = None then Some (None, offset) + | None -> None + | Some (var, var_coeff, offset) -> + if Option.is_none var then Some (None, offset) else if Z.(var_coeff = one) then Some (var, offset) else None @@ -498,8 +496,8 @@ struct let assign_vars_in_non_const_eq_class ats zts start size least_i least_b = for i = start to start + size - 1 do let (ai, t1, _) = zts.(i) in - let bj = const_offset t1 in - ats.(i) <- (ai, (Some least_i, Z.sub bj least_b)) + let bj = const_offset t1 in + ats.(i) <- (ai, (Some least_i, Z.sub bj least_b)) done in match zts with @@ -698,21 +696,6 @@ struct let substitute_exp t var exp ov = timing_wrap "substitution" (substitute_exp t var exp) ov - (** Assert a constraint expression. - - Additionally, we now also refine after positive guards when overflows might occur and there is only one variable inside the expression and the expression is an equality constraint check (==). - We check after the refinement if the new value of the variable is outside its integer bounds and if that is the case, either revert to the old state or set it to bottom. *) - - exception NotRefinable - - let meet_tcons_one_var_eq res expr = res - - (* meet_tcons -> meet with guard in if statement - texpr -> tree expr (right hand side of equality) - -> expression used to derive tcons -> used to check for overflow - tcons -> tree constraint (expression < 0) - -> does not have types (overflow is type dependent) - *) let print_coeff_vec l (env : Environment.t) = let print_element _ e = match e with | (a, Some x) -> print_string ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env x)) ^ " + ") @@ -724,6 +707,17 @@ struct print_string ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env (i-1))) ^ " + ") in List.fold_lefti print_element () l; print_newline () + (** Assert a constraint expression. + + Additionally, we now also refine after positive guards when overflows might occur and there is only one variable inside the expression and the expression is an equality constraint check (==). + We check after the refinement if the new value of the variable is outside its integer bounds and if that is the case, either revert to the old state or set it to bottom. *) + + (* meet_tcons -> meet with guard in if statement + texpr -> tree expr (right hand side of equality) + -> expression used to derive tcons -> used to check for overflow + tcons -> tree constraint (expression < 0) + -> does not have types (overflow is type dependent) + *) let meet_tcons t tcons _ = (* The expression is evaluated using an array of coefficients. The first element of the array belongs to the constant followed by the coefficients of all variables depending on the result in the array after the evaluating including resolving the constraints in t.d the tcons can be evaluated and additional constraints can be added to t.d *) @@ -731,47 +725,49 @@ struct match t.d with | None -> bot_env | Some d -> if is_bot_env t then bot_env else - let cv's = get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) in - let update (expr : Z.t Array.t)( c , v) = - match v with - | None -> Array.set expr 0 (Z.add expr.(0) c) - | Some idx -> match d.(idx) with - | (Some idx_i,c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; Array.set expr (idx_i + 1) (Z.add expr.(idx_i + 1) c) - | (None, c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) - in - List.iter (update expr) cv's ; - let counting count i a = if i = 0 || Z.equal a Z.zero then count else count+1 in - let var_count = Array.fold_lefti counting 0 expr - in - if var_count = 0 then - match Tcons1.get_typ tcons with - | EQ when Z.equal expr.(0) Z.zero -> t - | SUPEQ when Z.geq expr.(0) Z.zero -> t - | SUP when Z.gt expr.(0) Z.zero -> t - | DISEQ when not @@ Z.equal expr.(0) Z.zero -> t - | EQMOD scalar -> t - | _ -> bot_env (*Not supported right now - if Float.equal ( Float.modulo (Z.to_float expr.(0)) (convert_scalar scalar )) 0. then t else {d = None; env = t.env}*) - else if var_count = 1 then - let index = Array.findi (fun a -> not @@ Z.equal a Z.zero) expr in - let var = ( index, expr.(index)) in - let c = if Z.divisible expr.(0) @@ Tuple2.second var then Some (Z.(- expr.(0) / (Tuple2.second var))) else None in - match Tcons1.get_typ tcons with - | EQ -> if Option.is_none c then t else - let expr = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int (Option.get c)) in - meet t (assign_texpr (identity t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expr) - | _ -> t (*Not supported right now*) - else if var_count = 2 then - let v12 = Array.fold_righti (fun i a l -> if Z.equal a Z.zero || i = 0 then l else (i,a)::l) expr [] in - let a1 = Tuple2.second (List.hd v12) in - let a2 = Tuple2.second (List.hd @@ List.tl v12) in - let var1 = Environment.var_of_dim t.env (Tuple2.first (List.hd v12)) in - let var2 = Environment.var_of_dim t.env (Tuple2.first (List.hd @@ List.tl v12)) in - match Tcons1.get_typ tcons with - | EQ -> if Z.equal a1 Z.one && Z.equal a2 Z.one then meet t (assign_var (identity t.env) var1 var2) else t - | _-> t (*Not supported right now*) - else - t (*For any other case we don't know if the (in-) equality is true or false or even possible therefore we just return t *) + match get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with + | None -> t (*The (in-) equality is not linear, therefore we don't know anything about it. *) + | Some cv's -> + let update (expr : Z.t Array.t)( c , v) = + match v with + | None -> Array.set expr 0 (Z.add expr.(0) c) + | Some idx -> match d.(idx) with + | (Some idx_i,c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; Array.set expr (idx_i + 1) (Z.add expr.(idx_i + 1) c) + | (None, c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) + in + List.iter (update expr) cv's ; + let counting count i a = if i = 0 || Z.equal a Z.zero then count else count+1 in + let var_count = Array.fold_lefti counting 0 expr + in + if var_count = 0 then + match Tcons1.get_typ tcons with + | EQ when Z.equal expr.(0) Z.zero -> t + | SUPEQ when Z.geq expr.(0) Z.zero -> t + | SUP when Z.gt expr.(0) Z.zero -> t + | DISEQ when not @@ Z.equal expr.(0) Z.zero -> t + | EQMOD scalar -> t + | _ -> bot_env (*Not supported right now + if Float.equal ( Float.modulo (Z.to_float expr.(0)) (convert_scalar scalar )) 0. then t else {d = None; env = t.env}*) + else if var_count = 1 then + let index = Array.findi (fun a -> not @@ Z.equal a Z.zero) expr in + let var = ( index, expr.(index)) in + let c = if Z.divisible expr.(0) @@ Tuple2.second var then Some (Z.(- expr.(0) / (Tuple2.second var))) else None in + match Tcons1.get_typ tcons with + | EQ -> if Option.is_none c then t else + let expr = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int (Option.get c)) in + meet t (assign_texpr (identity t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expr) + | _ -> t (*Not supported right now*) + else if var_count = 2 then + let v12 = Array.fold_righti (fun i a l -> if Z.equal a Z.zero || i = 0 then l else (i,a)::l) expr [] in + let a1 = Tuple2.second (List.hd v12) in + let a2 = Tuple2.second (List.hd @@ List.tl v12) in + let var1 = Environment.var_of_dim t.env (Tuple2.first (List.hd v12)) in + let var2 = Environment.var_of_dim t.env (Tuple2.first (List.hd @@ List.tl v12)) in + match Tcons1.get_typ tcons with + | EQ -> if Z.equal a1 Z.one && Z.equal a2 Z.one then meet t (assign_var (identity t.env) var1 var2) else t + | _-> t (*Not supported right now*) + else + t (*For any other case we don't know if the (in-) equality is true or false or even possible therefore we just return t *) let meet_tcons t tcons expr = timing_wrap "meet_tcons" (meet_tcons t tcons) expr diff --git a/tests/regression/77-lin2vareq/10-complicated_expression.c b/tests/regression/77-lin2vareq/10-complicated_expression.c index 08c0a1d952..236b8574da 100644 --- a/tests/regression/77-lin2vareq/10-complicated_expression.c +++ b/tests/regression/77-lin2vareq/10-complicated_expression.c @@ -9,9 +9,13 @@ int main() { int result1 = 3 * (x + y) - 2 * x + 6; int result2 = 3 * (x + y) - 2 * k + 6; + int result3 = x * 3 - x * 2; + int result4 = x * 3 - x * k * x; __goblint_check(result1 == x + 21); // SUCCESS __goblint_check(result2 == x + 21); // UNKNOWN! + __goblint_check(result3 == x); // SUCCES + __goblint_check(result4 == x * 3 - x * k * x); // UNKNOWN! return 0; } From 7e0e26187de34c2dee4397dfd930db4d9f70d45e Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 3 Jan 2024 14:14:16 +0100 Subject: [PATCH 080/280] Worked in comments of pull request --- .../apron/linearTwoVarEqualityDomain.apron.ml | 108 ++++++++++-------- 1 file changed, 60 insertions(+), 48 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index ed65821e94..229dd99663 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -330,6 +330,14 @@ struct let sup_env = Environment.lce t1.env t2.env in let t1, t2 = change_d t1 sup_env ~add:true ~del:false, change_d t2 sup_env ~add:true ~del:false in let subst_var ts x t = + let adjust e = match e with + | (None, b') -> (None, b') + | (Some x', b') -> if x = x' then + (match t with + | (None, bt) -> (None, Z.(b' + bt)) + | (Some xt, bt) -> (Some xt, Z.(b' + bt))) + else (Some x', b')in + Stdlib.Option.iter (BatArray.modify adjust) !ts (* match !ts with | None -> () | Some ts' -> @@ -341,16 +349,14 @@ struct (match t with | (None, bt) -> ts'.(i) <- (None, Z.(b' + bt)) | (Some xt, bt) -> ts'.(i) <- (Some xt, Z.(b' + bt))) - done + done*) in let add_conj ts t i = - match !ts with - | None -> () - | Some ts' -> + let adjust ts' = (match t with | (None, b) -> (match ts'.(i) with - | (None, b') -> if b <> b' then ts := None; + | (None, b') -> if Z.(b <> b') then ts := None; | (Some j, b') -> subst_var ts j (None, Z.(b - b'))) | (Some j, b) -> (match ts'.(i) with @@ -363,17 +369,19 @@ struct (if Z.(b1 <> (b2 + b)) then ts := None) else if h1 < h2 then subst_var ts h2 (Some h1, Z.(b1 - (b + b2))) else subst_var ts h1 (Some h2, Z.(b + (b2 - b1)))))) + in + Stdlib.Option.iter adjust !ts in match t1.d, t2.d with - | None, _ -> { d = None; env = sup_env} - | _, None -> { d = None; env = sup_env} - | Some d1', Some d2' -> + | Some d1', Some d2' -> ( let ds = ref (Some (Array.copy d1')) in + Array.iteri (fun j e -> add_conj ds e j) d2'; (* if Array.length d2' <> 0 then for j = 0 to Array.length d2' - 1 do add_conj ds d2'.(j) j - done; - {d = !ds; env = sup_env} + done; *) + {d = !ds; env = sup_env} ) + | _ -> { d = None; env = sup_env} let meet t1 t2 = let res = meet t1 t2 in @@ -388,15 +396,14 @@ struct match t with | (None, b) -> (match ts.(i) with - | (None, b') -> Z.equal b b' + | (None, b') -> Z.(b = b') | _ -> false) | (Some j, b) -> (match ts.(i), ts.(j) with | (None, b1), (None, b2) -> Z.equal b1 (Z.add b2 b) | (Some h1, b1), (Some h2, b2) -> h1 = h2 && Z.equal b1 (Z.add b2 b) - | (Some _, _), (_, _) -> false - | (_, _), (Some _, _) -> false + | _ -> false ) in if env_comp = -2 || env_comp > 0 then false else @@ -419,12 +426,11 @@ struct let zts = Array.init (Array.length t1) (fun (i : int) -> (i, t1.(i), t2.(i))) in Some zts in - let const_offset t = match t with - | (_, b) -> b + let const_offset t = Tuple2.second t in let diff t1 t2 = Z.((const_offset t1) - (const_offset t2)) in - let cmp_z x y = + let cmp_z (_, t1i, t2i) (_, t1j, t2j) = let cmp_z_ref x y: int = match x, y with | (None, _), (None, _) -> 0 @@ -432,27 +438,23 @@ struct | (Some _, _), (None, _) -> 1 | (Some ii, _), (Some ij, _) -> ii - ij in - match x, y with - | (_, t1i, t2i), (_, t1j, t2j) -> - let diff_e1 = cmp_z_ref t1i t1j in - if diff_e1 <> 0 then diff_e1 else + let diff_e1 = cmp_z_ref t1i t1j in + if diff_e1 <> 0 then diff_e1 + else let diff_e2 = cmp_z_ref t2i t2j in if diff_e2 <> 0 then diff_e2 else Z.to_int (Z.((diff t1i t2i) - (diff t1j t2j))) in let sort_z_by_expr zts = + Stdlib.Option.iter (Array.stable_sort cmp_z) zts (* match zts with | None -> () - | Some zts' -> Array.stable_sort cmp_z zts' + | Some zts' -> Array.stable_sort cmp_z zts'*) in let sort_annotated ats = - let cmp_annotated x y : int = - match x, y with - | (i, _), (j, _) -> i - j + let cmp_annotated x y : int = (Tuple2.first x) - (Tuple2.first y) in - match ats with - | None -> () - | Some ats' -> Array.stable_sort cmp_annotated ats' + Stdlib.Option.iter (Array.stable_sort cmp_annotated) ats in let process_eq_classes zts = let is_const x = @@ -461,16 +463,17 @@ struct | _ -> false in let size_of_eq_class zts (start : int) : int = - let ref_elem = zts.(start) in - let remaining = (Array.length zts) - start - 1 in - let result = ref 0 in - for i = 0 to remaining do - let current_elem = zts.(start + i) in - if cmp_z ref_elem current_elem = 0 then result := !result + 1 - done; - !result + let iterate result i e = + if i >= start && cmp_z zts.(start) e == 0 then result + 1 + else result in + Array.fold_lefti iterate 0 zts in let least_index_var_in_eq_class zts start size : int * Z.t = + let (i, (_, b), (_, _)) = zts.(start)in + let result = (i,b) in + let iterate (a, b) i (j, (_, bj), (_, _))= + if i > start && j < a then (j,bj) else (a,b) in + Array.fold_lefti iterate result zts (* let result = ref (0, Z.zero) in match zts.(start) with | (i, (_, b), (_, _)) -> result := (i, b); @@ -479,19 +482,27 @@ struct | (j, (_, b), (_, _)) -> if j < fst !result then result := (j, b) done; - !result + !result*) in let all_are_const_in_eq_class zts start size : bool = Array.fold_left (fun b e -> b && (is_const e)) true zts in - let assign_vars_in_const_eq_class ats zts start size least_i least_b = + let assign_vars_in_const_eq_class ats zts start size least_i least_b = + let adjust i e = if i < start then e + else + let (ai, t1, t2) = zts.(i)in + if Z.equal (diff t1 t2) (Z.zero) then (ai, t1) + else + (ai, (Some least_i, Z.sub (Tuple2.second t1) least_b)) + (* for i = start to start + size - 1 do - match zts.(i) with - | (ai, t1, t2) -> if Z.equal (diff t1 t2) (Z.zero) then ats.(i) <- (ai, t1) - else - match t1 with - | (_, bj) -> ats.(i) <- (ai, (Some least_i, Z.sub bj least_b)) - done + let (ai, t1, t2) = zts.(i) in + if Z.equal (diff t1 t2) (Z.zero) then ats.(i) <- (ai, t1) + else + ats.(i) <- (ai, (Some least_i, Z.sub (Tuple2.second t1) least_b)) + done*) + in + BatArray.modifyi adjust ats in let assign_vars_in_non_const_eq_class ats zts start size least_i least_b = for i = start to start + size - 1 do @@ -508,9 +519,8 @@ struct while !i < Array.length zts' do let n = size_of_eq_class zts' !i in (if n = 1 then - let ztsi = zts'.(!i) in - match ztsi with - | (i', t1, t2) -> if is_const ztsi && Z.equal (diff t1 t2) (Z.zero) then + let (i', t1, t2) = zts'.(!i) in + if is_const (i', t1, t2) && Z.equal (diff t1 t2) (Z.zero) then result.(!i) <- (i', (None, const_offset t1)) else result.(!i) <- (i', (Some i', Z.zero)) else @@ -524,9 +534,10 @@ struct Some result in let strip_annotation ats = + Option.map (Array.map snd) ats (* match ats with | None -> None - | Some ats' -> Some (Array.map snd ats') + | Some ats' -> Some (Array.map snd ats')*) in let join_d t1 t2 = let zipped = ts_zip t1 t2 in @@ -758,7 +769,8 @@ struct meet t (assign_texpr (identity t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expr) | _ -> t (*Not supported right now*) else if var_count = 2 then - let v12 = Array.fold_righti (fun i a l -> if Z.equal a Z.zero || i = 0 then l else (i,a)::l) expr [] in + let get_vars i a l = if Z.equal a Z.zero || i = 0 then l else (i,a)::l in + let v12 = Array.fold_righti get_vars expr [] in let a1 = Tuple2.second (List.hd v12) in let a2 = Tuple2.second (List.hd @@ List.tl v12) in let var1 = Environment.var_of_dim t.env (Tuple2.first (List.hd v12)) in From 58a68f9682a80246968df777637e979a6512f2aa Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 3 Jan 2024 14:22:23 +0100 Subject: [PATCH 081/280] Added names to Tests --- .../regression/77-lin2vareq/02-reachability.c | 17 +++++++++ .../77-lin2vareq/03-known_expressions.c | 26 ++++++++++++++ tests/regression/77-lin2vareq/04-unknown.c | 36 +++++++++++++++++++ .../77-lin2vareq/09-constant_square.c | 13 +++++++ 4 files changed, 92 insertions(+) create mode 100644 tests/regression/77-lin2vareq/02-reachability.c create mode 100644 tests/regression/77-lin2vareq/03-known_expressions.c create mode 100644 tests/regression/77-lin2vareq/04-unknown.c create mode 100644 tests/regression/77-lin2vareq/09-constant_square.c diff --git a/tests/regression/77-lin2vareq/02-reachability.c b/tests/regression/77-lin2vareq/02-reachability.c new file mode 100644 index 0000000000..00182c38ed --- /dev/null +++ b/tests/regression/77-lin2vareq/02-reachability.c @@ -0,0 +1,17 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq +#include +#include + +int main() { + int x = 0; + int y = 0; + + x = 10; + y = 1; + + __goblint_check(x == 10 * y); //SUCCESS + + if(x == 10 * y) + return 0; + __goblint_check(0); // NOWARN (unreachable) +} diff --git a/tests/regression/77-lin2vareq/03-known_expressions.c b/tests/regression/77-lin2vareq/03-known_expressions.c new file mode 100644 index 0000000000..0a5eb1d3ec --- /dev/null +++ b/tests/regression/77-lin2vareq/03-known_expressions.c @@ -0,0 +1,26 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +// from https://dl.acm.org/doi/10.1145/2049706.2049710 + +#include + +int main() { + int x1 = 5, x2 = 10, x3 = 15, x4, x5, x6, x7, x8, x9, x10, x11, x12; + + x4 = 3 * x2 + 5; + x5 = 3 * x3 + 15; + x6 = x3 + 3; + x7 = x3 + 2; + x8 = 7 * x3 + 15; + x9 = 0; + x10 = 2 * x9 + 2; + x11 = 2 * x1 - 3; + x12 = 4 * x1 - 5; + + __goblint_check(x4 == 3 * x2 + 5); //SUCCESS + __goblint_check(x5 == 3 * x3 + 15); //SUCCESS + __goblint_check(x7 == x6 - 1); //SUCCESS + __goblint_check(x10 == 2 * x9 + 2); //SUCCESS + __goblint_check(x12 == 2 * x11 + 1); //SUCCESS + + return 0; +} \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/04-unknown.c b/tests/regression/77-lin2vareq/04-unknown.c new file mode 100644 index 0000000000..e250133918 --- /dev/null +++ b/tests/regression/77-lin2vareq/04-unknown.c @@ -0,0 +1,36 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +// from https://dl.acm.org/doi/10.1145/2049706.2049710 + +#include + +typedef int dataX_t; +typedef int dataY_t; + +dataX_t x_arr[100]; +dataY_t y_arr[100]; +dataX_t *x_ptr; +dataY_t *y_ptr; + +void access() { + *x_ptr = 42; + *y_ptr = *x_ptr + 10; +} + +int main() { + int i; + x_ptr = &x_arr[0]; + y_ptr = &y_arr[0]; + + for (i = 0; i < 100; i++) { + access(); + + __goblint_check(i == 8 * i + 0); //UNKNOWN! + __goblint_check(x_ptr == 8 * i + x_arr); //UNKNOWN! + __goblint_check(y_ptr == 4 * i + y_arr); //UNKNOWN! + + x_ptr++; + y_ptr++; + } + + return 0; +} diff --git a/tests/regression/77-lin2vareq/09-constant_square.c b/tests/regression/77-lin2vareq/09-constant_square.c new file mode 100644 index 0000000000..213a172585 --- /dev/null +++ b/tests/regression/77-lin2vareq/09-constant_square.c @@ -0,0 +1,13 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +#include + +int main() { + int x; + int y = 5; + + x = y * y; + + __goblint_check(x == y * y); //SUCCESS + + return 0; +} From eab72d695d74f3ea84be0cd720a71e209f9e4448 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 3 Jan 2024 14:54:00 +0100 Subject: [PATCH 082/280] adjustments according to comments --- .../apron/linearTwoVarEqualityDomain.apron.ml | 42 +++++-------------- tests/regression/77-lin2vareq/02.c | 17 -------- tests/regression/77-lin2vareq/03.c | 26 ------------ tests/regression/77-lin2vareq/04.c | 36 ---------------- tests/regression/77-lin2vareq/09.c | 13 ------ 5 files changed, 11 insertions(+), 123 deletions(-) delete mode 100644 tests/regression/77-lin2vareq/02.c delete mode 100644 tests/regression/77-lin2vareq/03.c delete mode 100644 tests/regression/77-lin2vareq/04.c delete mode 100644 tests/regression/77-lin2vareq/09.c diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 229dd99663..4ea977f78c 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -38,7 +38,8 @@ module EqualitiesArray = struct let empty () = [||] - let make_empty_array len = Array.mapi (fun i (x, y) -> (Some i, Z.zero)) (make len Equality.zero) + let make_empty_array len = let array = (make len Equality.zero) in + BatArray.modifyi (fun i (x, y) -> (Some i, Z.zero)) array; array let add_empty_column arr index = let num_vars = length arr in @@ -142,9 +143,7 @@ struct include SharedFunctions.VarManagementOps (EArray) - let size t = match t.d with - | None -> 0 - | Some d -> EArray.length d + let size t = BatOption.map_default (fun d -> EArray.length d) 0 t.d (* Returns the constant value of a variable, if we know the constant value of this variable. @@ -375,11 +374,7 @@ struct match t1.d, t2.d with | Some d1', Some d2' -> ( let ds = ref (Some (Array.copy d1')) in - Array.iteri (fun j e -> add_conj ds e j) d2'; (* - if Array.length d2' <> 0 then - for j = 0 to Array.length d2' - 1 do - add_conj ds d2'.(j) j - done; *) + Array.iteri (fun j e -> add_conj ds e j) d2'; {d = !ds; env = sup_env} ) | _ -> { d = None; env = sup_env} @@ -446,10 +441,7 @@ struct Z.to_int (Z.((diff t1i t2i) - (diff t1j t2j))) in let sort_z_by_expr zts = - Stdlib.Option.iter (Array.stable_sort cmp_z) zts (* - match zts with - | None -> () - | Some zts' -> Array.stable_sort cmp_z zts'*) + Stdlib.Option.iter (Array.stable_sort cmp_z) zts in let sort_annotated ats = let cmp_annotated x y : int = (Tuple2.first x) - (Tuple2.first y) @@ -473,16 +465,7 @@ struct let result = (i,b) in let iterate (a, b) i (j, (_, bj), (_, _))= if i > start && j < a then (j,bj) else (a,b) in - Array.fold_lefti iterate result zts (* - let result = ref (0, Z.zero) in - match zts.(start) with - | (i, (_, b), (_, _)) -> result := (i, b); - for i = start + 1 to start + size - 1 do - match zts.(i) with - | (j, (_, b), (_, _)) -> - if j < fst !result then result := (j, b) - done; - !result*) + Array.fold_lefti iterate result zts in let all_are_const_in_eq_class zts start size : bool = Array.fold_left (fun b e -> b && (is_const e)) true zts @@ -534,10 +517,7 @@ struct Some result in let strip_annotation ats = - Option.map (Array.map snd) ats (* - match ats with - | None -> None - | Some ats' -> Some (Array.map snd ats')*) + Option.map (Array.map snd) ats in let join_d t1 t2 = let zipped = ts_zip t1 t2 in @@ -708,15 +688,15 @@ struct let substitute_exp t var exp ov = timing_wrap "substitution" (substitute_exp t var exp) ov let print_coeff_vec l (env : Environment.t) = - let print_element _ e = match e with + let print_element e = match e with | (a, Some x) -> print_string ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env x)) ^ " + ") | (a, None) -> print_string ((Z.to_string a) ^ "+") in - List.fold_left print_element () l; print_newline () + List.iter print_element l; print_newline () let print_final_expr l (env : Environment.t) = - let print_element _ i a = if i = 0 then print_string ((Z.to_string a) ^ " + ") else + let print_element i a = if i = 0 then print_string ((Z.to_string a) ^ " + ") else print_string ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env (i-1))) ^ " + ") in - List.fold_lefti print_element () l; print_newline () + List.iteri print_element l; print_newline () (** Assert a constraint expression. diff --git a/tests/regression/77-lin2vareq/02.c b/tests/regression/77-lin2vareq/02.c deleted file mode 100644 index 00182c38ed..0000000000 --- a/tests/regression/77-lin2vareq/02.c +++ /dev/null @@ -1,17 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq -#include -#include - -int main() { - int x = 0; - int y = 0; - - x = 10; - y = 1; - - __goblint_check(x == 10 * y); //SUCCESS - - if(x == 10 * y) - return 0; - __goblint_check(0); // NOWARN (unreachable) -} diff --git a/tests/regression/77-lin2vareq/03.c b/tests/regression/77-lin2vareq/03.c deleted file mode 100644 index 0a5eb1d3ec..0000000000 --- a/tests/regression/77-lin2vareq/03.c +++ /dev/null @@ -1,26 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -// from https://dl.acm.org/doi/10.1145/2049706.2049710 - -#include - -int main() { - int x1 = 5, x2 = 10, x3 = 15, x4, x5, x6, x7, x8, x9, x10, x11, x12; - - x4 = 3 * x2 + 5; - x5 = 3 * x3 + 15; - x6 = x3 + 3; - x7 = x3 + 2; - x8 = 7 * x3 + 15; - x9 = 0; - x10 = 2 * x9 + 2; - x11 = 2 * x1 - 3; - x12 = 4 * x1 - 5; - - __goblint_check(x4 == 3 * x2 + 5); //SUCCESS - __goblint_check(x5 == 3 * x3 + 15); //SUCCESS - __goblint_check(x7 == x6 - 1); //SUCCESS - __goblint_check(x10 == 2 * x9 + 2); //SUCCESS - __goblint_check(x12 == 2 * x11 + 1); //SUCCESS - - return 0; -} \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/04.c b/tests/regression/77-lin2vareq/04.c deleted file mode 100644 index e250133918..0000000000 --- a/tests/regression/77-lin2vareq/04.c +++ /dev/null @@ -1,36 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -// from https://dl.acm.org/doi/10.1145/2049706.2049710 - -#include - -typedef int dataX_t; -typedef int dataY_t; - -dataX_t x_arr[100]; -dataY_t y_arr[100]; -dataX_t *x_ptr; -dataY_t *y_ptr; - -void access() { - *x_ptr = 42; - *y_ptr = *x_ptr + 10; -} - -int main() { - int i; - x_ptr = &x_arr[0]; - y_ptr = &y_arr[0]; - - for (i = 0; i < 100; i++) { - access(); - - __goblint_check(i == 8 * i + 0); //UNKNOWN! - __goblint_check(x_ptr == 8 * i + x_arr); //UNKNOWN! - __goblint_check(y_ptr == 4 * i + y_arr); //UNKNOWN! - - x_ptr++; - y_ptr++; - } - - return 0; -} diff --git a/tests/regression/77-lin2vareq/09.c b/tests/regression/77-lin2vareq/09.c deleted file mode 100644 index 213a172585..0000000000 --- a/tests/regression/77-lin2vareq/09.c +++ /dev/null @@ -1,13 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -#include - -int main() { - int x; - int y = 5; - - x = y * y; - - __goblint_check(x == y * y); //SUCCESS - - return 0; -} From d9114589c512fc32942e66e44317dbcb58a33999 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 3 Jan 2024 14:58:33 +0100 Subject: [PATCH 083/280] small adjustment --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 4ea977f78c..4033b26f90 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -743,9 +743,9 @@ struct let index = Array.findi (fun a -> not @@ Z.equal a Z.zero) expr in let var = ( index, expr.(index)) in let c = if Z.divisible expr.(0) @@ Tuple2.second var then Some (Z.(- expr.(0) / (Tuple2.second var))) else None in - match Tcons1.get_typ tcons with - | EQ -> if Option.is_none c then t else - let expr = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int (Option.get c)) in + match Tcons1.get_typ tcons, c with + | EQ, Some c-> + let expr = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int c) in meet t (assign_texpr (identity t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expr) | _ -> t (*Not supported right now*) else if var_count = 2 then From 667261f318d92bca1382930079ee2a7f237c4d23 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 3 Jan 2024 18:52:43 +0100 Subject: [PATCH 084/280] Implemented oerflow handling and added corresponding tests --- .../apron/affineEqualityDomain.apron.ml | 27 +----- .../apron/linearTwoVarEqualityDomain.apron.ml | 93 +++++++++++-------- src/cdomains/apron/sharedFunctions.apron.ml | 34 +++++++ .../77-lin2vareq/11-overflow_ignored.c | 16 ++++ tests/regression/77-lin2vareq/12-overflow.c | 23 +++++ .../77-lin2vareq/13-bounds_guards_ov.c | 18 ++++ 6 files changed, 149 insertions(+), 62 deletions(-) create mode 100644 tests/regression/77-lin2vareq/11-overflow_ignored.c create mode 100644 tests/regression/77-lin2vareq/12-overflow.c create mode 100644 tests/regression/77-lin2vareq/13-bounds_guards_ov.c diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 1e1a9ef12e..ae35c8e160 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -88,7 +88,7 @@ struct end (** As it is specifically used for the new affine equality domain, it can only provide bounds if the expression contains known constants only and in that case, min and max are the same. *) -module ExpressionBounds (Vc: AbstractVector) (Mx: AbstractMatrix): (SharedFunctions.ConvBounds with type t = VarManagement(Vc) (Mx).t) = +module ExpressionBounds (Vc: AbstractVector) (Mx: AbstractMatrix): (SharedFunctions.ExtendedConvBounds with type t = VarManagement(Vc) (Mx).t) = struct include VarManagement (Vc) (Mx) @@ -437,24 +437,7 @@ struct Additionally, we now also refine after positive guards when overflows might occur and there is only one variable inside the expression and the expression is an equality constraint check (==). We check after the refinement if the new value of the variable is outside its integer bounds and if that is the case, either revert to the old state or set it to bottom. *) - exception NotRefinable - - let meet_tcons_one_var_eq res expr = - let overflow_res res = if IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp expr) then res else raise NotRefinable in - match Convert.find_one_var expr with - | None -> overflow_res res - | Some v -> - let ik = Cilfacade.get_ikind v.vtype in - match Bounds.bound_texpr res (Convert.texpr1_of_cil_exp res res.env (Lval (Cil.var v)) true) with - | Some _, Some _ when not (Cil.isSigned ik) -> raise NotRefinable (* TODO: unsigned w/o bounds handled differently? *) - | Some min, Some max -> - assert (Z.equal min max); (* other bounds impossible in affeq *) - let (min_ik, max_ik) = IntDomain.Size.range ik in - if Z.compare min min_ik < 0 || Z.compare max max_ik > 0 then - if IntDomain.should_ignore_overflow ik then bot () else raise NotRefinable - else res - | exception Convert.Unsupported_CilExp _ - | _, _ -> overflow_res res + module BoundsCheck = SharedFunctions.BoundsCheckMeetTcons (Bounds) (V) let meet_tcons t tcons expr = let check_const cmp c = if cmp c Mpqf.zero then bot_env else t @@ -465,7 +448,7 @@ struct let res = if is_bot t then bot () else let opt_m = Matrix.rref_vec_with (Matrix.copy @@ Option.get t.d) e in if Option.is_none opt_m then bot () else {d = opt_m; env = t.env} in - meet_tcons_one_var_eq res expr + BoundsCheck.meet_tcons_one_var_eq res expr in match get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with | Some v -> @@ -477,12 +460,12 @@ struct | None, DISEQ | None, SUP -> begin match meet_vec v with - | exception NotRefinable -> t + | exception BoundsCheck.NotRefinable -> t | res -> if equal res t then bot_env else t end | None, EQ -> begin match meet_vec v with - | exception NotRefinable -> t + | exception BoundsCheck.NotRefinable -> t | res -> if is_bot res then bot_env else res end | _, _ -> t diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 4033b26f90..fd0ebf3e92 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -25,7 +25,7 @@ module Equality = struct let zero = (None, Z.zero) let var_zero i = (Some i, Z.zero) let to_int x = Z.to_int @@ snd x - let print : t -> unit = fun (a, b) -> match a with + let print (a, b) = match a with | None -> print_endline @@ "(None , " ^ Z.to_string b ^ ")" | Some x -> print_endline @@ "(Some " ^ string_of_int x ^ ", " ^ Z.to_string b ^ ")" end @@ -39,7 +39,7 @@ module EqualitiesArray = struct let empty () = [||] let make_empty_array len = let array = (make len Equality.zero) in - BatArray.modifyi (fun i (x, y) -> (Some i, Z.zero)) array; array + BatArray.modifyi (fun i (x, y) -> (Some i, Z.zero)) array; array let add_empty_column arr index = let num_vars = length arr in @@ -266,11 +266,14 @@ end (** TODO: overflow checking *) -module ExpressionBounds: (SharedFunctions.ConvBounds with type t = VarManagement.t) = +module ExpressionBounds: (SharedFunctions.ExtendedConvBounds with type t = VarManagement.t) = struct include VarManagement - let bound_texpr t texpr = None, None(*Some (Z.of_int (-1000)), Some (Z.of_int (1000)) TODO*) + let bound_texpr t texpr = let texpr = Texpr1.to_expr texpr in + match get_coeff t texpr with + | Some (None, offset) -> Some offset, Some offset + | _ -> None, None let bound_texpr d texpr1 = @@ -330,11 +333,11 @@ struct let t1, t2 = change_d t1 sup_env ~add:true ~del:false, change_d t2 sup_env ~add:true ~del:false in let subst_var ts x t = let adjust e = match e with - | (None, b') -> (None, b') - | (Some x', b') -> if x = x' then - (match t with - | (None, bt) -> (None, Z.(b' + bt)) - | (Some xt, bt) -> (Some xt, Z.(b' + bt))) + | (None, b') -> (None, b') + | (Some x', b') -> if x = x' then + (match t with + | (None, bt) -> (None, Z.(b' + bt)) + | (Some xt, bt) -> (Some xt, Z.(b' + bt))) else (Some x', b')in Stdlib.Option.iter (BatArray.modify adjust) !ts (* match !ts with @@ -355,7 +358,7 @@ struct (match t with | (None, b) -> (match ts'.(i) with - | (None, b') -> if Z.(b <> b') then ts := None; + | (None, b') -> if not @@ Z.equal b b' then ts := None; | (Some j, b') -> subst_var ts j (None, Z.(b - b'))) | (Some j, b) -> (match ts'.(i) with @@ -368,14 +371,14 @@ struct (if Z.(b1 <> (b2 + b)) then ts := None) else if h1 < h2 then subst_var ts h2 (Some h1, Z.(b1 - (b + b2))) else subst_var ts h1 (Some h2, Z.(b + (b2 - b1)))))) - in - Stdlib.Option.iter adjust !ts + in + Stdlib.Option.iter adjust !ts in match t1.d, t2.d with | Some d1', Some d2' -> ( - let ds = ref (Some (Array.copy d1')) in - Array.iteri (fun j e -> add_conj ds e j) d2'; - {d = !ds; env = sup_env} ) + let ds = ref (Some (Array.copy d1')) in + Array.iteri (fun j e -> add_conj ds e j) d2'; + {d = !ds; env = sup_env} ) | _ -> { d = None; env = sup_env} let meet t1 t2 = @@ -391,7 +394,7 @@ struct match t with | (None, b) -> (match ts.(i) with - | (None, b') -> Z.(b = b') + | (None, b') -> Z.equal b b' | _ -> false) | (Some j, b) -> (match ts.(i), ts.(j) with @@ -434,11 +437,11 @@ struct | (Some ii, _), (Some ij, _) -> ii - ij in let diff_e1 = cmp_z_ref t1i t1j in - if diff_e1 <> 0 then diff_e1 - else - let diff_e2 = cmp_z_ref t2i t2j in - if diff_e2 <> 0 then diff_e2 else - Z.to_int (Z.((diff t1i t2i) - (diff t1j t2j))) + if diff_e1 <> 0 then diff_e1 + else + let diff_e2 = cmp_z_ref t2i t2j in + if diff_e2 <> 0 then diff_e2 else + Z.to_int (Z.((diff t1i t2i) - (diff t1j t2j))) in let sort_z_by_expr zts = Stdlib.Option.iter (Array.stable_sort cmp_z) zts @@ -456,15 +459,15 @@ struct in let size_of_eq_class zts (start : int) : int = let iterate result i e = - if i >= start && cmp_z zts.(start) e == 0 then result + 1 - else result in + if i >= start && cmp_z zts.(start) e == 0 then result + 1 + else result in Array.fold_lefti iterate 0 zts in let least_index_var_in_eq_class zts start size : int * Z.t = let (i, (_, b), (_, _)) = zts.(start)in let result = (i,b) in let iterate (a, b) i (j, (_, bj), (_, _))= - if i > start && j < a then (j,bj) else (a,b) in + if i > start && j < a then (j,bj) else (a,b) in Array.fold_lefti iterate result zts in let all_are_const_in_eq_class zts start size : bool = @@ -472,12 +475,12 @@ struct in let assign_vars_in_const_eq_class ats zts start size least_i least_b = let adjust i e = if i < start then e - else - let (ai, t1, t2) = zts.(i)in - if Z.equal (diff t1 t2) (Z.zero) then (ai, t1) - else - (ai, (Some least_i, Z.sub (Tuple2.second t1) least_b)) - (* + else + let (ai, t1, t2) = zts.(i)in + if Z.equal (diff t1 t2) (Z.zero) then (ai, t1) + else + (ai, (Some least_i, Z.sub (Tuple2.second t1) least_b)) + (* for i = start to start + size - 1 do let (ai, t1, t2) = zts.(i) in if Z.equal (diff t1 t2) (Z.zero) then ats.(i) <- (ai, t1) @@ -504,8 +507,8 @@ struct (if n = 1 then let (i', t1, t2) = zts'.(!i) in if is_const (i', t1, t2) && Z.equal (diff t1 t2) (Z.zero) then - result.(!i) <- (i', (None, const_offset t1)) - else result.(!i) <- (i', (Some i', Z.zero)) + result.(!i) <- (i', (None, const_offset t1)) + else result.(!i) <- (i', (Some i', Z.zero)) else let (least_i, least_b) = least_index_var_in_eq_class zts' !i n in (if all_are_const_in_eq_class zts' !i n then @@ -709,10 +712,17 @@ struct tcons -> tree constraint (expression < 0) -> does not have types (overflow is type dependent) *) - let meet_tcons t tcons _ = + module BoundsCheck = SharedFunctions.BoundsCheckMeetTcons (Bounds) (V) + + let meet_tcons t tcons original_expr = (* The expression is evaluated using an array of coefficients. The first element of the array belongs to the constant followed by the coefficients of all variables depending on the result in the array after the evaluating including resolving the constraints in t.d the tcons can be evaluated and additional constraints can be added to t.d *) - let expr = Array.init ((Environment.size t.env) +1) (fun _ -> Z.zero) in + let overflow_handling res original_expr = + match BoundsCheck.meet_tcons_one_var_eq res original_expr with + | exception BoundsCheck.NotRefinable -> t + | res -> res + in + let expr = Array.init ((Environment.size t.env) + 1) (fun _ -> Z.zero) in match t.d with | None -> bot_env | Some d -> if is_bot_env t then bot_env else @@ -727,7 +737,7 @@ struct | (None, c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) in List.iter (update expr) cv's ; - let counting count i a = if i = 0 || Z.equal a Z.zero then count else count+1 in + let counting count i a = if i = 0 || Z.equal a Z.zero then count else count + 1 in let var_count = Array.fold_lefti counting 0 expr in if var_count = 0 then @@ -745,18 +755,21 @@ struct let c = if Z.divisible expr.(0) @@ Tuple2.second var then Some (Z.(- expr.(0) / (Tuple2.second var))) else None in match Tcons1.get_typ tcons, c with | EQ, Some c-> - let expr = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int c) in - meet t (assign_texpr (identity t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expr) + let expression = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int c) in + let res = meet t (assign_texpr (identity t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expression) + in overflow_handling res original_expr | _ -> t (*Not supported right now*) else if var_count = 2 then - let get_vars i a l = if Z.equal a Z.zero || i = 0 then l else (i,a)::l in - let v12 = Array.fold_righti get_vars expr [] in + let get_vars i a l = if Z.equal a Z.zero || i = 0 then l else (i - 1,a)::l in + let v12 = Array.fold_righti get_vars expr [] in let a1 = Tuple2.second (List.hd v12) in let a2 = Tuple2.second (List.hd @@ List.tl v12) in let var1 = Environment.var_of_dim t.env (Tuple2.first (List.hd v12)) in let var2 = Environment.var_of_dim t.env (Tuple2.first (List.hd @@ List.tl v12)) in match Tcons1.get_typ tcons with - | EQ -> if Z.equal a1 Z.one && Z.equal a2 Z.one then meet t (assign_var (identity t.env) var1 var2) else t + | EQ -> + let res = if Z.equal a1 Z.one && Z.equal a2 Z.one then meet t (assign_var (identity t.env) var1 var2) else t + in overflow_handling res original_expr | _-> t (*Not supported right now*) else t (*For any other case we don't know if the (in-) equality is true or false or even possible therefore we just return t *) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 4356ace127..d9f3886604 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -291,6 +291,7 @@ struct let bot () = {d = Some (RelDomain.empty ()); env = empty_env} + let get_env t = t.env let bot_env = {d = None; env = empty_env} let is_bot_env t = t.d = None @@ -488,3 +489,36 @@ module Mpqf = struct let get_num x = Z_mlgmpidl.z_of_mpzf @@ Mpqf.get_num x let hash x = 31 * (Z.hash (get_den x)) + Z.hash (get_num x) end + + +(** Overflow handling for meet_tcons in affineEqualityDomain and linearTwoVarEqualityDomain. + + It refines after positive guards when overflows might occur and there is only one variable inside the expression and the expression is an equality constraint check (==). + We check after the refinement if the new value of the variable is outside its integer bounds and if that is the case, either raise the exception "NotRefinable" or set it to bottom. *) +module type ExtendedConvBounds = +sig + include ConvBounds + val get_env: t -> Environment.t + val bot : unit -> t +end +module BoundsCheckMeetTcons (Bounds: ExtendedConvBounds) (V: SV) = struct + exception NotRefinable + module Convert = Convert (V) (Bounds) (struct let allow_global = true end) (Tracked) + + let meet_tcons_one_var_eq res expr = + let overflow_res res = if IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp expr) then res else raise NotRefinable in + match Convert.find_one_var expr with + | None -> overflow_res res + | Some v -> + let ik = Cilfacade.get_ikind v.vtype in + match Bounds.bound_texpr res (Convert.texpr1_of_cil_exp res (Bounds.get_env res) (Lval (Cil.var v)) true) with + | Some _, Some _ when not (Cil.isSigned ik) -> raise NotRefinable + | Some min, Some max -> + assert (Z.equal min max); (* other bounds impossible in affeq *) + let (min_ik, max_ik) = IntDomain.Size.range ik in + if Z.compare min min_ik < 0 || Z.compare max max_ik > 0 then + if IntDomain.should_ignore_overflow ik then Bounds.bot () else raise NotRefinable + else res + | exception Convert.Unsupported_CilExp _ + | _, _ -> overflow_res res +end \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/11-overflow_ignored.c b/tests/regression/77-lin2vareq/11-overflow_ignored.c new file mode 100644 index 0000000000..a575aca6bc --- /dev/null +++ b/tests/regression/77-lin2vareq/11-overflow_ignored.c @@ -0,0 +1,16 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none + +#include + +int main() { + int x; + int k; + int y; + + x = k + 1; + + __goblint_check(x == k + 1); // SUCCESS + + + return 0; +} diff --git a/tests/regression/77-lin2vareq/12-overflow.c b/tests/regression/77-lin2vareq/12-overflow.c new file mode 100644 index 0000000000..37b56a622a --- /dev/null +++ b/tests/regression/77-lin2vareq/12-overflow.c @@ -0,0 +1,23 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --enable ana.int.interval + +#include + +int main() { + int x; + int k; + int y; + + x = k + 1; + //there might be an overflow + __goblint_check(x == k + 1); // UNKNOWN! + + int unknown; + + if (unknown < 300 && unknown > 0) { + x = unknown; + // an overflow is not possible + __goblint_check(x == unknown); // SUCCESS + } + + return 0; +} \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/13-bounds_guards_ov.c b/tests/regression/77-lin2vareq/13-bounds_guards_ov.c new file mode 100644 index 0000000000..574e9a71a7 --- /dev/null +++ b/tests/regression/77-lin2vareq/13-bounds_guards_ov.c @@ -0,0 +1,18 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_top --enable ana.int.interval +// same test as 63-affeq/10-bounds_guards.ov.c +int main() { + int x, y; + int p = 0; + + if (x - 2 == __INT32_MAX__) { + __goblint_check(x == __INT32_MAX__ + 2); //UNKNOWN! + p = 1; + } + + __goblint_check(p == 0); //UNKNOWN! + + if (x + y == __INT32_MAX__) { + __goblint_check(1); + } + +} \ No newline at end of file From 2845adfef0fade707ee0f150fe71e2c774bba693 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 3 Jan 2024 19:06:27 +0100 Subject: [PATCH 085/280] bug fix --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 8 ++++---- tests/regression/77-lin2vareq/11-overflow_ignored.c | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index fd0ebf3e92..3d4af9e5cd 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -248,20 +248,20 @@ struct match t.d with | None -> t | Some t_d -> let d = EArray.copy t_d in - let subtract_const_from_var_for_single_equality const index element = + let subtract_const_from_var_for_single_equality index element = let (eq_var_opt, off2) = d.(index) in if index = var then match eq_var_opt with | None -> d.(index) <- (None, Z.(off2 + const)) - | Some eq_var -> begin if eq_var <> index then d.(index) <- (None, Z.(off2 + const)) end + | Some eq_var -> begin if eq_var <> index then d.(index) <- (eq_var_opt, Z.(off2 + const)) end else begin match eq_var_opt with | Some eq_var -> - if eq_var = var then d.(index) <- (Some eq_var, Z.(off2 - const)) + if eq_var = var then d.(index) <- (eq_var_opt, Z.(off2 - const)) | None -> () end in - EArray.iteri (subtract_const_from_var_for_single_equality const) d; {d = Some d; env = t.env} + EArray.iteri (subtract_const_from_var_for_single_equality) d; {d = Some d; env = t.env} end diff --git a/tests/regression/77-lin2vareq/11-overflow_ignored.c b/tests/regression/77-lin2vareq/11-overflow_ignored.c index a575aca6bc..2d226bc75f 100644 --- a/tests/regression/77-lin2vareq/11-overflow_ignored.c +++ b/tests/regression/77-lin2vareq/11-overflow_ignored.c @@ -11,6 +11,12 @@ int main() { __goblint_check(x == k + 1); // SUCCESS + for (int i = 0; i < 7; i++) { + x++; + k++; + } + + __goblint_check(x == k + 1); // SUCCESS return 0; } From 968232e55ff98a9b1fb3979cb4cef8b77fa08561 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Thu, 4 Jan 2024 13:18:07 +0100 Subject: [PATCH 086/280] new and shorter Join function --- .../apron/linearTwoVarEqualityDomain.apron.ml | 94 +++++++++++++++---- 1 file changed, 74 insertions(+), 20 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 3d4af9e5cd..ae25e93e2e 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -38,8 +38,7 @@ module EqualitiesArray = struct let empty () = [||] - let make_empty_array len = let array = (make len Equality.zero) in - BatArray.modifyi (fun i (x, y) -> (Some i, Z.zero)) array; array + let make_empty_array len = Array.init len (fun i -> (Some i, Z.zero)) let add_empty_column arr index = let num_vars = length arr in @@ -418,7 +417,54 @@ struct if M.tracing then M.tracel "leq" "leq a: %s b: %s -> %b \n" (show t1) (show t2) res ; res - let join a b = + let join a b = + let join_d ad bd = + let table = BatList.map2i (fun i a b -> (i,a,b)) (Array.to_list ad) (Array.to_list bd) in + let const_offset t = Tuple2.second t in + let diff t1 t2 = Z.((const_offset t1) - (const_offset t2)) in + let cmp_z (_, t1i, t2i) (_, t1j, t2j) = + let cmp_z_ref (x,_) (y,_): int = + match x, y with + | None, None -> 0 + | None, Some _ -> -1 + | Some _, None -> 1 + | Some ii, Some ij -> ii - ij + in + let diff_e1 = cmp_z_ref t1i t1j in + if diff_e1 <> 0 then diff_e1 + else + let diff_e2 = cmp_z_ref t2i t2j in + if diff_e2 <> 0 then diff_e2 else + Z.to_int (Z.((diff t1i t2i) - (diff t1j t2j))) + in + let new_components = BatList.group cmp_z table in + let modify idx_h b_h (idx, (opt1, z1), (opt2, z2)) = + if idx_h = idx then ad.(idx) <- (Some idx, Z.zero) + else if Option.(opt1 = opt2) && Z.(z1 = z2) then () + else ad.(idx) <- (Some idx_h, Z.(z1 - b_h)) + in + let iterate l = + match l with + | (idx_h, (_, b_h), _) :: t -> List.iter (modify idx_h b_h) l + | [] -> () (*This should not happen, consider throughing exception*) + in + List.iter iterate new_components; Some ad in + if is_bot_env a then b else if is_bot_env b then a else + match Option.get a.d, Option.get b.d with + | x, y when is_top a || is_top b -> let new_env = Environment.lce a.env b.env + in (identity new_env) + | x, y when (Environment.compare a.env b.env <> 0) -> + let sup_env = Environment.lce a.env b.env in + let mod_x = dim_add (Environment.dimchange a.env sup_env) x in + let mod_y = dim_add (Environment.dimchange b.env sup_env) y in + {d = join_d mod_x mod_y; env = sup_env} + | x, y when EArray.equal x y -> {d = Some x; env = a.env} + | x, y -> {d = join_d x y; env = a.env} + + + + (* + let join' a b = let ts_zip t1 t2 = if Array.length t1 <> Array.length t2 then None else let zts = Array.init (Array.length t1) (fun (i : int) -> (i, t1.(i), t2.(i))) in @@ -480,26 +526,32 @@ struct if Z.equal (diff t1 t2) (Z.zero) then (ai, t1) else (ai, (Some least_i, Z.sub (Tuple2.second t1) least_b)) - (* - for i = start to start + size - 1 do - let (ai, t1, t2) = zts.(i) in - if Z.equal (diff t1 t2) (Z.zero) then ats.(i) <- (ai, t1) - else - ats.(i) <- (ai, (Some least_i, Z.sub (Tuple2.second t1) least_b)) - done*) in BatArray.modifyi adjust ats in let assign_vars_in_non_const_eq_class ats zts start size least_i least_b = - for i = start to start + size - 1 do - let (ai, t1, _) = zts.(i) in - let bj = const_offset t1 in - ats.(i) <- (ai, (Some least_i, Z.sub bj least_b)) - done + let adjust i e = if i < start then e + else ( + let (ai, t1, _) = zts.(i) in + let bj = const_offset t1 in + (ai, (Some least_i, Z.sub bj least_b))) + in + BatArray.modifyi adjust ats in - match zts with - | None -> None - | Some zts' -> + let adjust_zts zts' = + (* + let mapi_zts i e = + let n = size_of_eq_class zts' i in + if n = 1 then + let (i', t1, t2) = zts'.(i) in + if is_const (i', t1, t2) && Z.equal (diff t1 t2) (Z.zero) then + (i', (None, const_offset t1)) + else (i', (Some i', Z.zero)) + else + let (least_i, least_b) = least_index_var_in_eq_class zts' i n in + (if all_are_const_in_eq_class zts' i n then + assign_vars_in_const_eq_class result zts' i n least_i least_b + else assign_vars_in_non_const_eq_class result zts'); e*) let result = Array.make (Array.length zts') (0, (None, Z.zero)) in let i = ref 0 in while !i < Array.length zts' do @@ -517,7 +569,9 @@ struct ); i := !i + n; done; - Some result + result + in + Stdlib.Option.map adjust_zts zts in let strip_annotation ats = Option.map (Array.map snd) ats @@ -541,7 +595,7 @@ struct {d = join_d mod_x mod_y; env = sup_env} | x, y when EArray.equal x y -> {d = Some x; env = a.env} | x, y -> {d = join_d x y; env = a.env} - +*) let join a b = timing_wrap "join" (join a) b let join a b = From 7c5dd9e15dac0171b945c64bc91441fe838bf31a Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Thu, 4 Jan 2024 13:24:11 +0100 Subject: [PATCH 087/280] Added some comments --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index ae25e93e2e..fe0e46874a 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -419,9 +419,11 @@ struct let join a b = let join_d ad bd = + (*This is the table which is later grouped*) let table = BatList.map2i (fun i a b -> (i,a,b)) (Array.to_list ad) (Array.to_list bd) in let const_offset t = Tuple2.second t in let diff t1 t2 = Z.((const_offset t1) - (const_offset t2)) in + (*compare two variables for grouping depending on delta function and reference index*) let cmp_z (_, t1i, t2i) (_, t1j, t2j) = let cmp_z_ref (x,_) (y,_): int = match x, y with @@ -437,7 +439,9 @@ struct if diff_e2 <> 0 then diff_e2 else Z.to_int (Z.((diff t1i t2i) - (diff t1j t2j))) in + (*Calculate new components as groups*) let new_components = BatList.group cmp_z table in + (*Adjust the domain array to represent the new components*) let modify idx_h b_h (idx, (opt1, z1), (opt2, z2)) = if idx_h = idx then ad.(idx) <- (Some idx, Z.zero) else if Option.(opt1 = opt2) && Z.(z1 = z2) then () @@ -449,7 +453,9 @@ struct | [] -> () (*This should not happen, consider throughing exception*) in List.iter iterate new_components; Some ad in - if is_bot_env a then b else if is_bot_env b then a else + (*Normalize the two domains a and b such that both talk about the same variables*) + if is_bot_env a then b else if is_bot_env b then a + else match Option.get a.d, Option.get b.d with | x, y when is_top a || is_top b -> let new_env = Environment.lce a.env b.env in (identity new_env) From c8393ff60ae5d98069e0be5201096f07064081fa Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Thu, 4 Jan 2024 13:46:52 +0100 Subject: [PATCH 088/280] fix format --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index fe0e46874a..ef2ae2b6bd 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -441,7 +441,7 @@ struct in (*Calculate new components as groups*) let new_components = BatList.group cmp_z table in - (*Adjust the domain array to represent the new components*) + (*Adjust the domain array to represent the new components*) let modify idx_h b_h (idx, (opt1, z1), (opt2, z2)) = if idx_h = idx then ad.(idx) <- (Some idx, Z.zero) else if Option.(opt1 = opt2) && Z.(z1 = z2) then () @@ -451,8 +451,8 @@ struct match l with | (idx_h, (_, b_h), _) :: t -> List.iter (modify idx_h b_h) l | [] -> () (*This should not happen, consider throughing exception*) - in - List.iter iterate new_components; Some ad in + in + List.iter iterate new_components; Some ad in (*Normalize the two domains a and b such that both talk about the same variables*) if is_bot_env a then b else if is_bot_env b then a else @@ -466,7 +466,7 @@ struct {d = join_d mod_x mod_y; env = sup_env} | x, y when EArray.equal x y -> {d = Some x; env = a.env} | x, y -> {d = join_d x y; env = a.env} - + (* From 3764ad7757d9d6f8f53c53ae9a02f9a4704436a6 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Thu, 4 Jan 2024 13:58:14 +0100 Subject: [PATCH 089/280] Small fix in join --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index ef2ae2b6bd..7776662455 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -419,6 +419,8 @@ struct let join a b = let join_d ad bd = + (*use copy of ad because result is later saved in there*) + let ad = Array.copy ad in (*This is the table which is later grouped*) let table = BatList.map2i (fun i a b -> (i,a,b)) (Array.to_list ad) (Array.to_list bd) in let const_offset t = Tuple2.second t in From 9922c5ca7181094d8aa93e6d7380647cb73a3552 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Thu, 4 Jan 2024 14:01:34 +0100 Subject: [PATCH 090/280] replaced identity with top_of --- .../apron/linearTwoVarEqualityDomain.apron.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 7776662455..3742241bcd 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -303,13 +303,13 @@ struct let is_bot t = equal t (bot ()) let is_bot_env t = t.d = None - (*this shows "top" for a specific environment to enable the calculations. It is the identity of all equalities*) - let identity env = {d = Some (Array.init (Environment.size env) (fun i -> (Some i, Z.zero))); env = env} + (*this shows "top" for a specific environment to enable the calculations. It is the top_of of all equalities*) + let top_of env = {d = Some (Array.init (Environment.size env) (fun i -> (Some i, Z.zero))); env = env} (*Should never be called but implemented for completeness *) let top () = {d = Some (EArray.empty()); env = empty_env} - (*is_top returns true for identity array and empty array *) + (*is_top returns true for top_of array and empty array *) let is_top t = GobOption.exists EArray.is_top_array t.d (* prints the current variable equalities with resolved variable names *) @@ -460,7 +460,7 @@ struct else match Option.get a.d, Option.get b.d with | x, y when is_top a || is_top b -> let new_env = Environment.lce a.env b.env - in (identity new_env) + in (top_of new_env) | x, y when (Environment.compare a.env b.env <> 0) -> let sup_env = Environment.lce a.env b.env in let mod_x = dim_add (Environment.dimchange a.env sup_env) x in @@ -595,7 +595,7 @@ struct if is_bot_env a then b else if is_bot_env b then a else match Option.get a.d, Option.get b.d with | x, y when is_top a || is_top b -> let new_env = Environment.lce a.env b.env - in (identity new_env) + in (top_of new_env) | x, y when (Environment.compare a.env b.env <> 0) -> let sup_env = Environment.lce a.env b.env in let mod_x = dim_add (Environment.dimchange a.env sup_env) x in @@ -818,7 +818,7 @@ struct match Tcons1.get_typ tcons, c with | EQ, Some c-> let expression = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int c) in - let res = meet t (assign_texpr (identity t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expression) + let res = meet t (assign_texpr (top_of t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expression) in overflow_handling res original_expr | _ -> t (*Not supported right now*) else if var_count = 2 then @@ -830,7 +830,7 @@ struct let var2 = Environment.var_of_dim t.env (Tuple2.first (List.hd @@ List.tl v12)) in match Tcons1.get_typ tcons with | EQ -> - let res = if Z.equal a1 Z.one && Z.equal a2 Z.one then meet t (assign_var (identity t.env) var1 var2) else t + let res = if Z.equal a1 Z.one && Z.equal a2 Z.one then meet t (assign_var (top_of t.env) var1 var2) else t in overflow_handling res original_expr | _-> t (*Not supported right now*) else From ad7fd1e91e04c83d5f07a68705e4c8df28319dc8 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 4 Jan 2024 14:55:22 +0100 Subject: [PATCH 091/280] added GobArray patch by Michael Schwarz --- .../apron/linearTwoVarEqualityDomain.apron.ml | 12 ++++---- src/util/std/gobArray.ml | 30 +++++++++++++++++++ src/util/std/goblint_std.ml | 1 + 3 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 src/util/std/gobArray.ml diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 3742241bcd..235cc4692d 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -82,7 +82,7 @@ module EqualitiesArray = struct let is_empty m = length m = 0 - let is_top_array m = Array.fold_lefti (fun b i (a, e) -> if Z.(e = zero) && Option.is_some a && Option.get a = i then b else false) true m + let is_top_array = GobArray.for_alli (fun i (a, e) -> GobOption.exists ((=) i) a && Z.(e = zero)) let find_reference_variable d var_index = fst d.(var_index) @@ -405,10 +405,10 @@ struct in if env_comp = -2 || env_comp > 0 then false else if is_bot_env t1 || is_top t2 then true else - if is_bot_env t2 || is_top t1 then false else ( + if is_bot_env t2 || is_top t1 then false else let m1, m2 = Option.get t1.d, Option.get t2.d in let m1' = if env_comp = 0 then m1 else dim_add (Environment.dimchange t1.env t2.env) m1 in - Array.fold_lefti (fun b i t -> b && implies m1' t i) true m2) + GobArray.for_alli (fun i t -> implies m1' t i) m2 let leq a b = timing_wrap "leq" (leq a) b @@ -446,7 +446,7 @@ struct (*Adjust the domain array to represent the new components*) let modify idx_h b_h (idx, (opt1, z1), (opt2, z2)) = if idx_h = idx then ad.(idx) <- (Some idx, Z.zero) - else if Option.(opt1 = opt2) && Z.(z1 = z2) then () + else if opt1 = opt2 && z1 = z2 then () else ad.(idx) <- (Some idx_h, Z.(z1 - b_h)) in let iterate l = @@ -799,9 +799,7 @@ struct | (None, c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) in List.iter (update expr) cv's ; - let counting count i a = if i = 0 || Z.equal a Z.zero then count else count + 1 in - let var_count = Array.fold_lefti counting 0 expr - in + let var_count = GobArray.count_matchingi (fun i a -> i <> 0 && not (Z.equal a Z.zero)) expr in if var_count = 0 then match Tcons1.get_typ tcons with | EQ when Z.equal expr.(0) Z.zero -> t diff --git a/src/util/std/gobArray.ml b/src/util/std/gobArray.ml new file mode 100644 index 0000000000..c88e3d8184 --- /dev/null +++ b/src/util/std/gobArray.ml @@ -0,0 +1,30 @@ +open BatArray + +(** Implementations here are from batteries and slightly modified. They are tuned for performance and not necessarily the same style non-library code should be written. *) + +let existsi p xs = + let n = length xs in + let rec loop i = + if i = n then false + else if p i (unsafe_get xs i) then true + else loop (succ i) + in + loop 0 + +let for_alli p xs = + let n = length xs in + let rec loop i = + if i = n then true + else if p i (unsafe_get xs i) then loop (succ i) + else false + in + loop 0 + +let count_matchingi p xs = + let n = length xs in + let count = ref 0 in + for i = 0 to n - 1 do + if p i (unsafe_get xs i) then + incr count + done; + !count diff --git a/src/util/std/goblint_std.ml b/src/util/std/goblint_std.ml index 0d548cac08..c8f3144880 100644 --- a/src/util/std/goblint_std.ml +++ b/src/util/std/goblint_std.ml @@ -12,6 +12,7 @@ module GobResult = GobResult module GobOption = GobOption module GobSys = GobSys module GobUnix = GobUnix +module GobArray = GobArray (** {1 Other libraries} From f872e5549c0599745c524df663a87075a2fd8497 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 4 Jan 2024 15:21:28 +0100 Subject: [PATCH 092/280] bug fix and refactored meet_tcons --- .../apron/linearTwoVarEqualityDomain.apron.ml | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 235cc4692d..0879d12bc5 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -779,48 +779,52 @@ struct let meet_tcons t tcons original_expr = (* The expression is evaluated using an array of coefficients. The first element of the array belongs to the constant followed by the coefficients of all variables depending on the result in the array after the evaluating including resolving the constraints in t.d the tcons can be evaluated and additional constraints can be added to t.d *) - let overflow_handling res original_expr = - match BoundsCheck.meet_tcons_one_var_eq res original_expr with - | exception BoundsCheck.NotRefinable -> t - | res -> res - in - let expr = Array.init ((Environment.size t.env) + 1) (fun _ -> Z.zero) in match t.d with | None -> bot_env - | Some d -> if is_bot_env t then bot_env else + | Some d -> + let overflow_handling res original_expr = + match BoundsCheck.meet_tcons_one_var_eq res original_expr with + | exception BoundsCheck.NotRefinable -> t + | res -> res + in + let expr = Array.init (Environment.size t.env) (fun _ -> Z.zero) in + let constant = ref (Z.zero) in + if is_bot_env t then bot_env else match get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with | None -> t (*The (in-) equality is not linear, therefore we don't know anything about it. *) | Some cv's -> - let update (expr : Z.t Array.t)( c , v) = + let update (c, v) = match v with - | None -> Array.set expr 0 (Z.add expr.(0) c) + | None -> constant := Z.(!constant + c) | Some idx -> match d.(idx) with - | (Some idx_i,c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) ; Array.set expr (idx_i + 1) (Z.add expr.(idx_i + 1) c) - | (None, c_i) -> Array.set expr 0 (Z.add expr.(0) (Z.mul c c_i)) + | (Some idx_i, c_i) -> constant := Z.(!constant + (c * c_i)); + expr.(idx_i) <- Z.(expr.(idx_i) + c) + | (None, c_i) -> constant := Z.(!constant + (c * c_i)) in - List.iter (update expr) cv's ; - let var_count = GobArray.count_matchingi (fun i a -> i <> 0 && not (Z.equal a Z.zero)) expr in + List.iter update cv's; + let var_count = GobArray.count_matchingi (fun _ a -> a <> Z.zero) expr in if var_count = 0 then match Tcons1.get_typ tcons with - | EQ when Z.equal expr.(0) Z.zero -> t - | SUPEQ when Z.geq expr.(0) Z.zero -> t - | SUP when Z.gt expr.(0) Z.zero -> t - | DISEQ when not @@ Z.equal expr.(0) Z.zero -> t + | EQ when Z.equal !constant Z.zero -> t + | SUPEQ when Z.geq !constant Z.zero -> t + | SUP when Z.gt !constant Z.zero -> t + | DISEQ when not @@ Z.equal !constant Z.zero -> t | EQMOD scalar -> t | _ -> bot_env (*Not supported right now if Float.equal ( Float.modulo (Z.to_float expr.(0)) (convert_scalar scalar )) 0. then t else {d = None; env = t.env}*) else if var_count = 1 then let index = Array.findi (fun a -> not @@ Z.equal a Z.zero) expr in - let var = ( index, expr.(index)) in - let c = if Z.divisible expr.(0) @@ Tuple2.second var then Some (Z.(- expr.(0) / (Tuple2.second var))) else None in + let var = (index, expr.(index)) in + let c = if Z.divisible !constant @@ Tuple2.second var then Some (Z.(-(!constant) / (Tuple2.second var))) + else None in match Tcons1.get_typ tcons, c with - | EQ, Some c-> - let expression = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int c) in + | EQ, Some c -> + let expression = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int c) in let res = meet t (assign_texpr (top_of t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expression) in overflow_handling res original_expr | _ -> t (*Not supported right now*) else if var_count = 2 then - let get_vars i a l = if Z.equal a Z.zero || i = 0 then l else (i - 1,a)::l in + let get_vars i a l = if Z.equal a Z.zero then l else (i, a)::l in let v12 = Array.fold_righti get_vars expr [] in let a1 = Tuple2.second (List.hd v12) in let a2 = Tuple2.second (List.hd @@ List.tl v12) in @@ -828,7 +832,9 @@ struct let var2 = Environment.var_of_dim t.env (Tuple2.first (List.hd @@ List.tl v12)) in match Tcons1.get_typ tcons with | EQ -> - let res = if Z.equal a1 Z.one && Z.equal a2 Z.one then meet t (assign_var (top_of t.env) var1 var2) else t + let res = if Z.equal a1 Z.one && Z.equal a2 Z.one + then meet t (assign_var (top_of t.env) var1 var2) + else t in overflow_handling res original_expr | _-> t (*Not supported right now*) else From 46802dd0fa8b0be6791ac910690f260b1bf9802c Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 4 Jan 2024 15:27:57 +0100 Subject: [PATCH 093/280] Added two test cases for join --- .../77-lin2vareq/14-join_after_guard.c | 17 ++++++++++++++ .../77-lin2vareq/15-join-non-constant.c | 22 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 tests/regression/77-lin2vareq/14-join_after_guard.c create mode 100644 tests/regression/77-lin2vareq/15-join-non-constant.c diff --git a/tests/regression/77-lin2vareq/14-join_after_guard.c b/tests/regression/77-lin2vareq/14-join_after_guard.c new file mode 100644 index 0000000000..73d13384f3 --- /dev/null +++ b/tests/regression/77-lin2vareq/14-join_after_guard.c @@ -0,0 +1,17 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq --set ana.relation.privatization top --set sem.int.signed_overflow assume_none +// from 63-affeq/04-join-after-guard +int main(void) { + int zero = 0; + int x = 0; + + int t, r, d; + if (t) { + r = 10; + d = 500; + } else { + r = 10; + } + __goblint_check(r == 10); //SUCCESS + __goblint_check(d == 500); //UNKNOWN! + return 0; +} diff --git a/tests/regression/77-lin2vareq/15-join-non-constant.c b/tests/regression/77-lin2vareq/15-join-non-constant.c new file mode 100644 index 0000000000..16b1ccd33e --- /dev/null +++ b/tests/regression/77-lin2vareq/15-join-non-constant.c @@ -0,0 +1,22 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --set ana.relation.privatization top --set sem.int.signed_overflow assume_none + +int main(void) { + int a, b, c, d; + + int t; + if (t) { + b = a + 2; + c = a + 7; + d = a + 1; + } else { + b = a + 30; + c = a + 35; + d = a + 10; + } + __goblint_check(c == b + 5); // SUCCESS + __goblint_check(c == b + 3); // FAILURE + __goblint_check(d == b + 3); // UNKNOWN! + __goblint_check(b == a + 2); // UNKNOWN! + + return 0; +} From 5f56257cb8d06adaf4fffb01899cd12017cf1478 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 4 Jan 2024 15:44:46 +0100 Subject: [PATCH 094/280] Fix trailing whitespace and newlines --- .../linearTwoVarEqualityAnalysis.apron.ml | 2 +- .../apron/linearTwoVarEqualityDomain.apron.ml | 319 +++++++++--------- src/cdomains/apron/sharedFunctions.apron.ml | 6 +- tests/regression/77-lin2vareq/00-basic.c | 1 - tests/regression/77-lin2vareq/01-iteration.c | 2 +- .../regression/77-lin2vareq/02-reachability.c | 2 +- .../77-lin2vareq/03-known_expressions.c | 2 +- tests/regression/77-lin2vareq/04-unknown.c | 4 +- .../regression/77-lin2vareq/05-associative.c | 2 +- .../regression/77-lin2vareq/07-commutative.c | 2 +- tests/regression/77-lin2vareq/08-loop.c | 2 +- tests/regression/77-lin2vareq/12-overflow.c | 2 +- .../77-lin2vareq/13-bounds_guards_ov.c | 2 +- 13 files changed, 173 insertions(+), 175 deletions(-) diff --git a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml index 786e38b73c..cc2af20cb2 100644 --- a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml +++ b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml @@ -32,5 +32,5 @@ let after_config () = MCP.register_analysis (module Spec : MCPSpec); GobConfig.set_string "ana.path_sens[+]" (Spec.name ()) -let _ = +let _ = AfterConfig.register after_config diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 0879d12bc5..58747a05cd 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -38,16 +38,16 @@ module EqualitiesArray = struct let empty () = [||] - let make_empty_array len = Array.init len (fun i -> (Some i, Z.zero)) + let make_empty_array len = Array.init len (fun i -> (Some i, Z.zero)) - let add_empty_column arr index = + let add_empty_column arr index = let num_vars = length arr in if index > num_vars then failwith "n too large" else let new_array = make (num_vars + 1) (Equality.var_zero index) in - if index = 0 - then blit arr 0 new_array 1 (num_vars - 1) - else blit arr 0 new_array 0 index; - if index <> num_vars + if index = 0 + then blit arr 0 new_array 1 (num_vars - 1) + else blit arr 0 new_array 0 index; + if index <> num_vars then blit arr index new_array (index + 1) (num_vars - index); new_array @@ -59,9 +59,9 @@ module EqualitiesArray = struct let offset_map = Array.init nc (fun j -> while !offset < nnc && !offset + j = indexes.(!offset) do incr offset; done; !offset) - in let add_offset_to_array_entry (var, offs) = + in let add_offset_to_array_entry (var, offs) = Option.map (fun var_index -> var_index + offset_map.(var_index)) var, offs in - let m' = make_empty_array (nc + nnc) + let m' = make_empty_array (nc + nnc) in Array.iteri (fun j eq -> m'.(j + offset_map.(j)) <- add_offset_to_array_entry eq) m; m' @@ -74,7 +74,7 @@ module EqualitiesArray = struct let offset = ref 0 in let offset_map = Array.init nc (fun j -> if indexes.(!offset) = j then (incr offset; 0) else !offset) - in let remove_offset_from_array_entry (var, offs) = + in let remove_offset_from_array_entry (var, offs) = Option.map (fun var_index -> var_index - offset_map.(var_index)) var, offs in Array.init (nc - nrc) (fun j -> remove_offset_from_array_entry m.(j + offset_map.(j))) @@ -87,24 +87,24 @@ module EqualitiesArray = struct let find_reference_variable d var_index = fst d.(var_index) - let find_vars_in_the_connected_component d ref_var = + let find_vars_in_the_connected_component d ref_var = filter (fun i -> let (var, _) = d.(i) in var = ref_var) (mapi const d) (* find a variable in the connected component with the least index, but not the reference variable. *) - let find_var_in_the_connected_component_with_least_index connected_component ref_var = + let find_var_in_the_connected_component_with_least_index connected_component ref_var = fold_left (fun curr_min i -> match curr_min with | None -> if i <> ref_var then Some i else None | Some curr_min -> if i < curr_min && i <> ref_var then Some i else Some curr_min) None connected_component (* Forget information about variable var in-place. The name reduce_col_with is because the affineEqualitiesDomain also defines this function, - and it represents the equalities with a matrix, not like in this case with an array. - We could think about changing this name, then we would need to change it also in + and it represents the equalities with a matrix, not like in this case with an array. + We could think about changing this name, then we would need to change it also in shared_Functions.apron.ml and vectorMatrix.ml and affineEqualitiesDomain.ml *) - let reduce_col_with d var = + let reduce_col_with d var = let ref_var_opt = find_reference_variable d var in d.(var) <- Equality.var_zero var; - begin match ref_var_opt with + begin match ref_var_opt with | None -> (* the variable is equal to a constant *) () | Some ref_var -> if ref_var <> var then () @@ -112,22 +112,22 @@ module EqualitiesArray = struct (* x_i is the reference variable of its connected component *) let dim_of_var = Some var in let connected_component = find_vars_in_the_connected_component d dim_of_var in - if length connected_component = 1 - then () (* x_i is the only element of its connected component *) + if length connected_component = 1 + then () (* x_i is the only element of its connected component *) else (* x_i is the reference variable -> we need to find a new reference variable *) let var_least_index = Option.get @@ find_var_in_the_connected_component_with_least_index connected_component ref_var in - let (_, off) = d.(var_least_index) in + let (_, off) = d.(var_least_index) in iteri (fun _ x -> let (_, off2) = d.(x) in if x <> ref_var then d.(x) <- (Some var_least_index, Z.(off2 - off))) connected_component; end (* Forget information about variable i but not in-place *) - let reduce_col m j = + let reduce_col m j = let copy = copy m in reduce_col_with copy j; - copy + copy - let remove_zero_rows t = t + let remove_zero_rows t = t end @@ -144,7 +144,7 @@ struct let size t = BatOption.map_default (fun d -> EArray.length d) 0 t.d - (* Returns the constant value of a variable, + (* Returns the constant value of a variable, if we know the constant value of this variable. Else it returns None. *) let get_variable_value_if_it_is_a_constant t var = @@ -155,22 +155,22 @@ struct Option.bind t.d (fun d -> get_constant d.(var)) let get_coeff_vec (t: t) texp = - (*Parses a Texpr to obtain a (coefficient, variable) pair list to repr. a sum of a variables that have a coefficient. If variable is None, the coefficient represents a constant offset. + (*Parses a Texpr to obtain a (coefficient, variable) pair list to repr. a sum of a variables that have a coefficient. If variable is None, the coefficient represents a constant offset. *) let open Apron.Texpr1 in let exception NotLinearExpr in let exception NotIntegerOffset in let negate coeff_var_list = List.map (fun (coeff, var) -> (Z.(-coeff), var)) coeff_var_list in - let multiply_with_Z number coeff_var_list = + let multiply_with_Z number coeff_var_list = List.map (fun (coeff, var) -> (Z.(number * coeff, var))) coeff_var_list in - let multiply a b = + let multiply a b = (* if one of them is a constant, then multiply. Otherwise, the expression is not linear*) match a, b with | [(a_coeff, None)], b -> multiply_with_Z a_coeff b | a, [(b_coeff, None)] -> multiply_with_Z b_coeff a | _ -> raise NotLinearExpr in - let mpqf_to_Z x = + let mpqf_to_Z x = if not(Z.equal (Mpqf.get_den x) Z.one) then raise NotIntegerOffset else Mpqf.get_num x in let rec convert_texpr texp = @@ -184,8 +184,8 @@ struct | Float x -> raise NotIntegerOffset | Mpqf x -> [(mpqf_to_Z x, None)] | Mpfrf x -> raise NotIntegerOffset end in of_union x - | Var x -> - let var_dim = Environment.dim_of_var t.env x in + | Var x -> + let var_dim = Environment.dim_of_var t.env x in begin match get_variable_value_if_it_is_a_constant t var_dim with | None -> [(Z.one, Some var_dim)] | Some constant -> [(constant, None)] @@ -208,24 +208,24 @@ struct let get_coeff (t: t) texp = (*Parses a Texpr to obtain a (variable, offset) pair to repr. a sum of a variable and an offset. - Returns None if the expression is not a sum between a variable (without coefficient) and a constant. + Returns None if the expression is not a sum between a variable (without coefficient) and a constant. *) let exception Not2VarExpr in - let sum_next_coefficient (var, current_var_offset, curr_offset) (next_coeff, next_var) = + let sum_next_coefficient (var, current_var_offset, curr_offset) (next_coeff, next_var) = begin match next_var with | None -> (* this element represents a constant offset *) (var, current_var_offset, Z.(curr_offset + next_coeff)) - | Some _ -> (* this element represents a variable with a coefficient + | Some _ -> (* this element represents a variable with a coefficient -> it must be always the same variable, else it's not a two-variable equality*) begin if Option.is_none var || next_var = var then - (next_var, Z.(current_var_offset + next_coeff), curr_offset) - else raise Not2VarExpr end + (next_var, Z.(current_var_offset + next_coeff), curr_offset) + else raise Not2VarExpr end end in let sum_coefficients summands_list_opt = Option.map (List.fold_left sum_next_coefficient (None, Z.zero, Z.zero)) summands_list_opt in match sum_coefficients (get_coeff_vec t texp) with - | exception _ -> None + | exception _ -> None | None -> None | Some (var, var_coeff, offset) -> if Option.is_none var then Some (None, offset) @@ -235,29 +235,29 @@ struct let get_coeff t texp = timing_wrap "coeff_vec" (get_coeff t) texp - let abstract_exists var t = match t.d with + let abstract_exists var t = match t.d with | Some d -> {t with d = Some (EArray.reduce_col d (Environment.dim_of_var t.env var))} | None -> t (* there are no variables in the current environment *) - let assign_const t var const = match t.d with + let assign_const t var const = match t.d with | None -> t | Some t_d -> let d = EArray.copy t_d in d.(var) <- (None, const); {d = Some d; env = t.env} - let subtract_const_from_var t var const = - match t.d with + let subtract_const_from_var t var const = + match t.d with | None -> t | Some t_d -> let d = EArray.copy t_d in let subtract_const_from_var_for_single_equality index element = - let (eq_var_opt, off2) = d.(index) in + let (eq_var_opt, off2) = d.(index) in if index = var then match eq_var_opt with | None -> d.(index) <- (None, Z.(off2 + const)) | Some eq_var -> begin if eq_var <> index then d.(index) <- (eq_var_opt, Z.(off2 + const)) end - else - begin match eq_var_opt with + else + begin match eq_var_opt with | Some eq_var -> if eq_var = var then d.(index) <- (eq_var_opt, Z.(off2 - const)) - | None -> () + | None -> () end in EArray.iteri (subtract_const_from_var_for_single_equality) d; {d = Some d; env = t.env} @@ -288,11 +288,11 @@ module D = struct include Printable.Std include ConvenienceOps (Mpqf) - include VarManagement + include VarManagement module Bounds = ExpressionBounds - module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (SharedFunctions.Tracked) + module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (SharedFunctions.Tracked) type var = V.t @@ -319,10 +319,10 @@ struct match tuple with | (None, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ Z.to_string offset ^ "\n" | (Some index, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ lookup index ^ " + " ^ Z.to_string offset ^ "\n" - in if is_top varM then "⊤\n" else + in if is_top varM then "⊤\n" else match varM.d with | None -> "⊥\n" - | Some arr -> if is_bot varM then "Bot \n" else Array.fold_left (fun acc elem -> acc ^ elem ) "" (Array.mapi show_var arr) + | Some arr -> if is_bot varM then "Bot \n" else Array.fold_left (fun acc elem -> acc ^ elem ) "" (Array.mapi show_var arr) let pretty () (x:t) = text (show x) let printXml f x = BatPrintf.fprintf f "\n\n\nequalities-array\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) @@ -330,13 +330,13 @@ struct let meet t1 t2 = let sup_env = Environment.lce t1.env t2.env in let t1, t2 = change_d t1 sup_env ~add:true ~del:false, change_d t2 sup_env ~add:true ~del:false in - let subst_var ts x t = + let subst_var ts x t = let adjust e = match e with | (None, b') -> (None, b') | (Some x', b') -> if x = x' then - (match t with + (match t with | (None, bt) -> (None, Z.(b' + bt)) - | (Some xt, bt) -> (Some xt, Z.(b' + bt))) + | (Some xt, bt) -> (Some xt, Z.(b' + bt))) else (Some x', b')in Stdlib.Option.iter (BatArray.modify adjust) !ts (* match !ts with @@ -347,38 +347,38 @@ struct match ts'.(i) with | (None, _) -> () | (Some x', b') -> if x = x' then - (match t with + (match t with | (None, bt) -> ts'.(i) <- (None, Z.(b' + bt)) | (Some xt, bt) -> ts'.(i) <- (Some xt, Z.(b' + bt))) done*) in - let add_conj ts t i = + let add_conj ts t i = let adjust ts' = (match t with - | (None, b) -> + | (None, b) -> (match ts'.(i) with | (None, b') -> if not @@ Z.equal b b' then ts := None; | (Some j, b') -> subst_var ts j (None, Z.(b - b'))) | (Some j, b) -> (match ts'.(i) with | (None, b1) -> subst_var ts j (None, Z.(b1 - b)) - | (Some h1, b1) -> + | (Some h1, b1) -> (match ts'.(j) with | (None, b2) -> subst_var ts i (None, Z.(b2 + b)) - | (Some h2, b2) -> - if h1 = h2 then + | (Some h2, b2) -> + if h1 = h2 then (if Z.(b1 <> (b2 + b)) then ts := None) else if h1 < h2 then subst_var ts h2 (Some h1, Z.(b1 - (b + b2))) else subst_var ts h1 (Some h2, Z.(b + (b2 - b1)))))) in Stdlib.Option.iter adjust !ts - in + in match t1.d, t2.d with | Some d1', Some d2' -> ( let ds = ref (Some (Array.copy d1')) in - Array.iteri (fun j e -> add_conj ds e j) d2'; + Array.iteri (fun j e -> add_conj ds e j) d2'; {d = !ds; env = sup_env} ) - | _ -> { d = None; env = sup_env} + | _ -> { d = None; env = sup_env} let meet t1 t2 = let res = meet t1 t2 in @@ -391,21 +391,21 @@ struct let env_comp = Environment.compare t1.env t2.env in (* Apron's Environment.compare has defined return values. *) let implies ts t i : bool = match t with - | (None, b) -> + | (None, b) -> (match ts.(i) with | (None, b') -> Z.equal b b' | _ -> false) - | (Some j, b) -> + | (Some j, b) -> (match ts.(i), ts.(j) with | (None, b1), (None, b2) -> Z.equal b1 (Z.add b2 b) | (Some h1, b1), (Some h2, b2) -> h1 = h2 && Z.equal b1 (Z.add b2 b) | _ -> false ) - in + in if env_comp = -2 || env_comp > 0 then false else if is_bot_env t1 || is_top t2 then true else - if is_bot_env t2 || is_top t1 then false else + if is_bot_env t2 || is_top t1 then false else let m1, m2 = Option.get t1.d, Option.get t2.d in let m1' = if env_comp = 0 then m1 else dim_add (Environment.dimchange t1.env t2.env) m1 in GobArray.for_alli (fun i t -> implies m1' t i) m2 @@ -417,8 +417,8 @@ struct if M.tracing then M.tracel "leq" "leq a: %s b: %s -> %b \n" (show t1) (show t2) res ; res - let join a b = - let join_d ad bd = + let join a b = + let join_d ad bd = (*use copy of ad because result is later saved in there*) let ad = Array.copy ad in (*This is the table which is later grouped*) @@ -426,19 +426,19 @@ struct let const_offset t = Tuple2.second t in let diff t1 t2 = Z.((const_offset t1) - (const_offset t2)) in (*compare two variables for grouping depending on delta function and reference index*) - let cmp_z (_, t1i, t2i) (_, t1j, t2j) = + let cmp_z (_, t1i, t2i) (_, t1j, t2j) = let cmp_z_ref (x,_) (y,_): int = match x, y with | None, None -> 0 | None, Some _ -> -1 | Some _, None -> 1 - | Some ii, Some ij -> ii - ij + | Some ii, Some ij -> ii - ij in let diff_e1 = cmp_z_ref t1i t1j in - if diff_e1 <> 0 then diff_e1 + if diff_e1 <> 0 then diff_e1 else let diff_e2 = cmp_z_ref t2i t2j in - if diff_e2 <> 0 then diff_e2 else + if diff_e2 <> 0 then diff_e2 else Z.to_int (Z.((diff t1i t2i) - (diff t1j t2j))) in (*Calculate new components as groups*) @@ -450,16 +450,16 @@ struct else ad.(idx) <- (Some idx_h, Z.(z1 - b_h)) in let iterate l = - match l with + match l with | (idx_h, (_, b_h), _) :: t -> List.iter (modify idx_h b_h) l - | [] -> () (*This should not happen, consider throughing exception*) + | [] -> () (*This should not happen, consider throughing exception*) in List.iter iterate new_components; Some ad in (*Normalize the two domains a and b such that both talk about the same variables*) - if is_bot_env a then b else if is_bot_env b then a + if is_bot_env a then b else if is_bot_env b then a else match Option.get a.d, Option.get b.d with - | x, y when is_top a || is_top b -> let new_env = Environment.lce a.env b.env + | x, y when is_top a || is_top b -> let new_env = Environment.lce a.env b.env in (top_of new_env) | x, y when (Environment.compare a.env b.env <> 0) -> let sup_env = Environment.lce a.env b.env in @@ -472,7 +472,7 @@ struct (* - let join' a b = + let join' a b = let ts_zip t1 t2 = if Array.length t1 <> Array.length t2 then None else let zts = Array.init (Array.length t1) (fun (i : int) -> (i, t1.(i), t2.(i))) in @@ -482,30 +482,30 @@ struct in let diff t1 t2 = Z.((const_offset t1) - (const_offset t2)) in - let cmp_z (_, t1i, t2i) (_, t1j, t2j) = + let cmp_z (_, t1i, t2i) (_, t1j, t2j) = let cmp_z_ref x y: int = match x, y with | (None, _), (None, _) -> 0 | (None, _), (Some _, _) -> -1 | (Some _, _), (None, _) -> 1 - | (Some ii, _), (Some ij, _) -> ii - ij + | (Some ii, _), (Some ij, _) -> ii - ij in let diff_e1 = cmp_z_ref t1i t1j in - if diff_e1 <> 0 then diff_e1 + if diff_e1 <> 0 then diff_e1 else let diff_e2 = cmp_z_ref t2i t2j in - if diff_e2 <> 0 then diff_e2 else + if diff_e2 <> 0 then diff_e2 else Z.to_int (Z.((diff t1i t2i) - (diff t1j t2j))) in let sort_z_by_expr zts = - Stdlib.Option.iter (Array.stable_sort cmp_z) zts + Stdlib.Option.iter (Array.stable_sort cmp_z) zts in - let sort_annotated ats = - let cmp_annotated x y : int = (Tuple2.first x) - (Tuple2.first y) + let sort_annotated ats = + let cmp_annotated x y : int = (Tuple2.first x) - (Tuple2.first y) in Stdlib.Option.iter (Array.stable_sort cmp_annotated) ats in - let process_eq_classes zts = + let process_eq_classes zts = let is_const x = match x with | (_, (None, _), (None, _)) -> true @@ -522,37 +522,37 @@ struct let result = (i,b) in let iterate (a, b) i (j, (_, bj), (_, _))= if i > start && j < a then (j,bj) else (a,b) in - Array.fold_lefti iterate result zts + Array.fold_lefti iterate result zts in - let all_are_const_in_eq_class zts start size : bool = + let all_are_const_in_eq_class zts start size : bool = Array.fold_left (fun b e -> b && (is_const e)) true zts in - let assign_vars_in_const_eq_class ats zts start size least_i least_b = - let adjust i e = if i < start then e - else + let assign_vars_in_const_eq_class ats zts start size least_i least_b = + let adjust i e = if i < start then e + else let (ai, t1, t2) = zts.(i)in if Z.equal (diff t1 t2) (Z.zero) then (ai, t1) else (ai, (Some least_i, Z.sub (Tuple2.second t1) least_b)) in - BatArray.modifyi adjust ats + BatArray.modifyi adjust ats in - let assign_vars_in_non_const_eq_class ats zts start size least_i least_b = + let assign_vars_in_non_const_eq_class ats zts start size least_i least_b = let adjust i e = if i < start then e else ( let (ai, t1, _) = zts.(i) in let bj = const_offset t1 in - (ai, (Some least_i, Z.sub bj least_b))) + (ai, (Some least_i, Z.sub bj least_b))) in BatArray.modifyi adjust ats in let adjust_zts zts' = (* - let mapi_zts i e = + let mapi_zts i e = let n = size_of_eq_class zts' i in if n = 1 then let (i', t1, t2) = zts'.(i) in - if is_const (i', t1, t2) && Z.equal (diff t1 t2) (Z.zero) then + if is_const (i', t1, t2) && Z.equal (diff t1 t2) (Z.zero) then (i', (None, const_offset t1)) else (i', (Some i', Z.zero)) else @@ -562,11 +562,11 @@ struct else assign_vars_in_non_const_eq_class result zts'); e*) let result = Array.make (Array.length zts') (0, (None, Z.zero)) in let i = ref 0 in - while !i < Array.length zts' do - let n = size_of_eq_class zts' !i in + while !i < Array.length zts' do + let n = size_of_eq_class zts' !i in (if n = 1 then let (i', t1, t2) = zts'.(!i) in - if is_const (i', t1, t2) && Z.equal (diff t1 t2) (Z.zero) then + if is_const (i', t1, t2) && Z.equal (diff t1 t2) (Z.zero) then result.(!i) <- (i', (None, const_offset t1)) else result.(!i) <- (i', (Some i', Z.zero)) else @@ -574,15 +574,15 @@ struct (if all_are_const_in_eq_class zts' !i n then assign_vars_in_const_eq_class result zts' !i n least_i least_b else assign_vars_in_non_const_eq_class result zts' !i n least_i least_b); - ); + ); i := !i + n; done; - result + result in Stdlib.Option.map adjust_zts zts in - let strip_annotation ats = - Option.map (Array.map snd) ats + let strip_annotation ats = + Option.map (Array.map snd) ats in let join_d t1 t2 = let zipped = ts_zip t1 t2 in @@ -594,7 +594,7 @@ struct in if is_bot_env a then b else if is_bot_env b then a else match Option.get a.d, Option.get b.d with - | x, y when is_top a || is_top b -> let new_env = Environment.lce a.env b.env + | x, y when is_top a || is_top b -> let new_env = Environment.lce a.env b.env in (top_of new_env) | x, y when (Environment.compare a.env b.env <> 0) -> let sup_env = Environment.lce a.env b.env in @@ -625,7 +625,7 @@ struct let pretty_diff () (x, y) = dprintf "%s: %a not leq %a" (name ()) pretty x pretty y - let forget_vars t vars = + let forget_vars t vars = if is_bot_env t || is_top t then t else let m = Option.get t.d in @@ -633,7 +633,7 @@ struct let rec rem_vars m vars' = begin match vars' with | [] -> m - | x :: xs -> rem_vars (remove_rels_with_var m x t.env true) xs end + | x :: xs -> rem_vars (remove_rels_with_var m x t.env true) xs end in {d = Some (EArray.remove_zero_rows @@ rem_vars (EArray.copy m) vars); env = t.env} let forget_vars t vars = @@ -643,29 +643,29 @@ struct let forget_vars t vars = timing_wrap "forget_vars" (forget_vars t) vars - (* implemented as described on page 10 in the paper about Fast Interprocedural Linear Two-Variable Equalities in the Section "Abstract Effect of Statements" + (* implemented as described on page 10 in the paper about Fast Interprocedural Linear Two-Variable Equalities in the Section "Abstract Effect of Statements" This makes a copy of the data structure, it doesn't change it in-place. *) let assign_texpr (t: VarManagement.t) var texp = let assigned_var = Environment.dim_of_var t.env var (* this is the variable we are assigning to *) in - begin match t.d with - | Some d -> + begin match t.d with + | Some d -> let abstract_exists_var = abstract_exists var t in begin match get_coeff t texp with - | None -> (* Statement "assigned_var = ?" (non-linear assignment) *) + | None -> (* Statement "assigned_var = ?" (non-linear assignment) *) abstract_exists_var - | Some (None, off) -> - (* Statement "assigned_var = off" (constant assignment) *) + | Some (None, off) -> + (* Statement "assigned_var = off" (constant assignment) *) assign_const abstract_exists_var assigned_var off - | Some (Some exp_var, off) when assigned_var = exp_var -> + | Some (Some exp_var, off) when assigned_var = exp_var -> (* Statement "assigned_var = assigned_var + off" *) subtract_const_from_var t assigned_var off - | Some (Some exp_var, off) -> - (* Statement "assigned_var = exp_var + off" (assigned_var is not the same as exp_var) *) + | Some (Some exp_var, off) -> + (* Statement "assigned_var = exp_var + off" (assigned_var is not the same as exp_var) *) let added_equality = EqualitiesArray.make_empty_array (VarManagement.size t) in - added_equality.(assigned_var) <- (Some exp_var, off); - meet abstract_exists_var {d = Some added_equality; env = t.env} - end - | None -> bot_env end + added_equality.(assigned_var) <- (Some exp_var, off); + meet abstract_exists_var {d = Some added_equality; env = t.env} + end + | None -> bot_env end @@ -698,21 +698,21 @@ struct (* This functionality is not common to C and is used for assignments of the form: x = y, y=x; which is not legitimate C grammar x and y should be assigned to the value of x and y before the assignment respectively. ==> x = y_old , y = x_old; - Therefore first apply the assignments to temporary variables x' and y' to keep the old dependencies of x and y + Therefore first apply the assignments to temporary variables x' and y' to keep the old dependencies of x and y and in a second round assign x' to x and y' to y *) - let assign_var_parallel t vv's = + let assign_var_parallel t vv's = let assigned_vars = List.map (function (v, _) -> v) vv's in let t = add_vars t assigned_vars in let primed_vars = List.init (List.length assigned_vars) (fun i -> Var.of_string (Int.to_string i ^"'")) in (* TODO: we use primed vars in analysis, conflict? *) let t_primed = add_vars t primed_vars in let multi_t = List.fold_left2 (fun t' v_prime (_,v') -> assign_var t' v_prime v') t_primed primed_vars vv's in match multi_t.d with - | Some arr when not @@ is_top multi_t -> + | Some arr when not @@ is_top multi_t -> let switched_arr = List.fold_left2 (fun multi_t assigned_var primed_var-> assign_var multi_t assigned_var primed_var) multi_t assigned_vars primed_vars in let res = drop_vars switched_arr primed_vars ~del:true in let x = Option.get res.d in - {d = Some x; env = res.env} + {d = Some x; env = res.env} | _ -> t let assign_var_parallel t vv's = @@ -752,15 +752,15 @@ struct let substitute_exp t var exp ov = timing_wrap "substitution" (substitute_exp t var exp) ov - let print_coeff_vec l (env : Environment.t) = - let print_element e = match e with - | (a, Some x) -> print_string ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env x)) ^ " + ") + let print_coeff_vec l (env : Environment.t) = + let print_element e = match e with + | (a, Some x) -> print_string ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env x)) ^ " + ") | (a, None) -> print_string ((Z.to_string a) ^ "+") in List.iter print_element l; print_newline () let print_final_expr l (env : Environment.t) = - let print_element i a = if i = 0 then print_string ((Z.to_string a) ^ " + ") else - print_string ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env (i-1))) ^ " + ") in + let print_element i a = if i = 0 then print_string ((Z.to_string a) ^ " + ") else + print_string ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env (i-1))) ^ " + ") in List.iteri print_element l; print_newline () (** Assert a constraint expression. @@ -776,10 +776,10 @@ struct *) module BoundsCheck = SharedFunctions.BoundsCheckMeetTcons (Bounds) (V) - let meet_tcons t tcons original_expr = - (* The expression is evaluated using an array of coefficients. The first element of the array belongs to the constant followed by the coefficients of all variables + let meet_tcons t tcons original_expr = + (* The expression is evaluated using an array of coefficients. The first element of the array belongs to the constant followed by the coefficients of all variables depending on the result in the array after the evaluating including resolving the constraints in t.d the tcons can be evaluated and additional constraints can be added to t.d *) - match t.d with + match t.d with | None -> bot_env | Some d -> let overflow_handling res original_expr = @@ -793,51 +793,51 @@ struct match get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with | None -> t (*The (in-) equality is not linear, therefore we don't know anything about it. *) | Some cv's -> - let update (c, v) = - match v with - | None -> constant := Z.(!constant + c) - | Some idx -> match d.(idx) with + let update (c, v) = + match v with + | None -> constant := Z.(!constant + c) + | Some idx -> match d.(idx) with | (Some idx_i, c_i) -> constant := Z.(!constant + (c * c_i)); - expr.(idx_i) <- Z.(expr.(idx_i) + c) + expr.(idx_i) <- Z.(expr.(idx_i) + c) | (None, c_i) -> constant := Z.(!constant + (c * c_i)) - in + in List.iter update cv's; let var_count = GobArray.count_matchingi (fun _ a -> a <> Z.zero) expr in - if var_count = 0 then - match Tcons1.get_typ tcons with + if var_count = 0 then + match Tcons1.get_typ tcons with | EQ when Z.equal !constant Z.zero -> t - | SUPEQ when Z.geq !constant Z.zero -> t - | SUP when Z.gt !constant Z.zero -> t - | DISEQ when not @@ Z.equal !constant Z.zero -> t + | SUPEQ when Z.geq !constant Z.zero -> t + | SUP when Z.gt !constant Z.zero -> t + | DISEQ when not @@ Z.equal !constant Z.zero -> t | EQMOD scalar -> t | _ -> bot_env (*Not supported right now if Float.equal ( Float.modulo (Z.to_float expr.(0)) (convert_scalar scalar )) 0. then t else {d = None; env = t.env}*) else if var_count = 1 then let index = Array.findi (fun a -> not @@ Z.equal a Z.zero) expr in let var = (index, expr.(index)) in - let c = if Z.divisible !constant @@ Tuple2.second var then Some (Z.(-(!constant) / (Tuple2.second var))) + let c = if Z.divisible !constant @@ Tuple2.second var then Some (Z.(-(!constant) / (Tuple2.second var))) else None in - match Tcons1.get_typ tcons, c with - | EQ, Some c -> - let expression = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int c) in - let res = meet t (assign_texpr (top_of t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expression) + match Tcons1.get_typ tcons, c with + | EQ, Some c -> + let expression = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int c) in + let res = meet t (assign_texpr (top_of t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expression) in overflow_handling res original_expr | _ -> t (*Not supported right now*) - else if var_count = 2 then + else if var_count = 2 then let get_vars i a l = if Z.equal a Z.zero then l else (i, a)::l in let v12 = Array.fold_righti get_vars expr [] in - let a1 = Tuple2.second (List.hd v12) in + let a1 = Tuple2.second (List.hd v12) in let a2 = Tuple2.second (List.hd @@ List.tl v12) in - let var1 = Environment.var_of_dim t.env (Tuple2.first (List.hd v12)) in + let var1 = Environment.var_of_dim t.env (Tuple2.first (List.hd v12)) in let var2 = Environment.var_of_dim t.env (Tuple2.first (List.hd @@ List.tl v12)) in - match Tcons1.get_typ tcons with - | EQ -> - let res = if Z.equal a1 Z.one && Z.equal a2 Z.one - then meet t (assign_var (top_of t.env) var1 var2) + match Tcons1.get_typ tcons with + | EQ -> + let res = if Z.equal a1 Z.one && Z.equal a2 Z.one + then meet t (assign_var (top_of t.env) var1 var2) else t in overflow_handling res original_expr | _-> t (*Not supported right now*) - else + else t (*For any other case we don't know if the (in-) equality is true or false or even possible therefore we just return t *) let meet_tcons t tcons expr = timing_wrap "meet_tcons" (meet_tcons t tcons) expr @@ -852,12 +852,12 @@ struct (* Assert a constraint expression. Defined in apronDomain.apron.ml - If the constraint is never fulfilled, then return bottom. + If the constraint is never fulfilled, then return bottom. Else the domain can be modified with the new information given by the constraint. It basically just calls the function meet_tcons. - It is called by eval (defined in sharedFunctions), but also when a guard in + It is called by eval (defined in sharedFunctions), but also when a guard in e.g. an if statement is encountered in the C code. *) @@ -865,21 +865,21 @@ struct let no_ov = Lazy.force no_ov in if M.tracing then M.tracel "assert_cons" "assert_cons with expr: %a %b\n" d_exp e no_ov; match Convert.tcons1_of_cil_exp d d.env e negate no_ov with - | tcons1 -> meet_tcons d tcons1 e + | tcons1 -> meet_tcons d tcons1 e | exception Convert.Unsupported_CilExp _ -> d let assert_cons d e negate no_ov = timing_wrap "assert_cons" (assert_cons d e negate) no_ov let relift t = t - (* representation as C expression + (* representation as C expression This function returns all the equalities that are saved in our datastructure t. Lincons -> linear constraint *) (*TODO*) let invariant t = [] - (*let invariant t = + (*let invariant t = match t.d with | None -> [] | Some m -> @@ -887,13 +887,13 @@ struct EArray.fold_left (fun acc row -> let coeff_vars = List.map (fun(var,off) -> Coeff.s_of_int off, Some var) row in - let cst = Coeff.s_of_int (snd (List.hd row)) in + let cst = Coeff.s_of_int (snd (List.hd row)) in Lincons1.make (Linexpr1.make t.env) Lincons1.EQ |> Lincons1.set_list coeff_vars (Some cst) |> (fun lc -> Lincons1.{lincons0 = Lincons0.of_lincons1 lc; env = t.env}) :: acc) [] m - in + in List.rev linear_constraints *) (* let invariant t = @@ -916,7 +916,7 @@ struct Lincons1.{ lincons0 = Lincons0.of_lincons1 lc; env = t.env } :: acc) [] m in - List.rev linear_constraints *) + List.rev linear_constraints *) let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 @@ -933,8 +933,7 @@ end module D2: RelationDomain.S3 with type var = Var.t = struct - module D = D + module D = D include SharedFunctions.AssertionModule (V) (D) include D end - diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index d9f3886604..699bb49bd4 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -477,7 +477,7 @@ end (* Multi-precision rational numbers, defined by Apron. Used by affineEqualityDomain and linearTwoVarEqualityDomain *) -module Mpqf = struct +module Mpqf = struct include Mpqf let compare = cmp let zero = of_int 0 @@ -512,7 +512,7 @@ module BoundsCheckMeetTcons (Bounds: ExtendedConvBounds) (V: SV) = struct | Some v -> let ik = Cilfacade.get_ikind v.vtype in match Bounds.bound_texpr res (Convert.texpr1_of_cil_exp res (Bounds.get_env res) (Lval (Cil.var v)) true) with - | Some _, Some _ when not (Cil.isSigned ik) -> raise NotRefinable + | Some _, Some _ when not (Cil.isSigned ik) -> raise NotRefinable | Some min, Some max -> assert (Z.equal min max); (* other bounds impossible in affeq *) let (min_ik, max_ik) = IntDomain.Size.range ik in @@ -521,4 +521,4 @@ module BoundsCheckMeetTcons (Bounds: ExtendedConvBounds) (V: SV) = struct else res | exception Convert.Unsupported_CilExp _ | _, _ -> overflow_res res -end \ No newline at end of file +end diff --git a/tests/regression/77-lin2vareq/00-basic.c b/tests/regression/77-lin2vareq/00-basic.c index 22d26ab725..f285bf28ca 100644 --- a/tests/regression/77-lin2vareq/00-basic.c +++ b/tests/regression/77-lin2vareq/00-basic.c @@ -19,4 +19,3 @@ int main() { return 0; } - diff --git a/tests/regression/77-lin2vareq/01-iteration.c b/tests/regression/77-lin2vareq/01-iteration.c index a028c74c77..790c7b76fd 100644 --- a/tests/regression/77-lin2vareq/01-iteration.c +++ b/tests/regression/77-lin2vareq/01-iteration.c @@ -14,4 +14,4 @@ int main() { return 0; } -//This test case checks whether the value of variable i is always equal to the value of variable j within the loop. \ No newline at end of file +//This test case checks whether the value of variable i is always equal to the value of variable j within the loop. diff --git a/tests/regression/77-lin2vareq/02-reachability.c b/tests/regression/77-lin2vareq/02-reachability.c index 00182c38ed..8b1694b502 100644 --- a/tests/regression/77-lin2vareq/02-reachability.c +++ b/tests/regression/77-lin2vareq/02-reachability.c @@ -10,7 +10,7 @@ int main() { y = 1; __goblint_check(x == 10 * y); //SUCCESS - + if(x == 10 * y) return 0; __goblint_check(0); // NOWARN (unreachable) diff --git a/tests/regression/77-lin2vareq/03-known_expressions.c b/tests/regression/77-lin2vareq/03-known_expressions.c index 0a5eb1d3ec..692bc34a2e 100644 --- a/tests/regression/77-lin2vareq/03-known_expressions.c +++ b/tests/regression/77-lin2vareq/03-known_expressions.c @@ -23,4 +23,4 @@ int main() { __goblint_check(x12 == 2 * x11 + 1); //SUCCESS return 0; -} \ No newline at end of file +} diff --git a/tests/regression/77-lin2vareq/04-unknown.c b/tests/regression/77-lin2vareq/04-unknown.c index e250133918..1b7ba2a9ed 100644 --- a/tests/regression/77-lin2vareq/04-unknown.c +++ b/tests/regression/77-lin2vareq/04-unknown.c @@ -4,7 +4,7 @@ #include typedef int dataX_t; -typedef int dataY_t; +typedef int dataY_t; dataX_t x_arr[100]; dataY_t y_arr[100]; @@ -23,7 +23,7 @@ int main() { for (i = 0; i < 100; i++) { access(); - + __goblint_check(i == 8 * i + 0); //UNKNOWN! __goblint_check(x_ptr == 8 * i + x_arr); //UNKNOWN! __goblint_check(y_ptr == 4 * i + y_arr); //UNKNOWN! diff --git a/tests/regression/77-lin2vareq/05-associative.c b/tests/regression/77-lin2vareq/05-associative.c index e44ee79b5a..0458eba558 100644 --- a/tests/regression/77-lin2vareq/05-associative.c +++ b/tests/regression/77-lin2vareq/05-associative.c @@ -15,4 +15,4 @@ int main() { } -//This test case checks the associative property \ No newline at end of file +//This test case checks the associative property diff --git a/tests/regression/77-lin2vareq/07-commutative.c b/tests/regression/77-lin2vareq/07-commutative.c index d9db8c9544..b88d3df15e 100644 --- a/tests/regression/77-lin2vareq/07-commutative.c +++ b/tests/regression/77-lin2vareq/07-commutative.c @@ -14,4 +14,4 @@ int main() { return 0; } -//This test case checks the commutative property. +//This test case checks the commutative property. diff --git a/tests/regression/77-lin2vareq/08-loop.c b/tests/regression/77-lin2vareq/08-loop.c index 4525a84434..7816895a24 100644 --- a/tests/regression/77-lin2vareq/08-loop.c +++ b/tests/regression/77-lin2vareq/08-loop.c @@ -14,5 +14,5 @@ void main(void) { k = k + 3; } __goblint_check(3 * i - k == 1); //UNKNOWN! - + } diff --git a/tests/regression/77-lin2vareq/12-overflow.c b/tests/regression/77-lin2vareq/12-overflow.c index 37b56a622a..d2b4b8e8ea 100644 --- a/tests/regression/77-lin2vareq/12-overflow.c +++ b/tests/regression/77-lin2vareq/12-overflow.c @@ -20,4 +20,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/tests/regression/77-lin2vareq/13-bounds_guards_ov.c b/tests/regression/77-lin2vareq/13-bounds_guards_ov.c index 574e9a71a7..18fefd743b 100644 --- a/tests/regression/77-lin2vareq/13-bounds_guards_ov.c +++ b/tests/regression/77-lin2vareq/13-bounds_guards_ov.c @@ -15,4 +15,4 @@ int main() { __goblint_check(1); } -} \ No newline at end of file +} From 4584f8c07a16873655b714df4d576ae4e1951575 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 4 Jan 2024 17:35:44 +0100 Subject: [PATCH 095/280] simplified join further --- .../apron/linearTwoVarEqualityDomain.apron.ml | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 58747a05cd..e6380161d8 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -304,7 +304,7 @@ struct let is_bot_env t = t.d = None (*this shows "top" for a specific environment to enable the calculations. It is the top_of of all equalities*) - let top_of env = {d = Some (Array.init (Environment.size env) (fun i -> (Some i, Z.zero))); env = env} + let top_of env = {d = Some (EArray.make_empty_array (Environment.size env)); env = env} (*Should never be called but implemented for completeness *) let top () = {d = Some (EArray.empty()); env = empty_env} @@ -422,24 +422,12 @@ struct (*use copy of ad because result is later saved in there*) let ad = Array.copy ad in (*This is the table which is later grouped*) - let table = BatList.map2i (fun i a b -> (i,a,b)) (Array.to_list ad) (Array.to_list bd) in + let table = BatList.map2i (fun i a b -> (i, a, b)) (Array.to_list ad) (Array.to_list bd) in let const_offset t = Tuple2.second t in - let diff t1 t2 = Z.((const_offset t1) - (const_offset t2)) in + let diff t1 t2 = Z.(const_offset t1 - const_offset t2) in (*compare two variables for grouping depending on delta function and reference index*) let cmp_z (_, t1i, t2i) (_, t1j, t2j) = - let cmp_z_ref (x,_) (y,_): int = - match x, y with - | None, None -> 0 - | None, Some _ -> -1 - | Some _, None -> 1 - | Some ii, Some ij -> ii - ij - in - let diff_e1 = cmp_z_ref t1i t1j in - if diff_e1 <> 0 then diff_e1 - else - let diff_e2 = cmp_z_ref t2i t2j in - if diff_e2 <> 0 then diff_e2 else - Z.to_int (Z.((diff t1i t2i) - (diff t1j t2j))) + Tuple3.compare (fst t1i, fst t2i, diff t1i t2i) (fst t1j, fst t2j, diff t1j t2j) in (*Calculate new components as groups*) let new_components = BatList.group cmp_z table in @@ -452,7 +440,7 @@ struct let iterate l = match l with | (idx_h, (_, b_h), _) :: t -> List.iter (modify idx_h b_h) l - | [] -> () (*This should not happen, consider throughing exception*) + | [] -> () (*This should not happen, consider throwing exception*) in List.iter iterate new_components; Some ad in (*Normalize the two domains a and b such that both talk about the same variables*) From 89ab4ba1c4de363ddc4c9a31fe3605abd52541eb Mon Sep 17 00:00:00 2001 From: Klara Fall <44259717+alina-weber@users.noreply.github.com> Date: Thu, 4 Jan 2024 18:11:25 +0100 Subject: [PATCH 096/280] Update src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml Co-authored-by: Michael Schwarz --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index e6380161d8..c2303c8264 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -427,7 +427,8 @@ struct let diff t1 t2 = Z.(const_offset t1 - const_offset t2) in (*compare two variables for grouping depending on delta function and reference index*) let cmp_z (_, t1i, t2i) (_, t1j, t2j) = - Tuple3.compare (fst t1i, fst t2i, diff t1i t2i) (fst t1j, fst t2j, diff t1j t2j) + let cmp_ref = Option.compare Int.compare in + Tuple3.compare ~cmp1:cmp_ref ~cmp2:cmp_ref ~cmp3:Z.compare (fst t1i, fst t2i, diff t1i t2i) (fst t1j, fst t2j, diff t1j t2j) in (*Calculate new components as groups*) let new_components = BatList.group cmp_z table in From e326fe1750451698cac1d0336c0fb8216c2964b8 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Thu, 4 Jan 2024 18:41:08 +0100 Subject: [PATCH 097/280] small adjustion --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index c2303c8264..99d2090980 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -426,8 +426,9 @@ struct let const_offset t = Tuple2.second t in let diff t1 t2 = Z.(const_offset t1 - const_offset t2) in (*compare two variables for grouping depending on delta function and reference index*) - let cmp_z (_, t1i, t2i) (_, t1j, t2j) = - let cmp_ref = Option.compare Int.compare in + let cmp_z (_, t1i, t2i) (_, t1j, t2j) = + (*Tuple3.compare (fst t1i, fst t2i, diff t1i t2i) (fst t1j, fst t2j, diff t1j t2j)*) + let cmp_ref : (int option -> int option -> int )= Option.compare ~cmp:Int.compare in Tuple3.compare ~cmp1:cmp_ref ~cmp2:cmp_ref ~cmp3:Z.compare (fst t1i, fst t2i, diff t1i t2i) (fst t1j, fst t2j, diff t1j t2j) in (*Calculate new components as groups*) From edb93b39838973a85c6a3edaaf6faae70bf0b195 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 4 Jan 2024 18:49:16 +0100 Subject: [PATCH 098/280] remove type annotation --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 99d2090980..315316dbf4 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -426,9 +426,8 @@ struct let const_offset t = Tuple2.second t in let diff t1 t2 = Z.(const_offset t1 - const_offset t2) in (*compare two variables for grouping depending on delta function and reference index*) - let cmp_z (_, t1i, t2i) (_, t1j, t2j) = - (*Tuple3.compare (fst t1i, fst t2i, diff t1i t2i) (fst t1j, fst t2j, diff t1j t2j)*) - let cmp_ref : (int option -> int option -> int )= Option.compare ~cmp:Int.compare in + let cmp_z (_, t1i, t2i) (_, t1j, t2j) = + let cmp_ref = Option.compare ~cmp:Int.compare in Tuple3.compare ~cmp1:cmp_ref ~cmp2:cmp_ref ~cmp3:Z.compare (fst t1i, fst t2i, diff t1i t2i) (fst t1j, fst t2j, diff t1j t2j) in (*Calculate new components as groups*) From debcdc11f05c51e9e822125e692dbc64993a7b8e Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Sun, 7 Jan 2024 15:09:47 +0100 Subject: [PATCH 099/280] test cases adapted --- gobview | 2 +- tests/regression/63-affeq/12-const_guards.c | 2 +- .../77-lin2vareq/02-equality_assertion.c | 17 +++++++++ .../{02-reachability.c => 04-reachability.c} | 0 .../77-lin2vareq/05-vars_equality.c | 26 ++++++++++++++ tests/regression/77-lin2vareq/06-pointer.c | 36 +++++++++++++++++++ ...pression.c => 07-complicated_expression.c} | 0 .../{06-distributive.c => 08-distributive.c} | 0 .../{05-associative.c => 09-associative.c} | 0 .../{07-commutative.c => 10-commutative.c} | 0 .../77-lin2vareq/11-complicated_expression.c | 19 ++++++++++ .../77-lin2vareq/{08-loop.c => 11-loop.c} | 0 .../77-lin2vareq/13-join_after_guard.c | 15 ++++++++ tests/regression/77-lin2vareq/14-coeff_vec.c | 21 +++++++++++ tests/regression/77-lin2vareq/15-env_order.c | 14 ++++++++ .../77-lin2vareq/16-multiple-vars.c | 9 +++++ .../regression/77-lin2vareq/17-partitioning.c | 33 +++++++++++++++++ .../77-lin2vareq/18-loop_relational.c | 20 +++++++++++ .../regression/77-lin2vareq/19-linear_loop.c | 20 +++++++++++ .../77-lin2vareq/20-commutative_2.c | 20 +++++++++++ .../77-lin2vareq/21-algebraic_equivalence.c | 22 ++++++++++++ .../77-lin2vareq/22-distributive_2.c | 23 ++++++++++++ .../{04-unknown.c => 23-unknown.c} | 0 ...constant_square.c => 24-constant_square.c} | 0 .../77-lin2vareq/25-simple_square.c | 13 +++++++ ...erflow_ignored.c => 26-overflow_ignored.c} | 0 .../77-lin2vareq/27-loop_increment.c | 20 +++++++++++ ...unds_guards_ov.c => 28-bounds_guards_ov.c} | 0 ...in_after_guard.c => 29-join_after_guard.c} | 0 ...-non-constant.c => 30-join-non-constant.c} | 0 30 files changed, 330 insertions(+), 2 deletions(-) create mode 100644 tests/regression/77-lin2vareq/02-equality_assertion.c rename tests/regression/77-lin2vareq/{02-reachability.c => 04-reachability.c} (100%) create mode 100644 tests/regression/77-lin2vareq/05-vars_equality.c create mode 100644 tests/regression/77-lin2vareq/06-pointer.c rename tests/regression/77-lin2vareq/{10-complicated_expression.c => 07-complicated_expression.c} (100%) rename tests/regression/77-lin2vareq/{06-distributive.c => 08-distributive.c} (100%) rename tests/regression/77-lin2vareq/{05-associative.c => 09-associative.c} (100%) rename tests/regression/77-lin2vareq/{07-commutative.c => 10-commutative.c} (100%) create mode 100644 tests/regression/77-lin2vareq/11-complicated_expression.c rename tests/regression/77-lin2vareq/{08-loop.c => 11-loop.c} (100%) create mode 100644 tests/regression/77-lin2vareq/13-join_after_guard.c create mode 100644 tests/regression/77-lin2vareq/14-coeff_vec.c create mode 100644 tests/regression/77-lin2vareq/15-env_order.c create mode 100644 tests/regression/77-lin2vareq/16-multiple-vars.c create mode 100644 tests/regression/77-lin2vareq/17-partitioning.c create mode 100644 tests/regression/77-lin2vareq/18-loop_relational.c create mode 100644 tests/regression/77-lin2vareq/19-linear_loop.c create mode 100644 tests/regression/77-lin2vareq/20-commutative_2.c create mode 100644 tests/regression/77-lin2vareq/21-algebraic_equivalence.c create mode 100644 tests/regression/77-lin2vareq/22-distributive_2.c rename tests/regression/77-lin2vareq/{04-unknown.c => 23-unknown.c} (100%) rename tests/regression/77-lin2vareq/{09-constant_square.c => 24-constant_square.c} (100%) create mode 100644 tests/regression/77-lin2vareq/25-simple_square.c rename tests/regression/77-lin2vareq/{11-overflow_ignored.c => 26-overflow_ignored.c} (100%) create mode 100644 tests/regression/77-lin2vareq/27-loop_increment.c rename tests/regression/77-lin2vareq/{13-bounds_guards_ov.c => 28-bounds_guards_ov.c} (100%) rename tests/regression/77-lin2vareq/{14-join_after_guard.c => 29-join_after_guard.c} (100%) rename tests/regression/77-lin2vareq/{15-join-non-constant.c => 30-join-non-constant.c} (100%) diff --git a/gobview b/gobview index 3de13d7412..42b07f8253 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit 3de13d74124ab7bc30d8be299f02570d8f498b84 +Subproject commit 42b07f825316052ec030370daf0d00ebe28ec092 diff --git a/tests/regression/63-affeq/12-const_guards.c b/tests/regression/63-affeq/12-const_guards.c index 82d08ed9c5..2d007c4e81 100644 --- a/tests/regression/63-affeq/12-const_guards.c +++ b/tests/regression/63-affeq/12-const_guards.c @@ -1,4 +1,4 @@ -//SKIP PARAM: --set ana.activated[+] affeq --enable ana.int.interval +//SKIP PARAM: --set ana.activated[+] affeq --enable ana.int.interval int main() { int two = 2; int three = 3; diff --git a/tests/regression/77-lin2vareq/02-equality_assertion.c b/tests/regression/77-lin2vareq/02-equality_assertion.c new file mode 100644 index 0000000000..00182c38ed --- /dev/null +++ b/tests/regression/77-lin2vareq/02-equality_assertion.c @@ -0,0 +1,17 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq +#include +#include + +int main() { + int x = 0; + int y = 0; + + x = 10; + y = 1; + + __goblint_check(x == 10 * y); //SUCCESS + + if(x == 10 * y) + return 0; + __goblint_check(0); // NOWARN (unreachable) +} diff --git a/tests/regression/77-lin2vareq/02-reachability.c b/tests/regression/77-lin2vareq/04-reachability.c similarity index 100% rename from tests/regression/77-lin2vareq/02-reachability.c rename to tests/regression/77-lin2vareq/04-reachability.c diff --git a/tests/regression/77-lin2vareq/05-vars_equality.c b/tests/regression/77-lin2vareq/05-vars_equality.c new file mode 100644 index 0000000000..0a5eb1d3ec --- /dev/null +++ b/tests/regression/77-lin2vareq/05-vars_equality.c @@ -0,0 +1,26 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +// from https://dl.acm.org/doi/10.1145/2049706.2049710 + +#include + +int main() { + int x1 = 5, x2 = 10, x3 = 15, x4, x5, x6, x7, x8, x9, x10, x11, x12; + + x4 = 3 * x2 + 5; + x5 = 3 * x3 + 15; + x6 = x3 + 3; + x7 = x3 + 2; + x8 = 7 * x3 + 15; + x9 = 0; + x10 = 2 * x9 + 2; + x11 = 2 * x1 - 3; + x12 = 4 * x1 - 5; + + __goblint_check(x4 == 3 * x2 + 5); //SUCCESS + __goblint_check(x5 == 3 * x3 + 15); //SUCCESS + __goblint_check(x7 == x6 - 1); //SUCCESS + __goblint_check(x10 == 2 * x9 + 2); //SUCCESS + __goblint_check(x12 == 2 * x11 + 1); //SUCCESS + + return 0; +} \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/06-pointer.c b/tests/regression/77-lin2vareq/06-pointer.c new file mode 100644 index 0000000000..e250133918 --- /dev/null +++ b/tests/regression/77-lin2vareq/06-pointer.c @@ -0,0 +1,36 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +// from https://dl.acm.org/doi/10.1145/2049706.2049710 + +#include + +typedef int dataX_t; +typedef int dataY_t; + +dataX_t x_arr[100]; +dataY_t y_arr[100]; +dataX_t *x_ptr; +dataY_t *y_ptr; + +void access() { + *x_ptr = 42; + *y_ptr = *x_ptr + 10; +} + +int main() { + int i; + x_ptr = &x_arr[0]; + y_ptr = &y_arr[0]; + + for (i = 0; i < 100; i++) { + access(); + + __goblint_check(i == 8 * i + 0); //UNKNOWN! + __goblint_check(x_ptr == 8 * i + x_arr); //UNKNOWN! + __goblint_check(y_ptr == 4 * i + y_arr); //UNKNOWN! + + x_ptr++; + y_ptr++; + } + + return 0; +} diff --git a/tests/regression/77-lin2vareq/10-complicated_expression.c b/tests/regression/77-lin2vareq/07-complicated_expression.c similarity index 100% rename from tests/regression/77-lin2vareq/10-complicated_expression.c rename to tests/regression/77-lin2vareq/07-complicated_expression.c diff --git a/tests/regression/77-lin2vareq/06-distributive.c b/tests/regression/77-lin2vareq/08-distributive.c similarity index 100% rename from tests/regression/77-lin2vareq/06-distributive.c rename to tests/regression/77-lin2vareq/08-distributive.c diff --git a/tests/regression/77-lin2vareq/05-associative.c b/tests/regression/77-lin2vareq/09-associative.c similarity index 100% rename from tests/regression/77-lin2vareq/05-associative.c rename to tests/regression/77-lin2vareq/09-associative.c diff --git a/tests/regression/77-lin2vareq/07-commutative.c b/tests/regression/77-lin2vareq/10-commutative.c similarity index 100% rename from tests/regression/77-lin2vareq/07-commutative.c rename to tests/regression/77-lin2vareq/10-commutative.c diff --git a/tests/regression/77-lin2vareq/11-complicated_expression.c b/tests/regression/77-lin2vareq/11-complicated_expression.c new file mode 100644 index 0000000000..08c0a1d952 --- /dev/null +++ b/tests/regression/77-lin2vareq/11-complicated_expression.c @@ -0,0 +1,19 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none + +#include + +int main() { + int x; + int k; + int y = 5; + + int result1 = 3 * (x + y) - 2 * x + 6; + int result2 = 3 * (x + y) - 2 * k + 6; + + __goblint_check(result1 == x + 21); // SUCCESS + __goblint_check(result2 == x + 21); // UNKNOWN! + + return 0; +} + +// This test case includes variable with unknown values diff --git a/tests/regression/77-lin2vareq/08-loop.c b/tests/regression/77-lin2vareq/11-loop.c similarity index 100% rename from tests/regression/77-lin2vareq/08-loop.c rename to tests/regression/77-lin2vareq/11-loop.c diff --git a/tests/regression/77-lin2vareq/13-join_after_guard.c b/tests/regression/77-lin2vareq/13-join_after_guard.c new file mode 100644 index 0000000000..7ea2741fb3 --- /dev/null +++ b/tests/regression/77-lin2vareq/13-join_after_guard.c @@ -0,0 +1,15 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq --set ana.relation.privatization top --set sem.int.signed_overflow assume_none +void main(void) { + //int zero = 0; + //int x = 0; + + int a, b, c; + if (a) { + b = 20; + c = 250; + } else { + b = 20; + } + __goblint_check(b == 20); + __goblint_check(c == 250); //UNKNOWN! +} \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/14-coeff_vec.c b/tests/regression/77-lin2vareq/14-coeff_vec.c new file mode 100644 index 0000000000..1cc351c5c6 --- /dev/null +++ b/tests/regression/77-lin2vareq/14-coeff_vec.c @@ -0,0 +1,21 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq + +int main() { + + unsigned int a = 1; + + unsigned int b = -a; + + __goblint_check(b == 4294967295); + + unsigned short int allbits = -1; + + short int signedallbits = allbits; + + __goblint_check(signedallbits == -1); + + short c = 32767; + c = c + 2; + + __goblint_check(c == -32767); +} \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/15-env_order.c b/tests/regression/77-lin2vareq/15-env_order.c new file mode 100644 index 0000000000..19ed368c06 --- /dev/null +++ b/tests/regression/77-lin2vareq/15-env_order.c @@ -0,0 +1,14 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none + int next; + +int main() { + next = 0; + int t; + next = t; + + if (next == 0) { + t = 5; + } + + __goblint_check(t == 5); //UNKNOWN! +} \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/16-multiple-vars.c b/tests/regression/77-lin2vareq/16-multiple-vars.c new file mode 100644 index 0000000000..8444d12256 --- /dev/null +++ b/tests/regression/77-lin2vareq/16-multiple-vars.c @@ -0,0 +1,9 @@ +//SKIP //PARAM: --set ana.activated[+] lin2vareq --enable ana.int.interval + +int f (int j) { + return j + 1; +} +int main() { + int test = f(10); + __goblint_check(test == 11); + } \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/17-partitioning.c b/tests/regression/77-lin2vareq/17-partitioning.c new file mode 100644 index 0000000000..c22affbe33 --- /dev/null +++ b/tests/regression/77-lin2vareq/17-partitioning.c @@ -0,0 +1,33 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +#include +#include + +int main() { + +int x,x1,x2,x3,x4,x5,x6,x7; + +if(x>5){ + x1 = x1; + x2 = x2; + x3 = x1; + x4 = x2 + 5; + x5 = x1 + 5; + x6 = x1 + 3; + x7 = x1 + 2; +} else { + x1 = x1; + x2 = x2; + x3 = x2 - 5; + x4 = x2 + 5; + x5 = x2; + x6 = x2 + 1; + x7 = x2; +} + +__goblint_check(x4 == x2 + 5); +__goblint_check(x5 == x3 + 5); +__goblint_check(x7 == x6 - 1); + +return 0; +} + diff --git a/tests/regression/77-lin2vareq/18-loop_relational.c b/tests/regression/77-lin2vareq/18-loop_relational.c new file mode 100644 index 0000000000..8151e649d2 --- /dev/null +++ b/tests/regression/77-lin2vareq/18-loop_relational.c @@ -0,0 +1,20 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +#include +#include + +int main() { + int x = 0; + int y = 10; + int z = 5; + + for(int i = 0; i < 3; i++) { + x += z; + y -= i; + z += 2; + } + + __goblint_check(x == 21); //UNKNOWN! + __goblint_check(y == 7); //UNKNOWN! + __goblint_check(z == 11); //UNKNOWN! + return 0; +} diff --git a/tests/regression/77-lin2vareq/19-linear_loop.c b/tests/regression/77-lin2vareq/19-linear_loop.c new file mode 100644 index 0000000000..a5e790fd4e --- /dev/null +++ b/tests/regression/77-lin2vareq/19-linear_loop.c @@ -0,0 +1,20 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +#include +#include + +int main() { + int x = 1; + int y = 1; + int z = 1; + + for (int i = 1; i <= 3; i++) { + x = x * i; + y = y + x; + z = z + (y - x); + } + + __goblint_check(x == 6); //UNKNOWN! + __goblint_check(z != 1); //UNKNOWN! + + return 0; +} diff --git a/tests/regression/77-lin2vareq/20-commutative_2.c b/tests/regression/77-lin2vareq/20-commutative_2.c new file mode 100644 index 0000000000..e6795368d0 --- /dev/null +++ b/tests/regression/77-lin2vareq/20-commutative_2.c @@ -0,0 +1,20 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +#include + +int main() { + int a = 5; + int b = 3; + int c = 2; + + for (int i = 0; i < c; i++) { + a += b; + b += 1; + } + + int expression1 = a * b; + int expression2 = b * a; + + __goblint_check(expression1 == expression2); //UNKNOWN + + return 0; +} diff --git a/tests/regression/77-lin2vareq/21-algebraic_equivalence.c b/tests/regression/77-lin2vareq/21-algebraic_equivalence.c new file mode 100644 index 0000000000..76b208783a --- /dev/null +++ b/tests/regression/77-lin2vareq/21-algebraic_equivalence.c @@ -0,0 +1,22 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +#include + +int main() { + int a = 5; + int b = 3; + int c = 2; + int d = 0; + + for (int i = 0; i < c; i++) { + a += i; + b *= 2; + d += (a - b); + } + + int expression1 = a * b + d; + int expression2 = b * a - (d * -1); + + __goblint_check(expression1 == expression2); //UNKNOWN! + + return 0; +} diff --git a/tests/regression/77-lin2vareq/22-distributive_2.c b/tests/regression/77-lin2vareq/22-distributive_2.c new file mode 100644 index 0000000000..57707eafb1 --- /dev/null +++ b/tests/regression/77-lin2vareq/22-distributive_2.c @@ -0,0 +1,23 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +#include + +int main() { + int a = 5; + int b = 3; + int c = 2; + int d = 1; + + for (int i = 0; i < 3; i++) { + a += d; + b += a; + c += b; + d *= 2; + } + + int expression1 = a * (b + c); + int expression2 = (a * b) + (a * c); + + __goblint_check(expression1 == expression2); //UNKNOWN! + + return 0; +} diff --git a/tests/regression/77-lin2vareq/04-unknown.c b/tests/regression/77-lin2vareq/23-unknown.c similarity index 100% rename from tests/regression/77-lin2vareq/04-unknown.c rename to tests/regression/77-lin2vareq/23-unknown.c diff --git a/tests/regression/77-lin2vareq/09-constant_square.c b/tests/regression/77-lin2vareq/24-constant_square.c similarity index 100% rename from tests/regression/77-lin2vareq/09-constant_square.c rename to tests/regression/77-lin2vareq/24-constant_square.c diff --git a/tests/regression/77-lin2vareq/25-simple_square.c b/tests/regression/77-lin2vareq/25-simple_square.c new file mode 100644 index 0000000000..213a172585 --- /dev/null +++ b/tests/regression/77-lin2vareq/25-simple_square.c @@ -0,0 +1,13 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +#include + +int main() { + int x; + int y = 5; + + x = y * y; + + __goblint_check(x == y * y); //SUCCESS + + return 0; +} diff --git a/tests/regression/77-lin2vareq/11-overflow_ignored.c b/tests/regression/77-lin2vareq/26-overflow_ignored.c similarity index 100% rename from tests/regression/77-lin2vareq/11-overflow_ignored.c rename to tests/regression/77-lin2vareq/26-overflow_ignored.c diff --git a/tests/regression/77-lin2vareq/27-loop_increment.c b/tests/regression/77-lin2vareq/27-loop_increment.c new file mode 100644 index 0000000000..cbf608b07b --- /dev/null +++ b/tests/regression/77-lin2vareq/27-loop_increment.c @@ -0,0 +1,20 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +#include + +int main() { + int i, j, k; + int size = 5; + + for (i = 0; i < size; ++i) { + j = 2 * i; + k = j + i; + + __goblint_check(j == 2 * i); //UNKNOWN! + + __goblint_check(k == j + i); //UNKNOWN! + + __goblint_check(k == 3 * i); //UNKNOWN! + } + + return 0; +} diff --git a/tests/regression/77-lin2vareq/13-bounds_guards_ov.c b/tests/regression/77-lin2vareq/28-bounds_guards_ov.c similarity index 100% rename from tests/regression/77-lin2vareq/13-bounds_guards_ov.c rename to tests/regression/77-lin2vareq/28-bounds_guards_ov.c diff --git a/tests/regression/77-lin2vareq/14-join_after_guard.c b/tests/regression/77-lin2vareq/29-join_after_guard.c similarity index 100% rename from tests/regression/77-lin2vareq/14-join_after_guard.c rename to tests/regression/77-lin2vareq/29-join_after_guard.c diff --git a/tests/regression/77-lin2vareq/15-join-non-constant.c b/tests/regression/77-lin2vareq/30-join-non-constant.c similarity index 100% rename from tests/regression/77-lin2vareq/15-join-non-constant.c rename to tests/regression/77-lin2vareq/30-join-non-constant.c From 13d38ad214aa1266a7b52ff917db61f9c028cc8e Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 7 Jan 2024 17:44:59 +0100 Subject: [PATCH 100/280] fixed a bug in join and also a bug in del_cols. Adapted corresponding test cases --- gobview | 2 +- .../apron/linearTwoVarEqualityDomain.apron.ml | 24 +++++++--------- tests/regression/63-affeq/12-const_guards.c | 4 +-- tests/regression/63-affeq/19-join_all_cases.c | 28 +++++++++++++++++++ .../77-lin2vareq/11-complicated_expression.c | 19 ------------- tests/regression/77-lin2vareq/15-env_order.c | 8 ++---- .../regression/77-lin2vareq/17-partitioning.c | 4 +-- 7 files changed, 46 insertions(+), 43 deletions(-) create mode 100644 tests/regression/63-affeq/19-join_all_cases.c delete mode 100644 tests/regression/77-lin2vareq/11-complicated_expression.c diff --git a/gobview b/gobview index 42b07f8253..3de13d7412 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit 42b07f825316052ec030370daf0d00ebe28ec092 +Subproject commit 3de13d74124ab7bc30d8be299f02570d8f498b84 diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 315316dbf4..f6230a2b3c 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -34,6 +34,8 @@ module EqualitiesArray = struct include Array type t = Equality.t Array.t [@@deriving eq, ord] + let print = Array.iter (fun k -> Equality.print k) + let hash : t -> int = Array.fold_left (fun acc a -> 31 * acc + Equality.hash a) 0 let empty () = [||] @@ -73,7 +75,7 @@ module EqualitiesArray = struct if nc = nrc then [||] else let offset = ref 0 in let offset_map = Array.init nc (fun j -> - if indexes.(!offset) = j then (incr offset; 0) else !offset) + if !offset < nrc && indexes.(!offset) = j then (incr offset; !offset) else !offset) in let remove_offset_from_array_entry (var, offs) = Option.map (fun var_index -> var_index - offset_map.(var_index)) var, offs in Array.init (nc - nrc) (fun j -> remove_offset_from_array_entry m.(j + offset_map.(j))) @@ -338,19 +340,7 @@ struct | (None, bt) -> (None, Z.(b' + bt)) | (Some xt, bt) -> (Some xt, Z.(b' + bt))) else (Some x', b')in - Stdlib.Option.iter (BatArray.modify adjust) !ts (* - match !ts with - | None -> () - | Some ts' -> - if Array.length ts' <> 0 then - for i = 0 to Array.length ts' - 1 do - match ts'.(i) with - | (None, _) -> () - | (Some x', b') -> if x = x' then - (match t with - | (None, bt) -> ts'.(i) <- (None, Z.(b' + bt)) - | (Some xt, bt) -> ts'.(i) <- (Some xt, Z.(b' + bt))) - done*) + Stdlib.Option.iter (BatArray.modify adjust) !ts in let add_conj ts t i = let adjust ts' = @@ -432,6 +422,9 @@ struct in (*Calculate new components as groups*) let new_components = BatList.group cmp_z table in + let only_equal_constants = List.for_all + (fun (_, (v_1, b_1), (v_2, b_2)) -> + Option.is_none v_1 && Option.is_none v_2 && b_1 == b_2) in (*Adjust the domain array to represent the new components*) let modify idx_h b_h (idx, (opt1, z1), (opt2, z2)) = if idx_h = idx then ad.(idx) <- (Some idx, Z.zero) @@ -440,6 +433,9 @@ struct in let iterate l = match l with + (* Case 1 first part in the paper *) + | l when only_equal_constants l -> () + (* Case 2 and second part of Case 1 in the paper *) | (idx_h, (_, b_h), _) :: t -> List.iter (modify idx_h b_h) l | [] -> () (*This should not happen, consider throwing exception*) in diff --git a/tests/regression/63-affeq/12-const_guards.c b/tests/regression/63-affeq/12-const_guards.c index 2d007c4e81..a5e75d9f8d 100644 --- a/tests/regression/63-affeq/12-const_guards.c +++ b/tests/regression/63-affeq/12-const_guards.c @@ -1,4 +1,4 @@ -//SKIP PARAM: --set ana.activated[+] affeq --enable ana.int.interval +//SKIP PARAM: --set ana.activated[+] affeq --enable ana.int.interval int main() { int two = 2; int three = 3; @@ -16,4 +16,4 @@ int main() { k = 1; } __goblint_check(k == 1); -} \ No newline at end of file +} diff --git a/tests/regression/63-affeq/19-join_all_cases.c b/tests/regression/63-affeq/19-join_all_cases.c new file mode 100644 index 0000000000..9f9ff5a6e9 --- /dev/null +++ b/tests/regression/63-affeq/19-join_all_cases.c @@ -0,0 +1,28 @@ +// SKIP PARAM: --set ana.activated[+] affeq --set ana.relation.privatization top --set sem.int.signed_overflow assume_none +void main(void) { + int x1, x2, x3, x4, x5, x6, x7, x8, x9; + int t; + if (t) { + x2 = 2; + x1 = 3; + x3 = 4; + x4 = 5; + x5 = x6 + 6; + x7 = x6 + 3; + x8 = x6 - 55; + } else { + x1 = 3; + x2 = 3; + x3 = 4; + x4 = 5; + x5 = x6 + 11; + x7 = x6 + 8; + x8 = x6 - 50; + } + __goblint_check(x1 == 3); + __goblint_check(x2 == 2); // UNKNOWN + __goblint_check(x3 == 4); + __goblint_check(x4 == 5); + __goblint_check(x7 == x5 - 3); + __goblint_check(x8 == x7 - 58); +} diff --git a/tests/regression/77-lin2vareq/11-complicated_expression.c b/tests/regression/77-lin2vareq/11-complicated_expression.c deleted file mode 100644 index 08c0a1d952..0000000000 --- a/tests/regression/77-lin2vareq/11-complicated_expression.c +++ /dev/null @@ -1,19 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none - -#include - -int main() { - int x; - int k; - int y = 5; - - int result1 = 3 * (x + y) - 2 * x + 6; - int result2 = 3 * (x + y) - 2 * k + 6; - - __goblint_check(result1 == x + 21); // SUCCESS - __goblint_check(result2 == x + 21); // UNKNOWN! - - return 0; -} - -// This test case includes variable with unknown values diff --git a/tests/regression/77-lin2vareq/15-env_order.c b/tests/regression/77-lin2vareq/15-env_order.c index 19ed368c06..f0613b4909 100644 --- a/tests/regression/77-lin2vareq/15-env_order.c +++ b/tests/regression/77-lin2vareq/15-env_order.c @@ -1,14 +1,12 @@ //SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none - int next; + int next; // global variables are initialized to 0 int main() { - next = 0; int t; - next = t; if (next == 0) { t = 5; } - __goblint_check(t == 5); //UNKNOWN! -} \ No newline at end of file + __goblint_check(t == 5); //SUCCESS +} diff --git a/tests/regression/77-lin2vareq/17-partitioning.c b/tests/regression/77-lin2vareq/17-partitioning.c index c22affbe33..d36f4d7dbb 100644 --- a/tests/regression/77-lin2vareq/17-partitioning.c +++ b/tests/regression/77-lin2vareq/17-partitioning.c @@ -1,4 +1,5 @@ // SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +// example from the paper #include #include @@ -13,7 +14,7 @@ if(x>5){ x4 = x2 + 5; x5 = x1 + 5; x6 = x1 + 3; - x7 = x1 + 2; + x7 = x1 + 2; } else { x1 = x1; x2 = x2; @@ -30,4 +31,3 @@ __goblint_check(x7 == x6 - 1); return 0; } - From 844e2495ec82c261120ca2203ac764fd8ad3b440 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 7 Jan 2024 17:49:10 +0100 Subject: [PATCH 101/280] Removed commented out code --- .../apron/linearTwoVarEqualityDomain.apron.ml | 135 ------------------ 1 file changed, 135 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index f6230a2b3c..69b6b19cee 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -454,141 +454,6 @@ struct | x, y when EArray.equal x y -> {d = Some x; env = a.env} | x, y -> {d = join_d x y; env = a.env} - - - (* - let join' a b = - let ts_zip t1 t2 = - if Array.length t1 <> Array.length t2 then None else - let zts = Array.init (Array.length t1) (fun (i : int) -> (i, t1.(i), t2.(i))) in - Some zts - in - let const_offset t = Tuple2.second t - in - let diff t1 t2 = Z.((const_offset t1) - (const_offset t2)) - in - let cmp_z (_, t1i, t2i) (_, t1j, t2j) = - let cmp_z_ref x y: int = - match x, y with - | (None, _), (None, _) -> 0 - | (None, _), (Some _, _) -> -1 - | (Some _, _), (None, _) -> 1 - | (Some ii, _), (Some ij, _) -> ii - ij - in - let diff_e1 = cmp_z_ref t1i t1j in - if diff_e1 <> 0 then diff_e1 - else - let diff_e2 = cmp_z_ref t2i t2j in - if diff_e2 <> 0 then diff_e2 else - Z.to_int (Z.((diff t1i t2i) - (diff t1j t2j))) - in - let sort_z_by_expr zts = - Stdlib.Option.iter (Array.stable_sort cmp_z) zts - in - let sort_annotated ats = - let cmp_annotated x y : int = (Tuple2.first x) - (Tuple2.first y) - in - Stdlib.Option.iter (Array.stable_sort cmp_annotated) ats - in - let process_eq_classes zts = - let is_const x = - match x with - | (_, (None, _), (None, _)) -> true - | _ -> false - in - let size_of_eq_class zts (start : int) : int = - let iterate result i e = - if i >= start && cmp_z zts.(start) e == 0 then result + 1 - else result in - Array.fold_lefti iterate 0 zts - in - let least_index_var_in_eq_class zts start size : int * Z.t = - let (i, (_, b), (_, _)) = zts.(start)in - let result = (i,b) in - let iterate (a, b) i (j, (_, bj), (_, _))= - if i > start && j < a then (j,bj) else (a,b) in - Array.fold_lefti iterate result zts - in - let all_are_const_in_eq_class zts start size : bool = - Array.fold_left (fun b e -> b && (is_const e)) true zts - in - let assign_vars_in_const_eq_class ats zts start size least_i least_b = - let adjust i e = if i < start then e - else - let (ai, t1, t2) = zts.(i)in - if Z.equal (diff t1 t2) (Z.zero) then (ai, t1) - else - (ai, (Some least_i, Z.sub (Tuple2.second t1) least_b)) - in - BatArray.modifyi adjust ats - in - let assign_vars_in_non_const_eq_class ats zts start size least_i least_b = - let adjust i e = if i < start then e - else ( - let (ai, t1, _) = zts.(i) in - let bj = const_offset t1 in - (ai, (Some least_i, Z.sub bj least_b))) - in - BatArray.modifyi adjust ats - in - let adjust_zts zts' = - (* - let mapi_zts i e = - let n = size_of_eq_class zts' i in - if n = 1 then - let (i', t1, t2) = zts'.(i) in - if is_const (i', t1, t2) && Z.equal (diff t1 t2) (Z.zero) then - (i', (None, const_offset t1)) - else (i', (Some i', Z.zero)) - else - let (least_i, least_b) = least_index_var_in_eq_class zts' i n in - (if all_are_const_in_eq_class zts' i n then - assign_vars_in_const_eq_class result zts' i n least_i least_b - else assign_vars_in_non_const_eq_class result zts'); e*) - let result = Array.make (Array.length zts') (0, (None, Z.zero)) in - let i = ref 0 in - while !i < Array.length zts' do - let n = size_of_eq_class zts' !i in - (if n = 1 then - let (i', t1, t2) = zts'.(!i) in - if is_const (i', t1, t2) && Z.equal (diff t1 t2) (Z.zero) then - result.(!i) <- (i', (None, const_offset t1)) - else result.(!i) <- (i', (Some i', Z.zero)) - else - let (least_i, least_b) = least_index_var_in_eq_class zts' !i n in - (if all_are_const_in_eq_class zts' !i n then - assign_vars_in_const_eq_class result zts' !i n least_i least_b - else assign_vars_in_non_const_eq_class result zts' !i n least_i least_b); - ); - i := !i + n; - done; - result - in - Stdlib.Option.map adjust_zts zts - in - let strip_annotation ats = - Option.map (Array.map snd) ats - in - let join_d t1 t2 = - let zipped = ts_zip t1 t2 in - sort_z_by_expr zipped; - let annotated = process_eq_classes zipped in - sort_annotated annotated; - let result = strip_annotation annotated in - result - in - if is_bot_env a then b else if is_bot_env b then a else - match Option.get a.d, Option.get b.d with - | x, y when is_top a || is_top b -> let new_env = Environment.lce a.env b.env - in (top_of new_env) - | x, y when (Environment.compare a.env b.env <> 0) -> - let sup_env = Environment.lce a.env b.env in - let mod_x = dim_add (Environment.dimchange a.env sup_env) x in - let mod_y = dim_add (Environment.dimchange b.env sup_env) y in - {d = join_d mod_x mod_y; env = sup_env} - | x, y when EArray.equal x y -> {d = Some x; env = a.env} - | x, y -> {d = join_d x y; env = a.env} -*) let join a b = timing_wrap "join" (join a) b let join a b = From ce415e552555570d81eba15c5be31170497972b1 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Mon, 8 Jan 2024 10:45:32 +0100 Subject: [PATCH 102/280] Small adjustment in Join --- .../apron/linearTwoVarEqualityDomain.apron.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 69b6b19cee..f0a7e8828e 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -422,21 +422,21 @@ struct in (*Calculate new components as groups*) let new_components = BatList.group cmp_z table in - let only_equal_constants = List.for_all + (*let only_equal_constants = List.for_all (fun (_, (v_1, b_1), (v_2, b_2)) -> - Option.is_none v_1 && Option.is_none v_2 && b_1 == b_2) in + Option.is_none v_1 && Option.is_none v_2 && b_1 == b_2) in*) (*Adjust the domain array to represent the new components*) let modify idx_h b_h (idx, (opt1, z1), (opt2, z2)) = - if idx_h = idx then ad.(idx) <- (Some idx, Z.zero) - else if opt1 = opt2 && z1 = z2 then () + if opt1 = opt2 && Z.(z1 = z2) then () + else if idx_h = idx then ad.(idx) <- (Some idx, Z.zero) else ad.(idx) <- (Some idx_h, Z.(z1 - b_h)) in let iterate l = match l with - (* Case 1 first part in the paper *) - | l when only_equal_constants l -> () + (* Case 1 first part in the paper + | l when only_equal_constants l -> () *) (* Case 2 and second part of Case 1 in the paper *) - | (idx_h, (_, b_h), _) :: t -> List.iter (modify idx_h b_h) l + | (idx_h, (_, b_h), _) :: t -> List.iter (modify idx_h b_h) l | [] -> () (*This should not happen, consider throwing exception*) in List.iter iterate new_components; Some ad in From c332c002fa1436ef58435af15619f543c69afcaf Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Mon, 8 Jan 2024 11:41:09 +0100 Subject: [PATCH 103/280] Beautification of test and join --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 3 +-- tests/regression/77-lin2vareq/13-join_after_guard.c | 5 +---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index f0a7e8828e..1671fe8ff9 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -413,8 +413,7 @@ struct let ad = Array.copy ad in (*This is the table which is later grouped*) let table = BatList.map2i (fun i a b -> (i, a, b)) (Array.to_list ad) (Array.to_list bd) in - let const_offset t = Tuple2.second t in - let diff t1 t2 = Z.(const_offset t1 - const_offset t2) in + let diff t1 t2 = Z.(snd t1 - snd t2) in (*compare two variables for grouping depending on delta function and reference index*) let cmp_z (_, t1i, t2i) (_, t1j, t2j) = let cmp_ref = Option.compare ~cmp:Int.compare in diff --git a/tests/regression/77-lin2vareq/13-join_after_guard.c b/tests/regression/77-lin2vareq/13-join_after_guard.c index 7ea2741fb3..9afdf4951e 100644 --- a/tests/regression/77-lin2vareq/13-join_after_guard.c +++ b/tests/regression/77-lin2vareq/13-join_after_guard.c @@ -1,8 +1,5 @@ //SKIP PARAM: --set ana.activated[+] lin2vareq --set ana.relation.privatization top --set sem.int.signed_overflow assume_none void main(void) { - //int zero = 0; - //int x = 0; - int a, b, c; if (a) { b = 20; @@ -12,4 +9,4 @@ void main(void) { } __goblint_check(b == 20); __goblint_check(c == 250); //UNKNOWN! -} \ No newline at end of file +} From 26e602f6dbeb539195dae298aa75f5ac1f0b6236 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Mon, 8 Jan 2024 12:25:30 +0100 Subject: [PATCH 104/280] Join Optimization --- .../apron/linearTwoVarEqualityDomain.apron.ml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 1671fe8ff9..d1b3bb1e2e 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -412,12 +412,12 @@ struct (*use copy of ad because result is later saved in there*) let ad = Array.copy ad in (*This is the table which is later grouped*) - let table = BatList.map2i (fun i a b -> (i, a, b)) (Array.to_list ad) (Array.to_list bd) in - let diff t1 t2 = Z.(snd t1 - snd t2) in + let table = BatList.map2i (fun i (ai, aj) (bi,bj) -> (i, Z.(aj - bj), (ai, aj), (bi,bj))) (Array.to_list ad) (Array.to_list bd) in + (*let diff t1 t2 = Z.(snd t1 - snd t2) in*) (*compare two variables for grouping depending on delta function and reference index*) - let cmp_z (_, t1i, t2i) (_, t1j, t2j) = + let cmp_z (_, t0i, t1i, t2i) (_, t0j, t1j, t2j) = let cmp_ref = Option.compare ~cmp:Int.compare in - Tuple3.compare ~cmp1:cmp_ref ~cmp2:cmp_ref ~cmp3:Z.compare (fst t1i, fst t2i, diff t1i t2i) (fst t1j, fst t2j, diff t1j t2j) + Tuple3.compare ~cmp1:cmp_ref ~cmp2:cmp_ref ~cmp3:Z.compare (fst t1i, fst t2i, t0i) (fst t1j, fst t2j, t0j) in (*Calculate new components as groups*) let new_components = BatList.group cmp_z table in @@ -425,7 +425,7 @@ struct (fun (_, (v_1, b_1), (v_2, b_2)) -> Option.is_none v_1 && Option.is_none v_2 && b_1 == b_2) in*) (*Adjust the domain array to represent the new components*) - let modify idx_h b_h (idx, (opt1, z1), (opt2, z2)) = + let modify idx_h b_h (idx, _, (opt1, z1), (opt2, z2)) = if opt1 = opt2 && Z.(z1 = z2) then () else if idx_h = idx then ad.(idx) <- (Some idx, Z.zero) else ad.(idx) <- (Some idx_h, Z.(z1 - b_h)) @@ -435,10 +435,11 @@ struct (* Case 1 first part in the paper | l when only_equal_constants l -> () *) (* Case 2 and second part of Case 1 in the paper *) - | (idx_h, (_, b_h), _) :: t -> List.iter (modify idx_h b_h) l + | (idx_h, _, (_, b_h), _) :: t -> List.iter (modify idx_h b_h) l | [] -> () (*This should not happen, consider throwing exception*) in - List.iter iterate new_components; Some ad in + List.iter iterate new_components; Some ad + in (*Normalize the two domains a and b such that both talk about the same variables*) if is_bot_env a then b else if is_bot_env b then a else From b12cabcfd5d1963e84f6885be824dfda4d9a49f3 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 8 Jan 2024 14:34:53 +0100 Subject: [PATCH 105/280] fix tests naming issue --- ...30-join-non-constant.c => 13-join-non-constant.c} | 0 tests/regression/77-lin2vareq/13-join_after_guard.c | 12 ------------ 2 files changed, 12 deletions(-) rename tests/regression/77-lin2vareq/{30-join-non-constant.c => 13-join-non-constant.c} (100%) delete mode 100644 tests/regression/77-lin2vareq/13-join_after_guard.c diff --git a/tests/regression/77-lin2vareq/30-join-non-constant.c b/tests/regression/77-lin2vareq/13-join-non-constant.c similarity index 100% rename from tests/regression/77-lin2vareq/30-join-non-constant.c rename to tests/regression/77-lin2vareq/13-join-non-constant.c diff --git a/tests/regression/77-lin2vareq/13-join_after_guard.c b/tests/regression/77-lin2vareq/13-join_after_guard.c deleted file mode 100644 index 9afdf4951e..0000000000 --- a/tests/regression/77-lin2vareq/13-join_after_guard.c +++ /dev/null @@ -1,12 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq --set ana.relation.privatization top --set sem.int.signed_overflow assume_none -void main(void) { - int a, b, c; - if (a) { - b = 20; - c = 250; - } else { - b = 20; - } - __goblint_check(b == 20); - __goblint_check(c == 250); //UNKNOWN! -} From d5bfcdda7f6631c6d5cc2a984c5a0aaa35b4cf26 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 8 Jan 2024 15:45:31 +0100 Subject: [PATCH 106/280] simplify a bit leq and meet --- .../apron/linearTwoVarEqualityDomain.apron.ml | 74 +++++++------------ 1 file changed, 25 insertions(+), 49 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index d1b3bb1e2e..c4cb145568 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -332,34 +332,25 @@ struct let meet t1 t2 = let sup_env = Environment.lce t1.env t2.env in let t1, t2 = change_d t1 sup_env ~add:true ~del:false, change_d t2 sup_env ~add:true ~del:false in - let subst_var ts x t = - let adjust e = match e with - | (None, b') -> (None, b') - | (Some x', b') -> if x = x' then - (match t with - | (None, bt) -> (None, Z.(b' + bt)) - | (Some xt, bt) -> (Some xt, Z.(b' + bt))) - else (Some x', b')in - Stdlib.Option.iter (BatArray.modify adjust) !ts + let subst_var ts x (vart, bt) = + let adjust (vare, b') = + if Option.eq ~eq:Int.equal (Some x) vare then (vart, Z.(b' + bt)) else (vare, b') in Option.may (BatArray.modify adjust) !ts in - let add_conj ts t i = + let add_conj ts (var, b) i = let adjust ts' = - (match t with - | (None, b) -> - (match ts'.(i) with - | (None, b') -> if not @@ Z.equal b b' then ts := None; - | (Some j, b') -> subst_var ts j (None, Z.(b - b'))) - | (Some j, b) -> - (match ts'.(i) with - | (None, b1) -> subst_var ts j (None, Z.(b1 - b)) - | (Some h1, b1) -> - (match ts'.(j) with - | (None, b2) -> subst_var ts i (None, Z.(b2 + b)) - | (Some h2, b2) -> - if h1 = h2 then - (if Z.(b1 <> (b2 + b)) then ts := None) - else if h1 < h2 then subst_var ts h2 (Some h1, Z.(b1 - (b + b2))) - else subst_var ts h1 (Some h2, Z.(b + (b2 - b1)))))) + let (var1, b1) = ts'.(i) in + (match var, var1 with + | None, None -> if not @@ Z.equal b b1 then ts := None; + | None, Some h1 -> subst_var ts h1 (None, Z.(b - b1)) + | Some j, None -> subst_var ts j (None, Z.(b1 - b)) + | Some j, Some h1 -> + (match ts'.(j) with + | (None, b2) -> subst_var ts i (None, Z.(b2 + b)) + | (Some h2, b2) -> + if h1 = h2 then + (if Z.(b1 <> (b2 + b)) then ts := None) + else if h1 < h2 then subst_var ts h2 (Some h1, Z.(b1 - (b + b2))) + else subst_var ts h1 (Some h2, Z.(b + (b2 - b1))))) in Stdlib.Option.iter adjust !ts in @@ -368,7 +359,7 @@ struct let ds = ref (Some (Array.copy d1')) in Array.iteri (fun j e -> add_conj ds e j) d2'; {d = !ds; env = sup_env} ) - | _ -> { d = None; env = sup_env} + | _ -> {d = None; env = sup_env} let meet t1 t2 = let res = meet t1 t2 in @@ -379,19 +370,11 @@ struct let leq t1 t2 = let env_comp = Environment.compare t1.env t2.env in (* Apron's Environment.compare has defined return values. *) - let implies ts t i : bool = - match t with - | (None, b) -> - (match ts.(i) with - | (None, b') -> Z.equal b b' - | _ -> false) - | (Some j, b) -> - (match ts.(i), ts.(j) with - | (None, b1), (None, b2) -> Z.equal b1 (Z.add b2 b) - | (Some h1, b1), (Some h2, b2) -> - h1 = h2 && Z.equal b1 (Z.add b2 b) - | _ -> false - ) + let implies ts (var, b) i = + let tuple_cmp = Tuple2.eq (Option.eq ~eq:Int.equal) (Z.equal) in + match var with + | None -> tuple_cmp (var, b) ts.(i) + | Some j -> tuple_cmp ts.(i) @@ Tuple2.map2 (Z.add b) ts.(j) in if env_comp = -2 || env_comp > 0 then false else if is_bot_env t1 || is_top t2 then true else @@ -421,24 +404,17 @@ struct in (*Calculate new components as groups*) let new_components = BatList.group cmp_z table in - (*let only_equal_constants = List.for_all - (fun (_, (v_1, b_1), (v_2, b_2)) -> - Option.is_none v_1 && Option.is_none v_2 && b_1 == b_2) in*) (*Adjust the domain array to represent the new components*) let modify idx_h b_h (idx, _, (opt1, z1), (opt2, z2)) = - if opt1 = opt2 && Z.(z1 = z2) then () - else if idx_h = idx then ad.(idx) <- (Some idx, Z.zero) + if opt1 = opt2 && Z.equal z1 z2 then () else ad.(idx) <- (Some idx_h, Z.(z1 - b_h)) in let iterate l = match l with - (* Case 1 first part in the paper - | l when only_equal_constants l -> () *) - (* Case 2 and second part of Case 1 in the paper *) | (idx_h, _, (_, b_h), _) :: t -> List.iter (modify idx_h b_h) l | [] -> () (*This should not happen, consider throwing exception*) in - List.iter iterate new_components; Some ad + List.iter iterate new_components; Some ad in (*Normalize the two domains a and b such that both talk about the same variables*) if is_bot_env a then b else if is_bot_env b then a From c9d0442257c51d31e16f15644f5e2990f2e84c4c Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 8 Jan 2024 18:09:06 +0100 Subject: [PATCH 107/280] divided meet function into meet and meet_with_one_conj and fixed bug in meet_tcons --- .../apron/linearTwoVarEqualityDomain.apron.ml | 87 +++++++++++-------- tests/regression/77-lin2vareq/30-meet-tcons.c | 14 +++ 2 files changed, 63 insertions(+), 38 deletions(-) create mode 100644 tests/regression/77-lin2vareq/30-meet-tcons.c diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index c4cb145568..a6ade252a1 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -329,36 +329,48 @@ struct let pretty () (x:t) = text (show x) let printXml f x = BatPrintf.fprintf f "\n\n\nequalities-array\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) - let meet t1 t2 = - let sup_env = Environment.lce t1.env t2.env in - let t1, t2 = change_d t1 sup_env ~add:true ~del:false, change_d t2 sup_env ~add:true ~del:false in + exception Contradiction + + let meet_with_one_conj_with ts i (var, b) = let subst_var ts x (vart, bt) = let adjust (vare, b') = - if Option.eq ~eq:Int.equal (Some x) vare then (vart, Z.(b' + bt)) else (vare, b') in Option.may (BatArray.modify adjust) !ts + if Option.eq ~eq:Int.equal (Some x) vare then (vart, Z.(b' + bt)) else (vare, b') in + BatArray.modify adjust ts in + let adjust ts' = + let (var1, b1) = ts'.(i) in + (match var, var1 with + | None, None -> if not @@ Z.equal b b1 then raise Contradiction + | None, Some h1 -> subst_var ts h1 (None, Z.(b - b1)) + | Some j, None -> subst_var ts j (None, Z.(b1 - b)) + | Some j, Some h1 -> + (match ts'.(j) with + | (None, b2) -> subst_var ts i (None, Z.(b2 + b)) + | (Some h2, b2) -> + if h1 = h2 then + (if Z.(b1 <> (b2 + b)) then raise Contradiction) + else if h1 < h2 then subst_var ts h2 (Some h1, Z.(b1 - (b + b2))) + else subst_var ts h1 (Some h2, Z.(b + (b2 - b1))))) in - let add_conj ts (var, b) i = - let adjust ts' = - let (var1, b1) = ts'.(i) in - (match var, var1 with - | None, None -> if not @@ Z.equal b b1 then ts := None; - | None, Some h1 -> subst_var ts h1 (None, Z.(b - b1)) - | Some j, None -> subst_var ts j (None, Z.(b1 - b)) - | Some j, Some h1 -> - (match ts'.(j) with - | (None, b2) -> subst_var ts i (None, Z.(b2 + b)) - | (Some h2, b2) -> - if h1 = h2 then - (if Z.(b1 <> (b2 + b)) then ts := None) - else if h1 < h2 then subst_var ts h2 (Some h1, Z.(b1 - (b + b2))) - else subst_var ts h1 (Some h2, Z.(b + (b2 - b1))))) - in - Stdlib.Option.iter adjust !ts + adjust ts + + let meet_with_one_conj t i e = + match t.d with + | None -> t + | Some d -> let res_d = Array.copy d in + match meet_with_one_conj_with res_d i e with + | exception Contradiction -> {d = None; env = t.env} + | () -> {d = Some res_d; env = t.env} + + let meet t1 t2 = + let sup_env = Environment.lce t1.env t2.env in + let t1, t2 = change_d t1 sup_env ~add:true ~del:false, change_d t2 sup_env ~add:true ~del:false in match t1.d, t2.d with | Some d1', Some d2' -> ( - let ds = ref (Some (Array.copy d1')) in - Array.iteri (fun j e -> add_conj ds e j) d2'; - {d = !ds; env = sup_env} ) + let res_d = Array.copy d1' in + match Array.iteri (meet_with_one_conj_with res_d) d2' with + | exception Contradiction -> {d = None; env = sup_env} + | () -> {d = Some res_d; env = sup_env} ) | _ -> {d = None; env = sup_env} let meet t1 t2 = @@ -487,9 +499,7 @@ struct subtract_const_from_var t assigned_var off | Some (Some exp_var, off) -> (* Statement "assigned_var = exp_var + off" (assigned_var is not the same as exp_var) *) - let added_equality = EqualitiesArray.make_empty_array (VarManagement.size t) in - added_equality.(assigned_var) <- (Some exp_var, off); - meet abstract_exists_var {d = Some added_equality; env = t.env} + meet_with_one_conj abstract_exists_var assigned_var (Some exp_var, off) end | None -> bot_env end @@ -636,30 +646,31 @@ struct | SUP when Z.gt !constant Z.zero -> t | DISEQ when not @@ Z.equal !constant Z.zero -> t | EQMOD scalar -> t - | _ -> bot_env (*Not supported right now - if Float.equal ( Float.modulo (Z.to_float expr.(0)) (convert_scalar scalar )) 0. then t else {d = None; env = t.env}*) + | _ -> bot_env else if var_count = 1 then let index = Array.findi (fun a -> not @@ Z.equal a Z.zero) expr in let var = (index, expr.(index)) in - let c = if Z.divisible !constant @@ Tuple2.second var then Some (Z.(-(!constant) / (Tuple2.second var))) + let c = if Z.divisible !constant @@ snd var then Some (Z.(-(!constant) / (snd var))) else None in match Tcons1.get_typ tcons, c with | EQ, Some c -> - let expression = Texpr1.to_expr @@ Texpr1.cst t.env (Coeff.s_of_int @@ Z.to_int c) in - let res = meet t (assign_texpr (top_of t.env) (Environment.var_of_dim t.env (Tuple2.first var)) expression) + let res = meet_with_one_conj t (fst var) (None, c) in overflow_handling res original_expr | _ -> t (*Not supported right now*) else if var_count = 2 then let get_vars i a l = if Z.equal a Z.zero then l else (i, a)::l in let v12 = Array.fold_righti get_vars expr [] in - let a1 = Tuple2.second (List.hd v12) in - let a2 = Tuple2.second (List.hd @@ List.tl v12) in - let var1 = Environment.var_of_dim t.env (Tuple2.first (List.hd v12)) in - let var2 = Environment.var_of_dim t.env (Tuple2.first (List.hd @@ List.tl v12)) in + let a1 = snd (List.hd v12) in + let a2 = snd (List.hd @@ List.tl v12) in + let var1 = fst (List.hd v12) in + let var2 = fst (List.hd @@ List.tl v12) in match Tcons1.get_typ tcons with | EQ -> - let res = if Z.equal a1 Z.one && Z.equal a2 Z.one - then meet t (assign_var (top_of t.env) var1 var2) + let res = + if Z.equal a1 Z.one && Z.equal a2 Z.(-one) + then meet_with_one_conj t var2 (Some var1, !constant) + else if Z.equal a1 Z.(-one) && Z.equal a2 Z.one + then meet_with_one_conj t var1 (Some var2, !constant) else t in overflow_handling res original_expr | _-> t (*Not supported right now*) diff --git a/tests/regression/77-lin2vareq/30-meet-tcons.c b/tests/regression/77-lin2vareq/30-meet-tcons.c new file mode 100644 index 0000000000..8286c62467 --- /dev/null +++ b/tests/regression/77-lin2vareq/30-meet-tcons.c @@ -0,0 +1,14 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none + +int main(void) { + int x, y, z; + + if (x == 0) { + __goblint_check(x == 0); // SUCCESS + } else if (y - x == 3) { + __goblint_check(y == x + 0); // FAILURE + __goblint_check(y - x == 3); // SUCCESS + } + + return 0; +} From 48a45cba27d782dc5ec36cac8f70af777a295337 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Mon, 8 Jan 2024 23:35:52 +0100 Subject: [PATCH 108/280] invariant function --- .../apron/linearTwoVarEqualityDomain.apron.ml | 56 +++++++------------ 1 file changed, 19 insertions(+), 37 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index a6ade252a1..f282858b01 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -715,45 +715,27 @@ struct Lincons -> linear constraint *) (*TODO*) - let invariant t = [] - (*let invariant t = + let invariant t = match t.d with | None -> [] - | Some m -> - let linear_constraints = - EArray.fold_left - (fun acc row -> - let coeff_vars = List.map (fun(var,off) -> Coeff.s_of_int off, Some var) row in - let cst = Coeff.s_of_int (snd (List.hd row)) in - Lincons1.make (Linexpr1.make t.env) Lincons1.EQ - |> Lincons1.set_list coeff_vars (Some cst) - |> (fun lc -> Lincons1.{lincons0 = Lincons0.of_lincons1 lc; env = t.env}) - :: acc) - [] m - in - List.rev linear_constraints *) - - (* let invariant t = - match t.d with - | None -> [] - | Some m -> - let linear_constraints = - EArray.fold_left - (fun acc row -> - let lc = - List.fold_left - (fun lc (var, off) -> - let coeff = Coeff.s_of_int off in - let var_opt = Some var in - Lincons1.set_coeff lc var_opt coeff) - (Lincons1.make (Linexpr1.make t.env) Lincons1.EQ) - row - |> fun lc -> Lincons1.set_cst lc (Coeff.s_of_int (snd (List.hd row))) - in - Lincons1.{ lincons0 = Lincons0.of_lincons1 lc; env = t.env } :: acc) - [] m - in - List.rev linear_constraints *) + | Some d -> + let earray = Lincons1.array_make t.env (Array.length d) in + for i = 0 to Array.length d - 1 do + let (var_opt, const) = d.(i) in + let coeff_vars = match var_opt with + | None -> [] + | Some var_index -> + let var = Environment.var_of_dim t.env var_index in + [(Coeff.s_of_int 1, var)] + in + let cst = Coeff.s_of_int (Z.to_int const) in + Lincons1.set_list (Lincons1.array_get earray i) coeff_vars (Some cst) + done; + let {lincons0_array; array_env}: Lincons1.earray = earray in + Array.to_list lincons0_array + |> List.map (fun lincons0 -> + Lincons1.{lincons0; env = array_env} + ) let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 From 2333abac41070a1bb818f5619031d10671c11274 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Tue, 9 Jan 2024 13:58:34 +0100 Subject: [PATCH 109/280] remove an indirection --- .../apron/linearTwoVarEqualityDomain.apron.ml | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index f282858b01..8f59e9ddb2 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -336,22 +336,19 @@ struct let adjust (vare, b') = if Option.eq ~eq:Int.equal (Some x) vare then (vart, Z.(b' + bt)) else (vare, b') in BatArray.modify adjust ts in - let adjust ts' = - let (var1, b1) = ts'.(i) in - (match var, var1 with - | None, None -> if not @@ Z.equal b b1 then raise Contradiction - | None, Some h1 -> subst_var ts h1 (None, Z.(b - b1)) - | Some j, None -> subst_var ts j (None, Z.(b1 - b)) - | Some j, Some h1 -> - (match ts'.(j) with - | (None, b2) -> subst_var ts i (None, Z.(b2 + b)) - | (Some h2, b2) -> - if h1 = h2 then - (if Z.(b1 <> (b2 + b)) then raise Contradiction) - else if h1 < h2 then subst_var ts h2 (Some h1, Z.(b1 - (b + b2))) - else subst_var ts h1 (Some h2, Z.(b + (b2 - b1))))) - in - adjust ts + let (var1, b1) = ts.(i) in + (match var, var1 with + | None, None -> if not @@ Z.equal b b1 then raise Contradiction + | None, Some h1 -> subst_var ts h1 (None, Z.(b - b1)) + | Some j, None -> subst_var ts j (None, Z.(b1 - b)) + | Some j, Some h1 -> + (match ts.(j) with + | (None, b2) -> subst_var ts i (None, Z.(b2 + b)) + | (Some h2, b2) -> + if h1 = h2 then + (if Z.(b1 <> (b2 + b)) then raise Contradiction) + else if h1 < h2 then subst_var ts h2 (Some h1, Z.(b1 - (b + b2))) + else subst_var ts h1 (Some h2, Z.(b + (b2 - b1))))) let meet_with_one_conj t i e = match t.d with @@ -714,7 +711,6 @@ struct This function returns all the equalities that are saved in our datastructure t. Lincons -> linear constraint *) - (*TODO*) let invariant t = match t.d with | None -> [] @@ -735,7 +731,7 @@ struct Array.to_list lincons0_array |> List.map (fun lincons0 -> Lincons1.{lincons0; env = array_env} - ) + ) let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 From 0b5cb5cba2245f0b54f76bc4e3345dc525ee5516 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini <62880949+reb-ddm@users.noreply.github.com> Date: Tue, 9 Jan 2024 20:45:20 +0100 Subject: [PATCH 110/280] fix spelling Co-authored-by: Julian Erhard --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 8f59e9ddb2..4146275972 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -1,4 +1,4 @@ -(** OCaml implementation of the linear two-Variable equalitie domain. +(** OCaml implementation of the linear two-variable equality domain. @see A. Flexeder, M. Petter, and H. Seidl Fast Interprocedural Linear Two-Variable Equalities. *) From e4034b336e3e6c8d69d6e11a90289bf8d94d667f Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 10 Jan 2024 13:28:06 +0100 Subject: [PATCH 111/280] function call test --- .../77-lin2vareq/31-function-call.c | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/regression/77-lin2vareq/31-function-call.c diff --git a/tests/regression/77-lin2vareq/31-function-call.c b/tests/regression/77-lin2vareq/31-function-call.c new file mode 100644 index 0000000000..b672fc0d36 --- /dev/null +++ b/tests/regression/77-lin2vareq/31-function-call.c @@ -0,0 +1,20 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none + +int myfunction(int x, int y, int z){ + if (x == 0) { + __goblint_check(x == 0); // SUCCESS + } else if (y - x == 3) { + __goblint_check(y == x + 0); // FAILURE + __goblint_check(y - x == 3); // SUCCESS + } + + return 5; +} + + + +int main(void) { + int x, y, z; + z = myfunction(); + return 0; +} From 68c42f668e8ee9c6a2409c0e35ff67062e62c1b0 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 10 Jan 2024 13:29:54 +0100 Subject: [PATCH 112/280] fix test --- tests/regression/77-lin2vareq/31-function-call.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/77-lin2vareq/31-function-call.c b/tests/regression/77-lin2vareq/31-function-call.c index b672fc0d36..8b28b6ce6b 100644 --- a/tests/regression/77-lin2vareq/31-function-call.c +++ b/tests/regression/77-lin2vareq/31-function-call.c @@ -1,6 +1,6 @@ // SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none -int myfunction(int x, int y, int z){ +int myfunction(int x, int y){ if (x == 0) { __goblint_check(x == 0); // SUCCESS } else if (y - x == 3) { @@ -15,6 +15,6 @@ int myfunction(int x, int y, int z){ int main(void) { int x, y, z; - z = myfunction(); + z = myfunction(x,y); return 0; } From 0b3e1e5c2c827eb034507427680d2b25abdd9e03 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Wed, 10 Jan 2024 14:22:31 +0100 Subject: [PATCH 113/280] fix invariant function --- .../apron/linearTwoVarEqualityDomain.apron.ml | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 4146275972..f6cee82e24 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -711,28 +711,25 @@ struct This function returns all the equalities that are saved in our datastructure t. Lincons -> linear constraint *) + (*TODO*) let invariant t = match t.d with | None -> [] | Some d -> - let earray = Lincons1.array_make t.env (Array.length d) in - for i = 0 to Array.length d - 1 do - let (var_opt, const) = d.(i) in + let acc = ref [] in + Array.iteri (fun i (var_opt, const) -> let coeff_vars = match var_opt with | None -> [] | Some var_index -> let var = Environment.var_of_dim t.env var_index in - [(Coeff.s_of_int 1, var)] + [(Coeff.s_of_int (-1), var)] in let cst = Coeff.s_of_int (Z.to_int const) in - Lincons1.set_list (Lincons1.array_get earray i) coeff_vars (Some cst) - done; - let {lincons0_array; array_env}: Lincons1.earray = earray in - Array.to_list lincons0_array - |> List.map (fun lincons0 -> - Lincons1.{lincons0; env = array_env} - ) - + let lincons = Lincons1.make (Linexpr1.make t.env) Lincons1.EQ in + Lincons1.set_list lincons coeff_vars (Some cst); + acc := lincons :: !acc + ) d; + List.rev !acc let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 From 324758af95b9ac054edb4a943c9e36f28ee928be Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Wed, 10 Jan 2024 15:42:43 +0100 Subject: [PATCH 114/280] fix invariant functio --- .../apron/linearTwoVarEqualityDomain.apron.ml | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index f6cee82e24..79949e7b27 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -712,7 +712,7 @@ struct Lincons -> linear constraint *) (*TODO*) - let invariant t = + (*let invariant t = match t.d with | None -> [] | Some d -> @@ -729,7 +729,29 @@ struct Lincons1.set_list lincons coeff_vars (Some cst); acc := lincons :: !acc ) d; - List.rev !acc + List.rev !acc *) + + let invariant t = + match t.d with + | None -> [] + | Some d -> + let acc = ref [] in + Array.iteri (fun i (var_opt, const) -> + let xi = Environment.var_of_dim t.env i in + let coeff_vars = + [(Coeff.s_of_int (-1), xi)] @ (match var_opt with + | None -> [] + | Some var_index -> + let var = Environment.var_of_dim t.env var_index in + [(Coeff.s_of_int 1, var)]) + in + let cst = Coeff.s_of_int (Z.to_int const) in + let lincons = Lincons1.make (Linexpr1.make t.env) Lincons1.EQ in + Lincons1.set_list lincons coeff_vars (Some cst); + acc := lincons :: !acc + ) d; + List.rev !acc + let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 From 26340f438b211175e961bcac5f145436c4f97268 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 10 Jan 2024 15:45:05 +0100 Subject: [PATCH 115/280] fixed big in del_cols --- .../apron/linearTwoVarEqualityDomain.apron.ml | 39 ++++++++++--------- src/cdomains/apron/sharedFunctions.apron.ml | 2 +- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 79949e7b27..f1130c88ed 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -75,10 +75,11 @@ module EqualitiesArray = struct if nc = nrc then [||] else let offset = ref 0 in let offset_map = Array.init nc (fun j -> - if !offset < nrc && indexes.(!offset) = j then (incr offset; !offset) else !offset) + if !offset < nrc && indexes.(!offset) = j then (incr offset; 0) else !offset) in let remove_offset_from_array_entry (var, offs) = Option.map (fun var_index -> var_index - offset_map.(var_index)) var, offs in - Array.init (nc - nrc) (fun j -> remove_offset_from_array_entry m.(j + offset_map.(j))) + Array.(copy m |> filteri (fun i _ -> not @@ Array.mem i indexes) + |> map remove_offset_from_array_entry) let del_cols m cols = timing_wrap "del_cols" (del_cols m) cols @@ -731,27 +732,27 @@ struct ) d; List.rev !acc *) - let invariant t = - match t.d with - | None -> [] - | Some d -> - let acc = ref [] in - Array.iteri (fun i (var_opt, const) -> - let xi = Environment.var_of_dim t.env i in - let coeff_vars = - [(Coeff.s_of_int (-1), xi)] @ (match var_opt with + let invariant t = + match t.d with + | None -> [] + | Some d -> + let acc = ref [] in + Array.iteri (fun i (var_opt, const) -> + let xi = Environment.var_of_dim t.env i in + let coeff_vars = + [(Coeff.s_of_int (-1), xi)] @ (match var_opt with | None -> [] | Some var_index -> let var = Environment.var_of_dim t.env var_index in [(Coeff.s_of_int 1, var)]) - in - let cst = Coeff.s_of_int (Z.to_int const) in - let lincons = Lincons1.make (Linexpr1.make t.env) Lincons1.EQ in - Lincons1.set_list lincons coeff_vars (Some cst); - acc := lincons :: !acc - ) d; - List.rev !acc - + in + let cst = Coeff.s_of_int (Z.to_int const) in + let lincons = Lincons1.make (Linexpr1.make t.env) Lincons1.EQ in + Lincons1.set_list lincons coeff_vars (Some cst); + acc := lincons :: !acc + ) d; + List.rev !acc + let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 699bb49bd4..2a9809cbae 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -307,7 +307,7 @@ struct let dim_remove (ch: Apron.Dim.change) m ~del = if Array.length ch.dim = 0 || RelDomain.is_empty m then m else ( Array.iteri (fun i x-> ch.dim.(i) <- x + i) ch.dim; - let m' = if not del then let m = RelDomain.copy m in Array.fold_left (fun y x -> RelDomain.reduce_col_with y x; y) m ch.dim else m in + let m' = Array.fold_left (fun y x -> RelDomain.reduce_col_with y x; y) (RelDomain.copy m) ch.dim in RelDomain.remove_zero_rows @@ RelDomain.del_cols m' ch.dim) let dim_remove ch m ~del = VectorMatrix.timing_wrap "dim remove" (fun del -> dim_remove ch m ~del:del) del From f4e1495eb4e9bb2598018e16b2333cf38921fd08 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Wed, 10 Jan 2024 23:40:23 +0100 Subject: [PATCH 116/280] fix invariant function --- .../apron/linearTwoVarEqualityDomain.apron.ml | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index f1130c88ed..e6e7c33473 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -732,27 +732,26 @@ struct ) d; List.rev !acc *) - let invariant t = - match t.d with - | None -> [] - | Some d -> - let acc = ref [] in - Array.iteri (fun i (var_opt, const) -> - let xi = Environment.var_of_dim t.env i in - let coeff_vars = - [(Coeff.s_of_int (-1), xi)] @ (match var_opt with - | None -> [] - | Some var_index -> - let var = Environment.var_of_dim t.env var_index in - [(Coeff.s_of_int 1, var)]) - in - let cst = Coeff.s_of_int (Z.to_int const) in - let lincons = Lincons1.make (Linexpr1.make t.env) Lincons1.EQ in - Lincons1.set_list lincons coeff_vars (Some cst); - acc := lincons :: !acc - ) d; - List.rev !acc - + let invariant t = + match t.d with + | None -> [] + | Some d -> + Array.fold_left (fun acc (i, (var_opt, const)) -> + let xi = Environment.var_of_dim t.env i in + let coeff_vars = + [(Coeff.s_of_int (-1), xi)] @ (match var_opt with + | None -> [] + | Some var_index -> + let var = Environment.var_of_dim t.env var_index in + [(Coeff.s_of_int 1, var)]) + in + let cst = Coeff.s_of_int (Z.to_int const) in + let lincons = Lincons1.make (Linexpr1.make t.env) Lincons1.EQ in + Lincons1.set_list lincons coeff_vars (Some cst); + lincons :: acc + ) [] (Array.mapi (fun i e -> (i, e)) d) + |> List.rev + let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 From 1bfdb88e428c85e8c604a9b5c8f46e1a3de8a572 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Thu, 11 Jan 2024 14:05:16 +0100 Subject: [PATCH 117/280] Test for simple pointers --- tests/regression/77-lin2vareq/32-pointers2.c | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/regression/77-lin2vareq/32-pointers2.c diff --git a/tests/regression/77-lin2vareq/32-pointers2.c b/tests/regression/77-lin2vareq/32-pointers2.c new file mode 100644 index 0000000000..f78c75a8e6 --- /dev/null +++ b/tests/regression/77-lin2vareq/32-pointers2.c @@ -0,0 +1,24 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq +// from https://dl.acm.org/doi/10.1145/2049706.2049710 + +#include + +int x; +int y; +int *x_ptr; +int *y_ptr; + +void access() { + *x_ptr = 42; + *y_ptr = *x_ptr + 10; +} + +int main() { + int i; + x_ptr = &x; + y_ptr = &y; + access(); + __goblint_check(*x_ptr == *y_ptr - 10 ); + __goblint_check(i + *x_ptr + 10 == i + *y_ptr ); //UNKNOWN! + +} From f091430391ec6d6ce52e450d49292298c014ee27 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Thu, 11 Jan 2024 14:13:07 +0100 Subject: [PATCH 118/280] fix invariant function --- .../apron/linearTwoVarEqualityDomain.apron.ml | 35 +++++-------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index e6e7c33473..e802364d86 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -713,45 +713,28 @@ struct Lincons -> linear constraint *) (*TODO*) - (*let invariant t = + + let invariant t = match t.d with | None -> [] | Some d -> - let acc = ref [] in - Array.iteri (fun i (var_opt, const) -> - let coeff_vars = match var_opt with - | None -> [] - | Some var_index -> - let var = Environment.var_of_dim t.env var_index in - [(Coeff.s_of_int (-1), var)] - in - let cst = Coeff.s_of_int (Z.to_int const) in - let lincons = Lincons1.make (Linexpr1.make t.env) Lincons1.EQ in - Lincons1.set_list lincons coeff_vars (Some cst); - acc := lincons :: !acc - ) d; - List.rev !acc *) - - let invariant t = - match t.d with - | None -> [] - | Some d -> - Array.fold_left (fun acc (i, (var_opt, const)) -> + Array.fold_lefti (fun acc i (var_opt, const) -> + if Some i = var_opt then acc + else let xi = Environment.var_of_dim t.env i in let coeff_vars = - [(Coeff.s_of_int (-1), xi)] @ (match var_opt with + (Coeff.s_of_int (-1), xi) :: (match var_opt with | None -> [] | Some var_index -> let var = Environment.var_of_dim t.env var_index in - [(Coeff.s_of_int 1, var)]) + if i = var_index then [] + else [(Coeff.s_of_int 1, var)]) in let cst = Coeff.s_of_int (Z.to_int const) in let lincons = Lincons1.make (Linexpr1.make t.env) Lincons1.EQ in Lincons1.set_list lincons coeff_vars (Some cst); lincons :: acc - ) [] (Array.mapi (fun i e -> (i, e)) d) - |> List.rev - + ) [] d let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 From fc61cbcfdbba3bfc4818b855870172f543d87279 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 11 Jan 2024 14:29:30 +0100 Subject: [PATCH 119/280] Test case for sum of variables with the same reference variable --- .../regression/77-lin2vareq/33-sum-of-two-vars.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/regression/77-lin2vareq/33-sum-of-two-vars.c diff --git a/tests/regression/77-lin2vareq/33-sum-of-two-vars.c b/tests/regression/77-lin2vareq/33-sum-of-two-vars.c new file mode 100644 index 0000000000..280660b102 --- /dev/null +++ b/tests/regression/77-lin2vareq/33-sum-of-two-vars.c @@ -0,0 +1,16 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none + +#include + +int main() { + int x, y, z; + + z = y; + x = y; + + __goblint_check(z == 2 * x - y); // SUCCESS + y = 3; + __goblint_check(z == 2 * x - y); // UNKNOWN + + return 0; +} From 3b9ad39ffd53e34e6287b72532bf881c579a7986 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Thu, 11 Jan 2024 19:59:54 +0100 Subject: [PATCH 120/280] fix invariant function --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index e802364d86..0a7bb6121b 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -724,11 +724,10 @@ struct let xi = Environment.var_of_dim t.env i in let coeff_vars = (Coeff.s_of_int (-1), xi) :: (match var_opt with - | None -> [] - | Some var_index -> - let var = Environment.var_of_dim t.env var_index in - if i = var_index then [] - else [(Coeff.s_of_int 1, var)]) + | Some var_index when i <> var_index -> + let var = Environment.var_of_dim t.env var_index in + [(Coeff.s_of_int 1, var)] + | _ -> [] ) in let cst = Coeff.s_of_int (Z.to_int const) in let lincons = Lincons1.make (Linexpr1.make t.env) Lincons1.EQ in From 97f25bea042f1e122b711bc7a23f8a265bb43fc8 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Fri, 12 Jan 2024 10:57:47 +0100 Subject: [PATCH 121/280] add unsound test case so that we can debug --- .../77-lin2vareq/34-svcomp-signextension.c | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/regression/77-lin2vareq/34-svcomp-signextension.c diff --git a/tests/regression/77-lin2vareq/34-svcomp-signextension.c b/tests/regression/77-lin2vareq/34-svcomp-signextension.c new file mode 100644 index 0000000000..4ebd3bdd02 --- /dev/null +++ b/tests/regression/77-lin2vareq/34-svcomp-signextension.c @@ -0,0 +1,26 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none + +#include +int main() { + + unsigned short int allbits = -1; + short int signedallbits = allbits; + int unsignedtosigned = allbits; + unsigned int unsignedtounsigned = allbits; + int signedtosigned = signedallbits; + unsigned int signedtounsigned = signedallbits; + + /* + printf ("unsignedtosigned: %d\n", unsignedtosigned); + printf ("unsignedtounsigned: %u\n", unsignedtounsigned); + printf ("signedtosigned: %d\n", signedtosigned); + printf ("signedtounsigned: %u\n", signedtounsigned); + */ + + if (unsignedtosigned == 65535 && unsignedtounsigned == 65535 && + signedtosigned == -1 && signedtounsigned == 4294967295) { + return (-1); + } + + return (0); +} From 876778520433d8745bbc4e343bda7c414a06077d Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Fri, 12 Jan 2024 11:05:46 +0100 Subject: [PATCH 122/280] add same test to affeq because it also fails there --- .../63-affeq/20-svcomp-signextension.c | 25 +++++++++++++++++++ .../77-lin2vareq/34-svcomp-signextension.c | 3 +-- 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 tests/regression/63-affeq/20-svcomp-signextension.c diff --git a/tests/regression/63-affeq/20-svcomp-signextension.c b/tests/regression/63-affeq/20-svcomp-signextension.c new file mode 100644 index 0000000000..c5ca3e360a --- /dev/null +++ b/tests/regression/63-affeq/20-svcomp-signextension.c @@ -0,0 +1,25 @@ +// SKIP PARAM: --set ana.activated[+] affeq --set sem.int.signed_overflow assume_none + +#include +int main() { + + unsigned short int allbits = -1; + short int signedallbits = allbits; + int unsignedtosigned = allbits; + unsigned int unsignedtounsigned = allbits; + int signedtosigned = signedallbits; + unsigned int signedtounsigned = signedallbits; + + /* + printf ("unsignedtosigned: %d\n", unsignedtosigned); + printf ("unsignedtounsigned: %u\n", unsignedtounsigned); + printf ("signedtosigned: %d\n", signedtosigned); + printf ("signedtounsigned: %u\n", signedtounsigned); + */ + + if (signedtounsigned == 4294967295) { + return (-1); + } + + return (0); +} diff --git a/tests/regression/77-lin2vareq/34-svcomp-signextension.c b/tests/regression/77-lin2vareq/34-svcomp-signextension.c index 4ebd3bdd02..bebb8e4bf6 100644 --- a/tests/regression/77-lin2vareq/34-svcomp-signextension.c +++ b/tests/regression/77-lin2vareq/34-svcomp-signextension.c @@ -17,8 +17,7 @@ int main() { printf ("signedtounsigned: %u\n", signedtounsigned); */ - if (unsignedtosigned == 65535 && unsignedtounsigned == 65535 && - signedtosigned == -1 && signedtounsigned == 4294967295) { + if (signedtounsigned == 4294967295) { return (-1); } From c2f41369870d6cffbc0b785e7748b68046ff9391 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 15 Jan 2024 09:56:47 +0100 Subject: [PATCH 123/280] remove unused function --- .../apron/linearTwoVarEqualityDomain.apron.ml | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 0a7bb6121b..9180f2156d 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -42,17 +42,6 @@ module EqualitiesArray = struct let make_empty_array len = Array.init len (fun i -> (Some i, Z.zero)) - let add_empty_column arr index = - let num_vars = length arr in - if index > num_vars then failwith "n too large" else - let new_array = make (num_vars + 1) (Equality.var_zero index) in - if index = 0 - then blit arr 0 new_array 1 (num_vars - 1) - else blit arr 0 new_array 0 index; - if index <> num_vars - then blit arr index new_array (index + 1) (num_vars - index); - new_array - let add_empty_columns m indexes = (** same as add_empty_columns for Matrix (see vectorMatrix.ml)*) let nnc = length indexes in if nnc = 0 then m else @@ -722,7 +711,7 @@ struct if Some i = var_opt then acc else let xi = Environment.var_of_dim t.env i in - let coeff_vars = + let coeff_vars = (Coeff.s_of_int (-1), xi) :: (match var_opt with | Some var_index when i <> var_index -> let var = Environment.var_of_dim t.env var_index in @@ -733,7 +722,7 @@ struct let lincons = Lincons1.make (Linexpr1.make t.env) Lincons1.EQ in Lincons1.set_list lincons coeff_vars (Some cst); lincons :: acc - ) [] d + ) [] d let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 From 9d441f935e473be8017f70e6c4f97e287566800d Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 15 Jan 2024 10:57:04 +0100 Subject: [PATCH 124/280] optimized reduce_col_with as suggested by Michael Petter --- .../apron/linearTwoVarEqualityDomain.apron.ml | 73 +++++++------------ tests/regression/77-lin2vareq/35-forget_var.c | 17 +++++ 2 files changed, 45 insertions(+), 45 deletions(-) create mode 100644 tests/regression/77-lin2vareq/35-forget_var.c diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 9180f2156d..4d0b52a040 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -76,42 +76,25 @@ module EqualitiesArray = struct let is_top_array = GobArray.for_alli (fun i (a, e) -> GobOption.exists ((=) i) a && Z.(e = zero)) - let find_reference_variable d var_index = fst d.(var_index) - - - let find_vars_in_the_connected_component d ref_var = - filter (fun i -> let (var, _) = d.(i) in var = ref_var) (mapi const d) - - (* find a variable in the connected component with the least index, but not the reference variable. *) - let find_var_in_the_connected_component_with_least_index connected_component ref_var = - fold_left (fun curr_min i -> match curr_min with - | None -> if i <> ref_var then Some i else None - | Some curr_min -> if i < curr_min && i <> ref_var then Some i else Some curr_min) None connected_component - (* Forget information about variable var in-place. The name reduce_col_with is because the affineEqualitiesDomain also defines this function, and it represents the equalities with a matrix, not like in this case with an array. We could think about changing this name, then we would need to change it also in shared_Functions.apron.ml and vectorMatrix.ml and affineEqualitiesDomain.ml *) let reduce_col_with d var = - let ref_var_opt = find_reference_variable d var in - d.(var) <- Equality.var_zero var; - begin match ref_var_opt with - | None -> (* the variable is equal to a constant *) () - | Some ref_var -> - if ref_var <> var then () - else - (* x_i is the reference variable of its connected component *) - let dim_of_var = Some var in - let connected_component = find_vars_in_the_connected_component d dim_of_var in - if length connected_component = 1 - then () (* x_i is the only element of its connected component *) - else - (* x_i is the reference variable -> we need to find a new reference variable *) - let var_least_index = Option.get @@ find_var_in_the_connected_component_with_least_index connected_component ref_var in - let (_, off) = d.(var_least_index) in - iteri (fun _ x -> let (_, off2) = d.(x) in if x <> ref_var then d.(x) <- (Some var_least_index, Z.(off2 - off))) connected_component; - end + (let ref_var_opt = fst d.(var) in + match ref_var_opt with + | Some ref_var when ref_var = var -> + (* var is the reference variable of its connected component *) + (let cluster = List.tl @@ fold_righti + (fun i (ref, offset) l -> if ref = ref_var_opt then i::l else l) d [] in + (* obtain cluster with common reference variable ref_var*) + match cluster with (* new ref_var is taken from head of the cluster *) + | head :: tail -> let headconst = snd d.(head) in (* take offset between old and new reference variable *) + List.iter (fun i -> d.(i) <- Z.(Some head, snd d.(i) - headconst)) cluster (* shift offset to match new reference variable *) + | _ -> ()) (* empty cluster means no work for us *) + | _ -> ()) (* variable is either a constant or expressed by another refvar *) + ; d.(var) <- Equality.var_zero var (* set d(var) to unknown, finally *) (* Forget information about variable i but not in-place *) let reduce_col m j = @@ -707,21 +690,21 @@ struct match t.d with | None -> [] | Some d -> - Array.fold_lefti (fun acc i (var_opt, const) -> - if Some i = var_opt then acc - else - let xi = Environment.var_of_dim t.env i in - let coeff_vars = - (Coeff.s_of_int (-1), xi) :: (match var_opt with - | Some var_index when i <> var_index -> - let var = Environment.var_of_dim t.env var_index in - [(Coeff.s_of_int 1, var)] - | _ -> [] ) - in - let cst = Coeff.s_of_int (Z.to_int const) in - let lincons = Lincons1.make (Linexpr1.make t.env) Lincons1.EQ in - Lincons1.set_list lincons coeff_vars (Some cst); - lincons :: acc + Array.fold_lefti (fun acc i (var_opt, const) -> + if Some i = var_opt then acc + else + let xi = Environment.var_of_dim t.env i in + let coeff_vars = + (Coeff.s_of_int (-1), xi) :: (match var_opt with + | Some var_index when i <> var_index -> + let var = Environment.var_of_dim t.env var_index in + [(Coeff.s_of_int 1, var)] + | _ -> [] ) + in + let cst = Coeff.s_of_int (Z.to_int const) in + let lincons = Lincons1.make (Linexpr1.make t.env) Lincons1.EQ in + Lincons1.set_list lincons coeff_vars (Some cst); + lincons :: acc ) [] d let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 diff --git a/tests/regression/77-lin2vareq/35-forget_var.c b/tests/regression/77-lin2vareq/35-forget_var.c new file mode 100644 index 0000000000..a115c52aa5 --- /dev/null +++ b/tests/regression/77-lin2vareq/35-forget_var.c @@ -0,0 +1,17 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none + +#include + +int main() { + int x, y, z; + + z = x; + + __goblint_check(z == x); // SUCCESS + + x = y * y; + + __goblint_check(x == z); // UNKNOWN + + return 0; +} From 8b92e55c18a72b100da892c9a0154764f5f40e8e Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 15 Jan 2024 11:21:36 +0100 Subject: [PATCH 125/280] fix test order --- tests/regression/63-affeq/12-const_guards.c | 2 +- tests/regression/77-lin2vareq/34-svcomp-signextension.c | 3 ++- .../19-join_all_cases.c => 77-lin2vareq/36-join_all_cases.c} | 0 3 files changed, 3 insertions(+), 2 deletions(-) rename tests/regression/{63-affeq/19-join_all_cases.c => 77-lin2vareq/36-join_all_cases.c} (100%) diff --git a/tests/regression/63-affeq/12-const_guards.c b/tests/regression/63-affeq/12-const_guards.c index a5e75d9f8d..82d08ed9c5 100644 --- a/tests/regression/63-affeq/12-const_guards.c +++ b/tests/regression/63-affeq/12-const_guards.c @@ -16,4 +16,4 @@ int main() { k = 1; } __goblint_check(k == 1); -} +} \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/34-svcomp-signextension.c b/tests/regression/77-lin2vareq/34-svcomp-signextension.c index bebb8e4bf6..1665ce8dd2 100644 --- a/tests/regression/77-lin2vareq/34-svcomp-signextension.c +++ b/tests/regression/77-lin2vareq/34-svcomp-signextension.c @@ -18,8 +18,9 @@ int main() { */ if (signedtounsigned == 4294967295) { + // __goblint_check(1); // reachable return (-1); } - +// __goblint_check(0); // NOWARN (unreachable) return (0); } diff --git a/tests/regression/63-affeq/19-join_all_cases.c b/tests/regression/77-lin2vareq/36-join_all_cases.c similarity index 100% rename from tests/regression/63-affeq/19-join_all_cases.c rename to tests/regression/77-lin2vareq/36-join_all_cases.c From 72f811d234d280579f49af45e89198aa5155642a Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 17 Jan 2024 13:42:43 +0100 Subject: [PATCH 126/280] add failing tests with casts --- .../77-lin2vareq/34-svcomp-signextension.c | 4 +-- .../77-lin2vareq/36-join_all_cases.c | 2 +- .../77-lin2vareq/37-cast-to-short.c | 27 +++++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 tests/regression/77-lin2vareq/37-cast-to-short.c diff --git a/tests/regression/77-lin2vareq/34-svcomp-signextension.c b/tests/regression/77-lin2vareq/34-svcomp-signextension.c index 1665ce8dd2..1d63e1bfb0 100644 --- a/tests/regression/77-lin2vareq/34-svcomp-signextension.c +++ b/tests/regression/77-lin2vareq/34-svcomp-signextension.c @@ -18,9 +18,9 @@ int main() { */ if (signedtounsigned == 4294967295) { - // __goblint_check(1); // reachable + __goblint_check(1); // reachable return (-1); } -// __goblint_check(0); // NOWARN (unreachable) +__goblint_check(0); // NOWARN (unreachable) return (0); } diff --git a/tests/regression/77-lin2vareq/36-join_all_cases.c b/tests/regression/77-lin2vareq/36-join_all_cases.c index 9f9ff5a6e9..841e7ca882 100644 --- a/tests/regression/77-lin2vareq/36-join_all_cases.c +++ b/tests/regression/77-lin2vareq/36-join_all_cases.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] affeq --set ana.relation.privatization top --set sem.int.signed_overflow assume_none +// SKIP PARAM: --set ana.activated[+] lin2vareq --set ana.relation.privatization top --set sem.int.signed_overflow assume_none void main(void) { int x1, x2, x3, x4, x5, x6, x7, x8, x9; int t; diff --git a/tests/regression/77-lin2vareq/37-cast-to-short.c b/tests/regression/77-lin2vareq/37-cast-to-short.c new file mode 100644 index 0000000000..fe4e4933bf --- /dev/null +++ b/tests/regression/77-lin2vareq/37-cast-to-short.c @@ -0,0 +1,27 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none + +#include +int main() { + + unsigned int allbits = -1; + int signedallbits = allbits; + short unsignedtosigned = allbits; + unsigned short unsignedtounsigned = allbits; + +// printf("allbits: %u\n", allbits); +// printf("signedallbits: %d\n", signedallbits); +// printf("unsignedtosigned: %hd\n", unsignedtosigned); +// printf("unsignedtounsigned: %hu\n", unsignedtounsigned); + + if (unsignedtounsigned == 4294967295) { + __goblint_check(0); // NOWARN (unreachable) + return (-1); + } + if (allbits == 4294967295 && signedallbits == -1 && unsignedtosigned == -1 && + unsignedtounsigned == 65535) { + __goblint_check(1); // reachable + return (-1); + } + __goblint_check(0); // NOWARN (unreachable) + return (0); +} From 9878cb5456412461717262f6026ce18610dd97e2 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Wed, 17 Jan 2024 13:50:49 +0100 Subject: [PATCH 127/280] delete duplicated tests --- .../77-lin2vareq/03-known_expressions.c | 26 ------------------- ...7-loop_increment.c => 03-loop_increment.c} | 0 ...erflow_ignored.c => 25-overflow_ignored.c} | 0 .../77-lin2vareq/25-simple_square.c | 13 ---------- .../{32-pointers2.c => 26-pointers2.c} | 0 ...unds_guards_ov.c => 27-bounds_guards_ov.c} | 0 ...in_after_guard.c => 28-join_after_guard.c} | 0 .../{30-meet-tcons.c => 29-meet-tcons.c} | 0 ...{31-function-call.c => 30-function-call.c} | 0 ...6-join_all_cases.c => 31-join_all_cases.c} | 0 ...sum-of-two-vars.c => 32-sum-of-two-vars.c} | 0 ...nextension.c => 33-svcomp-signextension.c} | 0 .../{35-forget_var.c => 34-forget_var.c} | 0 ...{37-cast-to-short.c => 35-cast-to-short.c} | 0 14 files changed, 39 deletions(-) delete mode 100644 tests/regression/77-lin2vareq/03-known_expressions.c rename tests/regression/77-lin2vareq/{27-loop_increment.c => 03-loop_increment.c} (100%) rename tests/regression/77-lin2vareq/{26-overflow_ignored.c => 25-overflow_ignored.c} (100%) delete mode 100644 tests/regression/77-lin2vareq/25-simple_square.c rename tests/regression/77-lin2vareq/{32-pointers2.c => 26-pointers2.c} (100%) rename tests/regression/77-lin2vareq/{28-bounds_guards_ov.c => 27-bounds_guards_ov.c} (100%) rename tests/regression/77-lin2vareq/{29-join_after_guard.c => 28-join_after_guard.c} (100%) rename tests/regression/77-lin2vareq/{30-meet-tcons.c => 29-meet-tcons.c} (100%) rename tests/regression/77-lin2vareq/{31-function-call.c => 30-function-call.c} (100%) rename tests/regression/77-lin2vareq/{36-join_all_cases.c => 31-join_all_cases.c} (100%) rename tests/regression/77-lin2vareq/{33-sum-of-two-vars.c => 32-sum-of-two-vars.c} (100%) rename tests/regression/77-lin2vareq/{34-svcomp-signextension.c => 33-svcomp-signextension.c} (100%) rename tests/regression/77-lin2vareq/{35-forget_var.c => 34-forget_var.c} (100%) rename tests/regression/77-lin2vareq/{37-cast-to-short.c => 35-cast-to-short.c} (100%) diff --git a/tests/regression/77-lin2vareq/03-known_expressions.c b/tests/regression/77-lin2vareq/03-known_expressions.c deleted file mode 100644 index 692bc34a2e..0000000000 --- a/tests/regression/77-lin2vareq/03-known_expressions.c +++ /dev/null @@ -1,26 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -// from https://dl.acm.org/doi/10.1145/2049706.2049710 - -#include - -int main() { - int x1 = 5, x2 = 10, x3 = 15, x4, x5, x6, x7, x8, x9, x10, x11, x12; - - x4 = 3 * x2 + 5; - x5 = 3 * x3 + 15; - x6 = x3 + 3; - x7 = x3 + 2; - x8 = 7 * x3 + 15; - x9 = 0; - x10 = 2 * x9 + 2; - x11 = 2 * x1 - 3; - x12 = 4 * x1 - 5; - - __goblint_check(x4 == 3 * x2 + 5); //SUCCESS - __goblint_check(x5 == 3 * x3 + 15); //SUCCESS - __goblint_check(x7 == x6 - 1); //SUCCESS - __goblint_check(x10 == 2 * x9 + 2); //SUCCESS - __goblint_check(x12 == 2 * x11 + 1); //SUCCESS - - return 0; -} diff --git a/tests/regression/77-lin2vareq/27-loop_increment.c b/tests/regression/77-lin2vareq/03-loop_increment.c similarity index 100% rename from tests/regression/77-lin2vareq/27-loop_increment.c rename to tests/regression/77-lin2vareq/03-loop_increment.c diff --git a/tests/regression/77-lin2vareq/26-overflow_ignored.c b/tests/regression/77-lin2vareq/25-overflow_ignored.c similarity index 100% rename from tests/regression/77-lin2vareq/26-overflow_ignored.c rename to tests/regression/77-lin2vareq/25-overflow_ignored.c diff --git a/tests/regression/77-lin2vareq/25-simple_square.c b/tests/regression/77-lin2vareq/25-simple_square.c deleted file mode 100644 index 213a172585..0000000000 --- a/tests/regression/77-lin2vareq/25-simple_square.c +++ /dev/null @@ -1,13 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -#include - -int main() { - int x; - int y = 5; - - x = y * y; - - __goblint_check(x == y * y); //SUCCESS - - return 0; -} diff --git a/tests/regression/77-lin2vareq/32-pointers2.c b/tests/regression/77-lin2vareq/26-pointers2.c similarity index 100% rename from tests/regression/77-lin2vareq/32-pointers2.c rename to tests/regression/77-lin2vareq/26-pointers2.c diff --git a/tests/regression/77-lin2vareq/28-bounds_guards_ov.c b/tests/regression/77-lin2vareq/27-bounds_guards_ov.c similarity index 100% rename from tests/regression/77-lin2vareq/28-bounds_guards_ov.c rename to tests/regression/77-lin2vareq/27-bounds_guards_ov.c diff --git a/tests/regression/77-lin2vareq/29-join_after_guard.c b/tests/regression/77-lin2vareq/28-join_after_guard.c similarity index 100% rename from tests/regression/77-lin2vareq/29-join_after_guard.c rename to tests/regression/77-lin2vareq/28-join_after_guard.c diff --git a/tests/regression/77-lin2vareq/30-meet-tcons.c b/tests/regression/77-lin2vareq/29-meet-tcons.c similarity index 100% rename from tests/regression/77-lin2vareq/30-meet-tcons.c rename to tests/regression/77-lin2vareq/29-meet-tcons.c diff --git a/tests/regression/77-lin2vareq/31-function-call.c b/tests/regression/77-lin2vareq/30-function-call.c similarity index 100% rename from tests/regression/77-lin2vareq/31-function-call.c rename to tests/regression/77-lin2vareq/30-function-call.c diff --git a/tests/regression/77-lin2vareq/36-join_all_cases.c b/tests/regression/77-lin2vareq/31-join_all_cases.c similarity index 100% rename from tests/regression/77-lin2vareq/36-join_all_cases.c rename to tests/regression/77-lin2vareq/31-join_all_cases.c diff --git a/tests/regression/77-lin2vareq/33-sum-of-two-vars.c b/tests/regression/77-lin2vareq/32-sum-of-two-vars.c similarity index 100% rename from tests/regression/77-lin2vareq/33-sum-of-two-vars.c rename to tests/regression/77-lin2vareq/32-sum-of-two-vars.c diff --git a/tests/regression/77-lin2vareq/34-svcomp-signextension.c b/tests/regression/77-lin2vareq/33-svcomp-signextension.c similarity index 100% rename from tests/regression/77-lin2vareq/34-svcomp-signextension.c rename to tests/regression/77-lin2vareq/33-svcomp-signextension.c diff --git a/tests/regression/77-lin2vareq/35-forget_var.c b/tests/regression/77-lin2vareq/34-forget_var.c similarity index 100% rename from tests/regression/77-lin2vareq/35-forget_var.c rename to tests/regression/77-lin2vareq/34-forget_var.c diff --git a/tests/regression/77-lin2vareq/37-cast-to-short.c b/tests/regression/77-lin2vareq/35-cast-to-short.c similarity index 100% rename from tests/regression/77-lin2vareq/37-cast-to-short.c rename to tests/regression/77-lin2vareq/35-cast-to-short.c From 35a2fcfc80af4335307a8cd953c04a278fee3f42 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 17 Jan 2024 14:23:57 +0100 Subject: [PATCH 128/280] optimized assignment such that it also understands sums of multiple different variables that have the same reference variable --- .../apron/linearTwoVarEqualityDomain.apron.ml | 7 +++++-- tests/regression/77-lin2vareq/32-sum-of-two-vars.c | 13 ++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 4d0b52a040..f1fffc51ef 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -161,9 +161,12 @@ struct | Mpfrf x -> raise NotIntegerOffset end in of_union x | Var x -> let var_dim = Environment.dim_of_var t.env x in - begin match get_variable_value_if_it_is_a_constant t var_dim with + begin match t.d with | None -> [(Z.one, Some var_dim)] - | Some constant -> [(constant, None)] + | Some d -> + (if Option.is_some (fst d.(var_dim)) then [(Z.one, fst d.(var_dim))] + else []) + @ [(snd d.(var_dim), None)] end | Unop (u, e, _, _) -> begin match u with diff --git a/tests/regression/77-lin2vareq/32-sum-of-two-vars.c b/tests/regression/77-lin2vareq/32-sum-of-two-vars.c index 280660b102..af04046f7e 100644 --- a/tests/regression/77-lin2vareq/32-sum-of-two-vars.c +++ b/tests/regression/77-lin2vareq/32-sum-of-two-vars.c @@ -3,14 +3,21 @@ #include int main() { - int x, y, z; + int x, y, z, w, k; z = y; x = y; + w = y; __goblint_check(z == 2 * x - y); // SUCCESS - y = 3; - __goblint_check(z == 2 * x - y); // UNKNOWN + + k = z + w - x + 5; + + __goblint_check(k == y + 5); //SUCCESS + + y = 3; + __goblint_check(k == y + 5); // UNKNOWN! + return 0; } From 25f829604376794a7e72e381137b96ab843e8c9a Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 18 Jan 2024 13:20:02 +0100 Subject: [PATCH 129/280] refactor VarManagement s.t. the names of del_cols and add_empty_columns are changed to more meaningful names --- .../apron/affineEqualityDomain.apron.ml | 25 +++++++++++- .../apron/linearTwoVarEqualityDomain.apron.ml | 39 +++++++++++++------ src/cdomains/apron/sharedFunctions.apron.ml | 28 ++----------- 3 files changed, 53 insertions(+), 39 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 5b7ebe784e..ec87ad0158 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -17,15 +17,36 @@ module Mpqf = SharedFunctions.Mpqf module V = RelationDomain.V +module AffineEqualityMatrix (Vec: AbstractVector) (Mx: AbstractMatrix) = + struct + include Mx(Mpqf) (Vec) + let dim_add (ch: Apron.Dim.change) m = + Array.modifyi (+) ch.dim; + add_empty_columns m ch.dim + + let dim_add ch m = timing_wrap "dim add" (dim_add ch) m + + + let dim_remove (ch: Apron.Dim.change) m ~del = + if Array.length ch.dim = 0 || is_empty m then + m + else ( + Array.modifyi (fun i x -> x + i) ch.dim; + let m' = if not del then let m = copy m in Array.fold_left (fun y x -> reduce_col_with y x; y) m ch.dim else m in + remove_zero_rows @@ del_cols m' ch.dim) + + let dim_remove ch m ~del = VectorMatrix.timing_wrap "dim remove" (fun del -> dim_remove ch m ~del:del) del + end (** It defines the type t of the affine equality domain (a struct that contains an optional matrix and an apron environment) and provides the functions needed for handling variables (which are defined by RelationDomain.D2) such as add_vars remove_vars. Furthermore, it provides the function get_coeff_vec that parses an apron expression into a vector of coefficients if the apron expression has an affine form. *) module VarManagement (Vec: AbstractVector) (Mx: AbstractMatrix)= struct module Vector = Vec (Mpqf) - module Matrix = Mx(Mpqf) (Vec) + module Matrix = AffineEqualityMatrix (Vec) (Mx) - include SharedFunctions.VarManagementOps (Mx(Mpqf) (Vec)) + let dim_add = Matrix.dim_add + include SharedFunctions.VarManagementOps(AffineEqualityMatrix (Vec) (Mx)) include ConvenienceOps(Mpqf) (** Get the constant from the vector if it is a constant *) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index f1fffc51ef..7456981b4d 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -42,7 +42,7 @@ module EqualitiesArray = struct let make_empty_array len = Array.init len (fun i -> (Some i, Z.zero)) - let add_empty_columns m indexes = (** same as add_empty_columns for Matrix (see vectorMatrix.ml)*) + let add_variables_to_domain m indexes = (** same as add_empty_columns for Matrix (see vectorMatrix.ml)*) let nnc = length indexes in if nnc = 0 then m else let nc = length m in @@ -56,7 +56,7 @@ module EqualitiesArray = struct in Array.iteri (fun j eq -> m'.(j + offset_map.(j)) <- add_offset_to_array_entry eq) m; m' - let del_cols m indexes = + let remove_variables_from_domain m indexes = let nrc = length indexes in if nrc = 0 || length m = 0 then m else @@ -70,7 +70,7 @@ module EqualitiesArray = struct Array.(copy m |> filteri (fun i _ -> not @@ Array.mem i indexes) |> map remove_offset_from_array_entry) - let del_cols m cols = timing_wrap "del_cols" (del_cols m) cols + let remove_variables_from_domain m cols = timing_wrap "del_cols" (remove_variables_from_domain m) cols let is_empty m = length m = 0 @@ -81,7 +81,7 @@ module EqualitiesArray = struct and it represents the equalities with a matrix, not like in this case with an array. We could think about changing this name, then we would need to change it also in shared_Functions.apron.ml and vectorMatrix.ml and affineEqualitiesDomain.ml *) - let reduce_col_with d var = + let forget_variable_with d var = (let ref_var_opt = fst d.(var) in match ref_var_opt with | Some ref_var when ref_var = var -> @@ -97,12 +97,27 @@ module EqualitiesArray = struct ; d.(var) <- Equality.var_zero var (* set d(var) to unknown, finally *) (* Forget information about variable i but not in-place *) - let reduce_col m j = + let forget_variable m j = let copy = copy m in - reduce_col_with copy j; + forget_variable_with copy j; copy - let remove_zero_rows t = t + let dim_add (ch: Apron.Dim.change) m = + Array.modifyi (fun i x -> x + i) ch.dim; (* could be written Array.modifyi (+) ch.dim; but that's too smart *) + add_variables_to_domain m ch.dim + + let dim_add ch m = timing_wrap "dim add" (dim_add ch) m + + let dim_remove (ch: Apron.Dim.change) m ~del = + if Array.length ch.dim = 0 || is_empty m then + m + else ( + Array.modifyi (fun i x -> x + i) ch.dim; + let m' = Array.fold_left (fun y x -> forget_variable_with y x; y) (copy m) ch.dim in + remove_variables_from_domain m' ch.dim) + + let dim_remove ch m ~del = VectorMatrix.timing_wrap "dim remove" (fun del -> dim_remove ch m ~del:del) del + end @@ -116,7 +131,7 @@ struct module EArray = EqualitiesArray include SharedFunctions.VarManagementOps (EArray) - + let dim_add = EArray.dim_add let size t = BatOption.map_default (fun d -> EArray.length d) 0 t.d (* Returns the constant value of a variable, @@ -214,7 +229,7 @@ struct let get_coeff t texp = timing_wrap "coeff_vec" (get_coeff t) texp let abstract_exists var t = match t.d with - | Some d -> {t with d = Some (EArray.reduce_col d (Environment.dim_of_var t.env var))} + | Some d -> {t with d = Some (EArray.forget_variable d (Environment.dim_of_var t.env var))} | None -> t (* there are no variables in the current environment *) let assign_const t var const = match t.d with @@ -365,7 +380,7 @@ struct if is_bot_env t1 || is_top t2 then true else if is_bot_env t2 || is_top t1 then false else let m1, m2 = Option.get t1.d, Option.get t2.d in - let m1' = if env_comp = 0 then m1 else dim_add (Environment.dimchange t1.env t2.env) m1 in + let m1' = if env_comp = 0 then m1 else VarManagement.dim_add (Environment.dimchange t1.env t2.env) m1 in GobArray.for_alli (fun i t -> implies m1' t i) m2 let leq a b = timing_wrap "leq" (leq a) b @@ -430,7 +445,7 @@ struct let remove_rels_with_var x var env imp = let j0 = Environment.dim_of_var env var in - if imp then (EArray.reduce_col_with x j0; x) else EArray.reduce_col x j0 + if imp then (EArray.forget_variable_with x j0; x) else EArray.forget_variable x j0 let narrow a b = a let pretty_diff () (x, y) = @@ -445,7 +460,7 @@ struct begin match vars' with | [] -> m | x :: xs -> rem_vars (remove_rels_with_var m x t.env true) xs end - in {d = Some (EArray.remove_zero_rows @@ rem_vars (EArray.copy m) vars); env = t.env} + in {d = Some (rem_vars (EArray.copy m) vars); env = t.env} let forget_vars t vars = let res = forget_vars t vars in diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 6acd38d8d6..281f950f7c 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -266,14 +266,9 @@ sig val hash : t -> int val empty : unit -> t val copy : t -> t - val add_empty_columns : t -> int array -> t val is_empty : t -> bool - val reduce_col_with : t -> int -> unit - - val remove_zero_rows : t -> t - - val del_cols : t -> int array -> t - + val dim_add : Apron.Dim.change -> t -> t + val dim_remove : Apron.Dim.change -> t -> del:bool-> t end (* Shared operations for the management of the Matrix or Array representations of the domains, @@ -298,23 +293,6 @@ struct let copy t = {t with d = Option.map RelDomain.copy t.d} - let dim_add (ch: Apron.Dim.change) m = - Array.modifyi (fun i x -> x + i) ch.dim; (* could be written Array.modifyi (+) ch.dim; but that's too smart *) - RelDomain.add_empty_columns m ch.dim - - let dim_add ch m = VectorMatrix.timing_wrap "dim add" (dim_add ch) m - - let dim_remove (ch: Apron.Dim.change) m ~del = - if Array.length ch.dim = 0 || RelDomain.is_empty m then - m - else ( - Array.modifyi (fun i x -> x + i) ch.dim; - let m' = Array.fold_left (fun y x -> RelDomain.reduce_col_with y x; y) (RelDomain.copy m) ch.dim in - RelDomain.remove_zero_rows @@ RelDomain.del_cols m' ch.dim) - - let dim_remove ch m ~del = VectorMatrix.timing_wrap "dim remove" (fun del -> dim_remove ch m ~del:del) del - - let change_d t new_env ~add ~del = if Environment.equal t.env new_env then t @@ -328,7 +306,7 @@ struct else Environment.dimchange new_env t.env in - {d = Some (if add then dim_add dim_change m else dim_remove dim_change m ~del:del); env = new_env} + {d = Some (if add then RelDomain.dim_add dim_change m else RelDomain.dim_remove dim_change m ~del:del); env = new_env} let change_d t new_env ~add ~del = VectorMatrix.timing_wrap "dimension change" (fun del -> change_d t new_env ~add:add ~del:del) del From 2c792e69d0e35cd538871e45a27d768a4f7215c0 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 18 Jan 2024 14:31:15 +0100 Subject: [PATCH 130/280] modified add_cols and del_cols as suggested by Michael Petter --- .../apron/affineEqualityDomain.apron.ml | 2 +- .../apron/linearTwoVarEqualityDomain.apron.ml | 60 ++++++++++++------- 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index ec87ad0158..c18bdec673 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -31,7 +31,7 @@ module AffineEqualityMatrix (Vec: AbstractVector) (Mx: AbstractMatrix) = if Array.length ch.dim = 0 || is_empty m then m else ( - Array.modifyi (fun i x -> x + i) ch.dim; + Array.modifyi (+) ch.dim; let m' = if not del then let m = copy m in Array.fold_left (fun y x -> reduce_col_with y x; y) m ch.dim else m in remove_zero_rows @@ del_cols m' ch.dim) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 7456981b4d..8c376c7fb1 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -42,19 +42,32 @@ module EqualitiesArray = struct let make_empty_array len = Array.init len (fun i -> (Some i, Z.zero)) - let add_variables_to_domain m indexes = (** same as add_empty_columns for Matrix (see vectorMatrix.ml)*) - let nnc = length indexes in - if nnc = 0 then m else - let nc = length m in - let offset = ref 0 in - let offset_map = Array.init nc (fun j -> - while !offset < nnc && !offset + j = indexes.(!offset) do incr offset; done; - !offset) - in let add_offset_to_array_entry (var, offs) = - Option.map (fun var_index -> var_index + offset_map.(var_index)) var, offs in - let m' = make_empty_array (nc + nnc) + let add_variables_to_domain m indexes = (* add new variables to domain with particular indices; translates old indices to keep consistency *) + (* the semantic of indexes can be retrieved from apron: https://antoinemine.github.io/Apron/doc/api/ocaml/Dim.html *) + if length indexes = 0 then m else + let next_offset_bump_list = (* an ascending list of indices, where the offset is bumped by 1 *) + Array.to_list indexes @ [ Array.length m ] (* terminate list with m to avoid out of bounds access *) + in + let offset_map = Array.make (Array.length m) 0 (* maps each variable to the number of variables that are added before this variable *) + in + let rec shift (offset, list) index = (* bumps offset & pops list, if/while index is heading the list *) + if index = List.hd list then + shift (offset+1, List.tl list) index + else (offset, list) + in + let _ = + Array.fold_lefti (* iterates over all indices of offset_map, overwrites content with current offset wrt. potential shift *) + (fun (offset, offset_bump_list) index _ -> + let newoffset, newlist = shift (offset, offset_bump_list) index in + offset_map.(index) <- newoffset; + (newoffset, newlist)) + (0, next_offset_bump_list) offset_map + in + let add_offset_to_array_entry (var, offs) = (* uses offset_map to obtain a new var_index, that is consistent with the new reference indices *) + Option.map (fun var_index -> var_index + offset_map.(var_index)) var, offs in + let m' = make_empty_array (length m + length indexes) in Array.iteri (fun j eq -> m'.(j + offset_map.(j)) <- add_offset_to_array_entry eq) m; - m' + m'(* produces a consistent new conj. of equalities *) let remove_variables_from_domain m indexes = let nrc = length indexes in @@ -62,13 +75,17 @@ module EqualitiesArray = struct else let nc = length m in if nc = nrc then [||] else - let offset = ref 0 in - let offset_map = Array.init nc (fun j -> - if !offset < nrc && indexes.(!offset) = j then (incr offset; 0) else !offset) - in let remove_offset_from_array_entry (var, offs) = - Option.map (fun var_index -> var_index - offset_map.(var_index)) var, offs in - Array.(copy m |> filteri (fun i _ -> not @@ Array.mem i indexes) - |> map remove_offset_from_array_entry) + let offset_map = Array.make (Array.length m) 0 + (* maps each variable to the number of variables that are removed before this variable *) + in let _ = Array.fold_lefti + (fun offset index _ -> + if offset < nrc && indexes.(offset) = index then offset + 1 + else (offset_map.(index) <- offset; offset)) + 0 offset_map in + let remove_offset_from_array_entry (var, offs) = + Option.map (fun var_index -> var_index - offset_map.(var_index)) var, offs in + Array.(copy m |> filteri (fun i _ -> not @@ Array.mem i indexes) (* filter out removed variables*) + |> map remove_offset_from_array_entry) (* adjust variable indexes *) let remove_variables_from_domain m cols = timing_wrap "del_cols" (remove_variables_from_domain m) cols @@ -103,7 +120,6 @@ module EqualitiesArray = struct copy let dim_add (ch: Apron.Dim.change) m = - Array.modifyi (fun i x -> x + i) ch.dim; (* could be written Array.modifyi (+) ch.dim; but that's too smart *) add_variables_to_domain m ch.dim let dim_add ch m = timing_wrap "dim add" (dim_add ch) m @@ -112,7 +128,7 @@ module EqualitiesArray = struct if Array.length ch.dim = 0 || is_empty m then m else ( - Array.modifyi (fun i x -> x + i) ch.dim; + Array.modifyi (+) ch.dim; let m' = Array.fold_left (fun y x -> forget_variable_with y x; y) (copy m) ch.dim in remove_variables_from_domain m' ch.dim) @@ -256,8 +272,6 @@ struct EArray.iteri (subtract_const_from_var_for_single_equality) d; {d = Some d; env = t.env} end - -(** TODO: overflow checking *) module ExpressionBounds: (SharedFunctions.ExtendedConvBounds with type t = VarManagement.t) = struct include VarManagement From e080da4b5ab6df56594f2f12246388acb181dda0 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 18 Jan 2024 15:00:31 +0100 Subject: [PATCH 131/280] modified subtract_const_from_var as suggested --- .../apron/linearTwoVarEqualityDomain.apron.ml | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 8c376c7fb1..b6251b4b4d 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -255,21 +255,26 @@ struct let subtract_const_from_var t var const = match t.d with | None -> t - | Some t_d -> let d = EArray.copy t_d in + | Some t_d -> + let d = EArray.copy t_d in let subtract_const_from_var_for_single_equality index element = - let (eq_var_opt, off2) = d.(index) in - if index = var then - match eq_var_opt with - | None -> d.(index) <- (None, Z.(off2 + const)) - | Some eq_var -> begin if eq_var <> index then d.(index) <- (eq_var_opt, Z.(off2 + const)) end - else - begin match eq_var_opt with - | Some eq_var -> - if eq_var = var then d.(index) <- (eq_var_opt, Z.(off2 - const)) - | None -> () + if index <> var then + begin match d.(index) with + | (Some eq_var, off2) when eq_var = var -> + d.(index) <- (Some eq_var, Z.(off2 - const)) + | _ -> () end in - EArray.iteri (subtract_const_from_var_for_single_equality) d; {d = Some d; env = t.env} + begin if d.(var) = (Some var, Z.zero) + (* var is a reference variable -> it can appear on the right-hand side of an equality *) + then + EArray.iteri (subtract_const_from_var_for_single_equality) d + else + (* var never appears on the right hand side-> we only need to modify the array entry at index var *) + d.(var) <- Tuple2.map2 (Z.add const) d.(var) + end; + {d = Some d; env = t.env} + end module ExpressionBounds: (SharedFunctions.ExtendedConvBounds with type t = VarManagement.t) = From acf258ac5930e29cee323329767142f982e2e0e6 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Thu, 18 Jan 2024 15:22:44 +0100 Subject: [PATCH 132/280] show more readable now --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index b6251b4b4d..40eb5d64fb 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -329,8 +329,8 @@ struct let lookup i = Var.to_string (Environment.var_of_dim varM.env i) in let show_var i tuple = match tuple with - | (None, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ Z.to_string offset ^ "\n" - | (Some index, offset) -> "Variable " ^ string_of_int i ^ " named " ^ (lookup i) ^ " equals " ^ lookup index ^ " + " ^ Z.to_string offset ^ "\n" + | (None, offset) -> (lookup i) ^ " = " ^ Z.to_string offset ^ "\n" + | (Some index, offset) -> (lookup i) ^ " = " ^ lookup index ^ " + " ^ Z.to_string offset ^ "\n" in if is_top varM then "⊤\n" else match varM.d with | None -> "⊥\n" From 9949d98df050a78b3521062fe970fd9ac0a9efd6 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 18 Jan 2024 15:46:17 +0100 Subject: [PATCH 133/280] many small adjustments that were requested --- .../apron/linearTwoVarEqualityDomain.apron.ml | 104 ++++++++---------- 1 file changed, 46 insertions(+), 58 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 40eb5d64fb..e9e5d93ed3 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -14,27 +14,23 @@ module M = Messages open Apron open VectorMatrix - - module Mpqf = SharedFunctions.Mpqf module Equality = struct (* (Some i, k) represents a sum of a variable with index i and the number k. (None, k) represents the number k. *) - type t = (int option * Z.t) [@@deriving eq, ord, hash] + type t = (int option * (Z.t [@printer Z.pp_print])) [@@deriving eq, ord, hash, show] let zero = (None, Z.zero) let var_zero i = (Some i, Z.zero) let to_int x = Z.to_int @@ snd x - let print (a, b) = match a with - | None -> print_endline @@ "(None , " ^ Z.to_string b ^ ")" - | Some x -> print_endline @@ "(Some " ^ string_of_int x ^ ", " ^ Z.to_string b ^ ")" end module EqualitiesArray = struct include Array type t = Equality.t Array.t [@@deriving eq, ord] - let print = Array.iter (fun k -> Equality.print k) + let show m = + Array.fold_right (fun k result -> Equality.show k ^ "\n" ^ result) m "" let hash : t -> int = Array.fold_left (fun acc a -> 31 * acc + Equality.hash a) 0 @@ -70,28 +66,27 @@ module EqualitiesArray = struct m'(* produces a consistent new conj. of equalities *) let remove_variables_from_domain m indexes = - let nrc = length indexes in - if nrc = 0 || length m = 0 then m + let nr_removed_colums = length indexes in + if nr_removed_colums = 0 || length m = 0 then m else - let nc = length m in - if nc = nrc then [||] else - let offset_map = Array.make (Array.length m) 0 - (* maps each variable to the number of variables that are removed before this variable *) - in let _ = Array.fold_lefti - (fun offset index _ -> - if offset < nrc && indexes.(offset) = index then offset + 1 - else (offset_map.(index) <- offset; offset)) - 0 offset_map in - let remove_offset_from_array_entry (var, offs) = - Option.map (fun var_index -> var_index - offset_map.(var_index)) var, offs in - Array.(copy m |> filteri (fun i _ -> not @@ Array.mem i indexes) (* filter out removed variables*) - |> map remove_offset_from_array_entry) (* adjust variable indexes *) + if length m = nr_removed_colums then [||] else + let offset_map = Array.make (Array.length m) 0 + (* maps each variable to the number of variables that are removed before this variable *) + in let _ = Array.fold_lefti + (fun offset index _ -> + if offset < nr_removed_colums && indexes.(offset) = index then offset + 1 + else (offset_map.(index) <- offset; offset)) + 0 offset_map in + let remove_offset_from_array_entry (var, offs) = + Option.map (fun var_index -> var_index - offset_map.(var_index)) var, offs in + Array.(copy m |> filteri (fun i _ -> not @@ Array.mem i indexes) (* filter out removed variables*) + |> map remove_offset_from_array_entry) (* adjust variable indexes *) let remove_variables_from_domain m cols = timing_wrap "del_cols" (remove_variables_from_domain m) cols let is_empty m = length m = 0 - let is_top_array = GobArray.for_alli (fun i (a, e) -> GobOption.exists ((=) i) a && Z.(e = zero)) + let is_top_array = GobArray.for_alli (fun i (a, e) -> GobOption.exists ((=) i) a && Z.equal e Z.zero) (* Forget information about variable var in-place. The name reduce_col_with is because the affineEqualitiesDomain also defines this function, @@ -140,8 +135,8 @@ end module V = RelationDomain.V -(** It defines the type t of the affine equality domain (a struct that contains an optional matrix and an apron environment) and provides the functions needed for handling variables (which are defined by RelationDomain.D2) such as add_vars remove_vars. - Furthermore, it provides the function get_coeff_vec that parses an apron expression into a vector of coefficients if the apron expression has an affine form. *) +(** [VarManagement] defines the type t of the affine equality domain (a record that contains an optional matrix and an apron environment) and provides the functions needed for handling variables (which are defined by [RelationDomain.D2]) such as [add_vars], [remove_vars]. + Furthermore, it provides the function [get_coeff_vec] that parses an apron expression into a vector of coefficients if the apron expression has an affine form. *) module VarManagement = struct module EArray = EqualitiesArray @@ -150,19 +145,9 @@ struct let dim_add = EArray.dim_add let size t = BatOption.map_default (fun d -> EArray.length d) 0 t.d - (* Returns the constant value of a variable, - if we know the constant value of this variable. - Else it returns None. *) - let get_variable_value_if_it_is_a_constant t var = - let get_constant (var, off) = match var with - | None -> Some off - | _ -> None - in - Option.bind t.d (fun d -> get_constant d.(var)) - + (** Parses a Texpr to obtain a (coefficient, variable) pair list to repr. a sum of a variables that have a coefficient. If variable is None, the coefficient represents a constant offset. + **) let get_coeff_vec (t: t) texp = - (*Parses a Texpr to obtain a (coefficient, variable) pair list to repr. a sum of a variables that have a coefficient. If variable is None, the coefficient represents a constant offset. - *) let open Apron.Texpr1 in let exception NotLinearExpr in let exception NotIntegerOffset in @@ -189,7 +174,8 @@ struct | Scalar x -> begin match x with | Float x -> raise NotIntegerOffset | Mpqf x -> [(mpqf_to_Z x, None)] - | Mpfrf x -> raise NotIntegerOffset end in of_union x + | Mpfrf x -> raise NotIntegerOffset end in + of_union x | Var x -> let var_dim = Environment.dim_of_var t.env x in begin match t.d with @@ -238,7 +224,7 @@ struct | None -> None | Some (var, var_coeff, offset) -> if Option.is_none var then Some (None, offset) - else if Z.(var_coeff = one) then Some (var, offset) + else if Z.equal var_coeff Z.one then Some (var, offset) else None @@ -250,18 +236,19 @@ struct let assign_const t var const = match t.d with | None -> t - | Some t_d -> let d = EArray.copy t_d in d.(var) <- (None, const); {d = Some d; env = t.env} + | Some t_d -> + let d = EArray.copy t_d in d.(var) <- (None, const); {d = Some d; env = t.env} let subtract_const_from_var t var const = match t.d with | None -> t | Some t_d -> let d = EArray.copy t_d in - let subtract_const_from_var_for_single_equality index element = + let subtract_const_from_var_for_single_equality index (eq_var_opt, off2) = if index <> var then - begin match d.(index) with - | (Some eq_var, off2) when eq_var = var -> - d.(index) <- (Some eq_var, Z.(off2 - const)) + begin match eq_var_opt with + | Some eq_var when eq_var = var -> + d.(index) <- (eq_var_opt, Z.(off2 - const)) | _ -> () end in @@ -318,7 +305,7 @@ struct (*this shows "top" for a specific environment to enable the calculations. It is the top_of of all equalities*) let top_of env = {d = Some (EArray.make_empty_array (Environment.size env)); env = env} - (*Should never be called but implemented for completeness *) + (*Is not expected to be called but implemented for completeness *) let top () = {d = Some (EArray.empty()); env = empty_env} (*is_top returns true for top_of array and empty array *) @@ -415,7 +402,6 @@ struct let ad = Array.copy ad in (*This is the table which is later grouped*) let table = BatList.map2i (fun i (ai, aj) (bi,bj) -> (i, Z.(aj - bj), (ai, aj), (bi,bj))) (Array.to_list ad) (Array.to_list bd) in - (*let diff t1 t2 = Z.(snd t1 - snd t2) in*) (*compare two variables for grouping depending on delta function and reference index*) let cmp_z (_, t0i, t1i, t2i) (_, t0j, t1j, t2j) = let cmp_ref = Option.compare ~cmp:Int.compare in @@ -431,7 +417,7 @@ struct let iterate l = match l with | (idx_h, _, (_, b_h), _) :: t -> List.iter (modify idx_h b_h) l - | [] -> () (*This should not happen, consider throwing exception*) + | [] -> let exception EmptyComponent in raise EmptyComponent in List.iter iterate new_components; Some ad in @@ -455,6 +441,7 @@ struct let res = join a b in if M.tracing then M.tracel "join" "join a: %s b: %s -> %s \n" (show a) (show b) (show res) ; res + let widen a b = let a_env = a.env in let b_env = b.env in @@ -466,7 +453,8 @@ struct let j0 = Environment.dim_of_var env var in if imp then (EArray.forget_variable_with x j0; x) else EArray.forget_variable x j0 - let narrow a b = a + let narrow a b = meet a b + let pretty_diff () (x, y) = dprintf "%s: %a not leq %a" (name ()) pretty x pretty y @@ -477,7 +465,7 @@ struct if List.is_empty vars then t else let rec rem_vars m vars' = begin match vars' with - | [] -> m + | [] -> m | x :: xs -> rem_vars (remove_rels_with_var m x t.env true) xs end in {d = Some (rem_vars (EArray.copy m) vars); env = t.env} @@ -595,16 +583,16 @@ struct let substitute_exp t var exp ov = timing_wrap "substitution" (substitute_exp t var exp) ov - let print_coeff_vec l (env : Environment.t) = - let print_element e = match e with - | (a, Some x) -> print_string ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env x)) ^ " + ") - | (a, None) -> print_string ((Z.to_string a) ^ "+") in - List.iter print_element l; print_newline () + let show_coeff_vec l (env : Environment.t) = + let show_element e = match e with + | (a, Some x) -> ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env x)) ^ " + ") + | (a, None) -> ((Z.to_string a) ^ "+") in + List.fold_right (fun k result -> show_element k ^ "\n" ^ result) l "" - let print_final_expr l (env : Environment.t) = - let print_element i a = if i = 0 then print_string ((Z.to_string a) ^ " + ") else - print_string ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env (i-1))) ^ " + ") in - List.iteri print_element l; print_newline () + let show_final_expr l (env : Environment.t) = + let show_element i a = if i = 0 then ((Z.to_string a) ^ " + ") else + ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env (i-1))) ^ " + ") in + List.fold_righti (fun i k result -> show_element i k ^ "\n" ^ result) l "" (** Assert a constraint expression. From 5c7ba6051a519f1d9ea1fbbeb0e5434b47acbd32 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Fri, 19 Jan 2024 14:09:17 +0100 Subject: [PATCH 134/280] adjusted configuration for svcomp-tests to run properly --- src/config/options.schema.json | 2 +- svcomp2var.json | 146 +++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 svcomp2var.json diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 0c83e342e0..e0c70384e0 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -852,7 +852,7 @@ "description": "Which domain should be used for the Apron analysis. Can be 'octagon', 'interval' or 'polyhedra'", "type": "string", - "enum": ["octagon", "interval", "polyhedra", "affeq"], + "enum": ["octagon", "interval", "polyhedra", "affeq", "lin2vareq"], "default": "octagon" }, "threshold_widening": { diff --git a/svcomp2var.json b/svcomp2var.json new file mode 100644 index 0000000000..7df6a3579c --- /dev/null +++ b/svcomp2var.json @@ -0,0 +1,146 @@ +{ + "ana": { + "sv-comp": { + "enabled": true, + "functions": true + }, + "int": { + "def_exc": true, + "enums": false, + "interval": true + }, + "float": { + "interval": true + }, + "activated": [ + "base", + "threadid", + "threadflag", + "threadreturn", + "mallocWrapper", + "mutexEvents", + "mutex", + "access", + "race", + "escape", + "expRelation", + "mhp", + "assert", + "var_eq", + "symb_locks", + "region", + "thread", + "threadJoins" , + "lin2vareq" + ], + "path_sens": [ + "mutex", + "malloc_null", + "uninit", + "expsplit", + "activeSetjmp", + "memLeak", + "threadflag" + ], + "context": { + "widen": false + }, + "malloc": { + "wrappers": [ + "kmalloc", + "__kmalloc", + "usb_alloc_urb", + "__builtin_alloca", + "kzalloc", + + "ldv_malloc", + + "kzalloc_node", + "ldv_zalloc", + "kmalloc_array", + "kcalloc", + + "ldv_xmalloc", + "ldv_xzalloc", + "ldv_calloc", + "ldv_kzalloc" + ] + }, + "base": { + "arrays": { + "domain": "partitioned" + } + }, + "race": { + "free": false, + "call": false + }, + "autotune": { + "enabled": true, + "activated": [ + "singleThreaded", + "mallocWrappers", + "noRecursiveIntervals", + "enums", + "congruence", + "wideningThresholds", + "loopUnrollHeuristic", + "memsafetySpecification", + "termination", + "tmpSpecialAnalysis" + ] + } + }, + "exp": { + "region-offsets": true + }, + "solver": "td3", + "sem": { + "unknown_function": { + "spawn": false + }, + "int": { + "signed_overflow": "assume_none" + }, + "null-pointer": { + "dereference": "assume_none" + } + }, + "witness": { + "graphml": { + "enabled": true, + "id": "enumerate", + "unknown": false + }, + "yaml": { + "enabled": true, + "format-version": "2.0", + "entry-types": [ + "invariant_set" + ], + "invariant-types": [ + "loop_invariant" + ] + }, + "invariant": { + "loop-head": true, + "after-lock": false, + "other": false, + "accessed": false, + "exact": true, + "exclude-vars": [ + "tmp\\(___[0-9]+\\)?", + "cond", + "RETURN", + "__\\(cil_\\)?tmp_?[0-9]*\\(_[0-9]+\\)?", + ".*____CPAchecker_TMP_[0-9]+", + "__VERIFIER_assert__cond", + "__ksymtab_.*", + "\\(ldv_state_variable\\|ldv_timer_state\\|ldv_timer_list\\|ldv_irq_\\(line_\\|data_\\)?[0-9]+\\|ldv_retval\\)_[0-9]+" + ] + } + }, + "pre": { + "enabled": false + } +} From 605d03e5cb51769842d5e5de8bb3175ef6e1c984 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Fri, 19 Jan 2024 14:10:39 +0100 Subject: [PATCH 135/280] proper sorting of files --- svcomp2var.json => conf/svcomp2var.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename svcomp2var.json => conf/svcomp2var.json (100%) diff --git a/svcomp2var.json b/conf/svcomp2var.json similarity index 100% rename from svcomp2var.json rename to conf/svcomp2var.json From f59246faadc4e6671ed90b28046ac4627c27570f Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Fri, 19 Jan 2024 22:56:34 +0100 Subject: [PATCH 136/280] test cases adapted --- ...pression.c => 06-complicated_expression.c} | 14 +++++--- tests/regression/77-lin2vareq/06-pointer.c | 36 ------------------- .../77-lin2vareq/{11-loop.c => 07-loop.c} | 4 ++- .../regression/77-lin2vareq/08-distributive.c | 17 --------- .../{12-overflow.c => 08-overflow.c} | 2 +- .../regression/77-lin2vareq/09-associative.c | 18 ---------- ...-non-constant.c => 09-join-non-constant.c} | 7 ++-- .../{14-coeff_vec.c => 10-coeff_vec.c} | 0 .../regression/77-lin2vareq/10-commutative.c | 17 --------- .../{17-partitioning.c => 11-partitioning.c} | 5 +-- ...loop_relational.c => 12-loop_relational.c} | 9 ++--- .../{19-linear_loop.c => 13-linear_loop.c} | 7 ++-- .../77-lin2vareq/14-overflow_ignored.c | 26 ++++++++++++++ ...unds_guards_ov.c => 15-bounds_guards_ov.c} | 3 +- tests/regression/77-lin2vareq/15-env_order.c | 12 ------- .../{29-meet-tcons.c => 16-meet-tcons.c} | 3 +- .../77-lin2vareq/16-multiple-vars.c | 9 ----- ...{30-function-call.c => 17-function-call.c} | 3 +- ...1-join_all_cases.c => 18-join_all_cases.c} | 5 +-- ...sum-of-two-vars.c => 19-sum-of-two-vars.c} | 1 + .../77-lin2vareq/20-commutative_2.c | 20 ----------- ...nextension.c => 20-svcomp-signextension.c} | 3 +- .../77-lin2vareq/21-algebraic_equivalence.c | 22 ------------ tests/regression/77-lin2vareq/21-forget_var.c | 18 ++++++++++ ...{35-cast-to-short.c => 22-cast-to-short.c} | 3 +- .../77-lin2vareq/22-distributive_2.c | 23 ------------ .../77-lin2vareq/23-function_call2.c | 32 +++++++++++++++++ tests/regression/77-lin2vareq/23-unknown.c | 36 ------------------- .../77-lin2vareq/24-constant_square.c | 13 ------- .../77-lin2vareq/24-relation-result.c | 18 ++++++++++ .../77-lin2vareq/25-global-variables.c | 22 ++++++++++++ .../77-lin2vareq/25-overflow_ignored.c | 22 ------------ tests/regression/77-lin2vareq/26-pointers2.c | 24 ------------- .../77-lin2vareq/28-join_after_guard.c | 17 --------- tests/regression/77-lin2vareq/34-forget_var.c | 17 --------- 35 files changed, 159 insertions(+), 329 deletions(-) rename tests/regression/77-lin2vareq/{07-complicated_expression.c => 06-complicated_expression.c} (53%) delete mode 100644 tests/regression/77-lin2vareq/06-pointer.c rename tests/regression/77-lin2vareq/{11-loop.c => 07-loop.c} (69%) delete mode 100644 tests/regression/77-lin2vareq/08-distributive.c rename tests/regression/77-lin2vareq/{12-overflow.c => 08-overflow.c} (81%) delete mode 100644 tests/regression/77-lin2vareq/09-associative.c rename tests/regression/77-lin2vareq/{13-join-non-constant.c => 09-join-non-constant.c} (55%) rename tests/regression/77-lin2vareq/{14-coeff_vec.c => 10-coeff_vec.c} (100%) delete mode 100644 tests/regression/77-lin2vareq/10-commutative.c rename tests/regression/77-lin2vareq/{17-partitioning.c => 11-partitioning.c} (77%) rename tests/regression/77-lin2vareq/{18-loop_relational.c => 12-loop_relational.c} (54%) rename tests/regression/77-lin2vareq/{19-linear_loop.c => 13-linear_loop.c} (56%) create mode 100644 tests/regression/77-lin2vareq/14-overflow_ignored.c rename tests/regression/77-lin2vareq/{27-bounds_guards_ov.c => 15-bounds_guards_ov.c} (65%) delete mode 100644 tests/regression/77-lin2vareq/15-env_order.c rename tests/regression/77-lin2vareq/{29-meet-tcons.c => 16-meet-tcons.c} (62%) delete mode 100644 tests/regression/77-lin2vareq/16-multiple-vars.c rename tests/regression/77-lin2vareq/{30-function-call.c => 17-function-call.c} (99%) rename tests/regression/77-lin2vareq/{31-join_all_cases.c => 18-join_all_cases.c} (67%) rename tests/regression/77-lin2vareq/{32-sum-of-two-vars.c => 19-sum-of-two-vars.c} (99%) delete mode 100644 tests/regression/77-lin2vareq/20-commutative_2.c rename tests/regression/77-lin2vareq/{33-svcomp-signextension.c => 20-svcomp-signextension.c} (80%) delete mode 100644 tests/regression/77-lin2vareq/21-algebraic_equivalence.c create mode 100644 tests/regression/77-lin2vareq/21-forget_var.c rename tests/regression/77-lin2vareq/{35-cast-to-short.c => 22-cast-to-short.c} (82%) delete mode 100644 tests/regression/77-lin2vareq/22-distributive_2.c create mode 100644 tests/regression/77-lin2vareq/23-function_call2.c delete mode 100644 tests/regression/77-lin2vareq/23-unknown.c delete mode 100644 tests/regression/77-lin2vareq/24-constant_square.c create mode 100644 tests/regression/77-lin2vareq/24-relation-result.c create mode 100644 tests/regression/77-lin2vareq/25-global-variables.c delete mode 100644 tests/regression/77-lin2vareq/25-overflow_ignored.c delete mode 100644 tests/regression/77-lin2vareq/26-pointers2.c delete mode 100644 tests/regression/77-lin2vareq/28-join_after_guard.c delete mode 100644 tests/regression/77-lin2vareq/34-forget_var.c diff --git a/tests/regression/77-lin2vareq/07-complicated_expression.c b/tests/regression/77-lin2vareq/06-complicated_expression.c similarity index 53% rename from tests/regression/77-lin2vareq/07-complicated_expression.c rename to tests/regression/77-lin2vareq/06-complicated_expression.c index 236b8574da..e9451a319f 100644 --- a/tests/regression/77-lin2vareq/07-complicated_expression.c +++ b/tests/regression/77-lin2vareq/06-complicated_expression.c @@ -1,23 +1,29 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +//SKIP PARAM: --set ana.activated[+] lin2vareq #include +#include int main() { int x; int k; int y = 5; + if (x > INT_MAX / 3 || k > INT_MAX / 2 || k > INT_MAX / x) { + printf("Potential overflow detected.\n"); + return -1; + } + int result1 = 3 * (x + y) - 2 * x + 6; int result2 = 3 * (x + y) - 2 * k + 6; int result3 = x * 3 - x * 2; int result4 = x * 3 - x * k * x; - __goblint_check(result1 == x + 21); // SUCCESS + __goblint_check(result1 == x + 21); // UNKNOWN! __goblint_check(result2 == x + 21); // UNKNOWN! - __goblint_check(result3 == x); // SUCCES + __goblint_check(result3 == x); // UNKNOWN! __goblint_check(result4 == x * 3 - x * k * x); // UNKNOWN! return 0; } -// This test case includes variable with unknown values + diff --git a/tests/regression/77-lin2vareq/06-pointer.c b/tests/regression/77-lin2vareq/06-pointer.c deleted file mode 100644 index e250133918..0000000000 --- a/tests/regression/77-lin2vareq/06-pointer.c +++ /dev/null @@ -1,36 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -// from https://dl.acm.org/doi/10.1145/2049706.2049710 - -#include - -typedef int dataX_t; -typedef int dataY_t; - -dataX_t x_arr[100]; -dataY_t y_arr[100]; -dataX_t *x_ptr; -dataY_t *y_ptr; - -void access() { - *x_ptr = 42; - *y_ptr = *x_ptr + 10; -} - -int main() { - int i; - x_ptr = &x_arr[0]; - y_ptr = &y_arr[0]; - - for (i = 0; i < 100; i++) { - access(); - - __goblint_check(i == 8 * i + 0); //UNKNOWN! - __goblint_check(x_ptr == 8 * i + x_arr); //UNKNOWN! - __goblint_check(y_ptr == 4 * i + y_arr); //UNKNOWN! - - x_ptr++; - y_ptr++; - } - - return 0; -} diff --git a/tests/regression/77-lin2vareq/11-loop.c b/tests/regression/77-lin2vareq/07-loop.c similarity index 69% rename from tests/regression/77-lin2vareq/11-loop.c rename to tests/regression/77-lin2vareq/07-loop.c index 7816895a24..5fea099f13 100644 --- a/tests/regression/77-lin2vareq/11-loop.c +++ b/tests/regression/77-lin2vareq/07-loop.c @@ -1,5 +1,7 @@ //SKIP PARAM: --set ana.activated[+] lin2vareq -// Example from https://link.springer.com/content/pdf/10.1007/BF00268497.pdf + +// Example from https://link.springer.com/content/pdf/10.1007/BF00268497.pdf --set ana.int.def_exc false --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false + #include void main(void) { diff --git a/tests/regression/77-lin2vareq/08-distributive.c b/tests/regression/77-lin2vareq/08-distributive.c deleted file mode 100644 index 43a701733f..0000000000 --- a/tests/regression/77-lin2vareq/08-distributive.c +++ /dev/null @@ -1,17 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -#include - -int main() { - int a = 5; - int b = 3; - int c = 2; - - int expression1 = a * (b + c); - int expression2 = (a * b) + (a * c); - - __goblint_check(expression1 == expression2); //SUCCESS - - return 0; -} - -//This test case checks the distributive property of multiplication over addition diff --git a/tests/regression/77-lin2vareq/12-overflow.c b/tests/regression/77-lin2vareq/08-overflow.c similarity index 81% rename from tests/regression/77-lin2vareq/12-overflow.c rename to tests/regression/77-lin2vareq/08-overflow.c index d2b4b8e8ea..c9d820c64f 100644 --- a/tests/regression/77-lin2vareq/12-overflow.c +++ b/tests/regression/77-lin2vareq/08-overflow.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --enable ana.int.interval +// SKIP PARAM: --set ana.activated[+] lin2vareq #include diff --git a/tests/regression/77-lin2vareq/09-associative.c b/tests/regression/77-lin2vareq/09-associative.c deleted file mode 100644 index 0458eba558..0000000000 --- a/tests/regression/77-lin2vareq/09-associative.c +++ /dev/null @@ -1,18 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -#include - -int main() { - int a = 5; - int b = 3; - int c = 2; - - int expression1 = (a + b) * c; - int expression2 = c * (b + a); - - __goblint_check(expression1 == expression2); //SUCCESS - - return 0; -} - - -//This test case checks the associative property diff --git a/tests/regression/77-lin2vareq/13-join-non-constant.c b/tests/regression/77-lin2vareq/09-join-non-constant.c similarity index 55% rename from tests/regression/77-lin2vareq/13-join-non-constant.c rename to tests/regression/77-lin2vareq/09-join-non-constant.c index 16b1ccd33e..8f2a5fbd2a 100644 --- a/tests/regression/77-lin2vareq/13-join-non-constant.c +++ b/tests/regression/77-lin2vareq/09-join-non-constant.c @@ -1,9 +1,10 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --set ana.relation.privatization top --set sem.int.signed_overflow assume_none +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none int main(void) { int a, b, c, d; int t; + if (t) { b = a + 2; c = a + 7; @@ -15,8 +16,8 @@ int main(void) { } __goblint_check(c == b + 5); // SUCCESS __goblint_check(c == b + 3); // FAILURE - __goblint_check(d == b + 3); // UNKNOWN! - __goblint_check(b == a + 2); // UNKNOWN! + __goblint_check(d == b + 3); // UNKNOWN + __goblint_check(b == a + 2); // UNKNOWN return 0; } diff --git a/tests/regression/77-lin2vareq/14-coeff_vec.c b/tests/regression/77-lin2vareq/10-coeff_vec.c similarity index 100% rename from tests/regression/77-lin2vareq/14-coeff_vec.c rename to tests/regression/77-lin2vareq/10-coeff_vec.c diff --git a/tests/regression/77-lin2vareq/10-commutative.c b/tests/regression/77-lin2vareq/10-commutative.c deleted file mode 100644 index b88d3df15e..0000000000 --- a/tests/regression/77-lin2vareq/10-commutative.c +++ /dev/null @@ -1,17 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -#include - -int main() { - int a = 5; - int b = 3; - - int expression1 = a * b; - - int expression2 = b * a; - - __goblint_check(expression1 == expression2); //SUCCESS - - return 0; -} - -//This test case checks the commutative property. diff --git a/tests/regression/77-lin2vareq/17-partitioning.c b/tests/regression/77-lin2vareq/11-partitioning.c similarity index 77% rename from tests/regression/77-lin2vareq/17-partitioning.c rename to tests/regression/77-lin2vareq/11-partitioning.c index d36f4d7dbb..56e40b6d8b 100644 --- a/tests/regression/77-lin2vareq/17-partitioning.c +++ b/tests/regression/77-lin2vareq/11-partitioning.c @@ -1,5 +1,6 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none -// example from the paper +// SKIP PARAM: --set ana.activated[+] lin2vareq + +// example from https://dl.acm.org/doi/10.1145/2049706.2049710 #include #include diff --git a/tests/regression/77-lin2vareq/18-loop_relational.c b/tests/regression/77-lin2vareq/12-loop_relational.c similarity index 54% rename from tests/regression/77-lin2vareq/18-loop_relational.c rename to tests/regression/77-lin2vareq/12-loop_relational.c index 8151e649d2..ddb865bcde 100644 --- a/tests/regression/77-lin2vareq/18-loop_relational.c +++ b/tests/regression/77-lin2vareq/12-loop_relational.c @@ -1,4 +1,5 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq +// SKIP PARAM: --set ana.activated[+] lin2vareq + #include #include @@ -13,8 +14,8 @@ int main() { z += 2; } - __goblint_check(x == 21); //UNKNOWN! - __goblint_check(y == 7); //UNKNOWN! - __goblint_check(z == 11); //UNKNOWN! + __goblint_check(x == 21); //UNKNOWN + __goblint_check(y == 7); //UNKNOWN + __goblint_check(z == 11); //UNKNOWN return 0; } diff --git a/tests/regression/77-lin2vareq/19-linear_loop.c b/tests/regression/77-lin2vareq/13-linear_loop.c similarity index 56% rename from tests/regression/77-lin2vareq/19-linear_loop.c rename to tests/regression/77-lin2vareq/13-linear_loop.c index a5e790fd4e..451d368ff6 100644 --- a/tests/regression/77-lin2vareq/19-linear_loop.c +++ b/tests/regression/77-lin2vareq/13-linear_loop.c @@ -1,4 +1,5 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +//SKIP PARAM: --set ana.activated[+] lin2vareq + #include #include @@ -13,8 +14,8 @@ int main() { z = z + (y - x); } - __goblint_check(x == 6); //UNKNOWN! - __goblint_check(z != 1); //UNKNOWN! + __goblint_check(x == 6); //UNKNOWN + __goblint_check(z != 1); //UNKNOWN return 0; } diff --git a/tests/regression/77-lin2vareq/14-overflow_ignored.c b/tests/regression/77-lin2vareq/14-overflow_ignored.c new file mode 100644 index 0000000000..6aaff5b879 --- /dev/null +++ b/tests/regression/77-lin2vareq/14-overflow_ignored.c @@ -0,0 +1,26 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq + +#include +#include + +int main() { + int x; + int k; + int y; + + x = k + 1; + + __goblint_check(x == k + 1); // UNKNOWN + + for (int i = 0; i < 7; i++) { + if (x > INT_MAX - 1 || k > INT_MAX - 1) { + printf("Potential overflow detected.\n"); + return -1; + } + x++; + k++; + } + + __goblint_check(x == k + 1); // UNKNOWN + return 0; +} \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/27-bounds_guards_ov.c b/tests/regression/77-lin2vareq/15-bounds_guards_ov.c similarity index 65% rename from tests/regression/77-lin2vareq/27-bounds_guards_ov.c rename to tests/regression/77-lin2vareq/15-bounds_guards_ov.c index 18fefd743b..bd5f953c7a 100644 --- a/tests/regression/77-lin2vareq/27-bounds_guards_ov.c +++ b/tests/regression/77-lin2vareq/15-bounds_guards_ov.c @@ -1,4 +1,5 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_top --enable ana.int.interval +//SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_top --enable ana.int.interval --set ana.int.def_exc false --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false + // same test as 63-affeq/10-bounds_guards.ov.c int main() { int x, y; diff --git a/tests/regression/77-lin2vareq/15-env_order.c b/tests/regression/77-lin2vareq/15-env_order.c deleted file mode 100644 index f0613b4909..0000000000 --- a/tests/regression/77-lin2vareq/15-env_order.c +++ /dev/null @@ -1,12 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none - int next; // global variables are initialized to 0 - -int main() { - int t; - - if (next == 0) { - t = 5; - } - - __goblint_check(t == 5); //SUCCESS -} diff --git a/tests/regression/77-lin2vareq/29-meet-tcons.c b/tests/regression/77-lin2vareq/16-meet-tcons.c similarity index 62% rename from tests/regression/77-lin2vareq/29-meet-tcons.c rename to tests/regression/77-lin2vareq/16-meet-tcons.c index 8286c62467..97dd7f7551 100644 --- a/tests/regression/77-lin2vareq/29-meet-tcons.c +++ b/tests/regression/77-lin2vareq/16-meet-tcons.c @@ -1,4 +1,5 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none --set ana.int.def_exc false --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false + int main(void) { int x, y, z; diff --git a/tests/regression/77-lin2vareq/16-multiple-vars.c b/tests/regression/77-lin2vareq/16-multiple-vars.c deleted file mode 100644 index 8444d12256..0000000000 --- a/tests/regression/77-lin2vareq/16-multiple-vars.c +++ /dev/null @@ -1,9 +0,0 @@ -//SKIP //PARAM: --set ana.activated[+] lin2vareq --enable ana.int.interval - -int f (int j) { - return j + 1; -} -int main() { - int test = f(10); - __goblint_check(test == 11); - } \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/30-function-call.c b/tests/regression/77-lin2vareq/17-function-call.c similarity index 99% rename from tests/regression/77-lin2vareq/30-function-call.c rename to tests/regression/77-lin2vareq/17-function-call.c index 8b28b6ce6b..12815e4626 100644 --- a/tests/regression/77-lin2vareq/30-function-call.c +++ b/tests/regression/77-lin2vareq/17-function-call.c @@ -1,5 +1,6 @@ // SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none + int myfunction(int x, int y){ if (x == 0) { __goblint_check(x == 0); // SUCCESS @@ -11,8 +12,6 @@ int myfunction(int x, int y){ return 5; } - - int main(void) { int x, y, z; z = myfunction(x,y); diff --git a/tests/regression/77-lin2vareq/31-join_all_cases.c b/tests/regression/77-lin2vareq/18-join_all_cases.c similarity index 67% rename from tests/regression/77-lin2vareq/31-join_all_cases.c rename to tests/regression/77-lin2vareq/18-join_all_cases.c index 841e7ca882..9029440848 100644 --- a/tests/regression/77-lin2vareq/31-join_all_cases.c +++ b/tests/regression/77-lin2vareq/18-join_all_cases.c @@ -1,4 +1,5 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --set ana.relation.privatization top --set sem.int.signed_overflow assume_none +// SKIP PARAM: --set ana.activated[+] lin2vareq --set ana.relation.privatization top --set sem.int.signed_overflow assume_none --set ana.int.def_exc false --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false + void main(void) { int x1, x2, x3, x4, x5, x6, x7, x8, x9; int t; @@ -20,7 +21,7 @@ void main(void) { x8 = x6 - 50; } __goblint_check(x1 == 3); - __goblint_check(x2 == 2); // UNKNOWN + __goblint_check(x2 == 2); // UNKNOWN! __goblint_check(x3 == 4); __goblint_check(x4 == 5); __goblint_check(x7 == x5 - 3); diff --git a/tests/regression/77-lin2vareq/32-sum-of-two-vars.c b/tests/regression/77-lin2vareq/19-sum-of-two-vars.c similarity index 99% rename from tests/regression/77-lin2vareq/32-sum-of-two-vars.c rename to tests/regression/77-lin2vareq/19-sum-of-two-vars.c index af04046f7e..1bcc6ed446 100644 --- a/tests/regression/77-lin2vareq/32-sum-of-two-vars.c +++ b/tests/regression/77-lin2vareq/19-sum-of-two-vars.c @@ -1,5 +1,6 @@ // SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none + #include int main() { diff --git a/tests/regression/77-lin2vareq/20-commutative_2.c b/tests/regression/77-lin2vareq/20-commutative_2.c deleted file mode 100644 index e6795368d0..0000000000 --- a/tests/regression/77-lin2vareq/20-commutative_2.c +++ /dev/null @@ -1,20 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none -#include - -int main() { - int a = 5; - int b = 3; - int c = 2; - - for (int i = 0; i < c; i++) { - a += b; - b += 1; - } - - int expression1 = a * b; - int expression2 = b * a; - - __goblint_check(expression1 == expression2); //UNKNOWN - - return 0; -} diff --git a/tests/regression/77-lin2vareq/33-svcomp-signextension.c b/tests/regression/77-lin2vareq/20-svcomp-signextension.c similarity index 80% rename from tests/regression/77-lin2vareq/33-svcomp-signextension.c rename to tests/regression/77-lin2vareq/20-svcomp-signextension.c index 1d63e1bfb0..aae06953bc 100644 --- a/tests/regression/77-lin2vareq/33-svcomp-signextension.c +++ b/tests/regression/77-lin2vareq/20-svcomp-signextension.c @@ -1,4 +1,5 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none --set ana.int.def_exc false --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false + #include int main() { diff --git a/tests/regression/77-lin2vareq/21-algebraic_equivalence.c b/tests/regression/77-lin2vareq/21-algebraic_equivalence.c deleted file mode 100644 index 76b208783a..0000000000 --- a/tests/regression/77-lin2vareq/21-algebraic_equivalence.c +++ /dev/null @@ -1,22 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -#include - -int main() { - int a = 5; - int b = 3; - int c = 2; - int d = 0; - - for (int i = 0; i < c; i++) { - a += i; - b *= 2; - d += (a - b); - } - - int expression1 = a * b + d; - int expression2 = b * a - (d * -1); - - __goblint_check(expression1 == expression2); //UNKNOWN! - - return 0; -} diff --git a/tests/regression/77-lin2vareq/21-forget_var.c b/tests/regression/77-lin2vareq/21-forget_var.c new file mode 100644 index 0000000000..fd4980514e --- /dev/null +++ b/tests/regression/77-lin2vareq/21-forget_var.c @@ -0,0 +1,18 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none --set ana.int.def_exc false --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false + + +#include + +int main() { + int x, y, z; + + z = x; + + __goblint_check(z == x); // SUCCESS + + x = y * y; + + __goblint_check(x == z); // UNKNOWN! + + return 0; +} \ No newline at end of file diff --git a/tests/regression/77-lin2vareq/35-cast-to-short.c b/tests/regression/77-lin2vareq/22-cast-to-short.c similarity index 82% rename from tests/regression/77-lin2vareq/35-cast-to-short.c rename to tests/regression/77-lin2vareq/22-cast-to-short.c index fe4e4933bf..d99ce4ecf1 100644 --- a/tests/regression/77-lin2vareq/35-cast-to-short.c +++ b/tests/regression/77-lin2vareq/22-cast-to-short.c @@ -1,4 +1,5 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none --set ana.int.def_exc false --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false + #include int main() { diff --git a/tests/regression/77-lin2vareq/22-distributive_2.c b/tests/regression/77-lin2vareq/22-distributive_2.c deleted file mode 100644 index 57707eafb1..0000000000 --- a/tests/regression/77-lin2vareq/22-distributive_2.c +++ /dev/null @@ -1,23 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none -#include - -int main() { - int a = 5; - int b = 3; - int c = 2; - int d = 1; - - for (int i = 0; i < 3; i++) { - a += d; - b += a; - c += b; - d *= 2; - } - - int expression1 = a * (b + c); - int expression2 = (a * b) + (a * c); - - __goblint_check(expression1 == expression2); //UNKNOWN! - - return 0; -} diff --git a/tests/regression/77-lin2vareq/23-function_call2.c b/tests/regression/77-lin2vareq/23-function_call2.c new file mode 100644 index 0000000000..ac174b54cb --- /dev/null +++ b/tests/regression/77-lin2vareq/23-function_call2.c @@ -0,0 +1,32 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none + +int myfunction(int x, int y){ + __goblint_check(x < y); + + if (x == 0) { + __goblint_check(x == 0); // SUCCESS + } else if (y - x == 3) { + __goblint_check(y == x + 0); // FAILURE + __goblint_check(y - x == 3); // SUCCESS + } + + return 5; +} + +int main(void) { + int x, y, z; + + x = 0; + y = 5; + z = myfunction(x, y); + + x = 2; + y = 5; + z = myfunction(x, y); + + x = 1; + y = 3; + z = myfunction(x, y); + + return 0; +} diff --git a/tests/regression/77-lin2vareq/23-unknown.c b/tests/regression/77-lin2vareq/23-unknown.c deleted file mode 100644 index 1b7ba2a9ed..0000000000 --- a/tests/regression/77-lin2vareq/23-unknown.c +++ /dev/null @@ -1,36 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -// from https://dl.acm.org/doi/10.1145/2049706.2049710 - -#include - -typedef int dataX_t; -typedef int dataY_t; - -dataX_t x_arr[100]; -dataY_t y_arr[100]; -dataX_t *x_ptr; -dataY_t *y_ptr; - -void access() { - *x_ptr = 42; - *y_ptr = *x_ptr + 10; -} - -int main() { - int i; - x_ptr = &x_arr[0]; - y_ptr = &y_arr[0]; - - for (i = 0; i < 100; i++) { - access(); - - __goblint_check(i == 8 * i + 0); //UNKNOWN! - __goblint_check(x_ptr == 8 * i + x_arr); //UNKNOWN! - __goblint_check(y_ptr == 4 * i + y_arr); //UNKNOWN! - - x_ptr++; - y_ptr++; - } - - return 0; -} diff --git a/tests/regression/77-lin2vareq/24-constant_square.c b/tests/regression/77-lin2vareq/24-constant_square.c deleted file mode 100644 index 213a172585..0000000000 --- a/tests/regression/77-lin2vareq/24-constant_square.c +++ /dev/null @@ -1,13 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -#include - -int main() { - int x; - int y = 5; - - x = y * y; - - __goblint_check(x == y * y); //SUCCESS - - return 0; -} diff --git a/tests/regression/77-lin2vareq/24-relation-result.c b/tests/regression/77-lin2vareq/24-relation-result.c new file mode 100644 index 0000000000..215d7ff3f8 --- /dev/null +++ b/tests/regression/77-lin2vareq/24-relation-result.c @@ -0,0 +1,18 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq + +#include + +int compute(int x, int y) { + int result = x * y - y; + return result; +} + +int main(void) { + int a = 2, b = 3; + int result = compute(a, b); + + __goblint_check(result == a * b - b); // SUCCESS + __goblint_check(result + b == a * b); // SUCCESS + + return 0; +} diff --git a/tests/regression/77-lin2vareq/25-global-variables.c b/tests/regression/77-lin2vareq/25-global-variables.c new file mode 100644 index 0000000000..e8eca25a8b --- /dev/null +++ b/tests/regression/77-lin2vareq/25-global-variables.c @@ -0,0 +1,22 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq +#include + +int x; +int y; + +void setX(int a) { + x = a; +} + +void setY() { + y = 2 * x + 3; +} + +int main() { + setX(5); + setY(); + + __goblint_check(y == 2 * x + 3); // SUCCESS + + return 0; +} diff --git a/tests/regression/77-lin2vareq/25-overflow_ignored.c b/tests/regression/77-lin2vareq/25-overflow_ignored.c deleted file mode 100644 index 2d226bc75f..0000000000 --- a/tests/regression/77-lin2vareq/25-overflow_ignored.c +++ /dev/null @@ -1,22 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none - -#include - -int main() { - int x; - int k; - int y; - - x = k + 1; - - __goblint_check(x == k + 1); // SUCCESS - - for (int i = 0; i < 7; i++) { - x++; - k++; - } - - __goblint_check(x == k + 1); // SUCCESS - - return 0; -} diff --git a/tests/regression/77-lin2vareq/26-pointers2.c b/tests/regression/77-lin2vareq/26-pointers2.c deleted file mode 100644 index f78c75a8e6..0000000000 --- a/tests/regression/77-lin2vareq/26-pointers2.c +++ /dev/null @@ -1,24 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -// from https://dl.acm.org/doi/10.1145/2049706.2049710 - -#include - -int x; -int y; -int *x_ptr; -int *y_ptr; - -void access() { - *x_ptr = 42; - *y_ptr = *x_ptr + 10; -} - -int main() { - int i; - x_ptr = &x; - y_ptr = &y; - access(); - __goblint_check(*x_ptr == *y_ptr - 10 ); - __goblint_check(i + *x_ptr + 10 == i + *y_ptr ); //UNKNOWN! - -} diff --git a/tests/regression/77-lin2vareq/28-join_after_guard.c b/tests/regression/77-lin2vareq/28-join_after_guard.c deleted file mode 100644 index 73d13384f3..0000000000 --- a/tests/regression/77-lin2vareq/28-join_after_guard.c +++ /dev/null @@ -1,17 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq --set ana.relation.privatization top --set sem.int.signed_overflow assume_none -// from 63-affeq/04-join-after-guard -int main(void) { - int zero = 0; - int x = 0; - - int t, r, d; - if (t) { - r = 10; - d = 500; - } else { - r = 10; - } - __goblint_check(r == 10); //SUCCESS - __goblint_check(d == 500); //UNKNOWN! - return 0; -} diff --git a/tests/regression/77-lin2vareq/34-forget_var.c b/tests/regression/77-lin2vareq/34-forget_var.c deleted file mode 100644 index a115c52aa5..0000000000 --- a/tests/regression/77-lin2vareq/34-forget_var.c +++ /dev/null @@ -1,17 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none - -#include - -int main() { - int x, y, z; - - z = x; - - __goblint_check(z == x); // SUCCESS - - x = y * y; - - __goblint_check(x == z); // UNKNOWN - - return 0; -} From c43904396b709ee498eff08214d45d3f4a7a1fe5 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Fri, 19 Jan 2024 23:14:11 +0100 Subject: [PATCH 137/280] test cases adapted --- tests/regression/77-lin2vareq/11-partitioning.c | 2 +- tests/regression/77-lin2vareq/15-bounds_guards_ov.c | 4 ++-- tests/regression/77-lin2vareq/16-meet-tcons.c | 2 +- tests/regression/77-lin2vareq/18-join_all_cases.c | 2 +- tests/regression/77-lin2vareq/20-svcomp-signextension.c | 3 +-- tests/regression/77-lin2vareq/21-forget_var.c | 2 +- tests/regression/77-lin2vareq/22-cast-to-short.c | 3 +-- 7 files changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/regression/77-lin2vareq/11-partitioning.c b/tests/regression/77-lin2vareq/11-partitioning.c index 56e40b6d8b..6436a862dc 100644 --- a/tests/regression/77-lin2vareq/11-partitioning.c +++ b/tests/regression/77-lin2vareq/11-partitioning.c @@ -1,6 +1,6 @@ // SKIP PARAM: --set ana.activated[+] lin2vareq - // example from https://dl.acm.org/doi/10.1145/2049706.2049710 + #include #include diff --git a/tests/regression/77-lin2vareq/15-bounds_guards_ov.c b/tests/regression/77-lin2vareq/15-bounds_guards_ov.c index bd5f953c7a..c66d4f3b06 100644 --- a/tests/regression/77-lin2vareq/15-bounds_guards_ov.c +++ b/tests/regression/77-lin2vareq/15-bounds_guards_ov.c @@ -1,6 +1,6 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_top --enable ana.int.interval --set ana.int.def_exc false --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false - +//SKIP PARAM: --set ana.activated[+] lin2vareq // same test as 63-affeq/10-bounds_guards.ov.c + int main() { int x, y; int p = 0; diff --git a/tests/regression/77-lin2vareq/16-meet-tcons.c b/tests/regression/77-lin2vareq/16-meet-tcons.c index 97dd7f7551..79cbf2ed81 100644 --- a/tests/regression/77-lin2vareq/16-meet-tcons.c +++ b/tests/regression/77-lin2vareq/16-meet-tcons.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none --set ana.int.def_exc false --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none int main(void) { diff --git a/tests/regression/77-lin2vareq/18-join_all_cases.c b/tests/regression/77-lin2vareq/18-join_all_cases.c index 9029440848..84fa3b6f91 100644 --- a/tests/regression/77-lin2vareq/18-join_all_cases.c +++ b/tests/regression/77-lin2vareq/18-join_all_cases.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --set ana.relation.privatization top --set sem.int.signed_overflow assume_none --set ana.int.def_exc false --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false +// SKIP PARAM: --set ana.activated[+] lin2vareq --set ana.relation.privatization top --set sem.int.signed_overflow assume_none void main(void) { int x1, x2, x3, x4, x5, x6, x7, x8, x9; diff --git a/tests/regression/77-lin2vareq/20-svcomp-signextension.c b/tests/regression/77-lin2vareq/20-svcomp-signextension.c index aae06953bc..1d63e1bfb0 100644 --- a/tests/regression/77-lin2vareq/20-svcomp-signextension.c +++ b/tests/regression/77-lin2vareq/20-svcomp-signextension.c @@ -1,5 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none --set ana.int.def_exc false --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false - +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none #include int main() { diff --git a/tests/regression/77-lin2vareq/21-forget_var.c b/tests/regression/77-lin2vareq/21-forget_var.c index fd4980514e..db69bc5f71 100644 --- a/tests/regression/77-lin2vareq/21-forget_var.c +++ b/tests/regression/77-lin2vareq/21-forget_var.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none --set ana.int.def_exc false --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none #include diff --git a/tests/regression/77-lin2vareq/22-cast-to-short.c b/tests/regression/77-lin2vareq/22-cast-to-short.c index d99ce4ecf1..fe4e4933bf 100644 --- a/tests/regression/77-lin2vareq/22-cast-to-short.c +++ b/tests/regression/77-lin2vareq/22-cast-to-short.c @@ -1,5 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none --set ana.int.def_exc false --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false - +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none #include int main() { From 189f6eb7023f018ace154375c4dd36eeda5eef5f Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 21 Jan 2024 14:22:24 +0100 Subject: [PATCH 138/280] adapted some tests --- .../77-lin2vareq/{00-basic.c => 01-basic.c} | 2 +- .../77-lin2vareq/02-equality_assertion.c | 17 ------ .../{01-iteration.c => 02-iteration.c} | 2 +- .../77-lin2vareq/03-loop_increment.c | 25 ++++----- .../regression/77-lin2vareq/04-reachability.c | 2 +- .../77-lin2vareq/05-vars_equality.c | 5 +- .../77-lin2vareq/06-complicated_expression.c | 35 ++++++------ tests/regression/77-lin2vareq/07-loop.c | 19 ++++--- .../77-lin2vareq/09-join-non-constant.c | 6 ++- .../regression/77-lin2vareq/11-partitioning.c | 53 ++++++++++--------- .../77-lin2vareq/12-loop_relational.c | 27 +++++----- .../regression/77-lin2vareq/13-linear_loop.c | 28 +++++----- .../77-lin2vareq/14-overflow_ignored.c | 21 ++++---- .../77-lin2vareq/18-join_all_cases.c | 46 ++++++++-------- .../77-lin2vareq/25-global-variables.c | 21 ++++---- .../77-lin2vareq/26-function_call3.c | 21 ++++++++ 16 files changed, 171 insertions(+), 159 deletions(-) rename tests/regression/77-lin2vareq/{00-basic.c => 01-basic.c} (53%) delete mode 100644 tests/regression/77-lin2vareq/02-equality_assertion.c rename tests/regression/77-lin2vareq/{01-iteration.c => 02-iteration.c} (87%) create mode 100644 tests/regression/77-lin2vareq/26-function_call3.c diff --git a/tests/regression/77-lin2vareq/00-basic.c b/tests/regression/77-lin2vareq/01-basic.c similarity index 53% rename from tests/regression/77-lin2vareq/00-basic.c rename to tests/regression/77-lin2vareq/01-basic.c index f285bf28ca..4d838b4e26 100644 --- a/tests/regression/77-lin2vareq/00-basic.c +++ b/tests/regression/77-lin2vareq/01-basic.c @@ -1,4 +1,4 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq +//SKIP PARAM: --set ana.activated[+] lin2vareq --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false --set sem.int.signed_overflow assume_none #include #include diff --git a/tests/regression/77-lin2vareq/02-equality_assertion.c b/tests/regression/77-lin2vareq/02-equality_assertion.c deleted file mode 100644 index 00182c38ed..0000000000 --- a/tests/regression/77-lin2vareq/02-equality_assertion.c +++ /dev/null @@ -1,17 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq -#include -#include - -int main() { - int x = 0; - int y = 0; - - x = 10; - y = 1; - - __goblint_check(x == 10 * y); //SUCCESS - - if(x == 10 * y) - return 0; - __goblint_check(0); // NOWARN (unreachable) -} diff --git a/tests/regression/77-lin2vareq/01-iteration.c b/tests/regression/77-lin2vareq/02-iteration.c similarity index 87% rename from tests/regression/77-lin2vareq/01-iteration.c rename to tests/regression/77-lin2vareq/02-iteration.c index 790c7b76fd..f960b144fe 100644 --- a/tests/regression/77-lin2vareq/01-iteration.c +++ b/tests/regression/77-lin2vareq/02-iteration.c @@ -8,7 +8,7 @@ int main() { for (i = 0; i < size; ++i) { j = i; - __goblint_check(i == j); //SUCESS + __goblint_check(i == j); //SUCCESS } return 0; diff --git a/tests/regression/77-lin2vareq/03-loop_increment.c b/tests/regression/77-lin2vareq/03-loop_increment.c index cbf608b07b..b5b1b965ab 100644 --- a/tests/regression/77-lin2vareq/03-loop_increment.c +++ b/tests/regression/77-lin2vareq/03-loop_increment.c @@ -1,20 +1,21 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none #include int main() { - int i, j, k; - int size = 5; + int i, j, k; + int size = 5; + i = 0; + j = 0; + k = 5; - for (i = 0; i < size; ++i) { - j = 2 * i; - k = j + i; + for (i = 1; i < size; ++i) { + j = i; + k = j + 5; + } - __goblint_check(j == 2 * i); //UNKNOWN! + __goblint_check(j + 1 == i); // SUCCESS - __goblint_check(k == j + i); //UNKNOWN! + __goblint_check(k == i + 4); // SUCCESS - __goblint_check(k == 3 * i); //UNKNOWN! - } - - return 0; + return 0; } diff --git a/tests/regression/77-lin2vareq/04-reachability.c b/tests/regression/77-lin2vareq/04-reachability.c index 8b1694b502..d1007ed68c 100644 --- a/tests/regression/77-lin2vareq/04-reachability.c +++ b/tests/regression/77-lin2vareq/04-reachability.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq +// SKIP PARAM: --set ana.activated[+] lin2vareq --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false #include #include diff --git a/tests/regression/77-lin2vareq/05-vars_equality.c b/tests/regression/77-lin2vareq/05-vars_equality.c index 0a5eb1d3ec..83c9de306c 100644 --- a/tests/regression/77-lin2vareq/05-vars_equality.c +++ b/tests/regression/77-lin2vareq/05-vars_equality.c @@ -1,5 +1,4 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq -// from https://dl.acm.org/doi/10.1145/2049706.2049710 +//SKIP PARAM: --set ana.activated[+] lin2vareq --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false #include @@ -23,4 +22,4 @@ int main() { __goblint_check(x12 == 2 * x11 + 1); //SUCCESS return 0; -} \ No newline at end of file +} diff --git a/tests/regression/77-lin2vareq/06-complicated_expression.c b/tests/regression/77-lin2vareq/06-complicated_expression.c index e9451a319f..02f17d16ab 100644 --- a/tests/regression/77-lin2vareq/06-complicated_expression.c +++ b/tests/regression/77-lin2vareq/06-complicated_expression.c @@ -1,29 +1,24 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq +// SKIP PARAM: --set sem.int.signed_overflow assume_none --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false --set ana.activated[+] lin2vareq #include -#include +#include + int main() { int x; int k; - int y = 5; - - if (x > INT_MAX / 3 || k > INT_MAX / 2 || k > INT_MAX / x) { - printf("Potential overflow detected.\n"); - return -1; + if (x < 300 && k < 300) { + int y = 5; + + int result1 = 3 * (x + y) - 2 * x + 6; + int result2 = 3 * (x + y) - 2 * k + 6; + int result3 = x * 3 - x * 2; + int result4 = x * 3 - x * k * x; + + __goblint_check(result1 == x + 21); // SUCCESS + __goblint_check(result2 == x + 21); // UNKNOWN! + __goblint_check(result3 == x); // SUCCES + __goblint_check(result4 == x * 3 - x * k * x); // UNKNOWN } - - int result1 = 3 * (x + y) - 2 * x + 6; - int result2 = 3 * (x + y) - 2 * k + 6; - int result3 = x * 3 - x * 2; - int result4 = x * 3 - x * k * x; - - __goblint_check(result1 == x + 21); // UNKNOWN! - __goblint_check(result2 == x + 21); // UNKNOWN! - __goblint_check(result3 == x); // UNKNOWN! - __goblint_check(result4 == x * 3 - x * k * x); // UNKNOWN! - return 0; } - - diff --git a/tests/regression/77-lin2vareq/07-loop.c b/tests/regression/77-lin2vareq/07-loop.c index 5fea099f13..703b6b0bbb 100644 --- a/tests/regression/77-lin2vareq/07-loop.c +++ b/tests/regression/77-lin2vareq/07-loop.c @@ -1,20 +1,23 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq +//SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none -// Example from https://link.springer.com/content/pdf/10.1007/BF00268497.pdf --set ana.int.def_exc false --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false +// Adapted example from https://link.springer.com/content/pdf/10.1007/BF00268497.pdf #include void main(void) { int i; + int j; int k; - i = 2; - k = 0; + if(k > 200){ + return 0; + } + j = k + 5; - while (i < 100) { - __goblint_check(3 * i - k == 1); //UNKNOWN! - i = i + 1; + while (j < 100) { + __goblint_check(j - k == 5); //SUCCESS + j = j + 3; k = k + 3; } - __goblint_check(3 * i - k == 1); //UNKNOWN! + __goblint_check(j - k == 5); //SUCCESS } diff --git a/tests/regression/77-lin2vareq/09-join-non-constant.c b/tests/regression/77-lin2vareq/09-join-non-constant.c index 8f2a5fbd2a..c5e93e0b54 100644 --- a/tests/regression/77-lin2vareq/09-join-non-constant.c +++ b/tests/regression/77-lin2vareq/09-join-non-constant.c @@ -1,5 +1,7 @@ // SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +#include + int main(void) { int a, b, c, d; @@ -16,8 +18,8 @@ int main(void) { } __goblint_check(c == b + 5); // SUCCESS __goblint_check(c == b + 3); // FAILURE - __goblint_check(d == b + 3); // UNKNOWN - __goblint_check(b == a + 2); // UNKNOWN + __goblint_check(d == b - 1); // UNKNOWN! + __goblint_check(b == a + 2); // UNKNOWN! return 0; } diff --git a/tests/regression/77-lin2vareq/11-partitioning.c b/tests/regression/77-lin2vareq/11-partitioning.c index 6436a862dc..3d6463af8b 100644 --- a/tests/regression/77-lin2vareq/11-partitioning.c +++ b/tests/regression/77-lin2vareq/11-partitioning.c @@ -1,34 +1,39 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none // example from https://dl.acm.org/doi/10.1145/2049706.2049710 -#include #include +#include +#include int main() { -int x,x1,x2,x3,x4,x5,x6,x7; + int x, x1, x2, x3, x4, x5, x6, x7; -if(x>5){ - x1 = x1; - x2 = x2; - x3 = x1; - x4 = x2 + 5; - x5 = x1 + 5; - x6 = x1 + 3; - x7 = x1 + 2; -} else { - x1 = x1; - x2 = x2; - x3 = x2 - 5; - x4 = x2 + 5; - x5 = x2; - x6 = x2 + 1; - x7 = x2; -} + if (x1 > INT_MAX - 5 || x2 > INT_MAX - 5 || x2 > INT_MIN + 5) { + return -1; + } + + if (x > 5) { + x1 = x1; + x2 = x2; + x3 = x1; + x4 = x2 + 5; + x5 = x1 + 5; + x6 = x1 + 3; + x7 = x1 + 2; + } else { + x1 = x1; + x2 = x2; + x3 = x2 - 5; + x4 = x2 + 5; + x5 = x2; + x6 = x2 + 1; + x7 = x2; + } -__goblint_check(x4 == x2 + 5); -__goblint_check(x5 == x3 + 5); -__goblint_check(x7 == x6 - 1); + __goblint_check(x4 == x2 + 5); // SUCCESS + __goblint_check(x5 == x3 + 5); // SUCCESS + __goblint_check(x7 == x6 - 1); // SUCCESS -return 0; + return 0; } diff --git a/tests/regression/77-lin2vareq/12-loop_relational.c b/tests/regression/77-lin2vareq/12-loop_relational.c index ddb865bcde..0254d00dc9 100644 --- a/tests/regression/77-lin2vareq/12-loop_relational.c +++ b/tests/regression/77-lin2vareq/12-loop_relational.c @@ -1,21 +1,22 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none + #include #include int main() { - int x = 0; - int y = 10; - int z = 5; + int x = 0; + int y = 10; + int z = 5; - for(int i = 0; i < 3; i++) { - x += z; - y -= i; - z += 2; - } + for (int i = 0; i < 3; i++) { + x = z; + y = i; + __goblint_check(x == z); // SUCCESS + z = 2; + __goblint_check(y == i); // SUCCESS + __goblint_check(z == 2); // SUCCESS + } - __goblint_check(x == 21); //UNKNOWN - __goblint_check(y == 7); //UNKNOWN - __goblint_check(z == 11); //UNKNOWN - return 0; + return 0; } diff --git a/tests/regression/77-lin2vareq/13-linear_loop.c b/tests/regression/77-lin2vareq/13-linear_loop.c index 451d368ff6..96f46217c6 100644 --- a/tests/regression/77-lin2vareq/13-linear_loop.c +++ b/tests/regression/77-lin2vareq/13-linear_loop.c @@ -1,21 +1,21 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none -#include #include +#include int main() { - int x = 1; - int y = 1; - int z = 1; - - for (int i = 1; i <= 3; i++) { - x = x * i; - y = y + x; - z = z + (y - x); - } + int x = 1; + int y = 1; + int z = 1; + int k; - __goblint_check(x == 6); //UNKNOWN - __goblint_check(z != 1); //UNKNOWN + for (int i = 1; i <= 3; i++) { + x = x * i; + y = x; + z = y + (y - x) + 2; + __goblint_check(x == y); // SUCCESS + __goblint_check(z == y + 2); // SUCCESS + } - return 0; + return 0; } diff --git a/tests/regression/77-lin2vareq/14-overflow_ignored.c b/tests/regression/77-lin2vareq/14-overflow_ignored.c index 6aaff5b879..d3e6ebf069 100644 --- a/tests/regression/77-lin2vareq/14-overflow_ignored.c +++ b/tests/regression/77-lin2vareq/14-overflow_ignored.c @@ -1,26 +1,27 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none -#include #include +#include int main() { int x; int k; int y; + if (k > INT_MAX - 8) { + printf("Potential overflow detected.\n"); + return -1; + } + x = k + 1; + __goblint_check(x == k + 1); // SUCCESS - __goblint_check(x == k + 1); // UNKNOWN - for (int i = 0; i < 7; i++) { - if (x > INT_MAX - 1 || k > INT_MAX - 1) { - printf("Potential overflow detected.\n"); - return -1; - } x++; k++; } - __goblint_check(x == k + 1); // UNKNOWN + __goblint_check(x == k + 1); // SUCCESS + return 0; -} \ No newline at end of file +} diff --git a/tests/regression/77-lin2vareq/18-join_all_cases.c b/tests/regression/77-lin2vareq/18-join_all_cases.c index 84fa3b6f91..69ba5d9aa3 100644 --- a/tests/regression/77-lin2vareq/18-join_all_cases.c +++ b/tests/regression/77-lin2vareq/18-join_all_cases.c @@ -3,27 +3,29 @@ void main(void) { int x1, x2, x3, x4, x5, x6, x7, x8, x9; int t; - if (t) { - x2 = 2; - x1 = 3; - x3 = 4; - x4 = 5; - x5 = x6 + 6; - x7 = x6 + 3; - x8 = x6 - 55; - } else { - x1 = 3; - x2 = 3; - x3 = 4; - x4 = 5; - x5 = x6 + 11; - x7 = x6 + 8; - x8 = x6 - 50; + if (x6 < 300 && x6 > -300) { + if (t) { + x2 = 2; + x1 = 3; + x3 = 4; + x4 = 5; + x5 = x6 + 6; + x7 = x6 + 3; + x8 = x6 - 55; + } else { + x1 = 3; + x2 = 3; + x3 = 4; + x4 = 5; + x5 = x6 + 11; + x7 = x6 + 8; + x8 = x6 - 50; + } + __goblint_check(x1 == 3); + __goblint_check(x2 == 2); // UNKNOWN! + __goblint_check(x3 == 4); + __goblint_check(x4 == 5); + __goblint_check(x7 == x5 - 3); + __goblint_check(x8 == x7 - 58); } - __goblint_check(x1 == 3); - __goblint_check(x2 == 2); // UNKNOWN! - __goblint_check(x3 == 4); - __goblint_check(x4 == 5); - __goblint_check(x7 == x5 - 3); - __goblint_check(x8 == x7 - 58); } diff --git a/tests/regression/77-lin2vareq/25-global-variables.c b/tests/regression/77-lin2vareq/25-global-variables.c index e8eca25a8b..a8cf6ba6a3 100644 --- a/tests/regression/77-lin2vareq/25-global-variables.c +++ b/tests/regression/77-lin2vareq/25-global-variables.c @@ -1,22 +1,21 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +#include #include int x; int y; -void setX(int a) { - x = a; -} - -void setY() { - y = 2 * x + 3; -} +void setY() { y = x + 3; } int main() { - setX(5); + int k; + x = k * k; + + if (x < INT_MAX - 3) { setY(); - __goblint_check(y == 2 * x + 3); // SUCCESS + __goblint_check(y == x + 3); // SUCCESS + } - return 0; + return 0; } diff --git a/tests/regression/77-lin2vareq/26-function_call3.c b/tests/regression/77-lin2vareq/26-function_call3.c new file mode 100644 index 0000000000..f406862b74 --- /dev/null +++ b/tests/regression/77-lin2vareq/26-function_call3.c @@ -0,0 +1,21 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none + +#include + +int check_equal(int x, int y, int z) { + __goblint_check(x == y); + __goblint_check(z == y); + __goblint_check(x == z); + return 8; +} + +int main(void) { + int x, y, z; + + y = x; + z = y; + + check_equal(x, y, z); + + return 0; +} From 1c7d625b5acde8ecde5d2ddc78c74443f6303e69 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 21 Jan 2024 14:45:11 +0100 Subject: [PATCH 139/280] added test with return value --- .../77-lin2vareq/27-function-return-value.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/regression/77-lin2vareq/27-function-return-value.c diff --git a/tests/regression/77-lin2vareq/27-function-return-value.c b/tests/regression/77-lin2vareq/27-function-return-value.c new file mode 100644 index 0000000000..5efae1873d --- /dev/null +++ b/tests/regression/77-lin2vareq/27-function-return-value.c @@ -0,0 +1,18 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none + +#include + +int k; + +int x_plus_three(int x) { + return x + 3; +} + +int main(void) { + int y, z; + z = x_plus_three(k); + + __goblint_check(z == k + 3); + + return 0; +} From f0e1f8a612031aec75c9108916237c82b9252075 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 21 Jan 2024 14:56:39 +0100 Subject: [PATCH 140/280] added semicolon to the show function --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index e9e5d93ed3..26ad4c3f2c 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -316,8 +316,9 @@ struct let lookup i = Var.to_string (Environment.var_of_dim varM.env i) in let show_var i tuple = match tuple with - | (None, offset) -> (lookup i) ^ " = " ^ Z.to_string offset ^ "\n" - | (Some index, offset) -> (lookup i) ^ " = " ^ lookup index ^ " + " ^ Z.to_string offset ^ "\n" + | (None, offset) -> (lookup i) ^ " = " ^ Z.to_string offset ^ ";\n" + | (Some index, offset) -> (lookup i) ^ " = " ^ lookup index ^ + (if offset <> Z.zero then " + " ^ Z.to_string offset else "") ^ ";\n" in if is_top varM then "⊤\n" else match varM.d with | None -> "⊥\n" From 8dbed52e4416065d59492619693c23d676cb37b1 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Tue, 23 Jan 2024 21:39:28 +0100 Subject: [PATCH 141/280] test cases adapted --- tests/regression/77-lin2vareq/28-widening.c | 18 ++++++++++++++++++ tests/regression/77-lin2vareq/29-arrowing.c | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 tests/regression/77-lin2vareq/28-widening.c create mode 100644 tests/regression/77-lin2vareq/29-arrowing.c diff --git a/tests/regression/77-lin2vareq/28-widening.c b/tests/regression/77-lin2vareq/28-widening.c new file mode 100644 index 0000000000..275bd7bc73 --- /dev/null +++ b/tests/regression/77-lin2vareq/28-widening.c @@ -0,0 +1,18 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq + +#include + +int main() { + int x = 0; + int y = 1; + + for (int i = 0; i < 1000; i++) { + x += i; + y += x; + } + + __goblint_check(x == 499500); //UNKNOWN + __goblint_check(y == 499501); //UNKNOWN + + return 0; +} diff --git a/tests/regression/77-lin2vareq/29-arrowing.c b/tests/regression/77-lin2vareq/29-arrowing.c new file mode 100644 index 0000000000..668f824592 --- /dev/null +++ b/tests/regression/77-lin2vareq/29-arrowing.c @@ -0,0 +1,18 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq + +#include + +int main() { + int x = 1000; + int y = 1; + + for (int i = 1000; i > 0; i--) { + x -= i; + y += x; + } + + __goblint_check(x == -499500); // UNKNOWN + __goblint_check(y == 500501); // UNKNOWN + + return 0; +} From 80ab2097356a69d6a1d582524f787fb7fa26ef3a Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 28 Jan 2024 09:58:25 +0100 Subject: [PATCH 142/280] implement MaySignedOverflow query in base analysis --- src/analyses/base.ml | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index fb2b5af517..09638fa3e9 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1216,6 +1216,50 @@ struct else Invariant.none + let rec exp_may_signed_overflow ctx exp = + match Cilfacade.get_ikind_exp exp with + | exception _ -> BoolDomain.MayBool.top () + | ik -> + let r = match eval_rv_ask_evalint (Analyses.ask_of_ctx ctx) ctx.global ctx.local exp with + | Int i -> `Lifted i + | _ -> Queries.ID.top () + | exception (IntDomain.ArithmeticOnIntegerBot _) -> Queries.ID.top () in + if Cil.isSigned ik && Queries.ID.is_top_of ik r then + true (** if EvalInt returns top, there was probably an overflow. + Otherwise, to be sure that there is no overflow, we need to check each subexpression *) + else + match exp with + | Const _ + | SizeOf _ + | SizeOfStr _ + | AlignOf _ + | AddrOfLabel _ -> false + | Real e + | Imag e + | SizeOfE e + | AlignOfE e + | UnOp (_, e, _) + | CastE (_, e) -> exp_may_signed_overflow ctx e + | BinOp (_, e1, e2, _) -> + exp_may_signed_overflow ctx e1 || exp_may_signed_overflow ctx e2 + | Question (e1, e2, e3, _) -> + exp_may_signed_overflow ctx e1 || exp_may_signed_overflow ctx e2 || exp_may_signed_overflow ctx e3 + | Lval lval + | AddrOf lval + | StartOf lval -> lval_may_signed_overflow ctx lval + and lval_may_signed_overflow ctx (lval : lval) = + let (host, offset) = lval in + let host_may_signed_overflow = function + | Var v -> false + | Mem e -> exp_may_signed_overflow ctx e + in + let rec offset_may_signed_overflow = function + | NoOffset -> false + | Index (e, o) -> exp_may_signed_overflow ctx e || offset_may_signed_overflow o + | Field (f, o) -> offset_may_signed_overflow o + in + host_may_signed_overflow host || offset_may_signed_overflow offset + let query ctx (type a) (q: a Q.t): a Q.result = match q with | Q.EvalFunvar e -> @@ -1381,6 +1425,7 @@ struct | Q.InvariantGlobal g -> let g: V.t = Obj.obj g in query_invariant_global ctx g + | Q.MaySignedOverflow e -> exp_may_signed_overflow ctx e | _ -> Q.Result.top q let update_variable variable typ value cpa = From 30a42cd07276a28b482024a1ebafe6495ab3b93d Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 28 Jan 2024 09:59:17 +0100 Subject: [PATCH 143/280] use new MaySignedOverflow query in relationAnalysis --- src/analyses/apron/relationAnalysis.apron.ml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index e572755930..8d06266164 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -176,7 +176,7 @@ struct let no_overflow (ask: Queries.ask) exp = match Cilfacade.get_ikind_exp exp with - | exception Invalid_argument _ -> false (* TODO: why this? *) + | exception Invalid_argument _ -> false | exception Cilfacade.TypeOfError _ -> false | ik -> if IntDomain.should_wrap ik then @@ -184,7 +184,7 @@ struct else if IntDomain.should_ignore_overflow ik then true else - not (Queries.ID.is_top_of ik (ask.f (EvalInt exp))) + not (ask.f (MaySignedOverflow exp)) let no_overflow ctx exp = lazy ( let res = no_overflow ctx exp in @@ -192,7 +192,6 @@ struct res ) - let assert_type_bounds ask rel x = assert (RD.Tracked.varinfo_tracked x); match Cilfacade.get_ikind x.vtype with @@ -619,8 +618,6 @@ struct |> Enum.fold (fun acc x -> Invariant.(acc && of_exp x)) Invariant.none let query ctx (type a) (q: a Queries.t): a Queries.result = - let no_overflow ctx' exp' = - IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp exp') in (* TODO: separate no_overflow? *) let open Queries in let st = ctx.local in let eval_int e no_ov = @@ -634,7 +631,7 @@ struct | EvalInt e -> if M.tracing then M.traceli "evalint" "relation query %a (%a)\n" d_exp e d_plainexp e; if M.tracing then M.trace "evalint" "relation st: %a\n" D.pretty ctx.local; - let r = eval_int e (lazy(no_overflow ctx e)) in + let r = eval_int e (no_overflow (Analyses.ask_of_ctx ctx) e) in if M.tracing then M.traceu "evalint" "relation query %a -> %a\n" d_exp e ID.pretty r; r | Queries.IterSysVars (vq, vf) -> From 7b05557f5df8268fd32e265d6e1fdf449bce9194 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 28 Jan 2024 10:08:45 +0100 Subject: [PATCH 144/280] add MaySignedOverflow query to queries.ml --- src/domains/queries.ml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index f5fc832a9e..9dacdd40cf 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -126,6 +126,7 @@ type _ t = | MustTermAllLoops: MustBool.t t | IsEverMultiThreaded: MayBool.t t | TmpSpecial: Mval.Exp.t -> ML.t t + | MaySignedOverflow: exp -> MayBool.t t type 'a result = 'a @@ -195,6 +196,7 @@ struct | MustTermAllLoops -> (module MustBool) | IsEverMultiThreaded -> (module MayBool) | TmpSpecial _ -> (module ML) + | MaySignedOverflow _ -> (module MayBool) (** Get bottom result for query. *) let bot (type a) (q: a t): a result = @@ -263,6 +265,7 @@ struct | MustTermAllLoops -> MustBool.top () | IsEverMultiThreaded -> MayBool.top () | TmpSpecial _ -> ML.top () + | MaySignedOverflow _ -> MayBool.top () end (* The type any_query can't be directly defined in Any as t, @@ -328,6 +331,7 @@ struct | Any IsEverMultiThreaded -> 55 | Any (TmpSpecial _) -> 56 | Any (IsAllocVar _) -> 57 + | Any (MaySignedOverflow _) -> 58 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -422,6 +426,7 @@ struct | Any (MayBeModifiedSinceSetjmp e) -> JmpBufDomain.BufferEntry.hash e | Any (MustBeSingleThreaded {since_start}) -> Hashtbl.hash since_start | Any (TmpSpecial lv) -> Mval.Exp.hash lv + | Any (MaySignedOverflow e) -> CilType.Exp.hash e (* IterSysVars: *) (* - argument is a function and functions cannot be compared in any meaningful way. *) (* - doesn't matter because IterSysVars is always queried from outside of the analysis, so MCP's query caching is not done for it. *) @@ -484,6 +489,7 @@ struct | Any MustTermAllLoops -> Pretty.dprintf "MustTermAllLoops" | Any IsEverMultiThreaded -> Pretty.dprintf "IsEverMultiThreaded" | Any (TmpSpecial lv) -> Pretty.dprintf "TmpSpecial %a" Mval.Exp.pretty lv + | Any (MaySignedOverflow e) -> Pretty.dprintf "MaySignedOverflow %a" CilType.Exp.pretty e end let to_value_domain_ask (ask: ask) = From 4136efe5fd005d0d06b59a1d0013564bd719cbf4 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 28 Jan 2024 10:09:10 +0100 Subject: [PATCH 145/280] remove all the old overflow analysis from affeq and lin2vareq and move bounds calculation to function eval_interval. Rename assert_const to assert_constraint for better readability --- .../apron/affineEqualityDomain.apron.ml | 133 ++++++++---------- .../apron/linearTwoVarEqualityDomain.apron.ml | 73 ++++------ src/cdomains/apron/sharedFunctions.apron.ml | 110 ++++----------- 3 files changed, 118 insertions(+), 198 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index c18bdec673..54a6b938a6 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -115,42 +115,13 @@ struct let get_coeff_vec t texp = timing_wrap "coeff_vec" (get_coeff_vec t) texp end -(** As it is specifically used for the new affine equality domain, it can only provide bounds if the expression contains known constants only and in that case, min and max are the same. *) -module ExpressionBounds (Vc: AbstractVector) (Mx: AbstractMatrix): (SharedFunctions.ExtendedConvBounds with type t = VarManagement(Vc) (Mx).t) = -struct - include VarManagement (Vc) (Mx) - - let bound_texpr t texpr = - let texpr = Texpr1.to_expr texpr in - match Option.bind (get_coeff_vec t texpr) to_constant_opt with - | Some c when Mpqf.get_den c = IntOps.BigIntOps.one -> - let int_val = Mpqf.get_num c in - Some int_val, Some int_val - | _ -> None, None - - - let bound_texpr d texpr1 = - let res = bound_texpr d texpr1 in - (if M.tracing then - match res with - | Some min, Some max -> M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string min) (IntOps.BigIntOps.to_string max) - | _ -> () - ); - res - - - let bound_texpr d texpr1 = timing_wrap "bounds calculation" (bound_texpr d) texpr1 -end - module D(Vc: AbstractVector) (Mx: AbstractMatrix) = struct include Printable.Std include ConvenienceOps (Mpqf) include VarManagement (Vc) (Mx) - module Bounds = ExpressionBounds (Vc) (Mx) - - module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (SharedFunctions.Tracked) + module Convert = SharedFunctions.Convert (V) (struct let allow_global = true end) (SharedFunctions.Tracked) type var = V.t @@ -205,6 +176,25 @@ struct let pretty () (x:t) = text (show x) let printXml f x = BatPrintf.fprintf f "\n\n\nmatrix\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) + let eval_interval t texpr = + let texpr = Texpr1.to_expr texpr in + match Option.bind (get_coeff_vec t texpr) to_constant_opt with + | Some c when Mpqf.get_den c = IntOps.BigIntOps.one -> + let int_val = Mpqf.get_num c in + Some int_val, Some int_val + | _ -> None, None + + let eval_interval d texpr1 = + let res = eval_interval d texpr1 in + (if M.tracing then + match res with + | Some min, Some max -> if M.tracing then M.tracel "eval_interval" "min: %s max: %s" (IntOps.BigIntOps.to_string min) (IntOps.BigIntOps.to_string max); + | _ -> () + ); + res + + let eval_interval d texpr1 = timing_wrap "eval_interval calculation" (eval_interval d) texpr1 + let name () = "affeq" let to_yojson _ = failwith "ToDo Implement in future" @@ -417,8 +407,7 @@ struct let assign_exp (t: VarManagement(Vc)(Mx).t) var exp (no_ov: bool Lazy.t) = let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in - (* TODO: Do we need to do a constant folding here? It happens for texpr1_of_cil_exp *) - match Convert.texpr1_expr_of_cil_exp t t.env exp (Lazy.force no_ov) with + match Convert.texpr1_expr_of_cil_exp t t.env exp no_ov with | exp -> assign_texpr t var exp | exception Convert.Unsupported_CilExp _ -> if is_bot t then t else forget_vars t [var] @@ -501,11 +490,10 @@ struct (** Assert a constraint expression. - Additionally, we now also refine after positive guards when overflows might occur and there is only one variable inside the expression and the expression is an equality constraint check (==). - We check after the refinement if the new value of the variable is outside its integer bounds and if that is the case, either revert to the old state or set it to bottom. *) - - - module BoundsCheck = SharedFunctions.BoundsCheckMeetTcons (Bounds) (V) + The overflow is completely handled by the flag "no_ov", + which is set in relationAnalysis.ml via the function no_overflow. + In case of a potential overflow, "no_ov" is set to false + and Convert.tcons1_of_cil_exp will raise the exception Unsupported_CilExp Overflow *) let meet_tcons t tcons expr = let check_const cmp c = if cmp c Mpqf.zero then bot_env else t in @@ -513,39 +501,35 @@ struct (* Flip the sign of the const. val in coeff vec *) let coeff = Vector.nth e (Vector.length e - 1) in Vector.set_val_with e (Vector.length e - 1) (Mpqf.neg coeff); - let res = - if is_bot t then - bot () - else - let opt_m = Matrix.rref_vec_with (Matrix.copy @@ Option.get t.d) e in - if Option.is_none opt_m then bot () else {d = opt_m; env = t.env} - in - BoundsCheck.meet_tcons_one_var_eq res expr + if is_bot t then + bot () + else + let opt_m = Matrix.rref_vec_with (Matrix.copy @@ Option.get t.d) e in + if Option.is_none opt_m then bot () else {d = opt_m; env = t.env} + in - try - match get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with - | Some v -> - begin match to_constant_opt v, Tcons1.get_typ tcons with - | Some c, DISEQ -> check_const (=:) c - | Some c, SUP -> check_const (<=:) c - | Some c, EQ -> check_const (<>:) c - | Some c, SUPEQ -> check_const (<:) c - | None, DISEQ - | None, SUP -> - if equal (meet_vec v) t then - bot_env - else - t - | None, EQ -> - let res = meet_vec v in - if is_bot res then - bot_env - else - res - | _ -> t - end - | None -> t - with BoundsCheck.NotRefinable -> t + match get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with + | Some v -> + begin match to_constant_opt v, Tcons1.get_typ tcons with + | Some c, DISEQ -> check_const (=:) c + | Some c, SUP -> check_const (<=:) c + | Some c, EQ -> check_const (<>:) c + | Some c, SUPEQ -> check_const (<:) c + | None, DISEQ + | None, SUP -> + if equal (meet_vec v) t then + bot_env + else + t + | None, EQ -> + let res = meet_vec v in + if is_bot res then + bot_env + else + res + | _ -> t + end + | None -> t let meet_tcons t tcons expr = timing_wrap "meet_tcons" (meet_tcons t tcons) expr @@ -557,14 +541,13 @@ struct if M.tracing then M.tracel "ops" "unify: %s %s -> %s\n" (show a) (show b) (show res); res - let assert_cons d e negate no_ov = - let no_ov = Lazy.force no_ov in - if M.tracing then M.tracel "assert_cons" "assert_cons with expr: %a %b" d_exp e no_ov; + let assert_constraint d e negate no_ov = + if M.tracing then M.tracel "assert_constraint" "assert_constraint with expr: %a %b" d_exp e (Lazy.force no_ov); match Convert.tcons1_of_cil_exp d d.env e negate no_ov with | tcons1 -> meet_tcons d tcons1 e | exception Convert.Unsupported_CilExp _ -> d - let assert_cons d e negate no_ov = timing_wrap "assert_cons" (assert_cons d e negate) no_ov + let assert_constraint d e negate no_ov = timing_wrap "assert_constraint" (assert_constraint d e negate) no_ov let relift t = t @@ -584,9 +567,9 @@ struct let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 - let env (t: Bounds.t) = t.env + let env t = t.env - type marshal = Bounds.t + type marshal = t let marshal t = t diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 26ad4c3f2c..2bfa613236 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -264,34 +264,13 @@ struct end -module ExpressionBounds: (SharedFunctions.ExtendedConvBounds with type t = VarManagement.t) = -struct - include VarManagement - - let bound_texpr t texpr = let texpr = Texpr1.to_expr texpr in - match get_coeff t texpr with - | Some (None, offset) -> Some offset, Some offset - | _ -> None, None - - - let bound_texpr d texpr1 = - let res = bound_texpr d texpr1 in - match res with - | Some min, Some max -> if M.tracing then M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string min) (IntOps.BigIntOps.to_string max); res - | _ -> res - - let bound_texpr d texpr1 = timing_wrap "bounds calculation" (bound_texpr d) texpr1 -end - module D = struct include Printable.Std include ConvenienceOps (Mpqf) include VarManagement - module Bounds = ExpressionBounds - - module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (SharedFunctions.Tracked) + module Convert = SharedFunctions.Convert (V) (struct let allow_global = true end) (SharedFunctions.Tracked) type var = V.t @@ -327,6 +306,20 @@ struct let pretty () (x:t) = text (show x) let printXml f x = BatPrintf.fprintf f "\n\n\nequalities-array\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) + + let eval_interval t texpr = let texpr = Texpr1.to_expr texpr in + match get_coeff t texpr with + | Some (None, offset) -> Some offset, Some offset + | _ -> None, None + + let eval_interval d texpr1 = + let res = eval_interval d texpr1 in + match res with + | Some min, Some max -> if M.tracing then M.tracel "eval_interval" "min: %s max: %s" (IntOps.BigIntOps.to_string min) (IntOps.BigIntOps.to_string max); res + | _ -> res + + let eval_interval d texpr1 = timing_wrap "eval_interval calculation" (eval_interval d) texpr1 + exception Contradiction let meet_with_one_conj_with ts i (var, b) = @@ -508,8 +501,8 @@ struct -> Convert.texpr1_expr_of_cil_exp handles overflow *) let assign_exp (t: VarManagement.t) var exp (no_ov: bool Lazy.t) = let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in - match Convert.texpr1_expr_of_cil_exp t t.env exp (Lazy.force no_ov) with - | exp -> assign_texpr t var exp + match Convert.texpr1_expr_of_cil_exp t t.env exp no_ov with + | texp -> assign_texpr t var texp | exception Convert.Unsupported_CilExp _ -> if is_bot_env t then t else forget_vars t [var] @@ -596,9 +589,10 @@ struct List.fold_righti (fun i k result -> show_element i k ^ "\n" ^ result) l "" (** Assert a constraint expression. - - Additionally, we now also refine after positive guards when overflows might occur and there is only one variable inside the expression and the expression is an equality constraint check (==). - We check after the refinement if the new value of the variable is outside its integer bounds and if that is the case, either revert to the old state or set it to bottom. *) + The overflow is completely handled by the flag "no_ov", + which is set in relationAnalysis.ml via the function no_overflow. + In case of a potential overflow, "no_ov" is set to false + and Convert.tcons1_of_cil_exp will raise the exception Unsupported_CilExp Overflow *) (* meet_tcons -> meet with guard in if statement texpr -> tree expr (right hand side of equality) @@ -606,19 +600,13 @@ struct tcons -> tree constraint (expression < 0) -> does not have types (overflow is type dependent) *) - module BoundsCheck = SharedFunctions.BoundsCheckMeetTcons (Bounds) (V) - let meet_tcons t tcons original_expr = + let meet_tcons t tcons original_expr no_ov = (* The expression is evaluated using an array of coefficients. The first element of the array belongs to the constant followed by the coefficients of all variables depending on the result in the array after the evaluating including resolving the constraints in t.d the tcons can be evaluated and additional constraints can be added to t.d *) match t.d with | None -> bot_env | Some d -> - let overflow_handling res original_expr = - match BoundsCheck.meet_tcons_one_var_eq res original_expr with - | exception BoundsCheck.NotRefinable -> t - | res -> res - in let expr = Array.init (Environment.size t.env) (fun _ -> Z.zero) in let constant = ref (Z.zero) in if is_bot_env t then bot_env else @@ -651,7 +639,7 @@ struct match Tcons1.get_typ tcons, c with | EQ, Some c -> let res = meet_with_one_conj t (fst var) (None, c) - in overflow_handling res original_expr + in res | _ -> t (*Not supported right now*) else if var_count = 2 then let get_vars i a l = if Z.equal a Z.zero then l else (i, a)::l in @@ -668,7 +656,7 @@ struct else if Z.equal a1 Z.(-one) && Z.equal a2 Z.one then meet_with_one_conj t var1 (Some var2, !constant) else t - in overflow_handling res original_expr + in res | _-> t (*Not supported right now*) else t (*For any other case we don't know if the (in-) equality is true or false or even possible therefore we just return t *) @@ -694,14 +682,13 @@ struct e.g. an if statement is encountered in the C code. *) - let assert_cons d e negate no_ov = - let no_ov = Lazy.force no_ov in - if M.tracing then M.tracel "assert_cons" "assert_cons with expr: %a %b\n" d_exp e no_ov; + let assert_constraint d e negate (no_ov: bool Lazy.t) = + if M.tracing then M.tracel "assert_constraint" "assert_constraint with expr: %a %b\n" d_exp e (Lazy.force no_ov); match Convert.tcons1_of_cil_exp d d.env e negate no_ov with - | tcons1 -> meet_tcons d tcons1 e + | tcons1 -> meet_tcons d tcons1 e no_ov | exception Convert.Unsupported_CilExp _ -> d - let assert_cons d e negate no_ov = timing_wrap "assert_cons" (assert_cons d e negate) no_ov + let assert_constraint d e negate no_ov = timing_wrap "assert_constraint" (assert_constraint d e negate) no_ov let relift t = t @@ -735,9 +722,9 @@ struct let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 - let env (t: Bounds.t) = t.env + let env t = t.env - type marshal = Bounds.t + type marshal = t (* marshal is not compatible with apron, therefore we don't have to implement it *) let marshal t = t diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 281f950f7c..a38699f363 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -57,15 +57,8 @@ type unsupported_cilExp = | BinOp_not_supported (** BinOp constructor not supported. *) [@@deriving show { with_path = false }] -(** Interface for Bounds which calculates bounds for expressions and is used inside the - Convert module. *) -module type ConvBounds = -sig - type t - val bound_texpr: t -> Texpr1.t -> Z.t option * Z.t option -end - (** Conversion from CIL expressions to Apron. *) -module ApronOfCil (V: SV) (Bounds: ConvBounds) (Arg: ConvertArg) (Tracked: RelationDomain.Tracked) = +module ApronOfCil (V: SV) (Arg: ConvertArg) (Tracked: RelationDomain.Tracked) = struct open Texpr1 open Tcons1 @@ -77,7 +70,7 @@ struct | _ -> None (* for other exception *) ) - let texpr1_expr_of_cil_exp d env exp no_ov = + let texpr1_expr_of_cil_exp _ env exp _ = (* recurse without env argument *) let rec texpr1_expr_of_cil_exp = function | Lval (Var v, NoOffset) when Tracked.varinfo_tracked v -> @@ -99,8 +92,7 @@ struct | exp -> match Cilfacade.get_ikind_exp exp with | ik -> - let expr = - match exp with + begin match exp with | UnOp (Neg, e, _) -> Unop (Neg, texpr1_expr_of_cil_exp e, Int, Near) | BinOp (PlusA, e1, e2, _) -> @@ -123,26 +115,22 @@ struct raise (Unsupported_CilExp (Cast_not_injective t)) end | _ -> - raise (Unsupported_CilExp Exp_not_supported) - in - if not no_ov then ( - let (type_min, type_max) = IntDomain.Size.range ik in - let texpr1 = Texpr1.of_expr env expr in - match Bounds.bound_texpr d texpr1 with - | Some min, Some max when BI.compare type_min min <= 0 && BI.compare max type_max <= 0 -> () - | min_opt, max_opt -> - if M.tracing then M.trace "apron" "may overflow: %a (%a, %a)\n" CilType.Exp.pretty exp (Pretty.docOpt (IntDomain.BigInt.pretty ())) min_opt (Pretty.docOpt (IntDomain.BigInt.pretty ())) max_opt; - raise (Unsupported_CilExp Overflow) - ); - expr + raise (Unsupported_CilExp Exp_not_supported) end | exception (Cilfacade.TypeOfError _ as e) | exception (Invalid_argument _ as e) -> raise (Unsupported_CilExp (Exp_typeOf e)) in texpr1_expr_of_cil_exp exp + let texpr1_expr_of_cil_exp d env exp no_ov = + let exp = Cil.constFold false exp in + if not @@ IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp exp) + && not (Lazy.force no_ov) then + (raise (Unsupported_CilExp Overflow)) + else + texpr1_expr_of_cil_exp d env exp no_ov + let texpr1_of_cil_exp d env e no_ov = - let e = Cil.constFold false e in Texpr1.of_expr env (texpr1_expr_of_cil_exp d env e no_ov) let tcons1_of_cil_exp d env e negate no_ov = @@ -249,9 +237,9 @@ struct end (** Conversion between CIL expressions and Apron. *) -module Convert (V: SV) (Bounds: ConvBounds) (Arg: ConvertArg) (Tracked: RelationDomain.Tracked)= +module Convert (V: SV) (Arg: ConvertArg) (Tracked: RelationDomain.Tracked)= struct - include ApronOfCil (V) (Bounds) (Arg) (Tracked) + include ApronOfCil (V) (Arg) (Tracked) include CilOfApron (V) end @@ -366,18 +354,18 @@ end -(** A more specific module type for RelationDomain.RelD2 with ConvBounds integrated and various apron elements. +(** A more specific module type for RelationDomain.RelD2 with various apron elements. It is designed to be the interface for the D2 modules in affineEqualityDomain and apronDomain and serves as a functor argument for AssertionModule. *) module type AssertionRelS = sig type t - module Bounds: ConvBounds with type t = t val is_bot_env: t -> bool val env: t -> Environment.t - val assert_cons: t -> exp -> bool -> bool Lazy.t -> t + val assert_constraint: t -> exp -> bool -> bool Lazy.t -> t + val eval_interval : t -> Texpr1.t -> Z.t option * Z.t option end module Tracked: RelationDomain.Tracked = @@ -401,28 +389,28 @@ struct type nonrec var = V.t module Tracked = Tracked - module Convert = Convert (V) (Bounds) (struct let allow_global = false end) (Tracked) + module Convert = Convert (V) (struct let allow_global = false end) (Tracked) - let rec exp_is_cons = function + let rec exp_is_constraint = function (* constraint *) | BinOp ((Lt | Gt | Le | Ge | Eq | Ne), _, _, _) -> true - | BinOp ((LAnd | LOr), e1, e2, _) -> exp_is_cons e1 && exp_is_cons e2 - | UnOp (LNot,e,_) -> exp_is_cons e + | BinOp ((LAnd | LOr), e1, e2, _) -> exp_is_constraint e1 && exp_is_constraint e2 + | UnOp (LNot,e,_) -> exp_is_constraint e (* expression *) | _ -> false - (* TODO: move logic-handling assert_cons from Apron back to here, after fixing affeq bot-bot join *) + (* TODO: move logic-handling assert_constraint from Apron back to here, after fixing affeq bot-bot join *) (** Assert any expression. *) let assert_inv d e negate no_ov = let e' = - if exp_is_cons e then + if exp_is_constraint e then e else (* convert non-constraint expression, such that we assert(e != 0) *) BinOp (Ne, e, zero, intType) in - assert_cons d e' negate no_ov + assert_constraint d e' negate no_ov let check_assert d e no_ov = if is_bot_env (assert_inv d e false no_ov) then @@ -433,10 +421,10 @@ struct `Top (** Evaluate non-constraint expression as interval. *) - let eval_interval_expr d e = - match Convert.texpr1_of_cil_exp d (env d) e false with (* why implicit false no_ov false here? *) + let eval_interval_expr d e no_ov = + match Convert.texpr1_of_cil_exp d (env d) e no_ov with | texpr1 -> - Bounds.bound_texpr d texpr1 + let c = eval_interval d texpr1 in c | exception Convert.Unsupported_CilExp _ -> (None, None) @@ -448,14 +436,14 @@ struct | exception Invalid_argument _ -> ID.top () (* real top, not a top of any ikind because we don't even know the ikind *) | ik -> - if M.tracing then M.trace "relation" "eval_int: exp_is_cons %a = %B\n" d_plainexp e (exp_is_cons e); - if exp_is_cons e then + if M.tracing then M.trace "relation" "eval_int: exp_is_constraint %a = %B\n" d_plainexp e (exp_is_constraint e); + if exp_is_constraint e then match check_assert d e no_ov with | `True -> ID.of_bool ik true | `False -> ID.of_bool ik false | `Top -> ID.top_of ik else - match eval_interval_expr d e with + match eval_interval_expr d e no_ov with | (Some min, Some max) -> ID.of_interval ~suppress_ovwarn:true ik (min, max) | (Some min, None) -> ID.starting ~suppress_ovwarn:true ik min | (None, Some max) -> ID.ending ~suppress_ovwarn:true ik max @@ -477,41 +465,3 @@ module Mpqf = struct let get_num x = Z_mlgmpidl.z_of_mpzf @@ Mpqf.get_num x let hash x = 31 * (Z.hash (get_den x)) + Z.hash (get_num x) end - - -(** Overflow handling for meet_tcons in affineEqualityDomain and linearTwoVarEqualityDomain. - - It refines after positive guards when overflows might occur and there is only one variable inside the expression and the expression is an equality constraint check (==). - We check after the refinement if the new value of the variable is outside its integer bounds and if that is the case, either raise the exception "NotRefinable" or set it to bottom. *) -module type ExtendedConvBounds = -sig - include ConvBounds - val get_env: t -> Environment.t - val bot : unit -> t -end -module BoundsCheckMeetTcons (Bounds: ExtendedConvBounds) (V: SV) = struct - exception NotRefinable - module Convert = Convert (V) (Bounds) (struct let allow_global = true end) (Tracked) - - let meet_tcons_one_var_eq res expr = - let overflow_res res = if IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp expr) then res else raise NotRefinable in - match Convert.find_one_var expr with - | None -> overflow_res res - | Some v -> - let ik = Cilfacade.get_ikind v.vtype in - if not (Cil.isSigned ik) then - raise NotRefinable - else - match Bounds.bound_texpr res (Convert.texpr1_of_cil_exp res (Bounds.get_env res) (Lval (Cil.var v)) true) with - | Some min, Some max -> - assert (Z.equal min max); (* other bounds impossible in affeq *) - let (min_ik, max_ik) = IntDomain.Size.range ik in - if Z.lt min min_ik || Z.gt max max_ik then - if IntDomain.should_ignore_overflow ik then - Bounds.bot () - else - raise NotRefinable - else res - | exception Convert.Unsupported_CilExp _ - | _ -> overflow_res res -end From f5a287fc38cecfaf8660536f7e1b912a0e3a5b5d Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 28 Jan 2024 10:10:56 +0100 Subject: [PATCH 146/280] move apron overflow analysis to apronDomain.apron.ml. Before it was in sharedFUnctions.ml, but given that I removed it from there, I had to put it back somewhere, because apronDomain also used that function. --- src/cdomains/apron/apronDomain.apron.ml | 101 ++++++++++++------------ 1 file changed, 49 insertions(+), 52 deletions(-) diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index 03b9558621..e66eed7a4a 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -144,7 +144,7 @@ sig val keep_filter_with : t -> (Var.t -> bool) -> unit val forget_vars_with : t -> Var.t list -> unit val assign_exp_with : t -> Var.t -> exp -> bool Lazy.t -> unit - val assign_exp_parallel_with : t -> (Var.t * exp) list -> bool -> unit (* TODO: why this one isn't lazy? *) + val assign_exp_parallel_with : t -> (Var.t * exp) list -> bool Lazy.t -> unit val assign_var_with : t -> Var.t -> Var.t -> unit val assign_var_parallel_with : t -> (Var.t * Var.t) list -> unit val substitute_exp_with : t -> Var.t -> exp -> bool Lazy.t-> unit @@ -235,8 +235,7 @@ end module AOps0 (Tracked: Tracked) (Man: Manager) = struct open SharedFunctions - module Bounds = Bounds (Man) - module Convert = Convert (V) (Bounds) (struct let allow_global = false end) (Tracked) + module Convert = Convert (V) (struct let allow_global = false end) (Tracked) type t = Man.mt A.t @@ -280,7 +279,7 @@ struct A.forget_array_with Man.mgr nd vs' false let assign_exp_with nd v e no_ov = - match Convert.texpr1_of_cil_exp nd (A.env nd) e (Lazy.force no_ov) with + match Convert.texpr1_of_cil_exp nd (A.env nd) e no_ov with | texpr1 -> if M.tracing then M.trace "apron" "assign_exp converted: %s\n" (Format.asprintf "%a" Texpr1.print texpr1); A.assign_texpr_with Man.mgr nd v texpr1 None @@ -347,7 +346,7 @@ struct A.assign_texpr_array Man.mgr d vs texpr1s None let substitute_exp_with nd v e no_ov = - match Convert.texpr1_of_cil_exp nd (A.env nd) e (Lazy.force no_ov) with + match Convert.texpr1_of_cil_exp nd (A.env nd) e no_ov with | texpr1 -> A.substitute_texpr_with Man.mgr nd v texpr1 None | exception Convert.Unsupported_CilExp _ -> @@ -361,7 +360,7 @@ struct ves |> List.enum |> Enum.map (Tuple2.map2 (fun e -> - match Convert.texpr1_of_cil_exp nd env e (Lazy.force no_ov) with + match Convert.texpr1_of_cil_exp nd env e no_ov with | texpr1 -> Some texpr1 | exception Convert.Unsupported_CilExp _ -> None )) @@ -388,31 +387,10 @@ struct let texpr1 = Texpr1.of_expr (A.env nd) (Var v') in A.substitute_texpr_with Man.mgr nd v texpr1 None - (** Special affeq one variable logic to match AffineEqualityDomain. *) - let meet_tcons_affeq_one_var d res e = - let overflow_res res = if IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp e) then res else d in - match Convert.find_one_var e with - | None -> overflow_res res - | Some v -> - let ik = Cilfacade.get_ikind v.vtype in - match Bounds.bound_texpr res (Convert.texpr1_of_cil_exp res res.env (Lval (Cil.var v)) true) with - | Some _, Some _ when not (Cil.isSigned ik) -> d (* TODO: unsigned w/o bounds handled differently? *) - | Some min, Some max -> - assert (Z.equal min max); (* other bounds impossible in affeq *) - let (min_ik, max_ik) = IntDomain.Size.range ik in - if Z.compare min min_ik < 0 || Z.compare max max_ik > 0 then - if IntDomain.should_ignore_overflow ik then A.bottom (A.manager d) (A.env d) else d - else res - (* TODO: Unsupported_CilExp check? *) - | _, _ -> overflow_res res - let meet_tcons d tcons1 e = let earray = Tcons1.array_make (A.env d) 1 in Tcons1.array_set earray 0 tcons1; - let res = A.meet_tcons_array Man.mgr d earray in - match Man.name () with - | "ApronAffEq" -> meet_tcons_affeq_one_var d res e (* TODO: don't hardcode by name, move to manager *) - | _ -> res + A.meet_tcons_array Man.mgr d earray let to_lincons_array d = A.to_lincons_array Man.mgr d @@ -514,50 +492,53 @@ struct include AOps (Tracked) (Man) include Tracked + module Bounds = Bounds(Man) + + let eval_interval = Bounds.bound_texpr + (** Assert a constraint expression. LAnd, LOr, LNot are directly supported by Apron domain in order to confirm logic-containing Apron invariants from witness while deep-query is disabled *) - let rec assert_cons d e negate (ov: bool Lazy.t) = - let no_ov = IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp e) in (* TODO: why ignores no_ov argument? *) + let rec assert_constraint d e negate (no_ov: bool Lazy.t) = match e with (* Apron doesn't properly meet with DISEQ constraints: https://github.com/antoinemine/apron/issues/37. Join Gt and Lt versions instead. *) | BinOp (Ne, lhs, rhs, intType) when not negate -> - let assert_gt = assert_cons d (BinOp (Gt, lhs, rhs, intType)) negate ov in - let assert_lt = assert_cons d (BinOp (Lt, lhs, rhs, intType)) negate ov in + let assert_gt = assert_constraint d (BinOp (Gt, lhs, rhs, intType)) negate no_ov in + let assert_lt = assert_constraint d (BinOp (Lt, lhs, rhs, intType)) negate no_ov in join assert_gt assert_lt | BinOp (Eq, lhs, rhs, intType) when negate -> - let assert_gt = assert_cons d (BinOp (Gt, lhs, rhs, intType)) (not negate) ov in - let assert_lt = assert_cons d (BinOp (Lt, lhs, rhs, intType)) (not negate) ov in + let assert_gt = assert_constraint d (BinOp (Gt, lhs, rhs, intType)) (not negate) no_ov in + let assert_lt = assert_constraint d (BinOp (Lt, lhs, rhs, intType)) (not negate) no_ov in join assert_gt assert_lt | BinOp (LAnd, lhs, rhs, intType) when not negate -> - let assert_l = assert_cons d lhs negate ov in - let assert_r = assert_cons d rhs negate ov in + let assert_l = assert_constraint d lhs negate no_ov in + let assert_r = assert_constraint d rhs negate no_ov in meet assert_l assert_r | BinOp (LAnd, lhs, rhs, intType) when negate -> - let assert_l = assert_cons d lhs negate ov in - let assert_r = assert_cons d rhs negate ov in + let assert_l = assert_constraint d lhs negate no_ov in + let assert_r = assert_constraint d rhs negate no_ov in join assert_l assert_r (* de Morgan *) | BinOp (LOr, lhs, rhs, intType) when not negate -> - let assert_l = assert_cons d lhs negate ov in - let assert_r = assert_cons d rhs negate ov in + let assert_l = assert_constraint d lhs negate no_ov in + let assert_r = assert_constraint d rhs negate no_ov in join assert_l assert_r | BinOp (LOr, lhs, rhs, intType) when negate -> - let assert_l = assert_cons d lhs negate ov in - let assert_r = assert_cons d rhs negate ov in + let assert_l = assert_constraint d lhs negate no_ov in + let assert_r = assert_constraint d rhs negate no_ov in meet assert_l assert_r (* de Morgan *) - | UnOp (LNot,e,_) -> assert_cons d e (not negate) ov + | UnOp (LNot,e,_) -> assert_constraint d e (not negate) no_ov | _ -> - begin match Convert.tcons1_of_cil_exp d (A.env d) e negate no_ov with + begin match Convert.tcons1_of_cil_exp d (A.env d) e negate no_ov with (*TODO*) | tcons1 -> - if M.tracing then M.trace "apron" "assert_cons %a %s\n" d_exp e (Format.asprintf "%a" Tcons1.print tcons1); - if M.tracing then M.trace "apron" "assert_cons st: %a\n" D.pretty d; + if M.tracing then M.trace "apron" "assert_constraint %a %s\n" d_exp e (Format.asprintf "%a" Tcons1.print tcons1); + if M.tracing then M.trace "apron" "assert_constraint st: %a\n" D.pretty d; let r = meet_tcons d tcons1 e in - if M.tracing then M.trace "apron" "assert_cons r: %a\n" D.pretty r; + if M.tracing then M.trace "apron" "assert_constraint r: %a\n" D.pretty r; r | exception Convert.Unsupported_CilExp reason -> - if M.tracing then M.trace "apron" "assert_cons %a unsupported: %s\n" d_exp e (SharedFunctions.show_unsupported_cilExp reason); + if M.tracing then M.trace "apron" "assert_constraint %a unsupported: %s\n" d_exp e (SharedFunctions.show_unsupported_cilExp reason); d end @@ -578,7 +559,7 @@ end module DHetero (Man: Manager): SLattice with type t = Man.mt A.t = struct include DBase (Man) - + module Bounds = Bounds(Man) let gce (x: Environment.t) (y: Environment.t): Environment.t = @@ -708,6 +689,21 @@ struct else false + let no_overflow_apron d env expr = + match Cilfacade.get_ikind_exp expr with + | ik -> + let (type_min, type_max) = IntDomain.Size.range ik in + let module Convert = SharedFunctions.Convert (V) (struct let allow_global = true end) (Tracked) in + let texpr = Convert.texpr1_of_cil_exp d env expr (Lazy.from_val true) in + begin match Bounds.bound_texpr d texpr with + | Some min, Some max when BI.compare type_min min <= 0 && BI.compare max type_max <= 0 + -> true + | min_opt, max_opt -> + if M.tracing then M.trace "apron" "may overflow: %a (%a, %a)\n" CilType.Exp.pretty expr (Pretty.docOpt (IntDomain.BigInt.pretty ())) min_opt (Pretty.docOpt (IntDomain.BigInt.pretty ())) max_opt; + false end + | exception (Cilfacade.TypeOfError _) + | exception (Invalid_argument _) -> false + let widen x y = let x_env = A.env x in let y_env = A.env y in @@ -723,11 +719,12 @@ struct ) else ( let exps = ResettableLazy.force WideningThresholds.exps in - let module Convert = SharedFunctions.Convert (V) (Bounds(Man)) (struct let allow_global = true end) (Tracked) in + let module Convert = SharedFunctions.Convert (V) (struct let allow_global = true end) (Tracked) in (* this implements widening_threshold with Tcons1 instead of Lincons1 *) let tcons1s = List.filter_map (fun e -> - let no_ov = IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp e) in - match Convert.tcons1_of_cil_exp y y_env e false no_ov with + let no_overflow e = lazy(no_overflow_apron y y_env e) + in + match Convert.tcons1_of_cil_exp y y_env e false (no_overflow e) with | tcons1 when A.sat_tcons Man.mgr y tcons1 -> Some tcons1 | _ From 80dc45f7dfda7687f009e90525145910d9e762ff Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 28 Jan 2024 10:12:23 +0100 Subject: [PATCH 147/280] fixed problem with affeq tests that was caused by 2 variables having the same name (it didn't even compile) --- tests/regression/63-affeq/02-unsigned_guards.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/regression/63-affeq/02-unsigned_guards.c b/tests/regression/63-affeq/02-unsigned_guards.c index 0f32ea9855..b5da331ec0 100644 --- a/tests/regression/63-affeq/02-unsigned_guards.c +++ b/tests/regression/63-affeq/02-unsigned_guards.c @@ -11,8 +11,10 @@ int main(){ __goblint_check(i == 3); // UNKNOWN! } - unsigned int i; - if (i - 2u == 4294967295u) { + unsigned int i2; + if (i2 - 2u == 4294967295u) { + printf("i2"); + printf("%u\n", i2); __goblint_check (i == 4294967297); // FAIL! } @@ -29,8 +31,8 @@ int main(){ __goblint_check(1); } - unsigned int x = 8; - if (x == 8u) { + unsigned int x2 = 8; + if (x2 == 8u) { __goblint_check(1); // reachable } return 0; From e19827f6cc810077f0ea5418eeecbab9fdee234f Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 28 Jan 2024 12:24:44 +0100 Subject: [PATCH 148/280] fixed MaySignedOverflow and apron overflow analysis and no_overflow for constraints and supported floating point variables in texpr1_expr_of_cil_expr --- src/analyses/apron/relationAnalysis.apron.ml | 10 +++- src/analyses/base.ml | 56 ++++++++++---------- src/cdomains/apron/apronDomain.apron.ml | 15 ++++-- src/cdomains/apron/sharedFunctions.apron.ml | 16 +++--- 4 files changed, 59 insertions(+), 38 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 8d06266164..6afdcfdb58 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -174,7 +174,7 @@ struct Since affine equalities can only keep track of integer bounds of expressions evaluating to definite constants, we now query the integer bounds information for expressions from other analysis. If an analysis returns bounds that are unequal to min and max of ikind , we can exclude the possibility that an overflow occurs and the abstract effect of the expression assignment can be used, i.e. we do not have to set the variable's value to top. *) - let no_overflow (ask: Queries.ask) exp = + let no_overflow_not_constraint (ask: Queries.ask) exp = match Cilfacade.get_ikind_exp exp with | exception Invalid_argument _ -> false | exception Cilfacade.TypeOfError _ -> false @@ -186,6 +186,14 @@ struct else not (ask.f (MaySignedOverflow exp)) + let rec no_overflow (ask: Queries.ask) exp = + match exp with + | BinOp ((Lt | Gt | Le | Ge | Eq | Ne), e1, e2, _) -> + no_overflow_not_constraint ask e1 && no_overflow_not_constraint ask e2 + | BinOp ((LAnd | LOr), e1, e2, _) -> no_overflow ask e1 && no_overflow ask e2 + | UnOp (LNot,e,_) -> no_overflow ask e + | exp -> no_overflow_not_constraint ask exp + let no_overflow ctx exp = lazy ( let res = no_overflow ctx exp in if M.tracing then M.tracel "no_ov" "no_ov %b exp: %a\n" res d_exp exp; diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 09638fa3e9..965f2c2a8d 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1220,33 +1220,33 @@ struct match Cilfacade.get_ikind_exp exp with | exception _ -> BoolDomain.MayBool.top () | ik -> - let r = match eval_rv_ask_evalint (Analyses.ask_of_ctx ctx) ctx.global ctx.local exp with - | Int i -> `Lifted i - | _ -> Queries.ID.top () - | exception (IntDomain.ArithmeticOnIntegerBot _) -> Queries.ID.top () in - if Cil.isSigned ik && Queries.ID.is_top_of ik r then - true (** if EvalInt returns top, there was probably an overflow. - Otherwise, to be sure that there is no overflow, we need to check each subexpression *) - else - match exp with - | Const _ - | SizeOf _ - | SizeOfStr _ - | AlignOf _ - | AddrOfLabel _ -> false - | Real e - | Imag e - | SizeOfE e - | AlignOfE e - | UnOp (_, e, _) - | CastE (_, e) -> exp_may_signed_overflow ctx e - | BinOp (_, e1, e2, _) -> + let exp_is_top = match eval_rv_ask_evalint (Analyses.ask_of_ctx ctx) ctx.global ctx.local exp with + | Int i ->Queries.ID.is_top_of ik (`Lifted i) + | _ -> true + | exception (IntDomain.ArithmeticOnIntegerBot _) -> true in + match exp with + | Const _ + | SizeOf _ + | SizeOfStr _ + | AlignOf _ + | AddrOfLabel _ -> false + | Real e + | Imag e + | SizeOfE e + | AlignOfE e + | CastE (_, e) -> exp_may_signed_overflow ctx e + | UnOp (_, e, _) -> + if Cil.isSigned ik && exp_is_top then true + (** if EvalInt returns top, there was probably an overflow. + Otherwise, to be sure that there is no overflow, we need to check each subexpression *) + else exp_may_signed_overflow ctx e + | BinOp (_, e1, e2, _) -> if Cil.isSigned ik && exp_is_top then true else exp_may_signed_overflow ctx e1 || exp_may_signed_overflow ctx e2 - | Question (e1, e2, e3, _) -> - exp_may_signed_overflow ctx e1 || exp_may_signed_overflow ctx e2 || exp_may_signed_overflow ctx e3 - | Lval lval - | AddrOf lval - | StartOf lval -> lval_may_signed_overflow ctx lval + | Question (e1, e2, e3, _) -> + exp_may_signed_overflow ctx e1 || exp_may_signed_overflow ctx e2 || exp_may_signed_overflow ctx e3 + | Lval lval + | AddrOf lval + | StartOf lval -> lval_may_signed_overflow ctx lval and lval_may_signed_overflow ctx (lval : lval) = let (host, offset) = lval in let host_may_signed_overflow = function @@ -1425,7 +1425,9 @@ struct | Q.InvariantGlobal g -> let g: V.t = Obj.obj g in query_invariant_global ctx g - | Q.MaySignedOverflow e -> exp_may_signed_overflow ctx e + | Q.MaySignedOverflow e -> (let res = exp_may_signed_overflow ctx e in + if M.tracing then M.traceli "signed_overflow" "base exp_may_signed_overflow %a. Result = %b\n" d_plainexp e res; res + ) | _ -> Q.Result.top q let update_variable variable typ value cpa = diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index e66eed7a4a..289a7d0fe1 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -689,11 +689,12 @@ struct else false + module Convert = SharedFunctions.Convert (V) (struct let allow_global = true end) (Tracked) + let no_overflow_apron d env expr = match Cilfacade.get_ikind_exp expr with | ik -> let (type_min, type_max) = IntDomain.Size.range ik in - let module Convert = SharedFunctions.Convert (V) (struct let allow_global = true end) (Tracked) in let texpr = Convert.texpr1_of_cil_exp d env expr (Lazy.from_val true) in begin match Bounds.bound_texpr d texpr with | Some min, Some max when BI.compare type_min min <= 0 && BI.compare max type_max <= 0 @@ -704,6 +705,15 @@ struct | exception (Cilfacade.TypeOfError _) | exception (Invalid_argument _) -> false + let no_overflow_apron_constraint d env expr = + match expr with + | BinOp (r, e1, e2, _) -> + (try (no_overflow_apron d env e1 && no_overflow_apron d env e2) + with (Convert.Unsupported_CilExp (Var_not_found v)) -> false) + | _ -> raise (Convert.Unsupported_CilExp Exp_not_supported) + + + let widen x y = let x_env = A.env x in let y_env = A.env y in @@ -719,10 +729,9 @@ struct ) else ( let exps = ResettableLazy.force WideningThresholds.exps in - let module Convert = SharedFunctions.Convert (V) (struct let allow_global = true end) (Tracked) in (* this implements widening_threshold with Tcons1 instead of Lincons1 *) let tcons1s = List.filter_map (fun e -> - let no_overflow e = lazy(no_overflow_apron y y_env e) + let no_overflow e = lazy(no_overflow_apron_constraint y y_env e) in match Convert.tcons1_of_cil_exp y y_env e false (no_overflow e) with | tcons1 when A.sat_tcons Man.mgr y tcons1 -> diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index a38699f363..7ad55b4a69 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -114,8 +114,8 @@ struct | exception Invalid_argument _ -> (* get_ikind in is_cast_injective *) raise (Unsupported_CilExp (Cast_not_injective t)) end - | _ -> - raise (Unsupported_CilExp Exp_not_supported) end + | _ -> (if M.tracing then M.trace "convert" "Unsupported_CilExp: %a\n" d_plainexp exp; + raise (Unsupported_CilExp Exp_not_supported)) end | exception (Cilfacade.TypeOfError _ as e) | exception (Invalid_argument _ as e) -> raise (Unsupported_CilExp (Exp_typeOf e)) @@ -123,12 +123,14 @@ struct texpr1_expr_of_cil_exp exp let texpr1_expr_of_cil_exp d env exp no_ov = + if M.tracing then M.trace "convert" "texpr1_expr_of_cil_exp: %a\n" d_plainexp exp; let exp = Cil.constFold false exp in - if not @@ IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp exp) - && not (Lazy.force no_ov) then - (raise (Unsupported_CilExp Overflow)) - else - texpr1_expr_of_cil_exp d env exp no_ov + begin try + if not @@ IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp exp) + && not (Lazy.force no_ov) then + (raise (Unsupported_CilExp Overflow)) + with Invalid_argument _ -> () end; + texpr1_expr_of_cil_exp d env exp no_ov let texpr1_of_cil_exp d env e no_ov = Texpr1.of_expr env (texpr1_expr_of_cil_exp d env e no_ov) From 813127b9a9b3378cb3168be55a58d6078e207452 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 28 Jan 2024 12:42:03 +0100 Subject: [PATCH 149/280] reverted all overflow handling of the apron domain to its original state, because the new overflow handling I implemented was not at all compatible with all the other apron domains, and I can't fix all the apron domains as well after having already fixed the overflow handling not only in our domain lin2vareq, but also in affine equalities --- src/cdomains/apron/apronDomain.apron.ml | 238 +++++++++++++++----- src/cdomains/apron/sharedFunctions.apron.ml | 6 +- 2 files changed, 189 insertions(+), 55 deletions(-) diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index 289a7d0fe1..10821d722a 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -144,7 +144,7 @@ sig val keep_filter_with : t -> (Var.t -> bool) -> unit val forget_vars_with : t -> Var.t list -> unit val assign_exp_with : t -> Var.t -> exp -> bool Lazy.t -> unit - val assign_exp_parallel_with : t -> (Var.t * exp) list -> bool Lazy.t -> unit + val assign_exp_parallel_with : t -> (Var.t * exp) list -> bool -> unit (* TODO: why this one isn't lazy? *) val assign_var_with : t -> Var.t -> Var.t -> unit val assign_var_parallel_with : t -> (Var.t * Var.t) list -> unit val substitute_exp_with : t -> Var.t -> exp -> bool Lazy.t-> unit @@ -231,11 +231,148 @@ sig end +(** Interface for Bounds which calculates bounds for expressions and is used inside the - Convert module. *) +module type ConvBounds = +sig + type t + val bound_texpr: t -> Texpr1.t -> Z.t option * Z.t option +end + +(** Conversion from CIL expressions to Apron used by the apron domains*) +module ApronOfCilForApronDomains (V: SV) (Bounds: ConvBounds) (Arg: ConvertArg) (Tracked: RelationDomain.Tracked) = +struct + open Texpr1 + open Tcons1 + + exception Unsupported_CilExp of unsupported_cilExp + + let () = Printexc.register_printer (function + | Unsupported_CilExp reason -> Some (show_unsupported_cilExp reason) + | _ -> None (* for other exception *) + ) + + let texpr1_expr_of_cil_exp d env exp no_ov = + (* recurse without env argument *) + let rec texpr1_expr_of_cil_exp = function + | Lval (Var v, NoOffset) when Tracked.varinfo_tracked v -> + if not v.vglob || Arg.allow_global then + let var = + if v.vglob then + V.global v + else + V.local v + in + if Environment.mem_var env var then + Var var + else + raise (Unsupported_CilExp (Var_not_found v)) + else + failwith "texpr1_expr_of_cil_exp: globals must be replaced with temporary locals" + | Const (CInt (i, _, _)) -> + Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z i))) + | exp -> + match Cilfacade.get_ikind_exp exp with + | ik -> + let expr = + match exp with + | UnOp (Neg, e, _) -> + Unop (Neg, texpr1_expr_of_cil_exp e, Int, Near) + | BinOp (PlusA, e1, e2, _) -> + Binop (Add, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) + | BinOp (MinusA, e1, e2, _) -> + Binop (Sub, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) + | BinOp (Mult, e1, e2, _) -> + Binop (Mul, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) + | BinOp (Div, e1, e2, _) -> + Binop (Div, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Zero) + | BinOp (Mod, e1, e2, _) -> + Binop (Mod, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) + | CastE (TInt (t_ik, _) as t, e) -> + begin match IntDomain.should_ignore_overflow t_ik || IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) + | true -> + Unop (Cast, texpr1_expr_of_cil_exp e, Int, Zero) (* TODO: what does Apron Cast actually do? just for floating point and rounding? *) + | false + | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) + | exception Invalid_argument _ -> (* get_ikind in is_cast_injective *) + raise (Unsupported_CilExp (Cast_not_injective t)) + end + | _ -> + raise (Unsupported_CilExp Exp_not_supported) + in + if not no_ov then ( + let (type_min, type_max) = IntDomain.Size.range ik in + let texpr1 = Texpr1.of_expr env expr in + match Bounds.bound_texpr d texpr1 with + | Some min, Some max when BI.compare type_min min <= 0 && BI.compare max type_max <= 0 -> () + | min_opt, max_opt -> + if M.tracing then M.trace "apron" "may overflow: %a (%a, %a)\n" CilType.Exp.pretty exp (Pretty.docOpt (IntDomain.BigInt.pretty ())) min_opt (Pretty.docOpt (IntDomain.BigInt.pretty ())) max_opt; + raise (Unsupported_CilExp Overflow) + ); + expr + | exception (Cilfacade.TypeOfError _ as e) + | exception (Invalid_argument _ as e) -> + raise (Unsupported_CilExp (Exp_typeOf e)) + in + texpr1_expr_of_cil_exp exp + + let texpr1_of_cil_exp d env e no_ov = + let e = Cil.constFold false e in + Texpr1.of_expr env (texpr1_expr_of_cil_exp d env e no_ov) + + let tcons1_of_cil_exp d env e negate no_ov = + let e = Cil.constFold false e in + let (texpr1_plus, texpr1_minus, typ) = + match e with + | BinOp (r, e1, e2, _) -> + let texpr1_1 = texpr1_expr_of_cil_exp d env e1 no_ov in + let texpr1_2 = texpr1_expr_of_cil_exp d env e2 no_ov in + (* Apron constraints always compare with 0 and only have comparisons one way *) + begin match r with + | Lt -> (texpr1_2, texpr1_1, SUP) (* e1 < e2 ==> e2 - e1 > 0 *) + | Gt -> (texpr1_1, texpr1_2, SUP) (* e1 > e2 ==> e1 - e2 > 0 *) + | Le -> (texpr1_2, texpr1_1, SUPEQ) (* e1 <= e2 ==> e2 - e1 >= 0 *) + | Ge -> (texpr1_1, texpr1_2, SUPEQ) (* e1 >= e2 ==> e1 - e2 >= 0 *) + | Eq -> (texpr1_1, texpr1_2, EQ) (* e1 == e2 ==> e1 - e2 == 0 *) + | Ne -> (texpr1_1, texpr1_2, DISEQ) (* e1 != e2 ==> e1 - e2 != 0 *) + | _ -> raise (Unsupported_CilExp BinOp_not_supported) + end + | _ -> raise (Unsupported_CilExp Exp_not_supported) + in + let inverse_typ = function + | EQ -> DISEQ + | DISEQ -> EQ + | SUPEQ -> SUP + | SUP -> SUPEQ + | EQMOD _ -> failwith "tcons1_of_cil_exp: cannot invert EQMOD" + in + let (texpr1_plus, texpr1_minus, typ) = + if negate then + (texpr1_minus, texpr1_plus, inverse_typ typ) + else + (texpr1_plus, texpr1_minus, typ) + in + let texpr1' = Binop (Sub, texpr1_plus, texpr1_minus, Int, Near) in + Tcons1.make (Texpr1.of_expr env texpr1') typ + + let find_one_var e = + Basetype.CilExp.get_vars e + |> List.filter Tracked.varinfo_tracked + |> function + | [v] -> Some v + | _ -> None +end + (** Convenience operations on A. *) module AOps0 (Tracked: Tracked) (Man: Manager) = struct open SharedFunctions - module Convert = Convert (V) (struct let allow_global = false end) (Tracked) + module Bounds = Bounds (Man) + module Convert = + struct + include ApronOfCilForApronDomains (V) (Bounds) (struct let allow_global = false end) (Tracked) + include CilOfApron (V) + end + type t = Man.mt A.t @@ -279,7 +416,7 @@ struct A.forget_array_with Man.mgr nd vs' false let assign_exp_with nd v e no_ov = - match Convert.texpr1_of_cil_exp nd (A.env nd) e no_ov with + match Convert.texpr1_of_cil_exp nd (A.env nd) e (Lazy.force no_ov) with | texpr1 -> if M.tracing then M.trace "apron" "assign_exp converted: %s\n" (Format.asprintf "%a" Texpr1.print texpr1); A.assign_texpr_with Man.mgr nd v texpr1 None @@ -346,7 +483,7 @@ struct A.assign_texpr_array Man.mgr d vs texpr1s None let substitute_exp_with nd v e no_ov = - match Convert.texpr1_of_cil_exp nd (A.env nd) e no_ov with + match Convert.texpr1_of_cil_exp nd (A.env nd) e (Lazy.force no_ov) with | texpr1 -> A.substitute_texpr_with Man.mgr nd v texpr1 None | exception Convert.Unsupported_CilExp _ -> @@ -360,7 +497,7 @@ struct ves |> List.enum |> Enum.map (Tuple2.map2 (fun e -> - match Convert.texpr1_of_cil_exp nd env e no_ov with + match Convert.texpr1_of_cil_exp nd env e (Lazy.force no_ov) with | texpr1 -> Some texpr1 | exception Convert.Unsupported_CilExp _ -> None )) @@ -387,10 +524,31 @@ struct let texpr1 = Texpr1.of_expr (A.env nd) (Var v') in A.substitute_texpr_with Man.mgr nd v texpr1 None + (** Special affeq one variable logic to match AffineEqualityDomain. *) + let meet_tcons_affeq_one_var d res e = + let overflow_res res = if IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp e) then res else d in + match Convert.find_one_var e with + | None -> overflow_res res + | Some v -> + let ik = Cilfacade.get_ikind v.vtype in + match Bounds.bound_texpr res (Convert.texpr1_of_cil_exp res res.env (Lval (Cil.var v)) true) with + | Some _, Some _ when not (Cil.isSigned ik) -> d (* TODO: unsigned w/o bounds handled differently? *) + | Some min, Some max -> + assert (Z.equal min max); (* other bounds impossible in affeq *) + let (min_ik, max_ik) = IntDomain.Size.range ik in + if Z.compare min min_ik < 0 || Z.compare max max_ik > 0 then + if IntDomain.should_ignore_overflow ik then A.bottom (A.manager d) (A.env d) else d + else res + (* TODO: Unsupported_CilExp check? *) + | _, _ -> overflow_res res + let meet_tcons d tcons1 e = let earray = Tcons1.array_make (A.env d) 1 in Tcons1.array_set earray 0 tcons1; - A.meet_tcons_array Man.mgr d earray + let res = A.meet_tcons_array Man.mgr d earray in + match Man.name () with + | "ApronAffEq" -> meet_tcons_affeq_one_var d res e (* TODO: don't hardcode by name, move to manager *) + | _ -> res let to_lincons_array d = A.to_lincons_array Man.mgr d @@ -491,46 +649,43 @@ struct include D include AOps (Tracked) (Man) include Tracked - - module Bounds = Bounds(Man) - let eval_interval = Bounds.bound_texpr - (** Assert a constraint expression. LAnd, LOr, LNot are directly supported by Apron domain in order to confirm logic-containing Apron invariants from witness while deep-query is disabled *) - let rec assert_constraint d e negate (no_ov: bool Lazy.t) = + let rec assert_constraint d e negate (ov: bool Lazy.t) = + let no_ov = IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp e) in (* TODO: why ignores no_ov argument? *) match e with (* Apron doesn't properly meet with DISEQ constraints: https://github.com/antoinemine/apron/issues/37. Join Gt and Lt versions instead. *) | BinOp (Ne, lhs, rhs, intType) when not negate -> - let assert_gt = assert_constraint d (BinOp (Gt, lhs, rhs, intType)) negate no_ov in - let assert_lt = assert_constraint d (BinOp (Lt, lhs, rhs, intType)) negate no_ov in + let assert_gt = assert_constraint d (BinOp (Gt, lhs, rhs, intType)) negate ov in + let assert_lt = assert_constraint d (BinOp (Lt, lhs, rhs, intType)) negate ov in join assert_gt assert_lt | BinOp (Eq, lhs, rhs, intType) when negate -> - let assert_gt = assert_constraint d (BinOp (Gt, lhs, rhs, intType)) (not negate) no_ov in - let assert_lt = assert_constraint d (BinOp (Lt, lhs, rhs, intType)) (not negate) no_ov in + let assert_gt = assert_constraint d (BinOp (Gt, lhs, rhs, intType)) (not negate) ov in + let assert_lt = assert_constraint d (BinOp (Lt, lhs, rhs, intType)) (not negate) ov in join assert_gt assert_lt | BinOp (LAnd, lhs, rhs, intType) when not negate -> - let assert_l = assert_constraint d lhs negate no_ov in - let assert_r = assert_constraint d rhs negate no_ov in + let assert_l = assert_constraint d lhs negate ov in + let assert_r = assert_constraint d rhs negate ov in meet assert_l assert_r | BinOp (LAnd, lhs, rhs, intType) when negate -> - let assert_l = assert_constraint d lhs negate no_ov in - let assert_r = assert_constraint d rhs negate no_ov in + let assert_l = assert_constraint d lhs negate ov in + let assert_r = assert_constraint d rhs negate ov in join assert_l assert_r (* de Morgan *) | BinOp (LOr, lhs, rhs, intType) when not negate -> - let assert_l = assert_constraint d lhs negate no_ov in - let assert_r = assert_constraint d rhs negate no_ov in + let assert_l = assert_constraint d lhs negate ov in + let assert_r = assert_constraint d rhs negate ov in join assert_l assert_r | BinOp (LOr, lhs, rhs, intType) when negate -> - let assert_l = assert_constraint d lhs negate no_ov in - let assert_r = assert_constraint d rhs negate no_ov in + let assert_l = assert_constraint d lhs negate ov in + let assert_r = assert_constraint d rhs negate ov in meet assert_l assert_r (* de Morgan *) - | UnOp (LNot,e,_) -> assert_constraint d e (not negate) no_ov + | UnOp (LNot,e,_) -> assert_constraint d e (not negate) ov | _ -> - begin match Convert.tcons1_of_cil_exp d (A.env d) e negate no_ov with (*TODO*) + begin match Convert.tcons1_of_cil_exp d (A.env d) e negate no_ov with | tcons1 -> if M.tracing then M.trace "apron" "assert_constraint %a %s\n" d_exp e (Format.asprintf "%a" Tcons1.print tcons1); if M.tracing then M.trace "apron" "assert_constraint st: %a\n" D.pretty d; @@ -559,7 +714,7 @@ end module DHetero (Man: Manager): SLattice with type t = Man.mt A.t = struct include DBase (Man) - module Bounds = Bounds(Man) + let gce (x: Environment.t) (y: Environment.t): Environment.t = @@ -689,31 +844,6 @@ struct else false - module Convert = SharedFunctions.Convert (V) (struct let allow_global = true end) (Tracked) - - let no_overflow_apron d env expr = - match Cilfacade.get_ikind_exp expr with - | ik -> - let (type_min, type_max) = IntDomain.Size.range ik in - let texpr = Convert.texpr1_of_cil_exp d env expr (Lazy.from_val true) in - begin match Bounds.bound_texpr d texpr with - | Some min, Some max when BI.compare type_min min <= 0 && BI.compare max type_max <= 0 - -> true - | min_opt, max_opt -> - if M.tracing then M.trace "apron" "may overflow: %a (%a, %a)\n" CilType.Exp.pretty expr (Pretty.docOpt (IntDomain.BigInt.pretty ())) min_opt (Pretty.docOpt (IntDomain.BigInt.pretty ())) max_opt; - false end - | exception (Cilfacade.TypeOfError _) - | exception (Invalid_argument _) -> false - - let no_overflow_apron_constraint d env expr = - match expr with - | BinOp (r, e1, e2, _) -> - (try (no_overflow_apron d env e1 && no_overflow_apron d env e2) - with (Convert.Unsupported_CilExp (Var_not_found v)) -> false) - | _ -> raise (Convert.Unsupported_CilExp Exp_not_supported) - - - let widen x y = let x_env = A.env x in let y_env = A.env y in @@ -729,11 +859,11 @@ struct ) else ( let exps = ResettableLazy.force WideningThresholds.exps in + let module Convert = ApronOfCilForApronDomains (V) (Bounds(Man)) (struct let allow_global = true end) (Tracked) in (* this implements widening_threshold with Tcons1 instead of Lincons1 *) let tcons1s = List.filter_map (fun e -> - let no_overflow e = lazy(no_overflow_apron_constraint y y_env e) - in - match Convert.tcons1_of_cil_exp y y_env e false (no_overflow e) with + let no_ov = IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp e) in + match Convert.tcons1_of_cil_exp y y_env e false no_ov with | tcons1 when A.sat_tcons Man.mgr y tcons1 -> Some tcons1 | _ diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 7ad55b4a69..d4c738f0fc 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -57,7 +57,11 @@ type unsupported_cilExp = | BinOp_not_supported (** BinOp constructor not supported. *) [@@deriving show { with_path = false }] -(** Conversion from CIL expressions to Apron. *) +(** Conversion from CIL expressions to Apron. + This is used by the domains "affine equalities" and "linear two variable equalities". + It also handles the overflow through the flag "no_ov". + For this reason it was divided from the Convert module for the pure apron domains "ApronOfCilForApronDomains", +*) module ApronOfCil (V: SV) (Arg: ConvertArg) (Tracked: RelationDomain.Tracked) = struct open Texpr1 From 46940b25f642973e6a3504c572bf7770de6fa414 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 29 Jan 2024 11:46:09 +0100 Subject: [PATCH 150/280] moved conversion to texpr of apron back to shared functions, because otherwise the apron domains were still using the new affeq conversion methods without the overflow check. Now the decision to use the old or the new conversion was moved to a module Ov that contains only a flag do_overflow_check. This fixes the termination tests not passing. --- .../apron/affineEqualityDomain.apron.ml | 52 +++-- src/cdomains/apron/apronDomain.apron.ml | 179 ++---------------- .../apron/linearTwoVarEqualityDomain.apron.ml | 39 ++-- src/cdomains/apron/sharedFunctions.apron.ml | 110 ++++++++++- 4 files changed, 171 insertions(+), 209 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 54a6b938a6..d6c5c2d522 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -115,13 +115,42 @@ struct let get_coeff_vec t texp = timing_wrap "coeff_vec" (get_coeff_vec t) texp end +(** As it is specifically used for the new affine equality domain, it can only provide bounds if the expression contains known constants only and in that case, min and max are the same. *) +module ExpressionBounds (Vc: AbstractVector) (Mx: AbstractMatrix): (SharedFunctions.ConvBounds with type t = VarManagement(Vc) (Mx).t) = +struct + include VarManagement (Vc) (Mx) + + let bound_texpr t texpr = + let texpr = Texpr1.to_expr texpr in + match Option.bind (get_coeff_vec t texpr) to_constant_opt with + | Some c when Mpqf.get_den c = IntOps.BigIntOps.one -> + let int_val = Mpqf.get_num c in + Some int_val, Some int_val + | _ -> None, None + + + let bound_texpr d texpr1 = + let res = bound_texpr d texpr1 in + (if M.tracing then + match res with + | Some min, Some max -> M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string min) (IntOps.BigIntOps.to_string max) + | _ -> () + ); + res + + + let bound_texpr d texpr1 = timing_wrap "bounds calculation" (bound_texpr d) texpr1 +end + module D(Vc: AbstractVector) (Mx: AbstractMatrix) = struct include Printable.Std include ConvenienceOps (Mpqf) include VarManagement (Vc) (Mx) - module Convert = SharedFunctions.Convert (V) (struct let allow_global = true end) (SharedFunctions.Tracked) + module Bounds = ExpressionBounds (Vc) (Mx) + + module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (struct let do_overflow_check = false end) (SharedFunctions.Tracked) type var = V.t @@ -176,24 +205,7 @@ struct let pretty () (x:t) = text (show x) let printXml f x = BatPrintf.fprintf f "\n\n\nmatrix\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) - let eval_interval t texpr = - let texpr = Texpr1.to_expr texpr in - match Option.bind (get_coeff_vec t texpr) to_constant_opt with - | Some c when Mpqf.get_den c = IntOps.BigIntOps.one -> - let int_val = Mpqf.get_num c in - Some int_val, Some int_val - | _ -> None, None - - let eval_interval d texpr1 = - let res = eval_interval d texpr1 in - (if M.tracing then - match res with - | Some min, Some max -> if M.tracing then M.tracel "eval_interval" "min: %s max: %s" (IntOps.BigIntOps.to_string min) (IntOps.BigIntOps.to_string max); - | _ -> () - ); - res - - let eval_interval d texpr1 = timing_wrap "eval_interval calculation" (eval_interval d) texpr1 + let eval_interval = Bounds.bound_texpr let name () = "affeq" @@ -579,6 +591,6 @@ end module D2(Vc: AbstractVector) (Mx: AbstractMatrix): RelationDomain.S3 with type var = Var.t = struct module D = D (Vc) (Mx) - include SharedFunctions.AssertionModule (V) (D) + include SharedFunctions.AssertionModule (V) (D) (struct let do_overflow_check = false end) include D end diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index 10821d722a..5752549602 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -230,148 +230,13 @@ sig include AOpsPure with type t := t end - -(** Interface for Bounds which calculates bounds for expressions and is used inside the - Convert module. *) -module type ConvBounds = -sig - type t - val bound_texpr: t -> Texpr1.t -> Z.t option * Z.t option -end - -(** Conversion from CIL expressions to Apron used by the apron domains*) -module ApronOfCilForApronDomains (V: SV) (Bounds: ConvBounds) (Arg: ConvertArg) (Tracked: RelationDomain.Tracked) = -struct - open Texpr1 - open Tcons1 - - exception Unsupported_CilExp of unsupported_cilExp - - let () = Printexc.register_printer (function - | Unsupported_CilExp reason -> Some (show_unsupported_cilExp reason) - | _ -> None (* for other exception *) - ) - - let texpr1_expr_of_cil_exp d env exp no_ov = - (* recurse without env argument *) - let rec texpr1_expr_of_cil_exp = function - | Lval (Var v, NoOffset) when Tracked.varinfo_tracked v -> - if not v.vglob || Arg.allow_global then - let var = - if v.vglob then - V.global v - else - V.local v - in - if Environment.mem_var env var then - Var var - else - raise (Unsupported_CilExp (Var_not_found v)) - else - failwith "texpr1_expr_of_cil_exp: globals must be replaced with temporary locals" - | Const (CInt (i, _, _)) -> - Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z i))) - | exp -> - match Cilfacade.get_ikind_exp exp with - | ik -> - let expr = - match exp with - | UnOp (Neg, e, _) -> - Unop (Neg, texpr1_expr_of_cil_exp e, Int, Near) - | BinOp (PlusA, e1, e2, _) -> - Binop (Add, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) - | BinOp (MinusA, e1, e2, _) -> - Binop (Sub, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) - | BinOp (Mult, e1, e2, _) -> - Binop (Mul, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) - | BinOp (Div, e1, e2, _) -> - Binop (Div, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Zero) - | BinOp (Mod, e1, e2, _) -> - Binop (Mod, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) - | CastE (TInt (t_ik, _) as t, e) -> - begin match IntDomain.should_ignore_overflow t_ik || IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) - | true -> - Unop (Cast, texpr1_expr_of_cil_exp e, Int, Zero) (* TODO: what does Apron Cast actually do? just for floating point and rounding? *) - | false - | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) - | exception Invalid_argument _ -> (* get_ikind in is_cast_injective *) - raise (Unsupported_CilExp (Cast_not_injective t)) - end - | _ -> - raise (Unsupported_CilExp Exp_not_supported) - in - if not no_ov then ( - let (type_min, type_max) = IntDomain.Size.range ik in - let texpr1 = Texpr1.of_expr env expr in - match Bounds.bound_texpr d texpr1 with - | Some min, Some max when BI.compare type_min min <= 0 && BI.compare max type_max <= 0 -> () - | min_opt, max_opt -> - if M.tracing then M.trace "apron" "may overflow: %a (%a, %a)\n" CilType.Exp.pretty exp (Pretty.docOpt (IntDomain.BigInt.pretty ())) min_opt (Pretty.docOpt (IntDomain.BigInt.pretty ())) max_opt; - raise (Unsupported_CilExp Overflow) - ); - expr - | exception (Cilfacade.TypeOfError _ as e) - | exception (Invalid_argument _ as e) -> - raise (Unsupported_CilExp (Exp_typeOf e)) - in - texpr1_expr_of_cil_exp exp - - let texpr1_of_cil_exp d env e no_ov = - let e = Cil.constFold false e in - Texpr1.of_expr env (texpr1_expr_of_cil_exp d env e no_ov) - - let tcons1_of_cil_exp d env e negate no_ov = - let e = Cil.constFold false e in - let (texpr1_plus, texpr1_minus, typ) = - match e with - | BinOp (r, e1, e2, _) -> - let texpr1_1 = texpr1_expr_of_cil_exp d env e1 no_ov in - let texpr1_2 = texpr1_expr_of_cil_exp d env e2 no_ov in - (* Apron constraints always compare with 0 and only have comparisons one way *) - begin match r with - | Lt -> (texpr1_2, texpr1_1, SUP) (* e1 < e2 ==> e2 - e1 > 0 *) - | Gt -> (texpr1_1, texpr1_2, SUP) (* e1 > e2 ==> e1 - e2 > 0 *) - | Le -> (texpr1_2, texpr1_1, SUPEQ) (* e1 <= e2 ==> e2 - e1 >= 0 *) - | Ge -> (texpr1_1, texpr1_2, SUPEQ) (* e1 >= e2 ==> e1 - e2 >= 0 *) - | Eq -> (texpr1_1, texpr1_2, EQ) (* e1 == e2 ==> e1 - e2 == 0 *) - | Ne -> (texpr1_1, texpr1_2, DISEQ) (* e1 != e2 ==> e1 - e2 != 0 *) - | _ -> raise (Unsupported_CilExp BinOp_not_supported) - end - | _ -> raise (Unsupported_CilExp Exp_not_supported) - in - let inverse_typ = function - | EQ -> DISEQ - | DISEQ -> EQ - | SUPEQ -> SUP - | SUP -> SUPEQ - | EQMOD _ -> failwith "tcons1_of_cil_exp: cannot invert EQMOD" - in - let (texpr1_plus, texpr1_minus, typ) = - if negate then - (texpr1_minus, texpr1_plus, inverse_typ typ) - else - (texpr1_plus, texpr1_minus, typ) - in - let texpr1' = Binop (Sub, texpr1_plus, texpr1_minus, Int, Near) in - Tcons1.make (Texpr1.of_expr env texpr1') typ - - let find_one_var e = - Basetype.CilExp.get_vars e - |> List.filter Tracked.varinfo_tracked - |> function - | [v] -> Some v - | _ -> None -end - (** Convenience operations on A. *) module AOps0 (Tracked: Tracked) (Man: Manager) = struct open SharedFunctions module Bounds = Bounds (Man) - module Convert = - struct - include ApronOfCilForApronDomains (V) (Bounds) (struct let allow_global = false end) (Tracked) - include CilOfApron (V) - end + module Convert = Convert (V) (Bounds) (struct let allow_global = false end) (struct let do_overflow_check = true end) (Tracked) + type t = Man.mt A.t @@ -416,7 +281,7 @@ struct A.forget_array_with Man.mgr nd vs' false let assign_exp_with nd v e no_ov = - match Convert.texpr1_of_cil_exp nd (A.env nd) e (Lazy.force no_ov) with + match Convert.texpr1_of_cil_exp nd (A.env nd) e no_ov with | texpr1 -> if M.tracing then M.trace "apron" "assign_exp converted: %s\n" (Format.asprintf "%a" Texpr1.print texpr1); A.assign_texpr_with Man.mgr nd v texpr1 None @@ -432,7 +297,7 @@ struct ves |> List.enum |> Enum.map (Tuple2.map2 (fun e -> - match Convert.texpr1_of_cil_exp nd env e no_ov with + match Convert.texpr1_of_cil_exp nd env e (Lazy.from_val no_ov) with | texpr1 -> Some texpr1 | exception Convert.Unsupported_CilExp _ -> None )) @@ -483,7 +348,7 @@ struct A.assign_texpr_array Man.mgr d vs texpr1s None let substitute_exp_with nd v e no_ov = - match Convert.texpr1_of_cil_exp nd (A.env nd) e (Lazy.force no_ov) with + match Convert.texpr1_of_cil_exp nd (A.env nd) e no_ov with | texpr1 -> A.substitute_texpr_with Man.mgr nd v texpr1 None | exception Convert.Unsupported_CilExp _ -> @@ -497,7 +362,7 @@ struct ves |> List.enum |> Enum.map (Tuple2.map2 (fun e -> - match Convert.texpr1_of_cil_exp nd env e (Lazy.force no_ov) with + match Convert.texpr1_of_cil_exp nd env e no_ov with | texpr1 -> Some texpr1 | exception Convert.Unsupported_CilExp _ -> None )) @@ -524,31 +389,10 @@ struct let texpr1 = Texpr1.of_expr (A.env nd) (Var v') in A.substitute_texpr_with Man.mgr nd v texpr1 None - (** Special affeq one variable logic to match AffineEqualityDomain. *) - let meet_tcons_affeq_one_var d res e = - let overflow_res res = if IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp e) then res else d in - match Convert.find_one_var e with - | None -> overflow_res res - | Some v -> - let ik = Cilfacade.get_ikind v.vtype in - match Bounds.bound_texpr res (Convert.texpr1_of_cil_exp res res.env (Lval (Cil.var v)) true) with - | Some _, Some _ when not (Cil.isSigned ik) -> d (* TODO: unsigned w/o bounds handled differently? *) - | Some min, Some max -> - assert (Z.equal min max); (* other bounds impossible in affeq *) - let (min_ik, max_ik) = IntDomain.Size.range ik in - if Z.compare min min_ik < 0 || Z.compare max max_ik > 0 then - if IntDomain.should_ignore_overflow ik then A.bottom (A.manager d) (A.env d) else d - else res - (* TODO: Unsupported_CilExp check? *) - | _, _ -> overflow_res res - let meet_tcons d tcons1 e = let earray = Tcons1.array_make (A.env d) 1 in Tcons1.array_set earray 0 tcons1; - let res = A.meet_tcons_array Man.mgr d earray in - match Man.name () with - | "ApronAffEq" -> meet_tcons_affeq_one_var d res e (* TODO: don't hardcode by name, move to manager *) - | _ -> res + A.meet_tcons_array Man.mgr d earray let to_lincons_array d = A.to_lincons_array Man.mgr d @@ -655,6 +499,7 @@ struct LAnd, LOr, LNot are directly supported by Apron domain in order to confirm logic-containing Apron invariants from witness while deep-query is disabled *) let rec assert_constraint d e negate (ov: bool Lazy.t) = + if M.tracing then M.trace "assert_constraint_apron" "%a ;;; %a\n" d_exp e d_plainexp e; let no_ov = IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp e) in (* TODO: why ignores no_ov argument? *) match e with (* Apron doesn't properly meet with DISEQ constraints: https://github.com/antoinemine/apron/issues/37. @@ -685,7 +530,7 @@ struct meet assert_l assert_r (* de Morgan *) | UnOp (LNot,e,_) -> assert_constraint d e (not negate) ov | _ -> - begin match Convert.tcons1_of_cil_exp d (A.env d) e negate no_ov with + begin match Convert.tcons1_of_cil_exp d (A.env d) e negate (Lazy.from_val no_ov) with | tcons1 -> if M.tracing then M.trace "apron" "assert_constraint %a %s\n" d_exp e (Format.asprintf "%a" Tcons1.print tcons1); if M.tracing then M.trace "apron" "assert_constraint st: %a\n" D.pretty d; @@ -859,10 +704,10 @@ struct ) else ( let exps = ResettableLazy.force WideningThresholds.exps in - let module Convert = ApronOfCilForApronDomains (V) (Bounds(Man)) (struct let allow_global = true end) (Tracked) in + let module Convert = SharedFunctions.Convert (V) (Bounds(Man)) (struct let allow_global = true end) (struct let do_overflow_check = true end)(Tracked) in (* this implements widening_threshold with Tcons1 instead of Lincons1 *) let tcons1s = List.filter_map (fun e -> - let no_ov = IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp e) in + let no_ov = lazy(IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp e)) in match Convert.tcons1_of_cil_exp y y_env e false no_ov with | tcons1 when A.sat_tcons Man.mgr y tcons1 -> Some tcons1 @@ -928,7 +773,7 @@ end module D (Man: Manager)= struct module DWO = DWithOps (Man) (DHetero (Man)) - include SharedFunctions.AssertionModule (V) (DWO) + include SharedFunctions.AssertionModule (V) (DWO) (struct let do_overflow_check = true end) include DWO module Tracked = Tracked module Man = Man diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 2bfa613236..0192fbebcf 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -264,13 +264,35 @@ struct end + +module ExpressionBounds: (SharedFunctions.ConvBounds with type t = VarManagement.t) = +struct + include VarManagement + + let bound_texpr t texpr = let texpr = Texpr1.to_expr texpr in + match get_coeff t texpr with + | Some (None, offset) -> Some offset, Some offset + | _ -> None, None + + + let bound_texpr d texpr1 = + let res = bound_texpr d texpr1 in + match res with + | Some min, Some max -> if M.tracing then M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string min) (IntOps.BigIntOps.to_string max); res + | _ -> res + + let bound_texpr d texpr1 = timing_wrap "bounds calculation" (bound_texpr d) texpr1 +end + module D = struct include Printable.Std include ConvenienceOps (Mpqf) include VarManagement - module Convert = SharedFunctions.Convert (V) (struct let allow_global = true end) (SharedFunctions.Tracked) + module Bounds = ExpressionBounds + + module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (struct let do_overflow_check = false end) (SharedFunctions.Tracked) type var = V.t @@ -307,18 +329,7 @@ struct let printXml f x = BatPrintf.fprintf f "\n\n\nequalities-array\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) - let eval_interval t texpr = let texpr = Texpr1.to_expr texpr in - match get_coeff t texpr with - | Some (None, offset) -> Some offset, Some offset - | _ -> None, None - - let eval_interval d texpr1 = - let res = eval_interval d texpr1 in - match res with - | Some min, Some max -> if M.tracing then M.tracel "eval_interval" "min: %s max: %s" (IntOps.BigIntOps.to_string min) (IntOps.BigIntOps.to_string max); res - | _ -> res - - let eval_interval d texpr1 = timing_wrap "eval_interval calculation" (eval_interval d) texpr1 + let eval_interval = Bounds.bound_texpr exception Contradiction @@ -735,6 +746,6 @@ end module D2: RelationDomain.S3 with type var = Var.t = struct module D = D - include SharedFunctions.AssertionModule (V) (D) + include SharedFunctions.AssertionModule (V) (D) (struct let do_overflow_check = false end) include D end diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index d4c738f0fc..6dcf5c216a 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -46,6 +46,11 @@ sig val allow_global: bool end +module type OverflowCheck = +sig + val do_overflow_check: bool +end + module type SV = RelationDomain.RV with type t = Var.t type unsupported_cilExp = @@ -57,12 +62,20 @@ type unsupported_cilExp = | BinOp_not_supported (** BinOp constructor not supported. *) [@@deriving show { with_path = false }] + +(** Interface for Bounds which calculates bounds for expressions and is used inside the - Convert module. *) +module type ConvBounds = +sig + type t + val bound_texpr: t -> Texpr1.t -> Z.t option * Z.t option +end + (** Conversion from CIL expressions to Apron. This is used by the domains "affine equalities" and "linear two variable equalities". It also handles the overflow through the flag "no_ov". For this reason it was divided from the Convert module for the pure apron domains "ApronOfCilForApronDomains", *) -module ApronOfCil (V: SV) (Arg: ConvertArg) (Tracked: RelationDomain.Tracked) = +module ApronOfCil (V: SV) (Bounds: ConvBounds) (Arg: ConvertArg) (Ov: OverflowCheck) (Tracked: RelationDomain.Tracked) = struct open Texpr1 open Tcons1 @@ -74,7 +87,79 @@ struct | _ -> None (* for other exception *) ) - let texpr1_expr_of_cil_exp _ env exp _ = + (** This version with an overflow check is used by the apron domains such as polyhedra and octagons. + They do the overflow handling while they convert the expression to Texpr1. *) + let texpr1_expr_of_cil_exp_with_overflow_check d env exp no_ov = + (* recurse without env argument *) + let rec texpr1_expr_of_cil_exp = function + | Lval (Var v, NoOffset) when Tracked.varinfo_tracked v -> + if not v.vglob || Arg.allow_global then + let var = + if v.vglob then + V.global v + else + V.local v + in + if Environment.mem_var env var then + Var var + else + raise (Unsupported_CilExp (Var_not_found v)) + else + failwith "texpr1_expr_of_cil_exp: globals must be replaced with temporary locals" + | Const (CInt (i, _, _)) -> + Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z i))) + | exp -> + match Cilfacade.get_ikind_exp exp with + | ik -> + let expr = + match exp with + | UnOp (Neg, e, _) -> + Unop (Neg, texpr1_expr_of_cil_exp e, Int, Near) + | BinOp (PlusA, e1, e2, _) -> + Binop (Add, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) + | BinOp (MinusA, e1, e2, _) -> + Binop (Sub, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) + | BinOp (Mult, e1, e2, _) -> + Binop (Mul, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) + | BinOp (Div, e1, e2, _) -> + Binop (Div, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Zero) + | BinOp (Mod, e1, e2, _) -> + Binop (Mod, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) + | CastE (TInt (t_ik, _) as t, e) -> + begin match IntDomain.should_ignore_overflow t_ik || IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) + | true -> + Unop (Cast, texpr1_expr_of_cil_exp e, Int, Zero) (* TODO: what does Apron Cast actually do? just for floating point and rounding? *) + | false + | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) + | exception Invalid_argument _ -> (* get_ikind in is_cast_injective *) + raise (Unsupported_CilExp (Cast_not_injective t)) + end + | _ -> + raise (Unsupported_CilExp Exp_not_supported) + in + if not no_ov then ( + let (type_min, type_max) = IntDomain.Size.range ik in + let texpr1 = Texpr1.of_expr env expr in + match Bounds.bound_texpr d texpr1 with + | Some min, Some max when BI.compare type_min min <= 0 && BI.compare max type_max <= 0 -> () + | min_opt, max_opt -> + if M.tracing then M.trace "apron" "may overflow: %a (%a, %a)\n" CilType.Exp.pretty exp (Pretty.docOpt (IntDomain.BigInt.pretty ())) min_opt (Pretty.docOpt (IntDomain.BigInt.pretty ())) max_opt; + raise (Unsupported_CilExp Overflow) + ); + expr + | exception (Cilfacade.TypeOfError _ as e) + | exception (Invalid_argument _ as e) -> + raise (Unsupported_CilExp (Exp_typeOf e)) + in + texpr1_expr_of_cil_exp exp + + let texpr1_of_cil_exp_with_overflow_check d env e no_ov = + let e = Cil.constFold false e in + Texpr1.of_expr env (texpr1_expr_of_cil_exp_with_overflow_check d env e no_ov) + + (** This version without an overflow check is used by the affeq and lin2vareq domains. + They do the overflow handling before converting to Texpr1, and store the result in the "no_ov" flag. *) + let texpr1_expr_of_cil_exp_no_ov env exp = (* recurse without env argument *) let rec texpr1_expr_of_cil_exp = function | Lval (Var v, NoOffset) when Tracked.varinfo_tracked v -> @@ -126,7 +211,7 @@ struct in texpr1_expr_of_cil_exp exp - let texpr1_expr_of_cil_exp d env exp no_ov = + let texpr1_expr_of_cil_exp env exp no_ov = if M.tracing then M.trace "convert" "texpr1_expr_of_cil_exp: %a\n" d_plainexp exp; let exp = Cil.constFold false exp in begin try @@ -134,11 +219,19 @@ struct && not (Lazy.force no_ov) then (raise (Unsupported_CilExp Overflow)) with Invalid_argument _ -> () end; - texpr1_expr_of_cil_exp d env exp no_ov + texpr1_expr_of_cil_exp_no_ov env exp + + let texpr1_expr_of_cil_exp d env exp no_ov = + if Ov.do_overflow_check then texpr1_expr_of_cil_exp_with_overflow_check d env exp (Lazy.force no_ov) + else texpr1_expr_of_cil_exp env exp no_ov let texpr1_of_cil_exp d env e no_ov = Texpr1.of_expr env (texpr1_expr_of_cil_exp d env e no_ov) + let texpr1_of_cil_exp d env e no_ov = + if Ov.do_overflow_check then texpr1_of_cil_exp_with_overflow_check d env e (Lazy.force no_ov) + else texpr1_of_cil_exp d env e no_ov + let tcons1_of_cil_exp d env e negate no_ov = let e = Cil.constFold false e in let (texpr1_plus, texpr1_minus, typ) = @@ -243,9 +336,9 @@ struct end (** Conversion between CIL expressions and Apron. *) -module Convert (V: SV) (Arg: ConvertArg) (Tracked: RelationDomain.Tracked)= +module Convert (V: SV) (Bounds: ConvBounds) (Arg: ConvertArg) (Ov: OverflowCheck) (Tracked: RelationDomain.Tracked)= struct - include ApronOfCil (V) (Arg) (Tracked) + include ApronOfCil (V) (Bounds) (Arg) (Ov: OverflowCheck) (Tracked) include CilOfApron (V) end @@ -366,6 +459,7 @@ module type AssertionRelS = sig type t + module Bounds: ConvBounds with type t = t val is_bot_env: t -> bool val env: t -> Environment.t @@ -389,13 +483,13 @@ struct type_tracked vi.vtype && (not @@ GobConfig.get_bool "annotation.goblint_relation_track" || hasTrackAttribute vi.vattr) end -module AssertionModule (V: SV) (AD: AssertionRelS) = +module AssertionModule (V: SV) (AD: AssertionRelS) (Ov: OverflowCheck) = struct include AD type nonrec var = V.t module Tracked = Tracked - module Convert = Convert (V) (struct let allow_global = false end) (Tracked) + module Convert = Convert (V) (Bounds) (struct let allow_global = false end) (Ov) (Tracked) let rec exp_is_constraint = function (* constraint *) From 760baa3c4620eaff45068a0250ed847d234571d1 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Mon, 29 Jan 2024 12:59:00 +0100 Subject: [PATCH 151/280] narrowing and casting tests --- .../77-lin2vareq/23-function_call2.c | 33 +++++++------------ .../77-lin2vareq/26-function_call3.c | 21 ------------ tests/regression/77-lin2vareq/26-macro.c | 23 +++++++++++++ tests/regression/77-lin2vareq/28-narrowing.c | 26 +++++++++++++++ tests/regression/77-lin2vareq/28-widening.c | 18 ---------- tests/regression/77-lin2vareq/29-arrowing.c | 18 ---------- .../77-lin2vareq/29-different_types.c | 31 +++++++++++++++++ 7 files changed, 91 insertions(+), 79 deletions(-) delete mode 100644 tests/regression/77-lin2vareq/26-function_call3.c create mode 100644 tests/regression/77-lin2vareq/26-macro.c create mode 100644 tests/regression/77-lin2vareq/28-narrowing.c delete mode 100644 tests/regression/77-lin2vareq/28-widening.c delete mode 100644 tests/regression/77-lin2vareq/29-arrowing.c create mode 100644 tests/regression/77-lin2vareq/29-different_types.c diff --git a/tests/regression/77-lin2vareq/23-function_call2.c b/tests/regression/77-lin2vareq/23-function_call2.c index ac174b54cb..f406862b74 100644 --- a/tests/regression/77-lin2vareq/23-function_call2.c +++ b/tests/regression/77-lin2vareq/23-function_call2.c @@ -1,32 +1,21 @@ // SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none -int myfunction(int x, int y){ - __goblint_check(x < y); +#include - if (x == 0) { - __goblint_check(x == 0); // SUCCESS - } else if (y - x == 3) { - __goblint_check(y == x + 0); // FAILURE - __goblint_check(y - x == 3); // SUCCESS - } - - return 5; +int check_equal(int x, int y, int z) { + __goblint_check(x == y); + __goblint_check(z == y); + __goblint_check(x == z); + return 8; } int main(void) { - int x, y, z; - - x = 0; - y = 5; - z = myfunction(x, y); + int x, y, z; - x = 2; - y = 5; - z = myfunction(x, y); + y = x; + z = y; - x = 1; - y = 3; - z = myfunction(x, y); + check_equal(x, y, z); - return 0; + return 0; } diff --git a/tests/regression/77-lin2vareq/26-function_call3.c b/tests/regression/77-lin2vareq/26-function_call3.c deleted file mode 100644 index f406862b74..0000000000 --- a/tests/regression/77-lin2vareq/26-function_call3.c +++ /dev/null @@ -1,21 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none - -#include - -int check_equal(int x, int y, int z) { - __goblint_check(x == y); - __goblint_check(z == y); - __goblint_check(x == z); - return 8; -} - -int main(void) { - int x, y, z; - - y = x; - z = y; - - check_equal(x, y, z); - - return 0; -} diff --git a/tests/regression/77-lin2vareq/26-macro.c b/tests/regression/77-lin2vareq/26-macro.c new file mode 100644 index 0000000000..24d4f98333 --- /dev/null +++ b/tests/regression/77-lin2vareq/26-macro.c @@ -0,0 +1,23 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq + +#include +#define LEN 32 +#define add(x,y) (x+y) +#define multiply(x,y) ((x) * (y)) +#define subtract(x,y) ((x) - (y)) + +int main(){ + int z = 32; + int a = 10; + int b = 20; + int result = add(a,b); + int result2 = multiply(a,b); + int result3 = subtract(a,b); + + __goblint_check(z == LEN); // SUCCESS + __goblint_check(result == 30); // SUCCESS + __goblint_check(result2 == 200); // SUCCESS + __goblint_check(result3 == -10); // SUCCESS + + return 0; +} diff --git a/tests/regression/77-lin2vareq/28-narrowing.c b/tests/regression/77-lin2vareq/28-narrowing.c new file mode 100644 index 0000000000..7e4b5a246e --- /dev/null +++ b/tests/regression/77-lin2vareq/28-narrowing.c @@ -0,0 +1,26 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq +#include + +int g = 0; + +int main() { + int x; + + for (x = 0; x < 50; x++) { + g = 1; + } + + if (x > 50) { + + for (int i = 0; i <= 0; i--) { + g = 57; + + int y; + + for (y = 0; y < x; y++) { + g = 42; + } + } + assert(1); // NOWARN (unreachable) + } +} diff --git a/tests/regression/77-lin2vareq/28-widening.c b/tests/regression/77-lin2vareq/28-widening.c deleted file mode 100644 index 275bd7bc73..0000000000 --- a/tests/regression/77-lin2vareq/28-widening.c +++ /dev/null @@ -1,18 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq - -#include - -int main() { - int x = 0; - int y = 1; - - for (int i = 0; i < 1000; i++) { - x += i; - y += x; - } - - __goblint_check(x == 499500); //UNKNOWN - __goblint_check(y == 499501); //UNKNOWN - - return 0; -} diff --git a/tests/regression/77-lin2vareq/29-arrowing.c b/tests/regression/77-lin2vareq/29-arrowing.c deleted file mode 100644 index 668f824592..0000000000 --- a/tests/regression/77-lin2vareq/29-arrowing.c +++ /dev/null @@ -1,18 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq - -#include - -int main() { - int x = 1000; - int y = 1; - - for (int i = 1000; i > 0; i--) { - x -= i; - y += x; - } - - __goblint_check(x == -499500); // UNKNOWN - __goblint_check(y == 500501); // UNKNOWN - - return 0; -} diff --git a/tests/regression/77-lin2vareq/29-different_types.c b/tests/regression/77-lin2vareq/29-different_types.c new file mode 100644 index 0000000000..3439b406cd --- /dev/null +++ b/tests/regression/77-lin2vareq/29-different_types.c @@ -0,0 +1,31 @@ +// PARAM: --set ana.activated[+] lin2vareq +#include + +int x = 42; +long y; +short z; + +int main() { + + y = (long)x; + z = (short)x; + + int a = (int)y; + short b = (short)y; + + int c = (int)z; + long d = (long)z; + + unsigned int u1 = (unsigned int)x; + unsigned long u2 = (unsigned long)y; + unsigned short u3 = (unsigned short)z; + + __goblint_check(x == a); + __goblint_check(x == c); + __goblint_check(y == d); + __goblint_check(x == (int)u1); + __goblint_check(y == (long)u2); + __goblint_check(z == (short)u3); + + return 0; +} From 1edbfe747a59ef79ec513ca75bd86c893beb5325 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 29 Jan 2024 16:49:47 +0100 Subject: [PATCH 152/280] added tests for overflow --- .../77-lin2vareq/30-termination-overflow.c | 13 +++++++++++++ .../77-lin2vareq/31-overflow-unknown.c | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 tests/regression/77-lin2vareq/30-termination-overflow.c create mode 100644 tests/regression/77-lin2vareq/31-overflow-unknown.c diff --git a/tests/regression/77-lin2vareq/30-termination-overflow.c b/tests/regression/77-lin2vareq/30-termination-overflow.c new file mode 100644 index 0000000000..81c6d90828 --- /dev/null +++ b/tests/regression/77-lin2vareq/30-termination-overflow.c @@ -0,0 +1,13 @@ +// SKIP TERM PARAM: --set "ana.activated[+]" lin2vareq + +#include + +int main() { + int i = 2147483647; + i++; + while (i <= 10) { + printf("%d\n", i); + } + + return 0; +} diff --git a/tests/regression/77-lin2vareq/31-overflow-unknown.c b/tests/regression/77-lin2vareq/31-overflow-unknown.c new file mode 100644 index 0000000000..40e073d9fb --- /dev/null +++ b/tests/regression/77-lin2vareq/31-overflow-unknown.c @@ -0,0 +1,19 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq +#include +#include + +int main(void) { + int x = 10; + + if (x + 2147483647 == 2147483657) { + return 0; + } + + __goblint_check(1); + + // Overflow + int c = 2147483647; + c = c + 1; + + __goblint_check(c < 2147483647); // UNKNOWN! +} From 6a96afd73503a0a16e6f5335feae27a5d70c9f36 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 29 Jan 2024 17:15:07 +0100 Subject: [PATCH 153/280] made overflow check in shared functions more readable and with less code duplication --- src/cdomains/apron/sharedFunctions.apron.ml | 111 +++++--------------- 1 file changed, 26 insertions(+), 85 deletions(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 6dcf5c216a..be82c114e2 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -89,7 +89,28 @@ struct (** This version with an overflow check is used by the apron domains such as polyhedra and octagons. They do the overflow handling while they convert the expression to Texpr1. *) - let texpr1_expr_of_cil_exp_with_overflow_check d env exp no_ov = + let overflow_handling_apron no_ov ik env expr d exp = + if not no_ov then ( + let (type_min, type_max) = IntDomain.Size.range ik in + let texpr1 = Texpr1.of_expr env expr in + match Bounds.bound_texpr d texpr1 with + | Some min, Some max when BI.compare type_min min <= 0 && BI.compare max type_max <= 0 -> () + | min_opt, max_opt -> + if M.tracing then M.trace "apron" "may overflow: %a (%a, %a)\n" CilType.Exp.pretty exp (Pretty.docOpt (IntDomain.BigInt.pretty ())) min_opt (Pretty.docOpt (IntDomain.BigInt.pretty ())) max_opt; + raise (Unsupported_CilExp Overflow) + ) + + (** This version without an overflow check is used by the affeq and lin2vareq domains. + They do the overflow handling before converting to Texpr1, and store the result in the "no_ov" flag. Therefore we only check the no_ov flag here. *) + let no_ov_overflow_handling no_ov ik env expr d exp = begin try + if not @@ IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp exp) + && not (Lazy.force no_ov) then + (raise (Unsupported_CilExp Overflow)) + with Invalid_argument _ -> () (* This exception is raised by Cilfacade.get_ikind_exp + when the expression is not an integer expression, for example if it is a float expression. *) + end + + let texpr1_expr_of_cil_exp_with_overflow_check d env exp no_ov overflow_handling = (* recurse without env argument *) let rec texpr1_expr_of_cil_exp = function | Lval (Var v, NoOffset) when Tracked.varinfo_tracked v -> @@ -136,16 +157,7 @@ struct end | _ -> raise (Unsupported_CilExp Exp_not_supported) - in - if not no_ov then ( - let (type_min, type_max) = IntDomain.Size.range ik in - let texpr1 = Texpr1.of_expr env expr in - match Bounds.bound_texpr d texpr1 with - | Some min, Some max when BI.compare type_min min <= 0 && BI.compare max type_max <= 0 -> () - | min_opt, max_opt -> - if M.tracing then M.trace "apron" "may overflow: %a (%a, %a)\n" CilType.Exp.pretty exp (Pretty.docOpt (IntDomain.BigInt.pretty ())) min_opt (Pretty.docOpt (IntDomain.BigInt.pretty ())) max_opt; - raise (Unsupported_CilExp Overflow) - ); + in overflow_handling no_ov ik env expr d exp; expr | exception (Cilfacade.TypeOfError _ as e) | exception (Invalid_argument _ as e) -> @@ -153,85 +165,14 @@ struct in texpr1_expr_of_cil_exp exp - let texpr1_of_cil_exp_with_overflow_check d env e no_ov = - let e = Cil.constFold false e in - Texpr1.of_expr env (texpr1_expr_of_cil_exp_with_overflow_check d env e no_ov) - - (** This version without an overflow check is used by the affeq and lin2vareq domains. - They do the overflow handling before converting to Texpr1, and store the result in the "no_ov" flag. *) - let texpr1_expr_of_cil_exp_no_ov env exp = - (* recurse without env argument *) - let rec texpr1_expr_of_cil_exp = function - | Lval (Var v, NoOffset) when Tracked.varinfo_tracked v -> - if not v.vglob || Arg.allow_global then - let var = - if v.vglob then - V.global v - else - V.local v - in - if Environment.mem_var env var then - Var var - else - raise (Unsupported_CilExp (Var_not_found v)) - else - failwith "texpr1_expr_of_cil_exp: globals must be replaced with temporary locals" - | Const (CInt (i, _, _)) -> - Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z i))) - | exp -> - match Cilfacade.get_ikind_exp exp with - | ik -> - begin match exp with - | UnOp (Neg, e, _) -> - Unop (Neg, texpr1_expr_of_cil_exp e, Int, Near) - | BinOp (PlusA, e1, e2, _) -> - Binop (Add, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) - | BinOp (MinusA, e1, e2, _) -> - Binop (Sub, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) - | BinOp (Mult, e1, e2, _) -> - Binop (Mul, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) - | BinOp (Div, e1, e2, _) -> - Binop (Div, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Zero) - | BinOp (Mod, e1, e2, _) -> - Binop (Mod, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) - | CastE (TInt (t_ik, _) as t, e) -> - begin match IntDomain.should_ignore_overflow t_ik || IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) - | true -> - Unop (Cast, texpr1_expr_of_cil_exp e, Int, Zero) (* TODO: what does Apron Cast actually do? just for floating point and rounding? *) - | false - | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) - | exception Invalid_argument _ -> (* get_ikind in is_cast_injective *) - raise (Unsupported_CilExp (Cast_not_injective t)) - end - | _ -> (if M.tracing then M.trace "convert" "Unsupported_CilExp: %a\n" d_plainexp exp; - raise (Unsupported_CilExp Exp_not_supported)) end - | exception (Cilfacade.TypeOfError _ as e) - | exception (Invalid_argument _ as e) -> - raise (Unsupported_CilExp (Exp_typeOf e)) - in - texpr1_expr_of_cil_exp exp - - let texpr1_expr_of_cil_exp env exp no_ov = - if M.tracing then M.trace "convert" "texpr1_expr_of_cil_exp: %a\n" d_plainexp exp; - let exp = Cil.constFold false exp in - begin try - if not @@ IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp exp) - && not (Lazy.force no_ov) then - (raise (Unsupported_CilExp Overflow)) - with Invalid_argument _ -> () end; - texpr1_expr_of_cil_exp_no_ov env exp - let texpr1_expr_of_cil_exp d env exp no_ov = - if Ov.do_overflow_check then texpr1_expr_of_cil_exp_with_overflow_check d env exp (Lazy.force no_ov) - else texpr1_expr_of_cil_exp env exp no_ov + let exp = Cil.constFold false exp in + if Ov.do_overflow_check then texpr1_expr_of_cil_exp_with_overflow_check d env exp (Lazy.force no_ov) overflow_handling_apron + else texpr1_expr_of_cil_exp_with_overflow_check d env exp no_ov no_ov_overflow_handling let texpr1_of_cil_exp d env e no_ov = Texpr1.of_expr env (texpr1_expr_of_cil_exp d env e no_ov) - let texpr1_of_cil_exp d env e no_ov = - if Ov.do_overflow_check then texpr1_of_cil_exp_with_overflow_check d env e (Lazy.force no_ov) - else texpr1_of_cil_exp d env e no_ov - let tcons1_of_cil_exp d env e negate no_ov = let e = Cil.constFold false e in let (texpr1_plus, texpr1_minus, typ) = From 10ba24a9980b4b45e32341642f7a8dc952cc426e Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 31 Jan 2024 13:10:04 +0100 Subject: [PATCH 154/280] fix indentation and one of the affeq tests --- src/cdomains/apron/apronDomain.apron.ml | 31 ++++++++--------- .../apron/linearTwoVarEqualityDomain.apron.ml | 8 +---- src/cdomains/apron/sharedFunctions.apron.ml | 34 +++++++++---------- .../63-affeq/20-svcomp-signextension.c | 4 +-- 4 files changed, 35 insertions(+), 42 deletions(-) diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index aa9a353b0e..aadf297ec2 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -498,39 +498,38 @@ struct LAnd, LOr, LNot are directly supported by Apron domain in order to confirm logic-containing Apron invariants from witness while deep-query is disabled *) - let rec assert_constraint ask d e negate (ov: bool Lazy.t) = + let rec assert_constraint ask d e negate (no_ov: bool Lazy.t) = if M.tracing then M.trace "assert_constraint_apron" "%a ;;; %a\n" d_exp e d_plainexp e; - let no_ov = IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp e) in (* TODO: why ignores no_ov argument? *) match e with (* Apron doesn't properly meet with DISEQ constraints: https://github.com/antoinemine/apron/issues/37. Join Gt and Lt versions instead. *) | BinOp (Ne, lhs, rhs, intType) when not negate -> - let assert_gt = assert_constraint ask d (BinOp (Gt, lhs, rhs, intType)) negate ov in - let assert_lt = assert_constraint ask d (BinOp (Lt, lhs, rhs, intType)) negate ov in + let assert_gt = assert_constraint ask d (BinOp (Gt, lhs, rhs, intType)) negate no_ov in + let assert_lt = assert_constraint ask d (BinOp (Lt, lhs, rhs, intType)) negate no_ov in join assert_gt assert_lt | BinOp (Eq, lhs, rhs, intType) when negate -> - let assert_gt = assert_constraint ask d (BinOp (Gt, lhs, rhs, intType)) (not negate) ov in - let assert_lt = assert_constraint ask d (BinOp (Lt, lhs, rhs, intType)) (not negate) ov in + let assert_gt = assert_constraint ask d (BinOp (Gt, lhs, rhs, intType)) (not negate) no_ov in + let assert_lt = assert_constraint ask d (BinOp (Lt, lhs, rhs, intType)) (not negate) no_ov in join assert_gt assert_lt | BinOp (LAnd, lhs, rhs, intType) when not negate -> - let assert_l = assert_constraint ask d lhs negate ov in - let assert_r = assert_constraint ask d rhs negate ov in + let assert_l = assert_constraint ask d lhs negate no_ov in + let assert_r = assert_constraint ask d rhs negate no_ov in meet assert_l assert_r | BinOp (LAnd, lhs, rhs, intType) when negate -> - let assert_l = assert_constraint ask d lhs negate ov in - let assert_r = assert_constraint ask d rhs negate ov in + let assert_l = assert_constraint ask d lhs negate no_ov in + let assert_r = assert_constraint ask d rhs negate no_ov in join assert_l assert_r (* de Morgan *) | BinOp (LOr, lhs, rhs, intType) when not negate -> - let assert_l = assert_constraint ask d lhs negate ov in - let assert_r = assert_constraint ask d rhs negate ov in + let assert_l = assert_constraint ask d lhs negate no_ov in + let assert_r = assert_constraint ask d rhs negate no_ov in join assert_l assert_r | BinOp (LOr, lhs, rhs, intType) when negate -> - let assert_l = assert_constraint ask d lhs negate ov in - let assert_r = assert_constraint ask d rhs negate ov in + let assert_l = assert_constraint ask d lhs negate no_ov in + let assert_r = assert_constraint ask d rhs negate no_ov in meet assert_l assert_r (* de Morgan *) - | UnOp (LNot,e,_) -> assert_constraint ask d e (not negate) ov + | UnOp (LNot,e,_) -> assert_constraint ask d e (not negate) no_ov | _ -> - begin match Convert.tcons1_of_cil_exp ask d (A.env d) e negate (Lazy.from_val no_ov) with + begin match Convert.tcons1_of_cil_exp ask d (A.env d) e negate no_ov with | tcons1 -> if M.tracing then M.trace "apron" "assert_constraint %a %s\n" d_exp e (Format.asprintf "%a" Tcons1.print tcons1); if M.tracing then M.trace "apron" "assert_constraint st: %a\n" D.pretty d; diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 060758a28b..5456334ee3 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -219,7 +219,7 @@ struct List.iter update cv's; let var_count = GobArray.count_matchingi (fun _ a -> a <> Z.zero) expr in if var_count == 0 then Some (None, !constant) - else if var_count == 1 then ( + else if var_count == 1 then ( let var = Array.findi (fun a -> a <> Z.zero) expr in if Z.(expr.(var) == Z.one) then Some (Some var, !constant) else None @@ -355,8 +355,6 @@ struct let pretty () (x:t) = text (show x) let printXml f x = BatPrintf.fprintf f "\n\n\nequalities-array\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) - - let eval_interval ask = Bounds.bound_texpr exception Contradiction @@ -531,8 +529,6 @@ struct end | None -> bot_env end - - let assign_texpr t var texp = timing_wrap "assign_texpr" (assign_texpr t var) texp (* no_ov -> no overflow @@ -736,8 +732,6 @@ struct This function returns all the equalities that are saved in our datastructure t. Lincons -> linear constraint *) - (*TODO*) - let invariant t = match t.d with | None -> [] diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 07586aabf6..f017cbf111 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -111,7 +111,7 @@ struct let texpr1_expr_of_cil_exp_old d env exp no_ov = (* recurse without env argument *) - let rec texpr1_expr_of_cil_exp exp = + let rec texpr1_expr_of_cil_exp exp = match exp with | Lval (Var v, NoOffset) when Tracked.varinfo_tracked v -> if not v.vglob || Arg.allow_global then @@ -148,8 +148,8 @@ struct Binop (Mod, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) | CastE (TInt (t_ik, _) as t, e) -> begin match IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) - | true -> texpr1_expr_of_cil_exp e - | false + | true -> texpr1_expr_of_cil_exp e + | false | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) | exception Invalid_argument _ -> (* get_ikind in is_cast_injective *) raise (Unsupported_CilExp (Cast_not_injective t)) @@ -162,17 +162,17 @@ struct | exception (Invalid_argument _ as e) -> raise (Unsupported_CilExp (Exp_typeOf e)) in - texpr1_expr_of_cil_exp exp + texpr1_expr_of_cil_exp exp let texpr1_expr_of_cil_exp_with_overflow_check (ask: Queries.ask) d env exp no_ov overflow_handling = - let query e ik = - let res = match ask.f (EvalInt e) with + let query e ik = + let res = match ask.f (EvalInt e) with | `Bot -> raise (Unsupported_CilExp Exp_not_supported) (* This should never happen according to Michael Schwarz *) | `Top -> IntDomain.IntDomTuple.top_of ik | `Lifted x -> x (* According to base.ml:704 cast should be unnecessary because it should be taken care of by EvalInt. *) in - (* If the returned interval is top of the expected ikind (i.e. the value is unknown ) or the returned interval is in range of the expected interval, return top + (* If the returned interval is top of the expected ikind (i.e. the value is unknown ) or the returned interval is in range of the expected interval, return top - If top is returned the expression will be rewritten. - If a constant is returned this specific value is casted to the expected ikind value - else we got an interval with unsupported bounds i.e. the value expression is known to be unknown and needs casting, which we do not support i.e. the expression is not supported*) @@ -181,7 +181,7 @@ struct if top then IntDomain.IntDomTuple.top_of ik else res in (* recurse without env argument *) - let rec texpr1_expr_of_cil_exp ask exp = + let rec texpr1_expr_of_cil_exp ask exp = match exp with | Lval (Var v, NoOffset) when Tracked.varinfo_tracked v -> if not v.vglob || Arg.allow_global then @@ -207,8 +207,8 @@ struct let ikind = Cilfacade.get_ikind_exp e in (* TODO AWE: raise unsupported cil exception on error *) let simp = query e ikind in let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to ikind simp in - if Option.is_some const then Const (CInt (Option.get const, ikind, None)) - else e + if Option.is_some const then Const (CInt (Option.get const, ikind, None)) + else e in match exp with | UnOp (Neg, e, _) -> @@ -225,16 +225,16 @@ struct Binop (Mod, texpr1_expr_of_cil_exp ask @@ simplify e1, texpr1_expr_of_cil_exp ask @@ simplify e2, Int, Near) | CastE (TInt (t_ik, _) as t, e) -> begin match IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) - | true -> texpr1_expr_of_cil_exp ask @@ simplify e - | false -> + | true -> texpr1_expr_of_cil_exp ask @@ simplify e + | false -> let res = query e @@ Cilfacade.get_ikind_exp e in let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to t_ik res in if Option.is_some const then Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z (Option.get const)))) else if IntDomain.IntDomTuple.is_top_of t_ik res then raise (Unsupported_CilExp (Cast_not_injective t)) else ( let (minimal, maximal) = IntDomain.Size.range t_ik in - match IntDomain.IntDomTuple.minimal res, IntDomain.IntDomTuple.maximal res with - | Some min, Some max when min >= minimal && max <= maximal -> texpr1_expr_of_cil_exp ask e + match IntDomain.IntDomTuple.minimal res, IntDomain.IntDomTuple.maximal res with + | Some min, Some max when min >= minimal && max <= maximal -> texpr1_expr_of_cil_exp ask e | _ -> raise (Unsupported_CilExp (Cast_not_injective t))) | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) | exception Invalid_argument _ -> (* get_ikind in is_cast_injective *) @@ -259,14 +259,14 @@ struct let texpr1_of_cil_exp ask d env e no_ov = let e = Cil.constFold false e in - let res = texpr1_expr_of_cil_exp ask d env e no_ov in + let res = texpr1_expr_of_cil_exp ask d env e no_ov in Texpr1.of_expr env res let tcons1_of_cil_exp_old d env e negate no_ov = let (texpr1_plus, texpr1_minus, typ) = match e with | BinOp (r, e1, e2, _) -> - let texpr1_1 = texpr1_expr_of_cil_exp_old d env e1 no_ov in + let texpr1_1 = texpr1_expr_of_cil_exp_old d env e1 no_ov in let texpr1_2 = texpr1_expr_of_cil_exp_old d env e2 no_ov in (* Apron constraints always compare with 0 and only have comparisons one way *) begin match r with @@ -301,7 +301,7 @@ struct let (texpr1_plus, texpr1_minus, typ) = match e with | BinOp (r, e1, e2, _) -> - let texpr1_1 = texpr1_expr_of_cil_exp ask d env e1 no_ov in + let texpr1_1 = texpr1_expr_of_cil_exp ask d env e1 no_ov in let texpr1_2 = texpr1_expr_of_cil_exp ask d env e2 no_ov in (* Apron constraints always compare with 0 and only have comparisons one way *) begin match r with diff --git a/tests/regression/63-affeq/20-svcomp-signextension.c b/tests/regression/63-affeq/20-svcomp-signextension.c index c5ca3e360a..15c8def185 100644 --- a/tests/regression/63-affeq/20-svcomp-signextension.c +++ b/tests/regression/63-affeq/20-svcomp-signextension.c @@ -1,5 +1,4 @@ // SKIP PARAM: --set ana.activated[+] affeq --set sem.int.signed_overflow assume_none - #include int main() { @@ -18,8 +17,9 @@ int main() { */ if (signedtounsigned == 4294967295) { + __goblint_check(1); // reachable return (-1); } - +__goblint_check(0); // NOWARN (unreachable) return (0); } From 480603670a43adb1032908e61d606225ef1b8c93 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 31 Jan 2024 14:36:59 +0100 Subject: [PATCH 155/280] Added Cast Analysis and merged with Overflow Analysis --- src/analyses/apron/relationAnalysis.apron.ml | 23 ++- .../apron/affineEqualityDomain.apron.ml | 63 +++--- src/cdomains/apron/apronDomain.apron.ml | 112 +++++------ .../apron/linearTwoVarEqualityDomain.apron.ml | 92 ++++++--- src/cdomains/apron/relationDomain.apron.ml | 8 +- src/cdomains/apron/sharedFunctions.apron.ml | 185 +++++++++++++++--- tests/regression/77-lin2vareq/28-narrowing.c | 2 +- 7 files changed, 319 insertions(+), 166 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 6afdcfdb58..7c13538c6f 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -208,8 +208,8 @@ struct (* TODO: don't go through CIL exp? *) let e1 = BinOp (Le, Lval (Cil.var x), (Cil.kintegerCilint ik type_max), intType) in let e2 = BinOp (Ge, Lval (Cil.var x), (Cil.kintegerCilint ik type_min), intType) in - let rel = RD.assert_inv rel e1 false (no_overflow ask e1) in (* TODO: how can be overflow when asserting type bounds? *) - let rel = RD.assert_inv rel e2 false (no_overflow ask e2) in + let rel = RD.assert_inv ask rel e1 false (no_overflow ask e1) in (* TODO: how can be overflow when asserting type bounds? *) + let rel = RD.assert_inv ask rel e2 false (no_overflow ask e2) in rel | exception Invalid_argument _ -> rel @@ -237,7 +237,6 @@ struct inner e (* Basic transfer functions. *) - let assign ctx (lv:lval) e = let st = ctx.local in if !AnalysisState.global_initialization && e = MyCFG.unknown_exp then @@ -250,7 +249,7 @@ struct assign_from_globals_wrapper ask ctx.global st simplified_e (fun apr' e' -> if M.tracing then M.traceli "relation" "assign inner %a = %a (%a)\n" CilType.Varinfo.pretty v d_exp e' d_plainexp e'; if M.tracing then M.trace "relation" "st: %a\n" RD.pretty apr'; - let r = RD.assign_exp apr' (RV.local v) e' (no_overflow ask simplified_e) in + let r = RD.assign_exp ask apr' (RV.local v) e' (no_overflow ask simplified_e) in if M.tracing then M.traceu "relation" "-> %a\n" RD.pretty r; r ) @@ -265,7 +264,7 @@ struct let ask = Analyses.ask_of_ctx ctx in let res = assign_from_globals_wrapper ask ctx.global st e (fun rel' e' -> (* not an assign, but must remove g#in-s still *) - RD.assert_inv rel' e' (not b) (no_overflow ask e) + RD.assert_inv ask rel' e' (not b) (no_overflow ask e) ) in if RD.is_bot_env res then raise Deadcode; @@ -301,6 +300,7 @@ struct let make_callee_rel ~thread ctx f args = let fundec = Node.find_fundec ctx.node in let st = ctx.local in + let ask = Analyses.ask_of_ctx ctx in let arg_assigns = GobList.combine_short f.sformals args (* TODO: is it right to ignore missing formals/args? *) |> List.filter_map (fun (x, e) -> if RD.Tracked.varinfo_tracked x then Some (RV.arg x, e) else None) @@ -313,10 +313,9 @@ struct if thread then new_rel else - let ask = Analyses.ask_of_ctx ctx in List.fold_left (fun new_rel (var, e) -> assign_from_globals_wrapper ask ctx.global {st with rel = new_rel} e (fun rel' e' -> - RD.assign_exp rel' var e' (no_overflow ask e) + RD.assign_exp ask rel' var e' (no_overflow ask e) ) ) new_rel arg_assigns in @@ -359,7 +358,7 @@ struct match e with | Some e -> assign_from_globals_wrapper ask ctx.global {st with rel = rel'} e (fun rel' e' -> - RD.assign_exp rel' RV.return e' (no_overflow ask e) + RD.assign_exp ask rel' RV.return e' (no_overflow ask e) ) | None -> rel' (* leaves V.return unconstrained *) @@ -405,7 +404,7 @@ struct let new_fun_rel = List.fold_left (fun new_fun_rel (var, e) -> assign_from_globals_wrapper ask ctx.global {st with rel = new_fun_rel} e (fun rel' e' -> (* not an assign, but still works? *) - RD.substitute_exp rel' var e' (no_overflow ask e) + RD.substitute_exp ask rel' var e' (no_overflow ask e) ) ) new_fun_rel arg_substitutes in @@ -498,7 +497,7 @@ struct let ask = Analyses.ask_of_ctx ctx in let res = assign_from_globals_wrapper ask ctx.global st e (fun apr' e' -> (* not an assign, but must remove g#in-s still *) - RD.assert_inv apr' e' false (no_overflow ask e) + RD.assert_inv ask apr' e' false (no_overflow ask e) ) in if RD.is_bot_env res then raise Deadcode; @@ -633,7 +632,7 @@ struct read_from_globals_wrapper (Analyses.ask_of_ctx ctx) ctx.global st esimple - (fun rel' e' -> RD.eval_int rel' e' no_ov) + (fun rel' e' -> RD.eval_int (Analyses.ask_of_ctx ctx) rel' e' no_ov) in match q with | EvalInt e -> @@ -698,7 +697,7 @@ struct let vars = Basetype.CilExp.get_vars e |> List.unique ~eq:CilType.Varinfo.equal |> List.filter RD.Tracked.varinfo_tracked in let rel = RD.forget_vars rel (List.map RV.local vars) in (* havoc *) let rel = List.fold_left (assert_type_bounds ask) rel vars in (* add type bounds to avoid overflow in top state *) - let rel = RD.assert_inv rel e false (no_overflow ask e_orig) in (* assume *) + let rel = RD.assert_inv ask rel e false (no_overflow ask e_orig) in (* assume *) let rel = RD.keep_vars rel (List.map RV.local vars) in (* restrict *) (* TODO: parallel write_global? *) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index d6c5c2d522..91fa063b25 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -18,25 +18,25 @@ module Mpqf = SharedFunctions.Mpqf module V = RelationDomain.V module AffineEqualityMatrix (Vec: AbstractVector) (Mx: AbstractMatrix) = - struct - include Mx(Mpqf) (Vec) - let dim_add (ch: Apron.Dim.change) m = - Array.modifyi (+) ch.dim; - add_empty_columns m ch.dim +struct + include Mx(Mpqf) (Vec) + let dim_add (ch: Apron.Dim.change) m = + Array.modifyi (+) ch.dim; + add_empty_columns m ch.dim - let dim_add ch m = timing_wrap "dim add" (dim_add ch) m + let dim_add ch m = timing_wrap "dim add" (dim_add ch) m - let dim_remove (ch: Apron.Dim.change) m ~del = - if Array.length ch.dim = 0 || is_empty m then - m - else ( - Array.modifyi (+) ch.dim; - let m' = if not del then let m = copy m in Array.fold_left (fun y x -> reduce_col_with y x; y) m ch.dim else m in - remove_zero_rows @@ del_cols m' ch.dim) + let dim_remove (ch: Apron.Dim.change) m ~del = + if Array.length ch.dim = 0 || is_empty m then + m + else ( + Array.modifyi (+) ch.dim; + let m' = if not del then let m = copy m in Array.fold_left (fun y x -> reduce_col_with y x; y) m ch.dim else m in + remove_zero_rows @@ del_cols m' ch.dim) - let dim_remove ch m ~del = VectorMatrix.timing_wrap "dim remove" (fun del -> dim_remove ch m ~del:del) del - end + let dim_remove ch m ~del = VectorMatrix.timing_wrap "dim remove" (fun del -> dim_remove ch m ~del:del) del +end (** It defines the type t of the affine equality domain (a struct that contains an optional matrix and an apron environment) and provides the functions needed for handling variables (which are defined by RelationDomain.D2) such as add_vars remove_vars. Furthermore, it provides the function get_coeff_vec that parses an apron expression into a vector of coefficients if the apron expression has an affine form. *) module VarManagement (Vec: AbstractVector) (Mx: AbstractMatrix)= @@ -205,7 +205,7 @@ struct let pretty () (x:t) = text (show x) let printXml f x = BatPrintf.fprintf f "\n\n\nmatrix\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) - let eval_interval = Bounds.bound_texpr + let eval_interval ask = Bounds.bound_texpr let name () = "affeq" @@ -417,15 +417,16 @@ struct let assign_texpr t var texp = timing_wrap "assign_texpr" (assign_texpr t var) texp - let assign_exp (t: VarManagement(Vc)(Mx).t) var exp (no_ov: bool Lazy.t) = + let assign_exp ask (t: VarManagement(Vc)(Mx).t) var exp (no_ov: bool Lazy.t) = let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in - match Convert.texpr1_expr_of_cil_exp t t.env exp no_ov with + (* TODO: Do we need to do a constant folding here? It happens for texpr1_of_cil_exp *) + match Convert.texpr1_expr_of_cil_exp ask t t.env exp no_ov with | exp -> assign_texpr t var exp | exception Convert.Unsupported_CilExp _ -> if is_bot t then t else forget_vars t [var] - let assign_exp t var exp no_ov = - let res = assign_exp t var exp no_ov in + let assign_exp ask t var exp no_ov = + let res = assign_exp ask t var exp no_ov in if M.tracing then M.tracel "ops" "assign_exp t:\n %s \n var: %s \n exp: %a\n no_ov: %b -> \n %s\n" (show t) (Var.to_string var) d_exp exp (Lazy.force no_ov) (show res) ; res @@ -488,17 +489,17 @@ struct if M.tracing then M.tracel "ops" "assign_var parallel'\n"; res - let substitute_exp t var exp no_ov = + let substitute_exp ask t var exp no_ov = let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in - let res = assign_exp t var exp no_ov in + let res = assign_exp ask t var exp no_ov in forget_vars res [var] - let substitute_exp t var exp ov = - let res = substitute_exp t var exp ov in + let substitute_exp ask t var exp ov = + let res = substitute_exp ask t var exp ov in if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %s \n exp: %a \n -> \n %s\n" (show t) (Var.to_string var) d_exp exp (show res); res - let substitute_exp t var exp ov = timing_wrap "substitution" (substitute_exp t var exp) ov + let substitute_exp ask t var exp ov = timing_wrap "substitution" (substitute_exp ask t var exp) ov (** Assert a constraint expression. @@ -507,7 +508,7 @@ struct In case of a potential overflow, "no_ov" is set to false and Convert.tcons1_of_cil_exp will raise the exception Unsupported_CilExp Overflow *) - let meet_tcons t tcons expr = + let meet_tcons ask t tcons expr = let check_const cmp c = if cmp c Mpqf.zero then bot_env else t in let meet_vec e = (* Flip the sign of the const. val in coeff vec *) @@ -553,13 +554,13 @@ struct if M.tracing then M.tracel "ops" "unify: %s %s -> %s\n" (show a) (show b) (show res); res - let assert_constraint d e negate no_ov = - if M.tracing then M.tracel "assert_constraint" "assert_constraint with expr: %a %b" d_exp e (Lazy.force no_ov); - match Convert.tcons1_of_cil_exp d d.env e negate no_ov with - | tcons1 -> meet_tcons d tcons1 e + let assert_constraint ask d e negate no_ov = + if M.tracing then M.tracel "assert_cons" "assert_cons with expr: %a %b" d_exp e (Lazy.force no_ov); + match Convert.tcons1_of_cil_exp ask d d.env e negate no_ov with + | tcons1 -> meet_tcons ask d tcons1 e | exception Convert.Unsupported_CilExp _ -> d - let assert_constraint d e negate no_ov = timing_wrap "assert_constraint" (assert_constraint d e negate) no_ov + let assert_constraint ask d e negate no_ov = timing_wrap "assert_cons" (assert_constraint ask d e negate) no_ov let relift t = t diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index 5752549602..aa9a353b0e 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -128,9 +128,9 @@ sig val keep_vars : t -> Var.t list -> t val keep_filter : t -> (Var.t -> bool) -> t val forget_vars : t -> Var.t list -> t - val assign_exp : t -> Var.t -> exp -> bool Lazy.t -> t + val assign_exp : Queries.ask -> t -> Var.t -> exp -> bool Lazy.t -> t val assign_var : t -> Var.t -> Var.t -> t - val substitute_exp : t -> Var.t -> exp -> bool Lazy.t -> t + val substitute_exp : Queries.ask-> t -> Var.t -> exp -> bool Lazy.t -> t end (** Imperative in-place environment and transfer functions. *) @@ -143,13 +143,13 @@ sig val keep_vars_with : t -> Var.t list -> unit val keep_filter_with : t -> (Var.t -> bool) -> unit val forget_vars_with : t -> Var.t list -> unit - val assign_exp_with : t -> Var.t -> exp -> bool Lazy.t -> unit - val assign_exp_parallel_with : t -> (Var.t * exp) list -> bool -> unit (* TODO: why this one isn't lazy? *) + val assign_exp_with : Queries.ask -> t -> Var.t -> exp -> bool Lazy.t -> unit + val assign_exp_parallel_with : Queries.ask -> t -> (Var.t * exp) list -> bool -> unit (* TODO: why this one isn't lazy? *) val assign_var_with : t -> Var.t -> Var.t -> unit val assign_var_parallel_with : t -> (Var.t * Var.t) list -> unit - val substitute_exp_with : t -> Var.t -> exp -> bool Lazy.t-> unit + val substitute_exp_with : Queries.ask -> t -> Var.t -> exp -> bool Lazy.t-> unit val substitute_exp_parallel_with : - t -> (Var.t * exp) list -> bool Lazy.t -> unit + Queries.ask -> t -> (Var.t * exp) list -> bool Lazy.t -> unit val substitute_var_with : t -> Var.t -> Var.t -> unit end @@ -189,17 +189,17 @@ struct let nd = copy d in forget_vars_with nd vs; nd - let assign_exp d v e no_ov = + let assign_exp ask d v e no_ov = let nd = copy d in - assign_exp_with nd v e no_ov; + assign_exp_with ask nd v e no_ov; nd let assign_var d v v' = let nd = copy d in assign_var_with nd v v'; nd - let substitute_exp d v e no_ov = + let substitute_exp ask d v e no_ov = let nd = copy d in - substitute_exp_with nd v e no_ov; + substitute_exp_with ask nd v e no_ov; nd end @@ -215,7 +215,7 @@ sig val mem_var : t -> Var.t -> bool val assign_var_parallel' : t -> Var.t list -> Var.t list -> t - val meet_tcons : t -> Tcons1.t -> exp -> t + val meet_tcons : Queries.ask -> t -> Tcons1.t -> exp -> t val to_lincons_array : t -> Lincons1.earray val of_lincons_array : Lincons1.earray -> t @@ -280,8 +280,8 @@ struct let vs' = Array.of_list vs in A.forget_array_with Man.mgr nd vs' false - let assign_exp_with nd v e no_ov = - match Convert.texpr1_of_cil_exp nd (A.env nd) e no_ov with + let assign_exp_with ask nd v e no_ov = + match Convert.texpr1_of_cil_exp ask nd (A.env nd) e no_ov with | texpr1 -> if M.tracing then M.trace "apron" "assign_exp converted: %s\n" (Format.asprintf "%a" Texpr1.print texpr1); A.assign_texpr_with Man.mgr nd v texpr1 None @@ -289,7 +289,7 @@ struct if M.tracing then M.trace "apron" "assign_exp unsupported\n"; forget_vars_with nd [v] - let assign_exp_parallel_with nd ves no_ov = + let assign_exp_parallel_with ask nd ves no_ov = (* TODO: non-_with version? *) let env = A.env nd in (* partition assigns with supported and unsupported exps *) @@ -297,7 +297,7 @@ struct ves |> List.enum |> Enum.map (Tuple2.map2 (fun e -> - match Convert.texpr1_of_cil_exp nd env e (Lazy.from_val no_ov) with + match Convert.texpr1_of_cil_exp ask nd env e (Lazy.from_val no_ov) with | texpr1 -> Some texpr1 | exception Convert.Unsupported_CilExp _ -> None )) @@ -347,14 +347,14 @@ struct in A.assign_texpr_array Man.mgr d vs texpr1s None - let substitute_exp_with nd v e no_ov = - match Convert.texpr1_of_cil_exp nd (A.env nd) e no_ov with + let substitute_exp_with ask nd v e no_ov = + match Convert.texpr1_of_cil_exp ask nd (A.env nd) e no_ov with | texpr1 -> A.substitute_texpr_with Man.mgr nd v texpr1 None | exception Convert.Unsupported_CilExp _ -> forget_vars_with nd [v] - let substitute_exp_parallel_with nd ves no_ov = + let substitute_exp_parallel_with ask nd ves no_ov = (* TODO: non-_with version? *) let env = A.env nd in (* partition substitutes with supported and unsupported exps *) @@ -362,7 +362,7 @@ struct ves |> List.enum |> Enum.map (Tuple2.map2 (fun e -> - match Convert.texpr1_of_cil_exp nd env e no_ov with + match Convert.texpr1_of_cil_exp ask nd env e no_ov with | texpr1 -> Some texpr1 | exception Convert.Unsupported_CilExp _ -> None )) @@ -389,7 +389,7 @@ struct let texpr1 = Texpr1.of_expr (A.env nd) (Var v') in A.substitute_texpr_with Man.mgr nd v texpr1 None - let meet_tcons d tcons1 e = + let meet_tcons ask d tcons1 e = let earray = Tcons1.array_make (A.env d) 1 in Tcons1.array_set earray 0 tcons1; A.meet_tcons_array Man.mgr d earray @@ -493,48 +493,48 @@ struct include D include AOps (Tracked) (Man) include Tracked - let eval_interval = Bounds.bound_texpr + let eval_interval ask = Bounds.bound_texpr (** Assert a constraint expression. LAnd, LOr, LNot are directly supported by Apron domain in order to confirm logic-containing Apron invariants from witness while deep-query is disabled *) - let rec assert_constraint d e negate (ov: bool Lazy.t) = + let rec assert_constraint ask d e negate (ov: bool Lazy.t) = if M.tracing then M.trace "assert_constraint_apron" "%a ;;; %a\n" d_exp e d_plainexp e; let no_ov = IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp e) in (* TODO: why ignores no_ov argument? *) match e with (* Apron doesn't properly meet with DISEQ constraints: https://github.com/antoinemine/apron/issues/37. Join Gt and Lt versions instead. *) | BinOp (Ne, lhs, rhs, intType) when not negate -> - let assert_gt = assert_constraint d (BinOp (Gt, lhs, rhs, intType)) negate ov in - let assert_lt = assert_constraint d (BinOp (Lt, lhs, rhs, intType)) negate ov in + let assert_gt = assert_constraint ask d (BinOp (Gt, lhs, rhs, intType)) negate ov in + let assert_lt = assert_constraint ask d (BinOp (Lt, lhs, rhs, intType)) negate ov in join assert_gt assert_lt | BinOp (Eq, lhs, rhs, intType) when negate -> - let assert_gt = assert_constraint d (BinOp (Gt, lhs, rhs, intType)) (not negate) ov in - let assert_lt = assert_constraint d (BinOp (Lt, lhs, rhs, intType)) (not negate) ov in + let assert_gt = assert_constraint ask d (BinOp (Gt, lhs, rhs, intType)) (not negate) ov in + let assert_lt = assert_constraint ask d (BinOp (Lt, lhs, rhs, intType)) (not negate) ov in join assert_gt assert_lt | BinOp (LAnd, lhs, rhs, intType) when not negate -> - let assert_l = assert_constraint d lhs negate ov in - let assert_r = assert_constraint d rhs negate ov in + let assert_l = assert_constraint ask d lhs negate ov in + let assert_r = assert_constraint ask d rhs negate ov in meet assert_l assert_r | BinOp (LAnd, lhs, rhs, intType) when negate -> - let assert_l = assert_constraint d lhs negate ov in - let assert_r = assert_constraint d rhs negate ov in + let assert_l = assert_constraint ask d lhs negate ov in + let assert_r = assert_constraint ask d rhs negate ov in join assert_l assert_r (* de Morgan *) | BinOp (LOr, lhs, rhs, intType) when not negate -> - let assert_l = assert_constraint d lhs negate ov in - let assert_r = assert_constraint d rhs negate ov in + let assert_l = assert_constraint ask d lhs negate ov in + let assert_r = assert_constraint ask d rhs negate ov in join assert_l assert_r | BinOp (LOr, lhs, rhs, intType) when negate -> - let assert_l = assert_constraint d lhs negate ov in - let assert_r = assert_constraint d rhs negate ov in + let assert_l = assert_constraint ask d lhs negate ov in + let assert_r = assert_constraint ask d rhs negate ov in meet assert_l assert_r (* de Morgan *) - | UnOp (LNot,e,_) -> assert_constraint d e (not negate) ov + | UnOp (LNot,e,_) -> assert_constraint ask d e (not negate) ov | _ -> - begin match Convert.tcons1_of_cil_exp d (A.env d) e negate (Lazy.from_val no_ov) with + begin match Convert.tcons1_of_cil_exp ask d (A.env d) e negate (Lazy.from_val no_ov) with | tcons1 -> if M.tracing then M.trace "apron" "assert_constraint %a %s\n" d_exp e (Format.asprintf "%a" Tcons1.print tcons1); if M.tracing then M.trace "apron" "assert_constraint st: %a\n" D.pretty d; - let r = meet_tcons d tcons1 e in + let r = meet_tcons ask d tcons1 e in if M.tracing then M.trace "apron" "assert_constraint r: %a\n" D.pretty r; r | exception Convert.Unsupported_CilExp reason -> @@ -689,6 +689,8 @@ struct else false + + let widen x y = let x_env = A.env x in let y_env = A.env y in @@ -708,7 +710,7 @@ struct (* this implements widening_threshold with Tcons1 instead of Lincons1 *) let tcons1s = List.filter_map (fun e -> let no_ov = lazy(IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp e)) in - match Convert.tcons1_of_cil_exp y y_env e false no_ov with + match Convert.tcons1_of_cil_exp_old y y_env e false no_ov with | tcons1 when A.sat_tcons Man.mgr y tcons1 -> Some tcons1 | _ @@ -789,8 +791,8 @@ sig module Tracked: RelationDomain.Tracked - val assert_inv : t -> exp -> bool -> bool Lazy.t -> t - val eval_int : t -> exp -> bool Lazy.t -> Queries.ID.t + val assert_inv : Queries.ask -> t -> exp -> bool -> bool Lazy.t -> t + val eval_int : Queries.ask -> t -> exp -> bool Lazy.t -> Queries.ID.t end @@ -871,12 +873,12 @@ struct let forget_vars_with (b, d) vs = BoxD.forget_vars_with b vs; D.forget_vars_with d vs - let assign_exp_with (b, d) v e no_ov = - BoxD.assign_exp_with b v e no_ov; - D.assign_exp_with d v e no_ov - let assign_exp_parallel_with (b, d) ves no_ov = - BoxD.assign_exp_parallel_with b ves no_ov; - D.assign_exp_parallel_with d ves no_ov + let assign_exp_with ask (b, d) v e no_ov = + BoxD.assign_exp_with ask b v e no_ov; + D.assign_exp_with ask d v e no_ov + let assign_exp_parallel_with ask (b, d) ves no_ov = + BoxD.assign_exp_parallel_with ask b ves no_ov; + D.assign_exp_parallel_with ask d ves no_ov let assign_var_with (b, d) v e = BoxD.assign_var_with b v e; D.assign_var_with d v e @@ -885,22 +887,22 @@ struct D.assign_var_parallel_with d vvs let assign_var_parallel' (b, d) vs v's = (BoxD.assign_var_parallel' b vs v's, D.assign_var_parallel' d vs v's) - let substitute_exp_with (b, d) v e no_ov = - BoxD.substitute_exp_with b v e no_ov; - D.substitute_exp_with d v e no_ov - let substitute_exp_parallel_with (b, d) ves no_ov = - BoxD.substitute_exp_parallel_with b ves no_ov; - D.substitute_exp_parallel_with d ves no_ov + let substitute_exp_with ask (b, d) v e no_ov = + BoxD.substitute_exp_with ask b v e no_ov; + D.substitute_exp_with ask d v e no_ov + let substitute_exp_parallel_with ask (b, d) ves no_ov = + BoxD.substitute_exp_parallel_with ask b ves no_ov; + D.substitute_exp_parallel_with ask d ves no_ov let substitute_var_with (b, d) v1 v2 = BoxD.substitute_var_with b v1 v2; D.substitute_var_with d v1 v2 - let meet_tcons (b, d) c e = (BoxD.meet_tcons b c e, D.meet_tcons d c e) + let meet_tcons ask (b, d) c e = (BoxD.meet_tcons ask b c e, D.meet_tcons ask d c e) let to_lincons_array (_, d) = D.to_lincons_array d let of_lincons_array a = (BoxD.of_lincons_array a, D.of_lincons_array a) let cil_exp_of_lincons1 = D.cil_exp_of_lincons1 - let assert_inv (b, d) e n no_ov = (BoxD.assert_inv b e n no_ov, D.assert_inv d e n no_ov) - let eval_int (_, d) = D.eval_int d + let assert_inv ask (b, d) e n no_ov = (BoxD.assert_inv ask b e n no_ov, D.assert_inv ask d e n no_ov) + let eval_int ask (_, d) = D.eval_int ask d let invariant (b, d) = (* diff via lincons *) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 0192fbebcf..060758a28b 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -202,30 +202,57 @@ struct | x -> Some(x) let get_coeff (t: t) texp = - (*Parses a Texpr to obtain a (variable, offset) pair to repr. a sum of a variable and an offset. - Returns None if the expression is not a sum between a variable (without coefficient) and a constant. - *) + let d = Option.get t.d in + let expr = Array.init (Environment.size t.env) (fun _ -> Z.zero) in + let constant = ref (Z.zero) in + match get_coeff_vec t texp with + | None -> None (*The (in-) equality is not linear, therefore we don't know anything about it. *) + | Some cv's -> + let update (c, v) = + match v with + | None -> constant := Z.(!constant + c) + | Some idx -> match d.(idx) with + | (Some idx_i, c_i) -> constant := Z.(!constant + (c * c_i)); + expr.(idx_i) <- Z.(expr.(idx_i) + c) + | (None, c_i) -> constant := Z.(!constant + (c * c_i)) + in + List.iter update cv's; + let var_count = GobArray.count_matchingi (fun _ a -> a <> Z.zero) expr in + if var_count == 0 then Some (None, !constant) + else if var_count == 1 then ( + let var = Array.findi (fun a -> a <> Z.zero) expr in + if Z.(expr.(var) == Z.one) then Some (Some var, !constant) + else None + ) + else None + + + + (*Parses a Texpr to obtain a (variable, offset) pair to repr. a sum of a variable and an offset. + Returns None if the expression is not a sum between a variable (without coefficient) and a constant. + let exception Not2VarExpr in let sum_next_coefficient (var, current_var_offset, curr_offset) (next_coeff, next_var) = - begin match next_var with - | None -> (* this element represents a constant offset *) - (var, current_var_offset, Z.(curr_offset + next_coeff)) - | Some _ -> (* this element represents a variable with a coefficient - -> it must be always the same variable, else it's not a two-variable equality*) - begin if Option.is_none var || next_var = var then - (next_var, Z.(current_var_offset + next_coeff), curr_offset) - else raise Not2VarExpr end - end in + begin match next_var with + | None -> (* this element represents a constant offset *) + (var, current_var_offset, Z.(curr_offset + next_coeff)) + | Some _ -> (* this element represents a variable with a coefficient + -> it must be always the same variable, else it's not a two-variable equality*) + begin if Option.is_none var || next_var = var then + (next_var, Z.(current_var_offset + next_coeff), curr_offset) + else raise Not2VarExpr end + end in let sum_coefficients summands_list_opt = - Option.map (List.fold_left sum_next_coefficient (None, Z.zero, Z.zero)) summands_list_opt + Option.map (List.fold_left sum_next_coefficient (None, Z.zero, Z.zero)) summands_list_opt in match sum_coefficients (get_coeff_vec t texp) with | exception _ -> None | None -> None | Some (var, var_coeff, offset) -> - if Option.is_none var then Some (None, offset) - else if Z.equal var_coeff Z.one then Some (var, offset) - else None + if Option.is_none var then Some (None, offset) + else if Z.equal var_coeff Z.one then Some (var, offset) + else None + *) let get_coeff t texp = timing_wrap "coeff_vec" (get_coeff t) texp @@ -234,6 +261,7 @@ struct | Some d -> {t with d = Some (EArray.forget_variable d (Environment.dim_of_var t.env var))} | None -> t (* there are no variables in the current environment *) + (* Copy because function is not "with" so should not mutate inputs *) let assign_const t var const = match t.d with | None -> t | Some t_d -> @@ -329,7 +357,7 @@ struct let printXml f x = BatPrintf.fprintf f "\n\n\nequalities-array\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) - let eval_interval = Bounds.bound_texpr + let eval_interval ask = Bounds.bound_texpr exception Contradiction @@ -510,22 +538,22 @@ struct (* no_ov -> no overflow if it's true then there is no overflow -> Convert.texpr1_expr_of_cil_exp handles overflow *) - let assign_exp (t: VarManagement.t) var exp (no_ov: bool Lazy.t) = + let assign_exp ask (t: VarManagement.t) var exp (no_ov: bool Lazy.t) = let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in - match Convert.texpr1_expr_of_cil_exp t t.env exp no_ov with + match Convert.texpr1_expr_of_cil_exp ask t t.env exp no_ov with | texp -> assign_texpr t var texp | exception Convert.Unsupported_CilExp _ -> if is_bot_env t then t else forget_vars t [var] - let assign_exp t var exp no_ov = - let res = assign_exp t var exp no_ov in + let assign_exp ask t var exp no_ov = + let res = assign_exp ask t var exp no_ov in if M.tracing then M.tracel "ops" "assign_exp t:\n %s \n var: %s \n exp: %a\n no_ov: %b -> \n %s\n" (show t) (Var.to_string var) d_exp exp (Lazy.force no_ov) (show res) ; res let assign_var (t: VarManagement.t) v v' = let t = add_vars t [v; v'] in let texpr1 = Texpr1.of_expr (t.env) (Var v') in - assign_texpr t v (Apron.Texpr1.to_expr texpr1) + assign_texpr t v @@ Apron.Texpr1.to_expr texpr1 let assign_var t v v' = let res = assign_var t v v' in @@ -576,17 +604,17 @@ struct if M.tracing then M.tracel "ops" "assign_var parallel'\n"; res - let substitute_exp t var exp no_ov = + let substitute_exp ask t var exp no_ov = let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in - let res = assign_exp t var exp no_ov in + let res = assign_exp ask t var exp no_ov in forget_vars res [var] - let substitute_exp t var exp ov = - let res = substitute_exp t var exp ov + let substitute_exp ask t var exp ov = + let res = substitute_exp ask t var exp ov in if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %s \n exp: %a \n -> \n %s\n" (show t) (Var.to_string var) d_exp exp (show res); res - let substitute_exp t var exp ov = timing_wrap "substitution" (substitute_exp t var exp) ov + let substitute_exp ask t var exp ov = timing_wrap "substitution" (substitute_exp ask t var exp) ov let show_coeff_vec l (env : Environment.t) = let show_element e = match e with @@ -612,7 +640,7 @@ struct -> does not have types (overflow is type dependent) *) - let meet_tcons t tcons original_expr no_ov = + let meet_tcons ask t tcons original_expr no_ov = (* The expression is evaluated using an array of coefficients. The first element of the array belongs to the constant followed by the coefficients of all variables depending on the result in the array after the evaluating including resolving the constraints in t.d the tcons can be evaluated and additional constraints can be added to t.d *) match t.d with @@ -693,13 +721,13 @@ struct e.g. an if statement is encountered in the C code. *) - let assert_constraint d e negate (no_ov: bool Lazy.t) = + let assert_constraint ask d e negate (no_ov: bool Lazy.t) = if M.tracing then M.tracel "assert_constraint" "assert_constraint with expr: %a %b\n" d_exp e (Lazy.force no_ov); - match Convert.tcons1_of_cil_exp d d.env e negate no_ov with - | tcons1 -> meet_tcons d tcons1 e no_ov + match Convert.tcons1_of_cil_exp ask d d.env e negate no_ov with + | tcons1 -> meet_tcons ask d tcons1 e no_ov | exception Convert.Unsupported_CilExp _ -> d - let assert_constraint d e negate no_ov = timing_wrap "assert_constraint" (assert_constraint d e negate) no_ov + let assert_constraint ask d e negate no_ov = timing_wrap "assert_constraint" (assert_constraint ask d e negate) no_ov let relift t = t diff --git a/src/cdomains/apron/relationDomain.apron.ml b/src/cdomains/apron/relationDomain.apron.ml index 48720b0382..abef848521 100644 --- a/src/cdomains/apron/relationDomain.apron.ml +++ b/src/cdomains/apron/relationDomain.apron.ml @@ -112,17 +112,17 @@ sig (** Lazy bool ov parameter has been added to functions where functions of the Convert module are used. This is to also to make used of the improved overflow handling. *) - val assign_exp : t -> var -> exp -> bool Lazy.t -> t + val assign_exp : Queries.ask -> t -> var -> exp -> bool Lazy.t -> t val assign_var : t -> var -> var -> t val assign_var_parallel_with : t -> (var * var) list -> unit val assign_var_parallel' : t -> var list -> var list -> t - val substitute_exp : t -> var -> exp -> bool Lazy.t -> t + val substitute_exp : Queries.ask -> t -> var -> exp -> bool Lazy.t -> t val unify: t -> t -> t val marshal: t -> marshal val unmarshal: marshal -> t val mem_var: t -> var -> bool - val assert_inv : t -> exp -> bool -> bool Lazy.t -> t - val eval_int : t -> exp -> bool Lazy.t -> Queries.ID.t + val assert_inv : Queries.ask -> t -> exp -> bool -> bool Lazy.t -> t + val eval_int : Queries.ask -> t -> exp -> bool Lazy.t -> Queries.ID.t end module type S3 = diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index be82c114e2..07586aabf6 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -1,5 +1,4 @@ (** Relational value domain utilities. *) - open GoblintCil open Batteries open GobApron @@ -90,7 +89,7 @@ struct (** This version with an overflow check is used by the apron domains such as polyhedra and octagons. They do the overflow handling while they convert the expression to Texpr1. *) let overflow_handling_apron no_ov ik env expr d exp = - if not no_ov then ( + if not (Lazy.force no_ov) then ( let (type_min, type_max) = IntDomain.Size.range ik in let texpr1 = Texpr1.of_expr env expr in match Bounds.bound_texpr d texpr1 with @@ -110,9 +109,10 @@ struct when the expression is not an integer expression, for example if it is a float expression. *) end - let texpr1_expr_of_cil_exp_with_overflow_check d env exp no_ov overflow_handling = + let texpr1_expr_of_cil_exp_old d env exp no_ov = (* recurse without env argument *) - let rec texpr1_expr_of_cil_exp = function + let rec texpr1_expr_of_cil_exp exp = + match exp with | Lval (Var v, NoOffset) when Tracked.varinfo_tracked v -> if not v.vglob || Arg.allow_global then let var = @@ -147,39 +147,162 @@ struct | BinOp (Mod, e1, e2, _) -> Binop (Mod, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) | CastE (TInt (t_ik, _) as t, e) -> - begin match IntDomain.should_ignore_overflow t_ik || IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) - | true -> - Unop (Cast, texpr1_expr_of_cil_exp e, Int, Zero) (* TODO: what does Apron Cast actually do? just for floating point and rounding? *) - | false + begin match IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) + | true -> texpr1_expr_of_cil_exp e + | false + | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) + | exception Invalid_argument _ -> (* get_ikind in is_cast_injective *) + raise (Unsupported_CilExp (Cast_not_injective t)) + end + | _ -> + raise (Unsupported_CilExp Exp_not_supported) + in overflow_handling_apron no_ov ik env expr d exp; + expr + | exception (Cilfacade.TypeOfError _ as e) + | exception (Invalid_argument _ as e) -> + raise (Unsupported_CilExp (Exp_typeOf e)) + in + texpr1_expr_of_cil_exp exp + + + let texpr1_expr_of_cil_exp_with_overflow_check (ask: Queries.ask) d env exp no_ov overflow_handling = + let query e ik = + let res = match ask.f (EvalInt e) with + | `Bot -> raise (Unsupported_CilExp Exp_not_supported) (* This should never happen according to Michael Schwarz *) + | `Top -> IntDomain.IntDomTuple.top_of ik + | `Lifted x -> x (* According to base.ml:704 cast should be unnecessary because it should be taken care of by EvalInt. *) + in + (* If the returned interval is top of the expected ikind (i.e. the value is unknown ) or the returned interval is in range of the expected interval, return top + - If top is returned the expression will be rewritten. + - If a constant is returned this specific value is casted to the expected ikind value + - else we got an interval with unsupported bounds i.e. the value expression is known to be unknown and needs casting, which we do not support i.e. the expression is not supported*) + let top = IntDomain.IntDomTuple.is_top_of ik res in + (* || (Option.is_some min && Option.is_some max && (minimal != maximal && (Option.get min >= minimal || Option.get max <= maximal))) in*) + if top then IntDomain.IntDomTuple.top_of ik else res + in + (* recurse without env argument *) + let rec texpr1_expr_of_cil_exp ask exp = + match exp with + | Lval (Var v, NoOffset) when Tracked.varinfo_tracked v -> + if not v.vglob || Arg.allow_global then + let var = + if v.vglob then + V.global v + else + V.local v + in + if Environment.mem_var env var then + Var var + else + raise (Unsupported_CilExp (Var_not_found v)) + else + failwith "texpr1_expr_of_cil_exp: globals must be replaced with temporary locals" + | Const (CInt (i, _, _)) -> + Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z i))) + | exp -> + match Cilfacade.get_ikind_exp exp with + | ik -> + let expr = + let simplify e = + let ikind = Cilfacade.get_ikind_exp e in (* TODO AWE: raise unsupported cil exception on error *) + let simp = query e ikind in + let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to ikind simp in + if Option.is_some const then Const (CInt (Option.get const, ikind, None)) + else e + in + match exp with + | UnOp (Neg, e, _) -> + Unop (Neg, texpr1_expr_of_cil_exp ask @@ simplify e, Int, Near) + | BinOp (PlusA, e1, e2, _) -> + Binop (Add, texpr1_expr_of_cil_exp ask @@ simplify e1, texpr1_expr_of_cil_exp ask @@ simplify e2, Int, Near) + | BinOp (MinusA, e1, e2, _) -> + Binop (Sub, texpr1_expr_of_cil_exp ask @@ simplify e1, texpr1_expr_of_cil_exp ask @@ simplify e2, Int, Near) + | BinOp (Mult, e1, e2, _) -> + Binop (Mul, texpr1_expr_of_cil_exp ask @@ simplify e1, texpr1_expr_of_cil_exp ask @@ simplify e2, Int, Near) + | BinOp (Div, e1, e2, _) -> + Binop (Div, texpr1_expr_of_cil_exp ask @@ simplify e1, texpr1_expr_of_cil_exp ask @@ simplify e2, Int, Zero) + | BinOp (Mod, e1, e2, _) -> + Binop (Mod, texpr1_expr_of_cil_exp ask @@ simplify e1, texpr1_expr_of_cil_exp ask @@ simplify e2, Int, Near) + | CastE (TInt (t_ik, _) as t, e) -> + begin match IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) + | true -> texpr1_expr_of_cil_exp ask @@ simplify e + | false -> + let res = query e @@ Cilfacade.get_ikind_exp e in + let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to t_ik res in + if Option.is_some const then Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z (Option.get const)))) + else if IntDomain.IntDomTuple.is_top_of t_ik res then raise (Unsupported_CilExp (Cast_not_injective t)) + else ( + let (minimal, maximal) = IntDomain.Size.range t_ik in + match IntDomain.IntDomTuple.minimal res, IntDomain.IntDomTuple.maximal res with + | Some min, Some max when min >= minimal && max <= maximal -> texpr1_expr_of_cil_exp ask e + | _ -> raise (Unsupported_CilExp (Cast_not_injective t))) | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) | exception Invalid_argument _ -> (* get_ikind in is_cast_injective *) raise (Unsupported_CilExp (Cast_not_injective t)) end | _ -> raise (Unsupported_CilExp Exp_not_supported) - in overflow_handling no_ov ik env expr d exp; + in + overflow_handling no_ov ik env expr d exp; expr | exception (Cilfacade.TypeOfError _ as e) | exception (Invalid_argument _ as e) -> raise (Unsupported_CilExp (Exp_typeOf e)) in - texpr1_expr_of_cil_exp exp + (* only if we are sure that no overflow / undefined behavior happens we convert the expression *) + texpr1_expr_of_cil_exp ask exp - let texpr1_expr_of_cil_exp d env exp no_ov = + let texpr1_expr_of_cil_exp ask d env exp no_ov = let exp = Cil.constFold false exp in - if Ov.do_overflow_check then texpr1_expr_of_cil_exp_with_overflow_check d env exp (Lazy.force no_ov) overflow_handling_apron - else texpr1_expr_of_cil_exp_with_overflow_check d env exp no_ov no_ov_overflow_handling + if Ov.do_overflow_check then texpr1_expr_of_cil_exp_with_overflow_check ask d env exp no_ov overflow_handling_apron + else texpr1_expr_of_cil_exp_with_overflow_check ask d env exp no_ov no_ov_overflow_handling - let texpr1_of_cil_exp d env e no_ov = - Texpr1.of_expr env (texpr1_expr_of_cil_exp d env e no_ov) + let texpr1_of_cil_exp ask d env e no_ov = + let e = Cil.constFold false e in + let res = texpr1_expr_of_cil_exp ask d env e no_ov in + Texpr1.of_expr env res + + let tcons1_of_cil_exp_old d env e negate no_ov = + let (texpr1_plus, texpr1_minus, typ) = + match e with + | BinOp (r, e1, e2, _) -> + let texpr1_1 = texpr1_expr_of_cil_exp_old d env e1 no_ov in + let texpr1_2 = texpr1_expr_of_cil_exp_old d env e2 no_ov in + (* Apron constraints always compare with 0 and only have comparisons one way *) + begin match r with + | Lt -> (texpr1_2, texpr1_1, SUP) (* e1 < e2 ==> e2 - e1 > 0 *) + | Gt -> (texpr1_1, texpr1_2, SUP) (* e1 > e2 ==> e1 - e2 > 0 *) + | Le -> (texpr1_2, texpr1_1, SUPEQ) (* e1 <= e2 ==> e2 - e1 >= 0 *) + | Ge -> (texpr1_1, texpr1_2, SUPEQ) (* e1 >= e2 ==> e1 - e2 >= 0 *) + | Eq -> (texpr1_1, texpr1_2, EQ) (* e1 == e2 ==> e1 - e2 == 0 *) + | Ne -> (texpr1_1, texpr1_2, DISEQ) (* e1 != e2 ==> e1 - e2 != 0 *) + | _ -> raise (Unsupported_CilExp BinOp_not_supported) + end + | _ -> raise (Unsupported_CilExp Exp_not_supported) + in + let inverse_typ = function + | EQ -> DISEQ + | DISEQ -> EQ + | SUPEQ -> SUP + | SUP -> SUPEQ + | EQMOD _ -> failwith "tcons1_of_cil_exp: cannot invert EQMOD" + in + let (texpr1_plus, texpr1_minus, typ) = + if negate then + (texpr1_minus, texpr1_plus, inverse_typ typ) + else + (texpr1_plus, texpr1_minus, typ) + in + let texpr1' = Binop (Sub, texpr1_plus, texpr1_minus, Int, Near) in + Tcons1.make (Texpr1.of_expr env texpr1') typ - let tcons1_of_cil_exp d env e negate no_ov = + let tcons1_of_cil_exp ask d env e negate no_ov = let e = Cil.constFold false e in let (texpr1_plus, texpr1_minus, typ) = match e with | BinOp (r, e1, e2, _) -> - let texpr1_1 = texpr1_expr_of_cil_exp d env e1 no_ov in - let texpr1_2 = texpr1_expr_of_cil_exp d env e2 no_ov in + let texpr1_1 = texpr1_expr_of_cil_exp ask d env e1 no_ov in + let texpr1_2 = texpr1_expr_of_cil_exp ask d env e2 no_ov in (* Apron constraints always compare with 0 and only have comparisons one way *) begin match r with | Lt -> (texpr1_2, texpr1_1, SUP) (* e1 < e2 ==> e2 - e1 > 0 *) @@ -405,8 +528,8 @@ sig val env: t -> Environment.t - val assert_constraint: t -> exp -> bool -> bool Lazy.t -> t - val eval_interval : t -> Texpr1.t -> Z.t option * Z.t option + val assert_constraint: Queries.ask -> t -> exp -> bool -> bool Lazy.t -> t + val eval_interval : Queries.ask -> t -> Texpr1.t -> Z.t option * Z.t option end module Tracked: RelationDomain.Tracked = @@ -443,7 +566,7 @@ struct (* TODO: move logic-handling assert_constraint from Apron back to here, after fixing affeq bot-bot join *) (** Assert any expression. *) - let assert_inv d e negate no_ov = + let assert_inv ask d e negate no_ov = let e' = if exp_is_constraint e then e @@ -451,26 +574,26 @@ struct (* convert non-constraint expression, such that we assert(e != 0) *) BinOp (Ne, e, zero, intType) in - assert_constraint d e' negate no_ov + assert_constraint ask d e' negate no_ov - let check_assert d e no_ov = - if is_bot_env (assert_inv d e false no_ov) then + let check_assert ask d e no_ov = + if is_bot_env (assert_inv ask d e false no_ov) then `False - else if is_bot_env (assert_inv d e true no_ov) then + else if is_bot_env (assert_inv ask d e true no_ov) then `True else `Top (** Evaluate non-constraint expression as interval. *) - let eval_interval_expr d e no_ov = - match Convert.texpr1_of_cil_exp d (env d) e no_ov with + let eval_interval_expr ask d e no_ov = + match Convert.texpr1_of_cil_exp ask d (env d) e no_ov with (* Resolve AWE: delete no_ov flags. *) | texpr1 -> - let c = eval_interval d texpr1 in c + let c = eval_interval ask d texpr1 in c | exception Convert.Unsupported_CilExp _ -> (None, None) (** Evaluate constraint or non-constraint expression as integer. *) - let eval_int d e no_ov = + let eval_int ask d e no_ov = let module ID = Queries.ID in match Cilfacade.get_ikind_exp e with | exception Cilfacade.TypeOfError _ @@ -479,12 +602,12 @@ struct | ik -> if M.tracing then M.trace "relation" "eval_int: exp_is_constraint %a = %B\n" d_plainexp e (exp_is_constraint e); if exp_is_constraint e then - match check_assert d e no_ov with + match check_assert ask d e no_ov with | `True -> ID.of_bool ik true | `False -> ID.of_bool ik false | `Top -> ID.top_of ik else - match eval_interval_expr d e no_ov with + match eval_interval_expr ask d e no_ov with | (Some min, Some max) -> ID.of_interval ~suppress_ovwarn:true ik (min, max) | (Some min, None) -> ID.starting ~suppress_ovwarn:true ik min | (None, Some max) -> ID.ending ~suppress_ovwarn:true ik max diff --git a/tests/regression/77-lin2vareq/28-narrowing.c b/tests/regression/77-lin2vareq/28-narrowing.c index 7e4b5a246e..b347e4e466 100644 --- a/tests/regression/77-lin2vareq/28-narrowing.c +++ b/tests/regression/77-lin2vareq/28-narrowing.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq +// SKIP PARAM: --set ana.activated[+] lin2vareq --enable ana.int.interval #include int g = 0; From 63455d8ecf124fe16d5691a3948884b8b55468b0 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 31 Jan 2024 15:08:18 +0100 Subject: [PATCH 156/280] changed ov to no_ov in some functions --- src/cdomains/apron/affineEqualityDomain.apron.ml | 6 +++--- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 6 +++--- src/cdomains/apron/relationDomain.apron.ml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 91fa063b25..5163b562be 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -494,12 +494,12 @@ struct let res = assign_exp ask t var exp no_ov in forget_vars res [var] - let substitute_exp ask t var exp ov = - let res = substitute_exp ask t var exp ov in + let substitute_exp ask t var exp no_ov = + let res = substitute_exp ask t var exp no_ov in if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %s \n exp: %a \n -> \n %s\n" (show t) (Var.to_string var) d_exp exp (show res); res - let substitute_exp ask t var exp ov = timing_wrap "substitution" (substitute_exp ask t var exp) ov + let substitute_exp ask t var exp no_ov = timing_wrap "substitution" (substitute_exp ask t var exp) no_ov (** Assert a constraint expression. diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 5456334ee3..1541b6fce9 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -605,12 +605,12 @@ struct let res = assign_exp ask t var exp no_ov in forget_vars res [var] - let substitute_exp ask t var exp ov = - let res = substitute_exp ask t var exp ov + let substitute_exp ask t var exp no_ov = + let res = substitute_exp ask t var exp no_ov in if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %s \n exp: %a \n -> \n %s\n" (show t) (Var.to_string var) d_exp exp (show res); res - let substitute_exp ask t var exp ov = timing_wrap "substitution" (substitute_exp ask t var exp) ov + let substitute_exp ask t var exp no_ov = timing_wrap "substitution" (substitute_exp ask t var exp) no_ov let show_coeff_vec l (env : Environment.t) = let show_element e = match e with diff --git a/src/cdomains/apron/relationDomain.apron.ml b/src/cdomains/apron/relationDomain.apron.ml index abef848521..ba85f9544c 100644 --- a/src/cdomains/apron/relationDomain.apron.ml +++ b/src/cdomains/apron/relationDomain.apron.ml @@ -109,7 +109,7 @@ sig val keep_filter : t -> (var -> bool) -> t val forget_vars : t -> var list -> t - (** Lazy bool ov parameter has been added to functions where functions of the Convert module are used. + (** Lazy bool no_ov parameter has been added to functions where functions of the Convert module are used. This is to also to make used of the improved overflow handling. *) val assign_exp : Queries.ask -> t -> var -> exp -> bool Lazy.t -> t From b3851533f6c8e932f8fddde6641be80a1219b2ae Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 31 Jan 2024 15:21:46 +0100 Subject: [PATCH 157/280] fixed conflict in base.ml --- src/analyses/base.ml | 672 +++++++++++++++++++++---------------------- 1 file changed, 336 insertions(+), 336 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 51385ee351..35a80bad5b 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -877,9 +877,9 @@ struct | CastE (t, Const (CStr (x,e))) -> (* VD.top () *) eval_rv ~ctx st (Const (CStr (x,e))) (* TODO safe? *) | CastE (t, exp) -> (let v = eval_rv ~ctx st exp in - try + try VD.cast ~torg:(Cilfacade.typeOf exp) t v - with Cilfacade.TypeOfError _ -> + with Cilfacade.TypeOfError _ -> VD.cast t v) | SizeOf _ | Real _ @@ -1236,7 +1236,7 @@ struct match Cilfacade.get_ikind_exp exp with | exception _ -> BoolDomain.MayBool.top () | ik -> - let exp_is_top = match eval_rv_ask_evalint (Analyses.ask_of_ctx ctx) ctx.global ctx.local exp with + let exp_is_top = match eval_rv_ask_evalint ~ctx:ctx ctx.local exp with | Int i ->Queries.ID.is_top_of ik (`Lifted i) | _ -> true | exception (IntDomain.ArithmeticOnIntegerBot _) -> true in @@ -2335,352 +2335,352 @@ struct end in let st = match desc.special args, f.vname with - | Memset { dest; ch; count; }, _ -> - (* TODO: check count *) - let eval_ch = eval_rv ~ctx st ch in - let dest_a, dest_typ = addr_type_of_exp dest in - let value = - match eval_ch with - | Int i when ID.to_int i = Some Z.zero -> - VD.zero_init_value dest_typ - | _ -> - VD.top_value dest_typ - in - set ~ctx st dest_a dest_typ value - | Bzero { dest; count; }, _ -> - (* TODO: share something with memset special case? *) - (* TODO: check count *) - let dest_a, dest_typ = addr_type_of_exp dest in - let value = VD.zero_init_value dest_typ in - set ~ctx st dest_a dest_typ value - | Memcpy { dest = dst; src; n; }, _ -> (* TODO: use n *) - memory_copying dst src (Some n) - | Strcpy { dest = dst; src; n }, _ -> string_manipulation dst src None false None (fun ar1 ar2 -> Array (CArrays.string_copy ar1 ar2 (eval_n n))) - | Strcat { dest = dst; src; n }, _ -> string_manipulation dst src None false None (fun ar1 ar2 -> Array (CArrays.string_concat ar1 ar2 (eval_n n))) - | Strlen s, _ -> - begin match lv with - | Some lv_val -> - let dest_a = eval_lv ~ctx st lv_val in - let dest_typ = Cilfacade.typeOfLval lv_val in - let v = eval_rv ~ctx st s in - let a = address_from_value v in - let value:value = - (* if s string literal, compute strlen in string literals domain *) - (* TODO: is this reliable? there could be a char* which isn't StrPtr *) - if CilType.Typ.equal (AD.type_of a) charPtrType then - Int (AD.to_string_length a) - (* else compute strlen in array domain *) - else - begin match get ~ctx st a None with - | Array array_s -> Int (CArrays.to_string_length array_s) - | _ -> VD.top_value (unrollType dest_typ) - end in - set ~ctx st dest_a dest_typ value - | None -> st - end - | Strstr { haystack; needle }, _ -> - begin match lv with - | Some lv_val -> - (* check if needle is a substring of haystack in string literals domain if haystack and needle are string literals, - else check in null bytes domain if both haystack and needle are / can be transformed to an array domain representation; - if needle is substring, assign the substring of haystack starting at the first occurrence of needle to dest, - if it surely isn't, assign a null_ptr *) - string_manipulation haystack needle lv true (Some (fun h_a n_a -> Address (AD.substring_extraction h_a n_a))) - (fun h_ar n_ar -> match CArrays.substring_extraction h_ar n_ar with - | CArrays.IsNotSubstr -> Address (AD.null_ptr) - | CArrays.IsSubstrAtIndex0 -> Address (eval_lv ~ctx st (mkMem ~addr:(Cil.stripCasts haystack) ~off:NoOffset)) - | CArrays.IsMaybeSubstr -> Address (AD.join (eval_lv ~ctx st - (mkMem ~addr:(Cil.stripCasts haystack) ~off:(Index (Offset.Index.Exp.any, NoOffset)))) (AD.null_ptr))) - | None -> st - end - | Strcmp { s1; s2; n }, _ -> - begin match lv with - | Some _ -> - (* when s1 and s2 are string literals, compare both completely or their first n characters in the string literals domain; - else compare them in the null bytes array domain if they are / can be transformed to an array domain representation *) - string_manipulation s1 s2 lv false (Some (fun s1_a s2_a -> Int (AD.string_comparison s1_a s2_a (eval_n n)))) - (fun s1_ar s2_ar -> Int (CArrays.string_comparison s1_ar s2_ar (eval_n n))) - | None -> st - end - | Abort, _ -> raise Deadcode - | ThreadExit { ret_val = exp }, _ -> - begin match ThreadId.get_current (Analyses.ask_of_ctx ctx) with - | `Lifted tid -> - ( - let rv = eval_rv ~ctx ctx.local exp in - ctx.sideg (V.thread tid) (G.create_thread rv); - (* TODO: emit thread return event so other analyses are aware? *) - (* TODO: publish still needed? *) - publish_all ctx `Return; (* like normal return *) - let ask = Analyses.ask_of_ctx ctx in - match ThreadId.get_current ask with - | `Lifted tid when ThreadReturn.is_current ask -> - ignore @@ Priv.thread_return ask (priv_getg ctx.global) (priv_sideg ctx.sideg) tid st - | _ -> ()) - | _ -> () - end; - raise Deadcode - | MutexAttrSetType {attr = attr; typ = mtyp}, _ -> - begin - let get_type lval = - let address = eval_lv ~ctx st lval in - AD.type_of address + | Memset { dest; ch; count; }, _ -> + (* TODO: check count *) + let eval_ch = eval_rv ~ctx st ch in + let dest_a, dest_typ = addr_type_of_exp dest in + let value = + match eval_ch with + | Int i when ID.to_int i = Some Z.zero -> + VD.zero_init_value dest_typ + | _ -> + VD.top_value dest_typ in - let dst_lval = mkMem ~addr:(Cil.stripCasts attr) ~off:NoOffset in - let dest_typ = get_type dst_lval in - let dest_a = eval_lv ~ctx st dst_lval in - match eval_rv ~ctx st mtyp with - | Int x -> - begin - match ID.to_int x with - | Some z -> - if M.tracing then M.tracel "attr" "setting\n"; - set ~ctx st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.of_int z)) - | None -> set ~ctx st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.top ())) - end - | _ -> set ~ctx st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.top ())) - end - | Identity e, _ -> - begin match lv with - | Some x -> assign ctx x e - | None -> ctx.local - end - (**Floating point classification and trigonometric functions defined in c99*) - | Math { fun_args; }, _ -> - let apply_unary fk float_fun x = - let eval_x = eval_rv ~ctx st x in - begin match eval_x with - | Float float_x -> float_fun (FD.cast_to fk float_x) - | _ -> failwith ("non-floating-point argument in call to function "^f.vname) + set ~ctx st dest_a dest_typ value + | Bzero { dest; count; }, _ -> + (* TODO: share something with memset special case? *) + (* TODO: check count *) + let dest_a, dest_typ = addr_type_of_exp dest in + let value = VD.zero_init_value dest_typ in + set ~ctx st dest_a dest_typ value + | Memcpy { dest = dst; src; n; }, _ -> (* TODO: use n *) + memory_copying dst src (Some n) + | Strcpy { dest = dst; src; n }, _ -> string_manipulation dst src None false None (fun ar1 ar2 -> Array (CArrays.string_copy ar1 ar2 (eval_n n))) + | Strcat { dest = dst; src; n }, _ -> string_manipulation dst src None false None (fun ar1 ar2 -> Array (CArrays.string_concat ar1 ar2 (eval_n n))) + | Strlen s, _ -> + begin match lv with + | Some lv_val -> + let dest_a = eval_lv ~ctx st lv_val in + let dest_typ = Cilfacade.typeOfLval lv_val in + let v = eval_rv ~ctx st s in + let a = address_from_value v in + let value:value = + (* if s string literal, compute strlen in string literals domain *) + (* TODO: is this reliable? there could be a char* which isn't StrPtr *) + if CilType.Typ.equal (AD.type_of a) charPtrType then + Int (AD.to_string_length a) + (* else compute strlen in array domain *) + else + begin match get ~ctx st a None with + | Array array_s -> Int (CArrays.to_string_length array_s) + | _ -> VD.top_value (unrollType dest_typ) + end in + set ~ctx st dest_a dest_typ value + | None -> st end - in - let apply_binary fk float_fun x y = - let eval_x = eval_rv ~ctx st x in - let eval_y = eval_rv ~ctx st y in - begin match eval_x, eval_y with - | Float float_x, Float float_y -> float_fun (FD.cast_to fk float_x) (FD.cast_to fk float_y) - | _ -> failwith ("non-floating-point argument in call to function "^f.vname) + | Strstr { haystack; needle }, _ -> + begin match lv with + | Some lv_val -> + (* check if needle is a substring of haystack in string literals domain if haystack and needle are string literals, + else check in null bytes domain if both haystack and needle are / can be transformed to an array domain representation; + if needle is substring, assign the substring of haystack starting at the first occurrence of needle to dest, + if it surely isn't, assign a null_ptr *) + string_manipulation haystack needle lv true (Some (fun h_a n_a -> Address (AD.substring_extraction h_a n_a))) + (fun h_ar n_ar -> match CArrays.substring_extraction h_ar n_ar with + | CArrays.IsNotSubstr -> Address (AD.null_ptr) + | CArrays.IsSubstrAtIndex0 -> Address (eval_lv ~ctx st (mkMem ~addr:(Cil.stripCasts haystack) ~off:NoOffset)) + | CArrays.IsMaybeSubstr -> Address (AD.join (eval_lv ~ctx st + (mkMem ~addr:(Cil.stripCasts haystack) ~off:(Index (Offset.Index.Exp.any, NoOffset)))) (AD.null_ptr))) + | None -> st end - in - let apply_abs ik x = - let eval_x = eval_rv ~ctx st x in - begin match eval_x with - | Int int_x -> - let xcast = ID.cast_to ik int_x in - (* the absolute value of the most-negative value is out of range for 2'complement types *) - (match (ID.minimal xcast), (ID.minimal (ID.top_of ik)) with - | _, None - | None, _ -> ID.top_of ik - | Some mx, Some mm when Z.equal mx mm -> ID.top_of ik - | _, _ -> - let x1 = ID.neg (ID.meet (ID.ending ik Z.zero) xcast) in - let x2 = ID.meet (ID.starting ik Z.zero) xcast in - ID.join x1 x2 - ) - | _ -> failwith ("non-integer argument in call to function "^f.vname) + | Strcmp { s1; s2; n }, _ -> + begin match lv with + | Some _ -> + (* when s1 and s2 are string literals, compare both completely or their first n characters in the string literals domain; + else compare them in the null bytes array domain if they are / can be transformed to an array domain representation *) + string_manipulation s1 s2 lv false (Some (fun s1_a s2_a -> Int (AD.string_comparison s1_a s2_a (eval_n n)))) + (fun s1_ar s2_ar -> Int (CArrays.string_comparison s1_ar s2_ar (eval_n n))) + | None -> st end - in - let result:value = - begin match fun_args with - | Nan (fk, str) when Cil.isPointerType (Cilfacade.typeOf str) -> Float (FD.nan_of fk) - | Nan _ -> failwith ("non-pointer argument in call to function "^f.vname) - | Inf fk -> Float (FD.inf_of fk) - | Isfinite x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isfinite x)) - | Isinf x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isinf x)) - | Isnan x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isnan x)) - | Isnormal x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isnormal x)) - | Signbit x -> Int (ID.cast_to IInt (apply_unary FDouble FD.signbit x)) - | Ceil (fk,x) -> Float (apply_unary fk FD.ceil x) - | Floor (fk,x) -> Float (apply_unary fk FD.floor x) - | Fabs (fk, x) -> Float (apply_unary fk FD.fabs x) - | Acos (fk, x) -> Float (apply_unary fk FD.acos x) - | Asin (fk, x) -> Float (apply_unary fk FD.asin x) - | Atan (fk, x) -> Float (apply_unary fk FD.atan x) - | Atan2 (fk, y, x) -> Float (apply_binary fk (fun y' x' -> FD.atan (FD.div y' x')) y x) - | Cos (fk, x) -> Float (apply_unary fk FD.cos x) - | Sin (fk, x) -> Float (apply_unary fk FD.sin x) - | Tan (fk, x) -> Float (apply_unary fk FD.tan x) - | Isgreater (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.gt x y)) - | Isgreaterequal (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.ge x y)) - | Isless (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.lt x y)) - | Islessequal (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.le x y)) - | Islessgreater (x,y) -> Int(ID.c_logor (ID.cast_to IInt (apply_binary FDouble FD.lt x y)) (ID.cast_to IInt (apply_binary FDouble FD.gt x y))) - | Isunordered (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.unordered x y)) - | Fmax (fd, x ,y) -> Float (apply_binary fd FD.fmax x y) - | Fmin (fd, x ,y) -> Float (apply_binary fd FD.fmin x y) - | Sqrt (fk, x) -> Float (apply_unary fk FD.sqrt x) - | Abs (ik, x) -> Int (ID.cast_to ik (apply_abs ik x)) + | Abort, _ -> raise Deadcode + | ThreadExit { ret_val = exp }, _ -> + begin match ThreadId.get_current (Analyses.ask_of_ctx ctx) with + | `Lifted tid -> + ( + let rv = eval_rv ~ctx ctx.local exp in + ctx.sideg (V.thread tid) (G.create_thread rv); + (* TODO: emit thread return event so other analyses are aware? *) + (* TODO: publish still needed? *) + publish_all ctx `Return; (* like normal return *) + let ask = Analyses.ask_of_ctx ctx in + match ThreadId.get_current ask with + | `Lifted tid when ThreadReturn.is_current ask -> + ignore @@ Priv.thread_return ask (priv_getg ctx.global) (priv_sideg ctx.sideg) tid st + | _ -> ()) + | _ -> () + end; + raise Deadcode + | MutexAttrSetType {attr = attr; typ = mtyp}, _ -> + begin + let get_type lval = + let address = eval_lv ~ctx st lval in + AD.type_of address + in + let dst_lval = mkMem ~addr:(Cil.stripCasts attr) ~off:NoOffset in + let dest_typ = get_type dst_lval in + let dest_a = eval_lv ~ctx st dst_lval in + match eval_rv ~ctx st mtyp with + | Int x -> + begin + match ID.to_int x with + | Some z -> + if M.tracing then M.tracel "attr" "setting\n"; + set ~ctx st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.of_int z)) + | None -> set ~ctx st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.top ())) + end + | _ -> set ~ctx st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.top ())) end - in - begin match lv with - | Some lv_val -> set ~ctx st (eval_lv ~ctx st lv_val) (Cilfacade.typeOfLval lv_val) result - | None -> st - end - (* handling thread creations *) - | ThreadCreate _, _ -> - invalidate_ret_lv ctx.local (* actual results joined via threadspawn *) - (* handling thread joins... sort of *) - | ThreadJoin { thread = id; ret_var }, _ -> - let st' = - (* TODO: should invalidate shallowly? https://github.com/goblint/analyzer/pull/1224#discussion_r1405826773 *) - match eval_rv ~ctx st ret_var with - | Int n when GobOption.exists (Z.equal Z.zero) (ID.to_int n) -> st - | Address ret_a -> - begin match eval_rv ~ctx st id with - | Thread a when ValueDomain.Threads.is_top a -> invalidate ~ctx st [ret_var] - | Thread a -> - let v = List.fold VD.join (VD.bot ()) (List.map (fun x -> G.thread (ctx.global (V.thread x))) (ValueDomain.Threads.elements a)) in - (* TODO: is this type right? *) - set ~ctx st ret_a (Cilfacade.typeOf ret_var) v - | _ -> invalidate ~ctx st [ret_var] + | Identity e, _ -> + begin match lv with + | Some x -> assign ctx x e + | None -> ctx.local + end + (**Floating point classification and trigonometric functions defined in c99*) + | Math { fun_args; }, _ -> + let apply_unary fk float_fun x = + let eval_x = eval_rv ~ctx st x in + begin match eval_x with + | Float float_x -> float_fun (FD.cast_to fk float_x) + | _ -> failwith ("non-floating-point argument in call to function "^f.vname) end - | _ -> invalidate ~ctx st [ret_var] - in - let st' = invalidate_ret_lv st' in - Priv.thread_join (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st' - | Unknown, "__goblint_assume_join" -> - let id = List.hd args in - Priv.thread_join ~force:true (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st - | Alloca size, _ -> begin - match lv with - | Some lv -> - let heap_var = AD.of_var (heap_var true ctx) in - (* ignore @@ printf "alloca will allocate %a bytes\n" ID.pretty (eval_int ~ctx size); *) - set_many ~ctx st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~ctx st size, true)); - (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address heap_var)] - | _ -> st - end - | Malloc size, _ -> begin - match lv with - | Some lv -> - let heap_var = - if (get_bool "sem.malloc.fail") - then AD.join (AD.of_var (heap_var false ctx)) AD.null_ptr - else AD.of_var (heap_var false ctx) - in - (* ignore @@ printf "malloc will allocate %a bytes\n" ID.pretty (eval_int ~ctx size); *) - set_many ~ctx st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~ctx st size, true)); - (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address heap_var)] - | _ -> st - end - | Calloc { count = n; size }, _ -> - begin match lv with - | Some lv -> (* array length is set to one, as num*size is done when turning into `Calloc *) - let heap_var = heap_var false ctx in - let add_null addr = - if get_bool "sem.malloc.fail" - then AD.join addr AD.null_ptr (* calloc can fail and return NULL *) - else addr in - let ik = Cilfacade.ptrdiff_ikind () in - let sizeval = eval_int ~ctx st size in - let countval = eval_int ~ctx st n in - if ID.to_int countval = Some Z.one then ( - set_many ~ctx st [ - (add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, false)); - (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_var heap_var))) - ] - ) - else ( - let blobsize = ID.mul (ID.cast_to ik @@ sizeval) (ID.cast_to ik @@ countval) in - (* the memory that was allocated by calloc is set to bottom, but we keep track that it originated from calloc, so when bottom is read from memory allocated by calloc it is turned to zero *) - set_many ~ctx st [ - (add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.one) (Blob (VD.bot (), blobsize, false)))); - (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_mval (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.zero, `NoOffset))))) - ] - ) - | _ -> st - end - | Realloc { ptr = p; size }, _ -> - (* Realloc shouldn't be passed non-dynamically allocated memory *) - check_invalid_mem_dealloc ctx f p; - begin match lv with - | Some lv -> - let p_rv = eval_rv ~ctx st p in - let p_addr = - match p_rv with - | Address a -> a - (* TODO: don't we already have logic for this? *) - | Int i when ID.to_int i = Some Z.zero -> AD.null_ptr - | Int i -> AD.top_ptr - | _ -> AD.top_ptr (* TODO: why does this ever happen? *) - in - let p_addr' = AD.remove NullPtr p_addr in (* realloc with NULL is same as malloc, remove to avoid unknown value from NullPtr access *) - let p_addr_get = get ~ctx st p_addr' None in (* implicitly includes join of malloc value (VD.bot) *) - let size_int = eval_int ~ctx st size in - let heap_val:value = Blob (p_addr_get, size_int, true) in (* copy old contents with new size *) - let heap_addr = AD.of_var (heap_var false ctx) in - let heap_addr' = - if get_bool "sem.malloc.fail" then - AD.join heap_addr AD.null_ptr - else - heap_addr - in - let lv_addr = eval_lv ~ctx st lv in - set_many ~ctx st [ - (heap_addr, TVoid [], heap_val); - (lv_addr, Cilfacade.typeOfLval lv, Address heap_addr'); - ] (* TODO: free (i.e. invalidate) old blob if successful? *) - | None -> - st - end - | Free ptr, _ -> - (* Free shouldn't be passed non-dynamically allocated memory *) - check_invalid_mem_dealloc ctx f ptr; - st - | Assert { exp; refine; _ }, _ -> assert_fn ctx exp refine - | Setjmp { env }, _ -> - let st' = match eval_rv ~ctx st env with - | Address jmp_buf -> - let value = VD.JmpBuf (ValueDomain.JmpBufs.Bufs.singleton (Target (ctx.prev_node, ctx.control_context ())), false) in - let r = set ~ctx st jmp_buf (Cilfacade.typeOf env) value in - if M.tracing then M.tracel "setjmp" "setting setjmp %a on %a -> %a\n" d_exp env D.pretty st D.pretty r; - r - | _ -> failwith "problem?!" - in - begin match lv with - | Some lv -> - set ~ctx st' (eval_lv ~ctx st lv) (Cilfacade.typeOfLval lv) (Int (ID.of_int IInt Z.zero)) - | None -> st' - end - | Longjmp {env; value}, _ -> - let ensure_not_zero (rv:value) = match rv with - | Int i -> - begin match ID.to_bool i with - | Some true -> rv - | Some false -> - M.error "Must: Longjmp with a value of 0 is silently changed to 1"; - Int (ID.of_int (ID.ikind i) Z.one) - | None -> - M.warn "May: Longjmp with a value of 0 is silently changed to 1"; - let ik = ID.ikind i in - Int (ID.join (ID.meet i (ID.of_excl_list ik [Z.zero])) (ID.of_int ik Z.one)) + in + let apply_binary fk float_fun x y = + let eval_x = eval_rv ~ctx st x in + let eval_y = eval_rv ~ctx st y in + begin match eval_x, eval_y with + | Float float_x, Float float_y -> float_fun (FD.cast_to fk float_x) (FD.cast_to fk float_y) + | _ -> failwith ("non-floating-point argument in call to function "^f.vname) end - | _ -> - M.warn ~category:Program "Arguments to longjmp are strange!"; - rv - in - let rv = ensure_not_zero @@ eval_rv ~ctx ctx.local value in - let t = Cilfacade.typeOf value in - set ~ctx ~t_override:t ctx.local (AD.of_var !longjmp_return) t rv (* Not raising Deadcode here, deadcode is raised at a higher level! *) - | Rand, _ -> - begin match lv with - | Some x -> - let result:value = (Int (ID.starting IInt Z.zero)) in - set ~ctx st (eval_lv ~ctx st x) (Cilfacade.typeOfLval x) result - | None -> st - end - | _, _ -> - let st = - special_unknown_invalidate ctx f args + in + let apply_abs ik x = + let eval_x = eval_rv ~ctx st x in + begin match eval_x with + | Int int_x -> + let xcast = ID.cast_to ik int_x in + (* the absolute value of the most-negative value is out of range for 2'complement types *) + (match (ID.minimal xcast), (ID.minimal (ID.top_of ik)) with + | _, None + | None, _ -> ID.top_of ik + | Some mx, Some mm when Z.equal mx mm -> ID.top_of ik + | _, _ -> + let x1 = ID.neg (ID.meet (ID.ending ik Z.zero) xcast) in + let x2 = ID.meet (ID.starting ik Z.zero) xcast in + ID.join x1 x2 + ) + | _ -> failwith ("non-integer argument in call to function "^f.vname) + end + in + let result:value = + begin match fun_args with + | Nan (fk, str) when Cil.isPointerType (Cilfacade.typeOf str) -> Float (FD.nan_of fk) + | Nan _ -> failwith ("non-pointer argument in call to function "^f.vname) + | Inf fk -> Float (FD.inf_of fk) + | Isfinite x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isfinite x)) + | Isinf x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isinf x)) + | Isnan x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isnan x)) + | Isnormal x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isnormal x)) + | Signbit x -> Int (ID.cast_to IInt (apply_unary FDouble FD.signbit x)) + | Ceil (fk,x) -> Float (apply_unary fk FD.ceil x) + | Floor (fk,x) -> Float (apply_unary fk FD.floor x) + | Fabs (fk, x) -> Float (apply_unary fk FD.fabs x) + | Acos (fk, x) -> Float (apply_unary fk FD.acos x) + | Asin (fk, x) -> Float (apply_unary fk FD.asin x) + | Atan (fk, x) -> Float (apply_unary fk FD.atan x) + | Atan2 (fk, y, x) -> Float (apply_binary fk (fun y' x' -> FD.atan (FD.div y' x')) y x) + | Cos (fk, x) -> Float (apply_unary fk FD.cos x) + | Sin (fk, x) -> Float (apply_unary fk FD.sin x) + | Tan (fk, x) -> Float (apply_unary fk FD.tan x) + | Isgreater (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.gt x y)) + | Isgreaterequal (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.ge x y)) + | Isless (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.lt x y)) + | Islessequal (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.le x y)) + | Islessgreater (x,y) -> Int(ID.c_logor (ID.cast_to IInt (apply_binary FDouble FD.lt x y)) (ID.cast_to IInt (apply_binary FDouble FD.gt x y))) + | Isunordered (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.unordered x y)) + | Fmax (fd, x ,y) -> Float (apply_binary fd FD.fmax x y) + | Fmin (fd, x ,y) -> Float (apply_binary fd FD.fmin x y) + | Sqrt (fk, x) -> Float (apply_unary fk FD.sqrt x) + | Abs (ik, x) -> Int (ID.cast_to ik (apply_abs ik x)) + end + in + begin match lv with + | Some lv_val -> set ~ctx st (eval_lv ~ctx st lv_val) (Cilfacade.typeOfLval lv_val) result + | None -> st + end + (* handling thread creations *) + | ThreadCreate _, _ -> + invalidate_ret_lv ctx.local (* actual results joined via threadspawn *) + (* handling thread joins... sort of *) + | ThreadJoin { thread = id; ret_var }, _ -> + let st' = + (* TODO: should invalidate shallowly? https://github.com/goblint/analyzer/pull/1224#discussion_r1405826773 *) + match eval_rv ~ctx st ret_var with + | Int n when GobOption.exists (Z.equal Z.zero) (ID.to_int n) -> st + | Address ret_a -> + begin match eval_rv ~ctx st id with + | Thread a when ValueDomain.Threads.is_top a -> invalidate ~ctx st [ret_var] + | Thread a -> + let v = List.fold VD.join (VD.bot ()) (List.map (fun x -> G.thread (ctx.global (V.thread x))) (ValueDomain.Threads.elements a)) in + (* TODO: is this type right? *) + set ~ctx st ret_a (Cilfacade.typeOf ret_var) v + | _ -> invalidate ~ctx st [ret_var] + end + | _ -> invalidate ~ctx st [ret_var] + in + let st' = invalidate_ret_lv st' in + Priv.thread_join (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st' + | Unknown, "__goblint_assume_join" -> + let id = List.hd args in + Priv.thread_join ~force:true (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st + | Alloca size, _ -> begin + match lv with + | Some lv -> + let heap_var = AD.of_var (heap_var true ctx) in + (* ignore @@ printf "alloca will allocate %a bytes\n" ID.pretty (eval_int ~ctx size); *) + set_many ~ctx st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~ctx st size, true)); + (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address heap_var)] + | _ -> st + end + | Malloc size, _ -> begin + match lv with + | Some lv -> + let heap_var = + if (get_bool "sem.malloc.fail") + then AD.join (AD.of_var (heap_var false ctx)) AD.null_ptr + else AD.of_var (heap_var false ctx) + in + (* ignore @@ printf "malloc will allocate %a bytes\n" ID.pretty (eval_int ~ctx size); *) + set_many ~ctx st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~ctx st size, true)); + (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address heap_var)] + | _ -> st + end + | Calloc { count = n; size }, _ -> + begin match lv with + | Some lv -> (* array length is set to one, as num*size is done when turning into `Calloc *) + let heap_var = heap_var false ctx in + let add_null addr = + if get_bool "sem.malloc.fail" + then AD.join addr AD.null_ptr (* calloc can fail and return NULL *) + else addr in + let ik = Cilfacade.ptrdiff_ikind () in + let sizeval = eval_int ~ctx st size in + let countval = eval_int ~ctx st n in + if ID.to_int countval = Some Z.one then ( + set_many ~ctx st [ + (add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, false)); + (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_var heap_var))) + ] + ) + else ( + let blobsize = ID.mul (ID.cast_to ik @@ sizeval) (ID.cast_to ik @@ countval) in + (* the memory that was allocated by calloc is set to bottom, but we keep track that it originated from calloc, so when bottom is read from memory allocated by calloc it is turned to zero *) + set_many ~ctx st [ + (add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.one) (Blob (VD.bot (), blobsize, false)))); + (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_mval (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.zero, `NoOffset))))) + ] + ) + | _ -> st + end + | Realloc { ptr = p; size }, _ -> + (* Realloc shouldn't be passed non-dynamically allocated memory *) + check_invalid_mem_dealloc ctx f p; + begin match lv with + | Some lv -> + let p_rv = eval_rv ~ctx st p in + let p_addr = + match p_rv with + | Address a -> a + (* TODO: don't we already have logic for this? *) + | Int i when ID.to_int i = Some Z.zero -> AD.null_ptr + | Int i -> AD.top_ptr + | _ -> AD.top_ptr (* TODO: why does this ever happen? *) + in + let p_addr' = AD.remove NullPtr p_addr in (* realloc with NULL is same as malloc, remove to avoid unknown value from NullPtr access *) + let p_addr_get = get ~ctx st p_addr' None in (* implicitly includes join of malloc value (VD.bot) *) + let size_int = eval_int ~ctx st size in + let heap_val:value = Blob (p_addr_get, size_int, true) in (* copy old contents with new size *) + let heap_addr = AD.of_var (heap_var false ctx) in + let heap_addr' = + if get_bool "sem.malloc.fail" then + AD.join heap_addr AD.null_ptr + else + heap_addr + in + let lv_addr = eval_lv ~ctx st lv in + set_many ~ctx st [ + (heap_addr, TVoid [], heap_val); + (lv_addr, Cilfacade.typeOfLval lv, Address heap_addr'); + ] (* TODO: free (i.e. invalidate) old blob if successful? *) + | None -> + st + end + | Free ptr, _ -> + (* Free shouldn't be passed non-dynamically allocated memory *) + check_invalid_mem_dealloc ctx f ptr; + st + | Assert { exp; refine; _ }, _ -> assert_fn ctx exp refine + | Setjmp { env }, _ -> + let st' = match eval_rv ~ctx st env with + | Address jmp_buf -> + let value = VD.JmpBuf (ValueDomain.JmpBufs.Bufs.singleton (Target (ctx.prev_node, ctx.control_context ())), false) in + let r = set ~ctx st jmp_buf (Cilfacade.typeOf env) value in + if M.tracing then M.tracel "setjmp" "setting setjmp %a on %a -> %a\n" d_exp env D.pretty st D.pretty r; + r + | _ -> failwith "problem?!" + in + begin match lv with + | Some lv -> + set ~ctx st' (eval_lv ~ctx st lv) (Cilfacade.typeOfLval lv) (Int (ID.of_int IInt Z.zero)) + | None -> st' + end + | Longjmp {env; value}, _ -> + let ensure_not_zero (rv:value) = match rv with + | Int i -> + begin match ID.to_bool i with + | Some true -> rv + | Some false -> + M.error "Must: Longjmp with a value of 0 is silently changed to 1"; + Int (ID.of_int (ID.ikind i) Z.one) + | None -> + M.warn "May: Longjmp with a value of 0 is silently changed to 1"; + let ik = ID.ikind i in + Int (ID.join (ID.meet i (ID.of_excl_list ik [Z.zero])) (ID.of_int ik Z.one)) + end + | _ -> + M.warn ~category:Program "Arguments to longjmp are strange!"; + rv + in + let rv = ensure_not_zero @@ eval_rv ~ctx ctx.local value in + let t = Cilfacade.typeOf value in + set ~ctx ~t_override:t ctx.local (AD.of_var !longjmp_return) t rv (* Not raising Deadcode here, deadcode is raised at a higher level! *) + | Rand, _ -> + begin match lv with + | Some x -> + let result:value = (Int (ID.starting IInt Z.zero)) in + set ~ctx st (eval_lv ~ctx st x) (Cilfacade.typeOfLval x) result + | None -> st + end + | _, _ -> + let st = + special_unknown_invalidate ctx f args (* * TODO: invalidate vars reachable via args * publish globals * if single-threaded: *call f*, privatize globals * else: spawn f *) - in - (* invalidate lhs in case of assign *) - invalidate_ret_lv st + in + (* invalidate lhs in case of assign *) + invalidate_ret_lv st in if get_bool "sem.noreturn.dead_code" && Cil.hasAttribute "noreturn" f.vattr then raise Deadcode else st From b362951fc2bc8a0a587551840d58240578f84dd1 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 31 Jan 2024 15:28:33 +0100 Subject: [PATCH 158/280] fix semgrep warning --- src/cdomains/apron/sharedFunctions.apron.ml | 26 +++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 848b5cfef2..eb40330455 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -206,8 +206,9 @@ struct let ikind = Cilfacade.get_ikind_exp e in (* TODO AWE: raise unsupported cil exception on error *) let simp = query e ikind in let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to ikind simp in - if Option.is_some const then Const (CInt (Option.get const, ikind, None)) - else e + match const with + | Some c -> Const (CInt (c, ikind, None)) + | None -> e in match exp with | UnOp (Neg, e, _) -> @@ -228,16 +229,17 @@ struct | false -> let res = query e @@ Cilfacade.get_ikind_exp e in let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to t_ik res in - if Option.is_some const then Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z (Option.get const)))) - else if IntDomain.IntDomTuple.is_top_of t_ik res then raise (Unsupported_CilExp (Cast_not_injective t)) - else ( - let (minimal, maximal) = IntDomain.Size.range t_ik in - match IntDomain.IntDomTuple.minimal res, IntDomain.IntDomTuple.maximal res with - | Some min, Some max when min >= minimal && max <= maximal -> texpr1_expr_of_cil_exp ask e - | _ -> raise (Unsupported_CilExp (Cast_not_injective t))) - | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) - | exception Invalid_argument _ -> (* get_ikind in is_cast_injective *) - raise (Unsupported_CilExp (Cast_not_injective t)) + match const with + | Some c -> Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z c))) + | None -> if IntDomain.IntDomTuple.is_top_of t_ik res then raise (Unsupported_CilExp (Cast_not_injective t)) + else ( + let (minimal, maximal) = IntDomain.Size.range t_ik in + match IntDomain.IntDomTuple.minimal res, IntDomain.IntDomTuple.maximal res with + | Some min, Some max when min >= minimal && max <= maximal -> texpr1_expr_of_cil_exp ask e + | _ -> raise (Unsupported_CilExp (Cast_not_injective t))) + | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) + | exception Invalid_argument _ -> (* get_ikind in is_cast_injective *) + raise (Unsupported_CilExp (Cast_not_injective t)) end | _ -> raise (Unsupported_CilExp Exp_not_supported) From 93dcd3cd865548299b340d439bd18483cf5e2ce7 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 31 Jan 2024 15:37:23 +0100 Subject: [PATCH 159/280] fixed indentation --- .../apron/linearTwoVarEqualityDomain.apron.ml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 1541b6fce9..a0bb8e3bdd 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -167,15 +167,13 @@ struct let rec convert_texpr texp = begin match texp with (*If x is a constant, replace it with its const. val. immediately*) - | Cst x -> let of_union union = - let open Coeff in - match union with - | Interval _ -> failwith "Not a constant" - | Scalar x -> begin match x with - | Float x -> raise NotIntegerOffset - | Mpqf x -> [(mpqf_to_Z x, None)] - | Mpfrf x -> raise NotIntegerOffset end in - of_union x + | Cst x -> + (let open Coeff in + match x with + | Interval _ -> failwith "Not a constant" + | Scalar (Float x) -> raise NotIntegerOffset + | Scalar (Mpqf x) -> [(mpqf_to_Z x, None)] + | Scalar (Mpfrf x) -> raise NotIntegerOffset) | Var x -> let var_dim = Environment.dim_of_var t.env x in begin match t.d with From 4fa7042a9fb44219fcf1ce1bcac790ec7cc7b6e1 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 31 Jan 2024 17:16:03 +0100 Subject: [PATCH 160/280] Narrowing test (not working though, but should) --- ...narrowing.c => 28-narrowing-on-steroids.c} | 19 ++++++++----- .../regression/77-lin2vareq/cast-to-short2.c | 27 +++++++++++++++++++ 2 files changed, 39 insertions(+), 7 deletions(-) rename tests/regression/77-lin2vareq/{28-narrowing.c => 28-narrowing-on-steroids.c} (53%) create mode 100644 tests/regression/77-lin2vareq/cast-to-short2.c diff --git a/tests/regression/77-lin2vareq/28-narrowing.c b/tests/regression/77-lin2vareq/28-narrowing-on-steroids.c similarity index 53% rename from tests/regression/77-lin2vareq/28-narrowing.c rename to tests/regression/77-lin2vareq/28-narrowing-on-steroids.c index b347e4e466..d2017086d9 100644 --- a/tests/regression/77-lin2vareq/28-narrowing.c +++ b/tests/regression/77-lin2vareq/28-narrowing-on-steroids.c @@ -1,26 +1,31 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --enable ana.int.interval +// SKIP PARAM: --set ana.activated[+] lin2vareq --enable ana.int.interval --set sem.int.signed_overflow assume_none #include -int g = 0; - int main() { + int a; + a = a % 10; + int b; + int c; + b = a + 1; + c = a + 2; int x; for (x = 0; x < 50; x++) { - g = 1; + a = 1; } if (x > 50) { for (int i = 0; i <= 0; i--) { - g = 57; + c = 57; int y; for (y = 0; y < x; y++) { - g = 42; + b = 42; } } - assert(1); // NOWARN (unreachable) + assert(0); // NOWARN (unreachable) } + assert(b + 1 == c);// SUCCESS } diff --git a/tests/regression/77-lin2vareq/cast-to-short2.c b/tests/regression/77-lin2vareq/cast-to-short2.c new file mode 100644 index 0000000000..c5cc059298 --- /dev/null +++ b/tests/regression/77-lin2vareq/cast-to-short2.c @@ -0,0 +1,27 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none + +#include +int main() { + + unsigned int allbits = -255 - 25; // choose a value which is not representable in short + int signedallbits = allbits; + short unsignedtosigned = allbits; + unsigned short unsignedtounsigned = allbits; + + printf("allbits: %u\n", allbits); + printf("signedallbits: %d\n", signedallbits); + printf("unsignedtosigned: %hd\n", unsignedtosigned); + printf("unsignedtounsigned: %hu\n", unsignedtounsigned); + + if (unsignedtounsigned == 4294967295) { +// __goblint_check(0); // NOWARN (unreachable) + return (-1); + } + if (allbits == 4294967295 && signedallbits == -1 && unsignedtosigned == -1 && + unsignedtounsigned == 65535) { + // __goblint_check(1); // reachable + return (-1); + } + // __goblint_check(0); // NOWARN (unreachable) + return (0); +} From 0e7a2e9415e16f217ccf0568860612ce5a0763ae Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Thu, 1 Feb 2024 13:09:34 +0100 Subject: [PATCH 161/280] unfinished adjustment of maySignedOverflow --- src/analyses/base.ml | 72 +++++++++++-------- .../77-lin2vareq/28-narrowing-on-steroids.c | 2 +- 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 35a80bad5b..e9b257893b 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1233,36 +1233,46 @@ struct Invariant.none let rec exp_may_signed_overflow ctx exp = - match Cilfacade.get_ikind_exp exp with - | exception _ -> BoolDomain.MayBool.top () - | ik -> - let exp_is_top = match eval_rv_ask_evalint ~ctx:ctx ctx.local exp with - | Int i ->Queries.ID.is_top_of ik (`Lifted i) - | _ -> true - | exception (IntDomain.ArithmeticOnIntegerBot _) -> true in - match exp with - | Const _ - | SizeOf _ - | SizeOfStr _ - | AlignOf _ - | AddrOfLabel _ -> false - | Real e - | Imag e - | SizeOfE e - | AlignOfE e - | CastE (_, e) -> exp_may_signed_overflow ctx e - | UnOp (_, e, _) -> - if Cil.isSigned ik && exp_is_top then true - (** if EvalInt returns top, there was probably an overflow. - Otherwise, to be sure that there is no overflow, we need to check each subexpression *) - else exp_may_signed_overflow ctx e - | BinOp (_, e1, e2, _) -> if Cil.isSigned ik && exp_is_top then true else - exp_may_signed_overflow ctx e1 || exp_may_signed_overflow ctx e2 - | Question (e1, e2, e3, _) -> - exp_may_signed_overflow ctx e1 || exp_may_signed_overflow ctx e2 || exp_may_signed_overflow ctx e3 - | Lval lval - | AddrOf lval - | StartOf lval -> lval_may_signed_overflow ctx lval + let res = match Cilfacade.get_ikind_exp exp with + | exception _ -> BoolDomain.MayBool.top () + | ik -> + let check e = + let res = match (Analyses.ask_of_ctx ctx).f (EvalInt e) with + | `Bot -> true (* TODO AWE: Throw exception? But which one? This should never happen according to Michael Schwarz *) + | `Lifted i -> if Queries.ID.is_top_of ik (`Lifted i) then true + else ( + let (minimal, maximal) = IntDomain.Size.range ik in + match IntDomain.IntDomTuple.minimal i, IntDomain.IntDomTuple.maximal i with + | Some min, Some max when min > minimal && max < maximal -> false + | _ -> true ) + | _ -> true in + if M.tracing then M.trace "signed_overflow" "may_signed_overflow-check: %a. Result = %b\n" d_plainexp e res; res + in + match exp with + | Const _ + | SizeOf _ + | SizeOfStr _ + | AlignOf _ + | AddrOfLabel _ -> false + | Real e + | Imag e + | SizeOfE e + | AlignOfE e + | CastE (_, e) -> exp_may_signed_overflow ctx e + | UnOp (_, e, _) -> + if Cil.isSigned ik && check exp then true + (** if EvalInt returns top, there was probably an overflow. + Otherwise, to be sure that there is no overflow, we need to check each subexpression *) + else exp_may_signed_overflow ctx e + | BinOp (_, e1, e2, _) -> + check e1 || check e2 ||exp_may_signed_overflow ctx e1 || exp_may_signed_overflow ctx e2 || (Cil.isSigned ik && check exp) + | Question (e1, e2, e3, _) -> + exp_may_signed_overflow ctx e1 || exp_may_signed_overflow ctx e2 || exp_may_signed_overflow ctx e3 + | Lval lval + | AddrOf lval + | StartOf lval -> lval_may_signed_overflow ctx lval + in + if M.tracing then M.trace "signed_overflow" "base exp_may_signed_overflow %a. Result = %b\n" d_plainexp exp res; res and lval_may_signed_overflow ctx (lval : lval) = let (host, offset) = lval in let host_may_signed_overflow = function @@ -1442,7 +1452,7 @@ struct let g: V.t = Obj.obj g in query_invariant_global ctx g | Q.MaySignedOverflow e -> (let res = exp_may_signed_overflow ctx e in - if M.tracing then M.traceli "signed_overflow" "base exp_may_signed_overflow %a. Result = %b\n" d_plainexp e res; res + if M.tracing then M.trace "signed_overflow" "base exp_may_signed_overflow %a. Result = %b\n" d_plainexp e res; res ) | _ -> Q.Result.top q diff --git a/tests/regression/77-lin2vareq/28-narrowing-on-steroids.c b/tests/regression/77-lin2vareq/28-narrowing-on-steroids.c index d2017086d9..0548021b92 100644 --- a/tests/regression/77-lin2vareq/28-narrowing-on-steroids.c +++ b/tests/regression/77-lin2vareq/28-narrowing-on-steroids.c @@ -2,7 +2,7 @@ #include int main() { - int a; + short a; a = a % 10; int b; int c; From 6dc7dddfd67410f0d57fabe754d457bdf1cc0a99 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 1 Feb 2024 14:53:05 +0100 Subject: [PATCH 162/280] fix the overflow analysis --- src/analyses/base.ml | 78 ++++++++++++++++++++++++++++++--------- src/util/std/gobOption.ml | 4 ++ 2 files changed, 64 insertions(+), 18 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index e9b257893b..02c56ca4d3 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1232,21 +1232,44 @@ struct else Invariant.none + (** + This query returns false if the expression [exp] will definitely not result in an overflow. + + Each subexpression is analyzed to see if an overflow happened. + For each operator in the expression, we use the query EvalInt to approximate the bounds of each + operand and we compute if in the worst case there could be an overflow. + + For now we return true if the expression contains a shift left. + *) let rec exp_may_signed_overflow ctx exp = let res = match Cilfacade.get_ikind_exp exp with | exception _ -> BoolDomain.MayBool.top () | ik -> - let check e = - let res = match (Analyses.ask_of_ctx ctx).f (EvalInt e) with - | `Bot -> true (* TODO AWE: Throw exception? But which one? This should never happen according to Michael Schwarz *) - | `Lifted i -> if Queries.ID.is_top_of ik (`Lifted i) then true - else ( - let (minimal, maximal) = IntDomain.Size.range ik in - match IntDomain.IntDomTuple.minimal i, IntDomain.IntDomTuple.maximal i with - | Some min, Some max when min > minimal && max < maximal -> false - | _ -> true ) - | _ -> true in - if M.tracing then M.trace "signed_overflow" "may_signed_overflow-check: %a. Result = %b\n" d_plainexp e res; res + let checkBinop e1 e2 binop = + match (Analyses.ask_of_ctx ctx).f (EvalInt e1), (Analyses.ask_of_ctx ctx).f (EvalInt e2) with + | `Bot, _ -> false + | _, `Bot -> false + | `Lifted i1, `Lifted i2 -> + ( let (min_ik, max_ik) = IntDomain.Size.range ik in + let (min_i1, max_i1) = (IntDomain.IntDomTuple.minimal i1, IntDomain.IntDomTuple.maximal i1) in + let (min_i2, max_i2) = (IntDomain.IntDomTuple.minimal i2, IntDomain.IntDomTuple.maximal i2) in + let possible_combinations = [binop min_i1 min_i2; binop min_i1 max_i2; binop max_i1 min_i2; binop max_i1 max_i2] in + let min_exp = List.min possible_combinations in + let max_exp = List.max possible_combinations in + match min_exp, max_exp with + | Some min, Some max when min >= min_ik && max <= max_ik -> false + | _ -> true) + | _ -> true in + let checkPredicate e pred = + match (Analyses.ask_of_ctx ctx).f (EvalInt e) with + | `Bot -> false + | `Lifted i -> + (let (min_ik, _) = IntDomain.Size.range ik in + let (min_i, max_i) = (IntDomain.IntDomTuple.minimal i, IntDomain.IntDomTuple.maximal i) in + match min_i with + | Some min when pred min min_ik -> false + | _ -> true) + | _ -> true in match exp with | Const _ @@ -1259,14 +1282,33 @@ struct | SizeOfE e | AlignOfE e | CastE (_, e) -> exp_may_signed_overflow ctx e - | UnOp (_, e, _) -> - if Cil.isSigned ik && check exp then true - (** if EvalInt returns top, there was probably an overflow. - Otherwise, to be sure that there is no overflow, we need to check each subexpression *) - else exp_may_signed_overflow ctx e - | BinOp (_, e1, e2, _) -> - check e1 || check e2 ||exp_may_signed_overflow ctx e1 || exp_may_signed_overflow ctx e2 || (Cil.isSigned ik && check exp) + | UnOp (unop, e, _) -> + (* check if the current operation causes a signed overflow *) + begin match unop with + | Neg -> (* an overflow happens when the lower bound of the interval is less than MIN_INT *) + Cil.isSigned ik && checkPredicate e (Z.lt) + (* operations that do not result in overflow in C: *) + | BNot|LNot -> false + end + (* look for overflow in subexpression *) + || exp_may_signed_overflow ctx e + | BinOp (binop, e1, e2, _) -> + (* check if the current operation causes a signed overflow *) + (Cil.isSigned ik && begin match binop with + | PlusA|PlusPI|IndexPI -> checkBinop e1 e2 (GobOption.unionWith Z.(+)) + | MinusA|MinusPI|MinusPP -> checkBinop e1 e2 (GobOption.unionWith Z.(-)) + | Mult -> checkBinop e1 e2 (GobOption.unionWith Z.mul) + | Div -> checkBinop e1 e2 (GobOption.unionWith Z.div) + | Mod -> (* an overflow happens when the second operand is negative *) + checkPredicate e2 (fun interval_bound _ -> Z.lt interval_bound Z.zero) + (* operations that do not result in overflow in C: *) + | Eq|Shiftrt|BAnd|BOr|BXor|Lt|Gt|Le|Ge|Ne|LAnd|LOr -> false + (* Shiftlt can cause overflow and also undefined behaviour in case the second operand is non-positive*) + | Shiftlt -> true end) + (* look for overflow in subexpression *) + || exp_may_signed_overflow ctx e1 || exp_may_signed_overflow ctx e2 | Question (e1, e2, e3, _) -> + (* does not result in overflow in C *) exp_may_signed_overflow ctx e1 || exp_may_signed_overflow ctx e2 || exp_may_signed_overflow ctx e3 | Lval lval | AddrOf lval diff --git a/src/util/std/gobOption.ml b/src/util/std/gobOption.ml index 3602575317..68517abfb5 100644 --- a/src/util/std/gobOption.ml +++ b/src/util/std/gobOption.ml @@ -6,6 +6,10 @@ let for_all p = function | Some x -> p x | None -> true +let unionWith binop opt1 opt2 = + match opt1, opt2 with + | Some x1, Some x2 -> Some (binop x1 x2) + | _ -> None (** Open this to use applicative functor/monad syntax for {!option}. *) module Syntax = From f483830a3a3c5f9f84186193b2854e355bc9c0c6 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 1 Feb 2024 14:59:55 +0100 Subject: [PATCH 163/280] fix overflow analysis --- src/analyses/base.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 02c56ca4d3..5f0c86f787 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1286,7 +1286,7 @@ struct (* check if the current operation causes a signed overflow *) begin match unop with | Neg -> (* an overflow happens when the lower bound of the interval is less than MIN_INT *) - Cil.isSigned ik && checkPredicate e (Z.lt) + Cil.isSigned ik && checkPredicate e (Z.geq) (* operations that do not result in overflow in C: *) | BNot|LNot -> false end @@ -1300,7 +1300,7 @@ struct | Mult -> checkBinop e1 e2 (GobOption.unionWith Z.mul) | Div -> checkBinop e1 e2 (GobOption.unionWith Z.div) | Mod -> (* an overflow happens when the second operand is negative *) - checkPredicate e2 (fun interval_bound _ -> Z.lt interval_bound Z.zero) + checkPredicate e2 (fun interval_bound _ -> Z.gt interval_bound Z.zero) (* operations that do not result in overflow in C: *) | Eq|Shiftrt|BAnd|BOr|BXor|Lt|Gt|Le|Ge|Ne|LAnd|LOr -> false (* Shiftlt can cause overflow and also undefined behaviour in case the second operand is non-positive*) From 3a0318a379e9cae592cb7b787851832d01e4737a Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 1 Feb 2024 15:05:14 +0100 Subject: [PATCH 164/280] fix overflow analysis --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 5f0c86f787..6866c53baf 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1286,7 +1286,7 @@ struct (* check if the current operation causes a signed overflow *) begin match unop with | Neg -> (* an overflow happens when the lower bound of the interval is less than MIN_INT *) - Cil.isSigned ik && checkPredicate e (Z.geq) + Cil.isSigned ik && checkPredicate e (Z.gt) (* operations that do not result in overflow in C: *) | BNot|LNot -> false end From 118064b4e925a01b3663919b5437f4c577cfafb1 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Thu, 1 Feb 2024 15:08:41 +0100 Subject: [PATCH 165/280] new tests for better overflow checking --- .../77-lin2vareq/32-overflow-on-steroids.c | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/regression/77-lin2vareq/32-overflow-on-steroids.c diff --git a/tests/regression/77-lin2vareq/32-overflow-on-steroids.c b/tests/regression/77-lin2vareq/32-overflow-on-steroids.c new file mode 100644 index 0000000000..c967d9c045 --- /dev/null +++ b/tests/regression/77-lin2vareq/32-overflow-on-steroids.c @@ -0,0 +1,33 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --enable ana.int.interval +#include +#include + +int main(void) { + int x = 10; + int b; + int a; + a = a %2; + b = a + 2; + + if (x + 2147483647 == 2147483657) { + return 0; + } + + __goblint_check(1); + + // Overflow + int c = 2147483647; + c = c + 1; + __goblint_check(c < 2147483647); // UNKNOWN! + + x = 300 % b; + x = x * 1147483647; // should overflow + __goblint_check (x < 2147483647); // UNKNOWN! + + int y = 300 % a; //might overflow + __goblint_check (y < 2147483647); // UNKNOWN! + + int z = y << (a-1); //might overflow + __goblint_check (z < 2147483647); // UNKNOWN! + +} From ea4cdced3d3568a3b46aa19e4757ceca0a9a9f0d Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Thu, 1 Feb 2024 15:25:47 +0100 Subject: [PATCH 166/280] add a thorough overflow test --- .../77-lin2vareq/32-overflow-on-steroids.c | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/tests/regression/77-lin2vareq/32-overflow-on-steroids.c b/tests/regression/77-lin2vareq/32-overflow-on-steroids.c index c967d9c045..b8275bac2f 100644 --- a/tests/regression/77-lin2vareq/32-overflow-on-steroids.c +++ b/tests/regression/77-lin2vareq/32-overflow-on-steroids.c @@ -1,33 +1,43 @@ // SKIP PARAM: --set ana.activated[+] lin2vareq --enable ana.int.interval #include -#include int main(void) { - int x = 10; int b; int a; - a = a %2; - b = a + 2; + int c; // c is an unknown value + a = a % 8; // a is in the interval [-7, 7] - if (x + 2147483647 == 2147483657) { - return 0; - } + b = c; // no overflow + __goblint_check(b == c);// SUCCESS - __goblint_check(1); + b = c * 1; // no overflow + __goblint_check(b == c);// SUCCESS - // Overflow - int c = 2147483647; - c = c + 1; - __goblint_check(c < 2147483647); // UNKNOWN! + b = c ? c : c; // no overflow + __goblint_check(b == c);// SUCCESS - x = 300 % b; - x = x * 1147483647; // should overflow - __goblint_check (x < 2147483647); // UNKNOWN! + b = a + 2; // no overflow + __goblint_check(b == a + 2);// SUCCESS - int y = 300 % a; //might overflow - __goblint_check (y < 2147483647); // UNKNOWN! + b = c + 2; // might overflow + __goblint_check(b == c + 2);// UNKNOWN! - int z = y << (a-1); //might overflow - __goblint_check (z < 2147483647); // UNKNOWN! + b = a - 2; // no overflow + __goblint_check(b == a - 2);// SUCCESS + + b = c - 2; // might overflow + __goblint_check(b == c - 2);// UNKNOWN! + + b = a * 2 - a * 1; // no overflow + __goblint_check(b == a);// SUCCESS + + b = c * 2 - c * 1; // might overflow + __goblint_check(b == c); // UNKNOWN! + + b = (-a) + a; // no overflow + __goblint_check(b == 0); // SUCCESS + + b = (-c) + c; // might overflow + __goblint_check(b == 0); // UNKNOWN! } From b0cda4f532c78d780785715b47994bfecea1c119 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Thu, 1 Feb 2024 15:36:29 +0100 Subject: [PATCH 167/280] next try narrow test --- .../77-lin2vareq/28-narrowing-on-steroids.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/regression/77-lin2vareq/28-narrowing-on-steroids.c b/tests/regression/77-lin2vareq/28-narrowing-on-steroids.c index 0548021b92..58ad376103 100644 --- a/tests/regression/77-lin2vareq/28-narrowing-on-steroids.c +++ b/tests/regression/77-lin2vareq/28-narrowing-on-steroids.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --enable ana.int.interval --set sem.int.signed_overflow assume_none +// SKIP PARAM: --set ana.activated[+] lin2vareq --enable ana.int.interval #include int main() { @@ -11,21 +11,19 @@ int main() { int x; for (x = 0; x < 50; x++) { - a = 1; + x = x+1; } - if (x > 50) { - for (int i = 0; i <= 0; i--) { - c = 57; + for (int i = 0; i <= 50; i++) { + x = 57; - int y; + int y; - for (y = 0; y < x; y++) { - b = 42; - } + for (y = 41 + a; y < i; y++) { + b = 42; } - assert(0); // NOWARN (unreachable) } assert(b + 1 == c);// SUCCESS + return 0; } From 2b0ad773c5893d6887028f593ac1c6a8f400180f Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Thu, 1 Feb 2024 18:12:40 +0100 Subject: [PATCH 168/280] tracing for narrow and widen as well as a narrow test showing that meet in narrow is necessary (specific solver is used to trigger relevant narrow call) --- .../apron/linearTwoVarEqualityDomain.apron.ml | 8 +++++ .../77-lin2vareq/28-narrowing-on-steroids.c | 30 +++++++++---------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index a0bb8e3bdd..754be42752 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -477,12 +477,20 @@ struct if Environment.equal a_env b_env then join a b else b + let widen a b = + let res = widen a b in + if M.tracing then M.tracel "widen" "widen a: %s b: %s -> %s \n" (show a) (show b) (show res) ; + res let remove_rels_with_var x var env imp = let j0 = Environment.dim_of_var env var in if imp then (EArray.forget_variable_with x j0; x) else EArray.forget_variable x j0 let narrow a b = meet a b + let narrow a b = + let res = narrow a b in + if M.tracing then M.tracel "narrow" "narrow a: %s b: %s -> %s \n" (show a) (show b) (show res) ; + res let pretty_diff () (x, y) = dprintf "%s: %a not leq %a" (name ()) pretty x pretty y diff --git a/tests/regression/77-lin2vareq/28-narrowing-on-steroids.c b/tests/regression/77-lin2vareq/28-narrowing-on-steroids.c index 58ad376103..3d4bfb0234 100644 --- a/tests/regression/77-lin2vareq/28-narrowing-on-steroids.c +++ b/tests/regression/77-lin2vareq/28-narrowing-on-steroids.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --enable ana.int.interval +// SKIP PARAM: --set ana.activated[+] lin2vareq --enable ana.int.interval --set solver slr3tp #include int main() { @@ -8,22 +8,22 @@ int main() { int c; b = a + 1; c = a + 2; - int x; - - for (x = 0; x < 50; x++) { - x = x+1; + int x, g; + + for(x=0; x < 50; x++){ + g = 1; } - - - for (int i = 0; i <= 50; i++) { - x = 57; - - int y; - - for (y = 41 + a; y < i; y++) { - b = 42; + b = a + x; + + // x = [50, 50] after narrow + if(b - a > 50){ // live after widen, but dead after narrow + // node after Pos(x>50) is marked dead at the end + // but the loop is not with x = [51,2147483647] + for(int i=0; i<=0 && i > -1000; i--){ + b = 8; } + assert(1); // NOWARN (unreachable) } - assert(b + 1 == c);// SUCCESS + __goblint_check(b == c + 48); return 0; } From 26bafba9fab94468b30edc4577d79d5e6a6c3d4c Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Fri, 2 Feb 2024 16:14:05 +0100 Subject: [PATCH 169/280] small changes requested in the pull request --- .../apron/linearTwoVarEqualityDomain.apron.ml | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 754be42752..4d38705cd5 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -79,7 +79,7 @@ module EqualitiesArray = struct 0 offset_map in let remove_offset_from_array_entry (var, offs) = Option.map (fun var_index -> var_index - offset_map.(var_index)) var, offs in - Array.(copy m |> filteri (fun i _ -> not @@ Array.mem i indexes) (* filter out removed variables*) + Array.(filteri (fun i _ -> not @@ Array.mem i indexes) m (* filter out removed variables*) |> map remove_offset_from_array_entry) (* adjust variable indexes *) let remove_variables_from_domain m cols = timing_wrap "del_cols" (remove_variables_from_domain m) cols @@ -295,18 +295,13 @@ module ExpressionBounds: (SharedFunctions.ConvBounds with type t = VarManagement struct include VarManagement - let bound_texpr t texpr = let texpr = Texpr1.to_expr texpr in - match get_coeff t texpr with - | Some (None, offset) -> Some offset, Some offset + let bound_texpr t texpr = + match get_coeff t (Texpr1.to_expr texpr) with + | Some (None, offset) -> + (if M.tracing then M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string offset) (IntOps.BigIntOps.to_string offset); + Some offset, Some offset) | _ -> None, None - - let bound_texpr d texpr1 = - let res = bound_texpr d texpr1 in - match res with - | Some min, Some max -> if M.tracing then M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string min) (IntOps.BigIntOps.to_string max); res - | _ -> res - let bound_texpr d texpr1 = timing_wrap "bounds calculation" (bound_texpr d) texpr1 end @@ -472,20 +467,15 @@ struct res let widen a b = - let a_env = a.env in - let b_env = b.env in - if Environment.equal a_env b_env then + if Environment.equal a.env b.env then join a b else b + let widen a b = let res = widen a b in if M.tracing then M.tracel "widen" "widen a: %s b: %s -> %s \n" (show a) (show b) (show res) ; res - let remove_rels_with_var x var env imp = - let j0 = Environment.dim_of_var env var in - if imp then (EArray.forget_variable_with x j0; x) else EArray.forget_variable x j0 - let narrow a b = meet a b let narrow a b = let res = narrow a b in @@ -498,13 +488,14 @@ struct let forget_vars t vars = if is_bot_env t || is_top t then t else - let m = Option.get t.d in - if List.is_empty vars then t else - let rec rem_vars m vars' = - begin match vars' with - | [] -> m - | x :: xs -> rem_vars (remove_rels_with_var m x t.env true) xs end - in {d = Some (rem_vars (EArray.copy m) vars); env = t.env} + if List.is_empty vars then t else + let m = EArray.copy @@ Option.get t.d + in + List.iter + (fun var -> + EArray.forget_variable_with m (Environment.dim_of_var t.env var)) + vars; + {d = Some m; env = t.env} let forget_vars t vars = let res = forget_vars t vars in From 7b303dec79611cd89f082af2eabd048946c34ad3 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Mon, 5 Feb 2024 03:04:34 +0100 Subject: [PATCH 170/280] fix compare Z --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 754be42752..5d79ffeb03 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -650,7 +650,8 @@ struct | Some d -> let expr = Array.init (Environment.size t.env) (fun _ -> Z.zero) in let constant = ref (Z.zero) in - if is_bot_env t then bot_env else + if is_bot_env t then bot_env + else match get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with | None -> t (*The (in-) equality is not linear, therefore we don't know anything about it. *) | Some cv's -> @@ -663,7 +664,7 @@ struct | (None, c_i) -> constant := Z.(!constant + (c * c_i)) in List.iter update cv's; - let var_count = GobArray.count_matchingi (fun _ a -> a <> Z.zero) expr in + let var_count = GobArray.count_matchingi (fun _ a -> Z.(a <> Z.zero)) expr in if var_count = 0 then match Tcons1.get_typ tcons with | EQ when Z.equal !constant Z.zero -> t From 9e160a0f6ebbc07f041c4165be0596fd35ec2c72 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Mon, 5 Feb 2024 03:15:33 +0100 Subject: [PATCH 171/280] Some requested adjsutments --- .../apron/linearTwoVarEqualityDomain.apron.ml | 36 ++----------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index f8bc0c188c..f19e27cf74 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -201,7 +201,7 @@ struct let get_coeff (t: t) texp = let d = Option.get t.d in - let expr = Array.init (Environment.size t.env) (fun _ -> Z.zero) in + let expr = Array.make (Environment.size t.env) Z.zero in let constant = ref (Z.zero) in match get_coeff_vec t texp with | None -> None (*The (in-) equality is not linear, therefore we don't know anything about it. *) @@ -215,7 +215,7 @@ struct | (None, c_i) -> constant := Z.(!constant + (c * c_i)) in List.iter update cv's; - let var_count = GobArray.count_matchingi (fun _ a -> a <> Z.zero) expr in + let var_count = BatArray.count_matching (fun a -> a <> Z.zero) expr in if var_count == 0 then Some (None, !constant) else if var_count == 1 then ( let var = Array.findi (fun a -> a <> Z.zero) expr in @@ -225,34 +225,6 @@ struct else None - - (*Parses a Texpr to obtain a (variable, offset) pair to repr. a sum of a variable and an offset. - Returns None if the expression is not a sum between a variable (without coefficient) and a constant. - - let exception Not2VarExpr in - let sum_next_coefficient (var, current_var_offset, curr_offset) (next_coeff, next_var) = - begin match next_var with - | None -> (* this element represents a constant offset *) - (var, current_var_offset, Z.(curr_offset + next_coeff)) - | Some _ -> (* this element represents a variable with a coefficient - -> it must be always the same variable, else it's not a two-variable equality*) - begin if Option.is_none var || next_var = var then - (next_var, Z.(current_var_offset + next_coeff), curr_offset) - else raise Not2VarExpr end - end in - let sum_coefficients summands_list_opt = - Option.map (List.fold_left sum_next_coefficient (None, Z.zero, Z.zero)) summands_list_opt - in - match sum_coefficients (get_coeff_vec t texp) with - | exception _ -> None - | None -> None - | Some (var, var_coeff, offset) -> - if Option.is_none var then Some (None, offset) - else if Z.equal var_coeff Z.one then Some (var, offset) - else None - *) - - let get_coeff t texp = timing_wrap "coeff_vec" (get_coeff t) texp let abstract_exists var t = match t.d with @@ -467,9 +439,7 @@ struct res let widen a b = - if Environment.equal a.env b.env then - join a b - else b + join a b let widen a b = let res = widen a b in From 65fbe08b0735b7d06a01b4a1ee58b032e2cae203 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Mon, 5 Feb 2024 17:17:17 +0100 Subject: [PATCH 172/280] removed unnecessary test --- .../regression/77-lin2vareq/cast-to-short2.c | 27 ------------------- 1 file changed, 27 deletions(-) delete mode 100644 tests/regression/77-lin2vareq/cast-to-short2.c diff --git a/tests/regression/77-lin2vareq/cast-to-short2.c b/tests/regression/77-lin2vareq/cast-to-short2.c deleted file mode 100644 index c5cc059298..0000000000 --- a/tests/regression/77-lin2vareq/cast-to-short2.c +++ /dev/null @@ -1,27 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none - -#include -int main() { - - unsigned int allbits = -255 - 25; // choose a value which is not representable in short - int signedallbits = allbits; - short unsignedtosigned = allbits; - unsigned short unsignedtounsigned = allbits; - - printf("allbits: %u\n", allbits); - printf("signedallbits: %d\n", signedallbits); - printf("unsignedtosigned: %hd\n", unsignedtosigned); - printf("unsignedtounsigned: %hu\n", unsignedtounsigned); - - if (unsignedtounsigned == 4294967295) { -// __goblint_check(0); // NOWARN (unreachable) - return (-1); - } - if (allbits == 4294967295 && signedallbits == -1 && unsignedtosigned == -1 && - unsignedtounsigned == 65535) { - // __goblint_check(1); // reachable - return (-1); - } - // __goblint_check(0); // NOWARN (unreachable) - return (0); -} From e2fe8c001fedb13e6811e989b7ef0cad0ebaaa47 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 5 Feb 2024 17:28:24 +0100 Subject: [PATCH 173/280] some requested changes --- .../linearTwoVarEqualityAnalysis.apron.ml | 2 +- src/analyses/apron/relationAnalysis.apron.ml | 4 +- src/analyses/base.ml | 8 ++-- .../apron/linearTwoVarEqualityDomain.apron.ml | 37 +++++++------------ src/cdomains/apron/sharedFunctions.apron.ml | 4 +- src/config/options.schema.json | 2 +- src/goblint_lib.ml | 2 + src/util/std/gobOption.ml | 2 +- 8 files changed, 27 insertions(+), 34 deletions(-) diff --git a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml index cc2af20cb2..69c01279b5 100644 --- a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml +++ b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml @@ -1,4 +1,4 @@ -(** {{!RelationAnalysis} Relational integer value analysis} using an OCaml implementation of the linear two-variable equalities domain ([...]). +(** {{!RelationAnalysis} Relational integer value analysis} using an OCaml implementation of the linear two-variable equalities domain ([lin2vareq]). @see A. Flexeder, M. Petter, and H. Seidl Fast Interprocedural Linear Two-Variable Equalities. *) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 787f307e54..ac9360d4e1 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -177,7 +177,7 @@ struct let no_overflow_not_constraint (ask: Queries.ask) exp = match Cilfacade.get_ikind_exp exp with - | exception Invalid_argument _ -> false + | exception Invalid_argument _ -> false (* is thrown by get_ikind_exp when the type of the expression is not an integer type *) | exception Cilfacade.TypeOfError _ -> false | ik -> if IntDomain.should_wrap ik then @@ -313,7 +313,6 @@ struct let make_callee_rel ~thread ctx f args = let fundec = Node.find_fundec ctx.node in let st = ctx.local in - let ask = Analyses.ask_of_ctx ctx in let arg_assigns = GobList.combine_short f.sformals args (* TODO: is it right to ignore missing formals/args? *) |> List.filter_map (fun (x, e) -> if RD.Tracked.varinfo_tracked x then Some (RV.arg x, e) else None) @@ -326,6 +325,7 @@ struct if thread then new_rel else + let ask = Analyses.ask_of_ctx ctx in List.fold_left (fun new_rel (var, e) -> assign_from_globals_wrapper ask ctx.global {st with rel = new_rel} e (fun rel' e' -> RD.assign_exp ask rel' var e' (no_overflow ask e) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 6866c53baf..f599b4cf76 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1295,10 +1295,10 @@ struct | BinOp (binop, e1, e2, _) -> (* check if the current operation causes a signed overflow *) (Cil.isSigned ik && begin match binop with - | PlusA|PlusPI|IndexPI -> checkBinop e1 e2 (GobOption.unionWith Z.(+)) - | MinusA|MinusPI|MinusPP -> checkBinop e1 e2 (GobOption.unionWith Z.(-)) - | Mult -> checkBinop e1 e2 (GobOption.unionWith Z.mul) - | Div -> checkBinop e1 e2 (GobOption.unionWith Z.div) + | PlusA|PlusPI|IndexPI -> checkBinop e1 e2 (GobOption.map2 Z.(+)) + | MinusA|MinusPI|MinusPP -> checkBinop e1 e2 (GobOption.map2 Z.(-)) + | Mult -> checkBinop e1 e2 (GobOption.map2 Z.mul) + | Div -> checkBinop e1 e2 (GobOption.map2 Z.div) | Mod -> (* an overflow happens when the second operand is negative *) checkPredicate e2 (fun interval_bound _ -> Z.gt interval_bound Z.zero) (* operations that do not result in overflow in C: *) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index f19e27cf74..897cfd4065 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -88,11 +88,7 @@ module EqualitiesArray = struct let is_top_array = GobArray.for_alli (fun i (a, e) -> GobOption.exists ((=) i) a && Z.equal e Z.zero) - (* Forget information about variable var in-place. - The name reduce_col_with is because the affineEqualitiesDomain also defines this function, - and it represents the equalities with a matrix, not like in this case with an array. - We could think about changing this name, then we would need to change it also in - shared_Functions.apron.ml and vectorMatrix.ml and affineEqualitiesDomain.ml *) + (* Forget information about variable var in-place. *) let forget_variable_with d var = (let ref_var_opt = fst d.(var) in match ref_var_opt with @@ -161,19 +157,14 @@ struct | a, [(b_coeff, None)] -> multiply_with_Z b_coeff a | _ -> raise NotLinearExpr in - let mpqf_to_Z x = - if not(Z.equal (Mpqf.get_den x) Z.one) then raise NotIntegerOffset - else Mpqf.get_num x in let rec convert_texpr texp = begin match texp with (*If x is a constant, replace it with its const. val. immediately*) - | Cst x -> - (let open Coeff in - match x with - | Interval _ -> failwith "Not a constant" - | Scalar (Float x) -> raise NotIntegerOffset - | Scalar (Mpqf x) -> [(mpqf_to_Z x, None)] - | Scalar (Mpfrf x) -> raise NotIntegerOffset) + | Cst (Interval _) ->failwith "Not a constant" + | Cst (Scalar x) -> + begin match SharedFunctions.int_of_scalar ?round:None x with + | Some x -> [(x, None)] + | None -> raise NotIntegerOffset end | Var x -> let var_dim = Environment.dim_of_var t.env x in begin match t.d with @@ -215,11 +206,11 @@ struct | (None, c_i) -> constant := Z.(!constant + (c * c_i)) in List.iter update cv's; - let var_count = BatArray.count_matching (fun a -> a <> Z.zero) expr in - if var_count == 0 then Some (None, !constant) - else if var_count == 1 then ( - let var = Array.findi (fun a -> a <> Z.zero) expr in - if Z.(expr.(var) == Z.one) then Some (Some var, !constant) + let var_count = BatArray.count_matching (fun a -> not @@ Z.equal a Z.zero) expr in + if var_count = 0 then Some (None, !constant) + else if var_count = 1 then ( + let var = Array.findi (fun a -> not @@ Z.equal a Z.zero) expr in + if Z.equal expr.(var) Z.one then Some (Some var, !constant) else None ) else None @@ -339,7 +330,7 @@ struct | (None, b2) -> subst_var ts i (None, Z.(b2 + b)) | (Some h2, b2) -> if h1 = h2 then - (if Z.(b1 <> (b2 + b)) then raise Contradiction) + (if not @@ Z.equal b1 Z.(b2 + b) then raise Contradiction) else if h1 < h2 then subst_var ts h2 (Some h1, Z.(b1 - (b + b2))) else subst_var ts h1 (Some h2, Z.(b + (b2 - b1))))) @@ -611,7 +602,7 @@ struct | Some d -> let expr = Array.init (Environment.size t.env) (fun _ -> Z.zero) in let constant = ref (Z.zero) in - if is_bot_env t then bot_env + if is_bot_env t then bot_env else match get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with | None -> t (*The (in-) equality is not linear, therefore we don't know anything about it. *) @@ -625,7 +616,7 @@ struct | (None, c_i) -> constant := Z.(!constant + (c * c_i)) in List.iter update cv's; - let var_count = GobArray.count_matchingi (fun _ a -> Z.(a <> Z.zero)) expr in + let var_count = GobArray.count_matchingi (fun _ a -> not @@ Z.equal a Z.zero) expr in if var_count = 0 then match Tcons1.get_typ tcons with | EQ when Z.equal !constant Z.zero -> t diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index eb40330455..7209d5cff0 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -587,9 +587,9 @@ struct (** Evaluate non-constraint expression as interval. *) let eval_interval_expr ask d e no_ov = - match Convert.texpr1_of_cil_exp ask d (env d) e no_ov with (* Resolve AWE: delete no_ov flags. *) + match Convert.texpr1_of_cil_exp ask d (env d) e no_ov with | texpr1 -> - let c = eval_interval ask d texpr1 in c + eval_interval ask d texpr1 | exception Convert.Unsupported_CilExp _ -> (None, None) diff --git a/src/config/options.schema.json b/src/config/options.schema.json index e0c70384e0..0c83e342e0 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -852,7 +852,7 @@ "description": "Which domain should be used for the Apron analysis. Can be 'octagon', 'interval' or 'polyhedra'", "type": "string", - "enum": ["octagon", "interval", "polyhedra", "affeq", "lin2vareq"], + "enum": ["octagon", "interval", "polyhedra", "affeq"], "default": "octagon" }, "threshold_widening": { diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index 4b2eecb632..2f8492ced2 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -76,6 +76,7 @@ module Base = Base module RelationAnalysis = RelationAnalysis module ApronAnalysis = ApronAnalysis module AffineEqualityAnalysis = AffineEqualityAnalysis +module LinearTwoVarEqualityAnalysis = LinearTwoVarEqualityAnalysis module VarEq = VarEq module CondVars = CondVars module TmpSpecial = TmpSpecial @@ -240,6 +241,7 @@ module ValueDomainQueries = ValueDomainQueries module RelationDomain = RelationDomain module ApronDomain = ApronDomain module AffineEqualityDomain = AffineEqualityDomain +module LinearTwoVarEqualityDomain = LinearTwoVarEqualityDomain (** {3 Concurrency} *) diff --git a/src/util/std/gobOption.ml b/src/util/std/gobOption.ml index 68517abfb5..55597a8e50 100644 --- a/src/util/std/gobOption.ml +++ b/src/util/std/gobOption.ml @@ -6,7 +6,7 @@ let for_all p = function | Some x -> p x | None -> true -let unionWith binop opt1 opt2 = +let map2 binop opt1 opt2 = match opt1, opt2 with | Some x1, Some x2 -> Some (binop x1 x2) | _ -> None From fb0d57e2c82afa081e081d4f0a36c1d5eec00c20 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 5 Feb 2024 17:35:09 +0100 Subject: [PATCH 174/280] revert changes to affeq test --- tests/regression/63-affeq/02-unsigned_guards.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/regression/63-affeq/02-unsigned_guards.c b/tests/regression/63-affeq/02-unsigned_guards.c index b5da331ec0..0f32ea9855 100644 --- a/tests/regression/63-affeq/02-unsigned_guards.c +++ b/tests/regression/63-affeq/02-unsigned_guards.c @@ -11,10 +11,8 @@ int main(){ __goblint_check(i == 3); // UNKNOWN! } - unsigned int i2; - if (i2 - 2u == 4294967295u) { - printf("i2"); - printf("%u\n", i2); + unsigned int i; + if (i - 2u == 4294967295u) { __goblint_check (i == 4294967297); // FAIL! } @@ -31,8 +29,8 @@ int main(){ __goblint_check(1); } - unsigned int x2 = 8; - if (x2 == 8u) { + unsigned int x = 8; + if (x == 8u) { __goblint_check(1); // reachable } return 0; From d8459fff69d92c2206789ccce8bac9c77a559199 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 5 Feb 2024 17:48:31 +0100 Subject: [PATCH 175/280] Remove intermediate module definition in lin2vareq, the same as jerhard did for affeq --- src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml | 9 ++------- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 9 +++------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml index 69c01279b5..8b38679a09 100644 --- a/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml +++ b/src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml @@ -7,17 +7,12 @@ include RelationAnalysis let spec_module: (module MCPSpec) Lazy.t = lazy ( - let module AD = LinearTwoVarEqualityDomain.D2 in - let module RD: RelationDomain.RD = - struct - module V = LinearTwoVarEqualityDomain.V - include AD - end + let module AD = LinearTwoVarEqualityDomain.D2 in let module Priv = (val RelationPriv.get_priv ()) in let module Spec = struct - include SpecFunctor (Priv) (RD) (RelationPrecCompareUtil.DummyUtil) + include SpecFunctor (Priv) (AD) (RelationPrecCompareUtil.DummyUtil) let name () = "lin2vareq" end in diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 897cfd4065..e89dc8b578 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -128,9 +128,6 @@ module EqualitiesArray = struct end - -module V = RelationDomain.V - (** [VarManagement] defines the type t of the affine equality domain (a record that contains an optional matrix and an apron environment) and provides the functions needed for handling variables (which are defined by [RelationDomain.D2]) such as [add_vars], [remove_vars]. Furthermore, it provides the function [get_coeff_vec] that parses an apron expression into a vector of coefficients if the apron expression has an affine form. *) module VarManagement = @@ -275,7 +272,7 @@ struct include VarManagement module Bounds = ExpressionBounds - + module V = RelationDomain.V module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (struct let do_overflow_check = false end) (SharedFunctions.Tracked) type var = V.t @@ -724,9 +721,9 @@ struct end -module D2: RelationDomain.S3 with type var = Var.t = +module D2: RelationDomain.RD with type var = Var.t = struct module D = D - include SharedFunctions.AssertionModule (V) (D) (struct let do_overflow_check = false end) + include SharedFunctions.AssertionModule (D.V) (D) (struct let do_overflow_check = false end) include D end From e07608f2d060adc884c958154327e8b83cb66417 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Mon, 5 Feb 2024 20:18:06 +0100 Subject: [PATCH 176/280] Test meet_tcons --- .../77-lin2vareq/33-meet-tcons-on-steroids.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/regression/77-lin2vareq/33-meet-tcons-on-steroids.c diff --git a/tests/regression/77-lin2vareq/33-meet-tcons-on-steroids.c b/tests/regression/77-lin2vareq/33-meet-tcons-on-steroids.c new file mode 100644 index 0000000000..501db8ee25 --- /dev/null +++ b/tests/regression/77-lin2vareq/33-meet-tcons-on-steroids.c @@ -0,0 +1,19 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --enable ana.int.interval +#include + +int main(void) { + short b; + short a; + int c = a; + int d = b; + int cc = c + 20; + int dd = d - 30; + a = 3 * 1543; + if (a*(c - cc) == a*(d -dd - 50)){ + __goblint_check(1);// (reachable) + return 0; + }else{ + __goblint_check(0);// NOWARN (unreachable) + return -1; + } +} From 418b7a6adefb90f563e03970da3d1db780a7169c Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Mon, 5 Feb 2024 21:51:05 +0100 Subject: [PATCH 177/280] fixed cast exception for non integer types --- src/cdomains/apron/sharedFunctions.apron.ml | 4 +++- tests/regression/77-lin2vareq/34-cast-non-int.c | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 tests/regression/77-lin2vareq/34-cast-non-int.c diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 7209d5cff0..0397954f4a 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -225,6 +225,7 @@ struct Binop (Mod, texpr1_expr_of_cil_exp ask @@ simplify e1, texpr1_expr_of_cil_exp ask @@ simplify e2, Int, Near) | CastE (TInt (t_ik, _) as t, e) -> begin match IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) + | exception _ -> raise (Unsupported_CilExp (Cast_not_injective t)) | true -> texpr1_expr_of_cil_exp ask @@ simplify e | false -> let res = query e @@ Cilfacade.get_ikind_exp e in @@ -238,7 +239,8 @@ struct | Some min, Some max when min >= minimal && max <= maximal -> texpr1_expr_of_cil_exp ask e | _ -> raise (Unsupported_CilExp (Cast_not_injective t))) | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) - | exception Invalid_argument _ -> (* get_ikind in is_cast_injective *) + | exception Invalid_argument _ + | _ -> (* get_ikind in is_cast_injective *) raise (Unsupported_CilExp (Cast_not_injective t)) end | _ -> diff --git a/tests/regression/77-lin2vareq/34-cast-non-int.c b/tests/regression/77-lin2vareq/34-cast-non-int.c new file mode 100644 index 0000000000..0c7f100f81 --- /dev/null +++ b/tests/regression/77-lin2vareq/34-cast-non-int.c @@ -0,0 +1,11 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +#include +//#include +int main(void) { + float b = 2.5; + float a = 1.5; + int c = (int) a; + int d = (int) b; + //printf("c: %d\nd: %d\n", c, d); + __goblint_check(d -c -1 == 0); // UNKNOWN +} From 860eb5e9a0e5a5f0fb12326e887593d6712d984f Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Mon, 5 Feb 2024 22:19:37 +0100 Subject: [PATCH 178/280] Some more fixes for get_ikind_exp --- src/cdomains/apron/sharedFunctions.apron.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 0397954f4a..19bc5c7f86 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -104,8 +104,8 @@ struct if not @@ IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp exp) && not (Lazy.force no_ov) then (raise (Unsupported_CilExp Overflow)) - with Invalid_argument _ -> () (* This exception is raised by Cilfacade.get_ikind_exp - when the expression is not an integer expression, for example if it is a float expression. *) + with Invalid_argument _ -> (raise (Unsupported_CilExp Overflow)) (* This exception is raised by Cilfacade.get_ikind_exp + when the expression is not an integer expression, for example if it is a float expression. *) end let texpr1_expr_of_cil_exp_old d env exp no_ov = @@ -203,7 +203,7 @@ struct | ik -> let expr = let simplify e = - let ikind = Cilfacade.get_ikind_exp e in (* TODO AWE: raise unsupported cil exception on error *) + let ikind = try (Cilfacade.get_ikind_exp e) with Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) in let simp = query e ikind in let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to ikind simp in match const with @@ -228,7 +228,7 @@ struct | exception _ -> raise (Unsupported_CilExp (Cast_not_injective t)) | true -> texpr1_expr_of_cil_exp ask @@ simplify e | false -> - let res = query e @@ Cilfacade.get_ikind_exp e in + let res = try (query e @@ Cilfacade.get_ikind_exp e) with Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) in let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to t_ik res in match const with | Some c -> Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z c))) From 82db782b5bf6185b445730fe0bd3c20bb28dea12 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Mon, 5 Feb 2024 22:24:03 +0100 Subject: [PATCH 179/280] Some more fixes for get_ikind_exp --- src/cdomains/apron/sharedFunctions.apron.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 19bc5c7f86..9b993ed887 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -104,8 +104,8 @@ struct if not @@ IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp exp) && not (Lazy.force no_ov) then (raise (Unsupported_CilExp Overflow)) - with Invalid_argument _ -> (raise (Unsupported_CilExp Overflow)) (* This exception is raised by Cilfacade.get_ikind_exp - when the expression is not an integer expression, for example if it is a float expression. *) + with Invalid_argument e -> raise (Unsupported_CilExp (Exp_typeOf expr))(* This exception is raised by Cilfacade.get_ikind_exp + when the expression is not an integer expression, for example if it is a float expression. *) end let texpr1_expr_of_cil_exp_old d env exp no_ov = From 746ade0003f5cee1a0868dd6395c8da29d7288b9 Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Mon, 5 Feb 2024 22:26:04 +0100 Subject: [PATCH 180/280] Some more fixes for get_ikind_exp --- src/cdomains/apron/sharedFunctions.apron.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 9b993ed887..662884f533 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -104,8 +104,8 @@ struct if not @@ IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp exp) && not (Lazy.force no_ov) then (raise (Unsupported_CilExp Overflow)) - with Invalid_argument e -> raise (Unsupported_CilExp (Exp_typeOf expr))(* This exception is raised by Cilfacade.get_ikind_exp - when the expression is not an integer expression, for example if it is a float expression. *) + with Invalid_argument e -> raise ((Unsupported_CilExp Exp_not_supported))(* This exception is raised by Cilfacade.get_ikind_exp + when the expression is not an integer expression, for example if it is a float expression. *) end let texpr1_expr_of_cil_exp_old d env exp no_ov = From 91623fc6a5ebf68cddba9f42ce85114ad01ed95e Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Tue, 6 Feb 2024 12:09:06 +0100 Subject: [PATCH 181/280] Fix Z.Overflow --- .../apron/linearTwoVarEqualityDomain.apron.ml | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index e89dc8b578..63e968050a 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -688,26 +688,26 @@ struct This function returns all the equalities that are saved in our datastructure t. Lincons -> linear constraint *) - let invariant t = + let invariant t = + let get_const acc i (var_opt, const) = if Some i = var_opt then acc + else ( + let xi = Environment.var_of_dim t.env i in + let coeff_vars = (Coeff.s_of_int (-1), xi) :: (match var_opt with + | Some var_index when i <> var_index -> + let var = Environment.var_of_dim t.env var_index in + [(Coeff.s_of_int 1, var)] + | _ -> [] ) + in + let typ = (Option.get @@ V.to_cil_varinfo xi).vtype in + let ikind = Cilfacade.get_ikind typ in (*Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z i))*) + let cst = Coeff.s_of_mpqf @@ Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z @@IntDomain.Size.cast ikind const) in + let lincons = Lincons1.make (Linexpr1.make t.env) Lincons1.EQ in + Lincons1.set_list lincons coeff_vars (Some cst); + lincons :: acc) + in match t.d with | None -> [] - | Some d -> - Array.fold_lefti (fun acc i (var_opt, const) -> - if Some i = var_opt then acc - else - let xi = Environment.var_of_dim t.env i in - let coeff_vars = - (Coeff.s_of_int (-1), xi) :: (match var_opt with - | Some var_index when i <> var_index -> - let var = Environment.var_of_dim t.env var_index in - [(Coeff.s_of_int 1, var)] - | _ -> [] ) - in - let cst = Coeff.s_of_int (Z.to_int const) in - let lincons = Lincons1.make (Linexpr1.make t.env) Lincons1.EQ in - Lincons1.set_list lincons coeff_vars (Some cst); - lincons :: acc - ) [] d + | Some d -> Array.fold_lefti get_const [] d let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 From 61a5ab7897f267edb6dfdec7343275d1952ea1d6 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Tue, 6 Feb 2024 14:40:18 +0100 Subject: [PATCH 182/280] delete unused tests --- tests/regression/77-lin2vareq/01-basic.c | 21 --------- .../77-lin2vareq/{07-loop.c => 01-loop.c} | 0 ...pression.c => 04-complicated_expression.c} | 0 .../regression/77-lin2vareq/04-reachability.c | 17 -------- .../{08-overflow.c => 05-overflow.c} | 0 .../77-lin2vareq/05-vars_equality.c | 25 ----------- ...-non-constant.c => 06-join-non-constant.c} | 0 .../{10-coeff_vec.c => 07-coeff_vec.c} | 0 .../{11-partitioning.c => 08-partitioning.c} | 0 ...loop_relational.c => 09-loop_relational.c} | 0 .../{13-linear_loop.c => 10-linear_loop.c} | 0 ...erflow_ignored.c => 11-overflow_ignored.c} | 0 ...unds_guards_ov.c => 12-bounds_guards_ov.c} | 0 .../{16-meet-tcons.c => 13-meet-tcons.c} | 0 ...{17-function-call.c => 14-function-call.c} | 0 ...8-join_all_cases.c => 15-join_all_cases.c} | 0 ...sum-of-two-vars.c => 16-sum-of-two-vars.c} | 0 ...nextension.c => 17-svcomp-signextension.c} | 0 .../{21-forget_var.c => 18-forget_var.c} | 0 ...{22-cast-to-short.c => 19-cast-to-short.c} | 0 ...3-function_call2.c => 20-function_call2.c} | 0 ...obal-variables.c => 21-global-variables.c} | 0 .../77-lin2vareq/22-cast-to-short2.c | 27 ++++++++++++ ...urn-value.c => 23-function-return-value.c} | 0 ...-steroids.c => 24-narrowing-on-steroids.c} | 0 .../77-lin2vareq/24-relation-result.c | 18 -------- .../77-lin2vareq/25-different_types.c | 31 +++++++++++++ tests/regression/77-lin2vareq/26-macro.c | 23 ---------- .../77-lin2vareq/26-termination-overflow.c | 13 ++++++ .../77-lin2vareq/27-overflow-unknown.c | 19 ++++++++ .../77-lin2vareq/28-overflow-on-steroids.c | 43 +++++++++++++++++++ 31 files changed, 133 insertions(+), 104 deletions(-) delete mode 100644 tests/regression/77-lin2vareq/01-basic.c rename tests/regression/77-lin2vareq/{07-loop.c => 01-loop.c} (100%) rename tests/regression/77-lin2vareq/{06-complicated_expression.c => 04-complicated_expression.c} (100%) delete mode 100644 tests/regression/77-lin2vareq/04-reachability.c rename tests/regression/77-lin2vareq/{08-overflow.c => 05-overflow.c} (100%) delete mode 100644 tests/regression/77-lin2vareq/05-vars_equality.c rename tests/regression/77-lin2vareq/{09-join-non-constant.c => 06-join-non-constant.c} (100%) rename tests/regression/77-lin2vareq/{10-coeff_vec.c => 07-coeff_vec.c} (100%) rename tests/regression/77-lin2vareq/{11-partitioning.c => 08-partitioning.c} (100%) rename tests/regression/77-lin2vareq/{12-loop_relational.c => 09-loop_relational.c} (100%) rename tests/regression/77-lin2vareq/{13-linear_loop.c => 10-linear_loop.c} (100%) rename tests/regression/77-lin2vareq/{14-overflow_ignored.c => 11-overflow_ignored.c} (100%) rename tests/regression/77-lin2vareq/{15-bounds_guards_ov.c => 12-bounds_guards_ov.c} (100%) rename tests/regression/77-lin2vareq/{16-meet-tcons.c => 13-meet-tcons.c} (100%) rename tests/regression/77-lin2vareq/{17-function-call.c => 14-function-call.c} (100%) rename tests/regression/77-lin2vareq/{18-join_all_cases.c => 15-join_all_cases.c} (100%) rename tests/regression/77-lin2vareq/{19-sum-of-two-vars.c => 16-sum-of-two-vars.c} (100%) rename tests/regression/77-lin2vareq/{20-svcomp-signextension.c => 17-svcomp-signextension.c} (100%) rename tests/regression/77-lin2vareq/{21-forget_var.c => 18-forget_var.c} (100%) rename tests/regression/77-lin2vareq/{22-cast-to-short.c => 19-cast-to-short.c} (100%) rename tests/regression/77-lin2vareq/{23-function_call2.c => 20-function_call2.c} (100%) rename tests/regression/77-lin2vareq/{25-global-variables.c => 21-global-variables.c} (100%) create mode 100644 tests/regression/77-lin2vareq/22-cast-to-short2.c rename tests/regression/77-lin2vareq/{27-function-return-value.c => 23-function-return-value.c} (100%) rename tests/regression/77-lin2vareq/{28-narrowing-on-steroids.c => 24-narrowing-on-steroids.c} (100%) delete mode 100644 tests/regression/77-lin2vareq/24-relation-result.c create mode 100644 tests/regression/77-lin2vareq/25-different_types.c delete mode 100644 tests/regression/77-lin2vareq/26-macro.c create mode 100644 tests/regression/77-lin2vareq/26-termination-overflow.c create mode 100644 tests/regression/77-lin2vareq/27-overflow-unknown.c create mode 100644 tests/regression/77-lin2vareq/28-overflow-on-steroids.c diff --git a/tests/regression/77-lin2vareq/01-basic.c b/tests/regression/77-lin2vareq/01-basic.c deleted file mode 100644 index 4d838b4e26..0000000000 --- a/tests/regression/77-lin2vareq/01-basic.c +++ /dev/null @@ -1,21 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false --set sem.int.signed_overflow assume_none - -#include -#include - -int main() { - int x = 0; - int y = 0; - - x = 1; - y = 1; - - __goblint_check(x == y); //SUCCESS - - x = 10; - - __goblint_check(x != y); //SUCCESS - __goblint_check(x == y); //FAIL - - return 0; -} diff --git a/tests/regression/77-lin2vareq/07-loop.c b/tests/regression/77-lin2vareq/01-loop.c similarity index 100% rename from tests/regression/77-lin2vareq/07-loop.c rename to tests/regression/77-lin2vareq/01-loop.c diff --git a/tests/regression/77-lin2vareq/06-complicated_expression.c b/tests/regression/77-lin2vareq/04-complicated_expression.c similarity index 100% rename from tests/regression/77-lin2vareq/06-complicated_expression.c rename to tests/regression/77-lin2vareq/04-complicated_expression.c diff --git a/tests/regression/77-lin2vareq/04-reachability.c b/tests/regression/77-lin2vareq/04-reachability.c deleted file mode 100644 index d1007ed68c..0000000000 --- a/tests/regression/77-lin2vareq/04-reachability.c +++ /dev/null @@ -1,17 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false -#include -#include - -int main() { - int x = 0; - int y = 0; - - x = 10; - y = 1; - - __goblint_check(x == 10 * y); //SUCCESS - - if(x == 10 * y) - return 0; - __goblint_check(0); // NOWARN (unreachable) -} diff --git a/tests/regression/77-lin2vareq/08-overflow.c b/tests/regression/77-lin2vareq/05-overflow.c similarity index 100% rename from tests/regression/77-lin2vareq/08-overflow.c rename to tests/regression/77-lin2vareq/05-overflow.c diff --git a/tests/regression/77-lin2vareq/05-vars_equality.c b/tests/regression/77-lin2vareq/05-vars_equality.c deleted file mode 100644 index 83c9de306c..0000000000 --- a/tests/regression/77-lin2vareq/05-vars_equality.c +++ /dev/null @@ -1,25 +0,0 @@ -//SKIP PARAM: --set ana.activated[+] lin2vareq --set ana.int.enums false --set ana.int.interval false --set ana.int.interval_set false --set ana.int.congruence false - -#include - -int main() { - int x1 = 5, x2 = 10, x3 = 15, x4, x5, x6, x7, x8, x9, x10, x11, x12; - - x4 = 3 * x2 + 5; - x5 = 3 * x3 + 15; - x6 = x3 + 3; - x7 = x3 + 2; - x8 = 7 * x3 + 15; - x9 = 0; - x10 = 2 * x9 + 2; - x11 = 2 * x1 - 3; - x12 = 4 * x1 - 5; - - __goblint_check(x4 == 3 * x2 + 5); //SUCCESS - __goblint_check(x5 == 3 * x3 + 15); //SUCCESS - __goblint_check(x7 == x6 - 1); //SUCCESS - __goblint_check(x10 == 2 * x9 + 2); //SUCCESS - __goblint_check(x12 == 2 * x11 + 1); //SUCCESS - - return 0; -} diff --git a/tests/regression/77-lin2vareq/09-join-non-constant.c b/tests/regression/77-lin2vareq/06-join-non-constant.c similarity index 100% rename from tests/regression/77-lin2vareq/09-join-non-constant.c rename to tests/regression/77-lin2vareq/06-join-non-constant.c diff --git a/tests/regression/77-lin2vareq/10-coeff_vec.c b/tests/regression/77-lin2vareq/07-coeff_vec.c similarity index 100% rename from tests/regression/77-lin2vareq/10-coeff_vec.c rename to tests/regression/77-lin2vareq/07-coeff_vec.c diff --git a/tests/regression/77-lin2vareq/11-partitioning.c b/tests/regression/77-lin2vareq/08-partitioning.c similarity index 100% rename from tests/regression/77-lin2vareq/11-partitioning.c rename to tests/regression/77-lin2vareq/08-partitioning.c diff --git a/tests/regression/77-lin2vareq/12-loop_relational.c b/tests/regression/77-lin2vareq/09-loop_relational.c similarity index 100% rename from tests/regression/77-lin2vareq/12-loop_relational.c rename to tests/regression/77-lin2vareq/09-loop_relational.c diff --git a/tests/regression/77-lin2vareq/13-linear_loop.c b/tests/regression/77-lin2vareq/10-linear_loop.c similarity index 100% rename from tests/regression/77-lin2vareq/13-linear_loop.c rename to tests/regression/77-lin2vareq/10-linear_loop.c diff --git a/tests/regression/77-lin2vareq/14-overflow_ignored.c b/tests/regression/77-lin2vareq/11-overflow_ignored.c similarity index 100% rename from tests/regression/77-lin2vareq/14-overflow_ignored.c rename to tests/regression/77-lin2vareq/11-overflow_ignored.c diff --git a/tests/regression/77-lin2vareq/15-bounds_guards_ov.c b/tests/regression/77-lin2vareq/12-bounds_guards_ov.c similarity index 100% rename from tests/regression/77-lin2vareq/15-bounds_guards_ov.c rename to tests/regression/77-lin2vareq/12-bounds_guards_ov.c diff --git a/tests/regression/77-lin2vareq/16-meet-tcons.c b/tests/regression/77-lin2vareq/13-meet-tcons.c similarity index 100% rename from tests/regression/77-lin2vareq/16-meet-tcons.c rename to tests/regression/77-lin2vareq/13-meet-tcons.c diff --git a/tests/regression/77-lin2vareq/17-function-call.c b/tests/regression/77-lin2vareq/14-function-call.c similarity index 100% rename from tests/regression/77-lin2vareq/17-function-call.c rename to tests/regression/77-lin2vareq/14-function-call.c diff --git a/tests/regression/77-lin2vareq/18-join_all_cases.c b/tests/regression/77-lin2vareq/15-join_all_cases.c similarity index 100% rename from tests/regression/77-lin2vareq/18-join_all_cases.c rename to tests/regression/77-lin2vareq/15-join_all_cases.c diff --git a/tests/regression/77-lin2vareq/19-sum-of-two-vars.c b/tests/regression/77-lin2vareq/16-sum-of-two-vars.c similarity index 100% rename from tests/regression/77-lin2vareq/19-sum-of-two-vars.c rename to tests/regression/77-lin2vareq/16-sum-of-two-vars.c diff --git a/tests/regression/77-lin2vareq/20-svcomp-signextension.c b/tests/regression/77-lin2vareq/17-svcomp-signextension.c similarity index 100% rename from tests/regression/77-lin2vareq/20-svcomp-signextension.c rename to tests/regression/77-lin2vareq/17-svcomp-signextension.c diff --git a/tests/regression/77-lin2vareq/21-forget_var.c b/tests/regression/77-lin2vareq/18-forget_var.c similarity index 100% rename from tests/regression/77-lin2vareq/21-forget_var.c rename to tests/regression/77-lin2vareq/18-forget_var.c diff --git a/tests/regression/77-lin2vareq/22-cast-to-short.c b/tests/regression/77-lin2vareq/19-cast-to-short.c similarity index 100% rename from tests/regression/77-lin2vareq/22-cast-to-short.c rename to tests/regression/77-lin2vareq/19-cast-to-short.c diff --git a/tests/regression/77-lin2vareq/23-function_call2.c b/tests/regression/77-lin2vareq/20-function_call2.c similarity index 100% rename from tests/regression/77-lin2vareq/23-function_call2.c rename to tests/regression/77-lin2vareq/20-function_call2.c diff --git a/tests/regression/77-lin2vareq/25-global-variables.c b/tests/regression/77-lin2vareq/21-global-variables.c similarity index 100% rename from tests/regression/77-lin2vareq/25-global-variables.c rename to tests/regression/77-lin2vareq/21-global-variables.c diff --git a/tests/regression/77-lin2vareq/22-cast-to-short2.c b/tests/regression/77-lin2vareq/22-cast-to-short2.c new file mode 100644 index 0000000000..c5cc059298 --- /dev/null +++ b/tests/regression/77-lin2vareq/22-cast-to-short2.c @@ -0,0 +1,27 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none + +#include +int main() { + + unsigned int allbits = -255 - 25; // choose a value which is not representable in short + int signedallbits = allbits; + short unsignedtosigned = allbits; + unsigned short unsignedtounsigned = allbits; + + printf("allbits: %u\n", allbits); + printf("signedallbits: %d\n", signedallbits); + printf("unsignedtosigned: %hd\n", unsignedtosigned); + printf("unsignedtounsigned: %hu\n", unsignedtounsigned); + + if (unsignedtounsigned == 4294967295) { +// __goblint_check(0); // NOWARN (unreachable) + return (-1); + } + if (allbits == 4294967295 && signedallbits == -1 && unsignedtosigned == -1 && + unsignedtounsigned == 65535) { + // __goblint_check(1); // reachable + return (-1); + } + // __goblint_check(0); // NOWARN (unreachable) + return (0); +} diff --git a/tests/regression/77-lin2vareq/27-function-return-value.c b/tests/regression/77-lin2vareq/23-function-return-value.c similarity index 100% rename from tests/regression/77-lin2vareq/27-function-return-value.c rename to tests/regression/77-lin2vareq/23-function-return-value.c diff --git a/tests/regression/77-lin2vareq/28-narrowing-on-steroids.c b/tests/regression/77-lin2vareq/24-narrowing-on-steroids.c similarity index 100% rename from tests/regression/77-lin2vareq/28-narrowing-on-steroids.c rename to tests/regression/77-lin2vareq/24-narrowing-on-steroids.c diff --git a/tests/regression/77-lin2vareq/24-relation-result.c b/tests/regression/77-lin2vareq/24-relation-result.c deleted file mode 100644 index 215d7ff3f8..0000000000 --- a/tests/regression/77-lin2vareq/24-relation-result.c +++ /dev/null @@ -1,18 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq - -#include - -int compute(int x, int y) { - int result = x * y - y; - return result; -} - -int main(void) { - int a = 2, b = 3; - int result = compute(a, b); - - __goblint_check(result == a * b - b); // SUCCESS - __goblint_check(result + b == a * b); // SUCCESS - - return 0; -} diff --git a/tests/regression/77-lin2vareq/25-different_types.c b/tests/regression/77-lin2vareq/25-different_types.c new file mode 100644 index 0000000000..3439b406cd --- /dev/null +++ b/tests/regression/77-lin2vareq/25-different_types.c @@ -0,0 +1,31 @@ +// PARAM: --set ana.activated[+] lin2vareq +#include + +int x = 42; +long y; +short z; + +int main() { + + y = (long)x; + z = (short)x; + + int a = (int)y; + short b = (short)y; + + int c = (int)z; + long d = (long)z; + + unsigned int u1 = (unsigned int)x; + unsigned long u2 = (unsigned long)y; + unsigned short u3 = (unsigned short)z; + + __goblint_check(x == a); + __goblint_check(x == c); + __goblint_check(y == d); + __goblint_check(x == (int)u1); + __goblint_check(y == (long)u2); + __goblint_check(z == (short)u3); + + return 0; +} diff --git a/tests/regression/77-lin2vareq/26-macro.c b/tests/regression/77-lin2vareq/26-macro.c deleted file mode 100644 index 24d4f98333..0000000000 --- a/tests/regression/77-lin2vareq/26-macro.c +++ /dev/null @@ -1,23 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq - -#include -#define LEN 32 -#define add(x,y) (x+y) -#define multiply(x,y) ((x) * (y)) -#define subtract(x,y) ((x) - (y)) - -int main(){ - int z = 32; - int a = 10; - int b = 20; - int result = add(a,b); - int result2 = multiply(a,b); - int result3 = subtract(a,b); - - __goblint_check(z == LEN); // SUCCESS - __goblint_check(result == 30); // SUCCESS - __goblint_check(result2 == 200); // SUCCESS - __goblint_check(result3 == -10); // SUCCESS - - return 0; -} diff --git a/tests/regression/77-lin2vareq/26-termination-overflow.c b/tests/regression/77-lin2vareq/26-termination-overflow.c new file mode 100644 index 0000000000..81c6d90828 --- /dev/null +++ b/tests/regression/77-lin2vareq/26-termination-overflow.c @@ -0,0 +1,13 @@ +// SKIP TERM PARAM: --set "ana.activated[+]" lin2vareq + +#include + +int main() { + int i = 2147483647; + i++; + while (i <= 10) { + printf("%d\n", i); + } + + return 0; +} diff --git a/tests/regression/77-lin2vareq/27-overflow-unknown.c b/tests/regression/77-lin2vareq/27-overflow-unknown.c new file mode 100644 index 0000000000..40e073d9fb --- /dev/null +++ b/tests/regression/77-lin2vareq/27-overflow-unknown.c @@ -0,0 +1,19 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq +#include +#include + +int main(void) { + int x = 10; + + if (x + 2147483647 == 2147483657) { + return 0; + } + + __goblint_check(1); + + // Overflow + int c = 2147483647; + c = c + 1; + + __goblint_check(c < 2147483647); // UNKNOWN! +} diff --git a/tests/regression/77-lin2vareq/28-overflow-on-steroids.c b/tests/regression/77-lin2vareq/28-overflow-on-steroids.c new file mode 100644 index 0000000000..b8275bac2f --- /dev/null +++ b/tests/regression/77-lin2vareq/28-overflow-on-steroids.c @@ -0,0 +1,43 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --enable ana.int.interval +#include + +int main(void) { + int b; + int a; + int c; // c is an unknown value + a = a % 8; // a is in the interval [-7, 7] + + b = c; // no overflow + __goblint_check(b == c);// SUCCESS + + b = c * 1; // no overflow + __goblint_check(b == c);// SUCCESS + + b = c ? c : c; // no overflow + __goblint_check(b == c);// SUCCESS + + b = a + 2; // no overflow + __goblint_check(b == a + 2);// SUCCESS + + b = c + 2; // might overflow + __goblint_check(b == c + 2);// UNKNOWN! + + b = a - 2; // no overflow + __goblint_check(b == a - 2);// SUCCESS + + b = c - 2; // might overflow + __goblint_check(b == c - 2);// UNKNOWN! + + b = a * 2 - a * 1; // no overflow + __goblint_check(b == a);// SUCCESS + + b = c * 2 - c * 1; // might overflow + __goblint_check(b == c); // UNKNOWN! + + b = (-a) + a; // no overflow + __goblint_check(b == 0); // SUCCESS + + b = (-c) + c; // might overflow + __goblint_check(b == 0); // UNKNOWN! + +} From ce98fc3b265e50903c8b2c6e8be9098bccd913fe Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Tue, 6 Feb 2024 16:33:12 +0100 Subject: [PATCH 183/280] merge do_overflow_check structure with allow_globl structure --- .../apron/affineEqualityDomain.apron.ml | 12 ++++++++-- src/cdomains/apron/apronDomain.apron.ml | 14 ++++++++--- .../apron/linearTwoVarEqualityDomain.apron.ml | 14 ++++++++--- src/cdomains/apron/sharedFunctions.apron.ml | 23 ++++++++----------- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index bd54beb144..61042c67b5 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -148,7 +148,11 @@ struct module Bounds = ExpressionBounds (Vc) (Mx) module V = RelationDomain.V - module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (struct let do_overflow_check = false end) (SharedFunctions.Tracked) + module Arg = struct + let allow_global = true + let do_overflow_check = false + end + module Convert = SharedFunctions.Convert (V) (Bounds) (Arg) (SharedFunctions.Tracked) type var = V.t @@ -591,6 +595,10 @@ end module D2(Vc: AbstractVector) (Mx: AbstractMatrix): RelationDomain.RD with type var = Var.t = struct module D = D (Vc) (Mx) - include SharedFunctions.AssertionModule (D.V) (D) (struct let do_overflow_check = false end) + module ConvArg = struct + let allow_global = false + let do_overflow_check = false + end + include SharedFunctions.AssertionModule (D.V) (D) (ConvArg) include D end diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index 1fda04de77..66e05328c7 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -232,7 +232,11 @@ module AOps0 (Tracked: Tracked) (Man: Manager) = struct open SharedFunctions module Bounds = Bounds (Man) - module Convert = Convert (V) (Bounds) (struct let allow_global = false end) (struct let do_overflow_check = true end) (Tracked) + module Arg = struct + let allow_global = false + let do_overflow_check = true + end + module Convert = Convert (V) (Bounds) (Arg) (Tracked) @@ -702,7 +706,11 @@ struct ) else ( let exps = ResettableLazy.force WideningThresholds.exps in - let module Convert = SharedFunctions.Convert (V) (Bounds(Man)) (struct let allow_global = true end) (struct let do_overflow_check = true end)(Tracked) in + let module Arg = struct + let allow_global = true + let do_overflow_check = true + end in + let module Convert = SharedFunctions.Convert (V) (Bounds(Man)) (Arg) (Tracked) in (* this implements widening_threshold with Tcons1 instead of Lincons1 *) let tcons1s = List.filter_map (fun e -> let no_ov = lazy(IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp e)) in @@ -772,7 +780,7 @@ end module D (Man: Manager)= struct module DWO = DWithOps (Man) (DHetero (Man)) - include SharedFunctions.AssertionModule (V) (DWO) (struct let do_overflow_check = true end) + include SharedFunctions.AssertionModule (V) (DWO) (DWO.Arg) include DWO module Tracked = Tracked module Man = Man diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 63e968050a..a7ba7d7935 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -273,7 +273,11 @@ struct module Bounds = ExpressionBounds module V = RelationDomain.V - module Convert = SharedFunctions.Convert (V) (Bounds) (struct let allow_global = true end) (struct let do_overflow_check = false end) (SharedFunctions.Tracked) + module Arg = struct + let allow_global = true + let do_overflow_check = false + end + module Convert = SharedFunctions.Convert (V) (Bounds) (Arg) (SharedFunctions.Tracked) type var = V.t @@ -688,7 +692,7 @@ struct This function returns all the equalities that are saved in our datastructure t. Lincons -> linear constraint *) - let invariant t = + let invariant t = let get_const acc i (var_opt, const) = if Some i = var_opt then acc else ( let xi = Environment.var_of_dim t.env i in @@ -724,6 +728,10 @@ end module D2: RelationDomain.RD with type var = Var.t = struct module D = D - include SharedFunctions.AssertionModule (D.V) (D) (struct let do_overflow_check = false end) + module ConvArg = struct + let allow_global = false + let do_overflow_check = false + end + include SharedFunctions.AssertionModule (D.V) (D) (ConvArg) include D end diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 662884f533..1e8e268a73 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -42,10 +42,6 @@ let int_of_scalar ?round (scalar: Scalar.t) = module type ConvertArg = sig val allow_global: bool -end - -module type OverflowCheck = -sig val do_overflow_check: bool end @@ -73,7 +69,7 @@ end It also handles the overflow through the flag "no_ov". For this reason it was divided from the Convert module for the pure apron domains "ApronOfCilForApronDomains", *) -module ApronOfCil (V: SV) (Bounds: ConvBounds) (Arg: ConvertArg) (Ov: OverflowCheck) (Tracked: RelationDomain.Tracked) = +module ApronOfCil (V: SV) (Bounds: ConvBounds) (Arg: ConvertArg) (Tracked: RelationDomain.Tracked) = struct open Texpr1 open Tcons1 @@ -203,7 +199,7 @@ struct | ik -> let expr = let simplify e = - let ikind = try (Cilfacade.get_ikind_exp e) with Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) in + let ikind = try (Cilfacade.get_ikind_exp e) with Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) in let simp = query e ikind in let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to ikind simp in match const with @@ -225,7 +221,7 @@ struct Binop (Mod, texpr1_expr_of_cil_exp ask @@ simplify e1, texpr1_expr_of_cil_exp ask @@ simplify e2, Int, Near) | CastE (TInt (t_ik, _) as t, e) -> begin match IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) - | exception _ -> raise (Unsupported_CilExp (Cast_not_injective t)) + | exception _ -> raise (Unsupported_CilExp (Cast_not_injective t)) | true -> texpr1_expr_of_cil_exp ask @@ simplify e | false -> let res = try (query e @@ Cilfacade.get_ikind_exp e) with Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) in @@ -239,7 +235,7 @@ struct | Some min, Some max when min >= minimal && max <= maximal -> texpr1_expr_of_cil_exp ask e | _ -> raise (Unsupported_CilExp (Cast_not_injective t))) | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) - | exception Invalid_argument _ + | exception Invalid_argument _ | _ -> (* get_ikind in is_cast_injective *) raise (Unsupported_CilExp (Cast_not_injective t)) end @@ -257,7 +253,7 @@ struct let texpr1_expr_of_cil_exp ask d env exp no_ov = let exp = Cil.constFold false exp in - if Ov.do_overflow_check then texpr1_expr_of_cil_exp_with_overflow_check ask d env exp no_ov overflow_handling_apron + if Arg.do_overflow_check then texpr1_expr_of_cil_exp_with_overflow_check ask d env exp no_ov overflow_handling_apron else texpr1_expr_of_cil_exp_with_overflow_check ask d env exp no_ov no_ov_overflow_handling let texpr1_of_cil_exp ask d env e no_ov = @@ -403,9 +399,9 @@ struct end (** Conversion between CIL expressions and Apron. *) -module Convert (V: SV) (Bounds: ConvBounds) (Arg: ConvertArg) (Ov: OverflowCheck) (Tracked: RelationDomain.Tracked)= +module Convert (V: SV) (Bounds: ConvBounds) (Arg: ConvertArg) (Tracked: RelationDomain.Tracked)= struct - include ApronOfCil (V) (Bounds) (Arg) (Ov: OverflowCheck) (Tracked) + include ApronOfCil (V) (Bounds) (Arg) (Tracked) include CilOfApron (V) end @@ -550,13 +546,12 @@ struct type_tracked vi.vtype && (not @@ GobConfig.get_bool "annotation.goblint_relation_track" || hasTrackAttribute vi.vattr) end -module AssertionModule (V: SV) (AD: AssertionRelS) (Ov: OverflowCheck) = +module AssertionModule (V: SV) (AD: AssertionRelS) (Arg: ConvertArg) = struct include AD type nonrec var = V.t module Tracked = Tracked - - module Convert = Convert (V) (Bounds) (struct let allow_global = false end) (Ov) (Tracked) + module Convert = Convert (V) (Bounds) (Arg) (Tracked) let rec exp_is_constraint = function (* constraint *) From 0fb929ba8dd9644f0969bae9c207dcd032ba29c6 Mon Sep 17 00:00:00 2001 From: jennieliangga Date: Tue, 6 Feb 2024 16:37:37 +0100 Subject: [PATCH 184/280] delete duplicated tests --- tests/regression/77-lin2vareq/18-forget_var.c | 2 - .../77-lin2vareq/29-different_types.c | 31 ------------- ...steroids.c => 29-meet-tcons-on-steroids.c} | 0 .../{34-cast-non-int.c => 30-cast-non-int.c} | 0 .../77-lin2vareq/30-termination-overflow.c | 13 ------ .../77-lin2vareq/31-overflow-unknown.c | 19 -------- .../77-lin2vareq/32-overflow-on-steroids.c | 43 ------------------- 7 files changed, 108 deletions(-) delete mode 100644 tests/regression/77-lin2vareq/29-different_types.c rename tests/regression/77-lin2vareq/{33-meet-tcons-on-steroids.c => 29-meet-tcons-on-steroids.c} (100%) rename tests/regression/77-lin2vareq/{34-cast-non-int.c => 30-cast-non-int.c} (100%) delete mode 100644 tests/regression/77-lin2vareq/30-termination-overflow.c delete mode 100644 tests/regression/77-lin2vareq/31-overflow-unknown.c delete mode 100644 tests/regression/77-lin2vareq/32-overflow-on-steroids.c diff --git a/tests/regression/77-lin2vareq/18-forget_var.c b/tests/regression/77-lin2vareq/18-forget_var.c index db69bc5f71..7fd40e5c61 100644 --- a/tests/regression/77-lin2vareq/18-forget_var.c +++ b/tests/regression/77-lin2vareq/18-forget_var.c @@ -1,6 +1,4 @@ // SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none - - #include int main() { diff --git a/tests/regression/77-lin2vareq/29-different_types.c b/tests/regression/77-lin2vareq/29-different_types.c deleted file mode 100644 index 3439b406cd..0000000000 --- a/tests/regression/77-lin2vareq/29-different_types.c +++ /dev/null @@ -1,31 +0,0 @@ -// PARAM: --set ana.activated[+] lin2vareq -#include - -int x = 42; -long y; -short z; - -int main() { - - y = (long)x; - z = (short)x; - - int a = (int)y; - short b = (short)y; - - int c = (int)z; - long d = (long)z; - - unsigned int u1 = (unsigned int)x; - unsigned long u2 = (unsigned long)y; - unsigned short u3 = (unsigned short)z; - - __goblint_check(x == a); - __goblint_check(x == c); - __goblint_check(y == d); - __goblint_check(x == (int)u1); - __goblint_check(y == (long)u2); - __goblint_check(z == (short)u3); - - return 0; -} diff --git a/tests/regression/77-lin2vareq/33-meet-tcons-on-steroids.c b/tests/regression/77-lin2vareq/29-meet-tcons-on-steroids.c similarity index 100% rename from tests/regression/77-lin2vareq/33-meet-tcons-on-steroids.c rename to tests/regression/77-lin2vareq/29-meet-tcons-on-steroids.c diff --git a/tests/regression/77-lin2vareq/34-cast-non-int.c b/tests/regression/77-lin2vareq/30-cast-non-int.c similarity index 100% rename from tests/regression/77-lin2vareq/34-cast-non-int.c rename to tests/regression/77-lin2vareq/30-cast-non-int.c diff --git a/tests/regression/77-lin2vareq/30-termination-overflow.c b/tests/regression/77-lin2vareq/30-termination-overflow.c deleted file mode 100644 index 81c6d90828..0000000000 --- a/tests/regression/77-lin2vareq/30-termination-overflow.c +++ /dev/null @@ -1,13 +0,0 @@ -// SKIP TERM PARAM: --set "ana.activated[+]" lin2vareq - -#include - -int main() { - int i = 2147483647; - i++; - while (i <= 10) { - printf("%d\n", i); - } - - return 0; -} diff --git a/tests/regression/77-lin2vareq/31-overflow-unknown.c b/tests/regression/77-lin2vareq/31-overflow-unknown.c deleted file mode 100644 index 40e073d9fb..0000000000 --- a/tests/regression/77-lin2vareq/31-overflow-unknown.c +++ /dev/null @@ -1,19 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq -#include -#include - -int main(void) { - int x = 10; - - if (x + 2147483647 == 2147483657) { - return 0; - } - - __goblint_check(1); - - // Overflow - int c = 2147483647; - c = c + 1; - - __goblint_check(c < 2147483647); // UNKNOWN! -} diff --git a/tests/regression/77-lin2vareq/32-overflow-on-steroids.c b/tests/regression/77-lin2vareq/32-overflow-on-steroids.c deleted file mode 100644 index b8275bac2f..0000000000 --- a/tests/regression/77-lin2vareq/32-overflow-on-steroids.c +++ /dev/null @@ -1,43 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] lin2vareq --enable ana.int.interval -#include - -int main(void) { - int b; - int a; - int c; // c is an unknown value - a = a % 8; // a is in the interval [-7, 7] - - b = c; // no overflow - __goblint_check(b == c);// SUCCESS - - b = c * 1; // no overflow - __goblint_check(b == c);// SUCCESS - - b = c ? c : c; // no overflow - __goblint_check(b == c);// SUCCESS - - b = a + 2; // no overflow - __goblint_check(b == a + 2);// SUCCESS - - b = c + 2; // might overflow - __goblint_check(b == c + 2);// UNKNOWN! - - b = a - 2; // no overflow - __goblint_check(b == a - 2);// SUCCESS - - b = c - 2; // might overflow - __goblint_check(b == c - 2);// UNKNOWN! - - b = a * 2 - a * 1; // no overflow - __goblint_check(b == a);// SUCCESS - - b = c * 2 - c * 1; // might overflow - __goblint_check(b == c); // UNKNOWN! - - b = (-a) + a; // no overflow - __goblint_check(b == 0); // SUCCESS - - b = (-c) + c; // might overflow - __goblint_check(b == 0); // UNKNOWN! - -} From 8892f3bb0b88b5a9f122060af6cd530e5c802e59 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Tue, 6 Feb 2024 18:04:22 +0100 Subject: [PATCH 185/280] revert unrelated changes in base.ml --- src/analyses/base.ml | 666 +++++++++++++++++++++---------------------- 1 file changed, 333 insertions(+), 333 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 16f8f94123..53523ab21b 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2391,352 +2391,352 @@ struct end in let st = match desc.special args, f.vname with - | Memset { dest; ch; count; }, _ -> - (* TODO: check count *) - let eval_ch = eval_rv ~ctx st ch in - let dest_a, dest_typ = addr_type_of_exp dest in - let value = - match eval_ch with - | Int i when ID.to_int i = Some Z.zero -> - VD.zero_init_value dest_typ - | _ -> - VD.top_value dest_typ - in - set ~ctx st dest_a dest_typ value - | Bzero { dest; count; }, _ -> - (* TODO: share something with memset special case? *) - (* TODO: check count *) - let dest_a, dest_typ = addr_type_of_exp dest in - let value = VD.zero_init_value dest_typ in - set ~ctx st dest_a dest_typ value - | Memcpy { dest = dst; src; n; }, _ -> (* TODO: use n *) - memory_copying dst src (Some n) - | Strcpy { dest = dst; src; n }, _ -> string_manipulation dst src None false None (fun ar1 ar2 -> Array (CArrays.string_copy ar1 ar2 (eval_n n))) - | Strcat { dest = dst; src; n }, _ -> string_manipulation dst src None false None (fun ar1 ar2 -> Array (CArrays.string_concat ar1 ar2 (eval_n n))) - | Strlen s, _ -> - begin match lv with - | Some lv_val -> - let dest_a = eval_lv ~ctx st lv_val in - let dest_typ = Cilfacade.typeOfLval lv_val in - let v = eval_rv ~ctx st s in - let a = address_from_value v in - let value:value = - (* if s string literal, compute strlen in string literals domain *) - (* TODO: is this reliable? there could be a char* which isn't StrPtr *) - if CilType.Typ.equal (AD.type_of a) charPtrType then - Int (AD.to_string_length a) - (* else compute strlen in array domain *) - else - begin match get ~ctx st a None with - | Array array_s -> Int (CArrays.to_string_length array_s) - | _ -> VD.top_value (unrollType dest_typ) - end in - set ~ctx st dest_a dest_typ value - | None -> st - end - | Strstr { haystack; needle }, _ -> - begin match lv with - | Some lv_val -> - (* check if needle is a substring of haystack in string literals domain if haystack and needle are string literals, - else check in null bytes domain if both haystack and needle are / can be transformed to an array domain representation; - if needle is substring, assign the substring of haystack starting at the first occurrence of needle to dest, - if it surely isn't, assign a null_ptr *) - string_manipulation haystack needle lv true (Some (fun h_a n_a -> Address (AD.substring_extraction h_a n_a))) - (fun h_ar n_ar -> match CArrays.substring_extraction h_ar n_ar with - | CArrays.IsNotSubstr -> Address (AD.null_ptr) - | CArrays.IsSubstrAtIndex0 -> Address (eval_lv ~ctx st (mkMem ~addr:(Cil.stripCasts haystack) ~off:NoOffset)) - | CArrays.IsMaybeSubstr -> Address (AD.join (eval_lv ~ctx st - (mkMem ~addr:(Cil.stripCasts haystack) ~off:(Index (Offset.Index.Exp.any, NoOffset)))) (AD.null_ptr))) - | None -> st - end - | Strcmp { s1; s2; n }, _ -> - begin match lv with - | Some _ -> - (* when s1 and s2 are string literals, compare both completely or their first n characters in the string literals domain; - else compare them in the null bytes array domain if they are / can be transformed to an array domain representation *) - string_manipulation s1 s2 lv false (Some (fun s1_a s2_a -> Int (AD.string_comparison s1_a s2_a (eval_n n)))) - (fun s1_ar s2_ar -> Int (CArrays.string_comparison s1_ar s2_ar (eval_n n))) - | None -> st - end - | Abort, _ -> raise Deadcode - | ThreadExit { ret_val = exp }, _ -> - begin match ThreadId.get_current (Analyses.ask_of_ctx ctx) with - | `Lifted tid -> - ( - let rv = eval_rv ~ctx ctx.local exp in - ctx.sideg (V.thread tid) (G.create_thread rv); - (* TODO: emit thread return event so other analyses are aware? *) - (* TODO: publish still needed? *) - publish_all ctx `Return; (* like normal return *) - let ask = Analyses.ask_of_ctx ctx in - match ThreadId.get_current ask with - | `Lifted tid when ThreadReturn.is_current ask -> - ignore @@ Priv.thread_return ask (priv_getg ctx.global) (priv_sideg ctx.sideg) tid st - | _ -> ()) - | _ -> () - end; - raise Deadcode - | MutexAttrSetType {attr = attr; typ = mtyp}, _ -> - begin - let get_type lval = - let address = eval_lv ~ctx st lval in - AD.type_of address - in - let dst_lval = mkMem ~addr:(Cil.stripCasts attr) ~off:NoOffset in - let dest_typ = get_type dst_lval in - let dest_a = eval_lv ~ctx st dst_lval in - match eval_rv ~ctx st mtyp with - | Int x -> - begin - match ID.to_int x with - | Some z -> - if M.tracing then M.tracel "attr" "setting\n"; - set ~ctx st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.of_int z)) - | None -> set ~ctx st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.top ())) - end - | _ -> set ~ctx st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.top ())) - end - | Identity e, _ -> - begin match lv with - | Some x -> assign ctx x e - | None -> ctx.local - end - (**Floating point classification and trigonometric functions defined in c99*) - | Math { fun_args; }, _ -> - let apply_unary fk float_fun x = - let eval_x = eval_rv ~ctx st x in - begin match eval_x with - | Float float_x -> float_fun (FD.cast_to fk float_x) - | _ -> failwith ("non-floating-point argument in call to function "^f.vname) - end - in - let apply_binary fk float_fun x y = - let eval_x = eval_rv ~ctx st x in - let eval_y = eval_rv ~ctx st y in - begin match eval_x, eval_y with - | Float float_x, Float float_y -> float_fun (FD.cast_to fk float_x) (FD.cast_to fk float_y) - | _ -> failwith ("non-floating-point argument in call to function "^f.vname) - end - in - let apply_abs ik x = - let eval_x = eval_rv ~ctx st x in - begin match eval_x with - | Int int_x -> - let xcast = ID.cast_to ik int_x in - (* the absolute value of the most-negative value is out of range for 2'complement types *) - (match (ID.minimal xcast), (ID.minimal (ID.top_of ik)) with - | _, None - | None, _ -> ID.top_of ik - | Some mx, Some mm when Z.equal mx mm -> ID.top_of ik - | _, _ -> - let x1 = ID.neg (ID.meet (ID.ending ik Z.zero) xcast) in - let x2 = ID.meet (ID.starting ik Z.zero) xcast in - ID.join x1 x2 - ) - | _ -> failwith ("non-integer argument in call to function "^f.vname) - end + | Memset { dest; ch; count; }, _ -> + (* TODO: check count *) + let eval_ch = eval_rv ~ctx st ch in + let dest_a, dest_typ = addr_type_of_exp dest in + let value = + match eval_ch with + | Int i when ID.to_int i = Some Z.zero -> + VD.zero_init_value dest_typ + | _ -> + VD.top_value dest_typ + in + set ~ctx st dest_a dest_typ value + | Bzero { dest; count; }, _ -> + (* TODO: share something with memset special case? *) + (* TODO: check count *) + let dest_a, dest_typ = addr_type_of_exp dest in + let value = VD.zero_init_value dest_typ in + set ~ctx st dest_a dest_typ value + | Memcpy { dest = dst; src; n; }, _ -> (* TODO: use n *) + memory_copying dst src (Some n) + | Strcpy { dest = dst; src; n }, _ -> string_manipulation dst src None false None (fun ar1 ar2 -> Array (CArrays.string_copy ar1 ar2 (eval_n n))) + | Strcat { dest = dst; src; n }, _ -> string_manipulation dst src None false None (fun ar1 ar2 -> Array (CArrays.string_concat ar1 ar2 (eval_n n))) + | Strlen s, _ -> + begin match lv with + | Some lv_val -> + let dest_a = eval_lv ~ctx st lv_val in + let dest_typ = Cilfacade.typeOfLval lv_val in + let v = eval_rv ~ctx st s in + let a = address_from_value v in + let value:value = + (* if s string literal, compute strlen in string literals domain *) + (* TODO: is this reliable? there could be a char* which isn't StrPtr *) + if CilType.Typ.equal (AD.type_of a) charPtrType then + Int (AD.to_string_length a) + (* else compute strlen in array domain *) + else + begin match get ~ctx st a None with + | Array array_s -> Int (CArrays.to_string_length array_s) + | _ -> VD.top_value (unrollType dest_typ) + end in + set ~ctx st dest_a dest_typ value + | None -> st + end + | Strstr { haystack; needle }, _ -> + begin match lv with + | Some lv_val -> + (* check if needle is a substring of haystack in string literals domain if haystack and needle are string literals, + else check in null bytes domain if both haystack and needle are / can be transformed to an array domain representation; + if needle is substring, assign the substring of haystack starting at the first occurrence of needle to dest, + if it surely isn't, assign a null_ptr *) + string_manipulation haystack needle lv true (Some (fun h_a n_a -> Address (AD.substring_extraction h_a n_a))) + (fun h_ar n_ar -> match CArrays.substring_extraction h_ar n_ar with + | CArrays.IsNotSubstr -> Address (AD.null_ptr) + | CArrays.IsSubstrAtIndex0 -> Address (eval_lv ~ctx st (mkMem ~addr:(Cil.stripCasts haystack) ~off:NoOffset)) + | CArrays.IsMaybeSubstr -> Address (AD.join (eval_lv ~ctx st + (mkMem ~addr:(Cil.stripCasts haystack) ~off:(Index (Offset.Index.Exp.any, NoOffset)))) (AD.null_ptr))) + | None -> st + end + | Strcmp { s1; s2; n }, _ -> + begin match lv with + | Some _ -> + (* when s1 and s2 are string literals, compare both completely or their first n characters in the string literals domain; + else compare them in the null bytes array domain if they are / can be transformed to an array domain representation *) + string_manipulation s1 s2 lv false (Some (fun s1_a s2_a -> Int (AD.string_comparison s1_a s2_a (eval_n n)))) + (fun s1_ar s2_ar -> Int (CArrays.string_comparison s1_ar s2_ar (eval_n n))) + | None -> st + end + | Abort, _ -> raise Deadcode + | ThreadExit { ret_val = exp }, _ -> + begin match ThreadId.get_current (Analyses.ask_of_ctx ctx) with + | `Lifted tid -> + ( + let rv = eval_rv ~ctx ctx.local exp in + ctx.sideg (V.thread tid) (G.create_thread rv); + (* TODO: emit thread return event so other analyses are aware? *) + (* TODO: publish still needed? *) + publish_all ctx `Return; (* like normal return *) + let ask = Analyses.ask_of_ctx ctx in + match ThreadId.get_current ask with + | `Lifted tid when ThreadReturn.is_current ask -> + ignore @@ Priv.thread_return ask (priv_getg ctx.global) (priv_sideg ctx.sideg) tid st + | _ -> ()) + | _ -> () + end; + raise Deadcode + | MutexAttrSetType {attr = attr; typ = mtyp}, _ -> + begin + let get_type lval = + let address = eval_lv ~ctx st lval in + AD.type_of address in - let result:value = - begin match fun_args with - | Nan (fk, str) when Cil.isPointerType (Cilfacade.typeOf str) -> Float (FD.nan_of fk) - | Nan _ -> failwith ("non-pointer argument in call to function "^f.vname) - | Inf fk -> Float (FD.inf_of fk) - | Isfinite x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isfinite x)) - | Isinf x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isinf x)) - | Isnan x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isnan x)) - | Isnormal x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isnormal x)) - | Signbit x -> Int (ID.cast_to IInt (apply_unary FDouble FD.signbit x)) - | Ceil (fk,x) -> Float (apply_unary fk FD.ceil x) - | Floor (fk,x) -> Float (apply_unary fk FD.floor x) - | Fabs (fk, x) -> Float (apply_unary fk FD.fabs x) - | Acos (fk, x) -> Float (apply_unary fk FD.acos x) - | Asin (fk, x) -> Float (apply_unary fk FD.asin x) - | Atan (fk, x) -> Float (apply_unary fk FD.atan x) - | Atan2 (fk, y, x) -> Float (apply_binary fk (fun y' x' -> FD.atan (FD.div y' x')) y x) - | Cos (fk, x) -> Float (apply_unary fk FD.cos x) - | Sin (fk, x) -> Float (apply_unary fk FD.sin x) - | Tan (fk, x) -> Float (apply_unary fk FD.tan x) - | Isgreater (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.gt x y)) - | Isgreaterequal (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.ge x y)) - | Isless (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.lt x y)) - | Islessequal (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.le x y)) - | Islessgreater (x,y) -> Int(ID.c_logor (ID.cast_to IInt (apply_binary FDouble FD.lt x y)) (ID.cast_to IInt (apply_binary FDouble FD.gt x y))) - | Isunordered (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.unordered x y)) - | Fmax (fd, x ,y) -> Float (apply_binary fd FD.fmax x y) - | Fmin (fd, x ,y) -> Float (apply_binary fd FD.fmin x y) - | Sqrt (fk, x) -> Float (apply_unary fk FD.sqrt x) - | Abs (ik, x) -> Int (ID.cast_to ik (apply_abs ik x)) + let dst_lval = mkMem ~addr:(Cil.stripCasts attr) ~off:NoOffset in + let dest_typ = get_type dst_lval in + let dest_a = eval_lv ~ctx st dst_lval in + match eval_rv ~ctx st mtyp with + | Int x -> + begin + match ID.to_int x with + | Some z -> + if M.tracing then M.tracel "attr" "setting\n"; + set ~ctx st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.of_int z)) + | None -> set ~ctx st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.top ())) end - in - begin match lv with - | Some lv_val -> set ~ctx st (eval_lv ~ctx st lv_val) (Cilfacade.typeOfLval lv_val) result - | None -> st - end - (* handling thread creations *) - | ThreadCreate _, _ -> - invalidate_ret_lv ctx.local (* actual results joined via threadspawn *) - (* handling thread joins... sort of *) - | ThreadJoin { thread = id; ret_var }, _ -> - let st' = - (* TODO: should invalidate shallowly? https://github.com/goblint/analyzer/pull/1224#discussion_r1405826773 *) - match eval_rv ~ctx st ret_var with - | Int n when GobOption.exists (Z.equal Z.zero) (ID.to_int n) -> st - | Address ret_a -> - begin match eval_rv ~ctx st id with - | Thread a when ValueDomain.Threads.is_top a -> invalidate ~ctx st [ret_var] - | Thread a -> - let v = List.fold VD.join (VD.bot ()) (List.map (fun x -> G.thread (ctx.global (V.thread x))) (ValueDomain.Threads.elements a)) in - (* TODO: is this type right? *) - set ~ctx st ret_a (Cilfacade.typeOf ret_var) v - | _ -> invalidate ~ctx st [ret_var] - end - | _ -> invalidate ~ctx st [ret_var] - in - let st' = invalidate_ret_lv st' in - Priv.thread_join (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st' - | Unknown, "__goblint_assume_join" -> - let id = List.hd args in - Priv.thread_join ~force:true (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st - | Alloca size, _ -> begin - match lv with - | Some lv -> - let heap_var = AD.of_var (heap_var true ctx) in - (* ignore @@ printf "alloca will allocate %a bytes\n" ID.pretty (eval_int ~ctx size); *) - set_many ~ctx st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~ctx st size, true)); - (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address heap_var)] - | _ -> st + | _ -> set ~ctx st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.top ())) + end + | Identity e, _ -> + begin match lv with + | Some x -> assign ctx x e + | None -> ctx.local + end + (**Floating point classification and trigonometric functions defined in c99*) + | Math { fun_args; }, _ -> + let apply_unary fk float_fun x = + let eval_x = eval_rv ~ctx st x in + begin match eval_x with + | Float float_x -> float_fun (FD.cast_to fk float_x) + | _ -> failwith ("non-floating-point argument in call to function "^f.vname) end - | Malloc size, _ -> begin - match lv with - | Some lv -> - let heap_var = - if (get_bool "sem.malloc.fail") - then AD.join (AD.of_var (heap_var false ctx)) AD.null_ptr - else AD.of_var (heap_var false ctx) - in - (* ignore @@ printf "malloc will allocate %a bytes\n" ID.pretty (eval_int ~ctx size); *) - set_many ~ctx st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~ctx st size, true)); - (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address heap_var)] - | _ -> st + in + let apply_binary fk float_fun x y = + let eval_x = eval_rv ~ctx st x in + let eval_y = eval_rv ~ctx st y in + begin match eval_x, eval_y with + | Float float_x, Float float_y -> float_fun (FD.cast_to fk float_x) (FD.cast_to fk float_y) + | _ -> failwith ("non-floating-point argument in call to function "^f.vname) end - | Calloc { count = n; size }, _ -> - begin match lv with - | Some lv -> (* array length is set to one, as num*size is done when turning into `Calloc *) - let heap_var = heap_var false ctx in - let add_null addr = - if get_bool "sem.malloc.fail" - then AD.join addr AD.null_ptr (* calloc can fail and return NULL *) - else addr in - let ik = Cilfacade.ptrdiff_ikind () in - let sizeval = eval_int ~ctx st size in - let countval = eval_int ~ctx st n in - if ID.to_int countval = Some Z.one then ( - set_many ~ctx st [ - (add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, false)); - (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_var heap_var))) - ] - ) - else ( - let blobsize = ID.mul (ID.cast_to ik @@ sizeval) (ID.cast_to ik @@ countval) in - (* the memory that was allocated by calloc is set to bottom, but we keep track that it originated from calloc, so when bottom is read from memory allocated by calloc it is turned to zero *) - set_many ~ctx st [ - (add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.one) (Blob (VD.bot (), blobsize, false)))); - (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_mval (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.zero, `NoOffset))))) - ] + in + let apply_abs ik x = + let eval_x = eval_rv ~ctx st x in + begin match eval_x with + | Int int_x -> + let xcast = ID.cast_to ik int_x in + (* the absolute value of the most-negative value is out of range for 2'complement types *) + (match (ID.minimal xcast), (ID.minimal (ID.top_of ik)) with + | _, None + | None, _ -> ID.top_of ik + | Some mx, Some mm when Z.equal mx mm -> ID.top_of ik + | _, _ -> + let x1 = ID.neg (ID.meet (ID.ending ik Z.zero) xcast) in + let x2 = ID.meet (ID.starting ik Z.zero) xcast in + ID.join x1 x2 ) - | _ -> st - end - | Realloc { ptr = p; size }, _ -> - (* Realloc shouldn't be passed non-dynamically allocated memory *) - check_invalid_mem_dealloc ctx f p; - begin match lv with - | Some lv -> - let p_rv = eval_rv ~ctx st p in - let p_addr = - match p_rv with - | Address a -> a - (* TODO: don't we already have logic for this? *) - | Int i when ID.to_int i = Some Z.zero -> AD.null_ptr - | Int i -> AD.top_ptr - | _ -> AD.top_ptr (* TODO: why does this ever happen? *) - in - let p_addr' = AD.remove NullPtr p_addr in (* realloc with NULL is same as malloc, remove to avoid unknown value from NullPtr access *) - let p_addr_get = get ~ctx st p_addr' None in (* implicitly includes join of malloc value (VD.bot) *) - let size_int = eval_int ~ctx st size in - let heap_val:value = Blob (p_addr_get, size_int, true) in (* copy old contents with new size *) - let heap_addr = AD.of_var (heap_var false ctx) in - let heap_addr' = - if get_bool "sem.malloc.fail" then - AD.join heap_addr AD.null_ptr - else - heap_addr - in - let lv_addr = eval_lv ~ctx st lv in - set_many ~ctx st [ - (heap_addr, TVoid [], heap_val); - (lv_addr, Cilfacade.typeOfLval lv, Address heap_addr'); - ] (* TODO: free (i.e. invalidate) old blob if successful? *) - | None -> - st + | _ -> failwith ("non-integer argument in call to function "^f.vname) end - | Free ptr, _ -> - (* Free shouldn't be passed non-dynamically allocated memory *) - check_invalid_mem_dealloc ctx f ptr; - st - | Assert { exp; refine; _ }, _ -> assert_fn ctx exp refine - | Setjmp { env }, _ -> - let st' = match eval_rv ~ctx st env with - | Address jmp_buf -> - let value = VD.JmpBuf (ValueDomain.JmpBufs.Bufs.singleton (Target (ctx.prev_node, ctx.control_context ())), false) in - let r = set ~ctx st jmp_buf (Cilfacade.typeOf env) value in - if M.tracing then M.tracel "setjmp" "setting setjmp %a on %a -> %a\n" d_exp env D.pretty st D.pretty r; - r - | _ -> failwith "problem?!" - in - begin match lv with - | Some lv -> - set ~ctx st' (eval_lv ~ctx st lv) (Cilfacade.typeOfLval lv) (Int (ID.of_int IInt Z.zero)) - | None -> st' - end - | Longjmp {env; value}, _ -> - let ensure_not_zero (rv:value) = match rv with - | Int i -> - begin match ID.to_bool i with - | Some true -> rv - | Some false -> - M.error "Must: Longjmp with a value of 0 is silently changed to 1"; - Int (ID.of_int (ID.ikind i) Z.one) - | None -> - M.warn "May: Longjmp with a value of 0 is silently changed to 1"; - let ik = ID.ikind i in - Int (ID.join (ID.meet i (ID.of_excl_list ik [Z.zero])) (ID.of_int ik Z.one)) - end - | _ -> - M.warn ~category:Program "Arguments to longjmp are strange!"; - rv - in - let rv = ensure_not_zero @@ eval_rv ~ctx ctx.local value in - let t = Cilfacade.typeOf value in - set ~ctx ~t_override:t ctx.local (AD.of_var !longjmp_return) t rv (* Not raising Deadcode here, deadcode is raised at a higher level! *) - | Rand, _ -> - begin match lv with - | Some x -> - let result:value = (Int (ID.starting IInt Z.zero)) in - set ~ctx st (eval_lv ~ctx st x) (Cilfacade.typeOfLval x) result - | None -> st + in + let result:value = + begin match fun_args with + | Nan (fk, str) when Cil.isPointerType (Cilfacade.typeOf str) -> Float (FD.nan_of fk) + | Nan _ -> failwith ("non-pointer argument in call to function "^f.vname) + | Inf fk -> Float (FD.inf_of fk) + | Isfinite x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isfinite x)) + | Isinf x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isinf x)) + | Isnan x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isnan x)) + | Isnormal x -> Int (ID.cast_to IInt (apply_unary FDouble FD.isnormal x)) + | Signbit x -> Int (ID.cast_to IInt (apply_unary FDouble FD.signbit x)) + | Ceil (fk,x) -> Float (apply_unary fk FD.ceil x) + | Floor (fk,x) -> Float (apply_unary fk FD.floor x) + | Fabs (fk, x) -> Float (apply_unary fk FD.fabs x) + | Acos (fk, x) -> Float (apply_unary fk FD.acos x) + | Asin (fk, x) -> Float (apply_unary fk FD.asin x) + | Atan (fk, x) -> Float (apply_unary fk FD.atan x) + | Atan2 (fk, y, x) -> Float (apply_binary fk (fun y' x' -> FD.atan (FD.div y' x')) y x) + | Cos (fk, x) -> Float (apply_unary fk FD.cos x) + | Sin (fk, x) -> Float (apply_unary fk FD.sin x) + | Tan (fk, x) -> Float (apply_unary fk FD.tan x) + | Isgreater (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.gt x y)) + | Isgreaterequal (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.ge x y)) + | Isless (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.lt x y)) + | Islessequal (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.le x y)) + | Islessgreater (x,y) -> Int(ID.c_logor (ID.cast_to IInt (apply_binary FDouble FD.lt x y)) (ID.cast_to IInt (apply_binary FDouble FD.gt x y))) + | Isunordered (x,y) -> Int(ID.cast_to IInt (apply_binary FDouble FD.unordered x y)) + | Fmax (fd, x ,y) -> Float (apply_binary fd FD.fmax x y) + | Fmin (fd, x ,y) -> Float (apply_binary fd FD.fmin x y) + | Sqrt (fk, x) -> Float (apply_unary fk FD.sqrt x) + | Abs (ik, x) -> Int (ID.cast_to ik (apply_abs ik x)) end - | _, _ -> - let st = - special_unknown_invalidate ctx f args + in + begin match lv with + | Some lv_val -> set ~ctx st (eval_lv ~ctx st lv_val) (Cilfacade.typeOfLval lv_val) result + | None -> st + end + (* handling thread creations *) + | ThreadCreate _, _ -> + invalidate_ret_lv ctx.local (* actual results joined via threadspawn *) + (* handling thread joins... sort of *) + | ThreadJoin { thread = id; ret_var }, _ -> + let st' = + (* TODO: should invalidate shallowly? https://github.com/goblint/analyzer/pull/1224#discussion_r1405826773 *) + match eval_rv ~ctx st ret_var with + | Int n when GobOption.exists (Z.equal Z.zero) (ID.to_int n) -> st + | Address ret_a -> + begin match eval_rv ~ctx st id with + | Thread a when ValueDomain.Threads.is_top a -> invalidate ~ctx st [ret_var] + | Thread a -> + let v = List.fold VD.join (VD.bot ()) (List.map (fun x -> G.thread (ctx.global (V.thread x))) (ValueDomain.Threads.elements a)) in + (* TODO: is this type right? *) + set ~ctx st ret_a (Cilfacade.typeOf ret_var) v + | _ -> invalidate ~ctx st [ret_var] + end + | _ -> invalidate ~ctx st [ret_var] + in + let st' = invalidate_ret_lv st' in + Priv.thread_join (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st' + | Unknown, "__goblint_assume_join" -> + let id = List.hd args in + Priv.thread_join ~force:true (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st + | Alloca size, _ -> begin + match lv with + | Some lv -> + let heap_var = AD.of_var (heap_var true ctx) in + (* ignore @@ printf "alloca will allocate %a bytes\n" ID.pretty (eval_int ~ctx size); *) + set_many ~ctx st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~ctx st size, true)); + (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address heap_var)] + | _ -> st + end + | Malloc size, _ -> begin + match lv with + | Some lv -> + let heap_var = + if (get_bool "sem.malloc.fail") + then AD.join (AD.of_var (heap_var false ctx)) AD.null_ptr + else AD.of_var (heap_var false ctx) + in + (* ignore @@ printf "malloc will allocate %a bytes\n" ID.pretty (eval_int ~ctx size); *) + set_many ~ctx st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~ctx st size, true)); + (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address heap_var)] + | _ -> st + end + | Calloc { count = n; size }, _ -> + begin match lv with + | Some lv -> (* array length is set to one, as num*size is done when turning into `Calloc *) + let heap_var = heap_var false ctx in + let add_null addr = + if get_bool "sem.malloc.fail" + then AD.join addr AD.null_ptr (* calloc can fail and return NULL *) + else addr in + let ik = Cilfacade.ptrdiff_ikind () in + let sizeval = eval_int ~ctx st size in + let countval = eval_int ~ctx st n in + if ID.to_int countval = Some Z.one then ( + set_many ~ctx st [ + (add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, false)); + (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_var heap_var))) + ] + ) + else ( + let blobsize = ID.mul (ID.cast_to ik @@ sizeval) (ID.cast_to ik @@ countval) in + (* the memory that was allocated by calloc is set to bottom, but we keep track that it originated from calloc, so when bottom is read from memory allocated by calloc it is turned to zero *) + set_many ~ctx st [ + (add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.one) (Blob (VD.bot (), blobsize, false)))); + (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_mval (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.zero, `NoOffset))))) + ] + ) + | _ -> st + end + | Realloc { ptr = p; size }, _ -> + (* Realloc shouldn't be passed non-dynamically allocated memory *) + check_invalid_mem_dealloc ctx f p; + begin match lv with + | Some lv -> + let p_rv = eval_rv ~ctx st p in + let p_addr = + match p_rv with + | Address a -> a + (* TODO: don't we already have logic for this? *) + | Int i when ID.to_int i = Some Z.zero -> AD.null_ptr + | Int i -> AD.top_ptr + | _ -> AD.top_ptr (* TODO: why does this ever happen? *) + in + let p_addr' = AD.remove NullPtr p_addr in (* realloc with NULL is same as malloc, remove to avoid unknown value from NullPtr access *) + let p_addr_get = get ~ctx st p_addr' None in (* implicitly includes join of malloc value (VD.bot) *) + let size_int = eval_int ~ctx st size in + let heap_val:value = Blob (p_addr_get, size_int, true) in (* copy old contents with new size *) + let heap_addr = AD.of_var (heap_var false ctx) in + let heap_addr' = + if get_bool "sem.malloc.fail" then + AD.join heap_addr AD.null_ptr + else + heap_addr + in + let lv_addr = eval_lv ~ctx st lv in + set_many ~ctx st [ + (heap_addr, TVoid [], heap_val); + (lv_addr, Cilfacade.typeOfLval lv, Address heap_addr'); + ] (* TODO: free (i.e. invalidate) old blob if successful? *) + | None -> + st + end + | Free ptr, _ -> + (* Free shouldn't be passed non-dynamically allocated memory *) + check_invalid_mem_dealloc ctx f ptr; + st + | Assert { exp; refine; _ }, _ -> assert_fn ctx exp refine + | Setjmp { env }, _ -> + let st' = match eval_rv ~ctx st env with + | Address jmp_buf -> + let value = VD.JmpBuf (ValueDomain.JmpBufs.Bufs.singleton (Target (ctx.prev_node, ctx.control_context ())), false) in + let r = set ~ctx st jmp_buf (Cilfacade.typeOf env) value in + if M.tracing then M.tracel "setjmp" "setting setjmp %a on %a -> %a\n" d_exp env D.pretty st D.pretty r; + r + | _ -> failwith "problem?!" + in + begin match lv with + | Some lv -> + set ~ctx st' (eval_lv ~ctx st lv) (Cilfacade.typeOfLval lv) (Int (ID.of_int IInt Z.zero)) + | None -> st' + end + | Longjmp {env; value}, _ -> + let ensure_not_zero (rv:value) = match rv with + | Int i -> + begin match ID.to_bool i with + | Some true -> rv + | Some false -> + M.error "Must: Longjmp with a value of 0 is silently changed to 1"; + Int (ID.of_int (ID.ikind i) Z.one) + | None -> + M.warn "May: Longjmp with a value of 0 is silently changed to 1"; + let ik = ID.ikind i in + Int (ID.join (ID.meet i (ID.of_excl_list ik [Z.zero])) (ID.of_int ik Z.one)) + end + | _ -> + M.warn ~category:Program "Arguments to longjmp are strange!"; + rv + in + let rv = ensure_not_zero @@ eval_rv ~ctx ctx.local value in + let t = Cilfacade.typeOf value in + set ~ctx ~t_override:t ctx.local (AD.of_var !longjmp_return) t rv (* Not raising Deadcode here, deadcode is raised at a higher level! *) + | Rand, _ -> + begin match lv with + | Some x -> + let result:value = (Int (ID.starting IInt Z.zero)) in + set ~ctx st (eval_lv ~ctx st x) (Cilfacade.typeOfLval x) result + | None -> st + end + | _, _ -> + let st = + special_unknown_invalidate ctx f args (* * TODO: invalidate vars reachable via args * publish globals * if single-threaded: *call f*, privatize globals * else: spawn f *) - in - (* invalidate lhs in case of assign *) - invalidate_ret_lv st + in + (* invalidate lhs in case of assign *) + invalidate_ret_lv st in if get_bool "sem.noreturn.dead_code" && Cil.hasAttribute "noreturn" f.vattr then raise Deadcode else st From 5b51a01f71b10ba090618049d479f1a53d98c3d2 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Tue, 6 Feb 2024 18:19:20 +0100 Subject: [PATCH 186/280] use ctx.ask --- src/analyses/base.ml | 4 ++-- src/cdomains/apron/sharedFunctions.apron.ml | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 53523ab21b..3cda0bbf05 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1247,7 +1247,7 @@ struct | exception _ -> BoolDomain.MayBool.top () | ik -> let checkBinop e1 e2 binop = - match (Analyses.ask_of_ctx ctx).f (EvalInt e1), (Analyses.ask_of_ctx ctx).f (EvalInt e2) with + match ctx.ask (EvalInt e1), ctx.ask (EvalInt e2) with | `Bot, _ -> false | _, `Bot -> false | `Lifted i1, `Lifted i2 -> @@ -1262,7 +1262,7 @@ struct | _ -> true) | _ -> true in let checkPredicate e pred = - match (Analyses.ask_of_ctx ctx).f (EvalInt e) with + match ctx.ask (EvalInt e) with | `Bot -> false | `Lifted i -> (let (min_ik, _) = IntDomain.Size.range ik in diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 1e8e268a73..e95b5769b2 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -235,8 +235,7 @@ struct | Some min, Some max when min >= minimal && max <= maximal -> texpr1_expr_of_cil_exp ask e | _ -> raise (Unsupported_CilExp (Cast_not_injective t))) | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) - | exception Invalid_argument _ - | _ -> (* get_ikind in is_cast_injective *) + | exception Invalid_argument _ -> raise (Unsupported_CilExp (Cast_not_injective t)) end | _ -> From 3e6367fcfd75cc44281c0c4f606a4cce33ccf915 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Tue, 6 Feb 2024 19:04:36 +0100 Subject: [PATCH 187/280] remove redundant preprocessing for no_overflow --- src/analyses/apron/relationAnalysis.apron.ml | 10 +--------- src/cdomains/apron/sharedFunctions.apron.ml | 5 +++-- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index ac9360d4e1..6876e7fb8a 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -175,7 +175,7 @@ struct Since affine equalities can only keep track of integer bounds of expressions evaluating to definite constants, we now query the integer bounds information for expressions from other analysis. If an analysis returns bounds that are unequal to min and max of ikind , we can exclude the possibility that an overflow occurs and the abstract effect of the expression assignment can be used, i.e. we do not have to set the variable's value to top. *) - let no_overflow_not_constraint (ask: Queries.ask) exp = + let no_overflow (ask: Queries.ask) exp = match Cilfacade.get_ikind_exp exp with | exception Invalid_argument _ -> false (* is thrown by get_ikind_exp when the type of the expression is not an integer type *) | exception Cilfacade.TypeOfError _ -> false @@ -187,14 +187,6 @@ struct else not (ask.f (MaySignedOverflow exp)) - let rec no_overflow (ask: Queries.ask) exp = - match exp with - | BinOp ((Lt | Gt | Le | Ge | Eq | Ne), e1, e2, _) -> - no_overflow_not_constraint ask e1 && no_overflow_not_constraint ask e2 - | BinOp ((LAnd | LOr), e1, e2, _) -> no_overflow ask e1 && no_overflow ask e2 - | UnOp (LNot,e,_) -> no_overflow ask e - | exp -> no_overflow_not_constraint ask exp - let no_overflow ctx exp = lazy ( let res = no_overflow ctx exp in if M.tracing then M.tracel "no_ov" "no_ov %b exp: %a\n" res d_exp exp; diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index e95b5769b2..15fa59ac33 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -97,8 +97,9 @@ struct (** This version without an overflow check is used by the affeq and lin2vareq domains. They do the overflow handling before converting to Texpr1, and store the result in the "no_ov" flag. Therefore we only check the no_ov flag here. *) let no_ov_overflow_handling no_ov ik env expr d exp = begin try - if not @@ IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp exp) - && not (Lazy.force no_ov) then + if IntDomain.should_wrap (Cilfacade.get_ikind_exp exp) || + (not @@ IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp exp) + && not (Lazy.force no_ov)) then (raise (Unsupported_CilExp Overflow)) with Invalid_argument e -> raise ((Unsupported_CilExp Exp_not_supported))(* This exception is raised by Cilfacade.get_ikind_exp when the expression is not an integer expression, for example if it is a float expression. *) From 72957427efcdd49daec8b59888f26541add28ebc Mon Sep 17 00:00:00 2001 From: Alina Weber Date: Wed, 7 Feb 2024 00:04:01 +0100 Subject: [PATCH 188/280] Adjusted comment --- src/cdomains/apron/sharedFunctions.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 15fa59ac33..b829b4526d 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -166,7 +166,7 @@ struct let res = match ask.f (EvalInt e) with | `Bot -> raise (Unsupported_CilExp Exp_not_supported) (* This should never happen according to Michael Schwarz *) | `Top -> IntDomain.IntDomTuple.top_of ik - | `Lifted x -> x (* According to base.ml:704 cast should be unnecessary because it should be taken care of by EvalInt. *) + | `Lifted x -> x (* Cast should be unnecessary because it should be taken care of by EvalInt. *) in (* If the returned interval is top of the expected ikind (i.e. the value is unknown ) or the returned interval is in range of the expected interval, return top - If top is returned the expression will be rewritten. From 61e2bce4f625e6f9e0fac990b4ad524b3df86b50 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 8 Feb 2024 20:36:55 +0100 Subject: [PATCH 189/280] Add CI jobs --- .github/workflows/coverage.yml | 3 +++ .github/workflows/locked.yml | 3 +++ .github/workflows/unlocked.yml | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 4b47a66e15..5984e70f0d 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -65,6 +65,9 @@ jobs: - name: Test apron regression (Mukherjee et. al SAS '17 paper') # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) run: ruby scripts/update_suite.rb group apron-mukherjee -s + - name: Test lin2vareq regression # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) + run: ruby scripts/update_suite.rb group lin2vareq -s + - name: Test apron termination regression # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) run: ruby scripts/update_suite.rb group termination -s diff --git a/.github/workflows/locked.yml b/.github/workflows/locked.yml index e25ccfcea1..333b04ef07 100644 --- a/.github/workflows/locked.yml +++ b/.github/workflows/locked.yml @@ -64,6 +64,9 @@ jobs: - name: Test apron regression (Mukherjee et. al SAS '17 paper') # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) run: ruby scripts/update_suite.rb group apron-mukherjee -s + - name: Test lin2vareq regression # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) + run: ruby scripts/update_suite.rb group lin2vareq -s + - name: Test apron termination regression # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) run: ruby scripts/update_suite.rb group termination -s diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 57fa0cb6b5..b76e76418f 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -92,6 +92,10 @@ jobs: if: ${{ matrix.apron }} run: ruby scripts/update_suite.rb group apron-mukherjee -s + - name: Test lin2vareq regression # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) + if: ${{ matrix.apron }} + run: ruby scripts/update_suite.rb group lin2vareq -s + - name: Test apron termination regression # skipped by default but CI has apron, so explicitly test group (which ignores skipping -- it's now a feature!) if: ${{ matrix.apron }} run: ruby scripts/update_suite.rb group termination -s From f7988678599e6da0e14f094c3ea8fe87993a06e3 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 8 Feb 2024 21:03:29 +0100 Subject: [PATCH 190/280] Rewrite `show` --- .../apron/linearTwoVarEqualityDomain.apron.ml | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index a7ba7d7935..2970de9e05 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -291,24 +291,30 @@ struct (*this shows "top" for a specific environment to enable the calculations. It is the top_of of all equalities*) let top_of env = {d = Some (EArray.make_empty_array (Environment.size env)); env = env} - (*Is not expected to be called but implemented for completeness *) + (** Is not expected to be called but implemented for completeness *) let top () = {d = Some (EArray.empty()); env = empty_env} - (*is_top returns true for top_of array and empty array *) + (** is_top returns true for top_of array and empty array *) let is_top t = GobOption.exists EArray.is_top_array t.d - (* prints the current variable equalities with resolved variable names *) + (** prints the current variable equalities with resolved variable names *) let show varM = let lookup i = Var.to_string (Environment.var_of_dim varM.env i) in - let show_var i tuple = - match tuple with - | (None, offset) -> (lookup i) ^ " = " ^ Z.to_string offset ^ ";\n" - | (Some index, offset) -> (lookup i) ^ " = " ^ lookup index ^ - (if offset <> Z.zero then " + " ^ Z.to_string offset else "") ^ ";\n" - in if is_top varM then "⊤\n" else - match varM.d with - | None -> "⊥\n" - | Some arr -> if is_bot varM then "Bot \n" else Array.fold_left (fun acc elem -> acc ^ elem ) "" (Array.mapi show_var arr) + let show_offs o = if Z.equal o Z.zero then "" else " + " ^ Z.to_string o in + let show_var i = function + | (None, o) -> (lookup i) ^ " = " ^ Z.to_string o ^ ";\n" + | (Some index, o) when i <> index -> + (lookup i) ^ " = " ^ lookup index ^ show_offs o ^ ";\n" + | _ -> "" + in + match varM.d with + | None -> "⊥\n" + | Some arr when EArray.is_top_array arr -> "⊤\n" + | Some arr -> + if is_bot varM then + "Bot \n" + else + Array.fold_lefti (fun acc i elem -> acc ^ show_var i elem) "" arr let pretty () (x:t) = text (show x) let printXml f x = BatPrintf.fprintf f "\n\n\nequalities-array\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) From a076907cf53bed7215760251f847da2f164d0af2 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 8 Feb 2024 21:25:48 +0100 Subject: [PATCH 191/280] Rewrite `invariant` --- .../apron/linearTwoVarEqualityDomain.apron.ml | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 2970de9e05..696fba7cd0 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -693,31 +693,31 @@ struct let relift t = t - (* representation as C expression + (** representation as C expression This function returns all the equalities that are saved in our datastructure t. Lincons -> linear constraint *) let invariant t = - let get_const acc i (var_opt, const) = if Some i = var_opt then acc - else ( + let of_coeff xi coeffs o = + let typ = (Option.get @@ V.to_cil_varinfo xi).vtype in + let ikind = Cilfacade.get_ikind typ in + let cst = Coeff.s_of_mpqf @@ Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z @@ IntDomain.Size.cast ikind o) in + let lincons = Lincons1.make (Linexpr1.make t.env) Lincons1.EQ in + Lincons1.set_list lincons coeffs (Some cst); + lincons + in + let get_const acc i = function + | (None, o) -> + let xi = Environment.var_of_dim t.env i in + of_coeff xi [(Coeff.s_of_int (-1), xi)] o :: acc + | (Some r, _) when r = i -> acc + | (Some r, o) -> let xi = Environment.var_of_dim t.env i in - let coeff_vars = (Coeff.s_of_int (-1), xi) :: (match var_opt with - | Some var_index when i <> var_index -> - let var = Environment.var_of_dim t.env var_index in - [(Coeff.s_of_int 1, var)] - | _ -> [] ) - in - let typ = (Option.get @@ V.to_cil_varinfo xi).vtype in - let ikind = Cilfacade.get_ikind typ in (*Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z i))*) - let cst = Coeff.s_of_mpqf @@ Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z @@IntDomain.Size.cast ikind const) in - let lincons = Lincons1.make (Linexpr1.make t.env) Lincons1.EQ in - Lincons1.set_list lincons coeff_vars (Some cst); - lincons :: acc) + let ri = Environment.var_of_dim t.env r in + of_coeff xi [(Coeff.s_of_int (-1), xi); (Coeff.s_of_int 1, ri)] o :: acc in - match t.d with - | None -> [] - | Some d -> Array.fold_lefti get_const [] d + BatOption.map_default (Array.fold_lefti get_const []) [] t.d let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1 From e352e2a8ae9204212c629571953bdbb57f09c66c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 8 Feb 2024 21:28:07 +0100 Subject: [PATCH 192/280] Doc comments --- .../apron/linearTwoVarEqualityDomain.apron.ml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 696fba7cd0..4c8812d6e5 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -288,7 +288,7 @@ struct let is_bot t = equal t (bot ()) let is_bot_env t = t.d = None - (*this shows "top" for a specific environment to enable the calculations. It is the top_of of all equalities*) + (* this shows "top" for a specific environment to enable the calculations. It is the top_of of all equalities *) let top_of env = {d = Some (EArray.make_empty_array (Environment.size env)); env = env} (** Is not expected to be called but implemented for completeness *) @@ -472,7 +472,7 @@ struct let forget_vars t vars = timing_wrap "forget_vars" (forget_vars t) vars - (* implemented as described on page 10 in the paper about Fast Interprocedural Linear Two-Variable Equalities in the Section "Abstract Effect of Statements" + (** implemented as described on page 10 in the paper about Fast Interprocedural Linear Two-Variable Equalities in the Section "Abstract Effect of Statements" This makes a copy of the data structure, it doesn't change it in-place. *) let assign_texpr (t: VarManagement.t) var texp = let assigned_var = Environment.dim_of_var t.env var (* this is the variable we are assigning to *) in @@ -592,15 +592,14 @@ struct The overflow is completely handled by the flag "no_ov", which is set in relationAnalysis.ml via the function no_overflow. In case of a potential overflow, "no_ov" is set to false - and Convert.tcons1_of_cil_exp will raise the exception Unsupported_CilExp Overflow *) + and Convert.tcons1_of_cil_exp will raise the exception Unsupported_CilExp Overflow - (* meet_tcons -> meet with guard in if statement + meet_tcons -> meet with guard in if statement texpr -> tree expr (right hand side of equality) -> expression used to derive tcons -> used to check for overflow tcons -> tree constraint (expression < 0) -> does not have types (overflow is type dependent) *) - let meet_tcons ask t tcons original_expr no_ov = (* The expression is evaluated using an array of coefficients. The first element of the array belongs to the constant followed by the coefficients of all variables depending on the result in the array after the evaluating including resolving the constraints in t.d the tcons can be evaluated and additional constraints can be added to t.d *) @@ -672,7 +671,7 @@ struct if M.tracing then M.tracel "ops" "unify: %s %s -> %s\n" (show a) (show b) (show res); res - (* Assert a constraint expression. Defined in apronDomain.apron.ml + (** Assert a constraint expression. Defined in apronDomain.apron.ml If the constraint is never fulfilled, then return bottom. Else the domain can be modified with the new information given by the constraint. From 1e3a52d955cfe69ab1fd4d1de7c5b72fec1c6452 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 8 Feb 2024 21:44:57 +0100 Subject: [PATCH 193/280] Make code a bit more idiomatic --- .../apron/linearTwoVarEqualityDomain.apron.ml | 100 ++++++++++-------- 1 file changed, 55 insertions(+), 45 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 4c8812d6e5..1f22a0addb 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -344,21 +344,26 @@ struct let meet_with_one_conj t i e = match t.d with | None -> t - | Some d -> let res_d = Array.copy d in - match meet_with_one_conj_with res_d i e with - | exception Contradiction -> {d = None; env = t.env} - | () -> {d = Some res_d; env = t.env} + | Some d -> + let res_d = Array.copy d in + try + meet_with_one_conj_with res_d i e; + {d = Some res_d; env = t.env} + with Contradiction -> + {d = None; env = t.env} let meet t1 t2 = let sup_env = Environment.lce t1.env t2.env in - let t1, t2 = change_d t1 sup_env ~add:true ~del:false, change_d t2 sup_env ~add:true ~del:false - in + let t1 = change_d t1 sup_env ~add:true ~del:false in + let t2 = change_d t2 sup_env ~add:true ~del:false in match t1.d, t2.d with | Some d1', Some d2' -> ( + try let res_d = Array.copy d1' in - match Array.iteri (meet_with_one_conj_with res_d) d2' with - | exception Contradiction -> {d = None; env = sup_env} - | () -> {d = Some res_d; env = sup_env} ) + Array.iteri (meet_with_one_conj_with res_d) d2'; + {d = Some res_d; env = sup_env} + with Contradiction -> + {d = None; env = sup_env}) | _ -> {d = None; env = sup_env} let meet t1 t2 = @@ -416,11 +421,15 @@ struct List.iter iterate new_components; Some ad in (*Normalize the two domains a and b such that both talk about the same variables*) - if is_bot_env a then b else if is_bot_env b then a + if is_bot_env a then + b + else if is_bot_env b then + a else match Option.get a.d, Option.get b.d with - | x, y when is_top a || is_top b -> let new_env = Environment.lce a.env b.env - in (top_of new_env) + | x, y when is_top a || is_top b -> + let new_env = Environment.lce a.env b.env in + top_of new_env | x, y when (Environment.compare a.env b.env <> 0) -> let sup_env = Environment.lce a.env b.env in let mod_x = dim_add (Environment.dimchange a.env sup_env) x in @@ -445,6 +454,7 @@ struct res let narrow a b = meet a b + let narrow a b = let res = narrow a b in if M.tracing then M.tracel "narrow" "narrow a: %s b: %s -> %s \n" (show a) (show b) (show res) ; @@ -454,11 +464,10 @@ struct dprintf "%s: %a not leq %a" (name ()) pretty x pretty y let forget_vars t vars = - if is_bot_env t || is_top t then t + if is_bot_env t || is_top t || List.is_empty vars then + t else - if List.is_empty vars then t else - let m = EArray.copy @@ Option.get t.d - in + let m = EArray.copy @@ Option.get t.d in List.iter (fun var -> EArray.forget_variable_with m (Environment.dim_of_var t.env var)) @@ -476,23 +485,23 @@ struct This makes a copy of the data structure, it doesn't change it in-place. *) let assign_texpr (t: VarManagement.t) var texp = let assigned_var = Environment.dim_of_var t.env var (* this is the variable we are assigning to *) in - begin match t.d with - | Some d -> - let abstract_exists_var = abstract_exists var t in - begin match get_coeff t texp with - | None -> (* Statement "assigned_var = ?" (non-linear assignment) *) - abstract_exists_var - | Some (None, off) -> - (* Statement "assigned_var = off" (constant assignment) *) - assign_const abstract_exists_var assigned_var off - | Some (Some exp_var, off) when assigned_var = exp_var -> - (* Statement "assigned_var = assigned_var + off" *) - subtract_const_from_var t assigned_var off - | Some (Some exp_var, off) -> - (* Statement "assigned_var = exp_var + off" (assigned_var is not the same as exp_var) *) - meet_with_one_conj abstract_exists_var assigned_var (Some exp_var, off) - end - | None -> bot_env end + match t.d with + | Some d -> + let abstract_exists_var = abstract_exists var t in + begin match get_coeff t texp with + | None -> (* Statement "assigned_var = ?" (non-linear assignment) *) + abstract_exists_var + | Some (None, off) -> + (* Statement "assigned_var = off" (constant assignment) *) + assign_const abstract_exists_var assigned_var off + | Some (Some exp_var, off) when assigned_var = exp_var -> + (* Statement "assigned_var = assigned_var + off" *) + subtract_const_from_var t assigned_var off + | Some (Some exp_var, off) -> + (* Statement "assigned_var = exp_var + off" (assigned_var is not the same as exp_var) *) + meet_with_one_conj abstract_exists_var assigned_var (Some exp_var, off) + end + | None -> bot_env let assign_texpr t var texp = timing_wrap "assign_texpr" (assign_texpr t var) texp @@ -520,14 +529,13 @@ struct let res = assign_var t v v' in if M.tracing then M.tracel "ops" "assign_var t:\n %s \n v: %s \n v': %s\n -> %s\n" (show t) (Var.to_string v) (Var.to_string v') (show res) ; res - (* This functionality is not common to C and is used for assignments of the form: x = y, y=x; which is not legitimate C grammar - x and y should be assigned to the value of x and y before the assignment respectively. - ==> x = y_old , y = x_old; - Therefore first apply the assignments to temporary variables x' and y' to keep the old dependencies of x and y + + (** Parallel assignment of variables. + First apply the assignments to temporary variables x' and y' to keep the old dependencies of x and y and in a second round assign x' to x and y' to y *) let assign_var_parallel t vv's = - let assigned_vars = List.map (function (v, _) -> v) vv's in + let assigned_vars = List.map fst vv's in let t = add_vars t assigned_vars in let primed_vars = List.init (List.length assigned_vars) (fun i -> Var.of_string (Int.to_string i ^"'")) in (* TODO: we use primed vars in analysis, conflict? *) let t_primed = add_vars t primed_vars in @@ -571,21 +579,23 @@ struct forget_vars res [var] let substitute_exp ask t var exp no_ov = - let res = substitute_exp ask t var exp no_ov - in if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %s \n exp: %a \n -> \n %s\n" (show t) (Var.to_string var) d_exp exp (show res); + let res = substitute_exp ask t var exp no_ov in + if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %s \n exp: %a \n -> \n %s\n" (show t) (Var.to_string var) d_exp exp (show res); res let substitute_exp ask t var exp no_ov = timing_wrap "substitution" (substitute_exp ask t var exp) no_ov let show_coeff_vec l (env : Environment.t) = - let show_element e = match e with - | (a, Some x) -> ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env x)) ^ " + ") - | (a, None) -> ((Z.to_string a) ^ "+") in + let show_element = function + | (a, Some x) -> ((Z.to_string a) ^ " * " ^ (Var.to_string (Environment.var_of_dim env x)) ^ " + ") + | (a, None) -> ((Z.to_string a) ^ "+") + in List.fold_right (fun k result -> show_element k ^ "\n" ^ result) l "" let show_final_expr l (env : Environment.t) = let show_element i a = if i = 0 then ((Z.to_string a) ^ " + ") else - ((Z.to_string a) ^ " * " ^ (Var.to_string ( Environment.var_of_dim env (i-1))) ^ " + ") in + ((Z.to_string a) ^ " * " ^ (Var.to_string (Environment.var_of_dim env (i-1))) ^ " + ") + in List.fold_righti (fun i k result -> show_element i k ^ "\n" ^ result) l "" (** Assert a constraint expression. @@ -622,7 +632,7 @@ struct | (None, c_i) -> constant := Z.(!constant + (c * c_i)) in List.iter update cv's; - let var_count = GobArray.count_matchingi (fun _ a -> not @@ Z.equal a Z.zero) expr in + let var_count = Array.count_matching (fun a -> not @@ Z.equal a Z.zero) expr in if var_count = 0 then match Tcons1.get_typ tcons with | EQ when Z.equal !constant Z.zero -> t From c3f099e8eedc67e103a79477d8b2cc60cda3db5c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 8 Feb 2024 21:48:34 +0100 Subject: [PATCH 194/280] indent --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 1f22a0addb..d33490583c 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -359,11 +359,12 @@ struct match t1.d, t2.d with | Some d1', Some d2' -> ( try - let res_d = Array.copy d1' in - Array.iteri (meet_with_one_conj_with res_d) d2'; - {d = Some res_d; env = sup_env} + let res_d = Array.copy d1' in + Array.iteri (meet_with_one_conj_with res_d) d2'; + {d = Some res_d; env = sup_env} with Contradiction -> - {d = None; env = sup_env}) + {d = None; env = sup_env} + ) | _ -> {d = None; env = sup_env} let meet t1 t2 = From 14a24b882dc1c3831b632985489d0e6367e95a4a Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 8 Feb 2024 21:53:05 +0100 Subject: [PATCH 195/280] Simplify --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index d33490583c..f8c34d8584 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -324,9 +324,12 @@ struct let meet_with_one_conj_with ts i (var, b) = let subst_var ts x (vart, bt) = - let adjust (vare, b') = - if Option.eq ~eq:Int.equal (Some x) vare then (vart, Z.(b' + bt)) else (vare, b') in - BatArray.modify adjust ts in + let adjust = function + | (Some vare, b') when vare = x -> (vart, Z.(b' + bt)) + | e -> e + in + BatArray.modify adjust ts + in let (var1, b1) = ts.(i) in (match var, var1 with | None, None -> if not @@ Z.equal b b1 then raise Contradiction From cc36e35c567327b91ee818ab15f5209ec6a9ff0b Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 8 Feb 2024 22:00:54 +0100 Subject: [PATCH 196/280] Replace unused arguments with `_` in `no_ov_overflow_handling` --- src/cdomains/apron/sharedFunctions.apron.ml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index b829b4526d..a058a8cafb 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -95,15 +95,18 @@ struct ) (** This version without an overflow check is used by the affeq and lin2vareq domains. - They do the overflow handling before converting to Texpr1, and store the result in the "no_ov" flag. Therefore we only check the no_ov flag here. *) - let no_ov_overflow_handling no_ov ik env expr d exp = begin try + They do the overflow handling before converting to Texpr1, and store the result in the "no_ov" flag. + Therefore we only check the no_ov flag here. *) + let no_ov_overflow_handling no_ov _ _ _ _ exp = + try if IntDomain.should_wrap (Cilfacade.get_ikind_exp exp) || (not @@ IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp exp) && not (Lazy.force no_ov)) then (raise (Unsupported_CilExp Overflow)) - with Invalid_argument e -> raise ((Unsupported_CilExp Exp_not_supported))(* This exception is raised by Cilfacade.get_ikind_exp - when the expression is not an integer expression, for example if it is a float expression. *) - end + with Invalid_argument e -> + (* This exception is raised by Cilfacade.get_ikind_exp) when the expression + is not an integer expression, for example if it is a float expression. *) + raise (Unsupported_CilExp Exp_not_supported) let texpr1_expr_of_cil_exp_old d env exp no_ov = (* recurse without env argument *) From 094f886742e7fd9d0030ddeec6fbae82f36f9c5c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 8 Feb 2024 22:06:39 +0100 Subject: [PATCH 197/280] Some simplifications --- src/cdomains/apron/sharedFunctions.apron.ml | 28 ++++++++++----------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index a058a8cafb..6f65794944 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -110,8 +110,7 @@ struct let texpr1_expr_of_cil_exp_old d env exp no_ov = (* recurse without env argument *) - let rec texpr1_expr_of_cil_exp exp = - match exp with + let rec texpr1_expr_of_cil_exp = function | Lval (Var v, NoOffset) when Tracked.varinfo_tracked v -> if not v.vglob || Arg.allow_global then let var = @@ -146,7 +145,7 @@ struct | BinOp (Mod, e1, e2, _) -> Binop (Mod, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) | CastE (TInt (t_ik, _) as t, e) -> - begin match IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) + begin match IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) | true -> texpr1_expr_of_cil_exp e | false | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) @@ -179,9 +178,8 @@ struct (* || (Option.is_some min && Option.is_some max && (minimal != maximal && (Option.get min >= minimal || Option.get max <= maximal))) in*) if top then IntDomain.IntDomTuple.top_of ik else res in - (* recurse without env argument *) - let rec texpr1_expr_of_cil_exp ask exp = - match exp with + (* recurse without env and ask arguments *) + let rec texpr1_expr_of_cil_exp = function | Lval (Var v, NoOffset) when Tracked.varinfo_tracked v -> if not v.vglob || Arg.allow_global then let var = @@ -212,21 +210,21 @@ struct in match exp with | UnOp (Neg, e, _) -> - Unop (Neg, texpr1_expr_of_cil_exp ask @@ simplify e, Int, Near) + Unop (Neg, texpr1_expr_of_cil_exp @@ simplify e, Int, Near) | BinOp (PlusA, e1, e2, _) -> - Binop (Add, texpr1_expr_of_cil_exp ask @@ simplify e1, texpr1_expr_of_cil_exp ask @@ simplify e2, Int, Near) + Binop (Add, texpr1_expr_of_cil_exp @@ simplify e1, texpr1_expr_of_cil_exp @@ simplify e2, Int, Near) | BinOp (MinusA, e1, e2, _) -> - Binop (Sub, texpr1_expr_of_cil_exp ask @@ simplify e1, texpr1_expr_of_cil_exp ask @@ simplify e2, Int, Near) + Binop (Sub, texpr1_expr_of_cil_exp @@ simplify e1, texpr1_expr_of_cil_exp @@ simplify e2, Int, Near) | BinOp (Mult, e1, e2, _) -> - Binop (Mul, texpr1_expr_of_cil_exp ask @@ simplify e1, texpr1_expr_of_cil_exp ask @@ simplify e2, Int, Near) + Binop (Mul, texpr1_expr_of_cil_exp @@ simplify e1, texpr1_expr_of_cil_exp @@ simplify e2, Int, Near) | BinOp (Div, e1, e2, _) -> - Binop (Div, texpr1_expr_of_cil_exp ask @@ simplify e1, texpr1_expr_of_cil_exp ask @@ simplify e2, Int, Zero) + Binop (Div, texpr1_expr_of_cil_exp @@ simplify e1, texpr1_expr_of_cil_exp @@ simplify e2, Int, Zero) | BinOp (Mod, e1, e2, _) -> - Binop (Mod, texpr1_expr_of_cil_exp ask @@ simplify e1, texpr1_expr_of_cil_exp ask @@ simplify e2, Int, Near) + Binop (Mod, texpr1_expr_of_cil_exp @@ simplify e1, texpr1_expr_of_cil_exp @@ simplify e2, Int, Near) | CastE (TInt (t_ik, _) as t, e) -> begin match IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) | exception _ -> raise (Unsupported_CilExp (Cast_not_injective t)) - | true -> texpr1_expr_of_cil_exp ask @@ simplify e + | true -> texpr1_expr_of_cil_exp @@ simplify e | false -> let res = try (query e @@ Cilfacade.get_ikind_exp e) with Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) in let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to t_ik res in @@ -236,7 +234,7 @@ struct else ( let (minimal, maximal) = IntDomain.Size.range t_ik in match IntDomain.IntDomTuple.minimal res, IntDomain.IntDomTuple.maximal res with - | Some min, Some max when min >= minimal && max <= maximal -> texpr1_expr_of_cil_exp ask e + | Some min, Some max when min >= minimal && max <= maximal -> texpr1_expr_of_cil_exp e | _ -> raise (Unsupported_CilExp (Cast_not_injective t))) | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) | exception Invalid_argument _ -> @@ -252,7 +250,7 @@ struct raise (Unsupported_CilExp (Exp_typeOf e)) in (* only if we are sure that no overflow / undefined behavior happens we convert the expression *) - texpr1_expr_of_cil_exp ask exp + texpr1_expr_of_cil_exp exp let texpr1_expr_of_cil_exp ask d env exp no_ov = let exp = Cil.constFold false exp in From 7d4676aee810ae3962114776ae53b95c24613342 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 8 Feb 2024 22:09:46 +0100 Subject: [PATCH 198/280] Simp --- src/cdomains/apron/sharedFunctions.apron.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 6f65794944..a936f6312f 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -254,12 +254,12 @@ struct let texpr1_expr_of_cil_exp ask d env exp no_ov = let exp = Cil.constFold false exp in - if Arg.do_overflow_check then texpr1_expr_of_cil_exp_with_overflow_check ask d env exp no_ov overflow_handling_apron - else texpr1_expr_of_cil_exp_with_overflow_check ask d env exp no_ov no_ov_overflow_handling + let ov_handler = if Arg.do_overflow_check then overflow_handling_apron else no_ov_overflow_handling in + texpr1_expr_of_cil_exp_with_overflow_check ask d env exp no_ov ov_handler let texpr1_of_cil_exp ask d env e no_ov = let e = Cil.constFold false e in - let res = texpr1_expr_of_cil_exp ask d env e no_ov in + let res = texpr1_expr_of_cil_exp ask d env e no_ov in Texpr1.of_expr env res let tcons1_of_cil_exp_old d env e negate no_ov = From 643bb791ab93d8b57ae6221340200f51d4643b3d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 8 Feb 2024 22:39:28 +0100 Subject: [PATCH 199/280] Simplify --- src/cdomains/apron/sharedFunctions.apron.ml | 31 +++++++++------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index a936f6312f..4ca4e13360 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -204,27 +204,22 @@ struct let ikind = try (Cilfacade.get_ikind_exp e) with Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) in let simp = query e ikind in let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to ikind simp in - match const with - | Some c -> Const (CInt (c, ikind, None)) - | None -> e + BatOption.map_default (fun c -> Const (CInt (c, ikind, None))) e const in + let texpr1 e = texpr1_expr_of_cil_exp (simplify e) in + let bop_near op e1 e2 = Binop (op, texpr1 e1, texpr1 e2, Int, Near) in match exp with - | UnOp (Neg, e, _) -> - Unop (Neg, texpr1_expr_of_cil_exp @@ simplify e, Int, Near) - | BinOp (PlusA, e1, e2, _) -> - Binop (Add, texpr1_expr_of_cil_exp @@ simplify e1, texpr1_expr_of_cil_exp @@ simplify e2, Int, Near) - | BinOp (MinusA, e1, e2, _) -> - Binop (Sub, texpr1_expr_of_cil_exp @@ simplify e1, texpr1_expr_of_cil_exp @@ simplify e2, Int, Near) - | BinOp (Mult, e1, e2, _) -> - Binop (Mul, texpr1_expr_of_cil_exp @@ simplify e1, texpr1_expr_of_cil_exp @@ simplify e2, Int, Near) - | BinOp (Div, e1, e2, _) -> - Binop (Div, texpr1_expr_of_cil_exp @@ simplify e1, texpr1_expr_of_cil_exp @@ simplify e2, Int, Zero) - | BinOp (Mod, e1, e2, _) -> - Binop (Mod, texpr1_expr_of_cil_exp @@ simplify e1, texpr1_expr_of_cil_exp @@ simplify e2, Int, Near) + | UnOp (Neg, e, _) -> Unop (Neg, texpr1 e, Int, Near) + | BinOp (PlusA, e1, e2, _) -> bop_near Add e1 e2 + | BinOp (MinusA, e1, e2, _) -> bop_near Sub e1 e2 + | BinOp (Mult, e1, e2, _) -> bop_near Mul e1 e2 + | BinOp (Mod, e1, e2, _) -> bop_near Mod e1 e2 + | BinOp (Div, e1, e2, _) -> + Binop (Div, texpr1 e1, texpr1 e2, Int, Zero) | CastE (TInt (t_ik, _) as t, e) -> begin match IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) | exception _ -> raise (Unsupported_CilExp (Cast_not_injective t)) - | true -> texpr1_expr_of_cil_exp @@ simplify e + | true -> texpr1 e | false -> let res = try (query e @@ Cilfacade.get_ikind_exp e) with Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) in let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to t_ik res in @@ -232,9 +227,9 @@ struct | Some c -> Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z c))) | None -> if IntDomain.IntDomTuple.is_top_of t_ik res then raise (Unsupported_CilExp (Cast_not_injective t)) else ( - let (minimal, maximal) = IntDomain.Size.range t_ik in + let (ik_min, ik_max) = IntDomain.Size.range t_ik in match IntDomain.IntDomTuple.minimal res, IntDomain.IntDomTuple.maximal res with - | Some min, Some max when min >= minimal && max <= maximal -> texpr1_expr_of_cil_exp e + | Some min, Some max when min >= ik_min && max <= ik_max -> texpr1_expr_of_cil_exp e | _ -> raise (Unsupported_CilExp (Cast_not_injective t))) | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) | exception Invalid_argument _ -> From faf75a96d01e1f38d7bbbdee8755d0959ba4e916 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 8 Feb 2024 22:49:09 +0100 Subject: [PATCH 200/280] Simplify --- src/cdomains/apron/sharedFunctions.apron.ml | 148 +++++++++----------- 1 file changed, 70 insertions(+), 78 deletions(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 4ca4e13360..2f459747fc 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -163,94 +163,86 @@ struct texpr1_expr_of_cil_exp exp - let texpr1_expr_of_cil_exp_with_overflow_check (ask: Queries.ask) d env exp no_ov overflow_handling = - let query e ik = - let res = match ask.f (EvalInt e) with + let texpr1_expr_of_cil_exp (ask:Queries.ask) d env exp no_ov = + let conv exp overflow_handling = + let query e ik = + match ask.f (EvalInt e) with | `Bot -> raise (Unsupported_CilExp Exp_not_supported) (* This should never happen according to Michael Schwarz *) | `Top -> IntDomain.IntDomTuple.top_of ik | `Lifted x -> x (* Cast should be unnecessary because it should be taken care of by EvalInt. *) in - (* If the returned interval is top of the expected ikind (i.e. the value is unknown ) or the returned interval is in range of the expected interval, return top - - If top is returned the expression will be rewritten. - - If a constant is returned this specific value is casted to the expected ikind value - - else we got an interval with unsupported bounds i.e. the value expression is known to be unknown and needs casting, which we do not support i.e. the expression is not supported*) - let top = IntDomain.IntDomTuple.is_top_of ik res in - (* || (Option.is_some min && Option.is_some max && (minimal != maximal && (Option.get min >= minimal || Option.get max <= maximal))) in*) - if top then IntDomain.IntDomTuple.top_of ik else res - in - (* recurse without env and ask arguments *) - let rec texpr1_expr_of_cil_exp = function - | Lval (Var v, NoOffset) when Tracked.varinfo_tracked v -> - if not v.vglob || Arg.allow_global then - let var = - if v.vglob then - V.global v + (* recurse without env and ask arguments *) + let rec texpr1_expr_of_cil_exp = function + | Lval (Var v, NoOffset) when Tracked.varinfo_tracked v -> + if not v.vglob || Arg.allow_global then + let var = + if v.vglob then + V.global v + else + V.local v + in + if Environment.mem_var env var then + Var var else - V.local v - in - if Environment.mem_var env var then - Var var + raise (Unsupported_CilExp (Var_not_found v)) else - raise (Unsupported_CilExp (Var_not_found v)) - else - failwith "texpr1_expr_of_cil_exp: globals must be replaced with temporary locals" - | Const (CInt (i, _, _)) -> - Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z i))) - | exp -> - match Cilfacade.get_ikind_exp exp with - | ik -> - let expr = - let simplify e = - let ikind = try (Cilfacade.get_ikind_exp e) with Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) in - let simp = query e ikind in - let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to ikind simp in - BatOption.map_default (fun c -> Const (CInt (c, ikind, None))) e const + failwith "texpr1_expr_of_cil_exp: globals must be replaced with temporary locals" + | Const (CInt (i, _, _)) -> + Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z i))) + | exp -> + match Cilfacade.get_ikind_exp exp with + | ik -> + let expr = + let simplify e = + let ikind = try (Cilfacade.get_ikind_exp e) with Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) in + let simp = query e ikind in + let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to ikind simp in + BatOption.map_default (fun c -> Const (CInt (c, ikind, None))) e const + in + let texpr1 e = texpr1_expr_of_cil_exp (simplify e) in + let bop_near op e1 e2 = Binop (op, texpr1 e1, texpr1 e2, Int, Near) in + match exp with + | UnOp (Neg, e, _) -> Unop (Neg, texpr1 e, Int, Near) + | BinOp (PlusA, e1, e2, _) -> bop_near Add e1 e2 + | BinOp (MinusA, e1, e2, _) -> bop_near Sub e1 e2 + | BinOp (Mult, e1, e2, _) -> bop_near Mul e1 e2 + | BinOp (Mod, e1, e2, _) -> bop_near Mod e1 e2 + | BinOp (Div, e1, e2, _) -> + Binop (Div, texpr1 e1, texpr1 e2, Int, Zero) + | CastE (TInt (t_ik, _) as t, e) -> + begin match IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) + | exception _ -> raise (Unsupported_CilExp (Cast_not_injective t)) + | true -> texpr1 e + | false -> + let res = try (query e @@ Cilfacade.get_ikind_exp e) with Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) in + let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to t_ik res in + match const with + | Some c -> Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z c))) + | None -> if IntDomain.IntDomTuple.is_top_of t_ik res then raise (Unsupported_CilExp (Cast_not_injective t)) + else ( + let (ik_min, ik_max) = IntDomain.Size.range t_ik in + match IntDomain.IntDomTuple.minimal res, IntDomain.IntDomTuple.maximal res with + | Some min, Some max when min >= ik_min && max <= ik_max -> texpr1_expr_of_cil_exp e + | _ -> raise (Unsupported_CilExp (Cast_not_injective t))) + | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) + | exception Invalid_argument _ -> + raise (Unsupported_CilExp (Cast_not_injective t)) + end + | _ -> + raise (Unsupported_CilExp Exp_not_supported) in - let texpr1 e = texpr1_expr_of_cil_exp (simplify e) in - let bop_near op e1 e2 = Binop (op, texpr1 e1, texpr1 e2, Int, Near) in - match exp with - | UnOp (Neg, e, _) -> Unop (Neg, texpr1 e, Int, Near) - | BinOp (PlusA, e1, e2, _) -> bop_near Add e1 e2 - | BinOp (MinusA, e1, e2, _) -> bop_near Sub e1 e2 - | BinOp (Mult, e1, e2, _) -> bop_near Mul e1 e2 - | BinOp (Mod, e1, e2, _) -> bop_near Mod e1 e2 - | BinOp (Div, e1, e2, _) -> - Binop (Div, texpr1 e1, texpr1 e2, Int, Zero) - | CastE (TInt (t_ik, _) as t, e) -> - begin match IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) - | exception _ -> raise (Unsupported_CilExp (Cast_not_injective t)) - | true -> texpr1 e - | false -> - let res = try (query e @@ Cilfacade.get_ikind_exp e) with Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) in - let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to t_ik res in - match const with - | Some c -> Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z c))) - | None -> if IntDomain.IntDomTuple.is_top_of t_ik res then raise (Unsupported_CilExp (Cast_not_injective t)) - else ( - let (ik_min, ik_max) = IntDomain.Size.range t_ik in - match IntDomain.IntDomTuple.minimal res, IntDomain.IntDomTuple.maximal res with - | Some min, Some max when min >= ik_min && max <= ik_max -> texpr1_expr_of_cil_exp e - | _ -> raise (Unsupported_CilExp (Cast_not_injective t))) - | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) - | exception Invalid_argument _ -> - raise (Unsupported_CilExp (Cast_not_injective t)) - end - | _ -> - raise (Unsupported_CilExp Exp_not_supported) - in - overflow_handling no_ov ik env expr d exp; - expr - | exception (Cilfacade.TypeOfError _ as e) - | exception (Invalid_argument _ as e) -> - raise (Unsupported_CilExp (Exp_typeOf e)) + overflow_handling no_ov ik env expr d exp; + expr + | exception (Cilfacade.TypeOfError _ as e) + | exception (Invalid_argument _ as e) -> + raise (Unsupported_CilExp (Exp_typeOf e)) + in + (* only if we are sure that no overflow / undefined behavior happens we convert the expression *) + texpr1_expr_of_cil_exp exp in - (* only if we are sure that no overflow / undefined behavior happens we convert the expression *) - texpr1_expr_of_cil_exp exp - - let texpr1_expr_of_cil_exp ask d env exp no_ov = let exp = Cil.constFold false exp in let ov_handler = if Arg.do_overflow_check then overflow_handling_apron else no_ov_overflow_handling in - texpr1_expr_of_cil_exp_with_overflow_check ask d env exp no_ov ov_handler + conv exp ov_handler let texpr1_of_cil_exp ask d env e no_ov = let e = Cil.constFold false e in From ed81c0d4cedc314705581072268ed1611b3cdb3f Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Fri, 9 Feb 2024 08:02:41 +0100 Subject: [PATCH 201/280] cleaned up shift and indexlist creation in add_variables_to_domain --- .../apron/linearTwoVarEqualityDomain.apron.ml | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index f8c34d8584..b305f41815 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -38,26 +38,28 @@ module EqualitiesArray = struct let make_empty_array len = Array.init len (fun i -> (Some i, Z.zero)) - let add_variables_to_domain m indexes = (* add new variables to domain with particular indices; translates old indices to keep consistency *) - (* the semantic of indexes can be retrieved from apron: https://antoinemine.github.io/Apron/doc/api/ocaml/Dim.html *) + (** add new variables to domain with particular indices; translates old indices to keep consistency + the semantics of indexes can be retrieved from apron: https://antoinemine.github.io/Apron/doc/api/ocaml/Dim.html *) + let add_variables_to_domain m indexes = if length indexes = 0 then m else - let next_offset_bump_list = (* an ascending list of indices, where the offset is bumped by 1 *) - Array.to_list indexes @ [ Array.length m ] (* terminate list with m to avoid out of bounds access *) - in let offset_map = Array.make (Array.length m) 0 (* maps each variable to the number of variables that are added before this variable *) in - let rec shift (offset, list) index = (* bumps offset & pops list, if/while index is heading the list *) - if index = List.hd list then - shift (offset+1, List.tl list) index - else (offset, list) - in let _ = - Array.fold_lefti (* iterates over all indices of offset_map, overwrites content with current offset wrt. potential shift *) - (fun (offset, offset_bump_list) index _ -> - let newoffset, newlist = shift (offset, offset_bump_list) index in + let rec shift (offset, list) index = (* bumps offset & pops list, if/while index is heading the list *) + match list with + | hd::tl when hd = index -> shift (offset+1, tl) index + | _ -> (offset, list) + in + Array.fold_lefti (* this is not a textbook fold. We rather use it as a means to iterate over the range + of all indices of offset_map, initializing the array at these indices as a side-effect. + We use fold here as a means of having an accumulator to keep track of the current offset + and the rest of the offset list. In case of more frequent use of this pattern, consider this as + a candidate template for a new library function *) + (fun offsetcontext index _ -> + let newoffset, newlist = shift offsetcontext index in offset_map.(index) <- newoffset; (newoffset, newlist)) - (0, next_offset_bump_list) offset_map + (0, Array.to_list indexes) offset_map in let add_offset_to_array_entry (var, offs) = (* uses offset_map to obtain a new var_index, that is consistent with the new reference indices *) Option.map (fun var_index -> var_index + offset_map.(var_index)) var, offs in From 2515fa02003dedd1bc028fb11e50f293d6a1fb1e Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 9 Feb 2024 11:19:48 +0100 Subject: [PATCH 202/280] satisfy ocp-indent --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index b305f41815..34b1c8afde 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -51,10 +51,10 @@ module EqualitiesArray = struct | _ -> (offset, list) in Array.fold_lefti (* this is not a textbook fold. We rather use it as a means to iterate over the range - of all indices of offset_map, initializing the array at these indices as a side-effect. - We use fold here as a means of having an accumulator to keep track of the current offset - and the rest of the offset list. In case of more frequent use of this pattern, consider this as - a candidate template for a new library function *) + of all indices of offset_map, initializing the array at these indices as a side-effect. + We use fold here as a means of having an accumulator to keep track of the current offset + and the rest of the offset list. In case of more frequent use of this pattern, consider this as + a candidate template for a new library function *) (fun offsetcontext index _ -> let newoffset, newlist = shift offsetcontext index in offset_map.(index) <- newoffset; From 5ecacbefa8ef7da2ab091490bb5742374928a3ef Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 9 Feb 2024 15:05:09 +0100 Subject: [PATCH 203/280] Add failing test --- tests/regression/46-apron2/61-fixpoint.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/regression/46-apron2/61-fixpoint.c diff --git a/tests/regression/46-apron2/61-fixpoint.c b/tests/regression/46-apron2/61-fixpoint.c new file mode 100644 index 0000000000..9fcb7715e3 --- /dev/null +++ b/tests/regression/46-apron2/61-fixpoint.c @@ -0,0 +1,17 @@ +// SKIP PARAM: --set ana.activated[+] apron --enable ana.int.interval --set ana.base.arrays.domain partitioned + +int main () +{ + char A [3]; + + A[2] = 0; + + char *str = A; + int i = 0; + + while (str[i] != 0) { + i++; + } + + return 0; +} From 7b88d2114e2567bf2a1cc5dfd98b5392da7336bb Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 9 Feb 2024 15:46:47 +0100 Subject: [PATCH 204/280] ocp-indent again --- .../apron/linearTwoVarEqualityDomain.apron.ml | 106 +++++++++--------- 1 file changed, 52 insertions(+), 54 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 34b1c8afde..cef1c035b2 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -620,62 +620,60 @@ struct (* The expression is evaluated using an array of coefficients. The first element of the array belongs to the constant followed by the coefficients of all variables depending on the result in the array after the evaluating including resolving the constraints in t.d the tcons can be evaluated and additional constraints can be added to t.d *) match t.d with - | None -> bot_env + | None -> bot_env (* same as is_bot_env t *) | Some d -> - let expr = Array.init (Environment.size t.env) (fun _ -> Z.zero) in - let constant = ref (Z.zero) in - if is_bot_env t then bot_env - else - match get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with - | None -> t (*The (in-) equality is not linear, therefore we don't know anything about it. *) - | Some cv's -> + match get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with + | None -> t (*The (in-) equality is not linear, therefore we don't know anything about it. *) + | Some cv's -> + let expr = Array.make (Environment.size t.env) Z.zero in + let refconstant = ref (Z.zero) in let update (c, v) = - match v with - | None -> constant := Z.(!constant + c) - | Some idx -> match d.(idx) with - | (Some idx_i, c_i) -> constant := Z.(!constant + (c * c_i)); - expr.(idx_i) <- Z.(expr.(idx_i) + c) - | (None, c_i) -> constant := Z.(!constant + (c * c_i)) - in - List.iter update cv's; - let var_count = Array.count_matching (fun a -> not @@ Z.equal a Z.zero) expr in - if var_count = 0 then - match Tcons1.get_typ tcons with - | EQ when Z.equal !constant Z.zero -> t - | SUPEQ when Z.geq !constant Z.zero -> t - | SUP when Z.gt !constant Z.zero -> t - | DISEQ when not @@ Z.equal !constant Z.zero -> t - | EQMOD scalar -> t - | _ -> bot_env - else if var_count = 1 then - let index = Array.findi (fun a -> not @@ Z.equal a Z.zero) expr in - let var = (index, expr.(index)) in - let c = if Z.divisible !constant @@ snd var then Some (Z.(-(!constant) / (snd var))) - else None in - match Tcons1.get_typ tcons, c with - | EQ, Some c -> - let res = meet_with_one_conj t (fst var) (None, c) - in res - | _ -> t (*Not supported right now*) - else if var_count = 2 then - let get_vars i a l = if Z.equal a Z.zero then l else (i, a)::l in - let v12 = Array.fold_righti get_vars expr [] in - let a1 = snd (List.hd v12) in - let a2 = snd (List.hd @@ List.tl v12) in - let var1 = fst (List.hd v12) in - let var2 = fst (List.hd @@ List.tl v12) in - match Tcons1.get_typ tcons with - | EQ -> - let res = - if Z.equal a1 Z.one && Z.equal a2 Z.(-one) - then meet_with_one_conj t var2 (Some var1, !constant) - else if Z.equal a1 Z.(-one) && Z.equal a2 Z.one - then meet_with_one_conj t var1 (Some var2, !constant) - else t - in res - | _-> t (*Not supported right now*) - else - t (*For any other case we don't know if the (in-) equality is true or false or even possible therefore we just return t *) + match v with + | None -> refconstant := Z.(!refconstant + c) + | Some idx -> match d.(idx) with + | (Some idx_i, c_i) -> refconstant := Z.(!refconstant + (c * c_i)); + expr.(idx_i) <- Z.(expr.(idx_i) + c) + | (None, c_i) -> refconstant := Z.(!refconstant + (c * c_i)) + in + List.iter update cv's; (* abstract simplification of the guard wrt. reference variables *) + let var_count = Array.count_matching (fun a -> not @@ Z.equal a Z.zero) expr in + let constant = !refconstant in (* containing the reference locally *) + if var_count = 0 then + match Tcons1.get_typ tcons with + | EQ when Z.equal constant Z.zero -> t + | SUPEQ when Z.geq constant Z.zero -> t + | SUP when Z.gt constant Z.zero -> t + | DISEQ when not @@ Z.equal constant Z.zero -> t + | EQMOD scalar -> t + | _ -> bot_env + else if var_count = 1 then + let index = Array.findi (fun a -> not @@ Z.equal a Z.zero) expr in + let varexpr = expr.(index) in + if Z.divisible constant varexpr && Tcons1.get_typ tcons = EQ then + meet_with_one_conj t index (None, (Z.(-(constant) / varexpr))) + else + t (*Not supported right now*) + else if var_count = 2 then + let get_vars i a l = if Z.equal a Z.zero then l else (i, a)::l in + (* WIP: the following line sums up the next 5, but would yields warning + let (var1,a1)::(var2,a2)::_ = Array.fold_righti get_vars expr [] in *) + let v12 = Array.fold_righti get_vars expr [] in + let a1 = snd (List.hd v12) in + let a2 = snd (List.hd @@ List.tl v12) in + let var1 = fst (List.hd v12) in + let var2 = fst (List.hd @@ List.tl v12) in + match Tcons1.get_typ tcons with + | EQ -> + let res = + if Z.equal a1 Z.one && Z.equal a2 Z.(-one) + then meet_with_one_conj t var2 (Some var1, constant) + else if Z.equal a1 Z.(-one) && Z.equal a2 Z.one + then meet_with_one_conj t var1 (Some var2, constant) + else t + in res + | _-> t (*Not supported right now*) + else + t (*For any other case we don't know if the (in-) equality is true or false or even possible therefore we just return t *) let meet_tcons t tcons expr = timing_wrap "meet_tcons" (meet_tcons t tcons) expr From bb48962607011ba9e9c951033fa7e7da52df0f57 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 9 Feb 2024 20:41:12 +0100 Subject: [PATCH 205/280] Simplify --- src/cdomains/apron/affineEqualityDomain.apron.ml | 1 + src/cdomains/apron/apronDomain.apron.ml | 4 ++-- src/cdomains/vectorMatrix.ml | 11 ----------- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 61042c67b5..e6cd3355bc 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -35,6 +35,7 @@ struct let dim_remove ch m ~del = VectorMatrix.timing_wrap "dim remove" (fun del -> dim_remove ch m ~del:del) del end + (** It defines the type t of the affine equality domain (a struct that contains an optional matrix and an apron environment) and provides the functions needed for handling variables (which are defined by RelationDomain.D2) such as add_vars remove_vars. Furthermore, it provides the function get_coeff_vec that parses an apron expression into a vector of coefficients if the apron expression has an affine form. *) module VarManagement (Vec: AbstractVector) (Mx: AbstractMatrix)= diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index 66e05328c7..9a59c6c50a 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -494,7 +494,9 @@ struct include D include AOps (Tracked) (Man) include Tracked + let eval_interval ask = Bounds.bound_texpr + (** Assert a constraint expression. LAnd, LOr, LNot are directly supported by Apron domain in order to @@ -689,8 +691,6 @@ struct else false - - let widen x y = let x_env = A.env x in let y_env = A.env y in diff --git a/src/cdomains/vectorMatrix.ml b/src/cdomains/vectorMatrix.ml index 64d5c5e35d..bfd623652c 100644 --- a/src/cdomains/vectorMatrix.ml +++ b/src/cdomains/vectorMatrix.ml @@ -137,8 +137,6 @@ sig val show: t -> string - val add_empty_column: t -> int -> t - val add_empty_columns: t -> int array -> t val append_row: t -> vec -> t @@ -313,15 +311,6 @@ module ArrayMatrix: AbstractMatrix = let copy m = timing_wrap "copy" (copy) m - let add_empty_column m n = - if is_empty m then m else - let nc = Array.length m.(0) in - if n > nc then failwith "n too large" else - let new_matrix = make_matrix (Array.length m) (Array.length m.(0) + 1) A.zero in - Array.iteri (fun i r -> if n = 0 then Array.blit r 0 new_matrix.(i) 1 (nc - 1) else - Array.blit r 0 new_matrix.(i) 0 n; if n <> nc then Array.blit r n new_matrix.(i) (n + 1) (nc - n)) m; - new_matrix - let add_empty_columns m cols = let nnc = Array.length cols in if is_empty m || nnc = 0 then m else From 1b4ac710360e269d3289d441244f8d1d0fe3d40f Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 9 Feb 2024 20:43:07 +0100 Subject: [PATCH 206/280] Rm unused function --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index cef1c035b2..ceb0e8abd8 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -598,12 +598,6 @@ struct in List.fold_right (fun k result -> show_element k ^ "\n" ^ result) l "" - let show_final_expr l (env : Environment.t) = - let show_element i a = if i = 0 then ((Z.to_string a) ^ " + ") else - ((Z.to_string a) ^ " * " ^ (Var.to_string (Environment.var_of_dim env (i-1))) ^ " + ") - in - List.fold_righti (fun i k result -> show_element i k ^ "\n" ^ result) l "" - (** Assert a constraint expression. The overflow is completely handled by the flag "no_ov", which is set in relationAnalysis.ml via the function no_overflow. From 506ab22e70540049e530018d53c1dfdc9634bd81 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 9 Feb 2024 20:47:00 +0100 Subject: [PATCH 207/280] Rm unneeded code --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index ceb0e8abd8..55ef209ca2 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -549,9 +549,7 @@ struct match multi_t.d with | Some arr when not @@ is_top multi_t -> let switched_arr = List.fold_left2 (fun multi_t assigned_var primed_var-> assign_var multi_t assigned_var primed_var) multi_t assigned_vars primed_vars in - let res = drop_vars switched_arr primed_vars ~del:true in - let x = Option.get res.d in - {d = Some x; env = res.env} + drop_vars switched_arr primed_vars ~del:true | _ -> t let assign_var_parallel t vv's = From 428e2ee20e4dd8778a527d2bc1855c8f0caffc61 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 9 Feb 2024 21:14:58 +0100 Subject: [PATCH 208/280] Simplify assign_texpr --- .../apron/linearTwoVarEqualityDomain.apron.ml | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 55ef209ca2..222b196154 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -217,10 +217,6 @@ struct let get_coeff t texp = timing_wrap "coeff_vec" (get_coeff t) texp - let abstract_exists var t = match t.d with - | Some d -> {t with d = Some (EArray.forget_variable d (Environment.dim_of_var t.env var))} - | None -> t (* there are no variables in the current environment *) - (* Copy because function is not "with" so should not mutate inputs *) let assign_const t var const = match t.d with | None -> t @@ -490,22 +486,22 @@ struct (** implemented as described on page 10 in the paper about Fast Interprocedural Linear Two-Variable Equalities in the Section "Abstract Effect of Statements" This makes a copy of the data structure, it doesn't change it in-place. *) let assign_texpr (t: VarManagement.t) var texp = - let assigned_var = Environment.dim_of_var t.env var (* this is the variable we are assigning to *) in match t.d with | Some d -> - let abstract_exists_var = abstract_exists var t in + let var_i = Environment.dim_of_var t.env var (* this is the variable we are assigning to *) in begin match get_coeff t texp with - | None -> (* Statement "assigned_var = ?" (non-linear assignment) *) - abstract_exists_var + | None -> + (* Statement "assigned_var = ?" (non-linear assignment) *) + forget_vars t [var] | Some (None, off) -> (* Statement "assigned_var = off" (constant assignment) *) - assign_const abstract_exists_var assigned_var off - | Some (Some exp_var, off) when assigned_var = exp_var -> + assign_const (forget_vars t [var]) var_i off + | Some (Some exp_var, off) when var_i = exp_var -> (* Statement "assigned_var = assigned_var + off" *) - subtract_const_from_var t assigned_var off + subtract_const_from_var t var_i off | Some (Some exp_var, off) -> (* Statement "assigned_var = exp_var + off" (assigned_var is not the same as exp_var) *) - meet_with_one_conj abstract_exists_var assigned_var (Some exp_var, off) + meet_with_one_conj (forget_vars t [var]) var_i (Some exp_var, off) end | None -> bot_env @@ -526,6 +522,7 @@ struct if M.tracing then M.tracel "ops" "assign_exp t:\n %s \n var: %s \n exp: %a\n no_ov: %b -> \n %s\n" (show t) (Var.to_string var) d_exp exp (Lazy.force no_ov) (show res) ; res + let assign_var (t: VarManagement.t) v v' = let t = add_vars t [v; v'] in let texpr1 = Texpr1.of_expr (t.env) (Var v') in From b50e9c7842de7edfa072be7761c855cd6576f51f Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 9 Feb 2024 21:20:03 +0100 Subject: [PATCH 209/280] Skip detour --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 222b196154..0d4845509c 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -525,8 +525,7 @@ struct let assign_var (t: VarManagement.t) v v' = let t = add_vars t [v; v'] in - let texpr1 = Texpr1.of_expr (t.env) (Var v') in - assign_texpr t v @@ Apron.Texpr1.to_expr texpr1 + assign_texpr t v (Var v') let assign_var t v v' = let res = assign_var t v v' in From 6388bb57e28984e0f772635b4f850cff093ab02b Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 9 Feb 2024 21:37:36 +0100 Subject: [PATCH 210/280] Add TODOs --- src/analyses/apron/relationAnalysis.apron.ml | 2 ++ src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 1 + 2 files changed, 3 insertions(+) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 6876e7fb8a..9f66356afb 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -411,6 +411,8 @@ struct let new_fun_rel = List.fold_left (fun new_fun_rel (var, e) -> assign_from_globals_wrapper ask ctx.global {st with rel = new_fun_rel} e (fun rel' e' -> (* not an assign, but still works? *) + (* substitute is the backwards semantics of assignment *) + (* https://antoinemine.github.io/Apron/doc/papers/expose_CEA_2007.pdf *) RD.substitute_exp ask rel' var e' (no_overflow ask e) ) ) new_fun_rel arg_substitutes diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 0d4845509c..20172b5755 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -573,6 +573,7 @@ struct if M.tracing then M.tracel "ops" "assign_var parallel'\n"; res + (* This is supposed to the be the backwards transformer for assign, not sure this is correct *) let substitute_exp ask t var exp no_ov = let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in let res = assign_exp ask t var exp no_ov in From 84f39b6268bef7439ab42bb19c53873f739f9dc2 Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Sat, 10 Feb 2024 17:07:59 +0100 Subject: [PATCH 211/280] gotten rid of the reference --- .../apron/linearTwoVarEqualityDomain.apron.ml | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 20172b5755..8c5a70fb3c 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -615,25 +615,23 @@ struct | None -> t (*The (in-) equality is not linear, therefore we don't know anything about it. *) | Some cv's -> let expr = Array.make (Environment.size t.env) Z.zero in - let refconstant = ref (Z.zero) in - let update (c, v) = - match v with - | None -> refconstant := Z.(!refconstant + c) - | Some idx -> match d.(idx) with - | (Some idx_i, c_i) -> refconstant := Z.(!refconstant + (c * c_i)); - expr.(idx_i) <- Z.(expr.(idx_i) + c) - | (None, c_i) -> refconstant := Z.(!refconstant + (c * c_i)) + (* for use in a fold, accumulating additive constants, and as a side-effect + expr is filled with a sum of just the reference variables that cv's is simplified to *) + let accumulate_constants a (c, v) = match v with + | None -> Z.(a + c) + | Some idx -> let (term,con) = d.(idx) in + (if Option.is_some term then expr.(idx) <- Z.(expr.(idx) + c) else (); + Z.(a + c * con)) in - List.iter update cv's; (* abstract simplification of the guard wrt. reference variables *) + let constant = List.fold_left accumulate_constants Z.zero cv's in (* abstract simplification of the guard wrt. reference variables *) let var_count = Array.count_matching (fun a -> not @@ Z.equal a Z.zero) expr in - let constant = !refconstant in (* containing the reference locally *) if var_count = 0 then match Tcons1.get_typ tcons with | EQ when Z.equal constant Z.zero -> t | SUPEQ when Z.geq constant Z.zero -> t | SUP when Z.gt constant Z.zero -> t | DISEQ when not @@ Z.equal constant Z.zero -> t - | EQMOD scalar -> t + | EQMOD _ -> t | _ -> bot_env else if var_count = 1 then let index = Array.findi (fun a -> not @@ Z.equal a Z.zero) expr in From 81d5106021c68eab513c14eae4c9f4ef3fa454d4 Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Sat, 10 Feb 2024 18:41:55 +0100 Subject: [PATCH 212/280] forgot about the option --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 8c5a70fb3c..a3f46080f9 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -620,7 +620,7 @@ struct let accumulate_constants a (c, v) = match v with | None -> Z.(a + c) | Some idx -> let (term,con) = d.(idx) in - (if Option.is_some term then expr.(idx) <- Z.(expr.(idx) + c) else (); + (Option.may (fun ter -> expr.(ter) <- Z.(expr.(ter) + c)) term; Z.(a + c * con)) in let constant = List.fold_left accumulate_constants Z.zero cv's in (* abstract simplification of the guard wrt. reference variables *) From c8516416d7867c0206fc985db70694d72cb04c0c Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Mon, 12 Feb 2024 21:55:46 +0100 Subject: [PATCH 213/280] got rid of count_matching and findi --- .../apron/linearTwoVarEqualityDomain.apron.ml | 64 ++++++++----------- 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index a3f46080f9..372b3db480 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -606,14 +606,12 @@ struct -> does not have types (overflow is type dependent) *) let meet_tcons ask t tcons original_expr no_ov = - (* The expression is evaluated using an array of coefficients. The first element of the array belongs to the constant followed by the coefficients of all variables - depending on the result in the array after the evaluating including resolving the constraints in t.d the tcons can be evaluated and additional constraints can be added to t.d *) match t.d with | None -> bot_env (* same as is_bot_env t *) | Some d -> match get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with | None -> t (*The (in-) equality is not linear, therefore we don't know anything about it. *) - | Some cv's -> + | Some cv's -> (* cv's contains a list of terms (i.e. coefficients and variables) that still need simplification to 2-var-lin *) let expr = Array.make (Environment.size t.env) Z.zero in (* for use in a fold, accumulating additive constants, and as a side-effect expr is filled with a sum of just the reference variables that cv's is simplified to *) @@ -624,43 +622,33 @@ struct Z.(a + c * con)) in let constant = List.fold_left accumulate_constants Z.zero cv's in (* abstract simplification of the guard wrt. reference variables *) - let var_count = Array.count_matching (fun a -> not @@ Z.equal a Z.zero) expr in - if var_count = 0 then - match Tcons1.get_typ tcons with - | EQ when Z.equal constant Z.zero -> t - | SUPEQ when Z.geq constant Z.zero -> t - | SUP when Z.gt constant Z.zero -> t - | DISEQ when not @@ Z.equal constant Z.zero -> t - | EQMOD _ -> t - | _ -> bot_env - else if var_count = 1 then - let index = Array.findi (fun a -> not @@ Z.equal a Z.zero) expr in - let varexpr = expr.(index) in - if Z.divisible constant varexpr && Tcons1.get_typ tcons = EQ then + let sum_of_terms = Array.fold_lefti (fun list v (c) -> if Z.equal c Z.zero then list else (c,v)::list) [] expr in + match sum_of_terms with + | [] -> (* no reference variables in the guard *) + begin match Tcons1.get_typ tcons with + | EQ when Z.equal constant Z.zero -> t + | SUPEQ when Z.geq constant Z.zero -> t + | SUP when Z.gt constant Z.zero -> t + | DISEQ when not @@ Z.equal constant Z.zero -> t + | EQMOD _ -> t + | _ -> bot_env (* all other results are violating the guard *) + end + | [(varexpr, index)] -> (* guard has a single reference variable only *) + begin + if Tcons1.get_typ tcons = EQ && Z.divisible constant varexpr then meet_with_one_conj t index (None, (Z.(-(constant) / varexpr))) - else - t (*Not supported right now*) - else if var_count = 2 then - let get_vars i a l = if Z.equal a Z.zero then l else (i, a)::l in - (* WIP: the following line sums up the next 5, but would yields warning - let (var1,a1)::(var2,a2)::_ = Array.fold_righti get_vars expr [] in *) - let v12 = Array.fold_righti get_vars expr [] in - let a1 = snd (List.hd v12) in - let a2 = snd (List.hd @@ List.tl v12) in - let var1 = fst (List.hd v12) in - let var2 = fst (List.hd @@ List.tl v12) in - match Tcons1.get_typ tcons with - | EQ -> - let res = - if Z.equal a1 Z.one && Z.equal a2 Z.(-one) + else + t (* only EQ is supported in equality based domains *) + end + | (a1,var1)::[(a2,var2)] -> (* two variables in relation needs a little sorting out *) + begin match Tcons1.get_typ tcons with + | EQ when Z.(a1 * a2 = -one) -> (* var1-var1 or var2-var1 *) + if Z.equal a1 Z.one then meet_with_one_conj t var2 (Some var1, constant) - else if Z.equal a1 Z.(-one) && Z.equal a2 Z.one - then meet_with_one_conj t var1 (Some var2, constant) - else t - in res - | _-> t (*Not supported right now*) - else - t (*For any other case we don't know if the (in-) equality is true or false or even possible therefore we just return t *) + else meet_with_one_conj t var1 (Some var2, constant) + | _-> t (* Not supported in equality based 2vars without coeffiients *) + end + | _ -> t (* For equalities of more then 2 vars we just return t *) let meet_tcons t tcons expr = timing_wrap "meet_tcons" (meet_tcons t tcons) expr From a1582e42921335626a29f9f7763fb18c657a0f8d Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 13 Feb 2024 09:36:13 +0100 Subject: [PATCH 214/280] next ref removed --- .../apron/linearTwoVarEqualityDomain.apron.ml | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 372b3db480..5504c22496 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -191,28 +191,22 @@ struct let get_coeff (t: t) texp = let d = Option.get t.d in - let expr = Array.make (Environment.size t.env) Z.zero in - let constant = ref (Z.zero) in match get_coeff_vec t texp with | None -> None (*The (in-) equality is not linear, therefore we don't know anything about it. *) | Some cv's -> - let update (c, v) = - match v with - | None -> constant := Z.(!constant + c) - | Some idx -> match d.(idx) with - | (Some idx_i, c_i) -> constant := Z.(!constant + (c * c_i)); - expr.(idx_i) <- Z.(expr.(idx_i) + c) - | (None, c_i) -> constant := Z.(!constant + (c * c_i)) + let expr = Array.make (Environment.size t.env) Z.zero in + let accumulate_constants a (c, v) = match v with + | None -> Z.(a + c) + | Some idx -> let (term,con) = d.(idx) in + (Option.may (fun ter -> expr.(ter) <- Z.(expr.(ter) + c)) term; + Z.(a + c * con)) in - List.iter update cv's; - let var_count = BatArray.count_matching (fun a -> not @@ Z.equal a Z.zero) expr in - if var_count = 0 then Some (None, !constant) - else if var_count = 1 then ( - let var = Array.findi (fun a -> not @@ Z.equal a Z.zero) expr in - if Z.equal expr.(var) Z.one then Some (Some var, !constant) - else None - ) - else None + let constant = List.fold_left accumulate_constants Z.zero cv's in (* abstract simplification of the guard wrt. reference variables *) + let sum_of_terms = Array.fold_lefti (fun list v (c) -> if Z.equal c Z.zero then list else (c,v)::list) [] expr in + match sum_of_terms with + | [] -> Some (None, constant) + | [(coeff,var)] when Z.equal coeff Z.one -> Some (Some var, constant) + |_ -> None let get_coeff t texp = timing_wrap "coeff_vec" (get_coeff t) texp From dab1cf25107146f68233706d3a6b56420fca36f7 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 13 Feb 2024 10:19:43 +0100 Subject: [PATCH 215/280] get_coeff_vec renovated --- .../apron/linearTwoVarEqualityDomain.apron.ml | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 5504c22496..96d3697e39 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -150,7 +150,7 @@ struct let multiply_with_Z number coeff_var_list = List.map (fun (coeff, var) -> (Z.(number * coeff, var))) coeff_var_list in let multiply a b = - (* if one of them is a constant, then multiply. Otherwise, the expression is not linear*) + (* if one of them is a constant, then multiply. Otherwise, the expression is not linear *) match a, b with | [(a_coeff, None)], b -> multiply_with_Z a_coeff b | a, [(b_coeff, None)] -> multiply_with_Z b_coeff a @@ -158,8 +158,8 @@ struct in let rec convert_texpr texp = begin match texp with - (*If x is a constant, replace it with its const. val. immediately*) - | Cst (Interval _) ->failwith "Not a constant" + (* If x is a constant, replace it with its const. val. immediately *) + | Cst (Interval _) -> failwith "Not a constant" | Cst (Scalar x) -> begin match SharedFunctions.int_of_scalar ?round:None x with | Some x -> [(x, None)] @@ -168,25 +168,21 @@ struct let var_dim = Environment.dim_of_var t.env x in begin match t.d with | None -> [(Z.one, Some var_dim)] - | Some d -> - (if Option.is_some (fst d.(var_dim)) then [(Z.one, fst d.(var_dim))] - else []) - @ [(snd d.(var_dim), None)] + | Some d -> + (match d.(var_dim) with + | (Some i, k) -> [(Z.one, Some i); (k, None)] + | (None, k) -> [(k, None)]) end - | Unop (u, e, _, _) -> - begin match u with - | Neg -> negate (convert_texpr e) - | Cast -> convert_texpr e (*Ignore since casts in apron are used for floating point nums and rounding in contrast to CIL casts*) - | Sqrt -> raise NotLinearExpr end - | Binop (b, e1, e2, _, _) -> - begin match b with - | Add -> List.concat [convert_texpr e1; convert_texpr e2] - | Sub -> List.concat [convert_texpr e1; negate (convert_texpr e2)] - | Mul -> multiply (convert_texpr e1) (convert_texpr e2) - | _ -> raise NotLinearExpr end - end + | Unop (Neg, e, _, _) -> negate (convert_texpr e) + | Unop (Cast, e, _, _) -> convert_texpr e (* Ignore since casts in apron are used for floating point nums and rounding in contrast to CIL casts *) + | Unop (Sqrt, e, _, _) -> raise NotLinearExpr + | Binop (Add, e1, e2, _, _) -> List.concat [convert_texpr e1; convert_texpr e2] + | Binop (Sub, e1, e2, _, _) -> List.concat [convert_texpr e1; negate (convert_texpr e2)] + | Binop (Mul, e1, e2, _, _) -> multiply (convert_texpr e1) (convert_texpr e2) + | Binop _ -> raise NotLinearExpr end in match convert_texpr texp with | exception NotLinearExpr -> None + | exception NotIntegerOffset -> None | x -> Some(x) let get_coeff (t: t) texp = From fcaa3a8da2d1887d55d17f185c03116881746055 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 13 Feb 2024 11:03:00 +0100 Subject: [PATCH 216/280] minor tweaks --- .../apron/linearTwoVarEqualityDomain.apron.ml | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 96d3697e39..258f29055e 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -145,7 +145,7 @@ struct let get_coeff_vec (t: t) texp = let open Apron.Texpr1 in let exception NotLinearExpr in - let exception NotIntegerOffset in + let exception ScalarIsInfinity in let negate coeff_var_list = List.map (fun (coeff, var) -> (Z.(-coeff), var)) coeff_var_list in let multiply_with_Z number coeff_var_list = List.map (fun (coeff, var) -> (Z.(number * coeff, var))) coeff_var_list in @@ -159,19 +159,19 @@ struct let rec convert_texpr texp = begin match texp with (* If x is a constant, replace it with its const. val. immediately *) - | Cst (Interval _) -> failwith "Not a constant" + | Cst (Interval _) -> failwith "constant was an interval; this is not supported" | Cst (Scalar x) -> begin match SharedFunctions.int_of_scalar ?round:None x with | Some x -> [(x, None)] - | None -> raise NotIntegerOffset end + | None -> raise ScalarIsInfinity end | Var x -> let var_dim = Environment.dim_of_var t.env x in begin match t.d with | None -> [(Z.one, Some var_dim)] | Some d -> (match d.(var_dim) with - | (Some i, k) -> [(Z.one, Some i); (k, None)] - | (None, k) -> [(k, None)]) + | (Some i, k) -> [(Z.one, Some i); (k, None)] + | (None, k) -> [(k, None)]) end | Unop (Neg, e, _, _) -> negate (convert_texpr e) | Unop (Cast, e, _, _) -> convert_texpr e (* Ignore since casts in apron are used for floating point nums and rounding in contrast to CIL casts *) @@ -182,7 +182,7 @@ struct | Binop _ -> raise NotLinearExpr end in match convert_texpr texp with | exception NotLinearExpr -> None - | exception NotIntegerOffset -> None + | exception ScalarIsInfinity -> None | x -> Some(x) let get_coeff (t: t) texp = @@ -624,18 +624,14 @@ struct | _ -> bot_env (* all other results are violating the guard *) end | [(varexpr, index)] -> (* guard has a single reference variable only *) - begin - if Tcons1.get_typ tcons = EQ && Z.divisible constant varexpr then - meet_with_one_conj t index (None, (Z.(-(constant) / varexpr))) - else - t (* only EQ is supported in equality based domains *) - end - | (a1,var1)::[(a2,var2)] -> (* two variables in relation needs a little sorting out *) + if Tcons1.get_typ tcons = EQ && Z.divisible constant varexpr then + meet_with_one_conj t index (None, (Z.(-(constant) / varexpr))) + else + t (* only EQ is supported in equality based domains *) + | [(a1,var1); (a2,var2)] -> (* two variables in relation needs a little sorting out *) begin match Tcons1.get_typ tcons with | EQ when Z.(a1 * a2 = -one) -> (* var1-var1 or var2-var1 *) - if Z.equal a1 Z.one - then meet_with_one_conj t var2 (Some var1, constant) - else meet_with_one_conj t var1 (Some var2, constant) + meet_with_one_conj t var2 (Some var1, Z.mul a1 constant) | _-> t (* Not supported in equality based 2vars without coeffiients *) end | _ -> t (* For equalities of more then 2 vars we just return t *) From 8b6b68a01e38c61ed7efcc505a26a06f566f78cf Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 13 Feb 2024 11:33:10 +0100 Subject: [PATCH 217/280] refactored and renamed --- .../apron/linearTwoVarEqualityDomain.apron.ml | 93 +++++++++---------- 1 file changed, 43 insertions(+), 50 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 258f29055e..dc932e3625 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -131,7 +131,7 @@ module EqualitiesArray = struct end (** [VarManagement] defines the type t of the affine equality domain (a record that contains an optional matrix and an apron environment) and provides the functions needed for handling variables (which are defined by [RelationDomain.D2]) such as [add_vars], [remove_vars]. - Furthermore, it provides the function [get_coeff_vec] that parses an apron expression into a vector of coefficients if the apron expression has an affine form. *) + Furthermore, it provides the function [simplified_monomials_from_texp] that converts an apron expression into a list of monomials of reference variables and a constant offset *) module VarManagement = struct module EArray = EqualitiesArray @@ -140,9 +140,8 @@ struct let dim_add = EArray.dim_add let size t = BatOption.map_default (fun d -> EArray.length d) 0 t.d - (** Parses a Texpr to obtain a (coefficient, variable) pair list to repr. a sum of a variables that have a coefficient. If variable is None, the coefficient represents a constant offset. - **) - let get_coeff_vec (t: t) texp = + (** Parses a Texpr to obtain a (coefficient, variable) pair list to repr. a sum of a variables that have a coefficient. If variable is None, the coefficient represents a constant offset. *) + let monomials_from_texp (t: t) texp = let open Apron.Texpr1 in let exception NotLinearExpr in let exception ScalarIsInfinity in @@ -185,9 +184,10 @@ struct | exception ScalarIsInfinity -> None | x -> Some(x) - let get_coeff (t: t) texp = + (** convert and simplify (wrt. reference variables) a texpr into a tuple of a list of monomials and a constant *) + let simplified_monomials_from_texp (t: t) texp = let d = Option.get t.d in - match get_coeff_vec t texp with + match monomials_from_texp t texp with | None -> None (*The (in-) equality is not linear, therefore we don't know anything about it. *) | Some cv's -> let expr = Array.make (Environment.size t.env) Z.zero in @@ -198,14 +198,18 @@ struct Z.(a + c * con)) in let constant = List.fold_left accumulate_constants Z.zero cv's in (* abstract simplification of the guard wrt. reference variables *) - let sum_of_terms = Array.fold_lefti (fun list v (c) -> if Z.equal c Z.zero then list else (c,v)::list) [] expr in - match sum_of_terms with - | [] -> Some (None, constant) - | [(coeff,var)] when Z.equal coeff Z.one -> Some (Some var, constant) - |_ -> None + Some (Array.fold_lefti (fun list v (c) -> if Z.equal c Z.zero then list else (c,v)::list) [] expr, constant) + let simplify_to_ref_and_offset (t: t) texp = + match simplified_monomials_from_texp t texp with + | None -> None + | Some (sum_of_terms, constant) -> + (match sum_of_terms with + | [] -> Some (None, constant) + | [(coeff,var)] when Z.equal coeff Z.one -> Some (Some var, constant) + |_ -> None) - let get_coeff t texp = timing_wrap "coeff_vec" (get_coeff t) texp + let simplify_to_ref_and_offset t texp = timing_wrap "coeff_vec" (simplify_to_ref_and_offset t) texp (* Copy because function is not "with" so should not mutate inputs *) let assign_const t var const = match t.d with @@ -244,7 +248,7 @@ struct include VarManagement let bound_texpr t texpr = - match get_coeff t (Texpr1.to_expr texpr) with + match simplify_to_ref_and_offset t (Texpr1.to_expr texpr) with | Some (None, offset) -> (if M.tracing then M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string offset) (IntOps.BigIntOps.to_string offset); Some offset, Some offset) @@ -479,7 +483,7 @@ struct match t.d with | Some d -> let var_i = Environment.dim_of_var t.env var (* this is the variable we are assigning to *) in - begin match get_coeff t texp with + begin match simplify_to_ref_and_offset t texp with | None -> (* Statement "assigned_var = ?" (non-linear assignment) *) forget_vars t [var] @@ -599,42 +603,31 @@ struct match t.d with | None -> bot_env (* same as is_bot_env t *) | Some d -> - match get_coeff_vec t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with - | None -> t (*The (in-) equality is not linear, therefore we don't know anything about it. *) - | Some cv's -> (* cv's contains a list of terms (i.e. coefficients and variables) that still need simplification to 2-var-lin *) - let expr = Array.make (Environment.size t.env) Z.zero in - (* for use in a fold, accumulating additive constants, and as a side-effect - expr is filled with a sum of just the reference variables that cv's is simplified to *) - let accumulate_constants a (c, v) = match v with - | None -> Z.(a + c) - | Some idx -> let (term,con) = d.(idx) in - (Option.may (fun ter -> expr.(ter) <- Z.(expr.(ter) + c)) term; - Z.(a + c * con)) - in - let constant = List.fold_left accumulate_constants Z.zero cv's in (* abstract simplification of the guard wrt. reference variables *) - let sum_of_terms = Array.fold_lefti (fun list v (c) -> if Z.equal c Z.zero then list else (c,v)::list) [] expr in - match sum_of_terms with - | [] -> (* no reference variables in the guard *) - begin match Tcons1.get_typ tcons with - | EQ when Z.equal constant Z.zero -> t - | SUPEQ when Z.geq constant Z.zero -> t - | SUP when Z.gt constant Z.zero -> t - | DISEQ when not @@ Z.equal constant Z.zero -> t - | EQMOD _ -> t - | _ -> bot_env (* all other results are violating the guard *) - end - | [(varexpr, index)] -> (* guard has a single reference variable only *) - if Tcons1.get_typ tcons = EQ && Z.divisible constant varexpr then - meet_with_one_conj t index (None, (Z.(-(constant) / varexpr))) - else - t (* only EQ is supported in equality based domains *) - | [(a1,var1); (a2,var2)] -> (* two variables in relation needs a little sorting out *) - begin match Tcons1.get_typ tcons with - | EQ when Z.(a1 * a2 = -one) -> (* var1-var1 or var2-var1 *) - meet_with_one_conj t var2 (Some var1, Z.mul a1 constant) - | _-> t (* Not supported in equality based 2vars without coeffiients *) - end - | _ -> t (* For equalities of more then 2 vars we just return t *) + match simplified_monomials_from_texp t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with + | None -> t + | Some (sum_of_terms, constant) ->( + match sum_of_terms with + | [] -> (* no reference variables in the guard *) + begin match Tcons1.get_typ tcons with + | EQ when Z.equal constant Z.zero -> t + | SUPEQ when Z.geq constant Z.zero -> t + | SUP when Z.gt constant Z.zero -> t + | DISEQ when not @@ Z.equal constant Z.zero -> t + | EQMOD _ -> t + | _ -> bot_env (* all other results are violating the guard *) + end + | [(varexpr, index)] -> (* guard has a single reference variable only *) + if Tcons1.get_typ tcons = EQ && Z.divisible constant varexpr then + meet_with_one_conj t index (None, (Z.(-(constant) / varexpr))) + else + t (* only EQ is supported in equality based domains *) + | [(a1,var1); (a2,var2)] -> (* two variables in relation needs a little sorting out *) + begin match Tcons1.get_typ tcons with + | EQ when Z.(a1 * a2 = -one) -> (* var1-var1 or var2-var1 *) + meet_with_one_conj t var2 (Some var1, Z.mul a1 constant) + | _-> t (* Not supported in equality based 2vars without coeffiients *) + end + | _ -> t (* For equalities of more then 2 vars we just return t *)) let meet_tcons t tcons expr = timing_wrap "meet_tcons" (meet_tcons t tcons) expr From 453a1bb209de5ea8556a9c502ab3337f919f0fc8 Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Thu, 15 Feb 2024 08:41:02 +0100 Subject: [PATCH 218/280] bot_env treatment more explicit --- .../apron/linearTwoVarEqualityDomain.apron.ml | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index dc932e3625..c7ed803bfa 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -278,7 +278,6 @@ struct let to_yojson _ = failwith "ToDo Implement in future" let is_bot t = equal t (bot ()) - let is_bot_env t = t.d = None (* this shows "top" for a specific environment to enable the calculations. It is the top_of of all equalities *) let top_of env = {d = Some (EArray.make_empty_array (Environment.size env)); env = env} @@ -417,22 +416,19 @@ struct List.iter iterate new_components; Some ad in (*Normalize the two domains a and b such that both talk about the same variables*) - if is_bot_env a then - b - else if is_bot_env b then - a - else - match Option.get a.d, Option.get b.d with - | x, y when is_top a || is_top b -> + match a.d, b.d with + | None, _ -> b + | _, None -> a + | Some x, Some y when is_top a || is_top b -> let new_env = Environment.lce a.env b.env in top_of new_env - | x, y when (Environment.compare a.env b.env <> 0) -> + | Some x, Some y when (Environment.compare a.env b.env <> 0) -> let sup_env = Environment.lce a.env b.env in let mod_x = dim_add (Environment.dimchange a.env sup_env) x in let mod_y = dim_add (Environment.dimchange b.env sup_env) y in {d = join_d mod_x mod_y; env = sup_env} - | x, y when EArray.equal x y -> {d = Some x; env = a.env} - | x, y -> {d = join_d x y; env = a.env} + | Some x, Some y when EArray.equal x y -> {d = Some x; env = a.env} + | Some x, Some y -> {d = join_d x y; env = a.env} let join a b = timing_wrap "join" (join a) b @@ -601,7 +597,7 @@ struct *) let meet_tcons ask t tcons original_expr no_ov = match t.d with - | None -> bot_env (* same as is_bot_env t *) + | None -> t | Some d -> match simplified_monomials_from_texp t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with | None -> t From cf5b1932aa9c07798ceefa021a20208171c41907 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Mon, 19 Feb 2024 14:38:16 +0100 Subject: [PATCH 219/280] small tweaks --- .../apron/linearTwoVarEqualityDomain.apron.ml | 40 ++++++++----------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index c7ed803bfa..1d3cb9e5aa 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -186,10 +186,9 @@ struct (** convert and simplify (wrt. reference variables) a texpr into a tuple of a list of monomials and a constant *) let simplified_monomials_from_texp (t: t) texp = - let d = Option.get t.d in - match monomials_from_texp t texp with - | None -> None (*The (in-) equality is not linear, therefore we don't know anything about it. *) - | Some cv's -> + BatOption.bind (monomials_from_texp t texp) + (fun monomiallist -> + let d = Option.get t.d in let expr = Array.make (Environment.size t.env) Z.zero in let accumulate_constants a (c, v) = match v with | None -> Z.(a + c) @@ -197,17 +196,16 @@ struct (Option.may (fun ter -> expr.(ter) <- Z.(expr.(ter) + c)) term; Z.(a + c * con)) in - let constant = List.fold_left accumulate_constants Z.zero cv's in (* abstract simplification of the guard wrt. reference variables *) - Some (Array.fold_lefti (fun list v (c) -> if Z.equal c Z.zero then list else (c,v)::list) [] expr, constant) + let constant = List.fold_left accumulate_constants Z.zero monomiallist in (* abstract simplification of the guard wrt. reference variables *) + Some (Array.fold_lefti (fun list v (c) -> if Z.equal c Z.zero then list else (c,v)::list) [] expr, constant) ) let simplify_to_ref_and_offset (t: t) texp = - match simplified_monomials_from_texp t texp with - | None -> None - | Some (sum_of_terms, constant) -> - (match sum_of_terms with - | [] -> Some (None, constant) - | [(coeff,var)] when Z.equal coeff Z.one -> Some (Some var, constant) - |_ -> None) + BatOption.bind (simplified_monomials_from_texp t texp ) + (fun (sum_of_terms, constant) -> + (match sum_of_terms with + | [] -> Some (None, constant) + | [(coeff,var)] when Z.equal coeff Z.one -> Some (Some var, constant) + |_ -> None)) let simplify_to_ref_and_offset t texp = timing_wrap "coeff_vec" (simplify_to_ref_and_offset t) texp @@ -277,15 +275,16 @@ struct let to_yojson _ = failwith "ToDo Implement in future" + (** t.d is some empty array and env is empty *) let is_bot t = equal t (bot ()) - (* this shows "top" for a specific environment to enable the calculations. It is the top_of of all equalities *) + (** forall x_i in env, x_i:=X_i+0 *) let top_of env = {d = Some (EArray.make_empty_array (Environment.size env)); env = env} - (** Is not expected to be called but implemented for completeness *) + (** env = \emptyset, d = Some([||]) *) let top () = {d = Some (EArray.empty()); env = empty_env} - (** is_top returns true for top_of array and empty array *) + (** is_top returns true for top_of array and empty array; precondition: t.env and t.d are of same size *) let is_top t = GobOption.exists EArray.is_top_array t.d (** prints the current variable equalities with resolved variable names *) @@ -504,8 +503,7 @@ struct let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in match Convert.texpr1_expr_of_cil_exp ask t t.env exp no_ov with | texp -> assign_texpr t var texp - | exception Convert.Unsupported_CilExp _ -> - if is_bot_env t then t else forget_vars t [var] + | exception Convert.Unsupported_CilExp _ -> forget_vars t [var] let assign_exp ask t var exp no_ov = let res = assign_exp ask t var exp no_ov in @@ -576,12 +574,6 @@ struct let substitute_exp ask t var exp no_ov = timing_wrap "substitution" (substitute_exp ask t var exp) no_ov - let show_coeff_vec l (env : Environment.t) = - let show_element = function - | (a, Some x) -> ((Z.to_string a) ^ " * " ^ (Var.to_string (Environment.var_of_dim env x)) ^ " + ") - | (a, None) -> ((Z.to_string a) ^ "+") - in - List.fold_right (fun k result -> show_element k ^ "\n" ^ result) l "" (** Assert a constraint expression. The overflow is completely handled by the flag "no_ov", From ec9d3d3131eba0575701ae806fb5f2383f2a6287 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Mon, 26 Feb 2024 16:17:31 +0100 Subject: [PATCH 220/280] with dunefile for lin2vareq --- tests/regression/77-lin2vareq/dune | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/regression/77-lin2vareq/dune diff --git a/tests/regression/77-lin2vareq/dune b/tests/regression/77-lin2vareq/dune new file mode 100644 index 0000000000..b4cede7223 --- /dev/null +++ b/tests/regression/77-lin2vareq/dune @@ -0,0 +1,10 @@ +(rule + (aliases runtest runaprontest) + (enabled_if %{lib-available:apron}) + (deps + (package goblint) + ../../../goblint ; update_suite calls local goblint + (:update_suite ../../../scripts/update_suite.rb) + (glob_files ??-*.c)) + (locks /update_suite) + (action (chdir ../../.. (run %{update_suite} group lin2vareq)))) \ No newline at end of file From b514242d36f83bbf2caa3723d33fe02b526b3f01 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Mon, 26 Feb 2024 16:38:08 +0100 Subject: [PATCH 221/280] pushed test to the back --- tests/regression/46-apron2/{61-fixpoint.c => 79-fixpoint.c} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/regression/46-apron2/{61-fixpoint.c => 79-fixpoint.c} (100%) diff --git a/tests/regression/46-apron2/61-fixpoint.c b/tests/regression/46-apron2/79-fixpoint.c similarity index 100% rename from tests/regression/46-apron2/61-fixpoint.c rename to tests/regression/46-apron2/79-fixpoint.c From 6961d735ed2b0dc2286269e7244615f2b6e40f99 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 27 Feb 2024 14:41:38 +0100 Subject: [PATCH 222/280] typo in filename --- ...lysis.no.apron.ml => linearTwoVarEqualityAnalysis.no-apron.ml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/analyses/apron/{linearTwoVarEqualityAnalysis.no.apron.ml => linearTwoVarEqualityAnalysis.no-apron.ml} (100%) diff --git a/src/analyses/apron/linearTwoVarEqualityAnalysis.no.apron.ml b/src/analyses/apron/linearTwoVarEqualityAnalysis.no-apron.ml similarity index 100% rename from src/analyses/apron/linearTwoVarEqualityAnalysis.no.apron.ml rename to src/analyses/apron/linearTwoVarEqualityAnalysis.no-apron.ml From 25407b52a9cca1a98452957a9be561b4f6aff83f Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 1 Mar 2024 14:04:18 +0100 Subject: [PATCH 223/280] fixed bug in base analysis' int evaluation wrt. comparison of addresses and constants --- src/analyses/base.ml | 7 +++-- src/analyses/mCP.ml | 4 ++- tests/regression/46-apron2/01-realfixpoint.c | 18 +++++++++++++ tests/regression/46-apron2/79-fixpoint.c | 17 ------------ .../46-apron2/79-reachable_bodies.c | 26 +++++++++++++++++++ 5 files changed, 52 insertions(+), 20 deletions(-) create mode 100644 tests/regression/46-apron2/01-realfixpoint.c delete mode 100644 tests/regression/46-apron2/79-fixpoint.c create mode 100644 tests/regression/46-apron2/79-reachable_bodies.c diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 116bb038e6..573843f5d3 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -321,8 +321,11 @@ struct | Address p, Int n | Int n, Address p when op=Eq || op=Ne -> let ik = Cilfacade.get_ikind t in - Int (match ID.to_bool n, AD.to_bool p with - | Some a, Some b -> ID.of_bool ik (op=Eq && a=b || op=Ne && a<>b) + Int (match ID.to_int n, ID.to_int (AD.to_int p) with + | Some a, Some b -> ID.of_bool ik (op=Eq && Z.equal a b || op=Ne && not @@ Z.equal a b) + | Some a, _ -> if AD.is_null p then ID.of_bool ik (op=Eq && Z.equal a Z.zero || op=Ne && not @@ Z.equal a Z.zero) + else if AD.is_not_null p && Z.equal a Z.zero then ID.of_bool ik (not @@ (op=Eq)) + else bool_top ik | _ -> bool_top ik) | Address p, Int n -> addToAddrOp p n diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index a3943651c0..18795e9716 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -272,7 +272,9 @@ struct } in (* meet results so that precision from all analyses is combined *) - Result.meet a @@ S.query ctx' q + let res = S.query ctx' q in + M.trace "queryanswers" "analysis %s query %a -> answer %a\n" (S.name ()) Queries.Any.pretty anyq Result.pretty res; + Result.meet a @@ res in match q with | Queries.WarnGlobal g -> diff --git a/tests/regression/46-apron2/01-realfixpoint.c b/tests/regression/46-apron2/01-realfixpoint.c new file mode 100644 index 0000000000..ca39a074a3 --- /dev/null +++ b/tests/regression/46-apron2/01-realfixpoint.c @@ -0,0 +1,18 @@ +// SKIP PARAM: --set ana.base.arrays.domain partitioned --enable ana.int.interval --set ana.activated[+] apron +#include +int main () +{ + char A [3]; + + A[2] = 0; + + char *str = A; + int i = 0; + + while (str[i] != 0) { + __goblint_check(1); // reachable + i++; + } + + return 0; +} diff --git a/tests/regression/46-apron2/79-fixpoint.c b/tests/regression/46-apron2/79-fixpoint.c deleted file mode 100644 index 9fcb7715e3..0000000000 --- a/tests/regression/46-apron2/79-fixpoint.c +++ /dev/null @@ -1,17 +0,0 @@ -// SKIP PARAM: --set ana.activated[+] apron --enable ana.int.interval --set ana.base.arrays.domain partitioned - -int main () -{ - char A [3]; - - A[2] = 0; - - char *str = A; - int i = 0; - - while (str[i] != 0) { - i++; - } - - return 0; -} diff --git a/tests/regression/46-apron2/79-reachable_bodies.c b/tests/regression/46-apron2/79-reachable_bodies.c new file mode 100644 index 0000000000..3157df2eca --- /dev/null +++ b/tests/regression/46-apron2/79-reachable_bodies.c @@ -0,0 +1,26 @@ +// SKIP PARAM: --set ana.base.arrays.domain partitioned --enable ana.int.interval --set ana.activated[+] apron +#include +int main () +{ + char A [3]; + + A[2] = 0; + + char *str = A; + int i = 0; + + if (str[i] != 0) { + __goblint_check(1); // reachable + i++; + } + if (str[i] != 0) { + __goblint_check(1); // reachable + i++; + } + if (str[i] != 0) { + __goblint_check(1); // reachable + i++; + } + + return 0; +} From e9ce7a8caeb09648cece8b6de1cfec761c6d624d Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Fri, 1 Mar 2024 22:46:54 +0100 Subject: [PATCH 224/280] Update src/analyses/mCP.ml Of course, this was stupid Co-authored-by: Michael Schwarz --- src/analyses/mCP.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index 18795e9716..5c14d4bf4b 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -273,7 +273,7 @@ struct in (* meet results so that precision from all analyses is combined *) let res = S.query ctx' q in - M.trace "queryanswers" "analysis %s query %a -> answer %a\n" (S.name ()) Queries.Any.pretty anyq Result.pretty res; + if M.tracing then M.trace "queryanswers" "analysis %s query %a -> answer %a\n" (S.name ()) Queries.Any.pretty anyq Result.pretty res; Result.meet a @@ res in match q with From 48bacb5f201c96938efad70b2b8abfc96712a1d8 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 5 Mar 2024 13:39:54 +0100 Subject: [PATCH 225/280] fixed overaggressive simplification of constraints via texpr1_expr_of_cil_exp/simplify via provision of a dummy ask --- src/analyses/apron/relationAnalysis.apron.ml | 24 +++++++++++++++++++- src/cdomains/apron/apronDomain.apron.ml | 3 ++- src/cdomains/apron/sharedFunctions.apron.ml | 10 ++++++-- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 65f91a8a52..16d194642a 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -706,7 +706,29 @@ struct let vars = Basetype.CilExp.get_vars e |> List.unique ~eq:CilType.Varinfo.equal |> List.filter RD.Tracked.varinfo_tracked in let rel = RD.forget_vars rel (List.map RV.local vars) in (* havoc *) let rel = List.fold_left (assert_type_bounds ask) rel vars in (* add type bounds to avoid overflow in top state *) - let rel = RD.assert_inv ask rel e false (no_overflow ask e_orig) in (* assume *) + let rec dummyask = + (* assert_inv calls texpr1_expr_of_cil_exp, which simplifies the constraint based on the pre-state of the transition; + this does not reflect the state after RD.forget_vars rel .... has been performed; to prevent this aggressive + simplification, we restrict read_int queries to a local dummy ask, that only dispatches to rel instead of the + full state *) + let f (type a) (q : a Queries.t) : a = + let eval_int e no_ov = + let esimple = replace_deref_exps dummyask.f e in + read_from_globals_wrapper + (dummyask) + ctx.global { st with rel } esimple + (fun rel' e' -> RD.eval_int (dummyask) rel' e' no_ov) + in + match q with + | EvalInt e -> + if M.tracing then M.traceli "relation" "evalint query %a (%a), ctx %a\n" d_exp e d_plainexp e D.pretty ctx.local; + let r = eval_int e (no_overflow (dummyask) e) in + if M.tracing then M.trace "relation" "evalint response %a -> %a\n" d_exp e ValueDomainQueries.ID.pretty r; + r + |_ -> Queries.Result.top q + in + ({ f } : Queries.ask) in + let rel = RD.assert_inv dummyask rel e false (no_overflow ask e_orig) in (* assume *) let rel = RD.keep_vars rel (List.map RV.local vars) in (* restrict *) (* TODO: parallel write_global? *) diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index 9a59c6c50a..629563eea3 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -390,7 +390,7 @@ struct let texpr1 = Texpr1.of_expr (A.env nd) (Var v') in A.substitute_texpr_with Man.mgr nd v texpr1 None - let meet_tcons ask d tcons1 e = + let meet_tcons _ d tcons1 e = let earray = Tcons1.array_make (A.env d) 1 in Tcons1.array_set earray 0 tcons1; A.meet_tcons_array Man.mgr d earray @@ -536,6 +536,7 @@ struct | tcons1 -> if M.tracing then M.trace "apron" "assert_constraint %a %s\n" d_exp e (Format.asprintf "%a" Tcons1.print tcons1); if M.tracing then M.trace "apron" "assert_constraint st: %a\n" D.pretty d; + if M.tracing then M.trace "apron" "assert_constraint tcons1: %s\n" (Format.asprintf "%a" Tcons1.print tcons1); let r = meet_tcons ask d tcons1 e in if M.tracing then M.trace "apron" "assert_constraint r: %a\n" D.pretty r; r diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 2f459747fc..dc0fc8fe6f 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -166,10 +166,13 @@ struct let texpr1_expr_of_cil_exp (ask:Queries.ask) d env exp no_ov = let conv exp overflow_handling = let query e ik = + let res = match ask.f (EvalInt e) with | `Bot -> raise (Unsupported_CilExp Exp_not_supported) (* This should never happen according to Michael Schwarz *) | `Top -> IntDomain.IntDomTuple.top_of ik - | `Lifted x -> x (* Cast should be unnecessary because it should be taken care of by EvalInt. *) + | `Lifted x -> x (* Cast should be unnecessary because it should be taken care of by EvalInt. *) in + if M.tracing then M.trace "relation" "texpr1_expr_of_cil_exp/query: %a -> %a\n" d_plainexp e IntDomain.IntDomTuple.pretty res; + res in (* recurse without env and ask arguments *) let rec texpr1_expr_of_cil_exp = function @@ -197,6 +200,7 @@ struct let ikind = try (Cilfacade.get_ikind_exp e) with Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) in let simp = query e ikind in let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to ikind simp in + if M.tracing then M.trace "relation" "texpr1_expr_of_cil_exp/simplify: %a -> %a\n" d_plainexp e IntDomain.IntDomTuple.pretty simp; BatOption.map_default (fun c -> Const (CInt (c, ikind, None))) e const in let texpr1 e = texpr1_expr_of_cil_exp (simplify e) in @@ -242,7 +246,9 @@ struct in let exp = Cil.constFold false exp in let ov_handler = if Arg.do_overflow_check then overflow_handling_apron else no_ov_overflow_handling in - conv exp ov_handler + let res = conv exp ov_handler in + if M.tracing then M.trace "relation" "texpr1_expr_of_cil_exp: %a -> %s\n" d_plainexp exp (Format.asprintf "%a" Texpr1.print_expr res); + res let texpr1_of_cil_exp ask d env e no_ov = let e = Cil.constFold false e in From f71a96675adb8e8338fab6b3086e7ac56b407f38 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 5 Mar 2024 14:41:28 +0100 Subject: [PATCH 226/280] gotten rid of _old versions of *_of_cil_exp --- src/cdomains/apron/apronDomain.apron.ml | 9 ++- src/cdomains/apron/sharedFunctions.apron.ml | 89 --------------------- 2 files changed, 8 insertions(+), 90 deletions(-) diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index 629563eea3..e4016a4725 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -715,7 +715,14 @@ struct (* this implements widening_threshold with Tcons1 instead of Lincons1 *) let tcons1s = List.filter_map (fun e -> let no_ov = lazy(IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp e)) in - match Convert.tcons1_of_cil_exp_old y y_env e false no_ov with + let dummyask = let f (type a) (q : a Queries.t) : a = + (* Convert.tcons1_of_cil_exp supports fancy aggressive simplifications of expressions + via querying the context for int constants that replace subexpressions; + we do not have a context here, so we just use a dummy ask replying top all the time *) + Queries.Result.top q + in + ({ f } : Queries.ask) in + match Convert.tcons1_of_cil_exp dummyask y y_env e false no_ov with | tcons1 when A.sat_tcons Man.mgr y tcons1 -> Some tcons1 | _ diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index dc0fc8fe6f..2315816168 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -108,61 +108,6 @@ struct is not an integer expression, for example if it is a float expression. *) raise (Unsupported_CilExp Exp_not_supported) - let texpr1_expr_of_cil_exp_old d env exp no_ov = - (* recurse without env argument *) - let rec texpr1_expr_of_cil_exp = function - | Lval (Var v, NoOffset) when Tracked.varinfo_tracked v -> - if not v.vglob || Arg.allow_global then - let var = - if v.vglob then - V.global v - else - V.local v - in - if Environment.mem_var env var then - Var var - else - raise (Unsupported_CilExp (Var_not_found v)) - else - failwith "texpr1_expr_of_cil_exp: globals must be replaced with temporary locals" - | Const (CInt (i, _, _)) -> - Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z i))) - | exp -> - match Cilfacade.get_ikind_exp exp with - | ik -> - let expr = - match exp with - | UnOp (Neg, e, _) -> - Unop (Neg, texpr1_expr_of_cil_exp e, Int, Near) - | BinOp (PlusA, e1, e2, _) -> - Binop (Add, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) - | BinOp (MinusA, e1, e2, _) -> - Binop (Sub, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) - | BinOp (Mult, e1, e2, _) -> - Binop (Mul, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) - | BinOp (Div, e1, e2, _) -> - Binop (Div, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Zero) - | BinOp (Mod, e1, e2, _) -> - Binop (Mod, texpr1_expr_of_cil_exp e1, texpr1_expr_of_cil_exp e2, Int, Near) - | CastE (TInt (t_ik, _) as t, e) -> - begin match IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) - | true -> texpr1_expr_of_cil_exp e - | false - | exception Cilfacade.TypeOfError _ (* typeOf inner e, not outer exp *) - | exception Invalid_argument _ -> (* get_ikind in is_cast_injective *) - raise (Unsupported_CilExp (Cast_not_injective t)) - end - | _ -> - raise (Unsupported_CilExp Exp_not_supported) - in overflow_handling_apron no_ov ik env expr d exp; - expr - | exception (Cilfacade.TypeOfError _ as e) - | exception (Invalid_argument _ as e) -> - raise (Unsupported_CilExp (Exp_typeOf e)) - in - texpr1_expr_of_cil_exp exp - - let texpr1_expr_of_cil_exp (ask:Queries.ask) d env exp no_ov = let conv exp overflow_handling = let query e ik = @@ -255,40 +200,6 @@ struct let res = texpr1_expr_of_cil_exp ask d env e no_ov in Texpr1.of_expr env res - let tcons1_of_cil_exp_old d env e negate no_ov = - let (texpr1_plus, texpr1_minus, typ) = - match e with - | BinOp (r, e1, e2, _) -> - let texpr1_1 = texpr1_expr_of_cil_exp_old d env e1 no_ov in - let texpr1_2 = texpr1_expr_of_cil_exp_old d env e2 no_ov in - (* Apron constraints always compare with 0 and only have comparisons one way *) - begin match r with - | Lt -> (texpr1_2, texpr1_1, SUP) (* e1 < e2 ==> e2 - e1 > 0 *) - | Gt -> (texpr1_1, texpr1_2, SUP) (* e1 > e2 ==> e1 - e2 > 0 *) - | Le -> (texpr1_2, texpr1_1, SUPEQ) (* e1 <= e2 ==> e2 - e1 >= 0 *) - | Ge -> (texpr1_1, texpr1_2, SUPEQ) (* e1 >= e2 ==> e1 - e2 >= 0 *) - | Eq -> (texpr1_1, texpr1_2, EQ) (* e1 == e2 ==> e1 - e2 == 0 *) - | Ne -> (texpr1_1, texpr1_2, DISEQ) (* e1 != e2 ==> e1 - e2 != 0 *) - | _ -> raise (Unsupported_CilExp BinOp_not_supported) - end - | _ -> raise (Unsupported_CilExp Exp_not_supported) - in - let inverse_typ = function - | EQ -> DISEQ - | DISEQ -> EQ - | SUPEQ -> SUP - | SUP -> SUPEQ - | EQMOD _ -> failwith "tcons1_of_cil_exp: cannot invert EQMOD" - in - let (texpr1_plus, texpr1_minus, typ) = - if negate then - (texpr1_minus, texpr1_plus, inverse_typ typ) - else - (texpr1_plus, texpr1_minus, typ) - in - let texpr1' = Binop (Sub, texpr1_plus, texpr1_minus, Int, Near) in - Tcons1.make (Texpr1.of_expr env texpr1') typ - let tcons1_of_cil_exp ask d env e negate no_ov = let e = Cil.constFold false e in let (texpr1_plus, texpr1_minus, typ) = From 99c1be4da3849af56ae2b0bda80ee7156dea90ab Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 5 Mar 2024 14:59:19 +0100 Subject: [PATCH 227/280] find_one_var is dead code after removal of meet_tcons_one_var_eq --- src/cdomains/apron/sharedFunctions.apron.ml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 2315816168..52048682aa 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -235,12 +235,6 @@ struct let texpr1' = Binop (Sub, texpr1_plus, texpr1_minus, Int, Near) in Tcons1.make (Texpr1.of_expr env texpr1') typ - let find_one_var e = - Basetype.CilExp.get_vars e - |> List.filter Tracked.varinfo_tracked - |> function - | [v] -> Some v - | _ -> None end (** Conversion from Apron to CIL expressions. *) From 550bb0af9cdc6802a88bcf2ab3214c030027ad58 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 5 Mar 2024 15:02:55 +0100 Subject: [PATCH 228/280] constFold is literally the first thing that is done straight down the calling sequence --- src/cdomains/apron/sharedFunctions.apron.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 52048682aa..eeb13a712c 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -196,7 +196,6 @@ struct res let texpr1_of_cil_exp ask d env e no_ov = - let e = Cil.constFold false e in let res = texpr1_expr_of_cil_exp ask d env e no_ov in Texpr1.of_expr env res From e50ad8dce6c7d5190a93b852c2ae074a13767bdd Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 6 Mar 2024 13:35:25 +0100 Subject: [PATCH 229/280] Unify overflow handling for relational domains Co-authored-by: Dr. Michael Petter Co-authored-by: Michael Petter Co-authored-by: Julian Erhard --- .../apron/affineEqualityDomain.apron.ml | 2 - src/cdomains/apron/apronDomain.apron.ml | 4 +- .../apron/linearTwoVarEqualityDomain.apron.ml | 46 ++++++++-------- src/cdomains/apron/sharedFunctions.apron.ml | 52 ++++++++----------- 4 files changed, 46 insertions(+), 58 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index e6cd3355bc..dcb3e0d29d 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -151,7 +151,6 @@ struct module V = RelationDomain.V module Arg = struct let allow_global = true - let do_overflow_check = false end module Convert = SharedFunctions.Convert (V) (Bounds) (Arg) (SharedFunctions.Tracked) @@ -598,7 +597,6 @@ struct module D = D (Vc) (Mx) module ConvArg = struct let allow_global = false - let do_overflow_check = false end include SharedFunctions.AssertionModule (D.V) (D) (ConvArg) include D diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index e4016a4725..7e66e13108 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -234,7 +234,6 @@ struct module Bounds = Bounds (Man) module Arg = struct let allow_global = false - let do_overflow_check = true end module Convert = Convert (V) (Bounds) (Arg) (Tracked) @@ -709,14 +708,13 @@ struct let exps = ResettableLazy.force WideningThresholds.exps in let module Arg = struct let allow_global = true - let do_overflow_check = true end in let module Convert = SharedFunctions.Convert (V) (Bounds(Man)) (Arg) (Tracked) in (* this implements widening_threshold with Tcons1 instead of Lincons1 *) let tcons1s = List.filter_map (fun e -> let no_ov = lazy(IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp e)) in let dummyask = let f (type a) (q : a Queries.t) : a = - (* Convert.tcons1_of_cil_exp supports fancy aggressive simplifications of expressions + (* Convert.tcons1_of_cil_exp supports fancy aggressive simplifications of expressions via querying the context for int constants that replace subexpressions; we do not have a context here, so we just use a dummy ask replying top all the time *) Queries.Result.top q diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 1d3cb9e5aa..9000223318 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -38,9 +38,9 @@ module EqualitiesArray = struct let make_empty_array len = Array.init len (fun i -> (Some i, Z.zero)) - (** add new variables to domain with particular indices; translates old indices to keep consistency + (** add new variables to domain with particular indices; translates old indices to keep consistency the semantics of indexes can be retrieved from apron: https://antoinemine.github.io/Apron/doc/api/ocaml/Dim.html *) - let add_variables_to_domain m indexes = + let add_variables_to_domain m indexes = if length indexes = 0 then m else let offset_map = Array.make (Array.length m) 0 (* maps each variable to the number of variables that are added before this variable *) in @@ -49,10 +49,10 @@ module EqualitiesArray = struct match list with | hd::tl when hd = index -> shift (offset+1, tl) index | _ -> (offset, list) - in - Array.fold_lefti (* this is not a textbook fold. We rather use it as a means to iterate over the range - of all indices of offset_map, initializing the array at these indices as a side-effect. - We use fold here as a means of having an accumulator to keep track of the current offset + in + Array.fold_lefti (* this is not a textbook fold. We rather use it as a means to iterate over the range + of all indices of offset_map, initializing the array at these indices as a side-effect. + We use fold here as a means of having an accumulator to keep track of the current offset and the rest of the offset list. In case of more frequent use of this pattern, consider this as a candidate template for a new library function *) (fun offsetcontext index _ -> @@ -167,7 +167,7 @@ struct let var_dim = Environment.dim_of_var t.env x in begin match t.d with | None -> [(Z.one, Some var_dim)] - | Some d -> + | Some d -> (match d.(var_dim) with | (Some i, k) -> [(Z.one, Some i); (k, None)] | (None, k) -> [(k, None)]) @@ -186,7 +186,7 @@ struct (** convert and simplify (wrt. reference variables) a texpr into a tuple of a list of monomials and a constant *) let simplified_monomials_from_texp (t: t) texp = - BatOption.bind (monomials_from_texp t texp) + BatOption.bind (monomials_from_texp t texp) (fun monomiallist -> let d = Option.get t.d in let expr = Array.make (Environment.size t.env) Z.zero in @@ -265,7 +265,6 @@ struct module V = RelationDomain.V module Arg = struct let allow_global = true - let do_overflow_check = false end module Convert = SharedFunctions.Convert (V) (Bounds) (Arg) (SharedFunctions.Tracked) @@ -293,15 +292,15 @@ struct let show_offs o = if Z.equal o Z.zero then "" else " + " ^ Z.to_string o in let show_var i = function | (None, o) -> (lookup i) ^ " = " ^ Z.to_string o ^ ";\n" - | (Some index, o) when i <> index -> + | (Some index, o) when i <> index -> (lookup i) ^ " = " ^ lookup index ^ show_offs o ^ ";\n" | _ -> "" - in + in match varM.d with | None -> "⊥\n" | Some arr when EArray.is_top_array arr -> "⊤\n" | Some arr -> - if is_bot varM then + if is_bot varM then "Bot \n" else Array.fold_lefti (fun acc i elem -> acc ^ show_var i elem) "" arr @@ -337,13 +336,13 @@ struct let meet_with_one_conj t i e = match t.d with | None -> t - | Some d -> + | Some d -> let res_d = Array.copy d in try meet_with_one_conj_with res_d i e; - {d = Some res_d; env = t.env} - with Contradiction -> - {d = None; env = t.env} + {d = Some res_d; env = t.env} + with Contradiction -> + {d = None; env = t.env} let meet t1 t2 = let sup_env = Environment.lce t1.env t2.env in @@ -355,7 +354,7 @@ struct let res_d = Array.copy d1' in Array.iteri (meet_with_one_conj_with res_d) d2'; {d = Some res_d; env = sup_env} - with Contradiction -> + with Contradiction -> {d = None; env = sup_env} ) | _ -> {d = None; env = sup_env} @@ -418,8 +417,8 @@ struct match a.d, b.d with | None, _ -> b | _, None -> a - | Some x, Some y when is_top a || is_top b -> - let new_env = Environment.lce a.env b.env in + | Some x, Some y when is_top a || is_top b -> + let new_env = Environment.lce a.env b.env in top_of new_env | Some x, Some y when (Environment.compare a.env b.env <> 0) -> let sup_env = Environment.lce a.env b.env in @@ -455,7 +454,7 @@ struct dprintf "%s: %a not leq %a" (name ()) pretty x pretty y let forget_vars t vars = - if is_bot_env t || is_top t || List.is_empty vars then + if is_bot_env t || is_top t || List.is_empty vars then t else let m = EArray.copy @@ Option.get t.d in @@ -568,7 +567,7 @@ struct forget_vars res [var] let substitute_exp ask t var exp no_ov = - let res = substitute_exp ask t var exp no_ov in + let res = substitute_exp ask t var exp no_ov in if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %s \n exp: %a \n -> \n %s\n" (show t) (Var.to_string var) d_exp exp (show res); res @@ -663,11 +662,11 @@ struct lincons in let get_const acc i = function - | (None, o) -> + | (None, o) -> let xi = Environment.var_of_dim t.env i in of_coeff xi [(Coeff.s_of_int (-1), xi)] o :: acc | (Some r, _) when r = i -> acc - | (Some r, o) -> + | (Some r, o) -> let xi = Environment.var_of_dim t.env i in let ri = Environment.var_of_dim t.env r in of_coeff xi [(Coeff.s_of_int (-1), xi); (Coeff.s_of_int 1, ri)] o :: acc @@ -691,7 +690,6 @@ struct module D = D module ConvArg = struct let allow_global = false - let do_overflow_check = false end include SharedFunctions.AssertionModule (D.V) (D) (ConvArg) include D diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index eeb13a712c..df394869d5 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -42,7 +42,6 @@ let int_of_scalar ?round (scalar: Scalar.t) = module type ConvertArg = sig val allow_global: bool - val do_overflow_check: bool end module type SV = RelationDomain.RV with type t = Var.t @@ -81,35 +80,28 @@ struct | _ -> None (* for other exception *) ) - (** This version with an overflow check is used by the apron domains such as polyhedra and octagons. - They do the overflow handling while they convert the expression to Texpr1. *) - let overflow_handling_apron no_ov ik env expr d exp = - if not (Lazy.force no_ov) then ( - let (type_min, type_max) = IntDomain.Size.range ik in - let texpr1 = Texpr1.of_expr env expr in - match Bounds.bound_texpr d texpr1 with - | Some min, Some max when Z.compare type_min min <= 0 && Z.compare max type_max <= 0 -> () - | min_opt, max_opt -> - if M.tracing then M.trace "apron" "may overflow: %a (%a, %a)\n" CilType.Exp.pretty exp (Pretty.docOpt (IntOps.BigIntOps.pretty ())) min_opt (Pretty.docOpt (IntOps.BigIntOps.pretty ())) max_opt; - raise (Unsupported_CilExp Overflow) - ) - - (** This version without an overflow check is used by the affeq and lin2vareq domains. - They do the overflow handling before converting to Texpr1, and store the result in the "no_ov" flag. - Therefore we only check the no_ov flag here. *) - let no_ov_overflow_handling no_ov _ _ _ _ exp = + (** This still tries to establish bounds via Bounds.bound_texpr, which may be more precise in case ana.int.interval + is disabled and the relational analysis manages to evaluate a value to an interval, which can then not be represented + as the result of an EvalInt query. This is a workaround and works as long as only one relational domain is used. *) + let overflow_handling no_ov ik env expr d exp = try - if IntDomain.should_wrap (Cilfacade.get_ikind_exp exp) || - (not @@ IntDomain.should_ignore_overflow (Cilfacade.get_ikind_exp exp) - && not (Lazy.force no_ov)) then - (raise (Unsupported_CilExp Overflow)) - with Invalid_argument e -> - (* This exception is raised by Cilfacade.get_ikind_exp) when the expression + if IntDomain.should_wrap (Cilfacade.get_ikind_exp exp) || not (Lazy.force no_ov) then ( + let (type_min, type_max) = IntDomain.Size.range ik in + let texpr1 = Texpr1.of_expr env expr in + match Bounds.bound_texpr d texpr1 with + | Some min, Some max when Z.compare type_min min <= 0 && Z.compare max type_max <= 0 -> + () + | min_opt, max_opt -> + if M.tracing then M.trace "apron" "may overflow: %a (%a, %a)\n" CilType.Exp.pretty exp (Pretty.docOpt (IntOps.BigIntOps.pretty ())) min_opt (Pretty.docOpt (IntOps.BigIntOps.pretty ())) max_opt; + raise (Unsupported_CilExp Overflow) + ) + with Invalid_argument e -> + (* This exception is raised by Cilfacade.get_ikind_exp) when the expression is not an integer expression, for example if it is a float expression. *) raise (Unsupported_CilExp Exp_not_supported) let texpr1_expr_of_cil_exp (ask:Queries.ask) d env exp no_ov = - let conv exp overflow_handling = + let conv exp = let query e ik = let res = match ask.f (EvalInt e) with @@ -156,7 +148,7 @@ struct | BinOp (MinusA, e1, e2, _) -> bop_near Sub e1 e2 | BinOp (Mult, e1, e2, _) -> bop_near Mul e1 e2 | BinOp (Mod, e1, e2, _) -> bop_near Mod e1 e2 - | BinOp (Div, e1, e2, _) -> + | BinOp (Div, e1, e2, _) -> Binop (Div, texpr1 e1, texpr1 e2, Int, Zero) | CastE (TInt (t_ik, _) as t, e) -> begin match IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) @@ -190,8 +182,7 @@ struct texpr1_expr_of_cil_exp exp in let exp = Cil.constFold false exp in - let ov_handler = if Arg.do_overflow_check then overflow_handling_apron else no_ov_overflow_handling in - let res = conv exp ov_handler in + let res = conv exp in if M.tracing then M.trace "relation" "texpr1_expr_of_cil_exp: %a -> %s\n" d_plainexp exp (Format.asprintf "%a" Texpr1.print_expr res); res @@ -504,7 +495,10 @@ struct | `Top -> ID.top_of ik else match eval_interval_expr ask d e no_ov with - | (Some min, Some max) -> ID.of_interval ~suppress_ovwarn:true ik (min, max) + | (Some min, Some max) -> + let r = ID.of_interval ~suppress_ovwarn:true ik (min, max) in + M.tracel "grannie" "smith %s %s -> %a \n" (Z.to_string min) (Z.to_string max) ID.pretty r; + r | (Some min, None) -> ID.starting ~suppress_ovwarn:true ik min | (None, Some max) -> ID.ending ~suppress_ovwarn:true ik max | (None, None) -> ID.top_of ik From d44b026830449cf06f456fc2bdc4d2b251da805b Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Wed, 6 Mar 2024 13:46:38 +0100 Subject: [PATCH 230/280] unnecessary trace --- src/cdomains/apron/sharedFunctions.apron.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index df394869d5..064c512646 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -497,7 +497,6 @@ struct match eval_interval_expr ask d e no_ov with | (Some min, Some max) -> let r = ID.of_interval ~suppress_ovwarn:true ik (min, max) in - M.tracel "grannie" "smith %s %s -> %a \n" (Z.to_string min) (Z.to_string max) ID.pretty r; r | (Some min, None) -> ID.starting ~suppress_ovwarn:true ik min | (None, Some max) -> ID.ending ~suppress_ovwarn:true ik max From 0818be81cae150c56f6c95ab8578eed84f3dbed1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 8 Mar 2024 16:28:45 +0200 Subject: [PATCH 231/280] Simplify a few expressions in lin2vareq --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 4 ++-- src/cdomains/apron/sharedFunctions.apron.ml | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 9000223318..eed9b1c8ad 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -175,8 +175,8 @@ struct | Unop (Neg, e, _, _) -> negate (convert_texpr e) | Unop (Cast, e, _, _) -> convert_texpr e (* Ignore since casts in apron are used for floating point nums and rounding in contrast to CIL casts *) | Unop (Sqrt, e, _, _) -> raise NotLinearExpr - | Binop (Add, e1, e2, _, _) -> List.concat [convert_texpr e1; convert_texpr e2] - | Binop (Sub, e1, e2, _, _) -> List.concat [convert_texpr e1; negate (convert_texpr e2)] + | Binop (Add, e1, e2, _, _) -> convert_texpr e1 @ convert_texpr e2 + | Binop (Sub, e1, e2, _, _) -> convert_texpr e1 @ negate (convert_texpr e2) | Binop (Mul, e1, e2, _, _) -> multiply (convert_texpr e1) (convert_texpr e2) | Binop _ -> raise NotLinearExpr end in match convert_texpr texp with diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 064c512646..7034d156c9 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -495,9 +495,7 @@ struct | `Top -> ID.top_of ik else match eval_interval_expr ask d e no_ov with - | (Some min, Some max) -> - let r = ID.of_interval ~suppress_ovwarn:true ik (min, max) in - r + | (Some min, Some max) -> ID.of_interval ~suppress_ovwarn:true ik (min, max) | (Some min, None) -> ID.starting ~suppress_ovwarn:true ik min | (None, Some max) -> ID.ending ~suppress_ovwarn:true ik max | (None, None) -> ID.top_of ik From 678b833779074ef4198ddc22a987a0d89c8a6b12 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 8 Mar 2024 16:29:05 +0200 Subject: [PATCH 232/280] Add TODOs to lin2vareq --- src/analyses/apron/relationAnalysis.apron.ml | 7 ++++--- src/analyses/base.ml | 1 + src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 16d194642a..65c251863f 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -201,6 +201,7 @@ struct (* TODO: don't go through CIL exp? *) let e1 = BinOp (Le, Lval (Cil.var x), (Cil.kintegerCilint ik type_max), intType) in let e2 = BinOp (Ge, Lval (Cil.var x), (Cil.kintegerCilint ik type_min), intType) in + (* TODO: do not duplicate no_overflow defined via ask: https://github.com/goblint/analyzer/pull/1297#discussion_r1477281950 *) let rel = RD.assert_inv ask rel e1 false (no_overflow ask e1) in (* TODO: how can be overflow when asserting type bounds? *) let rel = RD.assert_inv ask rel e2 false (no_overflow ask e2) in rel @@ -706,10 +707,10 @@ struct let vars = Basetype.CilExp.get_vars e |> List.unique ~eq:CilType.Varinfo.equal |> List.filter RD.Tracked.varinfo_tracked in let rel = RD.forget_vars rel (List.map RV.local vars) in (* havoc *) let rel = List.fold_left (assert_type_bounds ask) rel vars in (* add type bounds to avoid overflow in top state *) - let rec dummyask = - (* assert_inv calls texpr1_expr_of_cil_exp, which simplifies the constraint based on the pre-state of the transition; + let rec dummyask = + (* assert_inv calls texpr1_expr_of_cil_exp, which simplifies the constraint based on the pre-state of the transition; this does not reflect the state after RD.forget_vars rel .... has been performed; to prevent this aggressive - simplification, we restrict read_int queries to a local dummy ask, that only dispatches to rel instead of the + simplification, we restrict read_int queries to a local dummy ask, that only dispatches to rel instead of the full state *) let f (type a) (q : a Queries.t) : a = let eval_int e no_ov = diff --git a/src/analyses/base.ml b/src/analyses/base.ml index d402546d2d..fc6735be49 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1259,6 +1259,7 @@ struct For now we return true if the expression contains a shift left. *) + (* TODO: deduplicate https://github.com/goblint/analyzer/pull/1297#discussion_r1477804502 *) let rec exp_may_signed_overflow ctx exp = let res = match Cilfacade.get_ikind_exp exp with | exception _ -> BoolDomain.MayBool.top () diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index eed9b1c8ad..0e47f3a102 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -32,7 +32,7 @@ module EqualitiesArray = struct let show m = Array.fold_right (fun k result -> Equality.show k ^ "\n" ^ result) m "" - let hash : t -> int = Array.fold_left (fun acc a -> 31 * acc + Equality.hash a) 0 + let hash : t -> int = Array.fold_left (fun acc a -> 31 * acc + Equality.hash a) 0 (* TODO: derive *) let empty () = [||] @@ -50,6 +50,7 @@ module EqualitiesArray = struct | hd::tl when hd = index -> shift (offset+1, tl) index | _ -> (offset, list) in + (* TODO: https://github.com/goblint/analyzer/pull/1297#discussion_r1479505621 *) Array.fold_lefti (* this is not a textbook fold. We rather use it as a means to iterate over the range of all indices of offset_map, initializing the array at these indices as a side-effect. We use fold here as a means of having an accumulator to keep track of the current offset @@ -561,6 +562,7 @@ struct res (* This is supposed to the be the backwards transformer for assign, not sure this is correct *) + (* TODO: https://github.com/goblint/analyzer/pull/1297#discussion_r1484783039 *) let substitute_exp ask t var exp no_ov = let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in let res = assign_exp ask t var exp no_ov in From bdc62e79d7035a83df462bbb5eb8f3327b2ee1c2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 8 Mar 2024 16:29:14 +0200 Subject: [PATCH 233/280] Sort Goblint_std alphabetically --- src/util/std/goblint_std.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/std/goblint_std.ml b/src/util/std/goblint_std.ml index c8f3144880..5b623ead30 100644 --- a/src/util/std/goblint_std.ml +++ b/src/util/std/goblint_std.ml @@ -4,6 +4,7 @@ OCaml standard library extensions which are not provided by {!Batteries}. *) +module GobArray = GobArray module GobGc = GobGc module GobHashtbl = GobHashtbl module GobList = GobList @@ -12,7 +13,6 @@ module GobResult = GobResult module GobOption = GobOption module GobSys = GobSys module GobUnix = GobUnix -module GobArray = GobArray (** {1 Other libraries} From 8908b7ad39962b8cdb8d9d6a799b67b4740fa898 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Mon, 11 Mar 2024 14:37:44 +0100 Subject: [PATCH 234/280] implemented change requests; more docs and some refactoring --- src/cdomains/apron/sharedFunctions.apron.ml | 30 ++++++++++++--------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 7034d156c9..8470a2fffa 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -82,10 +82,15 @@ struct (** This still tries to establish bounds via Bounds.bound_texpr, which may be more precise in case ana.int.interval is disabled and the relational analysis manages to evaluate a value to an interval, which can then not be represented - as the result of an EvalInt query. This is a workaround and works as long as only one relational domain is used. *) + as the result of an EvalInt query. This is a workaround and works as long as only one relational domain is used. + With multiple domains and disabled interval domain, the queries will not be able to exchange interval information, + and each analysis will only be able to establish constant bounds, but only its own interval bounds and not interval bounds + established by other analyses.*) let overflow_handling no_ov ik env expr d exp = - try - if IntDomain.should_wrap (Cilfacade.get_ikind_exp exp) || not (Lazy.force no_ov) then ( + match Cilfacade.get_ikind_exp exp with + | exception Invalid_argument e -> raise (Unsupported_CilExp Exp_not_supported) (* expression is not an integer expression, i.e. float *) + | ik -> + if IntDomain.should_wrap ik || not (Lazy.force no_ov) then ( let (type_min, type_max) = IntDomain.Size.range ik in let texpr1 = Texpr1.of_expr env expr in match Bounds.bound_texpr d texpr1 with @@ -95,10 +100,6 @@ struct if M.tracing then M.trace "apron" "may overflow: %a (%a, %a)\n" CilType.Exp.pretty exp (Pretty.docOpt (IntOps.BigIntOps.pretty ())) min_opt (Pretty.docOpt (IntOps.BigIntOps.pretty ())) max_opt; raise (Unsupported_CilExp Overflow) ) - with Invalid_argument e -> - (* This exception is raised by Cilfacade.get_ikind_exp) when the expression - is not an integer expression, for example if it is a float expression. *) - raise (Unsupported_CilExp Exp_not_supported) let texpr1_expr_of_cil_exp (ask:Queries.ask) d env exp no_ov = let conv exp = @@ -133,6 +134,9 @@ struct match Cilfacade.get_ikind_exp exp with | ik -> let expr = + (** simplify asks for a constant value of some subexpression e, similar to a constant fold. In particular but not exclusively + this query is answered by the 2 var equalities domain itself. This normalizes arbitrary expressions to a point where they + might be able to be represented by means of 2 var equalities *) let simplify e = let ikind = try (Cilfacade.get_ikind_exp e) with Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) in let simp = query e ikind in @@ -152,15 +156,18 @@ struct Binop (Div, texpr1 e1, texpr1 e2, Int, Zero) | CastE (TInt (t_ik, _) as t, e) -> begin match IntDomain.Size.is_cast_injective ~from_type:(Cilfacade.typeOf e) ~to_type:t with (* TODO: unnecessary cast check due to overflow check below? or maybe useful in general to also assume type bounds based on argument types? *) - | exception _ -> raise (Unsupported_CilExp (Cast_not_injective t)) + | exception Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) | true -> texpr1 e - | false -> + | false -> (* Cast is not injective - we now try to establish suitable ranges manually *) + (* try to evaluate e by EvalInt Query *) let res = try (query e @@ Cilfacade.get_ikind_exp e) with Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) in + (* convert response to a constant *) let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to t_ik res in match const with - | Some c -> Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z c))) + | Some c -> Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z c))) (* Got a constant value -> use it straight away *) + (* I gotten top, we can not guarantee injectivity *) | None -> if IntDomain.IntDomTuple.is_top_of t_ik res then raise (Unsupported_CilExp (Cast_not_injective t)) - else ( + else ( (* Got a ranged value different from top, so let's check bounds manually *) let (ik_min, ik_max) = IntDomain.Size.range t_ik in match IntDomain.IntDomTuple.minimal res, IntDomain.IntDomTuple.maximal res with | Some min, Some max when min >= ik_min && max <= ik_max -> texpr1_expr_of_cil_exp e @@ -178,7 +185,6 @@ struct | exception (Invalid_argument _ as e) -> raise (Unsupported_CilExp (Exp_typeOf e)) in - (* only if we are sure that no overflow / undefined behavior happens we convert the expression *) texpr1_expr_of_cil_exp exp in let exp = Cil.constFold false exp in From 96ccd4af200dfa3a7182b7a5a6114e2d805b0125 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 11 Mar 2024 16:38:02 +0100 Subject: [PATCH 235/280] Add soundness check for unsigned overflows --- tests/regression/77-lin2vareq/31-careful.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/regression/77-lin2vareq/31-careful.c diff --git a/tests/regression/77-lin2vareq/31-careful.c b/tests/regression/77-lin2vareq/31-careful.c new file mode 100644 index 0000000000..dec0525cf0 --- /dev/null +++ b/tests/regression/77-lin2vareq/31-careful.c @@ -0,0 +1,22 @@ +// SKIP PARAM: --set ana.activated[+] lin2vareq --enable ana.int.interval +#include +#include + +int main(void) { + int top; + unsigned int i; + unsigned int j; + + if(top) { + i = 3; + j = i + UINT_MAX; + } else { + i = 2; + j = i + UINT_MAX; + } + + + // Both hold in the concrete + __goblint_check(j == i-1); //UNKNOWN + __goblint_check(j == i + UINT_MAX); //UNKNOWN +} From 924a70d542a5014896355adc26b696b3eb3cf011 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 11:06:28 +0200 Subject: [PATCH 236/280] Automatically add newline to trace output --- src/util/tracing/goblint_tracing.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/tracing/goblint_tracing.ml b/src/util/tracing/goblint_tracing.ml index 7881306217..122ebeb325 100644 --- a/src/util/tracing/goblint_tracing.ml +++ b/src/util/tracing/goblint_tracing.ml @@ -39,7 +39,7 @@ let traceTag (sys : string) : Pretty.doc = (text ((ind !indent_level) ^ "%%% " ^ sys ^ ": ")) let printtrace sys d: unit = - fprint stderr ~width:max_int ((traceTag sys) ++ d); + fprint stderr ~width:max_int ((traceTag sys) ++ d ++ line); flush stderr let gtrace always f sys var ?loc do_subsys fmt = From c908e2937bfaa2fe3f9bc2935d24fc47b7daadae Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 11:06:49 +0200 Subject: [PATCH 237/280] Remove newlines from trace calls --- src/analyses/accessAnalysis.ml | 10 +- src/analyses/apron/relationAnalysis.apron.ml | 40 +++--- src/analyses/apron/relationPriv.apron.ml | 118 +++++++-------- src/analyses/base.ml | 136 +++++++++--------- src/analyses/baseInvariant.ml | 66 ++++----- src/analyses/basePriv.ml | 118 +++++++-------- src/analyses/condVars.ml | 2 +- src/analyses/mCP.ml | 14 +- src/analyses/symbLocks.ml | 8 +- src/analyses/taintPartialContexts.ml | 4 +- src/analyses/threadEscape.ml | 6 +- src/analyses/tmpSpecial.ml | 8 +- src/analyses/varEq.ml | 10 +- src/cdomain/value/cdomains/addressDomain.ml | 20 +-- src/cdomain/value/cdomains/arrayDomain.ml | 2 +- src/cdomain/value/cdomains/floatDomain.ml | 14 +- src/cdomain/value/cdomains/intDomain.ml | 38 ++--- src/cdomain/value/cdomains/offset.ml | 2 +- src/cdomain/value/cdomains/structDomain.ml | 18 +-- src/cdomain/value/cdomains/valueDomain.ml | 8 +- .../apron/affineEqualityDomain.apron.ml | 22 +-- src/cdomains/apron/apronDomain.apron.ml | 40 +++--- src/cdomains/apron/sharedFunctions.apron.ml | 4 +- src/cdomains/symbLocksDomain.ml | 2 +- src/common/framework/cfgTools.ml | 24 ++-- src/config/gobConfig.ml | 6 +- src/domains/access.ml | 20 +-- src/framework/constraints.ml | 34 ++--- src/framework/control.ml | 12 +- src/incremental/compareAST.ml | 10 +- src/solver/postSolver.ml | 6 +- src/solver/td3.ml | 6 +- src/witness/argTools.ml | 2 +- src/witness/witness.ml | 8 +- src/witness/witnessConstraints.ml | 14 +- 35 files changed, 426 insertions(+), 426 deletions(-) diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index efad8b4c2e..dc435828fe 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -32,7 +32,7 @@ struct emit_single_threaded := List.mem (ModifiedSinceSetjmp.Spec.name ()) activated || List.mem (PoisonVariables.Spec.name ()) activated let do_access (ctx: (D.t, G.t, C.t, V.t) ctx) (kind:AccessKind.t) (reach:bool) (e:exp) = - if M.tracing then M.trace "access" "do_access %a %a %B\n" d_exp e AccessKind.pretty kind reach; + if M.tracing then M.trace "access" "do_access %a %a %B" d_exp e AccessKind.pretty kind reach; let reach_or_mpt: _ Queries.t = if reach then ReachableFrom e else MayPointTo e in let ad = ctx.ask reach_or_mpt in ctx.emit (Access {exp=e; ad; kind; reach}) @@ -42,15 +42,15 @@ struct + [deref=true], [reach=false] - Access [exp] by dereferencing once (may-point-to), used for lval writes and shallow special accesses. + [deref=true], [reach=true] - Access [exp] by dereferencing transitively (reachable), used for deep special accesses. *) let access_one_top ?(force=false) ?(deref=false) ctx (kind: AccessKind.t) reach exp = - if M.tracing then M.traceli "access" "access_one_top %a (kind = %a, reach = %B, deref = %B)\n" CilType.Exp.pretty exp AccessKind.pretty kind reach deref; + if M.tracing then M.traceli "access" "access_one_top %a (kind = %a, reach = %B, deref = %B)" CilType.Exp.pretty exp AccessKind.pretty kind reach deref; if force || !collect_local || !emit_single_threaded || ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) then ( if deref && Cil.isPointerType (Cilfacade.typeOf exp) then (* avoid dereferencing integers to unknown pointers, which cause many spurious type-based accesses *) do_access ctx kind reach exp; - if M.tracing then M.tracei "access" "distribute_access_exp\n"; + if M.tracing then M.tracei "access" "distribute_access_exp"; Access.distribute_access_exp (do_access ctx Read false) exp; - if M.tracing then M.traceu "access" "distribute_access_exp\n"; + if M.tracing then M.traceu "access" "distribute_access_exp"; ); - if M.tracing then M.traceu "access" "access_one_top\n" + if M.tracing then M.traceu "access" "access_one_top" (** We just lift start state, global and dependency functions: *) let startstate v = () diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 3913e7218e..675530d1f3 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -87,7 +87,7 @@ struct let e' = visitCilExpr visitor e in let rel = RD.add_vars st.rel (List.map RV.local (VH.values v_ins |> List.of_enum)) in (* add temporary g#in-s *) let rel' = VH.fold (fun v v_in rel -> - if M.tracing then M.trace "relation" "read_global %a %a\n" CilType.Varinfo.pretty v CilType.Varinfo.pretty v_in; + if M.tracing then M.trace "relation" "read_global %a %a" CilType.Varinfo.pretty v CilType.Varinfo.pretty v_in; read_global ask getg {st with rel} v v_in (* g#in = g; *) ) v_ins rel in @@ -121,7 +121,7 @@ struct let assign_from_globals_wrapper ask getg st e f = let (rel', e', v_ins) = read_globals_to_locals ask getg st e in - if M.tracing then M.trace "relation" "assign_from_globals_wrapper %a\n" d_exp e'; + if M.tracing then M.trace "relation" "assign_from_globals_wrapper %a" d_exp e'; let rel' = f rel' e' in (* x = e; *) let rel'' = RD.remove_vars rel' (List.map RV.local (VH.values v_ins |> List.of_enum)) in (* remove temporary g#in-s *) rel'' @@ -152,7 +152,7 @@ struct v_out.vattr <- v.vattr; (*copy the attributes because the tracking may depend on them. Otherwise an assertion fails *) let st = {st with rel = RD.add_vars st.rel [RV.local v_out]} in (* add temporary g#out *) let st' = {st with rel = f st v_out} in (* g#out = e; *) - if M.tracing then M.trace "relation" "write_global %a %a\n" CilType.Varinfo.pretty v CilType.Varinfo.pretty v_out; + if M.tracing then M.trace "relation" "write_global %a %a" CilType.Varinfo.pretty v CilType.Varinfo.pretty v_out; let st' = write_global ask getg sideg st' v v_out in (* g = g#out; *) let rel'' = RD.remove_vars st'.rel [RV.local v_out] in (* remove temporary g#out *) {st' with rel = rel''} @@ -189,7 +189,7 @@ struct let no_overflow ctx exp = lazy ( let res = no_overflow ctx exp in - if M.tracing then M.tracel "no_ov" "no_ov %b exp: %a\n" res d_exp exp; + if M.tracing then M.tracel "no_ov" "no_ov %b exp: %a" res d_exp exp; res ) @@ -250,19 +250,19 @@ struct st (* ignore extern inits because there's no body before assign, so env is empty... *) else ( let simplified_e = replace_deref_exps ctx.ask e in - if M.tracing then M.traceli "relation" "assign %a = %a (simplified to %a)\n" d_lval lv d_exp e d_exp simplified_e; + if M.tracing then M.traceli "relation" "assign %a = %a (simplified to %a)" d_lval lv d_exp e d_exp simplified_e; let ask = Analyses.ask_of_ctx ctx in let r = assign_to_global_wrapper ask ctx.global ctx.sideg st lv (fun st v -> assign_from_globals_wrapper ask ctx.global st simplified_e (fun apr' e' -> - if M.tracing then M.traceli "relation" "assign inner %a = %a (%a)\n" CilType.Varinfo.pretty v d_exp e' d_plainexp e'; - if M.tracing then M.trace "relation" "st: %a\n" RD.pretty apr'; + if M.tracing then M.traceli "relation" "assign inner %a = %a (%a)" CilType.Varinfo.pretty v d_exp e' d_plainexp e'; + if M.tracing then M.trace "relation" "st: %a" RD.pretty apr'; let r = RD.assign_exp apr' (RV.local v) e' (no_overflow ask simplified_e) in - if M.tracing then M.traceu "relation" "-> %a\n" RD.pretty r; + if M.tracing then M.traceu "relation" "-> %a" RD.pretty r; r ) ) in - if M.tracing then M.traceu "relation" "-> %a\n" D.pretty r; + if M.tracing then M.traceu "relation" "-> %a" D.pretty r; r ) @@ -334,7 +334,7 @@ struct | Some (Arg _) when not (List.mem_cmp Apron.Var.compare var arg_vars) -> true (* remove caller args, but keep just added args *) | _ -> false (* keep everything else (just added args, globals, global privs) *) ); - if M.tracing then M.tracel "combine" "relation enter newd: %a\n" RD.pretty new_rel; + if M.tracing then M.tracel "combine" "relation enter newd: %a" RD.pretty new_rel; new_rel let enter ctx r f args = @@ -391,11 +391,11 @@ struct let st = ctx.local in let reachable_from_args = reachable_from_args ctx args in let fundec = Node.find_fundec ctx.node in - if M.tracing then M.tracel "combine" "relation f: %a\n" CilType.Varinfo.pretty f.svar; - if M.tracing then M.tracel "combine" "relation formals: %a\n" (d_list "," CilType.Varinfo.pretty) f.sformals; - if M.tracing then M.tracel "combine" "relation args: %a\n" (d_list "," d_exp) args; - if M.tracing then M.tracel "combine" "relation st: %a\n" D.pretty st; - if M.tracing then M.tracel "combine" "relation fun_st: %a\n" D.pretty fun_st; + if M.tracing then M.tracel "combine" "relation f: %a" CilType.Varinfo.pretty f.svar; + if M.tracing then M.tracel "combine" "relation formals: %a" (d_list "," CilType.Varinfo.pretty) f.sformals; + if M.tracing then M.tracel "combine" "relation args: %a" (d_list "," d_exp) args; + if M.tracing then M.tracel "combine" "relation st: %a" D.pretty st; + if M.tracing then M.tracel "combine" "relation fun_st: %a" D.pretty fun_st; let new_fun_rel = RD.add_vars fun_st.rel (RD.vars st.rel) in let arg_substitutes = let filter_actuals (x,e) = @@ -419,7 +419,7 @@ struct in let any_local_reachable = any_local_reachable fundec reachable_from_args in let arg_vars = f.sformals |> List.filter (RD.Tracked.varinfo_tracked) |> List.map RV.arg in - if M.tracing then M.tracel "combine" "relation remove vars: %a\n" (docList (fun v -> Pretty.text (Apron.Var.to_string v))) arg_vars; + if M.tracing then M.tracel "combine" "relation remove vars: %a" (docList (fun v -> Pretty.text (Apron.Var.to_string v))) arg_vars; RD.remove_vars_with new_fun_rel arg_vars; (* fine to remove arg vars that also exist in caller because unify from new_rel adds them back with proper constraints *) let tainted = f_ask.f Queries.MayBeTainted in let tainted_vars = TaintPartialContexts.conv_varset tainted in @@ -433,7 +433,7 @@ struct ) in let unify_rel = RD.unify new_rel new_fun_rel in (* TODO: unify_with *) - if M.tracing then M.tracel "combine" "relation unifying %a %a = %a\n" RD.pretty new_rel RD.pretty new_fun_rel RD.pretty unify_rel; + if M.tracing then M.tracel "combine" "relation unifying %a %a = %a" RD.pretty new_rel RD.pretty new_fun_rel RD.pretty unify_rel; {fun_st with rel = unify_rel} let combine_assign ctx r fe f args fc fun_st (f_ask : Queries.ask) = @@ -647,10 +647,10 @@ struct in match q with | EvalInt e -> - if M.tracing then M.traceli "evalint" "relation query %a (%a)\n" d_exp e d_plainexp e; - if M.tracing then M.trace "evalint" "relation st: %a\n" D.pretty ctx.local; + if M.tracing then M.traceli "evalint" "relation query %a (%a)" d_exp e d_plainexp e; + if M.tracing then M.trace "evalint" "relation st: %a" D.pretty ctx.local; let r = eval_int e (lazy(no_overflow ctx e)) in - if M.tracing then M.traceu "evalint" "relation query %a -> %a\n" d_exp e ID.pretty r; + if M.tracing then M.traceu "evalint" "relation query %a -> %a" d_exp e ID.pretty r; r | Queries.IterSysVars (vq, vf) -> let vf' x = vf (Obj.repr x) in diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 5e78a02153..66548c117c 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -812,20 +812,20 @@ struct let lock_get_m oct local_m get_m = let joined = LRD.join local_m get_m in - if M.tracing then M.traceli "relationpriv" "lock_get_m:\n get=%a\n joined=%a\n" LRD.pretty get_m LRD.pretty joined; + if M.tracing then M.traceli "relationpriv" "lock_get_m:\n get=%a\n joined=%a" LRD.pretty get_m LRD.pretty joined; let r = LRD.fold (fun _ -> RD.meet) joined (RD.top ()) in - if M.tracing then M.trace "relationpriv" "meet=%a\n" RD.pretty r; + if M.tracing then M.trace "relationpriv" "meet=%a" RD.pretty r; let r = RD.meet oct r in - if M.tracing then M.traceu "relationpriv" "-> %a\n" RD.pretty r; + if M.tracing then M.traceu "relationpriv" "-> %a" RD.pretty r; r let lock oct local_m get_m = - if M.tracing then M.traceli "relationpriv" "cluster lock: local=%a\n" LRD.pretty local_m; + if M.tracing then M.traceli "relationpriv" "cluster lock: local=%a" LRD.pretty local_m; let r = lock_get_m oct local_m get_m in (* is_bot check commented out because it's unnecessarily expensive *) (* if RD.is_bot_env r then failwith "DownwardClosedCluster.lock: not downward closed?"; *) - if M.tracing then M.traceu "relationpriv" "-> %a\n" RD.pretty r; + if M.tracing then M.traceu "relationpriv" "-> %a" RD.pretty r; r let unlock w oct_side = @@ -903,7 +903,7 @@ struct (lad, lad_weak) let lock oct (local_m, _) (get_m, get_m') = - if M.tracing then M.traceli "relationpriv" "cluster lock: local=%a\n" LRD1.pretty local_m; + if M.tracing then M.traceli "relationpriv" "cluster lock: local=%a" LRD1.pretty local_m; let r = let locked = DCCluster.lock_get_m oct local_m get_m in if RD.is_bot_env locked then ( @@ -916,7 +916,7 @@ struct else locked in - if M.tracing then M.traceu "relationpriv" "-> %a\n" RD.pretty r; + if M.tracing then M.traceu "relationpriv" "-> %a" RD.pretty r; r let unlock w oct_side = @@ -954,17 +954,17 @@ struct let get_m_with_mutex_inits inits ask getg m = let get_m = get_relevant_writes ask m (G.mutex @@ getg (V.mutex m)) in - if M.tracing then M.traceli "relationpriv" "get_m_with_mutex_inits %a\n get=%a\n" LockDomain.Addr.pretty m LRD.pretty get_m; + if M.tracing then M.traceli "relationpriv" "get_m_with_mutex_inits %a\n get=%a" LockDomain.Addr.pretty m LRD.pretty get_m; let r = if not inits then get_m else let get_mutex_inits = merge_all @@ G.mutex @@ getg V.mutex_inits in let get_mutex_inits' = Cluster.keep_only_protected_globals ask m get_mutex_inits in - if M.tracing then M.trace "relationpriv" "inits=%a\n inits'=%a\n" LRD.pretty get_mutex_inits LRD.pretty get_mutex_inits'; + if M.tracing then M.trace "relationpriv" "inits=%a\n inits'=%a" LRD.pretty get_mutex_inits LRD.pretty get_mutex_inits'; LRD.join get_m get_mutex_inits' in - if M.tracing then M.traceu "relationpriv" "-> %a\n" LRD.pretty r; + if M.tracing then M.traceu "relationpriv" "-> %a" LRD.pretty r; r let atomic_mutex = LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var @@ -979,17 +979,17 @@ struct else get_relevant_writes_nofilter ask @@ G.mutex @@ getg (V.global g) in - if M.tracing then M.traceli "relationpriv" "get_mutex_global_g_with_mutex_inits %a\n get=%a\n" CilType.Varinfo.pretty g LRD.pretty get_mutex_global_g; + if M.tracing then M.traceli "relationpriv" "get_mutex_global_g_with_mutex_inits %a\n get=%a" CilType.Varinfo.pretty g LRD.pretty get_mutex_global_g; let r = if not inits then get_mutex_global_g else let get_mutex_inits = merge_all @@ G.mutex @@ getg V.mutex_inits in let get_mutex_inits' = Cluster.keep_global g get_mutex_inits in - if M.tracing then M.trace "relationpriv" "inits=%a\n inits'=%a\n" LRD.pretty get_mutex_inits LRD.pretty get_mutex_inits'; + if M.tracing then M.trace "relationpriv" "inits=%a\n inits'=%a" LRD.pretty get_mutex_inits LRD.pretty get_mutex_inits'; LRD.join get_mutex_global_g get_mutex_inits' in - if M.tracing then M.traceu "relationpriv" "-> %a\n" LRD.pretty r; + if M.tracing then M.traceu "relationpriv" "-> %a" LRD.pretty r; r let get_mutex_global_g_with_mutex_inits_atomic inits ask getg = @@ -1253,147 +1253,147 @@ struct module RelComponents = RelationDomain.RelComponents (RD) (D) let read_global ask getg st g x = - if M.tracing then M.traceli "relationpriv" "read_global %a %a\n" CilType.Varinfo.pretty g CilType.Varinfo.pretty x; - if M.tracing then M.trace "relationpriv" "st: %a\n" RelComponents.pretty st; + if M.tracing then M.traceli "relationpriv" "read_global %a %a" CilType.Varinfo.pretty g CilType.Varinfo.pretty x; + if M.tracing then M.trace "relationpriv" "st: %a" RelComponents.pretty st; let getg x = let r = getg x in - if M.tracing then M.trace "relationpriv" "getg %a -> %a\n" V.pretty x G.pretty r; + if M.tracing then M.trace "relationpriv" "getg %a -> %a" V.pretty x G.pretty r; r in let r = Priv.read_global ask getg st g x in - if M.tracing then M.traceu "relationpriv" "-> %a\n" RD.pretty r; + if M.tracing then M.traceu "relationpriv" "-> %a" RD.pretty r; r let write_global ?invariant ask getg sideg st g x = - if M.tracing then M.traceli "relationpriv" "write_global %a %a\n" CilType.Varinfo.pretty g CilType.Varinfo.pretty x; - if M.tracing then M.trace "relationpriv" "st: %a\n" RelComponents.pretty st; + if M.tracing then M.traceli "relationpriv" "write_global %a %a" CilType.Varinfo.pretty g CilType.Varinfo.pretty x; + if M.tracing then M.trace "relationpriv" "st: %a" RelComponents.pretty st; let getg x = let r = getg x in - if M.tracing then M.trace "relationpriv" "getg %a -> %a\n" V.pretty x G.pretty r; + if M.tracing then M.trace "relationpriv" "getg %a -> %a" V.pretty x G.pretty r; r in let sideg x v = - if M.tracing then M.trace "relationpriv" "sideg %a %a\n" V.pretty x G.pretty v; + if M.tracing then M.trace "relationpriv" "sideg %a %a" V.pretty x G.pretty v; sideg x v in let r = write_global ?invariant ask getg sideg st g x in - if M.tracing then M.traceu "relationpriv" "-> %a\n" RelComponents.pretty r; + if M.tracing then M.traceu "relationpriv" "-> %a" RelComponents.pretty r; r let lock ask getg st m = - if M.tracing then M.traceli "relationpriv" "lock %a\n" LockDomain.Addr.pretty m; - if M.tracing then M.trace "relationpriv" "st: %a\n" RelComponents.pretty st; + if M.tracing then M.traceli "relationpriv" "lock %a" LockDomain.Addr.pretty m; + if M.tracing then M.trace "relationpriv" "st: %a" RelComponents.pretty st; let getg x = let r = getg x in - if M.tracing then M.trace "relationpriv" "getg %a -> %a\n" V.pretty x G.pretty r; + if M.tracing then M.trace "relationpriv" "getg %a -> %a" V.pretty x G.pretty r; r in let r = lock ask getg st m in - if M.tracing then M.traceu "relationpriv" "-> %a\n" RelComponents.pretty r; + if M.tracing then M.traceu "relationpriv" "-> %a" RelComponents.pretty r; r let unlock ask getg sideg st m = - if M.tracing then M.traceli "relationpriv" "unlock %a\n" LockDomain.Addr.pretty m; - if M.tracing then M.trace "relationpriv" "st: %a\n" RelComponents.pretty st; + if M.tracing then M.traceli "relationpriv" "unlock %a" LockDomain.Addr.pretty m; + if M.tracing then M.trace "relationpriv" "st: %a" RelComponents.pretty st; let getg x = let r = getg x in - if M.tracing then M.trace "relationpriv" "getg %a -> %a\n" V.pretty x G.pretty r; + if M.tracing then M.trace "relationpriv" "getg %a -> %a" V.pretty x G.pretty r; r in let sideg x v = - if M.tracing then M.trace "relationpriv" "sideg %a %a\n" V.pretty x G.pretty v; + if M.tracing then M.trace "relationpriv" "sideg %a %a" V.pretty x G.pretty v; sideg x v in let r = unlock ask getg sideg st m in - if M.tracing then M.traceu "relationpriv" "-> %a\n" RelComponents.pretty r; + if M.tracing then M.traceu "relationpriv" "-> %a" RelComponents.pretty r; r let enter_multithreaded ask getg sideg st = - if M.tracing then M.traceli "relationpriv" "enter_multithreaded\n"; - if M.tracing then M.trace "relationpriv" "st: %a\n" RelComponents.pretty st; + if M.tracing then M.traceli "relationpriv" "enter_multithreaded"; + if M.tracing then M.trace "relationpriv" "st: %a" RelComponents.pretty st; let getg x = let r = getg x in - if M.tracing then M.trace "relationpriv" "getg %a -> %a\n" V.pretty x G.pretty r; + if M.tracing then M.trace "relationpriv" "getg %a -> %a" V.pretty x G.pretty r; r in let sideg x v = - if M.tracing then M.trace "relationpriv" "sideg %a %a\n" V.pretty x G.pretty v; + if M.tracing then M.trace "relationpriv" "sideg %a %a" V.pretty x G.pretty v; sideg x v in let r = enter_multithreaded ask getg sideg st in - if M.tracing then M.traceu "relationpriv" "-> %a\n" RelComponents.pretty r; + if M.tracing then M.traceu "relationpriv" "-> %a" RelComponents.pretty r; r let threadenter ask getg st = - if M.tracing then M.traceli "relationpriv" "threadenter\n"; - if M.tracing then M.trace "relationpriv" "st: %a\n" RelComponents.pretty st; + if M.tracing then M.traceli "relationpriv" "threadenter"; + if M.tracing then M.trace "relationpriv" "st: %a" RelComponents.pretty st; let getg x = let r = getg x in - if M.tracing then M.trace "relationpriv" "getg %a -> %a\n" V.pretty x G.pretty r; + if M.tracing then M.trace "relationpriv" "getg %a -> %a" V.pretty x G.pretty r; r in let r = threadenter ask getg st in - if M.tracing then M.traceu "relationpriv" "-> %a\n" RelComponents.pretty r; + if M.tracing then M.traceu "relationpriv" "-> %a" RelComponents.pretty r; r let sync ask getg sideg st reason = - if M.tracing then M.traceli "relationpriv" "sync\n"; - if M.tracing then M.trace "relationpriv" "st: %a\n" RelComponents.pretty st; + if M.tracing then M.traceli "relationpriv" "sync"; + if M.tracing then M.trace "relationpriv" "st: %a" RelComponents.pretty st; let getg x = let r = getg x in - if M.tracing then M.trace "relationpriv" "getg %a -> %a\n" V.pretty x G.pretty r; + if M.tracing then M.trace "relationpriv" "getg %a -> %a" V.pretty x G.pretty r; r in let sideg x v = - if M.tracing then M.trace "relationpriv" "sideg %a %a\n" V.pretty x G.pretty v; + if M.tracing then M.trace "relationpriv" "sideg %a %a" V.pretty x G.pretty v; sideg x v in let r = sync ask getg sideg st reason in - if M.tracing then M.traceu "relationpriv" "-> %a\n" RelComponents.pretty r; + if M.tracing then M.traceu "relationpriv" "-> %a" RelComponents.pretty r; r let escape node ask getg sideg st vs = - if M.tracing then M.traceli "relationpriv" "escape\n"; - if M.tracing then M.trace "relationpriv" "st: %a\n" RelComponents.pretty st; + if M.tracing then M.traceli "relationpriv" "escape"; + if M.tracing then M.trace "relationpriv" "st: %a" RelComponents.pretty st; let getg x = let r = getg x in - if M.tracing then M.trace "relationpriv" "getg %a -> %a\n" V.pretty x G.pretty r; + if M.tracing then M.trace "relationpriv" "getg %a -> %a" V.pretty x G.pretty r; r in let sideg x v = - if M.tracing then M.trace "relationpriv" "sideg %a %a\n" V.pretty x G.pretty v; + if M.tracing then M.trace "relationpriv" "sideg %a %a" V.pretty x G.pretty v; sideg x v in let r = escape node ask getg sideg st vs in - if M.tracing then M.traceu "relationpriv" "-> %a\n" RelComponents.pretty r; + if M.tracing then M.traceu "relationpriv" "-> %a" RelComponents.pretty r; r let thread_join ?force ask getg e st = - if M.tracing then M.traceli "relationpriv" "thread_join\n"; - if M.tracing then M.trace "relationpriv" "st: %a\n" RelComponents.pretty st; + if M.tracing then M.traceli "relationpriv" "thread_join"; + if M.tracing then M.trace "relationpriv" "st: %a" RelComponents.pretty st; let getg x = let r = getg x in - if M.tracing then M.trace "relationpriv" "getg %a -> %a\n" V.pretty x G.pretty r; + if M.tracing then M.trace "relationpriv" "getg %a -> %a" V.pretty x G.pretty r; r in let r = thread_join ?force ask getg e st in - if M.tracing then M.traceu "relationpriv" "-> %a\n" RelComponents.pretty r; + if M.tracing then M.traceu "relationpriv" "-> %a" RelComponents.pretty r; r let thread_return ask getg sideg tid st = - if M.tracing then M.traceli "relationpriv" "thread_return\n"; - if M.tracing then M.trace "relationpriv" "st: %a\n" RelComponents.pretty st; + if M.tracing then M.traceli "relationpriv" "thread_return"; + if M.tracing then M.trace "relationpriv" "st: %a" RelComponents.pretty st; let getg x = let r = getg x in - if M.tracing then M.trace "relationpriv" "getg %a -> %a\n" V.pretty x G.pretty r; + if M.tracing then M.trace "relationpriv" "getg %a -> %a" V.pretty x G.pretty r; r in let sideg x v = - if M.tracing then M.trace "relationpriv" "sideg %a %a\n" V.pretty x G.pretty v; + if M.tracing then M.trace "relationpriv" "sideg %a %a" V.pretty x G.pretty v; sideg x v in let r = thread_return ask getg sideg tid st in - if M.tracing then M.traceu "relationpriv" "-> %a\n" RelComponents.pretty r; + if M.tracing then M.traceu "relationpriv" "-> %a" RelComponents.pretty r; r end diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 9aca9e2079..853d6b1fe5 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -244,7 +244,7 @@ struct (* Evaluate binop for two abstract values: *) let evalbinop_base ~ctx (op: binop) (t1:typ) (a1:value) (t2:typ) (a2:value) (t:typ) :value = - if M.tracing then M.tracel "eval" "evalbinop %a %a %a\n" d_binop op VD.pretty a1 VD.pretty a2; + if M.tracing then M.tracel "eval" "evalbinop %a %a %a" d_binop op VD.pretty a1 VD.pretty a2; (* We define a conversion function for the easy cases when we can just use * the integer domain operations. *) let bool_top ik = ID.(join (of_int ik Z.zero) (of_int ik Z.one)) in @@ -358,7 +358,7 @@ struct let ay = AD.choose y in let handle_address_is_multiple addr = begin match Addr.to_var addr with | Some v when ctx.ask (Q.IsMultiple v) -> - if M.tracing then M.tracel "addr" "IsMultiple %a\n" CilType.Varinfo.pretty v; + if M.tracing then M.tracel "addr" "IsMultiple %a" CilType.Varinfo.pretty v; None | _ -> Some true @@ -366,7 +366,7 @@ struct in match Addr.semantic_equal ax ay with | Some true -> - if M.tracing then M.tracel "addr" "semantic_equal %a %a\n" AD.pretty x AD.pretty y; + if M.tracing then M.tracel "addr" "semantic_equal %a %a" AD.pretty x AD.pretty y; handle_address_is_multiple ax | Some false -> Some false | None -> None @@ -441,7 +441,7 @@ struct | _ -> ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) in - if M.tracing then M.tracel "sync" "sync multi=%B earlyglobs=%B\n" multi !earlyglobs; + if M.tracing then M.tracel "sync" "sync multi=%B earlyglobs=%B" multi !earlyglobs; if !earlyglobs || multi then WideningTokens.with_local_side_tokens (fun () -> Priv.sync (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) (priv_sideg ctx.sideg) ctx.local reason @@ -459,7 +459,7 @@ struct if (!earlyglobs || ThreadFlag.has_ever_been_multi ask) && is_global ask x then Priv.read_global ask (priv_getg ctx.global) st x else begin - if M.tracing then M.tracec "get" "Singlethreaded mode.\n"; + if M.tracing then M.tracec "get" "Singlethreaded mode."; CPA.find x st.cpa end @@ -470,14 +470,14 @@ struct let rec get ~ctx ?(top=VD.top ()) ?(full=false) (st: store) (addrs:address) (exp:exp option): value = let at = AD.type_of addrs in let firstvar = if M.tracing then match AD.to_var_may addrs with [] -> "" | x :: _ -> x.vname else "" in - if M.tracing then M.traceli "get" ~var:firstvar "Address: %a\nState: %a\n" AD.pretty addrs CPA.pretty st.cpa; + if M.tracing then M.traceli "get" ~var:firstvar "Address: %a\nState: %a" AD.pretty addrs CPA.pretty st.cpa; (* Finding a single varinfo*offset pair *) let res = let f_addr (x, offs) = (* get hold of the variable value, either from local or global state *) let var = get_var ~ctx st x in let v = VD.eval_offset (Queries.to_value_domain_ask (Analyses.ask_of_ctx ctx)) (fun x -> get ~ctx st x exp) var offs exp (Some (Var x, Offs.to_cil_offset offs)) x.vtype in - if M.tracing then M.tracec "get" "var = %a, %a = %a\n" VD.pretty var AD.pretty (AD.of_mval (x, offs)) VD.pretty v; + if M.tracing then M.tracec "get" "var = %a, %a = %a" VD.pretty var AD.pretty (AD.of_mval (x, offs)) VD.pretty v; if full then var else match v with | Blob (c,s,_) -> c | x -> x @@ -501,7 +501,7 @@ struct let f x a = VD.join (c @@ f x) a in (* Finally we join over all the addresses in the set. *) AD.fold f addrs (VD.bot ()) in - if M.tracing then M.traceu "get" "Result: %a\n" VD.pretty res; + if M.tracing then M.traceu "get" "Result: %a" VD.pretty res; res @@ -526,7 +526,7 @@ struct let rec reachable_from_value ask (value: value) (t: typ) (description: string) = let empty = AD.empty () in - if M.tracing then M.trace "reachability" "Checking value %a\n" VD.pretty value; + if M.tracing then M.trace "reachability" "Checking value %a" VD.pretty value; match value with | Top -> if not (VD.is_immediate_type t) then M.info ~category:Unsound "Unknown value in %s could be an escaped pointer address!" description; empty @@ -553,9 +553,9 @@ struct * all pointers within a structure should be considered, but we don't follow * pointers. We return a flattend representation, thus simply an address (set). *) let reachable_from_address ~ctx st (adr: address): address = - if M.tracing then M.tracei "reachability" "Checking for %a\n" AD.pretty adr; + if M.tracing then M.tracei "reachability" "Checking for %a" AD.pretty adr; let res = reachable_from_value (Analyses.ask_of_ctx ctx) (get ~ctx st adr None) (AD.type_of adr) (AD.show adr) in - if M.tracing then M.traceu "reachability" "Reachable addresses: %a\n" AD.pretty res; + if M.tracing then M.traceu "reachability" "Reachable addresses: %a" AD.pretty res; res (* The code for getting the variables reachable from the list of parameters. @@ -563,7 +563,7 @@ struct * addresses, as both AD elements abstracting individual (ambiguous) addresses * and the workset of visited addresses. *) let reachable_vars ~ctx (st: store) (args: address list): address list = - if M.tracing then M.traceli "reachability" "Checking reachable arguments from [%a]!\n" (d_list ", " AD.pretty) args; + if M.tracing then M.traceli "reachability" "Checking reachable arguments from [%a]!" (d_list ", " AD.pretty) args; let empty = AD.empty () in (* We begin looking at the parameters: *) let argset = List.fold_right (AD.join) args empty in @@ -581,7 +581,7 @@ struct workset := AD.diff collected !visited done; (* Return the list of elements that have been visited. *) - if M.tracing then M.traceu "reachability" "All reachable vars: %a\n" AD.pretty !visited; + if M.tracing then M.traceu "reachability" "All reachable vars: %a" AD.pretty !visited; List.map AD.singleton (AD.elements !visited) let reachable_vars ~ctx st args = Timing.wrap "reachability" (reachable_vars ~ctx st) args @@ -692,7 +692,7 @@ struct (* The evaluation function as mutually recursive eval_lv & eval_rv *) let rec eval_rv ~(ctx: _ ctx) (st: store) (exp:exp): value = - if M.tracing then M.traceli "evalint" "base eval_rv %a\n" d_exp exp; + if M.tracing then M.traceli "evalint" "base eval_rv %a" d_exp exp; let r = (* we have a special expression that should evaluate to top ... *) if exp = MyCFG.unknown_exp then @@ -700,7 +700,7 @@ struct else eval_rv_ask_evalint ~ctx st exp in - if M.tracing then M.traceu "evalint" "base eval_rv %a -> %a\n" d_exp exp VD.pretty r; + if M.tracing then M.traceu "evalint" "base eval_rv %a -> %a" d_exp exp VD.pretty r; r (** Evaluate expression using EvalInt query. @@ -709,13 +709,13 @@ struct Non-integer expression just delegate to next eval_rv function. *) and eval_rv_ask_evalint ~ctx st exp = let eval_next () = eval_rv_no_ask_evalint ~ctx st exp in - if M.tracing then M.traceli "evalint" "base eval_rv_ask_evalint %a\n" d_exp exp; + if M.tracing then M.traceli "evalint" "base eval_rv_ask_evalint %a" d_exp exp; let r:value = match Cilfacade.typeOf exp with | typ when Cil.isIntegralType typ && not (Cil.isConstant exp) -> (* don't EvalInt integer constants, base can do them precisely itself *) - if M.tracing then M.traceli "evalint" "base ask EvalInt %a\n" d_exp exp; + if M.tracing then M.traceli "evalint" "base ask EvalInt %a" d_exp exp; let a = ctx.ask (Q.EvalInt exp) in (* through queries includes eval_next, so no (exponential) branching is necessary *) - if M.tracing then M.traceu "evalint" "base ask EvalInt %a -> %a\n" d_exp exp Queries.ID.pretty a; + if M.tracing then M.traceu "evalint" "base ask EvalInt %a -> %a" d_exp exp Queries.ID.pretty a; begin match a with | `Bot -> eval_next () (* Base EvalInt returns bot on incorrect type (e.g. pthread_t); ignore and continue. *) (* | x -> Some (Int x) *) @@ -725,7 +725,7 @@ struct | exception Cilfacade.TypeOfError _ (* Bug: typeOffset: Field on a non-compound *) | _ -> eval_next () in - if M.tracing then M.traceu "evalint" "base eval_rv_ask_evalint %a -> %a\n" d_exp exp VD.pretty r; + if M.tracing then M.traceu "evalint" "base eval_rv_ask_evalint %a -> %a" d_exp exp VD.pretty r; r (** Evaluate expression without EvalInt query on outermost expression. @@ -750,7 +750,7 @@ struct Subexpressions delegate to [eval_rv], which may use queries on them. *) and eval_rv_base ~ctx (st: store) (exp:exp): value = let eval_rv = eval_rv_back_up in - if M.tracing then M.traceli "evalint" "base eval_rv_base %a\n" d_exp exp; + if M.tracing then M.traceli "evalint" "base eval_rv_base %a" d_exp exp; let binop_remove_same_casts ~extra_is_safe ~e1 ~e2 ~t1 ~t2 ~c1 ~c2 = let te1 = Cilfacade.typeOf e1 in let te2 = Cilfacade.typeOf e2 in @@ -910,7 +910,7 @@ struct | AddrOfLabel _ -> VD.top () in - if M.tracing then M.traceu "evalint" "base eval_rv_base %a -> %a\n" d_exp exp VD.pretty r; + if M.tracing then M.traceu "evalint" "base eval_rv_base %a -> %a" d_exp exp VD.pretty r; r and eval_rv_base_lval ~eval_lv ~ctx (st: store) (exp: exp) (lv: lval): value = @@ -937,7 +937,7 @@ struct | Addr (x, o) -> begin let at = Addr.Mval.type_of (x, o) in - if M.tracing then M.tracel "evalint" "cast_ok %a %a %a\n" Addr.pretty (Addr (x, o)) CilType.Typ.pretty (Cil.unrollType x.vtype) CilType.Typ.pretty at; + if M.tracing then M.tracel "evalint" "cast_ok %a %a %a" Addr.pretty (Addr (x, o)) CilType.Typ.pretty (Cil.unrollType x.vtype) CilType.Typ.pretty at; if at = TVoid [] then (* HACK: cast from alloc variable is always fine *) true else @@ -965,7 +965,7 @@ struct VD.top () (* upcasts not! *) in let v' = VD.cast t v in (* cast to the expected type (the abstract type might be something other than t since we don't change addresses upon casts!) *) - if M.tracing then M.tracel "cast" "Ptr-Deref: cast %a to %a = %a!\n" VD.pretty v d_type t VD.pretty v'; + if M.tracing then M.tracel "cast" "Ptr-Deref: cast %a to %a = %a!" VD.pretty v d_type t VD.pretty v'; let v' = VD.eval_offset (Queries.to_value_domain_ask (Analyses.ask_of_ctx ctx)) (fun x -> get ~ctx st x (Some exp)) v' (convert_offset ~ctx st ofs) (Some exp) None t in (* handle offset *) v' in @@ -989,7 +989,7 @@ struct (* Fallback to MustBeEqual query, could get extra precision from exprelation/var_eq. *) let must_be_equal () = let r = Q.must_be_equal (Analyses.ask_of_ctx ctx) e1 e2 in - if M.tracing then M.tracel "query" "MustBeEqual (%a, %a) = %b\n" d_exp e1 d_exp e2 r; + if M.tracing then M.tracel "query" "MustBeEqual (%a, %a) = %b" d_exp e1 d_exp e2 r; r in match op with @@ -1093,13 +1093,13 @@ struct let eval_rv ~ctx (st: store) (exp:exp): value = try let r = eval_rv ~ctx st exp in - if M.tracing then M.tracel "eval" "eval_rv %a = %a\n" d_exp exp VD.pretty r; + if M.tracing then M.tracel "eval" "eval_rv %a = %a" d_exp exp VD.pretty r; if VD.is_bot r then VD.top_value (Cilfacade.typeOf exp) else r with IntDomain.ArithmeticOnIntegerBot _ -> ValueDomain.Compound.top_value (Cilfacade.typeOf exp) let query_evalint ~ctx st e = - if M.tracing then M.traceli "evalint" "base query_evalint %a\n" d_exp e; + if M.tracing then M.traceli "evalint" "base query_evalint %a" d_exp e; let r = match eval_rv_no_ask_evalint ~ctx st e with | Int i -> `Lifted i (* cast should be unnecessary, eval_rv should guarantee right ikind already *) | Bot -> Queries.ID.top () (* out-of-scope variables cause bot, but query result should then be unknown *) @@ -1107,7 +1107,7 @@ struct | v -> M.debug ~category:Analyzer "Base EvalInt %a query answering bot instead of %a" d_exp e VD.pretty v; Queries.ID.bot () | exception (IntDomain.ArithmeticOnIntegerBot _) when not !AnalysisState.should_warn -> Queries.ID.top () (* for some privatizations, values can intermediately be bot because side-effects have not happened yet *) in - if M.tracing then M.traceu "evalint" "base query_evalint %a -> %a\n" d_exp e Queries.ID.pretty r; + if M.tracing then M.traceu "evalint" "base query_evalint %a -> %a" d_exp e Queries.ID.pretty r; r (* Evaluate an expression containing only locals. This is needed for smart joining the partitioned arrays where ctx is not accessible. *) @@ -1447,14 +1447,14 @@ struct * precise information about arrays. *) let set ~(ctx: _ ctx) ?(invariant=false) ?(blob_destructive=false) ?lval_raw ?rval_raw ?t_override (st: store) (lval: AD.t) (lval_type: Cil.typ) (value: value) : store = let update_variable x t y z = - if M.tracing then M.tracel "set" ~var:x.vname "update_variable: start '%s' '%a'\nto\n%a\n\n" x.vname VD.pretty y CPA.pretty z; + if M.tracing then M.tracel "set" ~var:x.vname "update_variable: start '%s' '%a'\nto\n%a\n" x.vname VD.pretty y CPA.pretty z; let r = update_variable x t y z in (* refers to defintion that is outside of set *) - if M.tracing then M.tracel "set" ~var:x.vname "update_variable: start '%s' '%a'\nto\n%a\nresults in\n%a\n" x.vname VD.pretty y CPA.pretty z CPA.pretty r; + if M.tracing then M.tracel "set" ~var:x.vname "update_variable: start '%s' '%a'\nto\n%a\nresults in\n%a" x.vname VD.pretty y CPA.pretty z CPA.pretty r; r in let firstvar = if M.tracing then match AD.to_var_may lval with [] -> "" | x :: _ -> x.vname else "" in let lval_raw = (Option.map (fun x -> Lval x) lval_raw) in - if M.tracing then M.tracel "set" ~var:firstvar "lval: %a\nvalue: %a\nstate: %a\n" AD.pretty lval VD.pretty value CPA.pretty st.cpa; + if M.tracing then M.tracel "set" ~var:firstvar "lval: %a\nvalue: %a\nstate: %a" AD.pretty lval VD.pretty value CPA.pretty st.cpa; (* Updating a single varinfo*offset pair. NB! This function's type does * not include the flag. *) let update_one_addr (x, offs) (st: store): store = @@ -1492,19 +1492,19 @@ struct else new_value in - if M.tracing then M.tracel "set" ~var:firstvar "update_one_addr: start with '%a' (type '%a') \nstate:%a\n\n" AD.pretty (AD.of_mval (x,offs)) d_type x.vtype D.pretty st; + if M.tracing then M.tracel "set" ~var:firstvar "update_one_addr: start with '%a' (type '%a') \nstate:%a\n" AD.pretty (AD.of_mval (x,offs)) d_type x.vtype D.pretty st; if isFunctionType x.vtype then begin - if M.tracing then M.tracel "set" ~var:firstvar "update_one_addr: returning: '%a' is a function type \n" d_type x.vtype; + if M.tracing then M.tracel "set" ~var:firstvar "update_one_addr: returning: '%a' is a function type " d_type x.vtype; st end else if get_bool "exp.globs_are_top" then begin - if M.tracing then M.tracel "set" ~var:firstvar "update_one_addr: BAD? exp.globs_are_top is set \n"; + if M.tracing then M.tracel "set" ~var:firstvar "update_one_addr: BAD? exp.globs_are_top is set "; { st with cpa = CPA.add x Top st.cpa } end else (* Check if we need to side-effect this one. We no longer generate * side-effects here, but the code still distinguishes these cases. *) if (!earlyglobs || ThreadFlag.has_ever_been_multi ask) && is_global ask x then begin - if M.tracing then M.tracel "set" ~var:x.vname "update_one_addr: update a global var '%s' ...\n" x.vname; + if M.tracing then M.tracel "set" ~var:x.vname "update_one_addr: update a global var '%s' ..." x.vname; let priv_getg = priv_getg ctx.global in (* Optimization to avoid evaluating integer values when setting them. The case when invariant = true requires the old_value to be sound for the meet. @@ -1515,12 +1515,12 @@ struct Priv.read_global ask priv_getg st x in let new_value = update_offset old_value in - if M.tracing then M.tracel "set" "update_offset %a -> %a\n" VD.pretty old_value VD.pretty new_value; + if M.tracing then M.tracel "set" "update_offset %a -> %a" VD.pretty old_value VD.pretty new_value; let r = Priv.write_global ~invariant ask priv_getg (priv_sideg ctx.sideg) st x new_value in - if M.tracing then M.tracel "set" ~var:x.vname "update_one_addr: updated a global var '%s' \nstate:%a\n\n" x.vname D.pretty r; + if M.tracing then M.tracel "set" ~var:x.vname "update_one_addr: updated a global var '%s' \nstate:%a\n" x.vname D.pretty r; r end else begin - if M.tracing then M.tracel "set" ~var:x.vname "update_one_addr: update a local var '%s' ...\n" x.vname; + if M.tracing then M.tracel "set" ~var:x.vname "update_one_addr: update a local var '%s' ..." x.vname; (* Normal update of the local state *) let new_value = update_offset (CPA.find x st.cpa) in (* what effect does changing this local variable have on arrays - @@ -1604,16 +1604,16 @@ struct (* We start from the current state and an empty list of global deltas, * and we assign to all the the different possible places: *) let nst = AD.fold update_one lval st in - (* if M.tracing then M.tracel "set" ~var:firstvar "new state1 %a\n" CPA.pretty nst; *) + (* if M.tracing then M.tracel "set" ~var:firstvar "new state1 %a" CPA.pretty nst; *) (* If the address was definite, then we just return it. If the address * was ambiguous, we have to join it with the initial state. *) let nst = if AD.cardinal lval > 1 then { nst with cpa = CPA.join st.cpa nst.cpa } else nst in - (* if M.tracing then M.tracel "set" ~var:firstvar "new state2 %a\n" CPA.pretty nst; *) + (* if M.tracing then M.tracel "set" ~var:firstvar "new state2 %a" CPA.pretty nst; *) nst with (* If any of the addresses are unknown, we ignore it!?! *) | SetDomain.Unsupported x -> - (* if M.tracing then M.tracel "set" ~var:firstvar "set got an exception '%s'\n" x; *) + (* if M.tracing then M.tracel "set" ~var:firstvar "set got an exception '%s'" x; *) M.info ~category:Unsound "Assignment to unknown address, assuming no write happened."; st let set_many ~ctx (st: store) lval_value_list: store = @@ -1690,7 +1690,7 @@ struct let set_savetop ~ctx ?lval_raw ?rval_raw st adr lval_t v : store = - if M.tracing then M.tracel "set" "savetop %a %a %a\n" AD.pretty adr d_type lval_t VD.pretty v; + if M.tracing then M.tracel "set" "savetop %a %a %a" AD.pretty adr d_type lval_t VD.pretty v; match v with | Top -> set ~ctx st adr lval_t (VD.top_value (AD.type_of adr)) ?lval_raw ?rval_raw | v -> set ~ctx st adr lval_t v ?lval_raw ?rval_raw @@ -1778,7 +1778,7 @@ struct | Some (x,offs) -> let t = v.vtype in let iv = VD.bot_value ~varAttr:v.vattr t in (* correct bottom value for top level variable *) - if M.tracing then M.tracel "set" "init bot value: %a\n" VD.pretty iv; + if M.tracing then M.tracel "set" "init bot value: %a" VD.pretty iv; let nv = VD.update_offset (Queries.to_value_domain_ask (Analyses.ask_of_ctx ctx)) iv offs rval_val (Some (Lval lval)) lval t in (* do desired update to value *) set_savetop ~ctx ctx.local (AD.of_var v) lval_t nv ~lval_raw:lval ~rval_raw:rval (* set top-level variable to updated value *) | None -> @@ -1795,28 +1795,28 @@ struct let valu = eval_rv ~ctx ctx.local exp in let refine () = let res = invariant ctx ctx.local exp tv in - if M.tracing then M.tracec "branch" "EqualSet result for expression %a is %a\n" d_exp exp Queries.ES.pretty (ctx.ask (Queries.EqualSet exp)); - if M.tracing then M.tracec "branch" "CondVars result for expression %a is %a\n" d_exp exp Queries.ES.pretty (ctx.ask (Queries.CondVars exp)); - if M.tracing then M.traceu "branch" "Invariant enforced!\n"; + if M.tracing then M.tracec "branch" "EqualSet result for expression %a is %a" d_exp exp Queries.ES.pretty (ctx.ask (Queries.EqualSet exp)); + if M.tracing then M.tracec "branch" "CondVars result for expression %a is %a" d_exp exp Queries.ES.pretty (ctx.ask (Queries.CondVars exp)); + if M.tracing then M.traceu "branch" "Invariant enforced!"; match ctx.ask (Queries.CondVars exp) with | s when Queries.ES.cardinal s = 1 -> let e = Queries.ES.choose s in invariant ctx res e tv | _ -> res in - if M.tracing then M.traceli "branch" ~subsys:["invariant"] "Evaluating branch for expression %a with value %a\n" d_exp exp VD.pretty valu; + if M.tracing then M.traceli "branch" ~subsys:["invariant"] "Evaluating branch for expression %a with value %a" d_exp exp VD.pretty valu; (* First we want to see, if we can determine a dead branch: *) match valu with (* For a boolean value: *) | Int value -> - if M.tracing then M.traceu "branch" "Expression %a evaluated to %a\n" d_exp exp ID.pretty value; + if M.tracing then M.traceu "branch" "Expression %a evaluated to %a" d_exp exp ID.pretty value; begin match ID.to_bool value with | Some v -> (* Eliminate the dead branch and just propagate to the true branch *) if v = tv then refine () else ( - if M.tracing then M.tracel "branch" "A The branch %B is dead!\n" tv; + if M.tracing then M.tracel "branch" "A The branch %B is dead!" tv; raise Deadcode ) | None -> @@ -1828,7 +1828,7 @@ struct | Address ad when not tv && AD.is_not_null ad -> raise Deadcode | Bot -> - if M.tracing then M.traceu "branch" "The branch %B is dead!\n" tv; + if M.tracing then M.traceu "branch" "The branch %B is dead!" tv; raise Deadcode (* Otherwise we try to impose an invariant: *) | _ -> @@ -1850,7 +1850,7 @@ struct let st: store = ctx.local in match fundec.svar.vname with | "__goblint_dummy_init" -> - if M.tracing then M.trace "init" "dummy init: %a\n" D.pretty st; + if M.tracing then M.trace "init" "dummy init: %a" D.pretty st; publish_all ctx `Init; (* otherfun uses __goblint_dummy_init, where we can properly side effect global initialization *) (* TODO: move into sync `Init *) @@ -1911,7 +1911,7 @@ struct ) let invalidate ?(deep=true) ~ctx (st:store) (exps: exp list): store = - if M.tracing && exps <> [] then M.tracel "invalidate" "Will invalidate expressions [%a]\n" (d_list ", " d_plainexp) exps; + if M.tracing && exps <> [] then M.tracel "invalidate" "Will invalidate expressions [%a]" (d_list ", " d_plainexp) exps; if exps <> [] then M.info ~category:Imprecise "Invalidating expressions: %a" (d_list ", " d_exp) exps; (* To invalidate a single address, we create a pair with its corresponding * top value. *) @@ -2166,7 +2166,7 @@ struct let special ctx (lv:lval option) (f: varinfo) (args: exp list) = let invalidate_ret_lv st = match lv with | Some lv -> - if M.tracing then M.tracel "invalidate" "Invalidating lhs %a for function call %s\n" d_plainlval lv f.vname; + if M.tracing then M.tracel "invalidate" "Invalidating lhs %a for function call %s" d_plainlval lv f.vname; invalidate ~deep:false ~ctx st [Cil.mkAddrOrStartOf lv] | None -> st in @@ -2176,7 +2176,7 @@ struct (addr, AD.type_of addr) in let forks = forkfun ctx lv f args in - if M.tracing then if not (List.is_empty forks) then M.tracel "spawn" "Base.special %s: spawning functions %a\n" f.vname (d_list "," CilType.Varinfo.pretty) (List.map BatTuple.Tuple4.second forks); + if M.tracing then if not (List.is_empty forks) then M.tracel "spawn" "Base.special %s: spawning functions %a" f.vname (d_list "," CilType.Varinfo.pretty) (List.map BatTuple.Tuple4.second forks); List.iter (fun (lval, f, args, multiple) -> ctx.spawn ~multiple lval f args) forks; let st: store = ctx.local in let desc = LF.find f in @@ -2408,7 +2408,7 @@ struct begin match ID.to_int x with | Some z -> - if M.tracing then M.tracel "attr" "setting\n"; + if M.tracing then M.tracel "attr" "setting"; set ~ctx st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.of_int z)) | None -> set ~ctx st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.top ())) end @@ -2607,7 +2607,7 @@ struct | Address jmp_buf -> let value = VD.JmpBuf (ValueDomain.JmpBufs.Bufs.singleton (Target (ctx.prev_node, ctx.control_context ())), false) in let r = set ~ctx st jmp_buf (Cilfacade.typeOf env) value in - if M.tracing then M.tracel "setjmp" "setting setjmp %a on %a -> %a\n" d_exp env D.pretty st D.pretty r; + if M.tracing then M.tracel "setjmp" "setting setjmp %a on %a -> %a" d_exp env D.pretty st D.pretty r; r | _ -> failwith "problem?!" in @@ -2664,7 +2664,7 @@ struct | Addr.Addr (v,o) when CPA.mem v fun_st.cpa -> begin let lval_type = Addr.type_of addr in - if M.tracing then M.trace "taintPC" "updating %a; type: %a\n" Addr.Mval.pretty (v,o) d_type lval_type; + if M.tracing then M.trace "taintPC" "updating %a; type: %a" Addr.Mval.pretty (v,o) d_type lval_type; match CPA.find_opt v (fun_st.cpa) with | None -> st (* partitioned arrays cannot be copied by individual lvalues, so if tainted just copy the whole callee value for the array variable *) @@ -2675,7 +2675,7 @@ struct begin let address = AD.singleton addr in let new_val = get ~ctx fun_st address None in - if M.tracing then M.trace "taintPC" "update val: %a\n\n" VD.pretty new_val; + if M.tracing then M.trace "taintPC" "update val: %a\n" VD.pretty new_val; let st' = set_savetop ~ctx st address lval_type new_val in match Dep.find_opt v fun_st.deps with | None -> st' @@ -2691,7 +2691,7 @@ struct let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) = let combine_one (st: D.t) (fun_st: D.t) = - if M.tracing then M.tracel "combine" "%a\n%a\n" CPA.pretty st.cpa CPA.pretty fun_st.cpa; + if M.tracing then M.tracel "combine" "%a\n%a" CPA.pretty st.cpa CPA.pretty fun_st.cpa; (* This function does miscellaneous things, but the main task was to give the * handle to the global state to the state return from the function, but now * the function tries to add all the context variables back to the callee. @@ -2702,29 +2702,29 @@ struct let cpa_noreturn = CPA.remove (return_varinfo ()) fun_st.cpa in let ask = Analyses.ask_of_ctx ctx in let tainted = f_ask.f Q.MayBeTainted in - if M.tracing then M.trace "taintPC" "combine for %s in base: tainted: %a\n" f.svar.vname AD.pretty tainted; - if M.tracing then M.trace "taintPC" "combine base:\ncaller: %a\ncallee: %a\n" CPA.pretty st.cpa CPA.pretty fun_st.cpa; + if M.tracing then M.trace "taintPC" "combine for %s in base: tainted: %a" f.svar.vname AD.pretty tainted; + if M.tracing then M.trace "taintPC" "combine base:\ncaller: %a\ncallee: %a" CPA.pretty st.cpa CPA.pretty fun_st.cpa; if AD.is_top tainted then let cpa_local = CPA.filter (fun x _ -> not (is_global ask x)) st.cpa in let cpa' = CPA.fold CPA.add cpa_noreturn cpa_local in (* add cpa_noreturn to cpa_local *) - if M.tracing then M.trace "taintPC" "combined: %a\n" CPA.pretty cpa'; + if M.tracing then M.trace "taintPC" "combined: %a" CPA.pretty cpa'; { fun_st with cpa = cpa' } else (* remove variables from caller cpa, that are global and not in the callee cpa *) let cpa_caller = CPA.filter (fun x _ -> (not (is_global ask x)) || CPA.mem x fun_st.cpa) st.cpa in - if M.tracing then M.trace "taintPC" "cpa_caller: %a\n" CPA.pretty cpa_caller; + if M.tracing then M.trace "taintPC" "cpa_caller: %a" CPA.pretty cpa_caller; (* add variables from callee that are not in caller yet *) let cpa_new = CPA.filter (fun x _ -> not (CPA.mem x cpa_caller)) cpa_noreturn in - if M.tracing then M.trace "taintPC" "cpa_new: %a\n" CPA.pretty cpa_new; + if M.tracing then M.trace "taintPC" "cpa_new: %a" CPA.pretty cpa_new; let cpa_caller' = CPA.fold CPA.add cpa_new cpa_caller in - if M.tracing then M.trace "taintPC" "cpa_caller': %a\n" CPA.pretty cpa_caller'; + if M.tracing then M.trace "taintPC" "cpa_caller': %a" CPA.pretty cpa_caller'; (* remove lvals from the tainted set that correspond to variables for which we just added a new mapping from the callee*) let tainted = AD.filter (function | Addr.Addr (v,_) -> not (CPA.mem v cpa_new) | _ -> false ) tainted in let st_combined = combine_st ctx {st with cpa = cpa_caller'} fun_st tainted in - if M.tracing then M.trace "taintPC" "combined: %a\n" CPA.pretty st_combined.cpa; + if M.tracing then M.trace "taintPC" "combined: %a" CPA.pretty st_combined.cpa; { fun_st with cpa = st_combined.cpa } in let nst = add_globals st fun_st in @@ -2896,7 +2896,7 @@ struct with Deadcode -> (* contradiction in unassume *) D.bot () in - if M.tracing then M.traceli "unassume" "base unassuming\n"; + if M.tracing then M.traceli "unassume" "base unassuming"; let r = match get_string "ana.base.invariant.unassume" with | "once" -> @@ -2907,7 +2907,7 @@ struct | _ -> assert false in - if M.tracing then M.traceu "unassume" "base unassumed\n"; + if M.tracing then M.traceu "unassume" "base unassumed"; r in M.info ~category:Witness "base unassumed invariant: %a" d_exp e; @@ -2929,7 +2929,7 @@ struct let st: store = ctx.local in match e with | Events.Lock (addr, _) when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) - if M.tracing then M.tracel "priv" "LOCK EVENT %a\n" LockDomain.Addr.pretty addr; + if M.tracing then M.tracel "priv" "LOCK EVENT %a" LockDomain.Addr.pretty addr; Priv.lock ask (priv_getg ctx.global) st addr | Events.Unlock addr when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) if addr = UnknownPtr then diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 7154768a75..8759f6ebb7 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -55,7 +55,7 @@ struct with Lattice.Uncomparable -> old_val let refine_lv_fallback ctx st lval value tv = - if M.tracing then M.tracec "invariant" "Restricting %a with %a\n" d_lval lval VD.pretty value; + if M.tracing then M.tracec "invariant" "Restricting %a with %a" d_lval lval VD.pretty value; let addr = eval_lv ~ctx st lval in if (AD.is_top addr) then st else @@ -73,10 +73,10 @@ struct let state_with_excluded = set st addr t_lval value ~ctx in let value = get ~ctx state_with_excluded addr None in let new_val = apply_invariant ~old_val ~new_val:value in - if M.tracing then M.traceu "invariant" "New value is %a\n" VD.pretty new_val; + if M.tracing then M.traceu "invariant" "New value is %a" VD.pretty new_val; (* make that address meet the invariant, i.e exclusion sets will be joined *) if is_some_bot new_val then ( - if M.tracing then M.tracel "branch" "C The branch %B is dead!\n" tv; + if M.tracing then M.tracel "branch" "C The branch %B is dead!" tv; contra st ) else if VD.is_bot new_val @@ -95,9 +95,9 @@ struct let v = apply_invariant ~old_val ~new_val in if is_some_bot v then contra st else ( - if M.tracing then M.tracel "inv" "improve variable %a from %a to %a (c = %a, c' = %a)\n" CilType.Varinfo.pretty var VD.pretty old_val VD.pretty v pretty c VD.pretty c'; + if M.tracing then M.tracel "inv" "improve variable %a from %a to %a (c = %a, c' = %a)" CilType.Varinfo.pretty var VD.pretty old_val VD.pretty v pretty c VD.pretty c'; let r = set' (Var var,NoOffset) v st in - if M.tracing then M.tracel "inv" "st from %a to %a\n" D.pretty st D.pretty r; + if M.tracing then M.tracel "inv" "st from %a to %a" D.pretty st D.pretty r; r ) | Var _, _ @@ -108,7 +108,7 @@ struct let v = apply_invariant ~old_val ~new_val:c' in if is_some_bot v then contra st else ( - if M.tracing then M.tracel "inv" "improve lval %a from %a to %a (c = %a, c' = %a)\n" d_lval x VD.pretty old_val VD.pretty v pretty c VD.pretty c'; + if M.tracing then M.tracel "inv" "improve lval %a from %a to %a (c = %a, c' = %a)" d_lval x VD.pretty old_val VD.pretty v pretty c VD.pretty c'; set' x v st ) @@ -119,7 +119,7 @@ struct match (op, lval, value, tv) with (* The true-branch where x == value: *) | Eq, x, value, true -> - if M.tracing then M.tracec "invariant" "Yes, %a equals %a\n" d_lval x VD.pretty value; + if M.tracing then M.tracec "invariant" "Yes, %a equals %a" d_lval x VD.pretty value; (match value with | Int n -> let ikind = Cilfacade.get_ikind_exp (Lval lval) in @@ -132,27 +132,27 @@ struct match ID.to_int n with | Some n -> (* When x != n, we can return a singleton exclusion set *) - if M.tracing then M.tracec "invariant" "Yes, %a is not %a\n" d_lval x GobZ.pretty n; + if M.tracing then M.tracec "invariant" "Yes, %a is not %a" d_lval x GobZ.pretty n; let ikind = Cilfacade.get_ikind_exp (Lval lval) in Some (x, Int (ID.of_excl_list ikind [n])) | None -> None end | Address n -> begin - if M.tracing then M.tracec "invariant" "Yes, %a is not %a\n" d_lval x AD.pretty n; + if M.tracing then M.tracec "invariant" "Yes, %a is not %a" d_lval x AD.pretty n; match eval_rv_address ~ctx st (Lval x) with | Address a when AD.is_definite n -> Some (x, Address (AD.diff a n)) | Top when AD.is_null n -> Some (x, Address AD.not_null) | v -> - if M.tracing then M.tracec "invariant" "No address invariant for: %a != %a\n" VD.pretty v AD.pretty n; + if M.tracing then M.tracec "invariant" "No address invariant for: %a != %a" VD.pretty v AD.pretty n; None end (* | Address a -> Some (x, value) *) | _ -> (* We can't say anything else, exclusion sets are finite, so not * being in one means an infinite number of values *) - if M.tracing then M.tracec "invariant" "Failed! (not a definite value)\n"; + if M.tracing then M.tracec "invariant" "Failed! (not a definite value)"; None end | Ne, x, value, _ -> helper Eq x value (not tv) @@ -165,7 +165,7 @@ struct let limit_from = if tv then ID.maximal else ID.minimal in match limit_from n with | Some n -> - if M.tracing then M.tracec "invariant" "Yes, success! %a is not %a\n\n" d_lval x GobZ.pretty n; + if M.tracing then M.tracec "invariant" "Yes, success! %a is not %a\n" d_lval x GobZ.pretty n; Some (x, Int (range_from n)) | None -> None end @@ -180,7 +180,7 @@ struct let limit_from = if tv then ID.maximal else ID.minimal in match limit_from n with | Some n -> - if M.tracing then M.tracec "invariant" "Yes, success! %a is not %a\n\n" d_lval x GobZ.pretty n; + if M.tracing then M.tracec "invariant" "Yes, success! %a is not %a\n" d_lval x GobZ.pretty n; Some (x, Int (range_from n)) | None -> None end @@ -189,10 +189,10 @@ struct | Gt, x, value, _ -> helper Le x value (not tv) | Ge, x, value, _ -> helper Lt x value (not tv) | _ -> - if M.tracing then M.trace "invariant" "Failed! (operation not supported)\n\n"; + if M.tracing then M.trace "invariant" "Failed! (operation not supported)\n"; None in - if M.tracing then M.traceli "invariant" "assume expression %a is %B\n" d_exp exp tv; + if M.tracing then M.traceli "invariant" "assume expression %a is %B" d_exp exp tv; let null_val (typ:typ):VD.t = match Cil.unrollType typ with | TPtr _ -> Address AD.null_ptr @@ -227,20 +227,20 @@ struct helper Ne x (null_val (Cilfacade.typeOf exp)) tv | UnOp (LNot,uexp,typ) -> derived_invariant uexp (not tv) | _ -> - if M.tracing then M.tracec "invariant" "Failed! (expression %a not understood)\n\n" d_plainexp exp; + if M.tracing then M.tracec "invariant" "Failed! (expression %a not understood)\n" d_plainexp exp; None in match derived_invariant exp tv with | Some (lval, value) -> refine_lv_fallback ctx st lval value tv | None -> - if M.tracing then M.traceu "invariant" "Doing nothing.\n"; + if M.tracing then M.traceu "invariant" "Doing nothing."; M.debug ~category:Analyzer "Invariant failed: expression \"%a\" not understood." d_exp exp; st let invariant ctx st exp tv: D.t = let fallback reason st = - if M.tracing then M.tracel "inv" "Can't handle %a.\n%t\n" d_plainexp exp reason; + if M.tracing then M.tracel "inv" "Can't handle %a.\n%t" d_plainexp exp reason; invariant_fallback ctx st exp tv in (* inverse values for binary operation a `op` b == c *) @@ -368,14 +368,14 @@ struct in let a' = excl b a in let b' = excl a b in - if M.tracing then M.tracel "inv" "inv_bin_int: unequal: %a and %a; ikind: %a; a': %a, b': %a\n" ID.pretty a ID.pretty b d_ikind ikind ID.pretty a' ID.pretty b'; + if M.tracing then M.tracel "inv" "inv_bin_int: unequal: %a and %a; ikind: %a; a': %a, b': %a" ID.pretty a ID.pretty b d_ikind ikind ID.pretty a' ID.pretty b'; meet_bin a' b' | _, _ -> a, b end | Lt | Le | Ge | Gt as op -> (match ID.minimal a, ID.maximal a, ID.minimal b, ID.maximal b with | Some l1, Some u1, Some l2, Some u2 -> - (* if M.tracing then M.tracel "inv" "Op: %s, l1: %Ld, u1: %Ld, l2: %Ld, u2: %Ld\n" (show_binop op) l1 u1 l2 u2; *) + (* if M.tracing then M.tracel "inv" "Op: %s, l1: %Ld, u1: %Ld, l2: %Ld, u2: %Ld" (show_binop op) l1 u1 l2 u2; *) (* TODO: This causes inconsistent results: def_exc and interval in conflict: binop m < 0 with (Not {-1}([-31,31]),[-1,0]) < (0,[0,0]) == (1,[1,1]) @@ -392,7 +392,7 @@ struct | _, _ -> a, b) | _ -> a, b) | BOr | BXor as op-> - if M.tracing then M.tracel "inv" "Unhandled operator %a\n" d_binop op; + if M.tracing then M.tracel "inv" "Unhandled operator %a" d_binop op; (* Be careful: inv_exp performs a meet on both arguments of the BOr / BXor. *) a, b | LAnd -> @@ -408,12 +408,12 @@ struct (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 + | None -> if M.tracing then M.tracel "inv" "Unhandled case for operator x %a 1 = %a" d_binop op ID.pretty c; a) + | _ -> if M.tracing then M.tracel "inv" "Unhandled case for operator x %a %a = %a" 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; + if M.tracing then M.tracel "inv" "Unhandled operator %a" d_binop op; a, b in let inv_bin_float (a, b) c op = @@ -513,7 +513,7 @@ struct else b | _ -> b) in - if M.tracing then M.trace "inv_float" "Div: (%a,%a) = %a yields (%a,%a) \n\n" FD.pretty a FD.pretty b FD.pretty c FD.pretty a' FD.pretty b'; + if M.tracing then M.trace "inv_float" "Div: (%a,%a) = %a yields (%a,%a) \n" FD.pretty a FD.pretty b FD.pretty c FD.pretty a' FD.pretty b'; meet_bin a' b' | Eq | Ne as op -> let both x = x, x in @@ -541,7 +541,7 @@ struct | _, _ -> a, b) | _ -> a, b) | op -> - if M.tracing then M.tracel "inv" "Unhandled operator %a\n" d_binop op; + if M.tracing then M.tracel "inv" "Unhandled operator %a" d_binop op; a, b with FloatDomain.ArithmeticOnFloatBot _ -> raise Analyses.Deadcode in @@ -672,20 +672,20 @@ struct | (BinOp (op, e1, e2, _) as e, Float _) | (BinOp (op, e1, e2, _) as e, Int _) -> let invert_binary_op c pretty c_int c_float = - if M.tracing then M.tracel "inv" "binop %a with %a %a %a == %a\n" d_exp e VD.pretty (eval e1 st) d_binop op VD.pretty (eval e2 st) pretty c; + if M.tracing then M.tracel "inv" "binop %a with %a %a %a == %a" d_exp e VD.pretty (eval e1 st) d_binop op VD.pretty (eval e2 st) pretty c; (match eval e1 st, eval e2 st with | Int a, Int b -> let ikind = Cilfacade.get_ikind_exp e1 in (* both operands have the same type (except for Shiftlt, Shiftrt)! *) let ikres = Cilfacade.get_ikind_exp e in (* might be different from argument types, e.g. for LT, GT, EQ, ... *) let a', b' = inv_bin_int (a, b) ikind (c_int ikres) op in - if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e ID.pretty (c_int ikind) ID.pretty a' ID.pretty b'; + if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a" d_exp e ID.pretty (c_int ikind) ID.pretty a' ID.pretty b'; let st' = inv_exp (Int a') e1 st in let st'' = inv_exp (Int b') e2 st' in st'' | Float a, Float b -> let fkind = Cilfacade.get_fkind_exp e1 in (* both operands have the same type *) let a', b' = inv_bin_float (a, b) (c_float fkind) op in - if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e FD.pretty (c_float fkind) FD.pretty a' FD.pretty b'; + if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a" d_exp e FD.pretty (c_float fkind) FD.pretty a' FD.pretty b'; let st' = inv_exp (Float a') e1 st in let st'' = inv_exp (Float b') e2 st' in st'' @@ -701,7 +701,7 @@ struct | Lval x, (Int _ | Float _ | Address _) -> (* meet x with c *) let update_lval c x c' pretty = refine_lv ctx st c x c' pretty exp in let t = Cil.unrollType (Cilfacade.typeOfLval x) in (* unroll type to deal with TNamed *) - if M.tracing then M.trace "invSpecial" "invariant with Lval %a, c_typed %a, type %a\n" d_lval x VD.pretty c_typed d_type t; + if M.tracing then M.trace "invSpecial" "invariant with Lval %a, c_typed %a, type %a" d_lval x VD.pretty c_typed d_type t; begin match c_typed with | Int c -> let c' = match t with @@ -715,7 +715,7 @@ struct begin match x, t with | (Var v, offs), TInt (ik, _) -> let tmpSpecial = ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) in - if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty tmpSpecial; + if M.tracing then M.trace "invSpecial" "qry Result: %a" Queries.ML.pretty tmpSpecial; begin match tmpSpecial with | `Lifted (Abs (ik, xInt)) -> let c' = ID.cast_to ik c in (* different ik! *) @@ -753,7 +753,7 @@ struct begin match x, t with | (Var v, offs), TFloat (fk, _) -> let tmpSpecial = ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) in - if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty tmpSpecial; + if M.tracing then M.trace "invSpecial" "qry Result: %a" Queries.ML.pretty tmpSpecial; begin match tmpSpecial 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 @@ -792,7 +792,7 @@ struct | TEnum ({ekind = ik_e; _ }, _) -> (* let c' = ID.cast_to ik_e c in *) let c' = ID.cast_to ik_e (ID.meet c (ID.cast_to ik (ID.top_of ik_e))) in (* TODO: cast without overflow, is this right for normal invariant? *) - if M.tracing then M.tracel "inv" "cast: %a from %a to %a: i = %a; cast c = %a to %a = %a\n" d_exp e d_ikind ik_e d_ikind ik ID.pretty i ID.pretty c d_ikind ik_e ID.pretty c'; + if M.tracing then M.tracel "inv" "cast: %a from %a to %a: i = %a; cast c = %a to %a = %a" d_exp e d_ikind ik_e d_ikind ik ID.pretty i ID.pretty c d_ikind ik_e ID.pretty c'; inv_exp (Int c') e st | x -> fallback (fun () -> Pretty.dprintf "CastE: e did evaluate to Int, but the type did not match %a" CilType.Typ.pretty t) st else diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 125429231e..49bfb9425d 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -97,7 +97,7 @@ struct let write_global ?(invariant=false) ask getg sideg (st: BaseComponents (D).t) x v = if invariant then ( (* Do not impose invariant, will not hold without privatization *) - if M.tracing then M.tracel "set" ~var:x.vname "update_one_addr: BAD! effect = '%B', or else is private! \n" (not invariant); + if M.tracing then M.tracel "set" ~var:x.vname "update_one_addr: BAD! effect = '%B', or else is private! " (not invariant); st ) else ( @@ -113,16 +113,16 @@ struct let sync ask getg sideg (st: BaseComponents (D).t) reason = (* For each global variable, we create the side effect *) let side_var (v: varinfo) (value) (st: BaseComponents (D).t) = - if M.tracing then M.traceli "globalize" ~var:v.vname "Tracing for %s\n" v.vname; + if M.tracing then M.traceli "globalize" ~var:v.vname "Tracing for %s" v.vname; let res = if is_global ask v then begin - if M.tracing then M.tracec "globalize" "Publishing its value: %a\n" VD.pretty value; + if M.tracing then M.tracec "globalize" "Publishing its value: %a" VD.pretty value; sideg v value; {st with cpa = CPA.remove v st.cpa} end else st in - if M.tracing then M.traceu "globalize" "Done!\n"; + if M.tracing then M.traceu "globalize" "Done!"; res in (* We fold over the local state, and side effect the globals *) @@ -154,7 +154,7 @@ struct let get_mutex_inits = getg V.mutex_inits in let is_in_Gm x _ = is_protected_by ask m x in let get_mutex_inits' = CPA.filter is_in_Gm get_mutex_inits in - if M.tracing then M.tracel "priv" "get_m_with_mutex_inits %a:\n get_m: %a\n get_mutex_inits: %a\n get_mutex_inits': %a\n" LockDomain.Addr.pretty m CPA.pretty get_m CPA.pretty get_mutex_inits CPA.pretty get_mutex_inits'; + if M.tracing then M.tracel "priv" "get_m_with_mutex_inits %a:\n get_m: %a\n get_mutex_inits: %a\n get_mutex_inits': %a" LockDomain.Addr.pretty m CPA.pretty get_m CPA.pretty get_mutex_inits CPA.pretty get_mutex_inits'; CPA.join get_m get_mutex_inits' (** [get_m_with_mutex_inits] optimized for implementation-specialized [read_global]. *) @@ -178,7 +178,7 @@ struct let cpa' = CPA.fold (fun x v acc -> if EscapeDomain.EscapedVars.mem x escaped (* && is_unprotected ask x *) then ( - if M.tracing then M.tracel "priv" "ESCAPE SIDE %a = %a\n" CilType.Varinfo.pretty x VD.pretty v; + if M.tracing then M.tracel "priv" "ESCAPE SIDE %a = %a" CilType.Varinfo.pretty x VD.pretty v; sideg (V.global x) (CPA.singleton x v); CPA.remove x acc ) @@ -194,8 +194,8 @@ struct let cpa' = CPA.fold (fun x v acc -> if is_global ask x (* && is_unprotected ask x *) then ( - if M.tracing then M.tracel "priv" "enter_multithreaded remove %a\n" CilType.Varinfo.pretty x; - if M.tracing then M.tracel "priv" "ENTER MULTITHREADED SIDE %a = %a\n" CilType.Varinfo.pretty x VD.pretty v; + if M.tracing then M.tracel "priv" "enter_multithreaded remove %a" CilType.Varinfo.pretty x; + if M.tracing then M.tracel "priv" "ENTER MULTITHREADED SIDE %a = %a" CilType.Varinfo.pretty x VD.pretty v; sideg (V.global x) (CPA.singleton x v); CPA.remove x acc ) @@ -255,7 +255,7 @@ struct No other privatization uses is_unprotected, so this hack is only needed here. *) let is_in_V x _ = is_protected_by ask m x && is_unprotected_without ask x m in let cpa' = CPA.filter is_in_V get_m in - if M.tracing then M.tracel "priv" "PerMutexOplusPriv.lock m=%a cpa'=%a\n" LockDomain.Addr.pretty m CPA.pretty cpa'; + if M.tracing then M.tracel "priv" "PerMutexOplusPriv.lock m=%a cpa'=%a" LockDomain.Addr.pretty m CPA.pretty cpa'; {st with cpa = CPA.fold CPA.add cpa' st.cpa} ) else @@ -264,7 +264,7 @@ struct let unlock ask getg sideg (st: BaseComponents (D).t) m = let is_in_Gm x _ = is_protected_by ask m x in let side_m_cpa = CPA.filter is_in_Gm st.cpa in - if M.tracing then M.tracel "priv" "PerMutexOplusPriv.unlock m=%a side_m_cpa=%a\n" LockDomain.Addr.pretty m CPA.pretty side_m_cpa; + if M.tracing then M.tracel "priv" "PerMutexOplusPriv.unlock m=%a side_m_cpa=%a" LockDomain.Addr.pretty m CPA.pretty side_m_cpa; sideg (V.mutex m) side_m_cpa; st @@ -318,7 +318,7 @@ struct CPA.find x st.cpa let read_global ask getg st x = let v = read_global ask getg st x in - if M.tracing then M.tracel "priv" "READ GLOBAL %a %B %a = %a\n" CilType.Varinfo.pretty x (is_unprotected ask x) CPA.pretty st.cpa VD.pretty v; + if M.tracing then M.tracel "priv" "READ GLOBAL %a %B %a = %a" CilType.Varinfo.pretty x (is_unprotected ask x) CPA.pretty st.cpa VD.pretty v; v let write_global ?(invariant=false) ask getg sideg (st: BaseComponents (D).t) x v = let cpa' = @@ -328,7 +328,7 @@ struct CPA.add x v st.cpa in if not invariant then ( - if M.tracing then M.tracel "priv" "WRITE GLOBAL SIDE %a = %a\n" CilType.Varinfo.pretty x VD.pretty v; + if M.tracing then M.tracel "priv" "WRITE GLOBAL SIDE %a = %a" CilType.Varinfo.pretty x VD.pretty v; sideg (V.global x) (CPA.singleton x v) (* Unlock after invariant will still side effect refined value (if protected) from CPA, because cannot distinguish from non-invariant write. *) ); @@ -346,7 +346,7 @@ struct let get_m = CPA.filter is_in_Gm get_m in let long_meet m1 m2 = CPA.long_map2 VD.meet m1 m2 in let meet = long_meet st.cpa get_m in - if M.tracing then M.tracel "priv" "LOCK %a:\n get_m: %a\n meet: %a\n" LockDomain.Addr.pretty m CPA.pretty get_m CPA.pretty meet; + if M.tracing then M.tracel "priv" "LOCK %a:\n get_m: %a\n meet: %a" LockDomain.Addr.pretty m CPA.pretty get_m CPA.pretty meet; {st with cpa = meet} ) else @@ -373,12 +373,12 @@ struct let cpa' = CPA.fold (fun x v cpa -> if is_global ask x && is_unprotected ask x (* && not (VD.is_top v) *) then ( - if M.tracing then M.tracel "priv" "SYNC SIDE %a = %a\n" CilType.Varinfo.pretty x VD.pretty v; + if M.tracing then M.tracel "priv" "SYNC SIDE %a = %a" CilType.Varinfo.pretty x VD.pretty v; sideg (V.global x) (CPA.singleton x v); CPA.remove x cpa ) else ( - if M.tracing then M.tracel "priv" "SYNC NOSIDE %a = %a\n" CilType.Varinfo.pretty x VD.pretty v; + if M.tracing then M.tracel "priv" "SYNC NOSIDE %a = %a" CilType.Varinfo.pretty x VD.pretty v; cpa ) ) st.cpa st.cpa @@ -457,7 +457,7 @@ struct let read_global ask getg st x = let v = read_global ask getg st x in - if M.tracing then M.tracel "priv" "READ GLOBAL %a %B %a = %a\n" CilType.Varinfo.pretty x (is_unprotected ~protection:Weak ask x) CPA.pretty st.cpa VD.pretty v; + if M.tracing then M.tracel "priv" "READ GLOBAL %a %B %a = %a" CilType.Varinfo.pretty x (is_unprotected ~protection:Weak ask x) CPA.pretty st.cpa VD.pretty v; v let write_global ?(invariant=false) ask getg sideg (st: BaseComponents (D).t) x v = @@ -469,7 +469,7 @@ struct else CPA.add x v st.cpa in - if M.tracing then M.tracel "priv" "WRITE GLOBAL SIDE %a = %a\n" CilType.Varinfo.pretty x VD.pretty v; + if M.tracing then M.tracel "priv" "WRITE GLOBAL SIDE %a = %a" CilType.Varinfo.pretty x VD.pretty v; let digest = Digest.current ask in let sidev = GMutex.singleton digest (CPA.singleton x v) in let l' = L.add lm (CPA.singleton x v) l in @@ -569,7 +569,7 @@ struct sideg V.mutex_inits (G.create_mutex sidev); let cpa' = CPA.fold (fun x v acc -> if EscapeDomain.EscapedVars.mem x escaped (* && is_unprotected ask x *) then ( - if M.tracing then M.tracel "priv" "ESCAPE SIDE %a = %a\n" CilType.Varinfo.pretty x VD.pretty v; + if M.tracing then M.tracel "priv" "ESCAPE SIDE %a = %a" CilType.Varinfo.pretty x VD.pretty v; let sidev = GMutex.singleton digest (CPA.singleton x v) in sideg (V.global x) (G.create_global sidev); CPA.remove x acc @@ -1204,10 +1204,10 @@ struct else GWeak.find lockset_init weaks in - if M.tracing then M.trace "priv" "d_cpa: %a\n" VD.pretty d_cpa; - if M.tracing then M.trace "priv" "d_sync: %a\n" VD.pretty d_sync; - if M.tracing then M.trace "priv" "d_weak: %a\n" VD.pretty d_weak; - if M.tracing then M.trace "priv" "d_init: %a\n" VD.pretty d_init; + if M.tracing then M.trace "priv" "d_cpa: %a" VD.pretty d_cpa; + if M.tracing then M.trace "priv" "d_sync: %a" VD.pretty d_sync; + if M.tracing then M.trace "priv" "d_weak: %a" VD.pretty d_weak; + if M.tracing then M.trace "priv" "d_init: %a" VD.pretty d_init; let d_weak = VD.join d_weak d_init in let d = VD.join d_cpa (VD.join d_sync d_weak) in d @@ -1364,9 +1364,9 @@ struct acc ) weaks (VD.bot ()) in - if M.tracing then M.trace "priv" "d_cpa: %a\n" VD.pretty d_cpa; - if M.tracing then M.trace "priv" "d_sync: %a\n" VD.pretty d_sync; - if M.tracing then M.trace "priv" "d_weak: %a\n" VD.pretty d_weak; + if M.tracing then M.trace "priv" "d_cpa: %a" VD.pretty d_cpa; + if M.tracing then M.trace "priv" "d_sync: %a" VD.pretty d_sync; + if M.tracing then M.trace "priv" "d_weak: %a" VD.pretty d_weak; let d = VD.join d_cpa (VD.join d_sync d_weak) in d @@ -1394,11 +1394,11 @@ struct let s = Lockset.remove m (current_lockset ask) in let (w, p) = st.priv in let p' = P.map (fun s' -> MinLocksets.add s s') p in - if M.tracing then M.traceli "priv" "unlock %a %a\n" Lock.pretty m CPA.pretty st.cpa; + if M.tracing then M.traceli "priv" "unlock %a %a" Lock.pretty m CPA.pretty st.cpa; let side_gsyncw = CPA.fold (fun x v acc -> if is_global ask x then ( let w_x = W.find x w in - if M.tracing then M.trace "priv" "gsyncw %a %a %a\n" CilType.Varinfo.pretty x VD.pretty v MinLocksets.pretty w_x; + if M.tracing then M.trace "priv" "gsyncw %a %a %a" CilType.Varinfo.pretty x VD.pretty v MinLocksets.pretty w_x; MinLocksets.fold (fun w acc -> let v = distr_init getg x v in GSyncW.add w (CPA.add x v (GSyncW.find w acc)) acc @@ -1407,7 +1407,7 @@ struct acc ) st.cpa (GSyncW.bot ()) in - if M.tracing then M.traceu "priv" "unlock %a %a\n" Lock.pretty m GSyncW.pretty side_gsyncw; + if M.tracing then M.traceu "priv" "unlock %a %a" Lock.pretty m GSyncW.pretty side_gsyncw; sideg (V.mutex m) (G.create_sync (GSync.singleton s side_gsyncw)); {st with priv = (w, p')} @@ -1685,98 +1685,98 @@ struct module BaseComponents = BaseComponents (D) let read_global ask getg st x = - if M.tracing then M.traceli "priv" "read_global %a\n" CilType.Varinfo.pretty x; - if M.tracing then M.trace "priv" "st: %a\n" BaseComponents.pretty st; + if M.tracing then M.traceli "priv" "read_global %a" CilType.Varinfo.pretty x; + if M.tracing then M.trace "priv" "st: %a" BaseComponents.pretty st; let getg x = let r = getg x in - if M.tracing then M.trace "priv" "getg %a -> %a\n" V.pretty x G.pretty r; + if M.tracing then M.trace "priv" "getg %a -> %a" V.pretty x G.pretty r; r in let v = Priv.read_global ask getg st x in - if M.tracing then M.traceu "priv" "-> %a\n" VD.pretty v; + if M.tracing then M.traceu "priv" "-> %a" VD.pretty v; v let write_global ?invariant ask getg sideg st x v = - if M.tracing then M.traceli "priv" "write_global %a %a\n" CilType.Varinfo.pretty x VD.pretty v; - if M.tracing then M.trace "priv" "st: %a\n" BaseComponents.pretty st; + if M.tracing then M.traceli "priv" "write_global %a %a" CilType.Varinfo.pretty x VD.pretty v; + if M.tracing then M.trace "priv" "st: %a" BaseComponents.pretty st; let getg x = let r = getg x in - if M.tracing then M.trace "priv" "getg %a -> %a\n" V.pretty x G.pretty r; + if M.tracing then M.trace "priv" "getg %a -> %a" V.pretty x G.pretty r; r in let sideg x v = - if M.tracing then M.trace "priv" "sideg %a %a\n" V.pretty x G.pretty v; + if M.tracing then M.trace "priv" "sideg %a %a" V.pretty x G.pretty v; sideg x v in let r = write_global ?invariant ask getg sideg st x v in - if M.tracing then M.traceu "priv" "-> %a\n" BaseComponents.pretty r; + if M.tracing then M.traceu "priv" "-> %a" BaseComponents.pretty r; r let lock ask getg st m = - if M.tracing then M.traceli "priv" "lock %a\n" LockDomain.Addr.pretty m; - if M.tracing then M.trace "priv" "st: %a\n" BaseComponents.pretty st; + if M.tracing then M.traceli "priv" "lock %a" LockDomain.Addr.pretty m; + if M.tracing then M.trace "priv" "st: %a" BaseComponents.pretty st; let getg x = let r = getg x in - if M.tracing then M.trace "priv" "getg %a -> %a\n" V.pretty x G.pretty r; + if M.tracing then M.trace "priv" "getg %a -> %a" V.pretty x G.pretty r; r in let r = lock ask getg st m in - if M.tracing then M.traceu "priv" "-> %a\n" BaseComponents.pretty r; + if M.tracing then M.traceu "priv" "-> %a" BaseComponents.pretty r; r let unlock ask getg sideg st m = - if M.tracing then M.traceli "priv" "unlock %a\n" LockDomain.Addr.pretty m; - if M.tracing then M.trace "priv" "st: %a\n" BaseComponents.pretty st; + if M.tracing then M.traceli "priv" "unlock %a" LockDomain.Addr.pretty m; + if M.tracing then M.trace "priv" "st: %a" BaseComponents.pretty st; let getg x = let r = getg x in - if M.tracing then M.trace "priv" "getg %a -> %a\n" V.pretty x G.pretty r; + if M.tracing then M.trace "priv" "getg %a -> %a" V.pretty x G.pretty r; r in let sideg x v = - if M.tracing then M.trace "priv" "sideg %a %a\n" V.pretty x G.pretty v; + if M.tracing then M.trace "priv" "sideg %a %a" V.pretty x G.pretty v; sideg x v in let r = unlock ask getg sideg st m in - if M.tracing then M.traceu "priv" "-> %a\n" BaseComponents.pretty r; + if M.tracing then M.traceu "priv" "-> %a" BaseComponents.pretty r; r let enter_multithreaded ask getg sideg st = - if M.tracing then M.traceli "priv" "enter_multithreaded\n"; - if M.tracing then M.trace "priv" "st: %a\n" BaseComponents.pretty st; + if M.tracing then M.traceli "priv" "enter_multithreaded"; + if M.tracing then M.trace "priv" "st: %a" BaseComponents.pretty st; let getg x = let r = getg x in - if M.tracing then M.trace "priv" "getg %a -> %a\n" V.pretty x G.pretty r; + if M.tracing then M.trace "priv" "getg %a -> %a" V.pretty x G.pretty r; r in let sideg x v = - if M.tracing then M.trace "priv" "sideg %a %a\n" V.pretty x G.pretty v; + if M.tracing then M.trace "priv" "sideg %a %a" V.pretty x G.pretty v; sideg x v in let r = enter_multithreaded ask getg sideg st in - if M.tracing then M.traceu "priv" "-> %a\n" BaseComponents.pretty r; + if M.tracing then M.traceu "priv" "-> %a" BaseComponents.pretty r; r let threadenter ask st = - if M.tracing then M.traceli "priv" "threadenter\n"; - if M.tracing then M.trace "priv" "st: %a\n" BaseComponents.pretty st; + if M.tracing then M.traceli "priv" "threadenter"; + if M.tracing then M.trace "priv" "st: %a" BaseComponents.pretty st; let r = threadenter ask st in - if M.tracing then M.traceu "priv" "-> %a\n" BaseComponents.pretty r; + if M.tracing then M.traceu "priv" "-> %a" BaseComponents.pretty r; r let sync ask getg sideg st reason = - if M.tracing then M.traceli "priv" "sync\n"; - if M.tracing then M.trace "priv" "st: %a\n" BaseComponents.pretty st; + if M.tracing then M.traceli "priv" "sync"; + if M.tracing then M.trace "priv" "st: %a" BaseComponents.pretty st; let getg x = let r = getg x in - if M.tracing then M.trace "priv" "getg %a -> %a\n" V.pretty x G.pretty r; + if M.tracing then M.trace "priv" "getg %a -> %a" V.pretty x G.pretty r; r in let sideg x v = - if M.tracing then M.trace "priv" "sideg %a %a\n" V.pretty x G.pretty v; + if M.tracing then M.trace "priv" "sideg %a %a" V.pretty x G.pretty v; sideg x v in let r = sync ask getg sideg st reason in - if M.tracing then M.traceu "priv" "-> %a\n" BaseComponents.pretty r; + if M.tracing then M.traceu "priv" "-> %a" BaseComponents.pretty r; r end diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 3b23dc03fc..393be0a857 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -108,7 +108,7 @@ struct let save_expr lval expr = match mustPointTo ctx (AddrOf lval) with | Some clval -> - if M.tracing then M.tracel "condvars" "CondVars: saving %a = %a\n" Mval.Exp.pretty clval d_exp expr; + if M.tracing then M.tracel "condvars" "CondVars: saving %a = %a" Mval.Exp.pretty clval d_exp expr; D.add clval (D.V.singleton expr) d (* if lval must point to clval, add expr *) | None -> d in diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index a3943651c0..2072097532 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -206,16 +206,16 @@ struct let octx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "do_emits" ~splits ~post_all octx'' n od in n, repr @@ S.event ctx' e octx' in - if M.tracing then M.traceli "event" "%a\n before: %a\n" Events.pretty e D.pretty ctx.local; + if M.tracing then M.traceli "event" "%a\n before: %a" Events.pretty e D.pretty ctx.local; let d, q = map_deadcode f @@ spec_list2 ctx.local octx.local in - if M.tracing then M.traceu "event" "%a\n after:%a\n" Events.pretty e D.pretty d; + if M.tracing then M.traceu "event" "%a\n after:%a" Events.pretty e D.pretty d; do_sideg ctx !sides; do_spawns ctx !spawns; do_splits ctx d !splits !emits; let d = do_emits ctx !emits d q in if q then raise Deadcode else ctx_with_local ctx d in - if M.tracing then M.traceli "event" "do_emits:\n"; + if M.tracing then M.traceli "event" "do_emits:"; let emits = if dead then List.filter Events.emit_on_deadcode emits @@ -224,7 +224,7 @@ struct in (* [emits] is in reverse order. *) let ctx' = List.fold_left do_emit (ctx_with_local ctx xs) (List.rev emits) in - if M.tracing then M.traceu "event" "\n"; + if M.tracing then M.traceu "event" ""; ctx'.local and branch (ctx:(D.t, G.t, C.t, V.t) ctx) (e:exp) (tv:bool) = @@ -247,15 +247,15 @@ struct (* Explicitly polymorphic type required here for recursive GADT call in ask. *) and query': type a. querycache:Obj.t Queries.Hashtbl.t -> Queries.Set.t -> (D.t, G.t, C.t, V.t) ctx -> a Queries.t -> a Queries.result = fun ~querycache asked ctx q -> let anyq = Queries.Any q in - if M.tracing then M.traceli "query" "query %a\n" Queries.Any.pretty anyq; + if M.tracing then M.traceli "query" "query %a" Queries.Any.pretty anyq; let r = match Queries.Hashtbl.find_option querycache anyq with | Some r -> - if M.tracing then M.trace "query" "cached\n"; + if M.tracing then M.trace "query" "cached"; Obj.obj r | None -> let module Result = (val Queries.Result.lattice q) in if Queries.Set.mem anyq asked then ( - if M.tracing then M.trace "query" "cycle\n"; + if M.tracing then M.trace "query" "cycle"; Result.top () (* query cycle *) ) else diff --git a/src/analyses/symbLocks.ml b/src/analyses/symbLocks.ml index c237967a7a..6fd18de6ff 100644 --- a/src/analyses/symbLocks.ml +++ b/src/analyses/symbLocks.ml @@ -65,11 +65,11 @@ struct | a when not (Queries.ES.is_bot a) -> Queries.ES.add e a | _ -> Queries.ES.singleton e in - if M.tracing then M.tracel "symb_locks" "get_all_locks exps %a = %a\n" d_plainexp e Queries.ES.pretty exps; - if M.tracing then M.tracel "symb_locks" "get_all_locks st = %a\n" D.pretty st; + if M.tracing then M.tracel "symb_locks" "get_all_locks exps %a = %a" d_plainexp e Queries.ES.pretty exps; + if M.tracing then M.tracel "symb_locks" "get_all_locks st = %a" D.pretty st; let add_locks x xs = PS.union (get_locks x st) xs in let r = Queries.ES.fold add_locks exps (PS.empty ()) in - if M.tracing then M.tracel "symb_locks" "get_all_locks %a = %a\n" d_plainexp e PS.pretty r; + if M.tracing then M.tracel "symb_locks" "get_all_locks %a = %a" d_plainexp e PS.pretty r; r let same_unknown_index (ask: Queries.ask) exp slocks = @@ -138,7 +138,7 @@ struct *) let one_perelem (e,a,l) xs = (* ignore (printf "one_perelem (%a,%a,%a)\n" Exp.pretty e Exp.pretty a Exp.pretty l); *) - if M.tracing then M.tracel "symb_locks" "one_perelem (%a,%a,%a)\n" Exp.pretty e Exp.pretty a Exp.pretty l; + if M.tracing then M.tracel "symb_locks" "one_perelem (%a,%a,%a)" Exp.pretty e Exp.pretty a Exp.pretty l; match Exp.fold_offs (Exp.replace_base (dummyFunDec.svar,`NoOffset) e l) with | Some (v, o) -> (* ignore (printf "adding lock %s\n" l); *) diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index 85dabd1c9d..960c981013 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -40,7 +40,7 @@ struct | _ -> false ) d in - if M.tracing then M.trace "taintPC" "returning from %s: tainted vars: %a\n without locals: %a\n" f.svar.vname D.pretty d D.pretty d_return; + if M.tracing then M.trace "taintPC" "returning from %s: tainted vars: %a\n without locals: %a" f.svar.vname D.pretty d D.pretty d_return; d_return @@ -49,7 +49,7 @@ struct [ctx.local, (D.bot ())] let combine_env ctx lval fexp f args fc au f_ask = - if M.tracing then M.trace "taintPC" "combine for %s in TaintPC: tainted: in function: %a before call: %a\n" f.svar.vname D.pretty au D.pretty ctx.local; + if M.tracing then M.trace "taintPC" "combine for %s in TaintPC: tainted: in function: %a before call: %a" f.svar.vname D.pretty au D.pretty ctx.local; D.union ctx.local au let combine_assign ctx (lvalOpt:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = diff --git a/src/analyses/threadEscape.ml b/src/analyses/threadEscape.ml index 21fa3a8cfb..1d8529288e 100644 --- a/src/analyses/threadEscape.ml +++ b/src/analyses/threadEscape.ml @@ -36,7 +36,7 @@ struct Queries.AD.fold to_extra ad (D.empty ()) (* Ignore soundness warnings, as invalidation proper will raise them. *) | ad -> - if M.tracing then M.tracel "escape" "reachable %a: %a\n" d_exp e Queries.AD.pretty ad; + if M.tracing then M.tracel "escape" "reachable %a: %a" d_exp e Queries.AD.pretty ad; D.empty () let mpt (ask: Queries.ask) e: D.t = @@ -50,7 +50,7 @@ struct AD.fold to_extra (AD.remove UnknownPtr ad) (D.empty ()) (* Ignore soundness warnings, as invalidation proper will raise them. *) | ad -> - if M.tracing then M.tracel "escape" "mpt %a: %a\n" d_exp e AD.pretty ad; + if M.tracing then M.tracel "escape" "mpt %a: %a" d_exp e AD.pretty ad; D.empty () let thread_id ctx = @@ -171,7 +171,7 @@ struct (* not reusing fctx.local to avoid unnecessarily early join of extra *) let escaped = reachable (Analyses.ask_of_ctx ctx) ptc_arg in let escaped = D.filter (fun v -> not v.vglob) escaped in - if M.tracing then M.tracel "escape" "%a: %a\n" d_exp ptc_arg D.pretty escaped; + if M.tracing then M.tracel "escape" "%a: %a" d_exp ptc_arg D.pretty escaped; let thread_id = thread_id ctx in emit_escape_event ctx escaped; side_effect_escape ctx escaped thread_id; diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index 9ed6da7c60..63d0bf3f32 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -25,7 +25,7 @@ struct (* transfer functions *) let assign ctx (lval:lval) (rval:exp) : D.t = - if M.tracing then M.tracel "tmpSpecial" "assignment of %a\n" d_lval lval; + if M.tracing then M.tracel "tmpSpecial" "assignment of %a" d_lval lval; (* Invalidate all entrys from the map that are possibly written by the assignment *) invalidate (Analyses.ask_of_ctx ctx) (mkAddrOf lval) ctx.local @@ -44,8 +44,8 @@ struct (* Just dbg prints *) (if M.tracing then match lval with - | Some lv -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with lval %a\n" f.vname d_lval lv - | _ -> if M.tracing then M.tracel "tmpSpecial" "Special: %s\n" f.vname); + | Some lv -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with lval %a" f.vname d_lval lv + | _ -> if M.tracing then M.tracel "tmpSpecial" "Special: %s" f.vname); let desc = LibraryFunctions.find f in @@ -76,7 +76,7 @@ struct in - if M.tracing then M.tracel "tmpSpecial" "Result: %a\n\n" D.pretty d; + if M.tracing then M.tracel "tmpSpecial" "Result: %a\n" D.pretty d; d diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 30b36404af..16e8c8490d 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -307,7 +307,7 @@ struct else (Messages.warn ~category:Analyzer ~msg:("Keep " ^sprint 80 (Exp.pretty () a)^" because of "^sprint 80 (Exp.pretty () b)) (); r) Messages.warn ~category:Analyzer ~msg:(sprint 80 (Exp.pretty () b) ^" changed lvalues: "^sprint 80 (Queries.LS.pretty () bls)) (); *) - if M.tracing then M.tracel "var_eq" "may_change %a %a = %B\n" CilType.Exp.pretty b CilType.Exp.pretty a r; + if M.tracing then M.tracel "var_eq" "may_change %a %a = %B" CilType.Exp.pretty b CilType.Exp.pretty a r; r (* Remove elements, that would change if the given lval would change.*) @@ -459,7 +459,7 @@ struct let remove_reachable ~deep ask es st = let rs = reachables ~deep ask es in - if M.tracing then M.tracel "var_eq" "remove_reachable %a: %a\n" (Pretty.d_list ", " d_exp) es AD.pretty rs; + if M.tracing then M.tracel "var_eq" "remove_reachable %a: %a" (Pretty.d_list ", " d_exp) es AD.pretty rs; (* Prior to https://github.com/goblint/analyzer/pull/694 checks were done "in the other direction": each expression in st was checked for reachability from es/rs using very conservative but also unsound reachable_from. It is unknown, why that was necessary. *) @@ -516,7 +516,7 @@ struct D.B.fold add es (Queries.ES.empty ()) let rec eq_set_clos e s = - if M.tracing then M.traceli "var_eq" "eq_set_clos %a\n" d_plainexp e; + if M.tracing then M.traceli "var_eq" "eq_set_clos %a" d_plainexp e; let r = match e with | AddrOf (Mem (BinOp (IndexPI, a, i, _)), os) -> (* convert IndexPI to Index offset *) @@ -552,7 +552,7 @@ struct | CastE (t,e) -> Queries.ES.map (fun e -> CastE (t,e)) (eq_set_clos e s) in - if M.tracing then M.traceu "var_eq" "eq_set_clos %a = %a\n" d_plainexp e Queries.ES.pretty r; + if M.tracing then M.traceu "var_eq" "eq_set_clos %a = %a" d_plainexp e Queries.ES.pretty r; r @@ -562,7 +562,7 @@ struct Queries.ID.of_bool (Cilfacade.get_ikind t) true | Queries.EqualSet e -> let r = eq_set_clos e ctx.local in - if M.tracing then M.tracel "var_eq" "equalset %a = %a\n" d_plainexp e Queries.ES.pretty r; + if M.tracing then M.tracel "var_eq" "equalset %a = %a" d_plainexp e Queries.ES.pretty r; r | Queries.Invariant context when GobConfig.get_bool "witness.invariant.exact" -> (* only exact equalities here *) let scope = Node.find_fundec ctx.node in diff --git a/src/cdomain/value/cdomains/addressDomain.ml b/src/cdomain/value/cdomains/addressDomain.ml index 263c1033bb..b153e71d4e 100644 --- a/src/cdomain/value/cdomains/addressDomain.ml +++ b/src/cdomain/value/cdomains/addressDomain.ml @@ -194,19 +194,19 @@ struct let equal x y = x == y || equal x y let widen x y = - if M.tracing then M.traceli "ad" "widen %a %a\n" pretty x pretty y; + if M.tracing then M.traceli "ad" "widen %a %a" pretty x pretty y; let r = widen x y in - if M.tracing then M.traceu "ad" "-> %a\n" pretty r; + if M.tracing then M.traceu "ad" "-> %a" pretty r; r let join x y = - if M.tracing then M.traceli "ad" "join %a %a\n" pretty x pretty y; + if M.tracing then M.traceli "ad" "join %a %a" pretty x pretty y; let r = join x y in - if M.tracing then M.traceu "ad" "-> %a\n" pretty r; + if M.tracing then M.traceu "ad" "-> %a" pretty r; r let leq x y = - if M.tracing then M.traceli "ad" "leq %a %a\n" pretty x pretty y; + if M.tracing then M.traceli "ad" "leq %a %a" pretty x pretty y; let r = x == y || leq x y in (* short-circuit with physical equality, not benchmarked *) - if M.tracing then M.traceu "ad" "-> %B\n" r; + if M.tracing then M.traceu "ad" "-> %B" r; r let null_ptr = singleton Addr.NullPtr @@ -383,15 +383,15 @@ struct let narrow x y = merge (fun x y -> widen x (join x y)) narrow x y let meet x y = - if M.tracing then M.traceli "ad" "meet %a %a\n" pretty x pretty y; + if M.tracing then M.traceli "ad" "meet %a %a" pretty x pretty y; let r = meet x y in - if M.tracing then M.traceu "ad" "-> %a\n" pretty r; + if M.tracing then M.traceu "ad" "-> %a" pretty r; r let narrow x y = - if M.tracing then M.traceli "ad" "narrow %a %a\n" pretty x pretty y; + if M.tracing then M.traceli "ad" "narrow %a %a" pretty x pretty y; let r = narrow x y in - if M.tracing then M.traceu "ad" "-> %a\n" pretty r; + if M.tracing then M.traceu "ad" "-> %a" pretty r; r let filter f ad = fold (fun addr ad -> if f addr then add addr ad else ad) ad (empty ()) diff --git a/src/cdomain/value/cdomains/arrayDomain.ml b/src/cdomain/value/cdomains/arrayDomain.ml index ebdaf90cd3..8958ee2303 100644 --- a/src/cdomain/value/cdomains/arrayDomain.ml +++ b/src/cdomain/value/cdomains/arrayDomain.ml @@ -532,7 +532,7 @@ struct let move_if_affected ?replace_with_const = move_if_affected_with_length ?replace_with_const None let set_with_length length (ask:VDQ.t) x (i,_) a = - if M.tracing then M.trace "update_offset" "part array set_with_length %a %s %a\n" pretty x (BatOption.map_default Basetype.CilExp.show "None" i) Val.pretty a; + if M.tracing then M.trace "update_offset" "part array set_with_length %a %s %a" pretty x (BatOption.map_default Basetype.CilExp.show "None" i) Val.pretty a; match i with | Some ie when CilType.Exp.equal ie (Lazy.force Offset.Index.Exp.all) -> (* TODO: Doesn't seem to work for unassume. *) diff --git a/src/cdomain/value/cdomains/floatDomain.ml b/src/cdomain/value/cdomains/floatDomain.ml index 503feba88e..c86770826c 100644 --- a/src/cdomain/value/cdomains/floatDomain.ml +++ b/src/cdomain/value/cdomains/floatDomain.ml @@ -374,9 +374,9 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | NaN, _ | _, NaN -> (0,0) (* comparisons involving NaN always return false *) | Top, _ | _, Top -> (0,1) (* comparisons with Top yield top *) (* neither of the arguments below is Top/Bot/NaN *) - | v1, v2 when v1 = min -> - (* v1 is the minimal element w.r.t. the order *) - if v2 <> min || reflexive then + | v1, v2 when v1 = min -> + (* v1 is the minimal element w.r.t. the order *) + if v2 <> min || reflexive then (* v2 is different, i.e., greater or the relation is reflexive *) (1,1) else @@ -389,7 +389,7 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct if v2 = max && reflexive then (* v2 is also maximal and the relation is reflexive *) (1,1) - else + else (0,0) | _, v2 when v2 = max -> (1,1) (* first argument cannot be max *) | _ -> (0, 1) @@ -652,7 +652,7 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct (** returns the min of the given mfun once computed with rounding mode Up and once with rounding mode down*) let safe_mathfun_down mfun f = min (mfun Down f) (mfun Up f) - (** This function does two things: + (** This function does two things: ** 1. projects l and h onto the interval [0, k*pi] (for k = 2 this is the phase length of sin/cos, for k = 1 it is the phase length of tan) ** 2. compresses/transforms the interval [0, k*pi] to the interval [0, 1] to ease further computations ** i.e. the function computes dist = distance, l'' = (l/(k*pi)) - floor(l/(k*pi)), h'' = (h/(k*pi)) - floor(h/(k*pi))*) @@ -684,7 +684,7 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct let eval_cos_cfun l h = let (dist, l'', h'') = project_and_compress l h 2. in - if Messages.tracing then Messages.trace "CstubsTrig" "cos: dist %s; l'' %s; h'' %s\n" (Float_t.to_string dist) (Float_t.to_string l'') (Float_t.to_string h''); + if Messages.tracing then Messages.trace "CstubsTrig" "cos: dist %s; l'' %s; h'' %s" (Float_t.to_string dist) (Float_t.to_string l'') (Float_t.to_string h''); if (dist <= Float_t.of_float Down 0.5) && (h'' <= Float_t.of_float Down 0.5) && (l'' <= h'') then (* case: monotonic decreasing interval*) Interval (safe_mathfun_down Float_t.cos h, safe_mathfun_up Float_t.cos l) @@ -707,7 +707,7 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct let eval_tan_cfun l h = let (dist, l'', h'') = project_and_compress l h 1. in - if Messages.tracing then Messages.trace "CstubsTrig" "tan: dist %s; l'' %s; h'' %s\n" (Float_t.to_string dist) (Float_t.to_string l'') (Float_t.to_string h''); + if Messages.tracing then Messages.trace "CstubsTrig" "tan: dist %s; l'' %s; h'' %s" (Float_t.to_string dist) (Float_t.to_string l'') (Float_t.to_string h''); if (dist <= Float_t.of_float Down 1.) && (Bool.not ((l'' <= Float_t.of_float Up 0.5) && (h'' >= Float_t.of_float Up 0.5))) then (* case: monotonic increasing interval*) Interval (safe_mathfun_down Float_t.tan l, safe_mathfun_up Float_t.tan h) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index 6220355ec6..0eab8fe82e 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -427,7 +427,7 @@ module Size = struct (* size in bits as int, range as int64 *) let is_cast_injective ~from_type ~to_type = let (from_min, from_max) = range (Cilfacade.get_ikind from_type) in let (to_min, to_max) = range (Cilfacade.get_ikind to_type) in - if M.tracing then M.trace "int" "is_cast_injective %a (%a, %a) -> %a (%a, %a)\n" CilType.Typ.pretty from_type GobZ.pretty from_min GobZ.pretty from_max CilType.Typ.pretty to_type GobZ.pretty to_min GobZ.pretty to_max; + if M.tracing then M.trace "int" "is_cast_injective %a (%a, %a) -> %a (%a, %a)" CilType.Typ.pretty from_type GobZ.pretty from_min GobZ.pretty from_max CilType.Typ.pretty to_type GobZ.pretty to_min GobZ.pretty to_max; Z.compare to_min from_min <= 0 && Z.compare from_max to_max <= 0 let cast t x = (* TODO: overflow is implementation-dependent! *) @@ -442,7 +442,7 @@ module Size = struct (* size in bits as int, range as int64 *) else if Z.lt y a then Z.add y c else y in - if M.tracing then M.tracel "cast" "Cast %a to range [%a, %a] (%a) = %a (%s in int64)\n" GobZ.pretty x GobZ.pretty a GobZ.pretty b GobZ.pretty c GobZ.pretty y (if is_int64_big_int y then "fits" else "does not fit"); + if M.tracing then M.tracel "cast" "Cast %a to range [%a, %a] (%a) = %a (%s in int64)" GobZ.pretty x GobZ.pretty a GobZ.pretty b GobZ.pretty c GobZ.pretty y (if is_int64_big_int y then "fits" else "does not fit"); y let min_range_sign_agnostic x = @@ -683,7 +683,7 @@ struct norm ik @@ Some (l2,u2) |> fst let widen ik x y = let r = widen ik x y in - if M.tracing && not (equal x y) then M.tracel "int" "interval widen %a %a -> %a\n" pretty x pretty y pretty r; + if M.tracing && not (equal x y) then M.tracel "int" "interval widen %a %a -> %a" pretty x pretty y pretty r; assert (leq x y); (* TODO: remove for performance reasons? *) r @@ -950,7 +950,7 @@ struct let refine_with_congruence ik x y = let refn = refine_with_congruence ik x y in - if M.tracing then M.trace "refine" "int_refine_with_congruence %a %a -> %a\n" pretty x pretty y pretty refn; + if M.tracing then M.trace "refine" "int_refine_with_congruence %a %a -> %a" pretty x pretty y pretty refn; refn let refine_with_interval ik a b = meet ik a b @@ -2822,7 +2822,7 @@ struct let leq x y = let res = leq x y in - if M.tracing then M.trace "congruence" "leq %a %a -> %a \n" pretty x pretty y pretty (Some (Z.of_int (Bool.to_int res), Z.zero)) ; + if M.tracing then M.trace "congruence" "leq %a %a -> %a " pretty x pretty y pretty (Some (Z.of_int (Bool.to_int res), Z.zero)) ; res let join ik (x:t) y = @@ -2834,7 +2834,7 @@ struct let join ik (x:t) y = let res = join ik x y in - if M.tracing then M.trace "congruence" "join %a %a -> %a\n" pretty x pretty y pretty res; + if M.tracing then M.trace "congruence" "join %a %a -> %a" pretty x pretty y pretty res; res @@ -2861,7 +2861,7 @@ struct let meet ik x y = let res = meet ik x y in - if M.tracing then M.trace "congruence" "meet %a %a -> %a\n" pretty x pretty y pretty res; + if M.tracing then M.trace "congruence" "meet %a %a -> %a" pretty x pretty y pretty res; res let to_int = function Some (c, m) when m =: Z.zero -> Some c | _ -> None @@ -2919,14 +2919,14 @@ struct let cast_to ?torg ?no_ov (t : Cil.ikind) x = let pretty_bool _ x = Pretty.text (string_of_bool x) in let res = cast_to ?torg ?no_ov t x in - if M.tracing then M.trace "cong-cast" "Cast %a to %a (no_ov: %a) = %a\n" pretty x Cil.d_ikind t (Pretty.docOpt (pretty_bool ())) no_ov pretty res; + if M.tracing then M.trace "cong-cast" "Cast %a to %a (no_ov: %a) = %a" pretty x Cil.d_ikind t (Pretty.docOpt (pretty_bool ())) no_ov pretty res; res let widen = join let widen ik x y = let res = widen ik x y in - if M.tracing then M.trace "congruence" "widen %a %a -> %a\n" pretty x pretty y pretty res; + if M.tracing then M.trace "congruence" "widen %a %a -> %a" pretty x pretty y pretty res; res let narrow = meet @@ -2958,7 +2958,7 @@ struct let shift_right ik x y = let res = shift_right ik x y in - if M.tracing then M.trace "congruence" "shift_right : %a %a becomes %a \n" pretty x pretty y pretty res; + if M.tracing then M.trace "congruence" "shift_right : %a %a becomes %a " pretty x pretty y pretty res; res let shift_left ik x y = @@ -2989,7 +2989,7 @@ struct let shift_left ik x y = let res = shift_left ik x y in - if M.tracing then M.trace "congruence" "shift_left : %a %a becomes %a \n" pretty x pretty y pretty res; + if M.tracing then M.trace "congruence" "shift_left : %a %a becomes %a " pretty x pretty y pretty res; res (* Handle unsigned overflows. @@ -3030,7 +3030,7 @@ struct let mul ?no_ov ik x y = let res = mul ?no_ov ik x y in - if M.tracing then M.trace "congruence" "mul : %a %a -> %a \n" pretty x pretty y pretty res; + if M.tracing then M.trace "congruence" "mul : %a %a -> %a " pretty x pretty y pretty res; res let neg ?(no_ov=false) ik x = @@ -3124,7 +3124,7 @@ struct normalize ik (Some (c1, Z.gcd m1 (Z.gcd c2 m2))) let rem ik x y = let res = rem ik x y in - if M.tracing then M.trace "congruence" "rem : %a %a -> %a \n" pretty x pretty y pretty res; + if M.tracing then M.trace "congruence" "rem : %a %a -> %a " pretty x pretty y pretty res; res let div ?(no_ov=false) ik x y = @@ -3166,14 +3166,14 @@ struct let ge ik x y = let res = ge ik x y in - if M.tracing then M.trace "congruence" "greater or equal : %a %a -> %a \n" pretty x pretty y pretty res; + if M.tracing then M.trace "congruence" "greater or equal : %a %a -> %a " pretty x pretty y pretty res; res let le ik x y = comparison ik (<=:) x y let le ik x y = let res = le ik x y in - if M.tracing then M.trace "congruence" "less or equal : %a %a -> %a \n" pretty x pretty y pretty res; + if M.tracing then M.trace "congruence" "less or equal : %a %a -> %a " pretty x pretty y pretty res; res let gt ik x y = comparison ik (>:) x y @@ -3181,14 +3181,14 @@ struct let gt ik x y = let res = gt ik x y in - if M.tracing then M.trace "congruence" "greater than : %a %a -> %a \n" pretty x pretty y pretty res; + if M.tracing then M.trace "congruence" "greater than : %a %a -> %a " pretty x pretty y pretty res; res let lt ik x y = comparison ik (<:) x y let lt ik x y = let res = lt ik x y in - if M.tracing then M.trace "congruence" "less than : %a %a -> %a \n" pretty x pretty y pretty res; + if M.tracing then M.trace "congruence" "less than : %a %a -> %a " pretty x pretty y pretty res; res let invariant_ikind e ik x = @@ -3232,7 +3232,7 @@ struct | Some (l, u) -> Pretty.dprintf "[%a,%a]" GobZ.pretty l GobZ.pretty u | _ -> Pretty.text ("Display Error") in let refn = refine_with_interval ik cong intv in - if M.tracing then M.trace "refine" "cong_refine_with_interval %a %a -> %a\n" pretty cong pretty_intv intv pretty refn; + if M.tracing then M.trace "refine" "cong_refine_with_interval %a %a -> %a" pretty cong pretty_intv intv pretty refn; refn let refine_with_congruence ik a b = meet ik a b @@ -3533,7 +3533,7 @@ module IntDomTupleImpl = struct List.iter (fun f -> dt := f !dt) (refine_functions ik); quit_loop := equal old_dt !dt; if is_bot !dt then dt := bot_of ik; quit_loop := true; - if M.tracing then M.trace "cong-refine-loop" "old: %a, new: %a\n" pretty old_dt pretty !dt; + if M.tracing then M.trace "cong-refine-loop" "old: %a, new: %a" pretty old_dt pretty !dt; done; | _ -> () ); !dt diff --git a/src/cdomain/value/cdomains/offset.ml b/src/cdomain/value/cdomains/offset.ml index c0b44717aa..7166e0642e 100644 --- a/src/cdomain/value/cdomains/offset.ml +++ b/src/cdomain/value/cdomains/offset.ml @@ -217,7 +217,7 @@ struct let semantic_equal ~typ1 xoffs ~typ2 yoffs = let x_index = to_index ~typ:typ1 xoffs in let y_index = to_index ~typ:typ2 yoffs in - if M.tracing then M.tracel "addr" "xoffs=%a typ1=%a xindex=%a yoffs=%a typ2=%a yindex=%a\n" pretty xoffs d_plaintype typ1 Idx.pretty x_index pretty yoffs d_plaintype typ2 Idx.pretty y_index; + if M.tracing then M.tracel "addr" "xoffs=%a typ1=%a xindex=%a yoffs=%a typ2=%a yindex=%a" pretty xoffs d_plaintype typ1 Idx.pretty x_index pretty yoffs d_plaintype typ2 Idx.pretty y_index; Idx.to_bool (Idx.eq x_index y_index) include Lattice.NoBotTop diff --git a/src/cdomain/value/cdomains/structDomain.ml b/src/cdomain/value/cdomains/structDomain.ml index b2d47c7e53..d24c287ad0 100644 --- a/src/cdomain/value/cdomains/structDomain.ml +++ b/src/cdomain/value/cdomains/structDomain.ml @@ -150,7 +150,7 @@ struct let create = hs_create let replace s field value = - if Messages.tracing then Messages.tracel "simplesets" "Normalize top Replace - s:\n%a\nfield:%a\nvalue: %a\n---------\n" HS.pretty s Basetype.CilField.pretty field Val.pretty value; + if Messages.tracing then Messages.tracel "simplesets" "Normalize top Replace - s:\n%a\nfield:%a\nvalue: %a\n---------" HS.pretty s Basetype.CilField.pretty field Val.pretty value; HS.map (fun s -> SS.replace s field value) s let get = hs_get @@ -209,7 +209,7 @@ struct let join_with_fct f x y = let appended = List.append (HS.elements x) (HS.elements y) in - if Messages.tracing then Messages.tracel "simplesets-fct" "Join-fct start!\nx: %a\ny: %a\n" pretty x pretty y; + if Messages.tracing then Messages.tracel "simplesets-fct" "Join-fct start!\nx: %a\ny: %a" pretty x pretty y; let reduce_list_with_fct join_f xs s = let rec aux unique remaining = match remaining with @@ -218,14 +218,14 @@ struct let (overlapping, rem_uniq) = List.partition (fun ss -> SS.leq h ss || SS.leq ss h ) unique in let joined = List.fold_left (fun el acc -> let res = join_f acc el in - if Messages.tracing then Messages.tracel "simplesets-fct" "Join-fct joining others!\nacc: %a\nel: %a\nres: %a\n" SS.pretty acc SS.pretty el SS.pretty res; + if Messages.tracing then Messages.tracel "simplesets-fct" "Join-fct joining others!\nacc: %a\nel: %a\nres: %a" SS.pretty acc SS.pretty el SS.pretty res; res ) h overlapping in aux (joined::rem_uniq) t in aux [] xs in let res = reduce_list_with_fct (SS.join_with_fct f) appended x in - if Messages.tracing then Messages.tracel "simplesets-fct" "Join-fct result!\nx: %a\ny: %a\nconverted: %a\nres: %a\n" pretty x pretty y pretty (HS.of_list appended) pretty res; + if Messages.tracing then Messages.tracel "simplesets-fct" "Join-fct result!\nx: %a\ny: %a\nconverted: %a\nres: %a" pretty x pretty y pretty (HS.of_list appended) pretty res; res let join = join_with_fct Val.join @@ -313,14 +313,14 @@ struct aux (joined::rem_uniq) t in let res = aux [] (HS.elements s) in - if Messages.tracing then Messages.tracel "reduce-key" "Reduced - s:\n%a\nto:\n%a\n---------\n" HS.pretty s HS.pretty res; + if Messages.tracing then Messages.tracel "reduce-key" "Reduced - s:\n%a\nto:\n%a\n---------" HS.pretty s HS.pretty res; (res, Some key) let reduce_key (x: t): t = reduce_key_with_fct (SS.join) x let replace (s,k) field value : t = let join_set s =if HS.is_bot s then s else HS.singleton (join_ss s) in - if Messages.tracing then Messages.tracel "keyedsets" "Replace - s:\n%a\nfield:%a\nvalue: %a\n---------\n" HS.pretty s Basetype.CilField.pretty field Val.pretty value ; + if Messages.tracing then Messages.tracel "keyedsets" "Replace - s:\n%a\nfield:%a\nvalue: %a\n---------" HS.pretty s Basetype.CilField.pretty field Val.pretty value ; let replaced = HS.map (fun s -> SS.replace s field value) s in let result_key = match find_key_field (s,k) with @@ -412,7 +412,7 @@ struct let join_with_fct f (x, k) (y, _) = let appended = List.append (HS.elements x) (HS.elements y) in - if Messages.tracing then Messages.tracel "bettersets" "Join-fct start!\nx: %a\ny: %a\n" HS.pretty x HS.pretty y; + if Messages.tracing then Messages.tracel "bettersets" "Join-fct start!\nx: %a\ny: %a" HS.pretty x HS.pretty y; let reduce_list_key_with_fct join_f (xs: variant list) (x: t) = match find_key_field x with | None -> x @@ -428,14 +428,14 @@ struct ) unique in let joined = List.fold_left (fun el acc -> let res = join_f acc el in - if Messages.tracing then Messages.tracel "bettersets" "Join-fct joining others!\nacc: %a\nel: %a\nres: %a\n" SS.pretty acc SS.pretty el SS.pretty res; + if Messages.tracing then Messages.tracel "bettersets" "Join-fct joining others!\nacc: %a\nel: %a\nres: %a" SS.pretty acc SS.pretty el SS.pretty res; res ) h overlapping in aux (joined::rem_uniq) t in (aux [] xs, Some key) in let res = reduce_list_key_with_fct (SS.join_with_fct f) appended (x,k) in - if Messages.tracing then Messages.tracel "bettersets" "Join-fct result!\nx: %a\ny: %a\nconverted: %a\nres: %a\n" HS.pretty x HS.pretty y HS.pretty (HS.of_list appended) pretty res; + if Messages.tracing then Messages.tracel "bettersets" "Join-fct result!\nx: %a\ny: %a\nconverted: %a\nres: %a" HS.pretty x HS.pretty y HS.pretty (HS.of_list appended) pretty res; res let join = join_with_fct Val.join diff --git a/src/cdomain/value/cdomains/valueDomain.ml b/src/cdomain/value/cdomains/valueDomain.ml index 61931bd891..2f2b7a774d 100644 --- a/src/cdomain/value/cdomains/valueDomain.ml +++ b/src/cdomain/value/cdomains/valueDomain.ml @@ -857,7 +857,7 @@ struct (* Funny, this does not compile without the final type annotation! *) let rec eval_offset (ask: VDQ.t) f (x: t) (offs:offs) (exp:exp option) (v:lval option) (t:typ): t = let rec do_eval_offset (ask:VDQ.t) f (x:t) (offs:offs) (exp:exp option) (l:lval option) (o:offset option) (v:lval option) (t:typ): t = - if M.tracing then M.traceli "eval_offset" "do_eval_offset %a %a (%a)\n" pretty x Offs.pretty offs (Pretty.docOpt (CilType.Exp.pretty ())) exp; + if M.tracing then M.traceli "eval_offset" "do_eval_offset %a %a (%a)" pretty x Offs.pretty offs (Pretty.docOpt (CilType.Exp.pretty ())) exp; let r = match x, offs with | Blob((va, _, orig) as c), `Index (_, ox) -> @@ -922,7 +922,7 @@ struct | _ -> M.warn ~category:Imprecise ~tags:[Category Program] "Trying to read an index, but was not given an array (%a)" pretty x; top () end in - if M.tracing then M.traceu "eval_offset" "do_eval_offset -> %a\n" pretty r; + if M.tracing then M.traceu "eval_offset" "do_eval_offset -> %a" pretty r; r in let l, o = match exp with @@ -933,7 +933,7 @@ struct let update_offset ?(blob_destructive=false) (ask: VDQ.t) (x:t) (offs:offs) (value:t) (exp:exp option) (v:lval) (t:typ): t = let rec do_update_offset (ask:VDQ.t) (x:t) (offs:offs) (value:t) (exp:exp option) (l:lval option) (o:offset option) (v:lval) (t:typ):t = - if M.tracing then M.traceli "update_offset" "do_update_offset %a %a (%a) %a\n" pretty x Offs.pretty offs (Pretty.docOpt (CilType.Exp.pretty ())) exp pretty value; + if M.tracing then M.traceli "update_offset" "do_update_offset %a %a (%a) %a" pretty x Offs.pretty offs (Pretty.docOpt (CilType.Exp.pretty ())) exp pretty value; let mu = function Blob (Blob (y, s', orig), s, orig2) -> Blob (y, ID.join s s',orig) | x -> x in let r = match x, offs with @@ -1108,7 +1108,7 @@ struct end in mu result in - if M.tracing then M.traceu "update_offset" "do_update_offset -> %a\n" pretty r; + if M.tracing then M.traceu "update_offset" "do_update_offset -> %a" pretty r; r in let l, o = match exp with diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 944515f4ec..967027e20a 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -333,7 +333,7 @@ struct let meet t1 t2 = let res = meet t1 t2 in - if M.tracing then M.tracel "meet" "meet a: %s b: %s -> %s \n" (show t1) (show t2) (show res) ; + if M.tracing then M.tracel "meet" "meet a: %s b: %s -> %s " (show t1) (show t2) (show res) ; res let meet t1 t2 = timing_wrap "meet" (meet t1) t2 @@ -359,7 +359,7 @@ struct let leq t1 t2 = let res = leq t1 t2 in - if M.tracing then M.tracel "leq" "leq a: %s b: %s -> %b \n" (show t1) (show t2) res ; + if M.tracing then M.tracel "leq" "leq a: %s b: %s -> %b " (show t1) (show t2) res ; res let join a b = @@ -428,7 +428,7 @@ struct let join a b = let res = join a b in - if M.tracing then M.tracel "join" "join a: %s b: %s -> %s \n" (show a) (show b) (show res) ; + if M.tracing then M.tracel "join" "join a: %s b: %s -> %s " (show a) (show b) (show res) ; res let widen a b = @@ -461,7 +461,7 @@ struct let forget_vars t vars = let res = forget_vars t vars in - if M.tracing then M.tracel "ops" "forget_vars %s -> %s\n" (show t) (show res); + if M.tracing then M.tracel "ops" "forget_vars %s -> %s" (show t) (show res); res let forget_vars t vars = timing_wrap "forget_vars" (forget_vars t) vars @@ -512,7 +512,7 @@ struct let assign_exp t var exp no_ov = let res = assign_exp t var exp no_ov in - if M.tracing then M.tracel "ops" "assign_exp t:\n %s \n var: %s \n exp: %a\n no_ov: %b -> \n %s\n" + if M.tracing then M.tracel "ops" "assign_exp t:\n %s \n var: %s \n exp: %a\n no_ov: %b -> \n %s" (show t) (Var.to_string var) d_exp exp (Lazy.force no_ov) (show res) ; res @@ -523,7 +523,7 @@ struct let assign_var t v v' = let res = assign_var t v v' in - if M.tracing then M.tracel "ops" "assign_var t:\n %s \n v: %s \n v': %s\n -> %s\n" (show t) (Var.to_string v) (Var.to_string v') (show res) ; + if M.tracing then M.tracel "ops" "assign_var t:\n %s \n v: %s \n v': %s\n -> %s" (show t) (Var.to_string v) (Var.to_string v') (show res) ; res let assign_var_parallel t vv's = @@ -551,7 +551,7 @@ struct let assign_var_parallel t vv's = let res = assign_var_parallel t vv's in - if M.tracing then M.tracel "ops" "assign_var parallel: %s -> %s \n" (show t) (show res); + if M.tracing then M.tracel "ops" "assign_var parallel: %s -> %s " (show t) (show res); res let assign_var_parallel t vv's = timing_wrap "var_parallel" (assign_var_parallel t) vv's @@ -562,7 +562,7 @@ struct t.env <- t'.env let assign_var_parallel_with t vv's = - if M.tracing then M.tracel "var_parallel" "assign_var parallel'\n"; + if M.tracing then M.tracel "var_parallel" "assign_var parallel'"; assign_var_parallel_with t vv's let assign_var_parallel' t vs1 vs2 = @@ -571,7 +571,7 @@ struct let assign_var_parallel' t vv's = let res = assign_var_parallel' t vv's in - if M.tracing then M.tracel "ops" "assign_var parallel'\n"; + if M.tracing then M.tracel "ops" "assign_var parallel'"; res let substitute_exp t var exp no_ov = @@ -581,7 +581,7 @@ struct let substitute_exp t var exp ov = let res = substitute_exp t var exp ov in - if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %s \n exp: %a \n -> \n %s\n" (show t) (Var.to_string var) d_exp exp (show res); + if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %s \n exp: %a \n -> \n %s" (show t) (Var.to_string var) d_exp exp (show res); res let substitute_exp t var exp ov = timing_wrap "substitution" (substitute_exp t var exp) ov @@ -662,7 +662,7 @@ struct let unify a b = let res = unify a b in - if M.tracing then M.tracel "ops" "unify: %s %s -> %s\n" (show a) (show b) (show res); + if M.tracing then M.tracel "ops" "unify: %s %s -> %s" (show a) (show b) (show res); res let assert_cons d e negate no_ov = diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index 158d3ca907..a0240eea8b 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -279,10 +279,10 @@ struct let assign_exp_with nd v e no_ov = match Convert.texpr1_of_cil_exp nd (A.env nd) e (Lazy.force no_ov) with | texpr1 -> - if M.tracing then M.trace "apron" "assign_exp converted: %s\n" (Format.asprintf "%a" Texpr1.print texpr1); + if M.tracing then M.trace "apron" "assign_exp converted: %s" (Format.asprintf "%a" Texpr1.print texpr1); A.assign_texpr_with Man.mgr nd v texpr1 None | exception Convert.Unsupported_CilExp _ -> - if M.tracing then M.trace "apron" "assign_exp unsupported\n"; + if M.tracing then M.trace "apron" "assign_exp unsupported"; forget_vars_with nd [v] let assign_exp_parallel_with nd ves no_ov = @@ -548,13 +548,13 @@ struct | _ -> begin match Convert.tcons1_of_cil_exp d (A.env d) e negate no_ov with | tcons1 -> - if M.tracing then M.trace "apron" "assert_cons %a %s\n" d_exp e (Format.asprintf "%a" Tcons1.print tcons1); - if M.tracing then M.trace "apron" "assert_cons st: %a\n" D.pretty d; + if M.tracing then M.trace "apron" "assert_cons %a %s" d_exp e (Format.asprintf "%a" Tcons1.print tcons1); + if M.tracing then M.trace "apron" "assert_cons st: %a" D.pretty d; let r = meet_tcons d tcons1 e in - if M.tracing then M.trace "apron" "assert_cons r: %a\n" D.pretty r; + if M.tracing then M.trace "apron" "assert_cons r: %a" D.pretty r; r | exception Convert.Unsupported_CilExp reason -> - if M.tracing then M.trace "apron" "assert_cons %a unsupported: %s\n" d_exp e (SharedFunctions.show_unsupported_cilExp reason); + if M.tracing then M.trace "apron" "assert_cons %a unsupported: %s" d_exp e (SharedFunctions.show_unsupported_cilExp reason); d end @@ -603,7 +603,7 @@ struct let strengthening j x y = (* TODO: optimize strengthening *) - if M.tracing then M.traceli "apron" "strengthening %a\n" pretty j; + if M.tracing then M.traceli "apron" "strengthening %a" pretty j; let x_env = A.env x in let y_env = A.env y in let j_env = A.env j in @@ -612,21 +612,21 @@ struct let x_cons = A.to_lincons_array Man.mgr x_j in let y_cons = A.to_lincons_array Man.mgr y_j in let try_add_con j con1 = - if M.tracing then M.tracei "apron" "try_add_con %s\n" (Format.asprintf "%a" (Lincons1.print: Format.formatter -> Lincons1.t -> unit) con1); + if M.tracing then M.tracei "apron" "try_add_con %s" (Format.asprintf "%a" (Lincons1.print: Format.formatter -> Lincons1.t -> unit) con1); let t = meet_lincons j con1 in let t_x = A.change_environment Man.mgr t x_env false in let t_y = A.change_environment Man.mgr t y_env false in let leq_x = A.is_leq Man.mgr x t_x in let leq_y = A.is_leq Man.mgr y t_y in - if M.tracing then M.trace "apron" "t: %a\n" pretty t; - if M.tracing then M.trace "apron" "t_x (leq x %B): %a\n" leq_x pretty t_x; - if M.tracing then M.trace "apron" "t_y (leq y %B): %a\n" leq_y pretty t_y; + if M.tracing then M.trace "apron" "t: %a" pretty t; + if M.tracing then M.trace "apron" "t_x (leq x %B): %a" leq_x pretty t_x; + if M.tracing then M.trace "apron" "t_y (leq y %B): %a" leq_y pretty t_y; if leq_x && leq_y then ( - if M.tracing then M.traceu "apron" "added\n"; + if M.tracing then M.traceu "apron" "added"; t ) else ( - if M.tracing then M.traceu "apron" "not added\n"; + if M.tracing then M.traceu "apron" "not added"; j ) in @@ -654,7 +654,7 @@ struct Array.concat [x_cons1_only_x; y_cons1_only_y; x_cons1_some_y; y_cons1_some_x] in let j = Array.fold_left try_add_con j cons1 in - if M.tracing then M.traceu "apron" "-> %a\n" pretty j; + if M.tracing then M.traceu "apron" "-> %a" pretty j; j let empty_env = Environment.make [||] [||] @@ -678,16 +678,16 @@ struct else if is_bot y then (* TODO: also for non-empty env *) x else ( - if M.tracing then M.traceli "apron" "join %a %a\n" pretty x pretty y; + if M.tracing then M.traceli "apron" "join %a %a" pretty x pretty y; let j = join x y in - if M.tracing then M.trace "apron" "j = %a\n" pretty j; + if M.tracing then M.trace "apron" "j = %a" pretty j; let j = if strengthening_enabled then (* TODO: skip if same envs? *) strengthening j x y else j in - if M.tracing then M.traceu "apron" "-> %a\n" pretty j; + if M.tracing then M.traceu "apron" "-> %a" pretty j; j ) @@ -750,10 +750,10 @@ struct y (* env increased, just use joined value in y, assuming env doesn't increase infinitely *) let widen x y = - if M.tracing then M.traceli "apron" "widen %a %a\n" pretty x pretty y; + if M.tracing then M.traceli "apron" "widen %a %a" pretty x pretty y; let w = widen x y in - if M.tracing then M.trace "apron" "widen same %B\n" (equal y w); - if M.tracing then M.traceu "apron" "-> %a\n" pretty w; + if M.tracing then M.trace "apron" "widen same %B" (equal y w); + if M.tracing then M.traceu "apron" "-> %a" pretty w; w let narrow x y = diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index f7b800a404..050114265e 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -129,7 +129,7 @@ struct match Bounds.bound_texpr d texpr1 with | Some min, Some max when Z.compare type_min min <= 0 && Z.compare max type_max <= 0 -> () | min_opt, max_opt -> - if M.tracing then M.trace "apron" "may overflow: %a (%a, %a)\n" CilType.Exp.pretty exp (Pretty.docOpt (IntOps.BigIntOps.pretty ())) min_opt (Pretty.docOpt (IntOps.BigIntOps.pretty ())) max_opt; + if M.tracing then M.trace "apron" "may overflow: %a (%a, %a)" CilType.Exp.pretty exp (Pretty.docOpt (IntOps.BigIntOps.pretty ())) min_opt (Pretty.docOpt (IntOps.BigIntOps.pretty ())) max_opt; raise (Unsupported_CilExp Overflow) ); expr @@ -335,7 +335,7 @@ struct | exception Invalid_argument _ -> ID.top () (* real top, not a top of any ikind because we don't even know the ikind *) | ik -> - if M.tracing then M.trace "relation" "eval_int: exp_is_cons %a = %B\n" d_plainexp e (exp_is_cons e); + if M.tracing then M.trace "relation" "eval_int: exp_is_cons %a = %B" d_plainexp e (exp_is_cons e); if exp_is_cons e then match check_assert d e no_ov with | `True -> ID.of_bool ik true diff --git a/src/cdomains/symbLocksDomain.ml b/src/cdomains/symbLocksDomain.ml index 85578d5fad..ba2b96e8d0 100644 --- a/src/cdomains/symbLocksDomain.ml +++ b/src/cdomains/symbLocksDomain.ml @@ -237,7 +237,7 @@ struct | _ , _ -> raise (Invalid_argument "") let from_exps a l : t option = - if M.tracing then M.tracel "symb_locks" "from_exps %a (%s) %a (%s)\n" d_plainexp a (ees_to_str (toEl a)) d_plainexp l (ees_to_str (toEl l)); + if M.tracing then M.tracel "symb_locks" "from_exps %a (%s) %a (%s)" d_plainexp a (ees_to_str (toEl a)) d_plainexp l (ees_to_str (toEl l)); let a, l = toEl a, toEl l in (* ignore (printf "from_exps:\n %s\n %s\n" (ees_to_str a) (ees_to_str l)); *) (*let rec fold_left2 f a xs ys = diff --git a/src/common/framework/cfgTools.ml b/src/common/framework/cfgTools.ml index 6a2540fd18..31635f47fd 100644 --- a/src/common/framework/cfgTools.ml +++ b/src/common/framework/cfgTools.ml @@ -79,7 +79,7 @@ let computeSCCs (module Cfg: CfgBidir) nodes = dfs_inner prev_node scc else if not (NH.mem scc.nodes prev_node) then ( (* prev_node has been visited, but not in current SCC, therefore is backwards edge to predecessor scc *) - if Messages.tracing then Messages.trace "cfg" "SCC edge: %s -> %s\n" (Node.show_id prev_node) (Node.show_id node); + if Messages.tracing then Messages.trace "cfg" "SCC edge: %s -> %s" (Node.show_id prev_node) (Node.show_id node); NH.modify_def [] node (List.cons (edges, prev_node)) scc.prev; NH.modify_def [] prev_node (List.cons (edges, node)) (NH.find node_scc prev_node).next; ) @@ -148,7 +148,7 @@ let createCFG (file: file) = which do not otherwise appear in the control flow graph. *) let skippedByEdge = CfgEdgeH.create 113 in - if Messages.tracing then Messages.trace "cfg" "Starting to build the cfg.\n\n"; + if Messages.tracing then Messages.trace "cfg" "Starting to build the cfg.\n"; let fd_nodes = NH.create 113 in @@ -163,7 +163,7 @@ let createCFG (file: file) = H.modify_def [] toNode (List.cons (edges,fromNode)) cfgB; H.modify_def [] fromNode (List.cons (edges,toNode)) cfgF; CfgEdgeH.replace skippedByEdge (fromNode, edges, toNode) skippedStatements; - if Messages.tracing then Messages.trace "cfg" "done\n\n" + if Messages.tracing then Messages.trace "cfg" "done\n" in let addEdge ?skippedStatements fromNode edge toNode = addEdges ?skippedStatements fromNode [edge] toNode @@ -181,7 +181,7 @@ let createCFG (file: file) = If not_found is true, then a stmt without succs will raise Not_found instead of returning that stmt. *) let find_real_stmt ?parent ?(not_found=false) stmt = - if Messages.tracing then Messages.tracei "cfg" "find_real_stmt not_found=%B stmt=%d\n" not_found stmt.sid; + if Messages.tracing then Messages.tracei "cfg" "find_real_stmt not_found=%B stmt=%d" not_found stmt.sid; let rec find visited_stmts stmt = if Messages.tracing then Messages.trace "cfg" "find_real_stmt visited=[%a] stmt=%d: %a\n" @@ -227,10 +227,10 @@ let createCFG (file: file) = try (* rev_path is the stack of all visited statements, excluding the final statement *) let final_stmt, rev_path = find [] stmt in - if Messages.tracing then Messages.traceu "cfg" "-> %d\n" final_stmt.sid; + if Messages.tracing then Messages.traceu "cfg" "-> %d" final_stmt.sid; final_stmt, List.rev rev_path with Not_found -> - if Messages.tracing then Messages.traceu "cfg" "-> Not_found\n"; + if Messages.tracing then Messages.traceu "cfg" "-> Not_found"; raise Not_found in addEdge_fromLoc (FunctionEntry dummy_func) (Ret (None, dummy_func)) (Function dummy_func); @@ -238,7 +238,7 @@ let createCFG (file: file) = iterGlobals file (fun glob -> match glob with | GFun (fd, fd_loc) -> - if Messages.tracing then Messages.trace "cfg" "Looking at the function %s.\n" fd.svar.vname; + if Messages.tracing then Messages.trace "cfg" "Looking at the function %s." fd.svar.vname; if get_bool "dbg.cilcfgdot" then Cfg.printCfgFilename ("cilcfg." ^ fd.svar.vname ^ ".dot") fd; @@ -252,7 +252,7 @@ let createCFG (file: file) = (* Return node to be used for infinite loop connection to end of function * lazy, so it's only added when actually needed *) let pseudo_return = lazy ( - if Messages.tracing then Messages.trace "cfg" "adding pseudo-return to the function %s.\n" fd.svar.vname; + if Messages.tracing then Messages.trace "cfg" "adding pseudo-return to the function %s." fd.svar.vname; let fd_end_loc = {fd_loc with line = fd_loc.endLine; byte = fd_loc.endByte; column = fd_loc.endColumn} in let newst = mkStmt (Return (None, fd_end_loc)) in newst.sid <- Cilfacade.get_pseudo_return_id fd; @@ -266,7 +266,7 @@ let createCFG (file: file) = let loop_head_neg1 = NH.create 3 in (* So for each statement in the function body, we do the following: *) let handle stmt = - if Messages.tracing then Messages.trace "cfg" "Statement %d at %a.\n" stmt.sid d_loc (Cilfacade.get_stmtLoc stmt); + if Messages.tracing then Messages.trace "cfg" "Statement %d at %a." stmt.sid d_loc (Cilfacade.get_stmtLoc stmt); let real_succs () = List.map (find_real_stmt ~parent:stmt) stmt.succs in @@ -321,7 +321,7 @@ let createCFG (file: file) = (* CIL eliminates the constant true If corresponding to constant true Loop. Then there is no Goto to after the loop and the CFG is unconnected (to Function node). An extra Neg(1) edge is added in such case. *) - if Messages.tracing then Messages.trace "cfg" "loop %d cont=%d brk=%d\n" stmt.sid cont.sid brk.sid; + if Messages.tracing then Messages.trace "cfg" "loop %d cont=%d brk=%d" stmt.sid cont.sid brk.sid; begin match find_real_stmt ~not_found:true brk with (* don't specify stmt as parent because if find_real_stmt finds cycle, it should not return the Loop statement *) | break_stmt, _ -> (* break statement is what follows the (constant true) Loop *) @@ -384,7 +384,7 @@ let createCFG (file: file) = in Timing.wrap ~args:[("function", `String fd.svar.vname)] "handle" (List.iter handle) fd.sallstmts; - if Messages.tracing then Messages.trace "cfg" "Over\n"; + if Messages.tracing then Messages.trace "cfg" "Over"; (* Connect remaining infinite loops (e.g made using goto) to end of function * via pseudo return node for demand driven solvers *) @@ -468,7 +468,7 @@ let createCFG (file: file) = raise (Not_connect fd) | _ -> () ); - if Messages.tracing then Messages.trace "cfg" "CFG building finished.\n\n"; + if Messages.tracing then Messages.trace "cfg" "CFG building finished.\n"; Logs.debug "cfgF (%a), cfgB (%a)" GobHashtbl.pretty_statistics (NH.stats cfgF) GobHashtbl.pretty_statistics (NH.stats cfgB); cfgF, cfgB, skippedByEdge diff --git a/src/config/gobConfig.ml b/src/config/gobConfig.ml index 11f050b354..d9eca80c0f 100644 --- a/src/config/gobConfig.ml +++ b/src/config/gobConfig.ml @@ -299,7 +299,7 @@ struct try let st = String.trim st in let x = get_value !json_conf (parse_path st) in - if Goblint_tracing.tracing then Goblint_tracing.trace "conf-reads" "Reading '%s', it is %a.\n" st GobYojson.pretty x; + if Goblint_tracing.tracing then Goblint_tracing.trace "conf-reads" "Reading '%s', it is %a." st GobYojson.pretty x; try f x with Yojson.Safe.Util.Type_error (s, _) -> Logs.error "The value for '%s' has the wrong type: %s" st s; @@ -351,7 +351,7 @@ struct (** Helper function for writing values. Handles the tracing. *) let set_path_string st v = - if Goblint_tracing.tracing then Goblint_tracing.trace "conf" "Setting '%s' to %a.\n" st GobYojson.pretty v; + if Goblint_tracing.tracing then Goblint_tracing.trace "conf" "Setting '%s' to %a." st GobYojson.pretty v; set_value v json_conf (parse_path st) let set_json st j = @@ -401,7 +401,7 @@ struct | Some fn -> let v = Yojson.Safe.from_channel % BatIO.to_input_channel |> File.with_file_in (Fpath.to_string fn) in merge v; - if Goblint_tracing.tracing then Goblint_tracing.trace "conf" "Merging with '%a', resulting\n%a.\n" GobFpath.pretty fn GobYojson.pretty !json_conf + if Goblint_tracing.tracing then Goblint_tracing.trace "conf" "Merging with '%a', resulting\n%a." GobFpath.pretty fn GobYojson.pretty !json_conf | None -> raise (Sys_error (Printf.sprintf "%s: No such file or diretory" (Fpath.to_string fn))) end diff --git a/src/domains/access.ml b/src/domains/access.ml index baa9d34220..c35fb3a16d 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -273,7 +273,7 @@ let get_val_type e: acc_typ = (** Add access to {!Memo} after distributing. *) let add_one ~side memo: unit = let ignorable = is_ignorable_memo memo in - if M.tracing then M.trace "access" "add_one %a (ignorable = %B)\n" Memo.pretty memo ignorable; + if M.tracing then M.trace "access" "add_one %a (ignorable = %B)" Memo.pretty memo ignorable; if not ignorable then side memo @@ -281,7 +281,7 @@ let add_one ~side memo: unit = Empty access sets are needed for prefix-type_suffix race checking. *) let rec add_distribute_outer ~side ~side_empty (ts: typsig) (o: Offset.Unit.t) = let memo = (`Type ts, o) in - if M.tracing then M.tracei "access" "add_distribute_outer %a\n" Memo.pretty memo; + if M.tracing then M.tracei "access" "add_distribute_outer %a" Memo.pretty memo; add_one ~side memo; (* Add actual access for non-recursive call, or empty access for recursive call when side is side_empty. *) (* distribute to variables of the type *) @@ -298,17 +298,17 @@ let rec add_distribute_outer ~side ~side_empty (ts: typsig) (o: Offset.Unit.t) = add_distribute_outer ~side:side_empty ~side_empty (TSComp (f.fcomp.cstruct, f.fcomp.cname, [])) (`Field (f, o)) (* Switch to side_empty. *) ) fields; - if M.tracing then M.traceu "access" "add_distribute_outer\n" + if M.tracing then M.traceu "access" "add_distribute_outer" (** Add access to known variable with offsets or unknown variable from expression. *) let add ~side ~side_empty e voffs = begin match voffs with | Some (v, o) -> (* known variable *) - if M.tracing then M.traceli "access" "add var %a%a\n" CilType.Varinfo.pretty v CilType.Offset.pretty o; + if M.tracing then M.traceli "access" "add var %a%a" CilType.Varinfo.pretty v CilType.Offset.pretty o; let memo = (`Var v, Offset.Unit.of_cil o) in add_one ~side memo | None -> (* unknown variable *) - if M.tracing then M.traceli "access" "add type %a\n" CilType.Exp.pretty e; + if M.tracing then M.traceli "access" "add type %a" CilType.Exp.pretty e; let ty = get_val_type e in (* extract old acc_typ from expression *) let (t, o) = match ty with (* convert acc_typ to type-based Memo (components) *) | `Struct (c, o) -> (TComp (c, []), o) @@ -318,7 +318,7 @@ let add ~side ~side_empty e voffs = | `NoOffset when not !collect_direct_arithmetic && isArithmeticType t -> () | _ -> add_distribute_outer ~side ~side_empty (Cil.typeSig t) o (* distribute to variables and outer offsets *) end; - if M.tracing then M.traceu "access" "add\n" + if M.tracing then M.traceu "access" "add" (** Distribute to {!AddrOf} of all read lvals in subexpressions. *) @@ -482,7 +482,7 @@ struct end let group_may_race (warn_accs:WarnAccs.t) = - if M.tracing then M.tracei "access" "group_may_race %a\n" WarnAccs.pretty warn_accs; + if M.tracing then M.tracei "access" "group_may_race %a" WarnAccs.pretty warn_accs; (* BFS to traverse one component with may_race edges *) let rec bfs' warn_accs ~todo ~visited = let todo_all = WarnAccs.union_all todo in @@ -539,7 +539,7 @@ let group_may_race (warn_accs:WarnAccs.t) = ) in let (comps, warn_accs) = components [] warn_accs in - if M.tracing then M.trace "access" "components %a\n" WarnAccs.pretty warn_accs; + if M.tracing then M.trace "access" "components %a" WarnAccs.pretty warn_accs; (* repeat BFS to find all prefix-type_suffix-only components starting from prefix accesses (symmetric) *) let rec components_cross comps ~prefix ~type_suffix = if AS.is_empty prefix then @@ -547,7 +547,7 @@ let group_may_race (warn_accs:WarnAccs.t) = else ( let prefix_acc = AS.choose prefix in let (warn_accs', comp) = bfs {(WarnAccs.empty ()) with prefix; type_suffix} {(WarnAccs.empty ()) with prefix=AS.singleton prefix_acc} in - if M.tracing then M.trace "access" "components_cross %a\n" WarnAccs.pretty warn_accs'; + if M.tracing then M.trace "access" "components_cross %a" WarnAccs.pretty warn_accs'; let comps' = if AS.cardinal comp > 1 then comp :: comps @@ -558,7 +558,7 @@ let group_may_race (warn_accs:WarnAccs.t) = ) in let components_cross = components_cross comps ~prefix:warn_accs.prefix ~type_suffix:warn_accs.type_suffix in - if M.tracing then M.traceu "access" "group_may_race\n"; + if M.tracing then M.traceu "access" "group_may_race"; components_cross let race_conf accs = diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 52022b8aee..4c146c486a 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -652,8 +652,8 @@ struct let tf_normal_call ctx lv e (f:fundec) args getl sidel getg sideg = let combine (cd, fc, fd) = - if M.tracing then M.traceli "combine" "local: %a\n" S.D.pretty cd; - if M.tracing then M.trace "combine" "function: %a\n" S.D.pretty fd; + if M.tracing then M.traceli "combine" "local: %a" S.D.pretty cd; + if M.tracing then M.trace "combine" "function: %a" S.D.pretty fd; let rec cd_ctx = { ctx with ask = (fun (type a) (q: a Queries.t) -> S.query cd_ctx q); @@ -700,7 +700,7 @@ struct S.D.join acc (S.combine_assign combine_assign_ctx lv e f args fc fd1_ctx.local (Analyses.ask_of_ctx fd1_ctx)) ) (S.D.bot ()) (S.paths_as_set fd_ctx) in - if M.tracing then M.traceu "combine" "combined local: %a\n" S.D.pretty r; + if M.tracing then M.traceu "combine" "combined local: %a" S.D.pretty r; r in let paths = S.enter ctx lv f args in @@ -710,10 +710,10 @@ struct (* Don't filter bot paths, otherwise LongjmpLifter is not called. *) (* let paths = List.filter (fun (c,fc,v) -> not (D.is_bot v)) paths in *) let paths = List.map (Tuple3.map2 Option.some) paths in - if M.tracing then M.traceli "combine" "combining\n"; + if M.tracing then M.traceli "combine" "combining"; let paths = List.map combine paths in let r = List.fold_left D.join (D.bot ()) paths in - if M.tracing then M.traceu "combine" "combined: %a\n" S.D.pretty r; + if M.tracing then M.traceu "combine" "combined: %a" S.D.pretty r; r let tf_special_call ctx lv f args = S.special ctx lv f args @@ -1135,13 +1135,13 @@ struct assert (D.cardinal ctx.local = 1); let cd = D.choose ctx.local in let k x y = - if M.tracing then M.traceli "combine" "function: %a\n" Spec.D.pretty x; + if M.tracing then M.traceli "combine" "function: %a" Spec.D.pretty x; try let r = Spec.combine_env (conv ctx cd) l fe f a fc x f_ask in - if M.tracing then M.traceu "combine" "combined function: %a\n" Spec.D.pretty r; + if M.tracing then M.traceu "combine" "combined function: %a" Spec.D.pretty r; D.add r y with Deadcode -> - if M.tracing then M.traceu "combine" "combined function: dead\n"; + if M.tracing then M.traceu "combine" "combined function: dead"; y in let d = D.fold k d (D.bot ()) in @@ -1151,13 +1151,13 @@ struct assert (D.cardinal ctx.local = 1); let cd = D.choose ctx.local in let k x y = - if M.tracing then M.traceli "combine" "function: %a\n" Spec.D.pretty x; + if M.tracing then M.traceli "combine" "function: %a" Spec.D.pretty x; try let r = Spec.combine_assign (conv ctx cd) l fe f a fc x f_ask in - if M.tracing then M.traceu "combine" "combined function: %a\n" Spec.D.pretty r; + if M.tracing then M.traceu "combine" "combined function: %a" Spec.D.pretty r; D.add r y with Deadcode -> - if M.tracing then M.traceu "combine" "combined function: dead\n"; + if M.tracing then M.traceu "combine" "combined function: dead"; y in let d = D.fold k d (D.bot ()) in @@ -1436,7 +1436,7 @@ struct | Target (target_node, target_context) -> let target_fundec = Node.find_fundec target_node in if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (ctx.control_context ()) then ( - if M.tracing then Messages.tracel "longjmp" "Fun: Potentially from same context, side-effect to %a\n" Node.pretty target_node; + if M.tracing then Messages.tracel "longjmp" "Fun: Potentially from same context, side-effect to %a" Node.pretty target_node; ctx.sideg (V.longjmpto (target_node, ctx.context ())) (G.create_local (Lazy.force combined)) (* No need to propagate this outwards here, the set of valid longjumps is part of the context, we can never have the same context setting the longjmp multiple times *) ) @@ -1450,9 +1450,9 @@ struct in JmpBufDomain.JmpBufSet.iter handle_target active_targets in - if M.tracing then M.tracel "longjmp" "longfd getg %a\n" CilType.Fundec.pretty f; + if M.tracing then M.tracel "longjmp" "longfd getg %a" CilType.Fundec.pretty f; let longfd = G.local (ctx.global (V.longjmpret (f, Option.get fc))) in - if M.tracing then M.tracel "longjmp" "longfd %a\n" D.pretty longfd; + if M.tracing then M.tracel "longjmp" "longfd %a" D.pretty longfd; if not (D.is_bot longfd) then handle_longjmp (ctx.local, fc, longfd); S.combine_env (conv_ctx) lv e f args fc fd f_ask @@ -1505,7 +1505,7 @@ struct (* Eval `env` again to avoid having to construct bespoke ctx to ask *) let targets = path_ctx.ask (EvalJumpBuf env) in let valid_targets = path_ctx.ask ValidLongJmp in - if M.tracing then Messages.tracel "longjmp" "Jumping to %a\n" JmpBufDomain.JmpBufSet.pretty targets; + if M.tracing then Messages.tracel "longjmp" "Jumping to %a" JmpBufDomain.JmpBufSet.pretty targets; let handle_target target = match target with | JmpBufDomain.BufferEntryOrTop.AllTargets -> M.warn ~category:Imprecise "Longjmp to potentially invalid target, as contents of buffer %a may be unknown! (imprecision due to heap?)" d_exp env; @@ -1513,11 +1513,11 @@ struct | Target (target_node, target_context) -> let target_fundec = Node.find_fundec target_node in if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (ctx.control_context ()) then ( - if M.tracing then Messages.tracel "longjmp" "Potentially from same context, side-effect to %a\n" Node.pretty target_node; + if M.tracing then Messages.tracel "longjmp" "Potentially from same context, side-effect to %a" Node.pretty target_node; ctx.sideg (V.longjmpto (target_node, ctx.context ())) (G.create_local (Lazy.force specialed)) ) else if JmpBufDomain.JmpBufSet.mem target valid_targets then ( - if M.tracing then Messages.tracel "longjmp" "Longjmp to somewhere else, side-effect to %i\n" (S.C.hash (ctx.context ())); + if M.tracing then Messages.tracel "longjmp" "Longjmp to somewhere else, side-effect to %i" (S.C.hash (ctx.context ())); ctx.sideg (V.longjmpret (current_fundec, ctx.context ())) (G.create_local (Lazy.force returned)) ) else diff --git a/src/framework/control.ml b/src/framework/control.ml index 6e8e5b7fc4..3f4bd5e320 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -282,7 +282,7 @@ struct let gh = GHT.create 13 in let getg v = GHT.find_default gh v (EQSys.G.bot ()) in let sideg v d = - if M.tracing then M.trace "global_inits" "sideg %a = %a\n" EQSys.GVar.pretty v EQSys.G.pretty d; + if M.tracing then M.trace "global_inits" "sideg %a = %a" EQSys.GVar.pretty v EQSys.G.pretty d; GHT.replace gh v (EQSys.G.join (getg v) d) in (* Old-style global function for context. @@ -311,16 +311,16 @@ struct let funs = ref [] in (*let count = ref 0 in*) let transfer_func (st : Spec.D.t) (loc, edge) : Spec.D.t = - if M.tracing then M.trace "con" "Initializer %a\n" CilType.Location.pretty loc; + if M.tracing then M.trace "con" "Initializer %a" CilType.Location.pretty loc; (*incr count; if (get_bool "dbg.verbose")&& (!count mod 1000 = 0) then Printf.printf "%d %!" !count; *) Goblint_tracing.current_loc := loc; match edge with | MyCFG.Entry func -> - if M.tracing then M.trace "global_inits" "Entry %a\n" d_lval (var func.svar); + if M.tracing then M.trace "global_inits" "Entry %a" d_lval (var func.svar); Spec.body {ctx with local = st} func | MyCFG.Assign (lval,exp) -> - if M.tracing then M.trace "global_inits" "Assign %a = %a\n" d_lval lval d_exp exp; + if M.tracing then M.trace "global_inits" "Assign %a = %a" d_lval lval d_exp exp; (match lval, exp with | (Var v,o), (AddrOf (Var f,NoOffset)) when v.vstorage <> Static && isFunctionType f.vtype -> @@ -330,7 +330,7 @@ struct let res = Spec.assign {ctx with local = st} lval exp in (* Needed for privatizations (e.g. None) that do not side immediately *) let res' = Spec.sync {ctx with local = res} `Normal in - if M.tracing then M.trace "global_inits" "\t\t -> state:%a\n" Spec.D.pretty res; + if M.tracing then M.trace "global_inits" "\t\t -> state:%a" Spec.D.pretty res; res' | _ -> failwith "Unsupported global initializer edge" in @@ -339,7 +339,7 @@ struct let old_loc = !Goblint_tracing.current_loc in let result : Spec.D.t = List.fold_left transfer_func with_externs edges in Goblint_tracing.current_loc := old_loc; - if M.tracing then M.trace "global_inits" "startstate: %a\n" Spec.D.pretty result; + if M.tracing then M.trace "global_inits" "startstate: %a" Spec.D.pretty result; result, !funs in diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index c79735c2b1..85f7db43b5 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -129,7 +129,7 @@ and eq_typ_acc ?(fun_parameter_name_comparison_enabled: bool = true) (a: typ) (b a, b, c, (updatedCompinfoRenames, updatedEnumRenames) in - if Messages.tracing then Messages.tracei "compareast" "eq_typ_acc %a vs %a (%a, %a)\n" d_type a d_type b pretty_length acc pretty_length !global_typ_acc; (* %a makes List.length calls lazy if compareast isn't being traced *) + if Messages.tracing then Messages.tracei "compareast" "eq_typ_acc %a vs %a (%a, %a)" d_type a d_type b pretty_length acc pretty_length !global_typ_acc; (* %a makes List.length calls lazy if compareast isn't being traced *) let r, updated_rename_mapping = match a, b with | TPtr (typ1, attr1), TPtr (typ2, attr2) -> eq_typ_acc typ1 typ2 ~rename_mapping ~acc &&>> forward_list_equal (eq_attribute ~acc) attr1 attr2 | TArray (typ1, (Some lenExp1), attr1), TArray (typ2, (Some lenExp2), attr2) -> eq_typ_acc typ1 typ2 ~rename_mapping ~acc &&>> eq_exp lenExp1 lenExp2 ~acc &&>> forward_list_equal (eq_attribute ~acc) attr1 attr2 @@ -150,7 +150,7 @@ and eq_typ_acc ?(fun_parameter_name_comparison_enabled: bool = true) (a: typ) (b (* The following two lines are a hack to ensure that anonymous types get the same name and thus, the same typsig *) | TComp (compinfo1, attr1), TComp (compinfo2, attr2) -> if mem_typ_acc a b acc || mem_typ_acc a b !global_typ_acc then ( - if Messages.tracing then Messages.trace "compareast" "in acc\n"; + if Messages.tracing then Messages.trace "compareast" "in acc"; true, rename_mapping ) else ( @@ -175,7 +175,7 @@ and eq_typ_acc ?(fun_parameter_name_comparison_enabled: bool = true) (a: typ) (b | TFloat (fk1, attr1), TFloat (fk2, attr2) -> (fk1 = fk2, rename_mapping) &&>> forward_list_equal (eq_attribute ~acc) attr1 attr2 | _, _ -> false, rename_mapping in - if Messages.tracing then Messages.traceu "compareast" "eq_typ_acc %a vs %a\n" d_type a d_type b; + if Messages.tracing then Messages.traceu "compareast" "eq_typ_acc %a vs %a" d_type a d_type b; (r, updated_rename_mapping) and eq_eitems (a: string * exp * location) (b: string * exp * location) ~(rename_mapping: rename_mapping) ~(acc: (typ * typ) list) = match a, b with @@ -272,11 +272,11 @@ and eq_compinfo (a: compinfo) (b: compinfo) (acc: (typ * typ) list) (rename_mapp (a.cdefined = b.cdefined) (* Ignore ckey, and ignore creferenced *) and eq_fieldinfo (a: fieldinfo) (b: fieldinfo) ~(acc: (typ * typ) list) ~(rename_mapping: rename_mapping) = - if Messages.tracing then Messages.tracei "compareast" "fieldinfo %s vs %s\n" a.fname b.fname; + if Messages.tracing then Messages.tracei "compareast" "fieldinfo %s vs %s" a.fname b.fname; let (r, rm) = (a.fname = b.fname, rename_mapping) &&>> eq_typ_acc a.ftype b.ftype ~acc &&> (a.fbitfield = b.fbitfield) &&>> forward_list_equal (eq_attribute ~acc) a.fattr b.fattr in - if Messages.tracing then Messages.traceu "compareast" "fieldinfo %s vs %s\n" a.fname b.fname; + if Messages.tracing then Messages.traceu "compareast" "fieldinfo %s vs %s" a.fname b.fname; (r, rm) and eq_offset (a: offset) (b: offset) ~(rename_mapping: rename_mapping) ~(acc: (typ * typ) list) : bool * rename_mapping = match a, b with diff --git a/src/solver/postSolver.ml b/src/solver/postSolver.ml index c8b0e1dfd6..4678cba462 100644 --- a/src/solver/postSolver.ml +++ b/src/solver/postSolver.ml @@ -190,7 +190,7 @@ struct let reachable = PS.init_reachable ~vh in let rec one_var x = - if M.tracing then M.trace "postsolver" "one_var %a reachable=%B system=%B\n" S.Var.pretty_trace x (VH.mem reachable x) (Option.is_some (S.system x)); + if M.tracing then M.trace "postsolver" "one_var %a reachable=%B system=%B" S.Var.pretty_trace x (VH.mem reachable x) (Option.is_some (S.system x)); if not (VH.mem reachable x) then ( VH.replace reachable x (); Option.may (one_constraint x) (S.system x) @@ -201,13 +201,13 @@ struct try VH.find vh y with Not_found -> S.Dom.bot () in let set y d = - if M.tracing then M.trace "postsolver" "one_side %a %a %a\n" S.Var.pretty_trace x S.Var.pretty_trace y S.Dom.pretty d; + if M.tracing then M.trace "postsolver" "one_side %a %a %a" S.Var.pretty_trace x S.Var.pretty_trace y S.Dom.pretty d; PS.one_side ~vh ~x ~y ~d; (* check before recursing *) one_var y in let rhs = f get set in - if M.tracing then M.trace "postsolver" "one_constraint %a %a\n" S.Var.pretty_trace x S.Dom.pretty rhs; + if M.tracing then M.trace "postsolver" "one_constraint %a %a" S.Var.pretty_trace x S.Dom.pretty rhs; PS.one_constraint ~vh ~x ~rhs in (Timing.wrap "postsolver_iter" (List.iter one_var)) vs; diff --git a/src/solver/td3.ml b/src/solver/td3.ml index fe7940a828..28cd424619 100644 --- a/src/solver/td3.ml +++ b/src/solver/td3.ml @@ -410,9 +410,9 @@ module Base = init y; (match x with None -> () | Some x -> if side_widen = "unstable_self" then add_infl x y); let widen a b = - if M.tracing then M.traceli "sol2" "side widen %a %a\n" S.Dom.pretty a S.Dom.pretty b; + if M.tracing then M.traceli "sol2" "side widen %a %a" S.Dom.pretty a S.Dom.pretty b; let r = S.Dom.widen a (S.Dom.join a b) in - if M.tracing then M.traceu "sol2" "-> %a\n" S.Dom.pretty r; + if M.tracing then M.traceu "sol2" "-> %a" S.Dom.pretty r; r in let old_sides = HM.find_default sides y VS.empty in @@ -1120,7 +1120,7 @@ module DepVals: GenericEqIncrSolver = in match all_deps_unchanged with | Some oldv -> - if M.tracing then M.trace "sol2" "All deps unchanged for %a, not evaluating RHS\n" S.Var.pretty_trace x; + if M.tracing then M.trace "sol2" "All deps unchanged for %a, not evaluating RHS" S.Var.pretty_trace x; oldv | None -> (* This needs to be done here as a local wrapper around get to avoid polluting dep_vals during earlier checks *) diff --git a/src/witness/argTools.ml b/src/witness/argTools.ml index 2d65911a5f..af0f2771eb 100644 --- a/src/witness/argTools.ml +++ b/src/witness/argTools.ml @@ -124,7 +124,7 @@ struct (* Exclude accumulated prevs, which were pruned *) if NHT.mem vars prev_lvar then ( let lvar' = (fst lvar, snd lvar, i) in - if M.tracing then M.trace "witness" "%s -( %a )-> %s\n" (Node.to_string prev_lvar) MyARG.pretty_inline_edge edge (Node.to_string lvar'); + if M.tracing then M.trace "witness" "%s -( %a )-> %s" (Node.to_string prev_lvar) MyARG.pretty_inline_edge edge (Node.to_string lvar'); NHT.modify_def [] lvar' (fun prevs -> (edge, prev_lvar) :: prevs) prev; NHT.modify_def [] prev_lvar (fun nexts -> (edge, lvar') :: nexts) next ) diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 48948fd354..fb88c2ce7b 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -229,7 +229,7 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) NH.add itered_nodes node (); write_node node; let is_sink = TaskResult.is_violation node || TaskResult.is_sink node in - if M.tracing then M.tracei "witness" "iter_node %s\n" (N.to_string node); + if M.tracing then M.tracei "witness" "iter_node %s" (N.to_string node); if not is_sink then begin let edge_to_nodes = Arg.next node @@ -250,17 +250,17 @@ let write_file filename (module Task:Task) (module TaskResult:WitnessTaskResult) |> BatList.unique_cmp ~cmp:[%ord: MyARG.inline_edge * N.t] in List.iter (fun (edge, to_node) -> - if M.tracing then M.tracec "witness" "edge %a to_node %s\n" MyARG.pretty_inline_edge edge (N.to_string to_node); + if M.tracing then M.tracec "witness" "edge %a to_node %s" MyARG.pretty_inline_edge edge (N.to_string to_node); write_node to_node; write_edge node edge to_node ) edge_to_nodes; - if M.tracing then M.traceu "witness" "iter_node %s\n" (N.to_string node); + if M.tracing then M.traceu "witness" "iter_node %s" (N.to_string node); List.iter (fun (edge, to_node) -> iter_node to_node ) edge_to_nodes end else - if M.tracing then M.traceu "witness" "iter_node %s\n" (N.to_string node); + if M.tracing then M.traceu "witness" "iter_node %s" (N.to_string node); end in diff --git a/src/witness/witnessConstraints.ml b/src/witness/witnessConstraints.ml index 8dedf77a79..bda7945855 100644 --- a/src/witness/witnessConstraints.ml +++ b/src/witness/witnessConstraints.ml @@ -221,16 +221,16 @@ struct let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | Queries.IterPrevVars f -> - if M.tracing then M.tracei "witness" "IterPrevVars\n"; + if M.tracing then M.tracei "witness" "IterPrevVars"; Dom.iter (fun x r -> - if M.tracing then M.tracei "witness" "x = %a\n" Spec.D.pretty x; + if M.tracing then M.tracei "witness" "x = %a" Spec.D.pretty x; R.iter (function ((n, c, j), e) -> - if M.tracing then M.tracec "witness" "n = %a\n" Node.pretty_plain n; - if M.tracing then M.tracec "witness" "c = %a\n" Spec.C.pretty c; - if M.tracing then M.tracec "witness" "j = %a\n" Spec.D.pretty j; + if M.tracing then M.tracec "witness" "n = %a" Node.pretty_plain n; + if M.tracing then M.tracec "witness" "c = %a" Spec.C.pretty c; + if M.tracing then M.tracec "witness" "j = %a" Spec.D.pretty j; f (I.to_int x) (n, Obj.repr c, I.to_int j) e ) r; - if M.tracing then M.traceu "witness" "\n" + if M.tracing then M.traceu "witness" "" ) (fst ctx.local); (* check that sync mappings don't leak into solution (except Function) *) (* TODO: disabled because we now use and leave Sync for every tf, @@ -239,7 +239,7 @@ struct | Function _ -> () (* returns post-sync in FromSpec *) | _ -> assert (Sync.is_bot (snd ctx.local)); end; *) - if M.tracing then M.traceu "witness" "\n"; + if M.tracing then M.traceu "witness" ""; () | Queries.IterVars f -> Dom.iter (fun x r -> From e7d6e5b0b655ae5826e99d5e4c116701fab6fb1f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 11:32:19 +0200 Subject: [PATCH 238/280] Remove newlines from more trace calls --- src/analyses/base.ml | 8 +-- src/analyses/baseInvariant.ml | 10 +-- src/analyses/mCP.ml | 2 +- src/analyses/tmpSpecial.ml | 2 +- src/analyses/varEq.ml | 8 +-- src/cdomain/value/cdomains/intDomain.ml | 6 +- src/common/framework/cfgTools.ml | 12 ++-- src/config/gobConfig.ml | 2 +- src/framework/constraints.ml | 2 +- src/solver/generic.ml | 14 ++--- src/solver/sLR.ml | 12 ++-- src/solver/sLRphased.ml | 18 +++--- src/solver/sLRterm.ml | 14 ++--- src/solver/td3.ml | 82 ++++++++++++------------- src/solver/topDown.ml | 34 +++++----- src/solver/topDown_deprecated.ml | 26 ++++---- src/solver/topDown_space_cache_term.ml | 34 +++++----- src/solver/topDown_term.ml | 26 ++++---- 18 files changed, 156 insertions(+), 156 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 853d6b1fe5..656ab53678 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1447,7 +1447,7 @@ struct * precise information about arrays. *) let set ~(ctx: _ ctx) ?(invariant=false) ?(blob_destructive=false) ?lval_raw ?rval_raw ?t_override (st: store) (lval: AD.t) (lval_type: Cil.typ) (value: value) : store = let update_variable x t y z = - if M.tracing then M.tracel "set" ~var:x.vname "update_variable: start '%s' '%a'\nto\n%a\n" x.vname VD.pretty y CPA.pretty z; + if M.tracing then M.tracel "set" ~var:x.vname "update_variable: start '%s' '%a'\nto\n%a" x.vname VD.pretty y CPA.pretty z; let r = update_variable x t y z in (* refers to defintion that is outside of set *) if M.tracing then M.tracel "set" ~var:x.vname "update_variable: start '%s' '%a'\nto\n%a\nresults in\n%a" x.vname VD.pretty y CPA.pretty z CPA.pretty r; r @@ -1492,7 +1492,7 @@ struct else new_value in - if M.tracing then M.tracel "set" ~var:firstvar "update_one_addr: start with '%a' (type '%a') \nstate:%a\n" AD.pretty (AD.of_mval (x,offs)) d_type x.vtype D.pretty st; + if M.tracing then M.tracel "set" ~var:firstvar "update_one_addr: start with '%a' (type '%a') \nstate:%a" AD.pretty (AD.of_mval (x,offs)) d_type x.vtype D.pretty st; if isFunctionType x.vtype then begin if M.tracing then M.tracel "set" ~var:firstvar "update_one_addr: returning: '%a' is a function type " d_type x.vtype; st @@ -1517,7 +1517,7 @@ struct let new_value = update_offset old_value in if M.tracing then M.tracel "set" "update_offset %a -> %a" VD.pretty old_value VD.pretty new_value; let r = Priv.write_global ~invariant ask priv_getg (priv_sideg ctx.sideg) st x new_value in - if M.tracing then M.tracel "set" ~var:x.vname "update_one_addr: updated a global var '%s' \nstate:%a\n" x.vname D.pretty r; + if M.tracing then M.tracel "set" ~var:x.vname "update_one_addr: updated a global var '%s' \nstate:%a" x.vname D.pretty r; r end else begin if M.tracing then M.tracel "set" ~var:x.vname "update_one_addr: update a local var '%s' ..." x.vname; @@ -2675,7 +2675,7 @@ struct begin let address = AD.singleton addr in let new_val = get ~ctx fun_st address None in - if M.tracing then M.trace "taintPC" "update val: %a\n" VD.pretty new_val; + if M.tracing then M.trace "taintPC" "update val: %a" VD.pretty new_val; let st' = set_savetop ~ctx st address lval_type new_val in match Dep.find_opt v fun_st.deps with | None -> st' diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 8759f6ebb7..6990ffb7f6 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -165,7 +165,7 @@ struct let limit_from = if tv then ID.maximal else ID.minimal in match limit_from n with | Some n -> - if M.tracing then M.tracec "invariant" "Yes, success! %a is not %a\n" d_lval x GobZ.pretty n; + if M.tracing then M.tracec "invariant" "Yes, success! %a is not %a" d_lval x GobZ.pretty n; Some (x, Int (range_from n)) | None -> None end @@ -180,7 +180,7 @@ struct let limit_from = if tv then ID.maximal else ID.minimal in match limit_from n with | Some n -> - if M.tracing then M.tracec "invariant" "Yes, success! %a is not %a\n" d_lval x GobZ.pretty n; + if M.tracing then M.tracec "invariant" "Yes, success! %a is not %a" d_lval x GobZ.pretty n; Some (x, Int (range_from n)) | None -> None end @@ -189,7 +189,7 @@ struct | Gt, x, value, _ -> helper Le x value (not tv) | Ge, x, value, _ -> helper Lt x value (not tv) | _ -> - if M.tracing then M.trace "invariant" "Failed! (operation not supported)\n"; + if M.tracing then M.trace "invariant" "Failed! (operation not supported)"; None in if M.tracing then M.traceli "invariant" "assume expression %a is %B" d_exp exp tv; @@ -227,7 +227,7 @@ struct helper Ne x (null_val (Cilfacade.typeOf exp)) tv | UnOp (LNot,uexp,typ) -> derived_invariant uexp (not tv) | _ -> - if M.tracing then M.tracec "invariant" "Failed! (expression %a not understood)\n" d_plainexp exp; + if M.tracing then M.tracec "invariant" "Failed! (expression %a not understood)" d_plainexp exp; None in match derived_invariant exp tv with @@ -513,7 +513,7 @@ struct else b | _ -> b) in - if M.tracing then M.trace "inv_float" "Div: (%a,%a) = %a yields (%a,%a) \n" FD.pretty a FD.pretty b FD.pretty c FD.pretty a' FD.pretty b'; + if M.tracing then M.trace "inv_float" "Div: (%a,%a) = %a yields (%a,%a)" FD.pretty a FD.pretty b FD.pretty c FD.pretty a' FD.pretty b'; meet_bin a' b' | Eq | Ne as op -> let both x = x, x in diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index 2072097532..4ae5c22d74 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -306,7 +306,7 @@ struct in if M.tracing then ( let module Result = (val Queries.Result.lattice q) in - M.traceu "query" "-> %a\n" Result.pretty r + M.traceu "query" "-> %a" Result.pretty r ); r diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index 63d0bf3f32..7196f5c741 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -76,7 +76,7 @@ struct in - if M.tracing then M.tracel "tmpSpecial" "Result: %a\n" D.pretty d; + if M.tracing then M.tracel "tmpSpecial" "Result: %a" D.pretty d; d diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 16e8c8490d..f56d3487bb 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -362,10 +362,10 @@ struct let add_eq ask (lv:lval) (rv:Exp.t) st = let lvt = unrollType @@ Cilfacade.typeOfLval lv in if M.tracing then ( - M.tracel "var_eq" "add_eq is_global_var %a = %B\n" d_plainlval lv (is_global_var ask (Lval lv) = Some false); - M.tracel "var_eq" "add_eq interesting %a = %B\n" d_plainexp rv (interesting rv); - M.tracel "var_eq" "add_eq is_global_var %a = %B\n" d_plainexp rv (is_global_var ask rv = Some false); - M.tracel "var_eq" "add_eq type %a = %B\n" d_plainlval lv (isIntegralType lvt || isPointerType lvt); + M.tracel "var_eq" "add_eq is_global_var %a = %B" d_plainlval lv (is_global_var ask (Lval lv) = Some false); + M.tracel "var_eq" "add_eq interesting %a = %B" d_plainexp rv (interesting rv); + M.tracel "var_eq" "add_eq is_global_var %a = %B" d_plainexp rv (is_global_var ask rv = Some false); + M.tracel "var_eq" "add_eq type %a = %B" d_plainlval lv (isIntegralType lvt || isPointerType lvt); ); if is_global_var ask (Lval lv) = Some false && interesting rv diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index 0eab8fe82e..e79bc86bef 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -3059,7 +3059,7 @@ struct let add ?no_ov ik x y = let res = add ?no_ov ik x y in if M.tracing then - M.trace "congruence" "add : %a %a -> %a \n" pretty x pretty y + M.trace "congruence" "add : %a %a -> %a" pretty x pretty y pretty res ; res @@ -3069,7 +3069,7 @@ struct let sub ?no_ov ik x y = let res = sub ?no_ov ik x y in if M.tracing then - M.trace "congruence" "sub : %a %a -> %a \n" pretty x pretty y + M.trace "congruence" "sub : %a %a -> %a" pretty x pretty y pretty res ; res @@ -3141,7 +3141,7 @@ struct let div ?no_ov ik x y = let res = div ?no_ov ik x y in if M.tracing then - M.trace "congruence" "div : %a %a -> %a \n" pretty x pretty y pretty + M.trace "congruence" "div : %a %a -> %a" pretty x pretty y pretty res ; res diff --git a/src/common/framework/cfgTools.ml b/src/common/framework/cfgTools.ml index 31635f47fd..45113c7d78 100644 --- a/src/common/framework/cfgTools.ml +++ b/src/common/framework/cfgTools.ml @@ -107,9 +107,9 @@ let computeSCCs (module Cfg: CfgBidir) nodes = if Messages.tracing then ( List.iter (fun scc -> let nodes = scc.nodes |> NH.keys |> BatList.of_enum in - Messages.trace "cfg" "SCC: %a\n" (d_list " " (fun () node -> text (Node.show_id node))) nodes; + Messages.trace "cfg" "SCC: %a" (d_list " " (fun () node -> text (Node.show_id node))) nodes; NH.iter (fun node _ -> - Messages.trace "cfg" "SCC entry: %s\n" (Node.show_id node) + Messages.trace "cfg" "SCC entry: %s" (Node.show_id node) ) scc.prev ) sccs ); @@ -148,7 +148,7 @@ let createCFG (file: file) = which do not otherwise appear in the control flow graph. *) let skippedByEdge = CfgEdgeH.create 113 in - if Messages.tracing then Messages.trace "cfg" "Starting to build the cfg.\n"; + if Messages.tracing then Messages.trace "cfg" "Starting to build the cfg."; let fd_nodes = NH.create 113 in @@ -163,7 +163,7 @@ let createCFG (file: file) = H.modify_def [] toNode (List.cons (edges,fromNode)) cfgB; H.modify_def [] fromNode (List.cons (edges,toNode)) cfgF; CfgEdgeH.replace skippedByEdge (fromNode, edges, toNode) skippedStatements; - if Messages.tracing then Messages.trace "cfg" "done\n" + if Messages.tracing then Messages.trace "cfg" "done" in let addEdge ?skippedStatements fromNode edge toNode = addEdges ?skippedStatements fromNode [edge] toNode @@ -184,7 +184,7 @@ let createCFG (file: file) = if Messages.tracing then Messages.tracei "cfg" "find_real_stmt not_found=%B stmt=%d" not_found stmt.sid; let rec find visited_stmts stmt = if Messages.tracing then - Messages.trace "cfg" "find_real_stmt visited=[%a] stmt=%d: %a\n" + Messages.trace "cfg" "find_real_stmt visited=[%a] stmt=%d: %a" (d_list "; " (fun () x -> Pretty.text (string_of_int x))) (List.map (fun s -> s.sid) visited_stmts) stmt.sid dn_stmt stmt; if @@ -468,7 +468,7 @@ let createCFG (file: file) = raise (Not_connect fd) | _ -> () ); - if Messages.tracing then Messages.trace "cfg" "CFG building finished.\n"; + if Messages.tracing then Messages.trace "cfg" "CFG building finished."; Logs.debug "cfgF (%a), cfgB (%a)" GobHashtbl.pretty_statistics (NH.stats cfgF) GobHashtbl.pretty_statistics (NH.stats cfgB); cfgF, cfgB, skippedByEdge diff --git a/src/config/gobConfig.ml b/src/config/gobConfig.ml index d9eca80c0f..355a999fbf 100644 --- a/src/config/gobConfig.ml +++ b/src/config/gobConfig.ml @@ -331,7 +331,7 @@ struct let wrap_get f x = (* self-observe options, which Spec construction depends on *) - if !building_spec && Goblint_tracing.tracing then Goblint_tracing.trace "config" "get during building_spec: %s\n" x; + if !building_spec && Goblint_tracing.tracing then Goblint_tracing.trace "config" "get during building_spec: %s" x; (* TODO: blacklist such building_spec option from server mode modification since it will have no effect (spec is already built) *) f x diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 4c146c486a..aba2ad695f 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -409,7 +409,7 @@ struct if ContextUtil.should_keep ~isAttr:GobContext ~keepOption:"ana.context.widen" ~keepAttr:"widen" ~removeAttr:"no-widen" f then ( let v_old = M.find f.svar m in (* S.D.bot () if not found *) let v_new = S.D.widen v_old (S.D.join v_old v_cur) in - Messages.(if tracing && not (S.D.equal v_old v_new) then tracel "widen-context" "enter results in new context for function %s\n" f.svar.vname); + Messages.(if tracing && not (S.D.equal v_old v_new) then tracel "widen-context" "enter results in new context for function %s" f.svar.vname); v_new, M.add f.svar v_new m ) else diff --git a/src/solver/generic.ml b/src/solver/generic.ml index 63c1973b94..df41eaa38d 100644 --- a/src/solver/generic.ml +++ b/src/solver/generic.ml @@ -45,7 +45,7 @@ struct let increase (v:Var.t) = let set v c = if not full_trace && (c > start_c && c > !max_c && (Option.is_none !max_var || not (Var.equal (Option.get !max_var) v))) then begin - if tracing then trace "sol" "Switched tracing to %a\n" Var.pretty_trace v; + if tracing then trace "sol" "Switched tracing to %a" Var.pretty_trace v; max_c := c; max_var := Some v end @@ -63,21 +63,21 @@ struct let new_var_event x = incr SolverStats.vars; - if tracing then trace "sol" "New %a\n" Var.pretty_trace x + if tracing then trace "sol" "New %a" Var.pretty_trace x let get_var_event x = - if full_trace then trace "sol" "Querying %a\n" Var.pretty_trace x + if full_trace then trace "sol" "Querying %a" Var.pretty_trace x let eval_rhs_event x = - if full_trace then trace "sol" "(Re-)evaluating %a\n" Var.pretty_trace x; + if full_trace then trace "sol" "(Re-)evaluating %a" Var.pretty_trace x; incr SolverStats.evals; if (get_bool "dbg.solver-progress") then (incr stack_d; Logs.debug "%d" !stack_d) let update_var_event x o n = if tracing then increase x; if full_trace || ((not (Dom.is_bot o)) && Option.is_some !max_var && Var.equal (Option.get !max_var) x) then begin - if tracing then tracei "sol_max" "(%d) Update to %a.\n" !max_c Var.pretty_trace x; - if tracing then traceu "sol_max" "%a\n\n" Dom.pretty_diff (n, o) + if tracing then tracei "sol_max" "(%d) Update to %a" !max_c Var.pretty_trace x; + if tracing then traceu "sol_max" "%a" Dom.pretty_diff (n, o) end (* solvers can assign this to print solver specific statistics using their data structures *) @@ -296,7 +296,7 @@ module SoundBoxSolverImpl = H.remove infl x; H.replace infl x [x]; if full_trace - then Messages.trace "sol" "Need to review %d deps.\n" (List.length deps); (* nosemgrep: trace-not-in-tracing *) + then Messages.trace "sol" "Need to review %d deps." (List.length deps); (* nosemgrep: trace-not-in-tracing *) (* solve all dependencies *) solve_all deps end diff --git a/src/solver/sLR.ml b/src/solver/sLR.ml index 4d9b7fdbb9..3b46f36f5e 100644 --- a/src/solver/sLR.ml +++ b/src/solver/sLR.ml @@ -62,8 +62,8 @@ module SLR3 = let old = HM.find rho x in let tmp = eq x (eval x) (side x) in let tmp = S.Dom.join tmp (sides x) in - if tracing then trace "sol" "Var: %a\n" S.Var.pretty_trace x ; - if tracing then trace "sol" "Contrib:%a\n" S.Dom.pretty tmp; + if tracing then trace "sol" "Var: %a" S.Var.pretty_trace x ; + if tracing then trace "sol" "Contrib:%a" S.Dom.pretty tmp; let tmp = if wpx then if HM.mem globals x then S.Dom.widen old tmp @@ -72,7 +72,7 @@ module SLR3 = in if not (S.Dom.equal old tmp) then begin update_var_event x old tmp; - if tracing then trace "sol" "New Value:%a\n\n" S.Dom.pretty tmp; + if tracing then trace "sol" "New Value:%a" S.Dom.pretty tmp; HM.replace rho x tmp; let w = try HM.find infl x with Not_found -> VS.empty in let w = if wpx then VS.add x w else w in @@ -401,11 +401,11 @@ module Make0 = let use_box = (not (V.ver>1)) || HM.mem wpoint x in let restart_mode_x = h_find_default restart_mode x (2*GobConfig.get_int "solvers.slr4.restart_count") in let rstrt = use_box && (V.ver>3) && D.leq tmp old && restart_mode_x <> 0 in - if tracing then trace "sol" "Var: %a\n" S.Var.pretty_trace x ; - if tracing then trace "sol" "Contrib:%a\n" S.Dom.pretty tmp; + if tracing then trace "sol" "Var: %a" S.Var.pretty_trace x ; + if tracing then trace "sol" "Contrib:%a" S.Dom.pretty tmp; let tmp = if use_box then box old tmp else tmp in if not (D.eq tmp old) then begin - if tracing then trace "sol" "New Value:%a\n\n" S.Dom.pretty tmp; + if tracing then trace "sol" "New Value:%a" S.Dom.pretty tmp; let _ = X.set_value x tmp in if V.ver>3 && restart_mode_x mod 2 = 1 && not (D.leq tmp old) then HM.replace restart_mode x (restart_mode_x - 1); diff --git a/src/solver/sLRphased.ml b/src/solver/sLRphased.ml index 628b709685..c65ebee73f 100644 --- a/src/solver/sLRphased.ml +++ b/src/solver/sLRphased.ml @@ -72,7 +72,7 @@ module Make = let effects = ref Set.empty in let side y d = assert (not (S.Dom.is_bot d)); - if tracing then trace "sol" "SIDE: Var: %a\nVal: %a\n" S.Var.pretty_trace y S.Dom.pretty d; + if tracing then trace "sol" "SIDE: Var: %a\nVal: %a" S.Var.pretty_trace y S.Dom.pretty d; let first = not (Set.mem y !effects) in effects := Set.add y !effects; if first then ( @@ -83,7 +83,7 @@ module Make = if not (HM.mem rho y) then ( if b then solve1 (HM.find key x - 1) ~side:true y else solve0 ~side:true y ) else ( - (* trace "sol" "SIDE: Var: %a already exists with Prio: %i and Val: %a\n" S.Var.pretty_trace y (HM.find key y) S.Dom.pretty d; *) + (* trace "sol" "SIDE: Var: %a already exists with Prio: %i and Val: %a" S.Var.pretty_trace y (HM.find key y) S.Dom.pretty d; *) if HM.find key y < 0 then HM.replace key y (Ref.post_decr count_side) ); q := H.add y !q @@ -101,28 +101,28 @@ module Make = let tmp = eq x eval side in let tmp = S.Dom.join tmp (sides x) in (* if (b && not (S.Dom.leq old tmp)) then ( *) - (* trace "sol" "Var: %a\nOld: %a\nTmp: %a\n" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp; *) + (* trace "sol" "Var: %a\nOld: %a\nTmp: %a" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp; *) (* assert false *) (* ); *) let val_new = if wpx then if b then let nar = narrow old tmp in - if tracing then trace "sol" "NARROW: Var: %a\nOld: %a\nNew: %a\nWiden: %a\n" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty nar; + if tracing then trace "sol" "NARROW: Var: %a\nOld: %a\nNew: %a\nWiden: %a" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty nar; nar else let wid = S.Dom.widen old (S.Dom.join old tmp) in - if tracing then trace "sol" "WIDEN: Var: %a\nOld: %a\nNew: %a\nWiden: %a\n" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty wid; + if tracing then trace "sol" "WIDEN: Var: %a\nOld: %a\nNew: %a\nWiden: %a" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty wid; wid else tmp in - if tracing then trace "sol" "Var: %a\n" S.Var.pretty_trace x ; - if tracing then trace "sol" "Contrib:%a\n" S.Dom.pretty val_new; + if tracing then trace "sol" "Var: %a" S.Var.pretty_trace x ; + if tracing then trace "sol" "Contrib:%a" S.Dom.pretty val_new; if S.Dom.equal old val_new then () else begin update_var_event x old val_new; - if tracing then trace "sol" "New Value:%a\n\n" S.Dom.pretty val_new; + if tracing then trace "sol" "New Value:%a" S.Dom.pretty val_new; HM.replace rho x val_new; let w = try HM.find infl x with Not_found -> VS.empty in (* let w = if wpx then VS.add x w else w in *) @@ -162,7 +162,7 @@ module Make = and sides x = let w = try HM.find set x with Not_found -> VS.empty in let v = Enum.fold (fun d z -> try S.Dom.join d (HPM.find rho' (z,x)) with Not_found -> d) (S.Dom.bot ()) (VS.enum w) - in if tracing then trace "sol" "SIDES: Var: %a\nVal: %a\n" S.Var.pretty_trace x S.Dom.pretty v; v + in if tracing then trace "sol" "SIDES: Var: %a\nVal: %a" S.Var.pretty_trace x S.Dom.pretty v; v and eq x get set = eval_rhs_event x; match S.system x with diff --git a/src/solver/sLRterm.ml b/src/solver/sLRterm.ml index b2678d3a68..fa58b59977 100644 --- a/src/solver/sLRterm.ml +++ b/src/solver/sLRterm.ml @@ -63,14 +63,14 @@ module SLR3term = HM.replace rho x (S.Dom.bot ()); HM.replace infl x (VS.add x VS.empty); let c = if side then count_side else count in - if tracing then trace "sol" "INIT: Var: %a with prio %d\n" S.Var.pretty_trace x !c; + if tracing then trace "sol" "INIT: Var: %a with prio %d" S.Var.pretty_trace x !c; HM.replace key x !c; decr c end in let sides x = let w = try HM.find set x with Not_found -> VS.empty in let v = Enum.fold (fun d z -> try S.Dom.join d (HPM.find rho' (z,x)) with Not_found -> d) (S.Dom.bot ()) (VS.enum w) in - if tracing then trace "sol" "SIDES: Var: %a\nVal: %a\n" S.Var.pretty_trace x S.Dom.pretty v; v + if tracing then trace "sol" "SIDES: Var: %a\nVal: %a" S.Var.pretty_trace x S.Dom.pretty v; v in let rec iterate b_old prio = if H.size !q = 0 || min_key q > prio then () @@ -121,7 +121,7 @@ module SLR3term = ) *) (* if S.Dom.is_bot d then print_endline "BOT" else *) - if tracing then trace "sol" "SIDE: Var: %a\nVal: %a\n" S.Var.pretty_trace y S.Dom.pretty d; + if tracing then trace "sol" "SIDE: Var: %a\nVal: %a" S.Var.pretty_trace y S.Dom.pretty d; let first = not (Set.mem y !effects) in effects := Set.add y !effects; if first then ( @@ -132,7 +132,7 @@ module SLR3term = ignore @@ do_var false y (* solve ~side:true y *) ) else ( - (* trace "sol" "SIDE: Var: %a already exists with Prio: %i and Val: %a\n" S.Var.pretty_trace y (HM.find key y) S.Dom.pretty d; *) + (* trace "sol" "SIDE: Var: %a already exists with Prio: %i and Val: %a" S.Var.pretty_trace y (HM.find key y) S.Dom.pretty d; *) if HM.find key y < 0 then ( HM.replace key y (Ref.post_decr count_side); q := rebuild !q @@ -171,12 +171,12 @@ module SLR3term = else tmp, b_old in - if tracing then trace "sol" "Var: %a\n" S.Var.pretty_trace x ; - if tracing then trace "sol" "Contrib:%a\n" S.Dom.pretty val_new; + if tracing then trace "sol" "Var: %a" S.Var.pretty_trace x ; + if tracing then trace "sol" "Contrib:%a" S.Dom.pretty val_new; if S.Dom.equal old val_new then () else begin update_var_event x old val_new; - if tracing then trace "sol" "New Value:%a\n\n" S.Dom.pretty val_new; + if tracing then trace "sol" "New Value:%a" S.Dom.pretty val_new; HM.replace rho x val_new; let w = try HM.find infl x with Not_found -> VS.empty in (* let w = if wpx then VS.add x w else w in *) diff --git a/src/solver/td3.ml b/src/solver/td3.ml index 28cd424619..7e2a18b042 100644 --- a/src/solver/td3.ml +++ b/src/solver/td3.ml @@ -266,7 +266,7 @@ module Base = let cache_sizes = ref [] in let add_infl y x = - if tracing then trace "sol2" "add_infl %a %a\n" S.Var.pretty_trace y S.Var.pretty_trace x; + if tracing then trace "sol2" "add_infl %a %a" S.Var.pretty_trace y S.Var.pretty_trace x; HM.replace infl y (VS.add x (try HM.find infl y with Not_found -> VS.empty)); HM.replace dep x (VS.add y (HM.find_default dep x VS.empty)); in @@ -277,7 +277,7 @@ module Base = (* Same as destabilize, but returns true if it destabilized a called var, or a var in vs which was stable. *) let rec destabilize_vs x = (* TODO remove? Only used for side_widen cycle. *) - if tracing then trace "sol2" "destabilize_vs %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "destabilize_vs %a" S.Var.pretty_trace x; let w = HM.find_default infl x VS.empty in HM.replace infl x VS.empty; VS.fold (fun y b -> @@ -287,11 +287,11 @@ module Base = HM.mem called y || destabilize_vs y || b || was_stable && List.mem_cmp S.Var.compare y vs ) w false and solve ?reuse_eq x phase = - if tracing then trace "sol2" "solve %a, phase: %s, called: %b, stable: %b, wpoint: %b\n" S.Var.pretty_trace x (show_phase phase) (HM.mem called x) (HM.mem stable x) (HM.mem wpoint x); + if tracing then trace "sol2" "solve %a, phase: %s, called: %b, stable: %b, wpoint: %b" S.Var.pretty_trace x (show_phase phase) (HM.mem called x) (HM.mem stable x) (HM.mem wpoint x); init x; assert (Hooks.system x <> None); if not (HM.mem called x || HM.mem stable x) then ( - if tracing then trace "sol2" "stable add %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "stable add %a" S.Var.pretty_trace x; HM.replace stable x (); HM.replace called x (); (* Here we cache HM.mem wpoint x before eq. If during eq eval makes x wpoint, then be still don't apply widening the first time, but just overwrite. @@ -305,7 +305,7 @@ module Base = match reuse_eq with | Some d when narrow_reuse -> (* Do not reset deps for reuse of eq *) - if tracing then trace "sol2" "eq reused %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "eq reused %a" S.Var.pretty_trace x; incr SolverStats.narrow_reuses; d | _ -> @@ -327,15 +327,15 @@ module Base = else box old eqd in - if tracing then trace "sol" "Var: %a (wp: %b)\nOld value: %a\nEqd: %a\nNew value: %a\n" S.Var.pretty_trace x wp S.Dom.pretty old S.Dom.pretty eqd S.Dom.pretty wpd; + if tracing then trace "sol" "Var: %a (wp: %b)\nOld value: %a\nEqd: %a\nNew value: %a" S.Var.pretty_trace x wp S.Dom.pretty old S.Dom.pretty eqd S.Dom.pretty wpd; if cache then ( - if tracing then trace "cache" "cache size %d for %a\n" (HM.length l) S.Var.pretty_trace x; + if tracing then trace "cache" "cache size %d for %a" (HM.length l) S.Var.pretty_trace x; cache_sizes := HM.length l :: !cache_sizes; ); if not (Timing.wrap "S.Dom.equal" (fun () -> S.Dom.equal old wpd) ()) then ( (* value changed *) - if tracing then trace "sol" "Changed\n"; - (* if tracing && not (S.Dom.is_bot old) && HM.mem wpoint x then trace "solchange" "%a (wpx: %b): %a -> %a\n" S.Var.pretty_trace x (HM.mem wpoint x) S.Dom.pretty old S.Dom.pretty wpd; *) - if tracing && not (S.Dom.is_bot old) && HM.mem wpoint x then trace "solchange" "%a (wpx: %b): %a\n" S.Var.pretty_trace x (HM.mem wpoint x) S.Dom.pretty_diff (wpd, old); + if tracing then trace "sol" "Changed"; + (* if tracing && not (S.Dom.is_bot old) && HM.mem wpoint x then trace "solchange" "%a (wpx: %b): %a -> %a" S.Var.pretty_trace x (HM.mem wpoint x) S.Dom.pretty old S.Dom.pretty wpd; *) + if tracing && not (S.Dom.is_bot old) && HM.mem wpoint x then trace "solchange" "%a (wpx: %b): %a" S.Var.pretty_trace x (HM.mem wpoint x) S.Dom.pretty_diff (wpd, old); update_var_event x old wpd; HM.replace rho x wpd; destabilize x; @@ -343,30 +343,30 @@ module Base = ) else ( (* TODO: why non-equal and non-stable checks in switched order compared to TD3 paper? *) if not (HM.mem stable x) then ( (* value unchanged, but not stable, i.e. destabilized itself during rhs *) - if tracing then trace "sol2" "solve still unstable %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "solve still unstable %a" S.Var.pretty_trace x; (solve[@tailcall]) x Widen ) else ( if term && phase = Widen && HM.mem wpoint x then ( (* TODO: or use wp? *) - if tracing then trace "sol2" "solve switching to narrow %a\n" S.Var.pretty_trace x; - if tracing then trace "sol2" "stable remove %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "solve switching to narrow %a" S.Var.pretty_trace x; + if tracing then trace "sol2" "stable remove %a" S.Var.pretty_trace x; HM.remove stable x; HM.remove superstable x; Hooks.stable_remove x; (solve[@tailcall]) ~reuse_eq:eqd x Narrow ) else if remove_wpoint && not space && (not term || phase = Narrow) then ( (* this makes e.g. nested loops precise, ex. tests/regression/34-localization/01-nested.c - if we do not remove wpoint, the inner loop head will stay a wpoint and widen the outer loop variable. *) - if tracing then trace "sol2" "solve removing wpoint %a (%b)\n" S.Var.pretty_trace x (HM.mem wpoint x); + if tracing then trace "sol2" "solve removing wpoint %a (%b)" S.Var.pretty_trace x (HM.mem wpoint x); HM.remove wpoint x ) ) ) ) and eq x get set = - if tracing then trace "sol2" "eq %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "eq %a" S.Var.pretty_trace x; match Hooks.system x with | None -> S.Dom.bot () | Some f -> f get set and simple_solve l x y = - if tracing then trace "sol2" "simple_solve %a (rhs: %b)\n" S.Var.pretty_trace y (Hooks.system y <> None); + if tracing then trace "sol2" "simple_solve %a (rhs: %b)" S.Var.pretty_trace y (Hooks.system y <> None); if Hooks.system y = None then (init y; HM.replace stable y (); HM.find rho y) else if not space || HM.mem wpoint y then (solve y Widen; HM.find rho y) else if HM.mem called y then (init y; HM.remove l y; HM.find rho y) else (* TODO: [HM.mem called y] is not in the TD3 paper, what is it for? optimization? *) @@ -380,7 +380,7 @@ module Base = else (if cache then HM.replace l y eqd; eqd) ) and eval l x y = - if tracing then trace "sol2" "eval %a ## %a\n" S.Var.pretty_trace x S.Var.pretty_trace y; + if tracing then trace "sol2" "eval %a ## %a" S.Var.pretty_trace x S.Var.pretty_trace y; get_var_event y; if HM.mem called y then ( if restart_wpoint && not (HM.mem wpoint y) then ( @@ -388,21 +388,21 @@ module Base = The loop body might then side effect the old value, see tests/incremental/06-local-wpoint-read. Here we avoid this, by setting it to bottom for the loop body eval. *) if not (restart_once && HM.mem restarted_wpoint y) then ( - if tracing then trace "sol2" "wpoint restart %a ## %a\n" S.Var.pretty_trace y S.Dom.pretty (HM.find_default rho y (S.Dom.bot ())); + if tracing then trace "sol2" "wpoint restart %a ## %a" S.Var.pretty_trace y S.Dom.pretty (HM.find_default rho y (S.Dom.bot ())); HM.replace rho y (S.Dom.bot ()); if restart_once then (* avoid populating hashtable unnecessarily *) HM.replace restarted_wpoint y (); ) ); - if tracing then trace "sol2" "eval adding wpoint %a from %a\n" S.Var.pretty_trace y S.Var.pretty_trace x; + if tracing then trace "sol2" "eval adding wpoint %a from %a" S.Var.pretty_trace y S.Var.pretty_trace x; HM.replace wpoint y (); ); let tmp = simple_solve l x y in if HM.mem rho y then add_infl y x; - if tracing then trace "sol2" "eval %a ## %a -> %a\n" S.Var.pretty_trace x S.Var.pretty_trace y S.Dom.pretty tmp; + if tracing then trace "sol2" "eval %a ## %a -> %a" S.Var.pretty_trace x S.Var.pretty_trace y S.Dom.pretty tmp; tmp and side ?x y d = (* side from x to y; only to variables y w/o rhs; x only used for trace *) - if tracing then trace "sol2" "side to %a (wpx: %b) from %a ## value: %a\n" S.Var.pretty_trace y (HM.mem wpoint y) (Pretty.docOpt (S.Var.pretty_trace ())) x S.Dom.pretty d; + if tracing then trace "sol2" "side to %a (wpx: %b) from %a ## value: %a" S.Var.pretty_trace y (HM.mem wpoint y) (Pretty.docOpt (S.Var.pretty_trace ())) x S.Dom.pretty d; if Hooks.system y <> None then ( Logs.warn "side-effect to unknown w/ rhs: %a, contrib: %a" S.Var.pretty_trace y S.Dom.pretty d; ); @@ -428,11 +428,11 @@ module Base = in let old = HM.find rho y in let tmp = op old d in - if tracing then trace "sol2" "stable add %a\n" S.Var.pretty_trace y; + if tracing then trace "sol2" "stable add %a" S.Var.pretty_trace y; HM.replace stable y (); if not (S.Dom.leq tmp old) then ( - if tracing && not (S.Dom.is_bot old) then trace "solside" "side to %a (wpx: %b) from %a: %a -> %a\n" S.Var.pretty_trace y (HM.mem wpoint y) (Pretty.docOpt (S.Var.pretty_trace ())) x S.Dom.pretty old S.Dom.pretty tmp; - if tracing && not (S.Dom.is_bot old) then trace "solchange" "side to %a (wpx: %b) from %a: %a\n" S.Var.pretty_trace y (HM.mem wpoint y) (Pretty.docOpt (S.Var.pretty_trace ())) x S.Dom.pretty_diff (tmp, old); + if tracing && not (S.Dom.is_bot old) then trace "solside" "side to %a (wpx: %b) from %a: %a -> %a" S.Var.pretty_trace y (HM.mem wpoint y) (Pretty.docOpt (S.Var.pretty_trace ())) x S.Dom.pretty old S.Dom.pretty tmp; + if tracing && not (S.Dom.is_bot old) then trace "solchange" "side to %a (wpx: %b) from %a: %a" S.Var.pretty_trace y (HM.mem wpoint y) (Pretty.docOpt (S.Var.pretty_trace ())) x S.Dom.pretty_diff (tmp, old); let sided = match x with | Some x -> let sided = VS.mem x old_sides in @@ -446,7 +446,7 @@ module Base = (* make y a widening point if ... This will only matter for the next side _ y. *) let wpoint_if e = if e then ( - if tracing then trace "sol2" "side adding wpoint %a from %a\n" S.Var.pretty_trace y (Pretty.docOpt (S.Var.pretty_trace ())) x; + if tracing then trace "sol2" "side adding wpoint %a from %a" S.Var.pretty_trace y (Pretty.docOpt (S.Var.pretty_trace ())) x; HM.replace wpoint y () ) in @@ -481,7 +481,7 @@ module Base = | x -> failwith ("Unknown value '" ^ x ^ "' for option solvers.td3.side_widen!") ) and init x = - if tracing then trace "sol2" "init %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "init %a" S.Var.pretty_trace x; if not (HM.mem rho x) then ( new_var_event x; HM.replace rho x (S.Dom.bot ()) @@ -489,7 +489,7 @@ module Base = in let set_start (x,d) = - if tracing then trace "sol2" "set_start %a ## %a\n" S.Var.pretty_trace x S.Dom.pretty d; + if tracing then trace "sol2" "set_start %a ## %a" S.Var.pretty_trace x S.Dom.pretty d; init x; HM.replace rho x d; HM.replace stable x (); @@ -497,11 +497,11 @@ module Base = in let rec destabilize_normal x = - if tracing then trace "sol2" "destabilize %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "destabilize %a" S.Var.pretty_trace x; let w = HM.find_default infl x VS.empty in HM.replace infl x VS.empty; VS.iter (fun y -> - if tracing then trace "sol2" "stable remove %a\n" S.Var.pretty_trace y; + if tracing then trace "sol2" "stable remove %a" S.Var.pretty_trace y; HM.remove stable y; HM.remove superstable y; Hooks.stable_remove y; @@ -519,7 +519,7 @@ module Base = if GobConfig.get_bool "incremental.load" then ( let restart_leaf x = - if tracing then trace "sol2" "Restarting to bot %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "Restarting to bot %a" S.Var.pretty_trace x; Logs.debug "Restarting to bot %a" S.Var.pretty_trace x; HM.replace rho x (S.Dom.bot ()); (* HM.remove rho x; *) @@ -539,7 +539,7 @@ module Base = (* destabilize which restarts side-effected vars *) (* side_fuel specifies how many times (in recursion depth) to destabilize side_infl, None means infinite *) let rec destabilize_with_side ~side_fuel x = - if tracing then trace "sol2" "destabilize_with_side %a %a\n" S.Var.pretty_trace x (Pretty.docOpt (Pretty.dprintf "%d")) side_fuel; + if tracing then trace "sol2" "destabilize_with_side %a %a" S.Var.pretty_trace x (Pretty.docOpt (Pretty.dprintf "%d")) side_fuel; (* retrieve and remove (side-effect) dependencies/influences *) let w_side_dep = HM.find_default side_dep x VS.empty in @@ -567,8 +567,8 @@ module Base = (* destabilize side dep to redo side effects *) VS.iter (fun y -> - if tracing then trace "sol2" "destabilize_with_side %a side_dep %a\n" S.Var.pretty_trace x S.Var.pretty_trace y; - if tracing then trace "sol2" "stable remove %a\n" S.Var.pretty_trace y; + if tracing then trace "sol2" "destabilize_with_side %a side_dep %a" S.Var.pretty_trace x S.Var.pretty_trace y; + if tracing then trace "sol2" "stable remove %a" S.Var.pretty_trace y; HM.remove stable y; HM.remove superstable y; Hooks.stable_remove y; @@ -578,8 +578,8 @@ module Base = (* destabilize eval infl *) VS.iter (fun y -> - if tracing then trace "sol2" "destabilize_with_side %a infl %a\n" S.Var.pretty_trace x S.Var.pretty_trace y; - if tracing then trace "sol2" "stable remove %a\n" S.Var.pretty_trace y; + if tracing then trace "sol2" "destabilize_with_side %a infl %a" S.Var.pretty_trace x S.Var.pretty_trace y; + if tracing then trace "sol2" "stable remove %a" S.Var.pretty_trace y; HM.remove stable y; HM.remove superstable y; Hooks.stable_remove y; @@ -596,8 +596,8 @@ module Base = in (* TODO: should this also be conditional on restart_only_globals? right now goes through function entry side effects, but just doesn't restart them *) VS.iter (fun y -> - if tracing then trace "sol2" "destabilize_with_side %a side_infl %a\n" S.Var.pretty_trace x S.Var.pretty_trace y; - if tracing then trace "sol2" "stable remove %a\n" S.Var.pretty_trace y; + if tracing then trace "sol2" "destabilize_with_side %a side_infl %a" S.Var.pretty_trace x S.Var.pretty_trace y; + if tracing then trace "sol2" "stable remove %a" S.Var.pretty_trace y; HM.remove stable y; HM.remove superstable y; Hooks.stable_remove y; @@ -669,8 +669,8 @@ module Base = HM.remove side_dep x; (* destabilize side dep to redo side effects *) VS.iter (fun y -> - if tracing then trace "sol2" "destabilize_leaf %a side_dep %a\n" S.Var.pretty_trace x S.Var.pretty_trace y; - if tracing then trace "sol2" "stable remove %a\n" S.Var.pretty_trace y; + if tracing then trace "sol2" "destabilize_leaf %a side_dep %a" S.Var.pretty_trace x S.Var.pretty_trace y; + if tracing then trace "sol2" "stable remove %a" S.Var.pretty_trace y; HM.remove stable y; HM.remove superstable y; Hooks.stable_remove y; @@ -839,7 +839,7 @@ module Base = let restore () = let get x = let d = get ~check:true x in - if tracing then trace "sol2" "restored var %a ## %a\n" S.Var.pretty_trace x S.Dom.pretty d + if tracing then trace "sol2" "restored var %a ## %a" S.Var.pretty_trace x S.Dom.pretty d in List.iter get vs; HM.filteri_inplace (fun x _ -> HM.mem visited x) rho @@ -847,7 +847,7 @@ module Base = Timing.wrap "restore" restore (); Logs.debug "Solved %d vars. Total of %d vars after restore." !SolverStats.vars (HM.length rho); let avg xs = if List.is_empty !cache_sizes then 0.0 else float_of_int (BatList.sum xs) /. float_of_int (List.length xs) in - if tracing && cache then trace "cache" "#caches: %d, max: %d, avg: %.2f\n" (List.length !cache_sizes) (List.max !cache_sizes) (avg !cache_sizes); + if tracing && cache then trace "cache" "#caches: %d, max: %d, avg: %.2f" (List.length !cache_sizes) (List.max !cache_sizes) (avg !cache_sizes); ); stop_event (); diff --git a/src/solver/topDown.ml b/src/solver/topDown.ml index f7da560057..1514309c97 100644 --- a/src/solver/topDown.ml +++ b/src/solver/topDown.ml @@ -32,7 +32,7 @@ module WP = let wpoint = HM.create 10 in let add_infl y x = - if tracing then trace "sol2" "add_infl %a %a\n" S.Var.pretty_trace y S.Var.pretty_trace x; + if tracing then trace "sol2" "add_infl %a %a" S.Var.pretty_trace y S.Var.pretty_trace x; HM.replace infl y (VS.add x (try HM.find infl y with Not_found -> VS.empty)) in let add_set x y d = @@ -42,15 +42,15 @@ module WP = in let is_side x = HM.mem set x in let rec destabilize x = - (* if tracing then trace "sol2" "destabilize %a\n" S.Var.pretty_trace x; *) + (* if tracing then trace "sol2" "destabilize %a" S.Var.pretty_trace x; *) let w = HM.find_default infl x VS.empty in HM.replace infl x VS.empty; VS.iter (fun y -> HM.remove stable y; - if tracing then trace "sol2" "destabilize %a\n" S.Var.pretty_trace y; + if tracing then trace "sol2" "destabilize %a" S.Var.pretty_trace y; if not (HM.mem called y) then destabilize y) w and solve x = - if tracing then trace "sol2" "solve %a, called: %b, stable: %b\n" S.Var.pretty_trace x (HM.mem called x) (HM.mem stable x); + if tracing then trace "sol2" "solve %a, called: %b, stable: %b" S.Var.pretty_trace x (HM.mem called x) (HM.mem stable x); if not (HM.mem called x || HM.mem stable x) then ( HM.replace stable x (); HM.replace called x (); @@ -59,22 +59,22 @@ module WP = let old = HM.find rho x in let tmp' = eq x (eval x) (side x) in let tmp = S.Dom.join tmp' (sides x) in - if tracing then trace "sol" "Var: %a\n" S.Var.pretty_trace x ; - if tracing then trace "sol" "Contrib:%a\n" S.Dom.pretty tmp; + if tracing then trace "sol" "Var: %a" S.Var.pretty_trace x ; + if tracing then trace "sol" "Contrib:%a" S.Dom.pretty tmp; let tmp = if is_side x then S.Dom.widen old (S.Dom.join old tmp) else if wpx then box old tmp else tmp in HM.remove called x; if not (S.Dom.equal old tmp) then ( - if tracing then if is_side x then trace "sol2" "solve side: old = %a, tmp = %a, widen = %a\n" S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty (S.Dom.widen old (S.Dom.join old tmp)); + if tracing then if is_side x then trace "sol2" "solve side: old = %a, tmp = %a, widen = %a" S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty (S.Dom.widen old (S.Dom.join old tmp)); update_var_event x old tmp; - if tracing then trace "sol" "New Value:%a\n\n" S.Dom.pretty tmp; - if tracing then trace "sol2" "new value for %a (wpx: %b, is_side: %b) is %a. Old value was %a\n" S.Var.pretty_trace x (HM.mem rho x) (is_side x) S.Dom.pretty tmp S.Dom.pretty old; + if tracing then trace "sol" "New Value:%a" S.Dom.pretty tmp; + if tracing then trace "sol2" "new value for %a (wpx: %b, is_side: %b) is %a. Old value was %a" S.Var.pretty_trace x (HM.mem rho x) (is_side x) S.Dom.pretty tmp S.Dom.pretty old; HM.replace rho x tmp; destabilize x; ); (solve[@tailcall]) x ) and eq x get set = - if tracing then trace "sol2" "eq %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "eq %a" S.Var.pretty_trace x; eval_rhs_event x; match S.system x with | None -> S.Dom.bot () @@ -89,7 +89,7 @@ module WP = in f get sidef and eval x y = - if tracing then trace "sol2" "eval %a ## %a\n" S.Var.pretty_trace x S.Var.pretty_trace y; + if tracing then trace "sol2" "eval %a ## %a" S.Var.pretty_trace x S.Var.pretty_trace y; get_var_event y; if HM.mem called y || S.system y = None then HM.replace wpoint y (); solve y; @@ -97,11 +97,11 @@ module WP = HM.find rho y and sides x = let w = try HM.find set x with Not_found -> VS.empty in - let d = Enum.fold (fun d y -> let r = try S.Dom.join d (HPM.find rho' (y,x)) with Not_found -> d in if tracing then trace "sol2" "sides: side %a from %a: %a\n" S.Var.pretty_trace x S.Var.pretty_trace y S.Dom.pretty r; r) (S.Dom.bot ()) (VS.enum w) in - if tracing then trace "sol2" "sides %a ## %a\n" S.Var.pretty_trace x S.Dom.pretty d; + let d = Enum.fold (fun d y -> let r = try S.Dom.join d (HPM.find rho' (y,x)) with Not_found -> d in if tracing then trace "sol2" "sides: side %a from %a: %a" S.Var.pretty_trace x S.Var.pretty_trace y S.Dom.pretty r; r) (S.Dom.bot ()) (VS.enum w) in + if tracing then trace "sol2" "sides %a ## %a" S.Var.pretty_trace x S.Dom.pretty d; d and side x y d = - if tracing then trace "sol2" "side %a ## %a (wpx: %b) ## %a\n" S.Var.pretty_trace x S.Var.pretty_trace y (HM.mem rho y) S.Dom.pretty d; + if tracing then trace "sol2" "side %a ## %a (wpx: %b) ## %a" S.Var.pretty_trace x S.Var.pretty_trace y (HM.mem rho y) S.Dom.pretty d; let old = try HPM.find rho' (x,y) with Not_found -> S.Dom.bot () in if not (S.Dom.equal old d) then ( add_set x y (S.Dom.join old d); @@ -109,7 +109,7 @@ module WP = solve y; ) and init x = - if tracing then trace "sol2" "init %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "init %a" S.Var.pretty_trace x; if not (HM.mem rho x) then ( new_var_event x; HM.replace rho x (S.Dom.bot ()) @@ -117,7 +117,7 @@ module WP = in let set_start (x,d) = - if tracing then trace "sol2" "set_start %a ## %a\n" S.Var.pretty_trace x S.Dom.pretty d; + if tracing then trace "sol2" "set_start %a ## %a" S.Var.pretty_trace x S.Dom.pretty d; init x; add_set x x d; solve x @@ -134,7 +134,7 @@ module WP = let gs = keys sidevs in HM.clear sidevs; if gs <> [] then ( - if tracing then trace "sol2" "Round %d: %d side-effected variables to solve\n" !n (List.length gs); + if tracing then trace "sol2" "Round %d: %d side-effected variables to solve" !n (List.length gs); incr n; List.iter solve gs; List.iter solve vs; diff --git a/src/solver/topDown_deprecated.ml b/src/solver/topDown_deprecated.ml index 4e9799cf78..16c45fcd16 100644 --- a/src/solver/topDown_deprecated.ml +++ b/src/solver/topDown_deprecated.ml @@ -38,16 +38,16 @@ module TD3 = let add_set x y d = HM.replace set y (VS.add x (try HM.find set y with Not_found -> VS.empty)); HPM.add rho' (x,y) d; HM.add sidevs y () in let is_side x = HM.mem set x in let make_wpoint x = - if tracing then trace "sol2" "make_wpoint %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "make_wpoint %a" S.Var.pretty_trace x; HM.replace wpoint x () in let rec destabilize x = - if tracing then trace "sol2" "destabilize %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "destabilize %a" S.Var.pretty_trace x; let t = HM.find_default infl x VS.empty in HM.replace infl x VS.empty; VS.iter (fun y -> HM.remove stable y; if not (HM.mem called y) then destabilize y) t and solve x = - if tracing then trace "sol2" "solve %a, called: %b, stable: %b\n" S.Var.pretty_trace x (HM.mem called x) (HM.mem stable x); + if tracing then trace "sol2" "solve %a, called: %b, stable: %b" S.Var.pretty_trace x (HM.mem called x) (HM.mem stable x); if not (HM.mem called x || HM.mem stable x) then begin HM.replace called x (); let wpx = HM.mem wpoint x in @@ -56,21 +56,21 @@ module TD3 = let old = HM.find rho x in let tmp = eq x (eval x) (side x) in let tmp = S.Dom.join tmp (sides x) in - if tracing then trace "sol" "Var: %a\n" S.Var.pretty_trace x ; - if tracing then trace "sol" "Contrib:%a\n" S.Dom.pretty tmp; + if tracing then trace "sol" "Var: %a" S.Var.pretty_trace x ; + if tracing then trace "sol" "Contrib:%a" S.Dom.pretty tmp; let tmp = if is_side x then S.Dom.widen old (S.Dom.join old tmp) else if wpx then box old tmp else tmp in HM.remove called x; if not (S.Dom.equal old tmp) then begin update_var_event x old tmp; - if tracing then trace "sol" "New Value:%a\n\n" S.Dom.pretty tmp; - if tracing then trace "sol2" "new value for %a (wpx: %b, is_side: %b) is %a. Old value was %a\n" S.Var.pretty_trace x wpx (is_side x) S.Dom.pretty tmp S.Dom.pretty old; + if tracing then trace "sol" "New Value:%a" S.Dom.pretty tmp; + if tracing then trace "sol2" "new value for %a (wpx: %b, is_side: %b) is %a. Old value was %a" S.Var.pretty_trace x wpx (is_side x) S.Dom.pretty tmp S.Dom.pretty old; HM.replace rho x tmp; destabilize x; (solve[@tailcall]) x; end; end; and eq x get set = - if tracing then trace "sol2" "eq %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "eq %a" S.Var.pretty_trace x; eval_rhs_event x; match S.system x with | None -> S.Dom.bot () @@ -85,7 +85,7 @@ module TD3 = in f get sidef and eval x y = - if tracing then trace "sol2" "eval %a ## %a\n" S.Var.pretty_trace x S.Var.pretty_trace y; + if tracing then trace "sol2" "eval %a ## %a" S.Var.pretty_trace x S.Var.pretty_trace y; get_var_event y; if not (HM.mem rho y) then init y; if HM.mem called y then make_wpoint y else if neg is_side y then solve y; @@ -94,11 +94,11 @@ module TD3 = and sides x = let w = try HM.find set x with Not_found -> VS.empty in let d = Enum.fold (fun d z -> try S.Dom.join d (HPM.find rho' (z,x)) with Not_found -> d) (S.Dom.bot ()) (VS.enum w) in - if tracing then trace "sol2" "sides %a ## %a\n" S.Var.pretty_trace x S.Dom.pretty d; + if tracing then trace "sol2" "sides %a ## %a" S.Var.pretty_trace x S.Dom.pretty d; d and side x y d = if S.Dom.is_bot d then () else - if tracing then trace "sol2" "side %a ## %a (wpx: %b) ## %a\n" S.Var.pretty_trace x S.Var.pretty_trace y (HM.mem wpoint y) S.Dom.pretty d; + if tracing then trace "sol2" "side %a ## %a (wpx: %b) ## %a" S.Var.pretty_trace x S.Var.pretty_trace y (HM.mem wpoint y) S.Dom.pretty d; if not (HM.mem rho y) then begin init y; add_set x y d; @@ -115,7 +115,7 @@ module TD3 = end end and init x = - if tracing then trace "sol2" "init %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "init %a" S.Var.pretty_trace x; if not (HM.mem rho x) then begin new_var_event x; HM.replace rho x (S.Dom.bot ()); @@ -124,7 +124,7 @@ module TD3 = in let set_start (x,d) = - if tracing then trace "sol2" "set_start %a ## %a\n" S.Var.pretty_trace x S.Dom.pretty d; + if tracing then trace "sol2" "set_start %a ## %a" S.Var.pretty_trace x S.Dom.pretty d; init x; add_set x x d; solve x diff --git a/src/solver/topDown_space_cache_term.ml b/src/solver/topDown_space_cache_term.ml index 689ebf56fb..0022756a31 100644 --- a/src/solver/topDown_space_cache_term.ml +++ b/src/solver/topDown_space_cache_term.ml @@ -29,16 +29,16 @@ module WP = let cache_sizes = ref [] in let add_infl y x = - if tracing then trace "sol2" "add_infl %a %a\n" S.Var.pretty_trace y S.Var.pretty_trace x; + if tracing then trace "sol2" "add_infl %a %a" S.Var.pretty_trace y S.Var.pretty_trace x; HM.replace infl y (VS.add x (try HM.find infl y with Not_found -> VS.empty)) in let rec destabilize x = - if tracing then trace "sol2" "destabilize %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "destabilize %a" S.Var.pretty_trace x; let w = HM.find_default infl x VS.empty in HM.replace infl x VS.empty; VS.iter (fun y -> HM.remove stable y; if not (HM.mem called y) then destabilize y) w and solve x phase = - if tracing then trace "sol2" "solve %a, called: %b, stable: %b\n" S.Var.pretty_trace x (HM.mem called x) (HM.mem stable x); + if tracing then trace "sol2" "solve %a, called: %b, stable: %b" S.Var.pretty_trace x (HM.mem called x) (HM.mem stable x); if not (HM.mem called x || HM.mem stable x) then ( HM.replace stable x (); HM.replace called x (); @@ -46,17 +46,17 @@ module WP = let l = HM.create 10 in let tmp = eq x (eval l x) (side l) in let tmp = S.Dom.join tmp (try HM.find rho' x with Not_found -> S.Dom.bot ()) in - if tracing then trace "sol" "Var: %a\n" S.Var.pretty_trace x ; - if tracing then trace "sol" "Contrib:%a\n" S.Dom.pretty tmp; + if tracing then trace "sol" "Var: %a" S.Var.pretty_trace x ; + if tracing then trace "sol" "Contrib:%a" S.Dom.pretty tmp; HM.remove called x; let tmp = match phase with Widen -> S.Dom.widen old (S.Dom.join old tmp) | Narrow -> S.Dom.narrow old tmp in - if tracing then trace "cache" "cache size %d for %a\n" (HM.length l) S.Var.pretty_trace x; + if tracing then trace "cache" "cache size %d for %a" (HM.length l) S.Var.pretty_trace x; cache_sizes := HM.length l :: !cache_sizes; if not (S.Dom.equal old tmp) then ( - (* if tracing then if is_side x then trace "sol2" "solve side: old = %a, tmp = %a, widen = %a\n" S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty (S.Dom.widen old (S.Dom.join old tmp)); *) + (* if tracing then if is_side x then trace "sol2" "solve side: old = %a, tmp = %a, widen = %a" S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty (S.Dom.widen old (S.Dom.join old tmp)); *) update_var_event x old tmp; - if tracing then trace "sol" "New Value:%a\n\n" S.Dom.pretty tmp; - (* if tracing then trace "sol2" "new value for %a (wpx: %b, is_side: %b) is %a. Old value was %a\n" S.Var.pretty_trace x (HM.mem rho x) (is_side x) S.Dom.pretty tmp S.Dom.pretty old; *) + if tracing then trace "sol" "New Value:%a" S.Dom.pretty tmp; + (* if tracing then trace "sol2" "new value for %a (wpx: %b, is_side: %b) is %a. Old value was %a" S.Var.pretty_trace x (HM.mem rho x) (is_side x) S.Dom.pretty tmp S.Dom.pretty old; *) HM.replace rho x tmp; destabilize x; (solve[@tailcall]) x phase; @@ -68,13 +68,13 @@ module WP = ); ) and eq x get set = - if tracing then trace "sol2" "eq %a \n" S.Var.pretty_trace x; + if tracing then trace "sol2" "eq %a" S.Var.pretty_trace x; eval_rhs_event x; match S.system x with | None -> S.Dom.bot () | Some f -> f get set and simple_solve l x y = - if tracing then trace "sol2" "simple_solve %a (rhs: %b)\n" S.Var.pretty_trace y (S.system y <> None); + if tracing then trace "sol2" "simple_solve %a (rhs: %b)" S.Var.pretty_trace y (S.system y <> None); if S.system y = None then init y; if HM.mem rho y then (solve y Widen; HM.find rho y) else if HM.mem called y then (init y; HM.remove l y; HM.find rho y) else @@ -87,13 +87,13 @@ module WP = else (HM.replace l y tmp; tmp) ) and eval l x y = - if tracing then trace "sol2" "eval %a ## %a\n" S.Var.pretty_trace x S.Var.pretty_trace y; + if tracing then trace "sol2" "eval %a ## %a" S.Var.pretty_trace x S.Var.pretty_trace y; get_var_event y; let tmp = simple_solve l x y in if HM.mem rho y then add_infl y x; tmp and side l y d = - if tracing then trace "sol2" "side to %a (wpx: %b) ## value: %a\n" S.Var.pretty_trace y (HM.mem rho y) S.Dom.pretty d; + if tracing then trace "sol2" "side to %a (wpx: %b) ## value: %a" S.Var.pretty_trace y (HM.mem rho y) S.Dom.pretty d; let old = try HM.find rho' y with Not_found -> S.Dom.bot () in if not (S.Dom.leq d old) then ( HM.replace rho' y (S.Dom.join old d); @@ -103,7 +103,7 @@ module WP = solve y Widen; ) and init x = - if tracing then trace "sol2" "init %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "init %a" S.Var.pretty_trace x; if not (HM.mem rho x) then ( new_var_event x; HM.replace rho x (S.Dom.bot ()) @@ -111,7 +111,7 @@ module WP = in let set_start (x,d) = - if tracing then trace "sol2" "set_start %a ## %a\n" S.Var.pretty_trace x S.Dom.pretty d; + if tracing then trace "sol2" "set_start %a ## %a" S.Var.pretty_trace x S.Dom.pretty d; init x; HM.replace rho x d; HM.replace rho' x d; @@ -175,7 +175,7 @@ module WP = let restore () = let get x = let d = get x in - if tracing then trace "sol2" "restored var %a ## %a\n" S.Var.pretty_trace x S.Dom.pretty d + if tracing then trace "sol2" "restored var %a ## %a" S.Var.pretty_trace x S.Dom.pretty d in List.iter get vs in @@ -183,7 +183,7 @@ module WP = Logs.debug "Solved %d vars. Total of %d vars after restore." !SolverStats.vars (HM.length rho); ); let avg xs = float_of_int (BatList.sum xs) /. float_of_int (List.length xs) in - if tracing then trace "cache" "#caches: %d, max: %d, avg: %.2f\n" (List.length !cache_sizes) (List.max !cache_sizes) (avg !cache_sizes); + if tracing then trace "cache" "#caches: %d, max: %d, avg: %.2f" (List.length !cache_sizes) (List.max !cache_sizes) (avg !cache_sizes); stop_event (); diff --git a/src/solver/topDown_term.ml b/src/solver/topDown_term.ml index d15493b5a1..9d89a42898 100644 --- a/src/solver/topDown_term.ml +++ b/src/solver/topDown_term.ml @@ -29,19 +29,19 @@ module WP = let wpoint = HM.create 10 in let add_infl y x = - if tracing then trace "sol2" "add_infl %a %a\n" S.Var.pretty_trace y S.Var.pretty_trace x; + if tracing then trace "sol2" "add_infl %a %a" S.Var.pretty_trace y S.Var.pretty_trace x; HM.replace infl y (VS.add x (try HM.find infl y with Not_found -> VS.empty)) in let rec destabilize x = - if tracing then trace "sol2" "destabilize %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "destabilize %a" S.Var.pretty_trace x; let w = HM.find_default infl x VS.empty in HM.replace infl x VS.empty; VS.iter (fun y -> HM.remove stable y; - (* if tracing then trace "sol2" "destabilize %a\n" S.Var.pretty_trace y; *) + (* if tracing then trace "sol2" "destabilize %a" S.Var.pretty_trace y; *) if not (HM.mem called y) then destabilize y) w and solve x phase = - if tracing then trace "sol2" "solve %a, called: %b, stable: %b\n" S.Var.pretty_trace x (HM.mem called x) (HM.mem stable x); + if tracing then trace "sol2" "solve %a, called: %b, stable: %b" S.Var.pretty_trace x (HM.mem called x) (HM.mem stable x); if not (HM.mem called x || HM.mem stable x) then ( HM.replace stable x (); HM.replace called x (); @@ -50,15 +50,15 @@ module WP = let old = HM.find rho x in let tmp = eq x (eval x) side in let tmp = S.Dom.join tmp (try HM.find rho' x with Not_found -> S.Dom.bot ()) in - if tracing then trace "sol" "Var: %a\n" S.Var.pretty_trace x ; - if tracing then trace "sol" "Contrib:%a\n" S.Dom.pretty tmp; + if tracing then trace "sol" "Var: %a" S.Var.pretty_trace x ; + if tracing then trace "sol" "Contrib:%a" S.Dom.pretty tmp; HM.remove called x; let tmp = if wpx then match phase with Widen -> S.Dom.widen old (S.Dom.join old tmp) | Narrow -> S.Dom.narrow old tmp else tmp in if not (S.Dom.equal old tmp) then ( - (* if tracing then if is_side x then trace "sol2" "solve side: old = %a, tmp = %a, widen = %a\n" S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty (S.Dom.widen old (S.Dom.join old tmp)); *) + (* if tracing then if is_side x then trace "sol2" "solve side: old = %a, tmp = %a, widen = %a" S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty (S.Dom.widen old (S.Dom.join old tmp)); *) update_var_event x old tmp; - if tracing then trace "sol" "New Value:%a\n\n" S.Dom.pretty tmp; - (* if tracing then trace "sol2" "new value for %a (wpx: %b, is_side: %b) is %a. Old value was %a\n" S.Var.pretty_trace x (HM.mem rho x) (is_side x) S.Dom.pretty tmp S.Dom.pretty old; *) + if tracing then trace "sol" "New Value:%a" S.Dom.pretty tmp; + (* if tracing then trace "sol2" "new value for %a (wpx: %b, is_side: %b) is %a. Old value was %a" S.Var.pretty_trace x (HM.mem rho x) (is_side x) S.Dom.pretty tmp S.Dom.pretty old; *) HM.replace rho x tmp; destabilize x; (solve[@tailcall]) x phase; @@ -70,13 +70,13 @@ module WP = ); ) and eq x get set = - if tracing then trace "sol2" "eq %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "eq %a" S.Var.pretty_trace x; eval_rhs_event x; match S.system x with | None -> S.Dom.bot () | Some f -> f get set and eval x y = - if tracing then trace "sol2" "eval %a ## %a\n" S.Var.pretty_trace x S.Var.pretty_trace y; + if tracing then trace "sol2" "eval %a ## %a" S.Var.pretty_trace x S.Var.pretty_trace y; get_var_event y; if HM.mem called y then HM.replace wpoint y (); solve y Widen; @@ -91,7 +91,7 @@ module WP = solve y Widen; ) and init x = - if tracing then trace "sol2" "init %a\n" S.Var.pretty_trace x; + if tracing then trace "sol2" "init %a" S.Var.pretty_trace x; if not (HM.mem rho x) then ( new_var_event x; HM.replace rho x (S.Dom.bot ()) @@ -99,7 +99,7 @@ module WP = in let set_start (x,d) = - if tracing then trace "sol2" "set_start %a ## %a\n" S.Var.pretty_trace x S.Dom.pretty d; + if tracing then trace "sol2" "set_start %a ## %a" S.Var.pretty_trace x S.Dom.pretty d; init x; HM.replace rho x d; solve x Widen From 5dbce341a9ce11341eebfe9a1707ff5344949fc1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 11:33:06 +0200 Subject: [PATCH 239/280] Remove newlines and add condition to more trace calls --- src/analyses/base.ml | 8 ++++---- src/cdomain/value/cdomains/valueDomain.ml | 24 ++++++++++++----------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 656ab53678..2bc9d99fdb 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -756,7 +756,7 @@ struct let te2 = Cilfacade.typeOf e2 in let both_arith_type = isArithmeticType te1 && isArithmeticType te2 in let is_safe = (extra_is_safe || VD.is_safe_cast t1 te1 && VD.is_safe_cast t2 te2) && not both_arith_type in - M.tracel "cast" "remove cast on both sides for %a? -> %b\n" d_exp exp is_safe; + if M.tracing then M.tracel "cast" "remove cast on both sides for %a? -> %b" d_exp exp is_safe; if is_safe then ( (* we can ignore the casts if the casts can't change the value *) let e1 = if isArithmeticType te1 then c1 else e1 in let e2 = if isArithmeticType te2 then c2 else e2 in @@ -772,7 +772,7 @@ struct (* seems like constFold already converts CChr to CInt *) | Const (CChr x) -> eval_rv ~ctx st (Const (charConstToInt x)) (* char becomes int, see Cil doc/ISO C 6.4.4.4.10 *) | Const (CInt (num,ikind,str)) -> - (match str with Some x -> M.tracel "casto" "CInt (%s, %a, %s)\n" (Z.to_string num) d_ikind ikind x | None -> ()); + (match str with Some x -> if M.tracing then M.tracel "casto" "CInt (%s, %a, %s)" (Z.to_string num) d_ikind ikind x | None -> ()); Int (ID.cast_to ikind (IntDomain.of_const (num,ikind,str))) | Const (CReal (_,fkind, Some str)) when not (Cilfacade.isComplexFKind fkind) -> Float (FD.of_string fkind str) (* prefer parsing from string due to higher precision *) | Const (CReal (num, fkind, None)) when not (Cilfacade.isComplexFKind fkind) && num = 0.0 -> Float (FD.of_const fkind num) (* constant 0 is ok, CIL creates these for zero-initializers; it is safe across float types *) @@ -918,7 +918,7 @@ struct | (Var v, ofs) -> get ~ctx st (eval_lv ~ctx st (Var v, ofs)) (Some exp) (* | Lval (Mem e, ofs) -> get ~ctx st (eval_lv ~ctx (Mem e, ofs)) *) | (Mem e, ofs) -> - (*M.tracel "cast" "Deref: lval: %a\n" d_plainlval lv;*) + (*if M.tracing then M.tracel "cast" "Deref: lval: %a" d_plainlval lv;*) let rec contains_vla (t:typ) = match t with | TPtr (t, _) -> contains_vla t | TArray(t, None, args) -> true @@ -1935,7 +1935,7 @@ struct if M.tracing && exps <> [] then ( let addrs = List.map (Tuple3.first) invalids' in let vs = List.map (Tuple3.third) invalids' in - M.tracel "invalidate" "Setting addresses [%a] to values [%a]\n" (d_list ", " AD.pretty) addrs (d_list ", " VD.pretty) vs + if M.tracing then M.tracel "invalidate" "Setting addresses [%a] to values [%a]" (d_list ", " AD.pretty) addrs (d_list ", " VD.pretty) vs ); set_many ~ctx st invalids' diff --git a/src/cdomain/value/cdomains/valueDomain.ml b/src/cdomain/value/cdomains/valueDomain.ml index 2f2b7a774d..52bcbd2c7d 100644 --- a/src/cdomain/value/cdomains/valueDomain.ml +++ b/src/cdomain/value/cdomains/valueDomain.ml @@ -373,32 +373,32 @@ struct let rec adjust_offs v o d = let ta = try Addr.Offs.type_of ~base:v.vtype o with Offset.Type_of_error (t,s) -> raise (CastError s) in let info = GobPretty.sprintf "Ptr-Cast %a from %a to %a" Addr.pretty (Addr.Addr (v,o)) d_type ta d_type t in - M.tracel "casta" "%s\n" info; + if M.tracing then M.tracel "casta" "%s" info; (* TODO: inline info? *) let err s = raise (CastError (s ^ " (" ^ info ^ ")")) in match Stdlib.compare (bitsSizeOf (stripVarLenArr t)) (bitsSizeOf (stripVarLenArr ta)) with (* TODO is it enough to compare the size? -> yes? *) | 0 -> - M.tracel "casta" "same size\n"; + if M.tracing then M.tracel "casta" "same size"; if not (typ_eq t ta) then err "Cast to different type of same size." - else (M.tracel "casta" "SUCCESS!\n"; o) + else (if M.tracing then M.tracel "casta" "SUCCESS!"; o) | c when c > 0 -> (* cast to bigger/outer type *) - M.tracel "casta" "cast to bigger size\n"; + if M.tracing then M.tracel "casta" "cast to bigger size"; if d = Some false then err "Ptr-cast to type of incompatible size!" else if o = `NoOffset then err "Ptr-cast to outer type, but no offset to remove." else if Addr.Offs.cmp_zero_offset o = `MustZero then adjust_offs v (Addr.Offs.remove_offset o) (Some true) else err "Ptr-cast to outer type, but possibly from non-zero offset." | _ -> (* cast to smaller/inner type *) - M.tracel "casta" "cast to smaller size\n"; + if M.tracing then M.tracel "casta" "cast to smaller size"; if d = Some true then err "Ptr-cast to type of incompatible size!" else begin match ta, t with (* struct to its first field *) | TComp ({cfields = fi::_; _}, _), _ -> - M.tracel "casta" "cast struct to its first field\n"; + if M.tracing then M.tracel "casta" "cast struct to its first field"; adjust_offs v (Addr.Offs.add_offset o (`Field (fi, `NoOffset))) (Some false) (* array of the same type but different length, e.g. assign array (with length) to array-ptr (no length) *) | TArray (t1, _, _), TArray (t2, _, _) when typ_eq t1 t2 -> o (* array to its first element *) | TArray _, _ -> - M.tracel "casta" "cast array to its first element\n"; + if M.tracing then M.tracel "casta" "cast array to its first element"; adjust_offs v (Addr.Offs.add_offset o (`Index (IndexDomain.of_int (Cilfacade.ptrdiff_ikind ()) Z.zero, `NoOffset))) (Some false) | _ -> err @@ Format.sprintf "Cast to neither array index nor struct field. is_zero_offset: %b" (Addr.Offs.cmp_zero_offset o = `MustZero) end @@ -415,7 +415,7 @@ struct begin try Addr (v, (adjust_offs v o None)) (* cast of one address by adjusting the abstract offset *) with | CastError s -> (* don't know how to handle this cast :( *) - M.tracel "caste" "%s\n" s; + if M.tracing then M.tracel "caste" "%s" s; a (* probably garbage, but this is deref's problem *) (*raise (CastError s)*) | SizeOfError (s,t) -> @@ -425,7 +425,8 @@ struct | x -> x (* TODO we should also keep track of the type here *) in let a' = AD.map one_addr a in - M.tracel "cast" "cast_addr %a to %a is %a!\n" AD.pretty a d_type t AD.pretty a'; a' + if M.tracing then M.tracel "cast" "cast_addr %a to %a is %a!" AD.pretty a d_type t AD.pretty a'; + a' (* this is called for: * 1. normal casts @@ -441,7 +442,7 @@ struct | JmpBuf _ -> v | _ -> - let log_top (_,l,_,_) = Messages.tracel "cast" "log_top at %d: %a to %a is top!\n" l pretty v d_type t in + let log_top (_,l,_,_) = if Messages.tracing then Messages.tracel "cast" "log_top at %d: %a to %a is top!" l pretty v d_type t in let t = unrollType t in let v' = match t with | TInt (ik,_) -> @@ -519,7 +520,8 @@ struct | _ -> log_top __POS__; assert false in let s_torg = match torg with Some t -> CilType.Typ.show t | None -> "?" in - Messages.tracel "cast" "cast %a from %s to %a is %a!\n" pretty v s_torg d_type t pretty v'; v' + if Messages.tracing then Messages.tracel "cast" "cast %a from %s to %a is %a!" pretty v s_torg d_type t pretty v'; + v' let warn_type op x y = From 89b5dc2e0c78dc491e774732a5f2767d108b274d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 11:33:35 +0200 Subject: [PATCH 240/280] Remove unnecessary trailing newlines from messages --- src/analyses/loopTermination.ml | 2 +- src/analyses/memLeak.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 857b6189d0..ab768e235d 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -74,7 +74,7 @@ struct | Queries.MustTermAllLoops -> let multithreaded = ctx.ask Queries.IsEverMultiThreaded in if multithreaded then ( - M.warn ~category:Termination "The program might not terminate! (Multithreaded)\n"; + M.warn ~category:Termination "The program might not terminate! (Multithreaded)"; false) else G.for_all (fun _ term_info -> term_info) (ctx.global ()) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 456d434be7..f2d707067d 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -169,7 +169,7 @@ struct let allocated_and_unreachable_mem = D.diff allocated_mem reachable_mem in if not (D.is_empty allocated_and_unreachable_mem) then ( set_mem_safety_flag InvalidMemTrack; - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "There is unreachable allocated heap memory at program exit. A memory leak might occur for the alloc vars %a\n" (Pretty.d_list ", " CilType.Varinfo.pretty) (D.elements allocated_and_unreachable_mem) + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "There is unreachable allocated heap memory at program exit. A memory leak might occur for the alloc vars %a" (Pretty.d_list ", " CilType.Varinfo.pretty) (D.elements allocated_and_unreachable_mem) ); (* Check and warn if some of the allocated memory is not deallocated at program exit *) match assert_exp_imprecise, exp with From 03162296776ec596a26a74b06a721796a3f5a0e8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 11:33:51 +0200 Subject: [PATCH 241/280] Remove unnecessary trailing newlines from logging --- src/analyses/mCP.ml | 2 +- src/maingoblint.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index 4ae5c22d74..27b5f7ce3c 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -58,7 +58,7 @@ struct let y = find_id yn in if not (exists (fun (y',_) -> y=y') xs) then begin let xn = find_spec_name x in - Logs.error "Activated analysis '%s' depends on '%s' and '%s' is not activated.\n" xn yn yn; + Logs.error "Activated analysis '%s' depends on '%s' and '%s' is not activated." xn yn yn; raise Stdlib.Exit end in diff --git a/src/maingoblint.ml b/src/maingoblint.ml index 4b67804bb8..4008c6c073 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -135,7 +135,7 @@ let check_arguments () = failwith "Option error" in let warn m = Logs.warn "%s" m in - if get_bool "allfuns" && not (get_bool "exp.earlyglobs") then (set_bool "exp.earlyglobs" true; warn "allfuns enables exp.earlyglobs.\n"); + if get_bool "allfuns" && not (get_bool "exp.earlyglobs") then (set_bool "exp.earlyglobs" true; warn "allfuns enables exp.earlyglobs."); if not @@ List.mem "escape" @@ get_string_list "ana.activated" then warn "Without thread escape analysis, every local variable whose address is taken is considered escaped, i.e., global!"; if List.mem "malloc_null" @@ get_string_list "ana.activated" && not @@ get_bool "sem.malloc.fail" then (set_bool "sem.malloc.fail" true; warn "The malloc_null analysis enables sem.malloc.fail."); if List.mem "memOutOfBounds" @@ get_string_list "ana.activated" && not @@ get_bool "cil.addNestedScopeAttr" then (set_bool "cil.addNestedScopeAttr" true; warn "The memOutOfBounds analysis enables cil.addNestedScopeAttr."); From 49f923772f9329424a4b89886833bf6138a30435 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 11:34:21 +0200 Subject: [PATCH 242/280] Remove unnecessary trailing newline from Analyses.VarF.pretty_trace --- src/framework/analyses.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 61f43cb1a5..2bb729501b 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -33,7 +33,7 @@ struct let getLocation (n,d) = Node.location n let pretty_trace () ((n,c) as x) = - if get_bool "dbg.trace.context" then dprintf "(%a, %a) on %a \n" Node.pretty_trace n LD.pretty c CilType.Location.pretty (getLocation x) + if get_bool "dbg.trace.context" then dprintf "(%a, %a) on %a" Node.pretty_trace n LD.pretty c CilType.Location.pretty (getLocation x) (* if get_bool "dbg.trace.context" then dprintf "(%a, %d) on %a" Node.pretty_trace n (LD.tag c) CilType.Location.pretty (getLocation x) *) else dprintf "%a on %a" Node.pretty_trace n CilType.Location.pretty (getLocation x) From 9d4dd8c20b9ccd1a2d54da8f946058477e0dab59 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Mar 2024 11:19:32 +0200 Subject: [PATCH 243/280] Remove duplicated tracing conditional Co-authored-by: Michael Schwarz --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 2bc9d99fdb..b82e6727d1 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1935,7 +1935,7 @@ struct if M.tracing && exps <> [] then ( let addrs = List.map (Tuple3.first) invalids' in let vs = List.map (Tuple3.third) invalids' in - if M.tracing then M.tracel "invalidate" "Setting addresses [%a] to values [%a]" (d_list ", " AD.pretty) addrs (d_list ", " VD.pretty) vs + M.tracel "invalidate" "Setting addresses [%a] to values [%a]" (d_list ", " AD.pretty) addrs (d_list ", " VD.pretty) vs ); set_many ~ctx st invalids' From 56ee1a6b9e81de9ab4030e40fa2f2e4d8e8a2f34 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 19 Mar 2024 13:06:19 +0100 Subject: [PATCH 244/280] overflow caching bug --- src/domains/queries.ml | 1 + tests/regression/46-apron2/81-overflow-caching.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 tests/regression/46-apron2/81-overflow-caching.c diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 9dacdd40cf..0733d40516 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -385,6 +385,7 @@ struct | Any (MayBeModifiedSinceSetjmp e1), Any (MayBeModifiedSinceSetjmp e2) -> JmpBufDomain.BufferEntry.compare e1 e2 | Any (MustBeSingleThreaded {since_start=s1;}), Any (MustBeSingleThreaded {since_start=s2;}) -> Stdlib.compare s1 s2 | Any (TmpSpecial lv1), Any (TmpSpecial lv2) -> Mval.Exp.compare lv1 lv2 + | Any (MaySignedOverflow e1), Any (MaySignedOverflow e2) -> CilType.Exp.compare e1 e2 (* only argumentless queries should remain *) | _, _ -> Stdlib.compare (order a) (order b) diff --git a/tests/regression/46-apron2/81-overflow-caching.c b/tests/regression/46-apron2/81-overflow-caching.c new file mode 100644 index 0000000000..d96aabed7a --- /dev/null +++ b/tests/regression/46-apron2/81-overflow-caching.c @@ -0,0 +1,14 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.relation.privatization top + +#include +#include + + int num = 1; + + +int main() { + + while(num > 0) + num++; // Here num overflows + return 0; +} From d9bd4280b3dca72cee7817ef6a3217d11821c063 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 19 Mar 2024 13:21:00 +0100 Subject: [PATCH 245/280] removed state-dependency from query_evalint --- src/analyses/base.ml | 2 +- src/cdomains/apron/sharedFunctions.apron.ml | 2 +- tests/regression/46-apron2/80-fixpoint-not-reached.c | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 tests/regression/46-apron2/80-fixpoint-not-reached.c diff --git a/src/analyses/base.ml b/src/analyses/base.ml index fc6735be49..208c78bbf6 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1105,7 +1105,7 @@ struct | Bot -> Queries.ID.top () (* out-of-scope variables cause bot, but query result should then be unknown *) | Top -> Queries.ID.top () (* some float computations cause top (57-float/01-base), but query result should then be unknown *) | v -> M.debug ~category:Analyzer "Base EvalInt %a query answering bot instead of %a" d_exp e VD.pretty v; Queries.ID.bot () - | exception (IntDomain.ArithmeticOnIntegerBot _) when not !AnalysisState.should_warn -> Queries.ID.top () (* for some privatizations, values can intermediately be bot because side-effects have not happened yet *) + | exception (IntDomain.ArithmeticOnIntegerBot _) -> Queries.ID.top () in if M.tracing then M.traceu "evalint" "base query_evalint %a -> %a\n" d_exp e Queries.ID.pretty r; r diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 8470a2fffa..1e17074088 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -189,7 +189,7 @@ struct in let exp = Cil.constFold false exp in let res = conv exp in - if M.tracing then M.trace "relation" "texpr1_expr_of_cil_exp: %a -> %s\n" d_plainexp exp (Format.asprintf "%a" Texpr1.print_expr res); + if M.tracing then M.trace "relation" "texpr1_expr_of_cil_exp: %a -> %s (%b)\n" d_plainexp exp (Format.asprintf "%a" Texpr1.print_expr res) (Lazy.force no_ov); res let texpr1_of_cil_exp ask d env e no_ov = diff --git a/tests/regression/46-apron2/80-fixpoint-not-reached.c b/tests/regression/46-apron2/80-fixpoint-not-reached.c new file mode 100644 index 0000000000..582734025b --- /dev/null +++ b/tests/regression/46-apron2/80-fixpoint-not-reached.c @@ -0,0 +1,7 @@ +//PARAM: --set sem.int.signed_overflow assume_none --set ana.activated[+] apron + +int main() { + int minInt = -2147483647 + -1; + int x = (minInt + -1) +1; + return 0; +} \ No newline at end of file From 575080101f32c37dbd04499cdcb83915ce860dc5 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 19 Mar 2024 15:15:36 +0100 Subject: [PATCH 246/280] reestablished bottom-free arithmetics on final solution --- src/analyses/base.ml | 2 +- tests/regression/46-apron2/81-overflow-caching.c | 3 +++ .../{80-fixpoint-not-reached.c => 82-fixpoint-not-reached.c} | 0 .../46-apron2/{79-reachable_bodies.c => 83-reachable_bodies.c} | 0 4 files changed, 4 insertions(+), 1 deletion(-) rename tests/regression/46-apron2/{80-fixpoint-not-reached.c => 82-fixpoint-not-reached.c} (100%) rename tests/regression/46-apron2/{79-reachable_bodies.c => 83-reachable_bodies.c} (100%) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 208c78bbf6..43e37c346c 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1105,7 +1105,7 @@ struct | Bot -> Queries.ID.top () (* out-of-scope variables cause bot, but query result should then be unknown *) | Top -> Queries.ID.top () (* some float computations cause top (57-float/01-base), but query result should then be unknown *) | v -> M.debug ~category:Analyzer "Base EvalInt %a query answering bot instead of %a" d_exp e VD.pretty v; Queries.ID.bot () - | exception (IntDomain.ArithmeticOnIntegerBot _) -> Queries.ID.top () + | exception (IntDomain.ArithmeticOnIntegerBot _) when not !AnalysisState.should_warn -> Queries.ID.bot () in if M.tracing then M.traceu "evalint" "base query_evalint %a -> %a\n" d_exp e Queries.ID.pretty r; r diff --git a/tests/regression/46-apron2/81-overflow-caching.c b/tests/regression/46-apron2/81-overflow-caching.c index d96aabed7a..720e63877c 100644 --- a/tests/regression/46-apron2/81-overflow-caching.c +++ b/tests/regression/46-apron2/81-overflow-caching.c @@ -10,5 +10,8 @@ int main() { while(num > 0) num++; // Here num overflows + + __goblint_check(1); // reachable + return 0; } diff --git a/tests/regression/46-apron2/80-fixpoint-not-reached.c b/tests/regression/46-apron2/82-fixpoint-not-reached.c similarity index 100% rename from tests/regression/46-apron2/80-fixpoint-not-reached.c rename to tests/regression/46-apron2/82-fixpoint-not-reached.c diff --git a/tests/regression/46-apron2/79-reachable_bodies.c b/tests/regression/46-apron2/83-reachable_bodies.c similarity index 100% rename from tests/regression/46-apron2/79-reachable_bodies.c rename to tests/regression/46-apron2/83-reachable_bodies.c From d290f19603d2384a3cf8e0b4fba4053b54adb826 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 19 Mar 2024 15:44:45 +0100 Subject: [PATCH 247/280] adapted linearTwoVariableEqualityDomain to new top handling --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 0e47f3a102..1e65354f37 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -285,7 +285,7 @@ struct let top () = {d = Some (EArray.empty()); env = empty_env} (** is_top returns true for top_of array and empty array; precondition: t.env and t.d are of same size *) - let is_top t = GobOption.exists EArray.is_top_array t.d + let is_top t = Environment.equal empty_env t.env && GobOption.exists EArray.is_top_array t.d (** prints the current variable equalities with resolved variable names *) let show varM = From 3927adc158b96b660516f9d4456bf91b906b6f98 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 20 Mar 2024 10:29:10 +0200 Subject: [PATCH 248/280] Bump to ppx_deriving_hash 0.1.2 --- dune-project | 2 +- goblint.opam | 2 +- goblint.opam.locked | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dune-project b/dune-project index d6ae4be7e7..5cc55d08fe 100644 --- a/dune-project +++ b/dune-project @@ -30,7 +30,7 @@ (yojson (>= 2.0.0)) (qcheck-core (>= 0.19)) ppx_deriving - ppx_deriving_hash + (ppx_deriving_hash (>= 0.1.2)) (ppx_deriving_yojson (>= 3.7.0)) (ounit2 :with-test) (qcheck-ounit :with-test) diff --git a/goblint.opam b/goblint.opam index d7724aad34..3d58bd6f52 100644 --- a/goblint.opam +++ b/goblint.opam @@ -28,7 +28,7 @@ depends: [ "yojson" {>= "2.0.0"} "qcheck-core" {>= "0.19"} "ppx_deriving" - "ppx_deriving_hash" + "ppx_deriving_hash" {>= "0.1.2"} "ppx_deriving_yojson" {>= "3.7.0"} "ounit2" {with-test} "qcheck-ounit" {with-test} diff --git a/goblint.opam.locked b/goblint.opam.locked index 2bb831d6ba..8c9e914575 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -82,7 +82,7 @@ depends: [ "pp" {= "1.1.2"} "ppx_derivers" {= "1.2.1"} "ppx_deriving" {= "5.2.1"} - "ppx_deriving_hash" {= "0.1.1"} + "ppx_deriving_hash" {= "0.1.2"} "ppx_deriving_yojson" {= "3.7.0"} "ppxlib" {= "0.28.0"} "qcheck-core" {= "0.20"} From dcaa97165585cf55f11d337dd650bce2476b2250 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 7 Mar 2024 14:06:36 +0200 Subject: [PATCH 249/280] Use ppx_deriving_hash for float, ref and array --- src/cdomains/vectorMatrix.ml | 3 +-- src/common/util/cilType.ml | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/cdomains/vectorMatrix.ml b/src/cdomains/vectorMatrix.ml index 64d5c5e35d..6eee7ded50 100644 --- a/src/cdomains/vectorMatrix.ml +++ b/src/cdomains/vectorMatrix.ml @@ -209,8 +209,7 @@ module ArrayVector: AbstractVector = struct include ConvenienceOps (A) include Array - type t = A.t array [@@deriving eq, ord] - let hash = Array.fold_left (fun acc a -> 31 * acc + A.hash a) 0 (* TODO: array in ppx_deriving_hash *) + type t = A.t array [@@deriving eq, ord, hash] let show t = let t = Array.to_list t in diff --git a/src/common/util/cilType.ml b/src/common/util/cilType.ml index fe4f257da1..a484a228bd 100644 --- a/src/common/util/cilType.ml +++ b/src/common/util/cilType.ml @@ -13,9 +13,6 @@ struct include Printable.StdLeaf end -let hash_float = Hashtbl.hash (* TODO: float hash in ppx_deriving_hash *) -let hash_ref hash r = hash !r (* TODO: ref in ppx_deriving_hash *) - module Location: sig From cb9061c62e3288a29e89a3eb4ab5a87c7e91def6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 7 Mar 2024 14:10:28 +0200 Subject: [PATCH 250/280] Use ppx_deriving_hash for inline record --- src/domains/queries.ml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index f5fc832a9e..72f9ef31ab 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -52,11 +52,10 @@ type maybepublic = {global: CilType.Varinfo.t; write: bool; protection: Protecti type maybepublicwithout = {global: CilType.Varinfo.t; write: bool; without_mutex: PreValueDomain.Addr.t; protection: Protection.t} [@@deriving ord, hash] type mustbeprotectedby = {mutex: PreValueDomain.Addr.t; global: CilType.Varinfo.t; write: bool; protection: Protection.t} [@@deriving ord, hash] type mustprotectedvars = {mutex: PreValueDomain.Addr.t; write: bool} [@@deriving ord, hash] -type memory_access = {exp: CilType.Exp.t; var_opt: CilType.Varinfo.t option; kind: AccessKind.t} [@@deriving ord, hash] type access = - | Memory of memory_access (** Memory location access (race). *) + | Memory of {exp: CilType.Exp.t; var_opt: CilType.Varinfo.t option; kind: AccessKind.t} (** Memory location access (race). *) | Point (** Program point and state access (MHP), independent of memory location. *) -[@@deriving ord, hash] (* TODO: fix ppx_deriving_hash on variant with inline record *) +[@@deriving ord, hash] type invariant_context = Invariant.context = { path: int option; lvals: Lval.Set.t; From 32ab56eed3a68f06c7ac34a451654680dda1ce11 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 20 Mar 2024 10:51:35 +0200 Subject: [PATCH 251/280] Use ppx_deriving_hash for more float and array --- src/cdomains/vectorMatrix.ml | 4 +--- src/common/cdomains/floatOps/floatOps.ml | 6 ++---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/cdomains/vectorMatrix.ml b/src/cdomains/vectorMatrix.ml index 6eee7ded50..114119a783 100644 --- a/src/cdomains/vectorMatrix.ml +++ b/src/cdomains/vectorMatrix.ml @@ -287,9 +287,7 @@ module ArrayMatrix: AbstractMatrix = include ConvenienceOps(A) module V = V(A) - type t = A.t array array [@@deriving eq, ord] - - let hash = Array.fold_left (Array.fold_left (fun acc a -> 31 * acc + A.hash a)) 0 + type t = A.t array array [@@deriving eq, ord, hash] let show x = Array.fold_left (^) "" (Array.map (fun v -> V.show @@ V.of_array v) x) diff --git a/src/common/cdomains/floatOps/floatOps.ml b/src/common/cdomains/floatOps/floatOps.ml index a791778857..6649091cec 100644 --- a/src/common/cdomains/floatOps/floatOps.ml +++ b/src/common/cdomains/floatOps/floatOps.ml @@ -49,7 +49,7 @@ let big_int_of_float f = (Z.pow (Z.of_int 2) (n - shift)) module CDouble = struct - type t = float [@@deriving eq, ord, to_yojson] + type t = float [@@deriving eq, ord, hash, to_yojson] let name = "double" let zero = Float.zero @@ -65,7 +65,6 @@ module CDouble = struct let pred = Float.pred let succ = Float.succ - let hash = Hashtbl.hash let to_string = Float.to_string let neg = Float.neg @@ -82,7 +81,7 @@ module CDouble = struct end module CFloat = struct - type t = float [@@deriving eq, ord, to_yojson] + type t = float [@@deriving eq, ord, hash, to_yojson] let name = "float" let zero = Float.zero @@ -99,7 +98,6 @@ module CFloat = struct let is_finite x = Float.is_finite x && x >= lower_bound && x <= upper_bound - let hash = Hashtbl.hash let to_string = Float.to_string let neg = Float.neg From 3e42bbb4e4d6dd1d65b87c2429a34a8e6dfd4687 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 23 Mar 2024 13:30:23 +0100 Subject: [PATCH 252/280] 77: Explain why tests that pass with base only are helpful --- tests/regression/77-lin2vareq/07-coeff_vec.c | 5 +++-- .../regression/77-lin2vareq/17-svcomp-signextension.c | 2 ++ tests/regression/77-lin2vareq/19-cast-to-short.c | 2 ++ tests/regression/77-lin2vareq/22-cast-to-short2.c | 10 ++++++---- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/tests/regression/77-lin2vareq/07-coeff_vec.c b/tests/regression/77-lin2vareq/07-coeff_vec.c index 1cc351c5c6..86e6aa121c 100644 --- a/tests/regression/77-lin2vareq/07-coeff_vec.c +++ b/tests/regression/77-lin2vareq/07-coeff_vec.c @@ -1,5 +1,6 @@ //SKIP PARAM: --set ana.activated[+] lin2vareq - +// This was problematic earlier where both branches were dead with lin2vareq +// Thus worth having even if it can be answered by base alone int main() { unsigned int a = 1; @@ -18,4 +19,4 @@ int main() { c = c + 2; __goblint_check(c == -32767); -} \ No newline at end of file +} diff --git a/tests/regression/77-lin2vareq/17-svcomp-signextension.c b/tests/regression/77-lin2vareq/17-svcomp-signextension.c index 1d63e1bfb0..cf2f0bdabc 100644 --- a/tests/regression/77-lin2vareq/17-svcomp-signextension.c +++ b/tests/regression/77-lin2vareq/17-svcomp-signextension.c @@ -1,4 +1,6 @@ // SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +// This was problematic earlier where we were unsound with lin2vareq +// Thus worth having even if it can be answered by base alone #include int main() { diff --git a/tests/regression/77-lin2vareq/19-cast-to-short.c b/tests/regression/77-lin2vareq/19-cast-to-short.c index fe4e4933bf..d1e3f857d7 100644 --- a/tests/regression/77-lin2vareq/19-cast-to-short.c +++ b/tests/regression/77-lin2vareq/19-cast-to-short.c @@ -1,4 +1,6 @@ // SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +// This was problematic earlier where we were unsound with lin2vareq +// Thus worth having even if it can be answered by base alone #include int main() { diff --git a/tests/regression/77-lin2vareq/22-cast-to-short2.c b/tests/regression/77-lin2vareq/22-cast-to-short2.c index c5cc059298..125b07bafb 100644 --- a/tests/regression/77-lin2vareq/22-cast-to-short2.c +++ b/tests/regression/77-lin2vareq/22-cast-to-short2.c @@ -1,4 +1,6 @@ // SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +// This was problematic earlier where both branches were dead with lin2vareq +// Thus worth having even if it can be answered by base alone #include int main() { @@ -14,14 +16,14 @@ int main() { printf("unsignedtounsigned: %hu\n", unsignedtounsigned); if (unsignedtounsigned == 4294967295) { -// __goblint_check(0); // NOWARN (unreachable) + __goblint_check(0); // NOWARN (unreachable) return (-1); } if (allbits == 4294967295 && signedallbits == -1 && unsignedtosigned == -1 && - unsignedtounsigned == 65535) { - // __goblint_check(1); // reachable + unsignedtounsigned == 65535) { + __goblint_check(1); // reachable return (-1); } - // __goblint_check(0); // NOWARN (unreachable) + return (0); } From 90334ae0fb5ec4ed1248ecb33e2f9d2afbeb4d6d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 23 Mar 2024 13:35:53 +0100 Subject: [PATCH 253/280] Add TODO for comment left over from review --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 1e65354f37..e9e24f1b7a 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -544,6 +544,8 @@ struct let assign_var_parallel t vv's = timing_wrap "var_parallel" (assign_var_parallel t) vv's let assign_var_parallel_with t vv's = + (* TODO: If we are angling for more performance, this might be a good place ot try. `assign_var_parallel_with` is used whenever a function is entered (body), + in unlock, at sync edges, and when entering multi-threaded mode. *) let t' = assign_var_parallel t vv's in t.d <- t'.d; t.env <- t'.env From a9b0b44ed6b8633d50c28915c6032bf3594499df Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 23 Mar 2024 13:41:48 +0100 Subject: [PATCH 254/280] Remove resolved TODO --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index e9e24f1b7a..b4bf5a7616 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -563,8 +563,6 @@ struct if M.tracing then M.tracel "ops" "assign_var parallel'\n"; res - (* This is supposed to the be the backwards transformer for assign, not sure this is correct *) - (* TODO: https://github.com/goblint/analyzer/pull/1297#discussion_r1484783039 *) let substitute_exp ask t var exp no_ov = let t = if not @@ Environment.mem_var t.env var then add_vars t [var] else t in let res = assign_exp ask t var exp no_ov in From 76aad67571aa882617f137178217a2837a80d6f1 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 23 Mar 2024 13:48:14 +0100 Subject: [PATCH 255/280] Make tracing output consistent --- src/cdomains/apron/affineEqualityDomain.apron.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index e3b525d73a..f6d5f6ece2 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -558,12 +558,12 @@ struct res let assert_constraint ask d e negate no_ov = - if M.tracing then M.tracel "assert_cons" "assert_cons with expr: %a %b" d_exp e (Lazy.force no_ov); + if M.tracing then M.tracel "assert_constraint" "assert_constraint with expr: %a %b" d_exp e (Lazy.force no_ov); match Convert.tcons1_of_cil_exp ask d d.env e negate no_ov with | tcons1 -> meet_tcons ask d tcons1 e | exception Convert.Unsupported_CilExp _ -> d - let assert_constraint ask d e negate no_ov = timing_wrap "assert_cons" (assert_constraint ask d e negate) no_ov + let assert_constraint ask d e negate no_ov = timing_wrap "assert_constraint" (assert_constraint ask d e negate) no_ov let relift t = t From 044d5b23f620fc86bac1b04412826e6c1d24ed71 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 23 Mar 2024 14:08:01 +0100 Subject: [PATCH 256/280] Replace `Lattice.Unit` with `Printable.Unit` for context --- src/analyses/abortUnless.ml | 2 +- src/analyses/accessAnalysis.ml | 2 +- src/analyses/activeLongjmp.ml | 2 +- src/analyses/modifiedSinceSetjmp.ml | 2 +- src/analyses/mutexTypeAnalysis.ml | 4 ++-- src/analyses/poisonVariables.ml | 2 +- src/analyses/taintPartialContexts.ml | 2 +- src/analyses/tmpSpecial.ml | 2 +- src/analyses/tutorials/constants.ml | 2 +- src/analyses/tutorials/taint.ml | 2 +- src/analyses/tutorials/unitAnalysis.ml | 4 ++-- src/analyses/useAfterFree.ml | 2 +- src/analyses/vla.ml | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/analyses/abortUnless.ml b/src/analyses/abortUnless.ml index ee4db69820..88937d027a 100644 --- a/src/analyses/abortUnless.ml +++ b/src/analyses/abortUnless.ml @@ -11,7 +11,7 @@ struct let name () = "abortUnless" module D = BoolDomain.MustBool - module C = Lattice.Unit + module C = Printable.Unit let context _ _ = () diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index efad8b4c2e..5ed926fd7e 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -14,7 +14,7 @@ struct let name () = "access" module D = Lattice.Unit - module C = Lattice.Unit + module C = Printable.Unit module V = struct diff --git a/src/analyses/activeLongjmp.ml b/src/analyses/activeLongjmp.ml index 9baa601ddc..027aef426a 100644 --- a/src/analyses/activeLongjmp.ml +++ b/src/analyses/activeLongjmp.ml @@ -11,7 +11,7 @@ struct (* The first component are the longjmp targets, the second are the longjmp callers *) module D = JmpBufDomain.ActiveLongjmps - module C = Lattice.Unit + module C = Printable.Unit let context _ _ = () diff --git a/src/analyses/modifiedSinceSetjmp.ml b/src/analyses/modifiedSinceSetjmp.ml index 93e55b2a17..f5bc8e9422 100644 --- a/src/analyses/modifiedSinceSetjmp.ml +++ b/src/analyses/modifiedSinceSetjmp.ml @@ -10,7 +10,7 @@ struct let name () = "modifiedSinceSetjmp" module D = JmpBufDomain.LocallyModifiedMap module VS = D.VarSet - module C = Lattice.Unit + module C = Printable.Unit let context _ _ = () diff --git a/src/analyses/mutexTypeAnalysis.ml b/src/analyses/mutexTypeAnalysis.ml index e640a261cd..3c3d855b4e 100644 --- a/src/analyses/mutexTypeAnalysis.ml +++ b/src/analyses/mutexTypeAnalysis.ml @@ -6,13 +6,13 @@ open Analyses module MAttr = ValueDomain.MutexAttr module LF = LibraryFunctions -module Spec : Analyses.MCPSpec with module D = Lattice.Unit and module C = Lattice.Unit = +module Spec : Analyses.MCPSpec with module D = Lattice.Unit and module C = Printable.Unit = struct include Analyses.IdentitySpec let name () = "pthreadMutexType" module D = Lattice.Unit - module C = Lattice.Unit + module C = Printable.Unit (* Removing indexes here avoids complicated lookups and allows to have the LVals as vars here, at the price that different types of mutexes in arrays are not dinstinguished *) module O = Offset.Unit diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index 7100534fab..67c1ed21d2 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -11,7 +11,7 @@ struct let name () = "poisonVariables" module D = VS - module C = Lattice.Unit + module C = Printable.Unit let context _ _ = () diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index 85dabd1c9d..88d4e42344 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -14,7 +14,7 @@ struct let name () = "taintPartialContexts" module D = AD - module C = Lattice.Unit + module C = Printable.Unit (* Add Lval or any Lval which it may point to to the set *) let taint_lval ctx (lval:lval) : D.t = diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index 9ed6da7c60..1afe90f125 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -16,7 +16,7 @@ struct module Deps = SetDomain.Reverse (SetDomain.ToppedSet (CilType.Exp) (struct let topname = "All" end)) module MLDeps = Lattice.Prod (ML) (Deps) module D = MapDomain.MapBot (Mval.Exp) (MLDeps) - module C = Lattice.Unit + module C = Printable.Unit let invalidate ask exp_w st = D.filter (fun _ (ml, deps) -> (Deps.for_all (fun arg -> not (VarEq.may_change ask exp_w arg)) deps)) st diff --git a/src/analyses/tutorials/constants.ml b/src/analyses/tutorials/constants.ml index e1d341e993..5136f65d40 100644 --- a/src/analyses/tutorials/constants.ml +++ b/src/analyses/tutorials/constants.ml @@ -15,7 +15,7 @@ struct (* Map of (local int) variables to flat integers *) module D = MapDomain.MapBot (Basetype.Variables) (I) (* No contexts*) - module C = Lattice.Unit + module C = Printable.Unit include Analyses.IdentitySpec let context _ _ = () diff --git a/src/analyses/tutorials/taint.ml b/src/analyses/tutorials/taint.ml index a978d0faf4..1015188d88 100644 --- a/src/analyses/tutorials/taint.ml +++ b/src/analyses/tutorials/taint.ml @@ -25,7 +25,7 @@ struct let name () = "taint" module D = Lattice.Unit (* TODO: Change such that you have a fitting local domain *) - module C = Lattice.Unit + module C = Printable.Unit (* We are context insensitive in this analysis *) let context _ _ = () diff --git a/src/analyses/tutorials/unitAnalysis.ml b/src/analyses/tutorials/unitAnalysis.ml index dc377cdd97..319d0039ba 100644 --- a/src/analyses/tutorials/unitAnalysis.ml +++ b/src/analyses/tutorials/unitAnalysis.ml @@ -3,7 +3,7 @@ open GoblintCil open Analyses -(* module Spec : Analyses.MCPSpec with module D = Lattice.Unit and module C = Lattice.Unit and type marshal = unit = *) +(* module Spec : Analyses.MCPSpec with module D = Lattice.Unit and module C = Printable.Unit and type marshal = unit = *) (* No signature so others can override module G *) module Spec = struct @@ -11,7 +11,7 @@ struct let name () = "unit" module D = Lattice.Unit - module C = Lattice.Unit + module C = Printable.Unit (* transfer functions *) let assign ctx (lval:lval) (rval:exp) : D.t = diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 69db6b4bfa..5e60121c72 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -20,7 +20,7 @@ struct let name () = "useAfterFree" module D = StackAndHeapVars - module C = Lattice.Unit + module C = Printable.Unit module G = ThreadIdToJoinedThreadsMap module V = VarinfoV diff --git a/src/analyses/vla.ml b/src/analyses/vla.ml index 665612aa99..832dec3d4c 100644 --- a/src/analyses/vla.ml +++ b/src/analyses/vla.ml @@ -9,7 +9,7 @@ struct let name () = "vla" module D = BoolDomain.MayBool - module C = Lattice.Unit + module C = Printable.Unit let context _ _ = () From 82eb105c4ec0ec77949cd7a909d7a3733d7da2ff Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 23 Mar 2024 14:22:37 +0100 Subject: [PATCH 257/280] Introduce `Analyses.IdentityUnitContextsSpec` --- src/analyses/activeLongjmp.ml | 5 +---- src/analyses/modifiedSinceSetjmp.ml | 5 +---- src/analyses/mutexTypeAnalysis.ml | 3 +-- src/analyses/poisonVariables.ml | 5 +---- src/analyses/taintPartialContexts.ml | 6 +----- src/analyses/tmpSpecial.ml | 5 +---- src/analyses/tutorials/constants.ml | 6 ++---- src/analyses/unassumeAnalysis.ml | 7 ++----- src/analyses/useAfterFree.ml | 6 +----- src/analyses/vla.ml | 5 +---- src/framework/analyses.ml | 6 ++++++ 11 files changed, 18 insertions(+), 41 deletions(-) diff --git a/src/analyses/activeLongjmp.ml b/src/analyses/activeLongjmp.ml index 027aef426a..47e2432662 100644 --- a/src/analyses/activeLongjmp.ml +++ b/src/analyses/activeLongjmp.ml @@ -5,15 +5,12 @@ open Analyses module Spec = struct - include Analyses.IdentitySpec + include Analyses.IdentityUnitContextsSpec let name () = "activeLongjmp" (* The first component are the longjmp targets, the second are the longjmp callers *) module D = JmpBufDomain.ActiveLongjmps - module C = Printable.Unit - - let context _ _ = () let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = let desc = LibraryFunctions.find f in diff --git a/src/analyses/modifiedSinceSetjmp.ml b/src/analyses/modifiedSinceSetjmp.ml index f5bc8e9422..515f63cdb4 100644 --- a/src/analyses/modifiedSinceSetjmp.ml +++ b/src/analyses/modifiedSinceSetjmp.ml @@ -5,14 +5,11 @@ open Analyses module Spec = struct - include Analyses.IdentitySpec + include Analyses.IdentityUnitContextsSpec let name () = "modifiedSinceSetjmp" module D = JmpBufDomain.LocallyModifiedMap module VS = D.VarSet - module C = Printable.Unit - - let context _ _ = () let add_to_all_defined vs d = D.map (fun vs' -> VS.union vs vs') d diff --git a/src/analyses/mutexTypeAnalysis.ml b/src/analyses/mutexTypeAnalysis.ml index 3c3d855b4e..a5849f3485 100644 --- a/src/analyses/mutexTypeAnalysis.ml +++ b/src/analyses/mutexTypeAnalysis.ml @@ -8,11 +8,10 @@ module LF = LibraryFunctions module Spec : Analyses.MCPSpec with module D = Lattice.Unit and module C = Printable.Unit = struct - include Analyses.IdentitySpec + include Analyses.IdentityUnitContextsSpec let name () = "pthreadMutexType" module D = Lattice.Unit - module C = Printable.Unit (* Removing indexes here avoids complicated lookups and allows to have the LVals as vars here, at the price that different types of mutexes in arrays are not dinstinguished *) module O = Offset.Unit diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index 67c1ed21d2..e8a4a9a404 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -6,14 +6,11 @@ open Analyses module Spec = struct - include Analyses.IdentitySpec + include Analyses.IdentityUnitContextsSpec module VS = SetDomain.ToppedSet(CilType.Varinfo) (struct let topname = "All vars" end) let name () = "poisonVariables" module D = VS - module C = Printable.Unit - - let context _ _ = () let check_mval tainted (addr: Queries.AD.elt) = match addr with diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index 88d4e42344..884950a408 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -10,19 +10,15 @@ module AD = ValueDomain.AD module Spec = struct - include Analyses.IdentitySpec + include Analyses.IdentityUnitContextsSpec let name () = "taintPartialContexts" module D = AD - module C = Printable.Unit (* Add Lval or any Lval which it may point to to the set *) let taint_lval ctx (lval:lval) : D.t = D.union (ctx.ask (Queries.MayPointTo (AddrOf lval))) ctx.local - (* this analysis is context insensitive*) - let context _ _ = () - (* transfer functions *) let assign ctx (lval:lval) (rval:exp) : D.t = taint_lval ctx lval diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index 1afe90f125..75fe149868 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -9,20 +9,17 @@ open Analyses module Spec = struct - include Analyses.IdentitySpec + include Analyses.IdentityUnitContextsSpec let name () = "tmpSpecial" module ML = LibraryDesc.MathLifted module Deps = SetDomain.Reverse (SetDomain.ToppedSet (CilType.Exp) (struct let topname = "All" end)) module MLDeps = Lattice.Prod (ML) (Deps) module D = MapDomain.MapBot (Mval.Exp) (MLDeps) - module C = Printable.Unit let invalidate ask exp_w st = D.filter (fun _ (ml, deps) -> (Deps.for_all (fun arg -> not (VarEq.may_change ask exp_w arg)) deps)) st - let context _ _ = () - (* transfer functions *) let assign ctx (lval:lval) (rval:exp) : D.t = if M.tracing then M.tracel "tmpSpecial" "assignment of %a\n" d_lval lval; diff --git a/src/analyses/tutorials/constants.ml b/src/analyses/tutorials/constants.ml index 5136f65d40..0c7d801df7 100644 --- a/src/analyses/tutorials/constants.ml +++ b/src/analyses/tutorials/constants.ml @@ -14,11 +14,9 @@ struct (* Map of (local int) variables to flat integers *) module D = MapDomain.MapBot (Basetype.Variables) (I) - (* No contexts*) - module C = Printable.Unit - include Analyses.IdentitySpec - let context _ _ = () + (* No contexts *) + include Analyses.IdentityUnitContextsSpec let is_integer_var (v: varinfo) = match v.vtype with diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 5895f242c9..88a2c5a5b1 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -12,19 +12,16 @@ module EH = Hashtbl.Make (CilType.Exp) module Spec = struct - include Analyses.IdentitySpec + (* TODO: Should be context-sensitive? Some spurious widening in knot_comb fails self-validation after self-unassume. *) + include Analyses.IdentityUnitContextsSpec let name () = "unassume" - (* TODO: Should be context-sensitive? Some spurious widening in knot_comb fails self-validation after self-unassume. *) - module C = Printable.Unit module D = SetDomain.Make (CilType.Exp) let startstate _ = D.empty () let morphstate _ _ = D.empty () let exitstate _ = D.empty () - let context _ _ = () - module Locator = WitnessUtil.Locator (Node) let locator: Locator.t = Locator.create () (* empty default, so don't have to use option everywhere *) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 5e60121c72..d5370ec988 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -15,18 +15,14 @@ module ThreadIdToJoinedThreadsMap = MapDomain.MapBot(ThreadIdDomain.ThreadLifted module Spec : Analyses.MCPSpec = struct - include Analyses.IdentitySpec + include Analyses.IdentityUnitContextsSpec let name () = "useAfterFree" module D = StackAndHeapVars - module C = Printable.Unit module G = ThreadIdToJoinedThreadsMap module V = VarinfoV - let context _ _ = () - - (* HELPER FUNCTIONS *) let get_current_threadid ctx = diff --git a/src/analyses/vla.ml b/src/analyses/vla.ml index 832dec3d4c..aca4fdead8 100644 --- a/src/analyses/vla.ml +++ b/src/analyses/vla.ml @@ -5,13 +5,10 @@ open Analyses module Spec = struct - include Analyses.IdentitySpec + include Analyses.IdentityUnitContextsSpec let name () = "vla" module D = BoolDomain.MayBool - module C = Printable.Unit - - let context _ _ = () let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = [ctx.local, false] diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 61f43cb1a5..d2b6f20445 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -416,6 +416,12 @@ struct let threadspawn ctx ~multiple lval f args fctx = ctx.local end +module IdentityUnitContextsSpec = struct + include IdentitySpec + module C = Printable.Unit + + let context _ _ = () +end module type SpecSys = sig From 2d08c2953b35741a57be7790da40e1559da951b9 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 23 Mar 2024 20:13:29 +0100 Subject: [PATCH 258/280] Typo --- tests/regression/02-base/01-thread_creation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/02-base/01-thread_creation.c b/tests/regression/02-base/01-thread_creation.c index 390b38ae13..ac7e9fc32e 100644 --- a/tests/regression/02-base/01-thread_creation.c +++ b/tests/regression/02-base/01-thread_creation.c @@ -21,7 +21,7 @@ int main() { glob1 = 7; __goblint_check(glob1 == 7); - // Creat the thread + // Create the thread pthread_create(&id, NULL, t_fun, NULL); // The values should remain the same From 9f16774981f7be4c232ef3d17d33493e7f673998 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 23 Mar 2024 20:14:33 +0100 Subject: [PATCH 259/280] ProtectionBasedTIDPriv --- src/analyses/basePriv.ml | 154 +++++++++++++++++++++++++++++++++++++ src/analyses/commonPriv.ml | 20 +++++ 2 files changed, 174 insertions(+) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 125429231e..02f2649331 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -787,6 +787,159 @@ struct let invariant_vars ask getg st = protected_vars ask end +module ProtectionBasedTIDPriv (Digest: Digest): S = +struct + include NoFinalize + include ConfCheck.RequireMutexActivatedInit + open Protection + + module P = + struct + include MustVars + let name () = "P" + end + (* W is implicitly represented by CPA domain *) + module D = P + + module G = MapDomain.MapBot_LiftTop (Digest) (VD) + module VUnprot = + struct + include VarinfoV (* [g]' *) + let name () = "unprotected" + end + module VProt = + struct + include VarinfoV (* [g] *) + let name () = "protected" + end + module V = + struct + include Printable.Either (VUnprot) (VProt) + let unprotected x = `Left x + let protected x = `Right x + end + + let get_possibles ask x = + G.fold (fun d v acc -> + if not (Digest.accounted_for ask ~current:(Digest.current ask) ~other:d) then + VD.join v acc + else + acc) x (VD.bot ()) + + let sideg_lift ask sideg x v = + let sidev = G.singleton (Digest.current ask) v in + sideg x sidev + + let startstate () = P.empty () + + let read_global (ask: Queries.ask) getg (st: BaseComponents (D).t) x = + if P.mem x st.priv then + CPA.find x st.cpa + else if is_unprotected ask x then + get_possibles ask @@ getg (V.unprotected x) (* CPA unnecessary because all values in GUnprot anyway *) + else + VD.join (CPA.find x st.cpa) (get_possibles ask (getg (V.protected x))) + + let write_global ?(invariant=false) (ask: Queries.ask) getg sideg (st: BaseComponents (D).t) x v = + let sideg = sideg_lift ask sideg in + if not invariant then ( + sideg (V.unprotected x) v; (* Delay publishing unprotected write in the atomic section. *) + if !earlyglobs then (* earlyglobs workaround for 13/60 *) + sideg (V.protected x) v + (* Unlock after invariant will still side effect refined value (if protected) from CPA, because cannot distinguish from non-invariant write since W is implicit. *) + ); + if is_unprotected ask x then + st + else + {st with cpa = CPA.add x v st.cpa; priv = P.add x st.priv} + + let lock ask getg st m = st + + let unlock ask getg sideg (st: BaseComponents (D).t) m = + let sideg = sideg_lift ask sideg in + (* TODO: what about G_m globals in cpa that weren't actually written? *) + CPA.fold (fun x v (st: BaseComponents (D).t) -> + if is_protected_by ask m x then ( (* is_in_Gm *) + (* Extra precision in implementation to pass tests: + If global is read-protected by multiple locks, + then inner unlock shouldn't yet publish. *) + if is_unprotected_without ask ~write:false x m then + sideg (V.protected x) v; + + if is_unprotected_without ask x m then (* is_in_V' *) + {st with cpa = CPA.remove x st.cpa; priv = P.remove x st.priv} + else + st + ) + else + st + ) st.cpa st + + let sync ask getg sideg (st: BaseComponents (D).t) reason = + let sideg = sideg_lift ask sideg in + match reason with + | `Join -> (* required for branched thread creation *) + CPA.fold (fun x v (st: BaseComponents (D).t) -> + if is_global ask x && is_unprotected ask x then ( + sideg (V.unprotected x) v; + sideg (V.protected x) v; (* must be like enter_multithreaded *) + {st with cpa = CPA.remove x st.cpa; priv = P.remove x st.priv} + ) + else + st + ) st.cpa st + | `Return + | `Normal + | `Init + | `Thread -> + st + + let escape ask getg sideg (st: BaseComponents (D).t) escaped = + let sideg = sideg_lift ask sideg in + let cpa' = CPA.fold (fun x v acc -> + if EscapeDomain.EscapedVars.mem x escaped then ( + sideg (V.unprotected x) v; + sideg (V.protected x) v; + CPA.remove x acc + ) + else + acc + ) st.cpa st.cpa + in + {st with cpa = cpa'} + + let enter_multithreaded ask getg sideg (st: BaseComponents (D).t) = + let sideg = sideg_lift ask sideg in + CPA.fold (fun x v (st: BaseComponents (D).t) -> + if is_global ask x then ( + sideg (V.unprotected x) v; + sideg (V.protected x) v; + {st with cpa = CPA.remove x st.cpa; priv = P.remove x st.priv} + ) + else + st + ) st.cpa st + + let threadenter = startstate_threadenter startstate + let threadspawn ask get set st = st + + let thread_join ?(force=false) ask get e st = st + let thread_return ask get set tid st = st + + let iter_sys_vars getg vq vf = + match vq with + | VarQuery.Global g -> + vf (V.unprotected g); + vf (V.protected g); + | _ -> () + + (* Unsupported *) + let invariant_global getg g = Invariant.none + + let invariant_vars ask getg st = protected_vars ask +end + + module AbstractLockCenteredGBase (WeakRange: Lattice.S) (SyncRange: Lattice.S) = struct open Locksets @@ -1790,6 +1943,7 @@ let priv_module: (module S) Lazy.t = | "mutex-meet" -> (module PerMutexMeetPriv) | "mutex-meet-tid" -> (module PerMutexMeetTIDPriv (ThreadDigest)) | "protection" -> (module ProtectionBasedPriv (struct let check_read_unprotected = false let handle_atomic = false end)) + | "protection-tid" -> (module ProtectionBasedTIDPriv (ThreadNotStartedDigest)) | "protection-atomic" -> (module ProtectionBasedPriv (struct let check_read_unprotected = false let handle_atomic = true end)) (* experimental *) | "protection-read" -> (module ProtectionBasedPriv (struct let check_read_unprotected = true let handle_atomic = false end)) | "protection-read-atomic" -> (module ProtectionBasedPriv (struct let check_read_unprotected = true let handle_atomic = true end)) (* experimental *) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 627b39166e..83bafb2f63 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -197,6 +197,26 @@ struct | _ -> false end +module ThreadNotStartedDigest:Digest = +struct + include ThreadIdDomain.ThreadLifted + + module TID = ThreadIdDomain.Thread + + let current (ask: Q.ask) = + ThreadId.get_current ask + + let accounted_for (ask: Q.ask) ~(current: t) ~(other: t) = + match current, other with + | `Lifted current, `Lifted other -> + if TID.is_unique current && TID.equal current other then + (* workaround `definitely_not_started` answers false when passing the same argument twice, TID.is_must_parent [main] [main] says true *) + false + else + MHP.definitely_not_started (current, ask.f Q.CreatedThreads) other + | _ -> false +end + module PerMutexTidCommon (Digest: Digest) (LD:Lattice.S) = struct include ConfCheck.RequireThreadFlagPathSensInit From 0dabb9d8bb9f014d9b90d36c9828852e286db4e2 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 25 Mar 2024 09:57:03 +0100 Subject: [PATCH 260/280] Add protection-tid to available privatizations --- src/config/options.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 8e4ca51fee..01740c1ee6 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -754,7 +754,7 @@ "description": "Which privatization to use? none/mutex-oplus/mutex-meet/mutex-meet-tid/protection/protection-read/mine/mine-nothread/mine-W/mine-W-noinit/lock/write/write+lock", "type": "string", - "enum": ["none", "mutex-oplus", "mutex-meet", "protection", "protection-atomic", "protection-read", "protection-read-atomic", "mine", "mine-nothread", "mine-W", "mine-W-noinit", "lock", "write", "write+lock","mutex-meet-tid"], + "enum": ["none", "mutex-oplus", "mutex-meet", "protection", "protection-tid", "protection-atomic", "protection-read", "protection-read-atomic", "mine", "mine-nothread", "mine-W", "mine-W-noinit", "lock", "write", "write+lock","mutex-meet-tid"], "default": "protection-read" }, "priv": { From 735c03b6247b9ff91ec0038cc00808cffd50003c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 25 Mar 2024 10:01:08 +0100 Subject: [PATCH 261/280] `MutexTypeAnalysis`: Simplify by including `UnitAnalysis.Spec` --- src/analyses/mutexTypeAnalysis.ml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/analyses/mutexTypeAnalysis.ml b/src/analyses/mutexTypeAnalysis.ml index a5849f3485..dab58726ec 100644 --- a/src/analyses/mutexTypeAnalysis.ml +++ b/src/analyses/mutexTypeAnalysis.ml @@ -8,10 +8,9 @@ module LF = LibraryFunctions module Spec : Analyses.MCPSpec with module D = Lattice.Unit and module C = Printable.Unit = struct - include Analyses.IdentityUnitContextsSpec + include UnitAnalysis.Spec let name () = "pthreadMutexType" - module D = Lattice.Unit (* Removing indexes here avoids complicated lookups and allows to have the LVals as vars here, at the price that different types of mutexes in arrays are not dinstinguished *) module O = Offset.Unit @@ -63,11 +62,6 @@ struct ctx.local | _ -> ctx.local - let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = [D.top ()] - let threadspawn ctx ~multiple lval f args fctx = ctx.local - let exitstate v = D.top () - let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | Queries.MutexType (v,o) -> (ctx.global (v,o):MutexAttrDomain.t) From e267f74c7b22455117e39b68ce19ffa7a71b46d2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 20 Mar 2024 11:37:00 +0200 Subject: [PATCH 262/280] Use deriving where possible --- src/cdomains/pthreadDomain.ml | 23 ++--------------------- src/common/util/intOps.ml | 20 ++++---------------- src/domain/mapDomain.ml | 3 +-- src/incremental/compareCIL.ml | 3 +-- src/util/std/gobFpath.ml | 4 +--- src/witness/argTools.ml | 14 +------------- 6 files changed, 10 insertions(+), 57 deletions(-) diff --git a/src/cdomains/pthreadDomain.ml b/src/cdomains/pthreadDomain.ml index 8cef57bdbd..2c12689dcf 100644 --- a/src/cdomains/pthreadDomain.ml +++ b/src/cdomains/pthreadDomain.ml @@ -25,8 +25,7 @@ end module D = struct include Printable.StdLeaf - type domain = { tid : Tid.t; pred : Pred.t; ctx : Ctx.t } [@@deriving to_yojson] - type t = domain + type t = { tid : Tid.t; pred : Pred.t; ctx : Ctx.t } [@@deriving eq, ord, hash, to_yojson] (** printing *) let show x = @@ -36,28 +35,10 @@ module D = struct (Pred.show x.pred) (Ctx.show x.ctx) - include Printable.SimpleShow(struct type t = domain let show = show end) + include Printable.SimpleShow(struct type nonrec t = t let show = show end) (* TODO: overrides derived to_yojson *) let name () = "pthread state" - (** let equal = Util.equals *) - let equal x y = - Tid.equal x.tid y.tid && Pred.equal x.pred y.pred && Ctx.equal x.ctx y.ctx - - - (** compare all fields with correspoding compare operators *) - let compare x y = - List.fold_left - (fun acc v -> if acc = 0 && v <> 0 then v else acc) - 0 - [ Tid.compare x.tid y.tid - ; Pred.compare x.pred y.pred - ; Ctx.compare x.ctx y.ctx - ] - - - (** let hash = Hashtbl.hash *) - let hash x = Hashtbl.hash (Tid.hash x.tid, Pred.hash x.pred, Ctx.hash x.ctx) let make tid pred ctx = { tid; pred; ctx } let bot () = { tid = Tid.bot (); pred = Pred.bot (); ctx = Ctx.bot () } let is_bot x = Tid.is_bot x.tid && Pred.is_bot x.pred && Ctx.is_bot x.ctx diff --git a/src/common/util/intOps.ml b/src/common/util/intOps.ml index fa9a96f465..5138a3c158 100644 --- a/src/common/util/intOps.ml +++ b/src/common/util/intOps.ml @@ -83,7 +83,7 @@ end * -------------------------------------------------------------- *) module NIntOpsBase : IntOpsBase with type t = int = struct - type t = int [@@deriving hash] + type t = int [@@deriving eq, ord, hash] let name () = "int" let zero = 0 let one = 1 @@ -108,9 +108,6 @@ struct let logxor = (lxor) let lognot = (lnot) - - let compare = compare - let equal = Int.equal let top_range a b = (a = min_int) && (b = max_int) let max = Int.max let min = Int.min @@ -129,7 +126,7 @@ end module Int32OpsBase : IntOpsBase with type t = int32 = struct - type t = int32 [@@deriving hash] + type t = int32 [@@deriving eq, ord, hash] let name () = "int32" let zero = 0l let one = 1l @@ -155,9 +152,6 @@ struct let lognot = Int32.lognot (* Int32 calls bitwise operations 'log' *) - let compare = Int32.compare - let equal = Int32.equal - let top_range a b = (0 = compare a Int32.min_int) && (0 = compare b Int32.max_int) let max = Int32.max @@ -177,7 +171,7 @@ end module Int64OpsBase : IntOpsBase with type t = int64 = struct - type t = int64 [@@deriving hash] + type t = int64 [@@deriving eq, ord, hash] let name () = "int64" let zero = 0L let one = 1L @@ -203,9 +197,6 @@ struct let lognot = Int64.lognot (* Int64 calls bitwise operations 'log' *) - let compare = Int64.compare - let equal = Int64.equal - let top_range a b = (0 = compare a Int64.min_int) && (0 = compare b Int64.max_int) let max = Int64.max @@ -225,7 +216,7 @@ end module BigIntOpsBase : IntOpsBase with type t = Z.t = struct - type t = Z.t + type t = Z.t [@@deriving eq, ord, hash] let name () = "Z" let zero = Z.zero let one = Z.one @@ -241,9 +232,6 @@ struct let rem = Z.rem let gcd = Z.gcd - let compare = Z.compare - let equal = Z.equal - let hash = Z.hash let top_range _ _ = false diff --git a/src/domain/mapDomain.ml b/src/domain/mapDomain.ml index 5f3c324317..a62fcb98e4 100644 --- a/src/domain/mapDomain.ml +++ b/src/domain/mapDomain.ml @@ -110,8 +110,7 @@ struct module Group = struct - type t = D.group - let compare = D.compare_group + type t = D.group [@@deriving ord] end module GroupMap = Map.Make (Group) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index befaee6c58..ea22e02a56 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -29,8 +29,7 @@ let get_varinfo gc = match gc.decls, gc.def with module GlobalColMap = Map.Make( struct - type t = global_col - let compare = compare_global_col + type t = global_col [@@deriving ord] end) let name_of_global g = match g with diff --git a/src/util/std/gobFpath.ml b/src/util/std/gobFpath.ml index cd09657e77..dfe7b3fb9d 100644 --- a/src/util/std/gobFpath.ml +++ b/src/util/std/gobFpath.ml @@ -1,7 +1,5 @@ -type t = Fpath.t [@@deriving show] +type t = Fpath.t [@@deriving eq, ord, show] -let equal = Fpath.equal -let compare = Fpath.compare let hash p = Hashtbl.hash (Fpath.to_string p) let pretty () p = GoblintCil.Pretty.text (Fpath.to_string p) diff --git a/src/witness/argTools.ml b/src/witness/argTools.ml index 2d65911a5f..7ec9dfbfc8 100644 --- a/src/witness/argTools.ml +++ b/src/witness/argTools.ml @@ -62,19 +62,7 @@ struct module Node = struct - type t = MyCFG.node * Spec.C.t * int - - let equal (n1, c1, i1) (n2, c2, i2) = - EQSys.LVar.equal (n1, c1) (n2, c2) && i1 = i2 - - let compare (n1, c1, i1) (n2, c2, i2) = - let r = EQSys.LVar.compare (n1, c1) (n2, c2) in - if r <> 0 then - r - else - Int.compare i1 i2 - - let hash (n, c, i) = 31 * EQSys.LVar.hash (n, c) + i + type t = Node.t * Spec.C.t * int [@@deriving eq, ord, hash] let cfgnode (n, c, i) = n let context_id (n, c, i) = Spec.C.tag c From 49177259b1789b2530652c340f8a7ef3c31b21a2 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 25 Mar 2024 10:50:22 +0100 Subject: [PATCH 263/280] Add test for protection-tid --- .../13-privatized/75-protection-tid.c | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/regression/13-privatized/75-protection-tid.c diff --git a/tests/regression/13-privatized/75-protection-tid.c b/tests/regression/13-privatized/75-protection-tid.c new file mode 100644 index 0000000000..66d4903ccf --- /dev/null +++ b/tests/regression/13-privatized/75-protection-tid.c @@ -0,0 +1,42 @@ +// PARAM: --set ana.base.privatization protection-tid +#include +#include + +int g; +pthread_mutex_t m; + +void* spoiler() { + int x; + pthread_mutex_lock(&m); + x=g; + pthread_mutex_unlock(&m); +} + +void* producer() +{ + pthread_mutex_lock(&m); + g = 8; + pthread_mutex_unlock(&m); + return 0; +} + +int main() +{ + pthread_t tid1; + pthread_t tid2; + + pthread_create(&tid1, 0, spoiler, 0); + + pthread_mutex_lock(&m); + __goblint_check(g == 0); + pthread_mutex_unlock(&m); + + pthread_create(&tid2, 0, producer, 0); + + + pthread_mutex_lock(&m); + __goblint_check(g == 0); //UNKNOWN! + pthread_mutex_unlock(&m); + + return 0; +} From 9a1486b5dc548b27fdb0f734bfa5de9a4b9094bb Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Mon, 25 Mar 2024 10:54:15 +0100 Subject: [PATCH 264/280] this is not at all reachable --- tests/regression/77-lin2vareq/22-cast-to-short2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/77-lin2vareq/22-cast-to-short2.c b/tests/regression/77-lin2vareq/22-cast-to-short2.c index 125b07bafb..d33098712a 100644 --- a/tests/regression/77-lin2vareq/22-cast-to-short2.c +++ b/tests/regression/77-lin2vareq/22-cast-to-short2.c @@ -21,7 +21,7 @@ int main() { } if (allbits == 4294967295 && signedallbits == -1 && unsignedtosigned == -1 && unsignedtounsigned == 65535) { - __goblint_check(1); // reachable + __goblint_check(0); // NOWARN (unreachable) return (-1); } From 8192f085d901bf858def0534f4e05964b7a95ca1 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 25 Mar 2024 10:55:29 +0100 Subject: [PATCH 265/280] Use functor --- src/analyses/basePriv.ml | 229 +++++++++++---------------------------- 1 file changed, 62 insertions(+), 167 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 02f2649331..8ff47fca3e 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -639,21 +639,7 @@ sig end (** Protection-Based Reading. *) -module ProtectionBasedPriv (Param: PerGlobalPrivParam): S = -struct - include NoFinalize - include ConfCheck.RequireMutexActivatedInit - open Protection - - module P = - struct - include MustVars - let name () = "P" - end - (* W is implicitly represented by CPA domain *) - module D = P - - module G = VD +module ProtectionBasedV = struct module VUnprot = struct include VarinfoV (* [g]' *) @@ -670,124 +656,46 @@ struct let unprotected x = `Left x let protected x = `Right x end +end - let startstate () = P.empty () - - let read_global (ask: Queries.ask) getg (st: BaseComponents (D).t) x = - if P.mem x st.priv then - CPA.find x st.cpa - else if Param.handle_atomic && ask.f MustBeAtomic then - VD.join (CPA.find x st.cpa) (getg (V.unprotected x)) (* Account for previous unpublished unprotected writes in current atomic section. *) - else if is_unprotected ask x then - getg (V.unprotected x) (* CPA unnecessary because all values in GUnprot anyway *) - else - VD.join (CPA.find x st.cpa) (getg (V.protected x)) - - let write_global ?(invariant=false) (ask: Queries.ask) getg sideg (st: BaseComponents (D).t) x v = - if not invariant then ( - if not (Param.handle_atomic && ask.f MustBeAtomic) then - sideg (V.unprotected x) v; (* Delay publishing unprotected write in the atomic section. *) - if !earlyglobs then (* earlyglobs workaround for 13/60 *) - sideg (V.protected x) v - (* Unlock after invariant will still side effect refined value (if protected) from CPA, because cannot distinguish from non-invariant write since W is implicit. *) - ); - if Param.handle_atomic && ask.f MustBeAtomic then - {st with cpa = CPA.add x v st.cpa; priv = P.add x st.priv} (* Keep write local as if it were protected by the atomic section. *) - else if is_unprotected ask x then - st - else - {st with cpa = CPA.add x v st.cpa; priv = P.add x st.priv} +(** Protection-Based Reading. *) +module type ProtectionBasedWrapper = +sig + module G: Lattice.S + module V: module type of (ProtectionBasedV.V) - let lock ask getg st m = st + val getg: Q.ask -> (V.t -> G.t) -> V.t -> VD.t + val sideg: Q.ask -> (V.t -> G.t -> unit) -> V.t -> VD.t -> unit +end - let unlock ask getg sideg (st: BaseComponents (D).t) m = - let atomic = Param.handle_atomic && LockDomain.Addr.equal m (LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var) in - (* TODO: what about G_m globals in cpa that weren't actually written? *) - CPA.fold (fun x v (st: BaseComponents (D).t) -> - if is_protected_by ask m x then ( (* is_in_Gm *) - (* Extra precision in implementation to pass tests: - If global is read-protected by multiple locks, - then inner unlock shouldn't yet publish. *) - if not Param.check_read_unprotected || is_unprotected_without ask ~write:false x m then - sideg (V.protected x) v; - if atomic then - sideg (V.unprotected x) v; (* Publish delayed unprotected write as if it were protected by the atomic section. *) - if is_unprotected_without ask x m then (* is_in_V' *) - {st with cpa = CPA.remove x st.cpa; priv = P.remove x st.priv} - else - st - ) - else - st - ) st.cpa st +module NoWrapper: ProtectionBasedWrapper = +struct + module G = VD + module V = ProtectionBasedV.V - let sync ask getg sideg (st: BaseComponents (D).t) reason = - match reason with - | `Join -> (* required for branched thread creation *) - CPA.fold (fun x v (st: BaseComponents (D).t) -> - if is_global ask x && is_unprotected ask x then ( - sideg (V.unprotected x) v; - sideg (V.protected x) v; (* must be like enter_multithreaded *) - {st with cpa = CPA.remove x st.cpa; priv = P.remove x st.priv} - ) - else - st - ) st.cpa st - | `Return - | `Normal - | `Init - | `Thread -> - st + let getg _ getg = getg + let sideg _ sideg = sideg +end - let escape ask getg sideg (st: BaseComponents (D).t) escaped = - let cpa' = CPA.fold (fun x v acc -> - if EscapeDomain.EscapedVars.mem x escaped then ( - sideg (V.unprotected x) v; - sideg (V.protected x) v; - CPA.remove x acc - ) - else - acc - ) st.cpa st.cpa - in - {st with cpa = cpa'} +module DigestWrapper(Digest: Digest): ProtectionBasedWrapper = struct + module G = MapDomain.MapBot_LiftTop (Digest) (VD) + module V = ProtectionBasedV.V - let enter_multithreaded ask getg sideg (st: BaseComponents (D).t) = - CPA.fold (fun x v (st: BaseComponents (D).t) -> - if is_global ask x then ( - sideg (V.unprotected x) v; - sideg (V.protected x) v; - {st with cpa = CPA.remove x st.cpa; priv = P.remove x st.priv} - ) + let getg ask getg x = + let vs = getg x in + G.fold (fun d v acc -> + if not (Digest.accounted_for ask ~current:(Digest.current ask) ~other:d) then + VD.join v acc else - st - ) st.cpa st + acc) vs (VD.bot ()) - let threadenter = startstate_threadenter startstate - let threadspawn ask get set st = st - - let thread_join ?(force=false) ask get e st = st - let thread_return ask get set tid st = st - - let iter_sys_vars getg vq vf = - match vq with - | VarQuery.Global g -> - vf (V.unprotected g); - vf (V.protected g); - | _ -> () - - let invariant_global getg g = - match g with - | `Left g' -> (* unprotected *) - ValueDomain.invariant_global (fun g -> getg (V.unprotected g)) g' - | `Right g -> (* protected *) - Invariant.none - - let invariant_vars ask getg st = protected_vars ask + let sideg ask sideg x v = + let sidev = G.singleton (Digest.current ask) v in + sideg x sidev end -module ProtectionBasedTIDPriv (Digest: Digest): S = +module ProtectionBasedPrivWrapper (Param: PerGlobalPrivParam)(Wrapper:ProtectionBasedWrapper): S = struct include NoFinalize include ConfCheck.RequireMutexActivatedInit @@ -798,57 +706,38 @@ struct include MustVars let name () = "P" end + (* W is implicitly represented by CPA domain *) module D = P - module G = MapDomain.MapBot_LiftTop (Digest) (VD) - module VUnprot = - struct - include VarinfoV (* [g]' *) - let name () = "unprotected" - end - module VProt = - struct - include VarinfoV (* [g] *) - let name () = "protected" - end - module V = - struct - include Printable.Either (VUnprot) (VProt) - let unprotected x = `Left x - let protected x = `Right x - end - - let get_possibles ask x = - G.fold (fun d v acc -> - if not (Digest.accounted_for ask ~current:(Digest.current ask) ~other:d) then - VD.join v acc - else - acc) x (VD.bot ()) - - let sideg_lift ask sideg x v = - let sidev = G.singleton (Digest.current ask) v in - sideg x sidev + module G = Wrapper.G + module V = ProtectionBasedV.V let startstate () = P.empty () let read_global (ask: Queries.ask) getg (st: BaseComponents (D).t) x = + let getg = Wrapper.getg ask getg in if P.mem x st.priv then CPA.find x st.cpa + else if Param.handle_atomic && ask.f MustBeAtomic then + VD.join (CPA.find x st.cpa) (getg (V.unprotected x)) (* Account for previous unpublished unprotected writes in current atomic section. *) else if is_unprotected ask x then - get_possibles ask @@ getg (V.unprotected x) (* CPA unnecessary because all values in GUnprot anyway *) + getg (V.unprotected x) (* CPA unnecessary because all values in GUnprot anyway *) else - VD.join (CPA.find x st.cpa) (get_possibles ask (getg (V.protected x))) + VD.join (CPA.find x st.cpa) (getg (V.protected x)) let write_global ?(invariant=false) (ask: Queries.ask) getg sideg (st: BaseComponents (D).t) x v = - let sideg = sideg_lift ask sideg in + let sideg = Wrapper.sideg ask sideg in if not invariant then ( - sideg (V.unprotected x) v; (* Delay publishing unprotected write in the atomic section. *) + if not (Param.handle_atomic && ask.f MustBeAtomic) then + sideg (V.unprotected x) v; (* Delay publishing unprotected write in the atomic section. *) if !earlyglobs then (* earlyglobs workaround for 13/60 *) sideg (V.protected x) v (* Unlock after invariant will still side effect refined value (if protected) from CPA, because cannot distinguish from non-invariant write since W is implicit. *) ); - if is_unprotected ask x then + if Param.handle_atomic && ask.f MustBeAtomic then + {st with cpa = CPA.add x v st.cpa; priv = P.add x st.priv} (* Keep write local as if it were protected by the atomic section. *) + else if is_unprotected ask x then st else {st with cpa = CPA.add x v st.cpa; priv = P.add x st.priv} @@ -856,15 +745,18 @@ struct let lock ask getg st m = st let unlock ask getg sideg (st: BaseComponents (D).t) m = - let sideg = sideg_lift ask sideg in + let sideg = Wrapper.sideg ask sideg in + let atomic = Param.handle_atomic && LockDomain.Addr.equal m (LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var) in (* TODO: what about G_m globals in cpa that weren't actually written? *) CPA.fold (fun x v (st: BaseComponents (D).t) -> if is_protected_by ask m x then ( (* is_in_Gm *) (* Extra precision in implementation to pass tests: If global is read-protected by multiple locks, then inner unlock shouldn't yet publish. *) - if is_unprotected_without ask ~write:false x m then + if not Param.check_read_unprotected || is_unprotected_without ask ~write:false x m then sideg (V.protected x) v; + if atomic then + sideg (V.unprotected x) v; (* Publish delayed unprotected write as if it were protected by the atomic section. *) if is_unprotected_without ask x m then (* is_in_V' *) {st with cpa = CPA.remove x st.cpa; priv = P.remove x st.priv} @@ -876,7 +768,7 @@ struct ) st.cpa st let sync ask getg sideg (st: BaseComponents (D).t) reason = - let sideg = sideg_lift ask sideg in + let sideg = Wrapper.sideg ask sideg in match reason with | `Join -> (* required for branched thread creation *) CPA.fold (fun x v (st: BaseComponents (D).t) -> @@ -895,7 +787,7 @@ struct st let escape ask getg sideg (st: BaseComponents (D).t) escaped = - let sideg = sideg_lift ask sideg in + let sideg = Wrapper.sideg ask sideg in let cpa' = CPA.fold (fun x v acc -> if EscapeDomain.EscapedVars.mem x escaped then ( sideg (V.unprotected x) v; @@ -909,7 +801,7 @@ struct {st with cpa = cpa'} let enter_multithreaded ask getg sideg (st: BaseComponents (D).t) = - let sideg = sideg_lift ask sideg in + let sideg = Wrapper.sideg ask sideg in CPA.fold (fun x v (st: BaseComponents (D).t) -> if is_global ask x then ( sideg (V.unprotected x) v; @@ -933,13 +825,16 @@ struct vf (V.protected g); | _ -> () - (* Unsupported *) let invariant_global getg g = Invariant.none + (* match g with + | `Left g' -> (* unprotected *) + ValueDomain.invariant_global (fun g -> getg (V.unprotected g)) g' + | `Right g -> (* protected *) + Invariant.none *) let invariant_vars ask getg st = protected_vars ask end - module AbstractLockCenteredGBase (WeakRange: Lattice.S) (SyncRange: Lattice.S) = struct open Locksets @@ -1942,11 +1837,11 @@ let priv_module: (module S) Lazy.t = | "mutex-oplus" -> (module PerMutexOplusPriv) | "mutex-meet" -> (module PerMutexMeetPriv) | "mutex-meet-tid" -> (module PerMutexMeetTIDPriv (ThreadDigest)) - | "protection" -> (module ProtectionBasedPriv (struct let check_read_unprotected = false let handle_atomic = false end)) - | "protection-tid" -> (module ProtectionBasedTIDPriv (ThreadNotStartedDigest)) - | "protection-atomic" -> (module ProtectionBasedPriv (struct let check_read_unprotected = false let handle_atomic = true end)) (* experimental *) - | "protection-read" -> (module ProtectionBasedPriv (struct let check_read_unprotected = true let handle_atomic = false end)) - | "protection-read-atomic" -> (module ProtectionBasedPriv (struct let check_read_unprotected = true let handle_atomic = true end)) (* experimental *) + | "protection" -> (module ProtectionBasedPrivWrapper (struct let check_read_unprotected = false let handle_atomic = false end)(NoWrapper)) + | "protection-tid" -> (module ProtectionBasedPrivWrapper (struct let check_read_unprotected = false let handle_atomic = false end)(DigestWrapper(ThreadNotStartedDigest))) + | "protection-atomic" -> (module ProtectionBasedPrivWrapper (struct let check_read_unprotected = false let handle_atomic = true end)(NoWrapper)) (* experimental *) + | "protection-read" -> (module ProtectionBasedPrivWrapper (struct let check_read_unprotected = true let handle_atomic = false end)(NoWrapper)) + | "protection-read-atomic" -> (module ProtectionBasedPrivWrapper (struct let check_read_unprotected = true let handle_atomic = true end)(NoWrapper)) (* experimental *) | "mine" -> (module MinePriv) | "mine-nothread" -> (module MineNoThreadPriv) | "mine-W" -> (module MineWPriv (struct let side_effect_global_init = true end)) From 352c4043a0b11b8b0e546acb5adeaf26ed174fca Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 25 Mar 2024 11:08:13 +0100 Subject: [PATCH 266/280] Pass `ask` to `invariant_global` --- src/analyses/base.ml | 2 +- src/analyses/basePriv.ml | 25 +++++++++++++------------ src/analyses/basePriv.mli | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 9aca9e2079..7c6b2bf73f 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1243,7 +1243,7 @@ struct (* TODO: account for single-threaded values without earlyglobs. *) match g with | `Left g' -> (* priv *) - Priv.invariant_global (priv_getg ctx.global) g' + Priv.invariant_global (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) g' | `Right _ -> (* thread return *) Invariant.none ) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 8ff47fca3e..1ba1f80a39 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -42,7 +42,7 @@ sig val thread_join: ?force:bool -> Q.ask -> (V.t -> G.t) -> Cil.exp -> BaseComponents (D).t -> BaseComponents (D).t val thread_return: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> ThreadIdDomain.Thread.t -> BaseComponents (D).t -> BaseComponents (D).t - val invariant_global: (V.t -> G.t) -> V.t -> Invariant.t + val invariant_global: Q.ask -> (V.t -> G.t) -> V.t -> Invariant.t val invariant_vars: Q.ask -> (V.t -> G.t) -> BaseComponents (D).t -> varinfo list val init: unit -> unit @@ -131,7 +131,7 @@ struct let thread_join ?(force=false) ask get e st = st let thread_return ask get set tid st = st - let invariant_global getg g = + let invariant_global ask getg g = ValueDomain.invariant_global getg g let invariant_vars ask getg st = [] @@ -211,7 +211,7 @@ struct let thread_join ?(force=false) ask get e st = st let thread_return ask get set tid st = st - let invariant_global getg = function + let invariant_global ask getg = function | `Right g' -> (* global *) ValueDomain.invariant_global (read_unprotected_global getg) g' | _ -> (* mutex *) @@ -621,7 +621,7 @@ struct let get_mutex_inits' = CPA.find x get_mutex_inits in VD.join get_mutex_global_x' get_mutex_inits' - let invariant_global getg = function + let invariant_global ask getg = function | `Middle g -> (* global *) ValueDomain.invariant_global (read_unprotected_global getg) g | `Left _ @@ -825,12 +825,13 @@ struct vf (V.protected g); | _ -> () - let invariant_global getg g = Invariant.none - (* match g with - | `Left g' -> (* unprotected *) - ValueDomain.invariant_global (fun g -> getg (V.unprotected g)) g' - | `Right g -> (* protected *) - Invariant.none *) + let invariant_global ask getg g = + let getg = Wrapper.getg ask getg in + match g with + | `Left g' -> (* unprotected *) + ValueDomain.invariant_global (fun g -> getg (V.unprotected g)) g' + | `Right g -> (* protected *) + Invariant.none let invariant_vars ask getg st = protected_vars ask end @@ -889,7 +890,7 @@ struct open Locksets - let invariant_global getg = function + let invariant_global ask getg = function | `Right g' -> (* global *) ValueDomain.invariant_global (fun x -> GWeak.fold (fun s' tm acc -> @@ -1681,7 +1682,7 @@ struct let threadenter ask st = time "threadenter" (Priv.threadenter ask) st let threadspawn ask get set st = time "threadspawn" (Priv.threadspawn ask get set) st let iter_sys_vars getg vq vf = time "iter_sys_vars" (Priv.iter_sys_vars getg vq) vf - let invariant_global getg v = time "invariant_global" (Priv.invariant_global getg) v + let invariant_global ask getg v = time "invariant_global" (Priv.invariant_global ask getg) v let invariant_vars ask getg st = time "invariant_vars" (Priv.invariant_vars ask getg) st let thread_join ?(force=false) ask get e st = time "thread_join" (Priv.thread_join ~force ask get e) st diff --git a/src/analyses/basePriv.mli b/src/analyses/basePriv.mli index 6906e6e4e1..e176a450fa 100644 --- a/src/analyses/basePriv.mli +++ b/src/analyses/basePriv.mli @@ -31,7 +31,7 @@ sig val thread_join: ?force:bool -> Queries.ask -> (V.t -> G.t) -> Cil.exp -> BaseDomain.BaseComponents (D).t -> BaseDomain.BaseComponents (D).t val thread_return: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> ThreadIdDomain.Thread.t -> BaseDomain.BaseComponents (D).t -> BaseDomain.BaseComponents (D).t - val invariant_global: (V.t -> G.t) -> V.t -> Invariant.t + val invariant_global: Queries.ask -> (V.t -> G.t) -> V.t -> Invariant.t (** Provides [Queries.InvariantGlobal] result for base. Should account for all unprotected/weak values of global variables. *) From df0d0f5c481f9274cc68561ecf30598140e46bd6 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 25 Mar 2024 11:18:27 +0100 Subject: [PATCH 267/280] Add second more involved example --- .../13-privatized/76-protection-tid2.c | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 tests/regression/13-privatized/76-protection-tid2.c diff --git a/tests/regression/13-privatized/76-protection-tid2.c b/tests/regression/13-privatized/76-protection-tid2.c new file mode 100644 index 0000000000..f756b2b51f --- /dev/null +++ b/tests/regression/13-privatized/76-protection-tid2.c @@ -0,0 +1,56 @@ +// PARAM: --set ana.base.privatization protection-tid --enable ana.int.interval +#include +#include + +int g; +pthread_mutex_t m; + +void* spoiler() { + pthread_mutex_lock(&m); + g = 4; + pthread_mutex_unlock(&m); +} + +void* otherproducer() { + pthread_mutex_lock(&m); + g = 9; + pthread_mutex_unlock(&m); +} + +void* producer() +{ + pthread_t tid1; + pthread_mutex_lock(&m); + g = 8; + pthread_mutex_unlock(&m); + + pthread_mutex_lock(&m); + __goblint_check(g < 9); + pthread_mutex_unlock(&m); + + pthread_create(&tid1, 0, otherproducer, 0); + + return 0; +} + +int main() +{ + pthread_t tid1; + pthread_t tid2; + + pthread_create(&tid1, 0, spoiler, 0); + + pthread_mutex_lock(&m); + __goblint_check(g < 5); + pthread_mutex_unlock(&m); + + pthread_create(&tid2, 0, producer, 0); + + + pthread_mutex_lock(&m); + __goblint_check(g < 5); //UNKNOWN! + __goblint_check(g < 10); + pthread_mutex_unlock(&m); + + return 0; +} From a64b85abe3ff8ffb0632b105f060bcc8e2c0cb98 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 25 Mar 2024 12:30:45 +0100 Subject: [PATCH 268/280] Add `protection-read-tid` --- src/analyses/basePriv.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 1ba1f80a39..c15f3bf8f0 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -1842,6 +1842,7 @@ let priv_module: (module S) Lazy.t = | "protection-tid" -> (module ProtectionBasedPrivWrapper (struct let check_read_unprotected = false let handle_atomic = false end)(DigestWrapper(ThreadNotStartedDigest))) | "protection-atomic" -> (module ProtectionBasedPrivWrapper (struct let check_read_unprotected = false let handle_atomic = true end)(NoWrapper)) (* experimental *) | "protection-read" -> (module ProtectionBasedPrivWrapper (struct let check_read_unprotected = true let handle_atomic = false end)(NoWrapper)) + | "protection-read-tid" -> (module ProtectionBasedPrivWrapper (struct let check_read_unprotected = true let handle_atomic = false end)(DigestWrapper(ThreadNotStartedDigest))) | "protection-read-atomic" -> (module ProtectionBasedPrivWrapper (struct let check_read_unprotected = true let handle_atomic = true end)(NoWrapper)) (* experimental *) | "mine" -> (module MinePriv) | "mine-nothread" -> (module MineNoThreadPriv) From 3deeba37bb45ee037682f4eec96fb932e064e966 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 25 Mar 2024 12:34:01 +0100 Subject: [PATCH 269/280] `protection-read-tid` in `options.schema.json` --- src/config/options.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 01740c1ee6..af877c32ab 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -754,7 +754,7 @@ "description": "Which privatization to use? none/mutex-oplus/mutex-meet/mutex-meet-tid/protection/protection-read/mine/mine-nothread/mine-W/mine-W-noinit/lock/write/write+lock", "type": "string", - "enum": ["none", "mutex-oplus", "mutex-meet", "protection", "protection-tid", "protection-atomic", "protection-read", "protection-read-atomic", "mine", "mine-nothread", "mine-W", "mine-W-noinit", "lock", "write", "write+lock","mutex-meet-tid"], + "enum": ["none", "mutex-oplus", "mutex-meet", "protection", "protection-tid", "protection-atomic", "protection-read", "protection-read-tid", "protection-read-atomic", "mine", "mine-nothread", "mine-W", "mine-W-noinit", "lock", "write", "write+lock","mutex-meet-tid"], "default": "protection-read" }, "priv": { From 508bf67a1f70dc1ef1b694d695c917a7936c760e Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 25 Mar 2024 13:14:01 +0100 Subject: [PATCH 270/280] Fix semantics of `TID.is_must_parent` and resulting changes --- src/analyses/commonPriv.ml | 6 +----- src/analyses/threadEscape.ml | 9 ++++++--- src/analyses/useAfterFree.ml | 7 +++++-- src/cdomain/value/cdomains/threadIdDomain.ml | 7 +++++-- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 83bafb2f63..1fefb847b1 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -209,11 +209,7 @@ struct let accounted_for (ask: Q.ask) ~(current: t) ~(other: t) = match current, other with | `Lifted current, `Lifted other -> - if TID.is_unique current && TID.equal current other then - (* workaround `definitely_not_started` answers false when passing the same argument twice, TID.is_must_parent [main] [main] says true *) - false - else - MHP.definitely_not_started (current, ask.f Q.CreatedThreads) other + MHP.definitely_not_started (current, ask.f Q.CreatedThreads) other | _ -> false end diff --git a/src/analyses/threadEscape.ml b/src/analyses/threadEscape.ml index 21fa3a8cfb..cc970b04b0 100644 --- a/src/analyses/threadEscape.ml +++ b/src/analyses/threadEscape.ml @@ -80,7 +80,10 @@ struct if ThreadIdSet.is_empty threads then false else begin - let possibly_started current = function + let other_possibly_started current = function + | `Lifted tid when (ThreadId.Thread.equal current tid && ThreadId.Thread.is_unique current) -> + (* if our own (unique) thread is started here, that is not a problem *) + false | `Lifted tid -> let threads = ctx.ask Queries.CreatedThreads in let not_started = MHP.definitely_not_started (current, threads) tid in @@ -97,14 +100,14 @@ struct in match ctx.ask Queries.CurrentThreadId with | `Lifted current -> - let possibly_started = ThreadIdSet.exists (possibly_started current) threads in + let possibly_started = ThreadIdSet.exists (other_possibly_started current) threads in if possibly_started then true else let current_is_unique = ThreadId.Thread.is_unique current in let any_equal_current threads = ThreadIdSet.exists (equal_current current) threads in if not current_is_unique && any_equal_current threads then - (* Another instance of the non-unqiue current thread may have escaped the variable *) + (* Another instance of the non-unique current thread may have escaped the variable *) true else (* Check whether current unique thread has escaped the variable *) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 69db6b4bfa..d9ae0d7ddf 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -40,8 +40,11 @@ struct (* If we're single-threaded or there are no threads freeing the memory, we have nothing to WARN about *) if ctx.ask (Queries.MustBeSingleThreaded { since_start = true }) || G.is_empty freeing_threads then () else begin - let possibly_started current tid joined_threads = + let other_possibly_started current tid joined_threads = match tid with + | `Lifted tid when (ThreadId.Thread.equal current tid && ThreadId.Thread.is_unique current) -> + (* if our own (unique) thread is started here, that is not a problem *) + false | `Lifted tid -> let created_threads = ctx.ask Queries.CreatedThreads in let not_started = MHP.definitely_not_started (current, created_threads) tid in @@ -62,7 +65,7 @@ struct let bug_name = if is_double_free then "Double Free" else "Use After Free" in match get_current_threadid ctx with | `Lifted current -> - let possibly_started = G.exists (possibly_started current) freeing_threads in + let possibly_started = G.exists (other_possibly_started current) freeing_threads in if possibly_started then begin if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "There's a thread that's been started in parallel with the memory-freeing threads for heap variable %a. %s might occur" CilType.Varinfo.pretty heap_var bug_name diff --git a/src/cdomain/value/cdomains/threadIdDomain.ml b/src/cdomain/value/cdomains/threadIdDomain.ml index 19ac502c23..4da71688a3 100644 --- a/src/cdomain/value/cdomains/threadIdDomain.ml +++ b/src/cdomain/value/cdomains/threadIdDomain.ml @@ -55,7 +55,7 @@ struct (struct let name = "no index" end))) (struct let name = "no node" end)) - let show (f, ni_opt) = + let show (f, ni_opt) = let vname = f.vname in match ni_opt with | None -> vname @@ -141,7 +141,10 @@ struct S.is_empty s let is_must_parent (p,s) (p',s') = - if not (S.is_empty s) then + if (not (S.is_empty s)) then + false + else if (P.equal p' p && S.equal s s') then + (* We do not consider a thread its own parent *) false else let cdef_ancestor = P.common_suffix p p' in From f27cfc6b25b2ff9d733139713a8936bfeadbdba7 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 25 Mar 2024 13:38:12 +0100 Subject: [PATCH 271/280] Simplify --- src/analyses/basePriv.ml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index c15f3bf8f0..507456177b 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -662,17 +662,15 @@ end module type ProtectionBasedWrapper = sig module G: Lattice.S - module V: module type of (ProtectionBasedV.V) - val getg: Q.ask -> (V.t -> G.t) -> V.t -> VD.t - val sideg: Q.ask -> (V.t -> G.t -> unit) -> V.t -> VD.t -> unit + val getg: Q.ask -> (ProtectionBasedV.V.t -> G.t) -> ProtectionBasedV.V.t -> VD.t + val sideg: Q.ask -> (ProtectionBasedV.V.t -> G.t -> unit) -> ProtectionBasedV.V.t -> VD.t -> unit end module NoWrapper: ProtectionBasedWrapper = struct module G = VD - module V = ProtectionBasedV.V let getg _ getg = getg let sideg _ sideg = sideg @@ -680,7 +678,6 @@ end module DigestWrapper(Digest: Digest): ProtectionBasedWrapper = struct module G = MapDomain.MapBot_LiftTop (Digest) (VD) - module V = ProtectionBasedV.V let getg ask getg x = let vs = getg x in From 43aa3bd0b72f0c4be41a6dc08d437fe8ffd5a68d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Mar 2024 11:48:38 +0200 Subject: [PATCH 272/280] Add SKIP to two new Apron tests (PR #1297) --- tests/regression/46-apron2/82-fixpoint-not-reached.c | 2 +- tests/regression/77-lin2vareq/25-different_types.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/regression/46-apron2/82-fixpoint-not-reached.c b/tests/regression/46-apron2/82-fixpoint-not-reached.c index 582734025b..1dff575a7c 100644 --- a/tests/regression/46-apron2/82-fixpoint-not-reached.c +++ b/tests/regression/46-apron2/82-fixpoint-not-reached.c @@ -1,4 +1,4 @@ -//PARAM: --set sem.int.signed_overflow assume_none --set ana.activated[+] apron +// SKIP PARAM: --set sem.int.signed_overflow assume_none --set ana.activated[+] apron int main() { int minInt = -2147483647 + -1; diff --git a/tests/regression/77-lin2vareq/25-different_types.c b/tests/regression/77-lin2vareq/25-different_types.c index 3439b406cd..8430eeedd5 100644 --- a/tests/regression/77-lin2vareq/25-different_types.c +++ b/tests/regression/77-lin2vareq/25-different_types.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] lin2vareq +// SKIP PARAM: --set ana.activated[+] lin2vareq #include int x = 42; @@ -6,7 +6,7 @@ long y; short z; int main() { - + y = (long)x; z = (short)x; From 8179039619b527f8131422eb70e9ac184946661e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Mar 2024 15:48:46 +0200 Subject: [PATCH 273/280] Increase zarith lower bound to 1.10 for Z.divisible --- dune-project | 2 +- goblint.opam | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dune-project b/dune-project index d6ae4be7e7..757c37b087 100644 --- a/dune-project +++ b/dune-project @@ -26,7 +26,7 @@ (ocaml (>= 4.10)) (goblint-cil (>= 2.0.3)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. (batteries (>= 3.5.1)) - (zarith (>= 1.8)) + (zarith (>= 1.10)) (yojson (>= 2.0.0)) (qcheck-core (>= 0.19)) ppx_deriving diff --git a/goblint.opam b/goblint.opam index d7724aad34..b9f4d838df 100644 --- a/goblint.opam +++ b/goblint.opam @@ -24,7 +24,7 @@ depends: [ "ocaml" {>= "4.10"} "goblint-cil" {>= "2.0.3"} "batteries" {>= "3.5.1"} - "zarith" {>= "1.8"} + "zarith" {>= "1.10"} "yojson" {>= "2.0.0"} "qcheck-core" {>= "0.19"} "ppx_deriving" From 1c782bde36705e33ce413658b71ea68d40516eb9 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 26 Mar 2024 16:07:54 +0100 Subject: [PATCH 274/280] Rewrite `accounted_for` for more clarity Co-authored-by: Simmo Saan --- src/cdomain/value/cdomains/threadIdDomain.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomain/value/cdomains/threadIdDomain.ml b/src/cdomain/value/cdomains/threadIdDomain.ml index 4da71688a3..fff6734f27 100644 --- a/src/cdomain/value/cdomains/threadIdDomain.ml +++ b/src/cdomain/value/cdomains/threadIdDomain.ml @@ -141,9 +141,9 @@ struct S.is_empty s let is_must_parent (p,s) (p',s') = - if (not (S.is_empty s)) then + if not (S.is_empty s) then false - else if (P.equal p' p && S.equal s s') then + else if P.equal p' p && S.is_empty s' then (* s is already empty *) (* We do not consider a thread its own parent *) false else From 6961ad23c5e32832924d155427b15685f73165b7 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 26 Mar 2024 16:16:57 +0100 Subject: [PATCH 275/280] Comment on `ThreadDigest` vs `ThreadNotStartedDigest` --- src/analyses/commonPriv.ml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 1fefb847b1..16bf6df889 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -174,6 +174,9 @@ sig val accounted_for: Q.ask -> current:t -> other:t -> bool end +(** Digest to be be used for analyses that account for all join-local contributions in some locally tracked datastructure, akin to the L component from the analyses in + @see Schwarz, M., Saan, S., Seidl, H., Erhard, J., Vojdani, V. Clustered Relational Thread-Modular Abstract Interpretation with Local Traces. +*) module ThreadDigest: Digest = struct include ThreadIdDomain.ThreadLifted @@ -197,6 +200,9 @@ struct | _ -> false end +(** Ego-Lane Derived digest based on whether given threads have been started yet, can be used to refine any analysis + @see PhD thesis of M. Schwarz once it is published ;) +*) module ThreadNotStartedDigest:Digest = struct include ThreadIdDomain.ThreadLifted From 603b90966f120a97eda93a6774f9e4f30808ef2b Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 26 Mar 2024 16:19:01 +0100 Subject: [PATCH 276/280] Typo --- src/analyses/commonPriv.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 16bf6df889..6376a6ca11 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -174,7 +174,7 @@ sig val accounted_for: Q.ask -> current:t -> other:t -> bool end -(** Digest to be be used for analyses that account for all join-local contributions in some locally tracked datastructure, akin to the L component from the analyses in +(** Digest to be used for analyses that account for all join-local contributions in some locally tracked datastructure, akin to the L component from the analyses in @see Schwarz, M., Saan, S., Seidl, H., Erhard, J., Vojdani, V. Clustered Relational Thread-Modular Abstract Interpretation with Local Traces. *) module ThreadDigest: Digest = From 8205242dab8ba6fc5d221950668b7df409ff4569 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Mar 2024 18:19:14 +0200 Subject: [PATCH 277/280] Make tracing unindent explicit in WitnessConstraints --- src/witness/witnessConstraints.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/witness/witnessConstraints.ml b/src/witness/witnessConstraints.ml index bda7945855..4af9dd898d 100644 --- a/src/witness/witnessConstraints.ml +++ b/src/witness/witnessConstraints.ml @@ -230,7 +230,7 @@ struct if M.tracing then M.tracec "witness" "j = %a" Spec.D.pretty j; f (I.to_int x) (n, Obj.repr c, I.to_int j) e ) r; - if M.tracing then M.traceu "witness" "" + if M.tracing then M.traceu "witness" "" (* unindent! *) ) (fst ctx.local); (* check that sync mappings don't leak into solution (except Function) *) (* TODO: disabled because we now use and leave Sync for every tf, From 657eb7c9ccb1cf04f2217088c5f84e1279115f41 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Mar 2024 18:22:29 +0200 Subject: [PATCH 278/280] Fix full_trace calls in Generic --- src/solver/generic.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/solver/generic.ml b/src/solver/generic.ml index df41eaa38d..1f0df57843 100644 --- a/src/solver/generic.ml +++ b/src/solver/generic.ml @@ -66,10 +66,10 @@ struct if tracing then trace "sol" "New %a" Var.pretty_trace x let get_var_event x = - if full_trace then trace "sol" "Querying %a" Var.pretty_trace x + if tracing && full_trace then trace "sol" "Querying %a" Var.pretty_trace x let eval_rhs_event x = - if full_trace then trace "sol" "(Re-)evaluating %a" Var.pretty_trace x; + if tracing && full_trace then trace "sol" "(Re-)evaluating %a" Var.pretty_trace x; incr SolverStats.evals; if (get_bool "dbg.solver-progress") then (incr stack_d; Logs.debug "%d" !stack_d) @@ -295,7 +295,7 @@ module SoundBoxSolverImpl = (* remove old influences of [x] -- they will be re-generated if still needed *) H.remove infl x; H.replace infl x [x]; - if full_trace + if Messages.tracing && full_trace then Messages.trace "sol" "Need to review %d deps." (List.length deps); (* nosemgrep: trace-not-in-tracing *) (* solve all dependencies *) solve_all deps From cab3aa8953d7e0e22242fa11870969beae123132 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Mar 2024 18:48:33 +0200 Subject: [PATCH 279/280] Fix lin2vareq indentation (PR #1297) --- .../apron/linearTwoVarEqualityDomain.apron.ml | 74 +++++++++---------- src/cdomains/apron/sharedFunctions.apron.ml | 8 +- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index b4bf5a7616..c52d0a7152 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -188,17 +188,17 @@ struct (** convert and simplify (wrt. reference variables) a texpr into a tuple of a list of monomials and a constant *) let simplified_monomials_from_texp (t: t) texp = BatOption.bind (monomials_from_texp t texp) - (fun monomiallist -> - let d = Option.get t.d in - let expr = Array.make (Environment.size t.env) Z.zero in - let accumulate_constants a (c, v) = match v with - | None -> Z.(a + c) - | Some idx -> let (term,con) = d.(idx) in - (Option.may (fun ter -> expr.(ter) <- Z.(expr.(ter) + c)) term; - Z.(a + c * con)) - in - let constant = List.fold_left accumulate_constants Z.zero monomiallist in (* abstract simplification of the guard wrt. reference variables *) - Some (Array.fold_lefti (fun list v (c) -> if Z.equal c Z.zero then list else (c,v)::list) [] expr, constant) ) + (fun monomiallist -> + let d = Option.get t.d in + let expr = Array.make (Environment.size t.env) Z.zero in + let accumulate_constants a (c, v) = match v with + | None -> Z.(a + c) + | Some idx -> let (term,con) = d.(idx) in + (Option.may (fun ter -> expr.(ter) <- Z.(expr.(ter) + c)) term; + Z.(a + c * con)) + in + let constant = List.fold_left accumulate_constants Z.zero monomiallist in (* abstract simplification of the guard wrt. reference variables *) + Some (Array.fold_lefti (fun list v (c) -> if Z.equal c Z.zero then list else (c,v)::list) [] expr, constant) ) let simplify_to_ref_and_offset (t: t) texp = BatOption.bind (simplified_monomials_from_texp t texp ) @@ -415,19 +415,19 @@ struct List.iter iterate new_components; Some ad in (*Normalize the two domains a and b such that both talk about the same variables*) - match a.d, b.d with - | None, _ -> b - | _, None -> a - | Some x, Some y when is_top a || is_top b -> - let new_env = Environment.lce a.env b.env in - top_of new_env - | Some x, Some y when (Environment.compare a.env b.env <> 0) -> - let sup_env = Environment.lce a.env b.env in - let mod_x = dim_add (Environment.dimchange a.env sup_env) x in - let mod_y = dim_add (Environment.dimchange b.env sup_env) y in - {d = join_d mod_x mod_y; env = sup_env} - | Some x, Some y when EArray.equal x y -> {d = Some x; env = a.env} - | Some x, Some y -> {d = join_d x y; env = a.env} + match a.d, b.d with + | None, _ -> b + | _, None -> a + | Some x, Some y when is_top a || is_top b -> + let new_env = Environment.lce a.env b.env in + top_of new_env + | Some x, Some y when (Environment.compare a.env b.env <> 0) -> + let sup_env = Environment.lce a.env b.env in + let mod_x = dim_add (Environment.dimchange a.env sup_env) x in + let mod_y = dim_add (Environment.dimchange b.env sup_env) y in + {d = join_d mod_x mod_y; env = sup_env} + | Some x, Some y when EArray.equal x y -> {d = Some x; env = a.env} + | Some x, Some y -> {d = join_d x y; env = a.env} let join a b = timing_wrap "join" (join a) b @@ -473,7 +473,7 @@ struct let forget_vars t vars = timing_wrap "forget_vars" (forget_vars t) vars (** implemented as described on page 10 in the paper about Fast Interprocedural Linear Two-Variable Equalities in the Section "Abstract Effect of Statements" - This makes a copy of the data structure, it doesn't change it in-place. *) + This makes a copy of the data structure, it doesn't change it in-place. *) let assign_texpr (t: VarManagement.t) var texp = match t.d with | Some d -> @@ -522,7 +522,7 @@ struct (** Parallel assignment of variables. First apply the assignments to temporary variables x' and y' to keep the old dependencies of x and y - and in a second round assign x' to x and y' to y + and in a second round assign x' to x and y' to y *) let assign_var_parallel t vv's = let assigned_vars = List.map fst vv's in @@ -583,10 +583,10 @@ struct and Convert.tcons1_of_cil_exp will raise the exception Unsupported_CilExp Overflow meet_tcons -> meet with guard in if statement - texpr -> tree expr (right hand side of equality) - -> expression used to derive tcons -> used to check for overflow - tcons -> tree constraint (expression < 0) - -> does not have types (overflow is type dependent) + texpr -> tree expr (right hand side of equality) + -> expression used to derive tcons -> used to check for overflow + tcons -> tree constraint (expression < 0) + -> does not have types (overflow is type dependent) *) let meet_tcons ask t tcons original_expr no_ov = match t.d with @@ -630,13 +630,13 @@ struct (** Assert a constraint expression. Defined in apronDomain.apron.ml - If the constraint is never fulfilled, then return bottom. - Else the domain can be modified with the new information given by the constraint. + If the constraint is never fulfilled, then return bottom. + Else the domain can be modified with the new information given by the constraint. - It basically just calls the function meet_tcons. + It basically just calls the function meet_tcons. - It is called by eval (defined in sharedFunctions), but also when a guard in - e.g. an if statement is encountered in the C code. + It is called by eval (defined in sharedFunctions), but also when a guard in + e.g. an if statement is encountered in the C code. *) let assert_constraint ask d e negate (no_ov: bool Lazy.t) = @@ -651,9 +651,9 @@ struct (** representation as C expression - This function returns all the equalities that are saved in our datastructure t. + This function returns all the equalities that are saved in our datastructure t. - Lincons -> linear constraint *) + Lincons -> linear constraint *) let invariant t = let of_coeff xi coeffs o = let typ = (Option.get @@ V.to_cil_varinfo xi).vtype in diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 1e17074088..47bf927f1a 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -105,10 +105,10 @@ struct let conv exp = let query e ik = let res = - match ask.f (EvalInt e) with - | `Bot -> raise (Unsupported_CilExp Exp_not_supported) (* This should never happen according to Michael Schwarz *) - | `Top -> IntDomain.IntDomTuple.top_of ik - | `Lifted x -> x (* Cast should be unnecessary because it should be taken care of by EvalInt. *) in + match ask.f (EvalInt e) with + | `Bot -> raise (Unsupported_CilExp Exp_not_supported) (* This should never happen according to Michael Schwarz *) + | `Top -> IntDomain.IntDomTuple.top_of ik + | `Lifted x -> x (* Cast should be unnecessary because it should be taken care of by EvalInt. *) in if M.tracing then M.trace "relation" "texpr1_expr_of_cil_exp/query: %a -> %a\n" d_plainexp e IntDomain.IntDomTuple.pretty res; res in From 007f150efb176bd2fdbbe1c8e6c6334d30a6b389 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Mar 2024 18:50:04 +0200 Subject: [PATCH 280/280] Remove newlines in debugging docs --- docs/developer-guide/debugging.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/developer-guide/debugging.md b/docs/developer-guide/debugging.md index 680e15fe3d..929ac968f8 100644 --- a/docs/developer-guide/debugging.md +++ b/docs/developer-guide/debugging.md @@ -13,25 +13,25 @@ Goblint extensively uses [CIL's `Pretty`](https://people.eecs.berkeley.edu/~necu * Logging CIL values (e.g. an expression `exp`) using the corresponding pretty-printer `d_exp` from `Cil` module: ```ocaml -Logs.debug "A CIL exp: %a\n" d_exp exp; +Logs.debug "A CIL exp: %a" d_exp exp; ``` * Logging Goblint's `Printable` values (e.g. a domain `D` element `d`) using the corresponding pretty-printer `D.pretty`: ```ocaml -Logs.debug "A domain element: %a\n" D.pretty d; +Logs.debug "A domain element: %a" D.pretty d; ``` * Logging primitives (e.g. OCaml ints, strings, etc) using the standard [OCaml `Printf`](https://ocaml.org/api/Printf.html) specifiers: ```ocaml -Logs.debug "An int and a string: %d %s\n" 42 "magic"; +Logs.debug "An int and a string: %d %s" 42 "magic"; ``` * Logging lists of pretty-printables (e.g. expressions list `exps`) using `d_list`: ```ocaml -Logs.debug "Some expressions: %a\n" (d_list ", " d_exp) exps; +Logs.debug "Some expressions: %a" (d_list ", " d_exp) exps; ``` @@ -42,7 +42,7 @@ Recompile with tracing enabled: `./scripts/trace_on.sh`. Instead of logging use a tracing function from the `Messages` module, which is often aliased to just `M` (and pick a relevant name instead of `mything`): ```ocaml -if M.tracing then M.trace "mything" "A domain element: %a\n" D.pretty d; +if M.tracing then M.trace "mything" "A domain element: %a" D.pretty d; ``` Then run Goblint with the additional argument `--trace mything`.