Skip to content

Commit 4ac477e

Browse files
authored
Merge branch 'master' into master
2 parents fc22ec0 + db6db2d commit 4ac477e

20 files changed

+551
-149
lines changed

src/arg/argTools.ml

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -137,19 +137,6 @@ struct
137137
| Statement stmt -> Printf.sprintf "s%d(%d)[%s]" stmt.sid c_tag i_str
138138
| Function f -> Printf.sprintf "ret%d%s(%d)[%s]" f.svar.vid f.svar.vname c_tag i_str
139139
| FunctionEntry f -> Printf.sprintf "fun%d%s(%d)[%s]" f.svar.vid f.svar.vname c_tag i_str
140-
141-
(* TODO: less hacky way (without ask_indices) to move node *)
142-
let is_live (n, c, i) = not (Spec.D.is_bot (get (n, c)))
143-
let move_opt (n, c, i) to_n =
144-
match ask_indices (to_n, c) with
145-
| [] -> None
146-
| [to_i] ->
147-
let to_node = (to_n, c, to_i) in
148-
BatOption.filter is_live (Some to_node)
149-
| _ :: _ :: _ ->
150-
failwith "Node.move_opt: ambiguous moved index"
151-
let equal_node_context (n1, c1, i1) (n2, c2, i2) =
152-
EQSys.LVar.equal (n1, c1) (n2, c2)
153140
end
154141

155142
module NHT = BatHashtbl.Make (Node)

src/arg/myARG.ml

Lines changed: 58 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,19 @@ sig
1212
val context_id: t -> int
1313
val path_id: t -> int
1414
val to_string: t -> string
15-
16-
val move_opt: t -> MyCFG.node -> t option
17-
val equal_node_context: t -> t -> bool
1815
end
1916

2017
module type Edge =
2118
sig
22-
type t
19+
type t [@@deriving eq, ord]
2320

2421
val embed: MyCFG.edge -> t
2522
val to_string: t -> string
2623
end
2724

2825
module CFGEdge: Edge with type t = MyCFG.edge =
2926
struct
30-
type t = edge
27+
type t = Edge.t [@@deriving eq, ord]
3128

3229
let embed e = e
3330
let to_string e = GobPretty.sprint Edge.pretty_plain e
@@ -102,7 +99,7 @@ end
10299

103100
module InlineEdge: Edge with type t = inline_edge =
104101
struct
105-
type t = inline_edge
102+
type t = inline_edge [@@deriving eq, ord]
106103

107104
let embed e = CFGEdge e
108105
let to_string e = InlineEdgePrintable.show e
@@ -130,15 +127,6 @@ struct
130127
nl
131128
|> List.map Node.to_string
132129
|> String.concat "@"
133-
134-
let move_opt nl to_node =
135-
let open GobOption.Syntax in
136-
match nl with
137-
| [] -> None
138-
| n :: stack ->
139-
let+ to_n = Node.move_opt n to_node in
140-
to_n :: stack
141-
let equal_node_context _ _ = failwith "StackNode: equal_node_context"
142130
end
143131

144132
module Stack (Arg: S with module Edge = InlineEdge):
@@ -265,16 +253,19 @@ struct
265253
end
266254
end
267255

256+
type cfg_path = (MyCFG.edge * MyCFG.node) list
268257

269258
module type SIntra =
270259
sig
271-
val next: MyCFG.node -> (MyCFG.edge * MyCFG.node) list
260+
val next: MyCFG.node -> (MyCFG.edge * MyCFG.node * cfg_path list) list
261+
(** @return Also the original CFG paths corresponding to the step. *)
272262
end
273263

274264
module type SIntraOpt =
275265
sig
276266
include SIntra
277-
val next_opt: MyCFG.node -> ((MyCFG.edge * MyCFG.node) list) option
267+
val next_opt: MyCFG.node -> ((MyCFG.edge * MyCFG.node * cfg_path list) list) option
268+
(** @return Also the original CFG paths corresponding to the step. *)
278269
end
279270

280271
module CfgIntra (Cfg:CfgForward): SIntraOpt =
@@ -283,51 +274,38 @@ struct
283274
let open GobList.Syntax in
284275
let* (es, to_n) = Cfg.next node in
285276
let+ (_, e) = es in
286-
(e, to_n)
277+
(e, to_n, [[(e, to_n)]])
287278
let next_opt _ = None
288279
end
289280

290-
let partition_if_next if_next_n =
291-
(* TODO: refactor, check extra edges for error *)
292-
let test_next b = List.find (function
293-
| (Test (_, b'), _) when b = b' -> true
294-
| (_, _) -> false
295-
) if_next_n
281+
let cartesian_concat_paths (ps : cfg_path list) (qs : cfg_path list) : cfg_path list = List.concat_map (fun p -> List.map (fun q -> p @ q) qs) ps
282+
283+
let partition_if_next if_next =
284+
let (if_next_trues, if_next_falses) = List.partition (function
285+
| (Test (_, b), _, _) -> b
286+
| (_, _, _) -> failwith "partition_if_next: not Test edge"
287+
) if_next
296288
in
297-
(* assert (List.length if_next <= 2); *)
298-
match test_next true, test_next false with
299-
| (Test (e_true, true), if_true_next_n), (Test (e_false, false), if_false_next_n) when Basetype.CilExp.equal e_true e_false ->
300-
(e_true, if_true_next_n, if_false_next_n)
301-
| _, _ -> failwith "partition_if_next: bad branches"
289+
match if_next_trues, if_next_falses with
290+
| [(Test (e_true, true), if_true_next_n, if_true_next_ps)], [(Test (e_false, false), if_false_next_n, if_false_next_ps)] when Basetype.CilExp.equal e_true e_false ->
291+
(e_true, (if_true_next_n, if_true_next_ps), (if_false_next_n, if_false_next_ps))
292+
| _, _ ->
293+
(* This fails due to any of the following:
294+
- Either true or false branch is missing.
295+
- Either true or false branch has multiple different exps or nodes (same exp, branch and node should only occur once by construction/assumption).
296+
- True and false branch have different exps. *)
297+
failwith "partition_if_next: bad branches"
302298

303299
module UnCilLogicIntra (Arg: SIntraOpt): SIntraOpt =
304300
struct
305301
open Cil
306-
(* TODO: questionable (=) and (==) use here *)
307-
308-
let is_equiv_stmtkind sk1 sk2 = match sk1, sk2 with
309-
| Instr is1, Instr is2 -> GobList.equal (=) is1 is2
310-
| Return _, Return _ -> sk1 = sk2
311-
| _, _ -> false (* TODO: also consider others? not sure if they ever get duplicated *)
312-
let is_equiv_stmt s1 s2 = is_equiv_stmtkind s1.skind s2.skind (* TODO: also consider labels *)
313-
let is_equiv_node n1 n2 = match n1, n2 with
314-
| Statement s1, Statement s2 -> is_equiv_stmt s1 s2
315-
| _, _ -> false (* TODO: also consider FunctionEntry & Function? *)
316-
let is_equiv_edge e1 e2 = match e1, e2 with
317-
| Entry f1, Entry f2 -> f1 == f2 (* physical equality for fundec to avoid cycle *)
318-
| Ret (exp1, f1), Ret (exp2, f2) -> exp1 = exp2 && f1 == f2 (* physical equality for fundec to avoid cycle *)
319-
| _, _ -> e1 = e2
320-
let rec is_equiv_chain n1 n2 =
321-
Node.equal n1 n2 || (is_equiv_node n1 n2 && is_equiv_chain_next n1 n2)
322-
and is_equiv_chain_next n1 n2 = match Arg.next n1, Arg.next n2 with
323-
| [(e1, to_n1)], [(e2, to_n2)] ->
324-
is_equiv_edge e1 e2 && is_equiv_chain to_n1 to_n2
325-
| _, _-> false
326302

303+
let () =
304+
assert (not !Cabs2cil.allowDuplication) (* duplication makes it more annoying to detect cilling *)
327305

328306
let rec next_opt' n = match n with
329307
| Statement {sid; skind=If _; _} when GobConfig.get_bool "exp.arg.uncil" ->
330-
let (e, if_true_next_n, if_false_next_n) = partition_if_next (Arg.next n) in
308+
let (e, (if_true_next_n, if_true_next_ps), (if_false_next_n, if_false_next_ps)) = partition_if_next (Arg.next n) in
331309
(* avoid infinite recursion with sid <> sid2 in if_nondet_var *)
332310
(* TODO: why physical comparison if_false_next_n != n doesn't work? *)
333311
(* TODO: need to handle longer loops? *)
@@ -336,24 +314,24 @@ struct
336314
(* && *)
337315
| Statement {sid=sid2; skind=If _; _}, _ when sid <> sid2 && CilType.Location.equal loc (Node.location if_true_next_n) ->
338316
(* get e2 from edge because recursive next returns it there *)
339-
let (e2, if_true_next_true_next_n, if_true_next_false_next_n) = partition_if_next (next if_true_next_n) in
340-
if is_equiv_chain if_false_next_n if_true_next_false_next_n then
317+
let (e2, (if_true_next_true_next_n, if_true_next_true_next_ps), (if_true_next_false_next_n, if_true_next_false_next_ps)) = partition_if_next (next if_true_next_n) in
318+
if Node.equal if_false_next_n if_true_next_false_next_n then
341319
let exp = BinOp (LAnd, e, e2, intType) in
342320
Some [
343-
(Test (exp, true), if_true_next_true_next_n);
344-
(Test (exp, false), if_false_next_n)
321+
(Test (exp, true), if_true_next_true_next_n, cartesian_concat_paths if_true_next_ps if_true_next_true_next_ps);
322+
(Test (exp, false), if_true_next_false_next_n, if_false_next_ps @ cartesian_concat_paths if_true_next_ps if_true_next_false_next_ps) (* concat two different path families to same false node *)
345323
]
346324
else
347325
None
348326
(* || *)
349327
| _, Statement {sid=sid2; skind=If _; _} when sid <> sid2 && CilType.Location.equal loc (Node.location if_false_next_n) ->
350328
(* get e2 from edge because recursive next returns it there *)
351-
let (e2, if_false_next_true_next_n, if_false_next_false_next_n) = partition_if_next (next if_false_next_n) in
352-
if is_equiv_chain if_true_next_n if_false_next_true_next_n then
329+
let (e2, (if_false_next_true_next_n, if_false_next_true_next_ps), (if_false_next_false_next_n, if_false_next_false_next_ps)) = partition_if_next (next if_false_next_n) in
330+
if Node.equal if_true_next_n if_false_next_true_next_n then
353331
let exp = BinOp (LOr, e, e2, intType) in
354332
Some [
355-
(Test (exp, true), if_true_next_n);
356-
(Test (exp, false), if_false_next_false_next_n)
333+
(Test (exp, true), if_false_next_true_next_n, if_true_next_ps @ cartesian_concat_paths if_false_next_ps if_false_next_true_next_ps); (* concat two different path families to same true node *)
334+
(Test (exp, false), if_false_next_false_next_n, cartesian_concat_paths if_false_next_ps if_false_next_false_next_ps)
357335
]
358336
else
359337
None
@@ -381,14 +359,14 @@ struct
381359

382360
let next_opt' n = match n with
383361
| Statement {skind=If _; _} when GobConfig.get_bool "exp.arg.uncil" ->
384-
let (e_cond, if_true_next_n, if_false_next_n) = partition_if_next (Arg.next n) in
362+
let (e_cond, (if_true_next_n, if_true_next_ps), (if_false_next_n, if_false_next_ps)) = partition_if_next (Arg.next n) in
385363
let loc = Node.location n in
386364
if CilType.Location.equal (Node.location if_true_next_n) loc && CilType.Location.equal (Node.location if_false_next_n) loc then
387365
match Arg.next if_true_next_n, Arg.next if_false_next_n with
388-
| [(Assign (v_true, e_true), if_true_next_next_n)], [(Assign (v_false, e_false), if_false_next_next_n)] when v_true = v_false && Node.equal if_true_next_next_n if_false_next_next_n ->
366+
| [(Assign (v_true, e_true), if_true_next_next_n, if_true_next_next_ps)], [(Assign (v_false, e_false), if_false_next_next_n, if_false_next_next_ps)] when v_true = v_false && Node.equal if_true_next_next_n if_false_next_next_n ->
389367
let exp = ternary e_cond e_true e_false in
390368
Some [
391-
(Assign (v_true, exp), if_true_next_next_n)
369+
(Assign (v_true, exp), if_true_next_next_n, cartesian_concat_paths if_true_next_ps if_true_next_next_ps @ cartesian_concat_paths if_false_next_ps if_false_next_next_ps) (* concat two different path families with same variable to same node *)
392370
]
393371
| _, _ -> None
394372
else
@@ -407,14 +385,30 @@ module Intra (ArgIntra: SIntraOpt) (Arg: S):
407385
struct
408386
include Arg
409387

388+
(** Starting from ARG node [node], follow CFG path [p] to the resulting ARG node.
389+
Returns multiple ARG nodes if ARG contains path-sensitivity splits on the same CFG path. *)
390+
let rec follow node p =
391+
let open GobList.Syntax in
392+
match p with
393+
| [] -> [node]
394+
| (e, to_n) :: p' ->
395+
let* (_, node') = List.filter (fun (e', to_node) ->
396+
Edge.equal (Edge.embed e) e' && Node0.equal to_n (Node.cfgnode to_node)
397+
) (Arg.next node)
398+
in
399+
follow node' p'
400+
410401
let next node =
411-
let open GobOption.Syntax in
402+
let open GobList.Syntax in
412403
match ArgIntra.next_opt (Node.cfgnode node) with
413404
| None -> Arg.next node
414405
| Some next ->
415406
next
416-
|> BatList.filter_map (fun (e, to_n) ->
417-
let+ to_node = Node.move_opt node to_n in
407+
|> BatList.concat_map (fun (e, to_n, p) ->
408+
let* p in
409+
let+ to_node = follow node p in
410+
assert (Node0.equal to_n (Node.cfgnode to_node)); (* should always hold by follow filter above *)
418411
(Edge.embed e, to_node)
419412
)
413+
|> BatList.unique_cmp ~cmp:[%ord: Edge.t * Node.t] (* after following paths, there may be duplicates because same ARG node can be reached via same ARG edge via multiple uncilled CFG paths *) (* TODO: avoid generating duplicates in the first place? *)
420414
end

src/cdomains/apron/gobApron.apron.ml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ module Coeff =
1919
struct
2020
include Coeff
2121

22+
let pp = print
23+
include Printable.SimpleFormat (
24+
struct
25+
type nonrec t = t
26+
let pp = pp
27+
end
28+
)
29+
2230
let s_of_z z = Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z z))
2331
end
2432

src/cdomains/apron/sharedFunctions.apron.ml

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ let int_of_scalar ?(scalewith=Z.one) ?round (scalar: Scalar.t) =
4141
| _ ->
4242
failwith ("int_of_scalar: unsupported: " ^ Scalar.show scalar)
4343

44+
let int_of_coeff ?scalewith ?round (coeff: Coeff.union_5) =
45+
match coeff with
46+
| Scalar scalar -> int_of_scalar ?scalewith ?round scalar
47+
| Interval _ -> None
4448

4549
module type ConvertArg =
4650
sig
@@ -269,37 +273,44 @@ struct
269273

270274
let longlong = TInt(ILongLong,[])
271275

276+
let int_of_coeff_warn ~scalewith coeff =
277+
match int_of_coeff ~scalewith coeff with
278+
| Some i -> i
279+
| None ->
280+
M.warn ~category:Analyzer "Invariant Apron: coefficient is not int: %a" Coeff.pretty coeff;
281+
raise Unsupported_Linexpr1
272282

273283
(** Returned boolean indicates whether returned expression should be negated. *)
274-
let coeff_to_const ~scalewith (c:Coeff.union_5) =
275-
match c with
276-
| Scalar c ->
277-
(match int_of_scalar ?scalewith c with
278-
| Some i ->
279-
let ci,truncation = truncateCilint ILongLong i in
280-
if truncation = NoTruncation then
281-
if Z.compare i Z.zero >= 0 then
282-
false, Const (CInt(i,ILongLong,None))
283-
else
284-
(* attempt to negate if that does not cause an overflow *)
285-
let cneg, truncation = truncateCilint ILongLong (Z.neg i) in
286-
if truncation = NoTruncation then
287-
true, Const (CInt((Z.neg i),ILongLong,None))
288-
else
289-
false, Const (CInt(i,ILongLong,None))
290-
else
291-
(M.warn ~category:Analyzer "Invariant Apron: coefficient is not int: %a" Scalar.pretty c; raise Unsupported_Linexpr1)
292-
| None -> raise Unsupported_Linexpr1)
293-
| _ -> raise Unsupported_Linexpr1
284+
let cil_exp_of_int i =
285+
let ci,truncation = truncateCilint ILongLong i in
286+
if truncation = NoTruncation then
287+
if Z.compare i Z.zero >= 0 then
288+
false, Const (CInt(i,ILongLong,None))
289+
else
290+
(* attempt to negate if that does not cause an overflow *)
291+
let cneg, truncation = truncateCilint ILongLong (Z.neg i) in
292+
if truncation = NoTruncation then
293+
true, Const (CInt((Z.neg i),ILongLong,None))
294+
else
295+
false, Const (CInt(i,ILongLong,None))
296+
else
297+
raise Unsupported_Linexpr1
294298

295299
(** Returned boolean indicates whether returned expression should be negated. *)
296300
let cil_exp_of_linexpr1_term ~scalewith (c: Coeff.t) v =
297301
match V.to_cil_varinfo v with
298302
| Some vinfo when IntDomain.Size.is_cast_injective ~from_type:vinfo.vtype ~to_type:(TInt(ILongLong,[])) ->
299303
let var = Cilfacade.mkCast ~e:(Lval(Var vinfo,NoOffset)) ~newt:longlong in
300-
let flip, coeff = coeff_to_const ~scalewith c in
301-
let prod = BinOp(Mult, coeff, var, longlong) in
302-
flip, prod
304+
let i = int_of_coeff_warn ~scalewith c in
305+
if Z.equal i Z.one then
306+
false, var
307+
else if Z.equal i Z.minus_one then
308+
true, var
309+
else (
310+
let flip, coeff = cil_exp_of_int i in
311+
let prod = BinOp(Mult, coeff, var, longlong) in
312+
flip, prod
313+
)
303314
| None ->
304315
M.warn ~category:Analyzer "Invariant Apron: cannot convert to cil var: %a" Var.pretty v;
305316
raise Unsupported_Linexpr1
@@ -308,8 +319,14 @@ struct
308319
raise Unsupported_Linexpr1
309320

310321
(** Returned booleans indicates whether returned expressions should be negated. *)
311-
let cil_exp_of_linexpr1 ?scalewith (linexpr1:Linexpr1.t) =
312-
let terms = ref [coeff_to_const ~scalewith (Linexpr1.get_cst linexpr1)] in
322+
let cil_exp_of_linexpr1 ~scalewith (linexpr1:Linexpr1.t) =
323+
let terms =
324+
let c = Linexpr1.get_cst linexpr1 in
325+
if Coeff.is_zero c then
326+
ref []
327+
else
328+
ref [cil_exp_of_int (int_of_coeff_warn ~scalewith c)]
329+
in
313330
let append_summand (c:Coeff.union_5) v =
314331
if not (Coeff.is_zero c) then
315332
terms := cil_exp_of_linexpr1_term ~scalewith c v :: !terms
@@ -369,7 +386,7 @@ struct
369386
| DISEQ -> Ne
370387
| EQMOD _ -> raise Unsupported_Linexpr1
371388
in
372-
Some (Cil.constFold false @@ BinOp(binop, lhs, rhs, TInt(IInt,[]))) (* constFold removes multiplication by factor 1 *)
389+
Some (BinOp(binop, lhs, rhs, TInt(IInt,[])))
373390
with
374391
Unsupported_Linexpr1 -> None
375392
end

src/common/util/cilfacade.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ let init () =
9090
(* lineDirectiveStyle := None; *)
9191
RmUnused.keepUnused := true;
9292
print_CIL_Input := true;
93-
Cabs2cil.allowDuplication := false;
93+
Cabs2cil.allowDuplication := false; (* needed for ARG uncilling, maybe something else as well? *)
9494
Cabs2cil.silenceLongDoubleWarning := true;
9595
Cabs2cil.addLoopConditionLabels := true
9696

src/util/std/gobYaml.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ let to_string' ?(len=65535 * 4) ?encoding ?scalar_style ?layout_style v =
33
let rec aux len =
44
match Yaml.to_string ~len ?encoding ?scalar_style ?layout_style v with
55
| Ok _ as o -> o
6-
| Error (`Msg ("scalar failed" | "doc_end failed" | "seq_end failed")) when len < Sys.max_string_length / 2 ->
6+
| Error (`Msg ("scalar failed" | "doc_end failed" | "seq_end failed" | "mapping_start failed")) when len < Sys.max_string_length / 2 ->
77
aux (len * 2)
88
| Error (`Msg _) as e -> e
99
in

0 commit comments

Comments
 (0)