Skip to content

Commit b9785ab

Browse files
committed
Applied changes requested in PR goblint#731. compareCFG now propagates rename_mapping.
1 parent 1caaa79 commit b9785ab

File tree

4 files changed

+96
-59
lines changed

4 files changed

+96
-59
lines changed

src/incremental/compareAST.ml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ let rename_mapping_to_string (rename_mapping: rename_mapping) =
6363

6464
"(local=" ^ local_string ^ "; methods=[" ^ methods_string ^ "]; glob_vars=" ^ global_var_string ^ ")"
6565

66+
let is_rename_mapping_empty (rename_mapping: rename_mapping) =
67+
let local, methods, glob_vars, _= rename_mapping in
68+
StringMap.is_empty local && VarinfoMap.is_empty methods && VarinfoMap.is_empty glob_vars
69+
6670
let identifier_of_global glob =
6771
match glob with
6872
| GFun (fundec, l) -> {name = fundec.svar.vname; global_t = Fun}
@@ -86,9 +90,9 @@ let (&&>) (prev_result: bool * rename_mapping) (b: bool) : bool * rename_mapping
8690
(prev_equal && b, rename_mapping)
8791

8892
(*Same as Goblist.eq but propagates the rename_mapping*)
89-
let forward_list_equal f l1 l2 (prev_result: rename_mapping) : bool * rename_mapping =
93+
let forward_list_equal ?(propF = (&&>>)) f l1 l2 (prev_result: rename_mapping) : bool * rename_mapping =
9094
if ((List.compare_lengths l1 l2) = 0) then
91-
List.fold_left2 (fun (b, r) x y -> if b then f x y r else (b, r)) (true, prev_result) l1 l2
95+
List.fold_left2 (fun (b, r) x y -> propF (b, r) (f x y)) (true, prev_result) l1 l2
9296
else false, prev_result
9397

9498
(* hack: CIL generates new type names for anonymous types - we want to ignore these *)

src/incremental/compareCFG.ml

Lines changed: 55 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,42 @@ open Queue
33
open Cil
44
include CompareAST
55

6-
(*Non propagating version of &&>>. Discards the new rename_mapping and alwas propagates the one in prev_result*)
6+
(*Non propagating version of &&>>. Discards the new rename_mapping and alwas propagates the one in prev_result. However propagates the renames_on_success*)
77
let (&&<>) (prev_result: bool * rename_mapping) f : bool * rename_mapping =
88
let (prev_equal, prev_rm) = prev_result in
9+
let (a, b, c, _) = prev_rm in
10+
911
if prev_equal then
10-
let (r, _) = f prev_rm in
11-
(r, prev_rm)
12+
let (r, (_, _, _, updated_renames_on_success)) = f prev_rm in
13+
(r, (a, b, c, updated_renames_on_success))
1214
else false, prev_rm
1315

14-
let eq_node (x, fun1) (y, fun2) : bool =
15-
let empty_rename_mapping: rename_mapping = emptyRenameMapping in
16+
let eq_node (x, fun1) (y, fun2) rename_mapping =
1617
match x,y with
17-
| Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) empty_rename_mapping |> fst
18-
| Function f1, Function f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping |> fst
19-
| FunctionEntry f1, FunctionEntry f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping |> fst
20-
| _ -> false
18+
| Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) rename_mapping
19+
| Function f1, Function f2 -> eq_varinfo f1.svar f2.svar rename_mapping
20+
| FunctionEntry f1, FunctionEntry f2 -> eq_varinfo f1.svar f2.svar rename_mapping
21+
| _ -> false, rename_mapping
2122

2223
(* TODO: compare ASMs properly instead of simply always assuming that they are not the same *)
23-
let eq_edge x y =
24-
let empty_rename_mapping: rename_mapping = emptyRenameMapping in
25-
let (r, _) = match x, y with
26-
| Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 empty_rename_mapping &&<> eq_exp rv1 rv2
27-
| Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 empty_rename_mapping &&<> forward_list_equal eq_exp ars1 ars2
28-
| Proc (Some r1,f1,ars1), Proc (Some r2,f2,ars2) ->
29-
eq_lval r1 r2 empty_rename_mapping &&<> eq_exp f1 f2 &&<> forward_list_equal eq_exp ars1 ars2
30-
| Entry f1, Entry f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping
31-
| Ret (None,fd1), Ret (None,fd2) -> eq_varinfo fd1.svar fd2.svar empty_rename_mapping
32-
| Ret (Some r1,fd1), Ret (Some r2,fd2) -> eq_exp r1 r2 empty_rename_mapping &&<> eq_varinfo fd1.svar fd2.svar
33-
| Test (p1,b1), Test (p2,b2) -> eq_exp p1 p2 empty_rename_mapping &&> (b1 = b2)
34-
| ASM _, ASM _ -> false, empty_rename_mapping
35-
| Skip, Skip -> true, empty_rename_mapping
36-
| VDecl v1, VDecl v2 -> eq_varinfo v1 v2 empty_rename_mapping
37-
| _ -> false, empty_rename_mapping
38-
in
39-
r
24+
let eq_edge x y rename_mapping =
25+
match x, y with
26+
| Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 rename_mapping &&<> eq_exp rv1 rv2
27+
| Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 rename_mapping &&<> forward_list_equal eq_exp ars1 ars2
28+
| Proc (Some r1,f1,ars1), Proc (Some r2,f2,ars2) ->
29+
eq_lval r1 r2 rename_mapping &&<> eq_exp f1 f2 &&<> forward_list_equal eq_exp ars1 ars2
30+
| Entry f1, Entry f2 -> eq_varinfo f1.svar f2.svar rename_mapping
31+
| Ret (None,fd1), Ret (None,fd2) -> eq_varinfo fd1.svar fd2.svar rename_mapping
32+
| Ret (Some r1,fd1), Ret (Some r2,fd2) -> eq_exp r1 r2 rename_mapping &&<> eq_varinfo fd1.svar fd2.svar
33+
| Test (p1,b1), Test (p2,b2) -> eq_exp p1 p2 rename_mapping &&> (b1 = b2)
34+
| ASM _, ASM _ -> false, rename_mapping
35+
| Skip, Skip -> true, rename_mapping
36+
| VDecl v1, VDecl v2 -> eq_varinfo v1 v2 rename_mapping
37+
| _ -> false, rename_mapping
38+
4039

4140
(* The order of the edges in the list is relevant. Therefore compare them one to one without sorting first *)
42-
let eq_edge_list xs ys = GobList.equal eq_edge xs ys
41+
let eq_edge_list xs ys = forward_list_equal ~propF:(&&<>) eq_edge xs ys
4342

4443
let to_edge_list ls = List.map (fun (loc, edge) -> edge) ls
4544

@@ -52,38 +51,41 @@ type biDirectionNodeMap = {node1to2: node NH.t; node2to1: node NH.t}
5251
* in the succesors of fromNode2 in the new CFG. Matching node tuples are added to the waitingList to repeat the matching
5352
* process on their successors. If a node from the old CFG can not be matched, it is added to diff and no further
5453
* comparison is done for its successors. The two function entry nodes make up the tuple to start the comparison from. *)
55-
let compareCfgs (module CfgOld : CfgForward) (module CfgNew : CfgForward) fun1 fun2 =
54+
55+
let compareCfgs (module CfgOld : CfgForward) (module CfgNew : CfgForward) fun1 fun2 rename_mapping : biDirectionNodeMap * unit NH.t * rename_mapping =
5656
let diff = NH.create 113 in
5757
let same = {node1to2=NH.create 113; node2to1=NH.create 113} in
5858
let waitingList : (node * node) t = Queue.create () in
5959

60-
let rec compareNext () =
61-
if Queue.is_empty waitingList then ()
60+
let rec compareNext () rename_mapping : rename_mapping =
61+
if Queue.is_empty waitingList then rename_mapping
6262
else
6363
let fromNode1, fromNode2 = Queue.take waitingList in
6464
let outList1 = CfgOld.next fromNode1 in
6565
let outList2 = CfgNew.next fromNode2 in
6666

6767
(* Find a matching edge and successor node for (edgeList1, toNode1) in the list of successors of fromNode2.
6868
* If successful, add the matching node tuple to same, else add toNode1 to the differing nodes. *)
69-
let findMatch (edgeList1, toNode1) =
70-
let rec aux remSuc = match remSuc with
71-
| [] -> NH.replace diff toNode1 ()
69+
let findMatch (edgeList1, toNode1) rename_mapping : rename_mapping =
70+
let rec aux remSuc rename_mapping : rename_mapping = match remSuc with
71+
| [] -> NH.replace diff toNode1 (); rename_mapping
7272
| (locEdgeList2, toNode2)::remSuc' ->
7373
let edgeList2 = to_edge_list locEdgeList2 in
7474
(* TODO: don't allow pseudo return node to be equal to normal return node, could make function unchanged, but have different sallstmts *)
75-
if eq_node (toNode1, fun1) (toNode2, fun2) && eq_edge_list edgeList1 edgeList2 then
75+
let (isEq, updatedRenameMapping) = (true, rename_mapping) &&>> eq_node (toNode1, fun1) (toNode2, fun2) &&>> eq_edge_list edgeList1 edgeList2 in
76+
if isEq then
7677
begin
7778
match NH.find_opt same.node1to2 toNode1 with
78-
| Some n2 -> if not (Node.equal n2 toNode2) then NH.replace diff toNode1 ()
79-
| None -> NH.replace same.node1to2 toNode1 toNode2; NH.replace same.node2to1 toNode2 toNode1; Queue.add (toNode1, toNode2) waitingList
79+
| Some n2 -> if not (Node.equal n2 toNode2) then NH.replace diff toNode1 (); updatedRenameMapping
80+
| None -> NH.replace same.node1to2 toNode1 toNode2; NH.replace same.node2to1 toNode2 toNode1; Queue.add (toNode1, toNode2) waitingList;
81+
updatedRenameMapping
8082
end
81-
else aux remSuc' in
82-
aux outList2 in
83+
else aux remSuc' updatedRenameMapping in
84+
aux outList2 rename_mapping in
8385
(* For a toNode1 from the list of successors of fromNode1, check whether it might have duplicate matches.
8486
* In that case declare toNode1 as differing node. Else, try finding a match in the list of successors
8587
* of fromNode2 in the new CFG using findMatch. *)
86-
let iterOuts (locEdgeList1, toNode1) =
88+
let iterOuts (locEdgeList1, toNode1) rename_mapping : rename_mapping =
8789
let edgeList1 = to_edge_list locEdgeList1 in
8890
(* Differentiate between a possibly duplicate Test(1,false) edge and a single occurence. In the first
8991
* case the edge is directly added to the diff set to avoid undetected ambiguities during the recursive
@@ -94,13 +96,18 @@ let compareCfgs (module CfgOld : CfgForward) (module CfgNew : CfgForward) fun1 f
9496
let posAmbigEdge edgeList = let findTestFalseEdge (ll,_) = testFalseEdge (snd (List.hd ll)) in
9597
let numDuplicates l = List.length (List.find_all findTestFalseEdge l) in
9698
testFalseEdge (List.hd edgeList) && (numDuplicates outList1 > 1 || numDuplicates outList2 > 1) in
97-
if posAmbigEdge edgeList1 then NH.replace diff toNode1 ()
98-
else findMatch (edgeList1, toNode1) in
99-
List.iter iterOuts outList1; compareNext () in
99+
if posAmbigEdge edgeList1 then (NH.replace diff toNode1 (); rename_mapping)
100+
else findMatch (edgeList1, toNode1) rename_mapping in
101+
let updatedRenameMapping = List.fold_left (fun rm e -> iterOuts e rm) rename_mapping outList1 in
102+
compareNext () updatedRenameMapping
103+
in
100104

101105
let entryNode1, entryNode2 = (FunctionEntry fun1, FunctionEntry fun2) in
102-
NH.replace same.node1to2 entryNode1 entryNode2; NH.replace same.node2to1 entryNode2 entryNode1;
103-
Queue.push (entryNode1,entryNode2) waitingList; compareNext (); (same, diff)
106+
NH.replace same.node1to2 entryNode1 entryNode2;
107+
NH.replace same.node2to1 entryNode2 entryNode1;
108+
Queue.push (entryNode1,entryNode2) waitingList;
109+
let updatedRenameMapping = compareNext () rename_mapping in
110+
same, diff, updatedRenameMapping
104111

105112
(* This is the second phase of the CFG comparison of functions. It removes the nodes from the matching node set 'same'
106113
* that have an incoming backedge in the new CFG that can be reached from a differing new node. This is important to
@@ -123,7 +130,8 @@ let reexamine f1 f2 (same : biDirectionNodeMap) (diffNodes1 : unit NH.t) (module
123130
repeat ();
124131
NH.to_seq same.node1to2, NH.to_seq_keys diffNodes1
125132

126-
let compareFun (module CfgOld : CfgForward) (module CfgNew : CfgBidir) fun1 fun2 =
127-
let same, diff = Stats.time "compare-phase1" (fun () -> compareCfgs (module CfgOld) (module CfgNew) fun1 fun2) () in
133+
134+
let compareFun (module CfgOld : CfgForward) (module CfgNew : CfgBidir) fun1 fun2 rename_mapping : (node * node) list * node list * rename_mapping =
135+
let same, diff, rename_mapping = Stats.time "compare-phase1" (fun () -> compareCfgs (module CfgOld) (module CfgNew) fun1 fun2 rename_mapping) () in
128136
let unchanged, diffNodes1 = Stats.time "compare-phase2" (fun () -> reexamine fun1 fun2 same diff (module CfgOld) (module CfgNew)) () in
129-
List.of_seq unchanged, List.of_seq diffNodes1
137+
List.of_seq unchanged, List.of_seq diffNodes1, rename_mapping

src/incremental/compareGlobals.ml

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
open Cil
22
open MyCFG
3+
open CilMaps
34
include CompareAST
45
include CompareCFG
56

@@ -47,19 +48,43 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo
4748
| _, _ -> false, rename_mapping
4849
in
4950

50-
let headerSizeEqual, headerRenameMapping = rename_mapping_aware_compare a.sformals b.sformals (StringMap.empty) in
51-
let actHeaderRenameMapping: rename_mapping = (headerRenameMapping, global_function_rename_mapping, global_var_rename_mapping, ([], [])) in
51+
let unchangedHeader, headerRenameMapping, renamesOnSuccessHeader = match cfgs with
52+
| None -> (
53+
let headerSizeEqual, headerRenameMapping = rename_mapping_aware_compare a.sformals b.sformals (StringMap.empty) in
54+
let actHeaderRenameMapping: rename_mapping = (headerRenameMapping, global_function_rename_mapping, global_var_rename_mapping, ([], [])) in
55+
56+
let (unchangedHeader, (_, _, _, renamesOnSuccessHeader)) = eq_varinfo a.svar b.svar actHeaderRenameMapping &&>>
57+
forward_list_equal eq_varinfo a.sformals b.sformals in
58+
unchangedHeader, headerRenameMapping, renamesOnSuccessHeader
59+
)
60+
| Some _ -> (
61+
let unchangedHeader, headerRenameMapping = eq_varinfo a.svar b.svar emptyRenameMapping &&>>
62+
forward_list_equal eq_varinfo a.sformals b.sformals in
63+
let (_, _, _, renamesOnSuccessHeader) = headerRenameMapping in
64+
65+
(unchangedHeader && is_rename_mapping_empty headerRenameMapping), StringMap.empty, renamesOnSuccessHeader
66+
)
67+
in
5268

53-
let (unchangedHeader, (_, _, _, renamesOnSuccessHeader)) = eq_varinfo a.svar b.svar actHeaderRenameMapping &&>> forward_list_equal eq_varinfo a.sformals b.sformals in
5469
let identical, diffOpt, (_, renamed_method_dependencies, renamed_global_vars_dependencies, renamesOnSuccess) =
5570
if should_reanalyze a then
5671
false, None, emptyRenameMapping
5772
else
5873
(* Here the local variables are checked to be equal *)
59-
let sizeEqual, local_rename = rename_mapping_aware_compare a.slocals b.slocals headerRenameMapping in
60-
let rename_mapping: rename_mapping = (local_rename, global_function_rename_mapping, global_var_rename_mapping, renamesOnSuccessHeader) in
74+
(*flag: when running on cfg, true iff the locals are identical; on ast: if the size of the locals stayed the same*)
75+
let flag, rename_mapping =
76+
match cfgs with
77+
| None -> (
78+
let sizeEqual, local_rename = rename_mapping_aware_compare a.slocals b.slocals headerRenameMapping in
79+
sizeEqual, (local_rename, global_function_rename_mapping, global_var_rename_mapping, renamesOnSuccessHeader)
80+
)
81+
| Some _ -> (
82+
let isEqual, rename_mapping = forward_list_equal eq_varinfo a.slocals b.slocals (StringMap.empty, VarinfoMap.empty, VarinfoMap.empty, renamesOnSuccessHeader) in
83+
isEqual && is_rename_mapping_empty rename_mapping, rename_mapping
84+
)
85+
in
6186

62-
let sameDef = unchangedHeader && sizeEqual in
87+
let sameDef = unchangedHeader && flag in
6388
if not sameDef then
6489
(false, None, emptyRenameMapping)
6590
else
@@ -70,8 +95,8 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo
7095
| Some (cfgOld, (cfgNew, cfgNewBack)) ->
7196
let module CfgOld : MyCFG.CfgForward = struct let next = cfgOld end in
7297
let module CfgNew : MyCFG.CfgBidir = struct let prev = cfgNewBack let next = cfgNew end in
73-
let matches, diffNodes1 = compareFun (module CfgOld) (module CfgNew) a b in
74-
if diffNodes1 = [] then (true, None, emptyRenameMapping)
75-
else (false, Some {unchangedNodes = matches; primObsoleteNodes = diffNodes1}, emptyRenameMapping)
98+
let matches, diffNodes1, updated_rename_mapping = compareFun (module CfgOld) (module CfgNew) a b rename_mapping in
99+
if diffNodes1 = [] then (true, None, updated_rename_mapping)
100+
else (false, Some {unchangedNodes = matches; primObsoleteNodes = diffNodes1}, updated_rename_mapping)
76101
in
77102
identical, unchangedHeader, diffOpt, renamed_method_dependencies, renamed_global_vars_dependencies, renamesOnSuccess

src/incremental/updateCil.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ let update_ids (old_file: file) (ids: max_ids) (new_file: file) (changes: change
4545
old_f.svar.vname <- f.svar.vname;
4646
f.svar.vid <- old_f.svar.vid;
4747
List.iter2 (fun l o_l -> l.vid <- o_l.vid; o_l.vname <- l.vname) f.slocals old_f.slocals;
48-
List.iter2 (fun lo o_f -> lo.vid <- o_f.vid) f.sformals old_f.sformals;
48+
List.iter2 (fun lo o_f -> lo.vid <- o_f.vid; o_f.vname <- lo.vname) f.sformals old_f.sformals;
4949
List.iter2 (fun s o_s -> s.sid <- o_s.sid) f.sallstmts old_f.sallstmts;
5050
List.iter (fun s -> store_node_location (Statement s) (Cilfacade.get_stmtLoc s)) f.sallstmts;
5151

0 commit comments

Comments
 (0)