-
Notifications
You must be signed in to change notification settings - Fork 84
Handle renaming of local variables in incremental analysis (AST) #731
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 31 commits
4256428
27dd10f
6bc0fca
ca6670b
c1e165c
d589278
9eb3f87
d652715
08da3fb
3a11fb9
26d0702
b7fac89
abf871b
4745a3f
7d702f7
c645c68
aed7a3a
9e95ddb
7e89ec2
0c6b9c4
d5979f1
37e5703
3b60fb7
fd691c5
686a3da
0a0ee34
8b28e89
77bd926
7371dc9
7b320c9
40281fe
60468d4
9c8e226
a9d297c
31283c9
b3487ba
a3f48fa
6fe1202
8b5fbd9
fd3f228
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| test=$1 | ||
|
|
||
| base=./tests/incremental | ||
| source=$base/${test}_1.c | ||
| conf=$base/$test.json | ||
| patch1=$base/${test}_1.patch | ||
| patch2=$base/${test}_2.patch | ||
|
|
||
| args="--enable dbg.debug --enable printstats -v" | ||
|
|
||
| cat $source | ||
|
|
||
| ./goblint --conf $conf $args --enable incremental.save $source &> $base/$test.before.log --html | ||
|
|
||
| patch -p0 -b <$patch1 | ||
|
|
||
| cat $source | ||
|
|
||
| ./goblint --conf $conf $args --enable incremental.load --enable incremental.save $source &> $base/$test.after.incr1.log --html | ||
|
|
||
| patch -p0 <$patch2 | ||
|
|
||
| cat $source | ||
|
|
||
| ./goblint --conf $conf $args --enable incremental.load --enable incremental.save --set save_run $base/$test-incrementalrun $source &> $base/$test.after.incr2.log --html | ||
|
|
||
|
|
||
| #./goblint --conf $conf $args --enable incremental.only-rename --set save_run $base/$test-originalrun $source &> $base/$test.after.scratch.log --html | ||
| #./goblint --conf $conf --enable solverdiffs --compare_runs $base/$test-originalrun $base/$test-incrementalrun $source --html | ||
|
|
||
| patch -p0 -b -R <$patch2 | ||
| patch -p0 -b -R <$patch1 | ||
| # rm -r $base/$test-originalrun $base/$test-incrementalrun | ||
| rm -r $base/$test-incrementalrun | ||
|
|
||
| cat $source | ||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| open Cil | ||
| open MyCFG | ||
| open CilMaps | ||
| include CompareAST | ||
| include CompareCFG | ||
|
|
||
|
|
@@ -36,18 +37,45 @@ let should_reanalyze (fdec: Cil.fundec) = | |
| (* If some CFGs of the two functions to be compared are provided, a fine-grained CFG comparison is done that also determines which | ||
| * nodes of the function changed. If on the other hand no CFGs are provided, the "old" AST comparison on the CIL.file is | ||
| * used for functions. Then no information is collected regarding which parts/nodes of the function changed. *) | ||
| let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) = | ||
| let unchangedHeader = eq_varinfo a.svar b.svar && GobList.equal eq_varinfo a.sformals b.sformals in | ||
| let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (global_rename_mapping: method_rename_assumptions) = | ||
| let local_rename_map: (string, string) Hashtbl.t = Hashtbl.create (List.length a.slocals) in | ||
|
|
||
| if (List.length a.slocals) = (List.length b.slocals) then | ||
| List.combine a.slocals b.slocals |> | ||
| List.map (fun x -> match x with (a, b) -> (a.vname, b.vname)) |> | ||
| List.iter (fun pair -> match pair with (a, b) -> Hashtbl.replace local_rename_map a b); | ||
|
|
||
|
|
||
| (* Compares the two varinfo lists, returning as a first element, if the size of the two lists are equal, | ||
| * and as a second a rename_mapping, holding the rename assumptions *) | ||
| let rec rename_mapping_aware_compare (alocals: varinfo list) (blocals: varinfo list) (rename_mapping: string StringMap.t) = match alocals, blocals with | ||
| | [], [] -> true, rename_mapping | ||
| | origLocal :: als, nowLocal :: bls -> | ||
| let new_mapping = StringMap.add origLocal.vname nowLocal.vname rename_mapping in | ||
|
|
||
| (*TODO: maybe optimize this with eq_varinfo*) | ||
| rename_mapping_aware_compare als bls new_mapping | ||
| | _, _ -> false, rename_mapping | ||
| in | ||
|
|
||
| let headerSizeEqual, headerRenameMapping = rename_mapping_aware_compare a.sformals b.sformals (StringMap.empty) in | ||
| let actHeaderRenameMapping = (headerRenameMapping, global_rename_mapping) in | ||
|
|
||
| let unchangedHeader = eq_varinfo a.svar b.svar actHeaderRenameMapping && GobList.equal (eq_varinfo2 actHeaderRenameMapping) a.sformals b.sformals in | ||
| let identical, diffOpt = | ||
| if should_reanalyze a then | ||
| false, None | ||
| else | ||
| let sameDef = unchangedHeader && GobList.equal eq_varinfo a.slocals b.slocals in | ||
| (* Here the local variables are checked to be equal *) | ||
| let sizeEqual, local_rename = rename_mapping_aware_compare a.slocals b.slocals headerRenameMapping in | ||
| let rename_mapping: rename_mapping = (local_rename, global_rename_mapping) in | ||
|
|
||
| let sameDef = unchangedHeader && sizeEqual in | ||
|
||
| if not sameDef then | ||
| (false, None) | ||
| else | ||
| match cfgs with | ||
| | None -> eq_block (a.sbody, a) (b.sbody, b), None | ||
| | None -> eq_block (a.sbody, a) (b.sbody, b) rename_mapping, None | ||
| | Some (cfgOld, (cfgNew, cfgNewBack)) -> | ||
| let module CfgOld : MyCFG.CfgForward = struct let next = cfgOld end in | ||
| let module CfgNew : MyCFG.CfgBidir = struct let prev = cfgNewBack let next = cfgNew end in | ||
|
|
@@ -57,17 +85,44 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) = | |
| in | ||
| identical, unchangedHeader, diffOpt | ||
|
|
||
| let eq_glob (a: global) (b: global) (cfgs : (cfg * (cfg * cfg)) option) = match a, b with | ||
| | GFun (f,_), GFun (g,_) -> eqF f g cfgs | ||
| | GVar (x, init_x, _), GVar (y, init_y, _) -> eq_varinfo x y, false, None (* ignore the init_info - a changed init of a global will lead to a different start state *) | ||
| | GVarDecl (x, _), GVarDecl (y, _) -> eq_varinfo x y, false, None | ||
| let eq_glob (a: global) (b: global) (cfgs : (cfg * (cfg * cfg)) option) (global_rename_mapping: method_rename_assumptions) = match a, b with | ||
| | GFun (f,_), GFun (g,_) -> | ||
| let identical, unchangedHeader, diffOpt = eqF f g cfgs global_rename_mapping in | ||
|
|
||
| identical, unchangedHeader, diffOpt | ||
stilscher marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| | GVar (x, init_x, _), GVar (y, init_y, _) -> eq_varinfo x y (StringMap.empty, VarinfoMap.empty), false, None (* ignore the init_info - a changed init of a global will lead to a different start state *) | ||
| | GVarDecl (x, _), GVarDecl (y, _) -> eq_varinfo x y (StringMap.empty, VarinfoMap.empty), false, None | ||
| | _ -> ignore @@ Pretty.printf "Not comparable: %a and %a\n" Cil.d_global a Cil.d_global b; false, false, None | ||
|
|
||
| let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = | ||
| let cfgs = if GobConfig.get_string "incremental.compare" = "cfg" | ||
| then Some (CfgTools.getCFG oldAST |> fst, CfgTools.getCFG newAST) | ||
| else None in | ||
|
|
||
| let generate_global_rename_mapping map global = | ||
| try | ||
| let ident = identifier_of_global global in | ||
| let old_global = GlobalMap.find ident map in | ||
|
|
||
| match old_global, global with | ||
| | GFun(f, _), GFun (g, _) -> | ||
| let renamed_params: string StringMap.t = if (List.length f.sformals) = (List.length g.sformals) then | ||
| let mappings = List.combine f.sformals g.sformals |> | ||
| List.filter (fun (original, now) -> not (original.vname = now.vname)) |> | ||
| List.map (fun (original, now) -> (original.vname, now.vname)) |> | ||
| List.to_seq | ||
| in | ||
|
|
||
| StringMap.add_seq mappings StringMap.empty | ||
| else StringMap.empty in | ||
|
|
||
| if not (f.svar.vname = g.svar.vname) || (StringMap.cardinal renamed_params) > 0 then | ||
| Some (f.svar, {original_method_name=f.svar.vname; new_method_name=g.svar.vname; parameter_renames=renamed_params}) | ||
| else None | ||
| | _, _ -> None | ||
| with Not_found -> None | ||
| in | ||
|
|
||
| let addGlobal map global = | ||
| try | ||
| let gid = identifier_of_global global in | ||
|
|
@@ -79,14 +134,15 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = | |
| with | ||
| Not_found -> map | ||
| in | ||
|
|
||
| let changes = empty_change_info () in | ||
| global_typ_acc := []; | ||
| let checkUnchanged map global = | ||
| let findChanges map global global_rename_mapping = | ||
| try | ||
| let ident = identifier_of_global global in | ||
| let old_global = GlobalMap.find ident map in | ||
| (* Do a (recursive) equal comparison ignoring location information *) | ||
| let identical, unchangedHeader, diff = eq old_global global cfgs in | ||
| let identical, unchangedHeader, diff = eq old_global global cfgs global_rename_mapping in | ||
| if identical | ||
| then changes.unchanged <- {current = global; old = old_global} :: changes.unchanged | ||
| else changes.changed <- {current = global; old = old_global; unchangedHeader; diff} :: changes.changed | ||
|
|
@@ -100,10 +156,18 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = | |
| (* Store a map from functionNames in the old file to the function definition*) | ||
| let oldMap = Cil.foldGlobals oldAST addGlobal GlobalMap.empty in | ||
| let newMap = Cil.foldGlobals newAST addGlobal GlobalMap.empty in | ||
|
|
||
| let global_rename_mapping: method_rename_assumptions = Cil.foldGlobals newAST (fun (current_global_rename_mapping: method_rename_assumption VarinfoMap.t) global -> | ||
| match generate_global_rename_mapping oldMap global with | ||
| | Some (funVar, rename_mapping) -> VarinfoMap.add funVar rename_mapping current_global_rename_mapping | ||
| | None -> current_global_rename_mapping | ||
| ) VarinfoMap.empty | ||
| in | ||
|
|
||
| (* For each function in the new file, check whether a function with the same name | ||
| already existed in the old version, and whether it is the same function. *) | ||
| Cil.iterGlobals newAST | ||
| (fun glob -> checkUnchanged oldMap glob); | ||
| (fun glob -> findChanges oldMap glob global_rename_mapping); | ||
|
|
||
| (* We check whether functions have been added or removed *) | ||
| Cil.iterGlobals newAST (fun glob -> if not (checkExists oldMap glob) then changes.added <- (glob::changes.added)); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| open Cil | ||
|
|
||
| module VarinfoOrdered = struct | ||
| type t = varinfo | ||
|
|
||
| (*x.svar.uid cannot be used, as they may overlap between old and now AST*) | ||
| let compare (x: varinfo) (y: varinfo) = String.compare x.vname y.vname | ||
| end | ||
|
|
||
|
|
||
| module VarinfoMap = Map.Make(VarinfoOrdered) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| int main() { | ||
| int a = 0; | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| --- tests/incremental/04-var-rename/01-unused_rename.c | ||
| +++ tests/incremental/04-var-rename/01-unused_rename.c | ||
| @@ -1,4 +1,4 @@ | ||
| int main() { | ||
| - int a = 0; | ||
| + int b = 0; | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| local variable a is renamed to b. | ||
| a/b is not used. | ||
| No semantic changes. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| #include<stdio.h> | ||
|
|
||
| //a is renamed to c, but the usage of a is replaced by b | ||
| int main() { | ||
| int a = 0; | ||
| int b = 1; | ||
|
|
||
| printf("Print %d", a); | ||
|
|
||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| --- tests/incremental/04-var-rename/02-rename_and_shuffle.c | ||
| +++ tests/incremental/04-var-rename/02-rename_and_shuffle.c | ||
| @@ -2,10 +2,10 @@ | ||
|
|
||
| //a is renamed to c, but the usage of a is replaced by b | ||
| int main() { | ||
| - int a = 0; | ||
| + int c = 0; | ||
| int b = 1; | ||
|
|
||
| - printf("Print %d", a); | ||
| + printf("Print %d", b); | ||
|
|
||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| a is renamed to c, but the usage of a is replaced by b. | ||
| Semantic changes. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| #include<stdio.h> | ||
|
|
||
| //a is renamed to c, but its usages stay the same | ||
| int main() { | ||
| int a = 0; | ||
| int b = 1; | ||
|
|
||
| printf("Print %d", a); | ||
|
|
||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
|
|
||
| } |
Uh oh!
There was an error while loading. Please reload this page.