From 42564280244a7de4b69f16f4de0796faf12784ef Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Thu, 17 Mar 2022 14:53:35 +0100 Subject: [PATCH 01/90] Added basic test cases for changed variable names. --- tests/incremental/04-var-rename/00-unused_rename.c | 4 ++++ tests/incremental/04-var-rename/00-unused_rename.json | 3 +++ tests/incremental/04-var-rename/00-unused_rename.patch | 8 ++++++++ tests/incremental/04-var-rename/01-nothing.c | 4 ++++ tests/incremental/04-var-rename/01-nothing.json | 3 +++ tests/incremental/04-var-rename/01-nothing.patch | 8 ++++++++ tests/incremental/04-var-rename/diffs/00-unused_rename.c | 4 ++++ tests/incremental/04-var-rename/diffs/01-nothing.c | 5 +++++ 8 files changed, 39 insertions(+) create mode 100644 tests/incremental/04-var-rename/00-unused_rename.c create mode 100644 tests/incremental/04-var-rename/00-unused_rename.json create mode 100644 tests/incremental/04-var-rename/00-unused_rename.patch create mode 100644 tests/incremental/04-var-rename/01-nothing.c create mode 100644 tests/incremental/04-var-rename/01-nothing.json create mode 100644 tests/incremental/04-var-rename/01-nothing.patch create mode 100644 tests/incremental/04-var-rename/diffs/00-unused_rename.c create mode 100644 tests/incremental/04-var-rename/diffs/01-nothing.c diff --git a/tests/incremental/04-var-rename/00-unused_rename.c b/tests/incremental/04-var-rename/00-unused_rename.c new file mode 100644 index 0000000000..31eacd5bf9 --- /dev/null +++ b/tests/incremental/04-var-rename/00-unused_rename.c @@ -0,0 +1,4 @@ +int main() { + int a = 0; + return 0; +} diff --git a/tests/incremental/04-var-rename/00-unused_rename.json b/tests/incremental/04-var-rename/00-unused_rename.json new file mode 100644 index 0000000000..544b7b4ddd --- /dev/null +++ b/tests/incremental/04-var-rename/00-unused_rename.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/00-unused_rename.patch b/tests/incremental/04-var-rename/00-unused_rename.patch new file mode 100644 index 0000000000..d3d15e3bc7 --- /dev/null +++ b/tests/incremental/04-var-rename/00-unused_rename.patch @@ -0,0 +1,8 @@ +--- tests/incremental/04-var-rename/00-unused_rename.c ++++ tests/incremental/04-var-rename/00-unused_rename.c +@@ -1,4 +1,4 @@ + int main() { +- int a = 0; ++ int b = 0; + return 0; + } diff --git a/tests/incremental/04-var-rename/01-nothing.c b/tests/incremental/04-var-rename/01-nothing.c new file mode 100644 index 0000000000..3dc9c8f6e6 --- /dev/null +++ b/tests/incremental/04-var-rename/01-nothing.c @@ -0,0 +1,4 @@ +int main() { + int x = 0; + return 0; +} diff --git a/tests/incremental/04-var-rename/01-nothing.json b/tests/incremental/04-var-rename/01-nothing.json new file mode 100644 index 0000000000..544b7b4ddd --- /dev/null +++ b/tests/incremental/04-var-rename/01-nothing.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/01-nothing.patch b/tests/incremental/04-var-rename/01-nothing.patch new file mode 100644 index 0000000000..663c19abfc --- /dev/null +++ b/tests/incremental/04-var-rename/01-nothing.patch @@ -0,0 +1,8 @@ +--- tests/incremental/04-var-rename/01-nothing.c ++++ tests/incremental/04-var-rename/01-nothing.c +@@ -1,4 +1,5 @@ + int main() { + int x = 0; ++ + return 0; + } diff --git a/tests/incremental/04-var-rename/diffs/00-unused_rename.c b/tests/incremental/04-var-rename/diffs/00-unused_rename.c new file mode 100644 index 0000000000..1fbd3f6638 --- /dev/null +++ b/tests/incremental/04-var-rename/diffs/00-unused_rename.c @@ -0,0 +1,4 @@ +int main() { + int b = 0; + return 0; +} diff --git a/tests/incremental/04-var-rename/diffs/01-nothing.c b/tests/incremental/04-var-rename/diffs/01-nothing.c new file mode 100644 index 0000000000..3c9e6cafd7 --- /dev/null +++ b/tests/incremental/04-var-rename/diffs/01-nothing.c @@ -0,0 +1,5 @@ +int main() { + int x = 0; + + return 0; +} From 27dd10f7455ef488d62e981d3d40c2f71ca54898 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Fri, 18 Mar 2022 15:23:22 +0100 Subject: [PATCH 02/90] Rename detection works for simple cases --- src/incremental/compareAST.ml | 212 ++++++++++-------- src/incremental/compareCFG.ml | 25 ++- src/incremental/compareCIL.ml | 32 ++- .../04-var-rename/02-rename_and_shuffle.c | 11 + .../04-var-rename/02-rename_and_shuffle.json | 3 + .../04-var-rename/02-rename_and_shuffle.patch | 15 ++ .../04-var-rename/03-rename_with_usage.c | 11 + .../04-var-rename/03-rename_with_usage.json | 3 + .../04-var-rename/03-rename_with_usage.patch | 15 ++ .../diffs/02-rename_and_shuffle.c | 11 + .../diffs/03-rename_with_usage.c | 11 + 11 files changed, 241 insertions(+), 108 deletions(-) create mode 100644 tests/incremental/04-var-rename/02-rename_and_shuffle.c create mode 100644 tests/incremental/04-var-rename/02-rename_and_shuffle.json create mode 100644 tests/incremental/04-var-rename/02-rename_and_shuffle.patch create mode 100644 tests/incremental/04-var-rename/03-rename_with_usage.c create mode 100644 tests/incremental/04-var-rename/03-rename_with_usage.json create mode 100644 tests/incremental/04-var-rename/03-rename_with_usage.patch create mode 100644 tests/incremental/04-var-rename/diffs/02-rename_and_shuffle.c create mode 100644 tests/incremental/04-var-rename/diffs/03-rename_with_usage.c diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 1fb1965c7a..ba484693a0 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -5,6 +5,9 @@ type global_type = Fun | Decl | Var and global_identifier = {name: string ; global_t: global_type} [@@deriving ord] +(*context is carried through the stack when comparing the AST. Holds a list of rename assumptions.*) +type context = (string * string) list + let identifier_of_global glob = match glob with | GFun (fundec, l) -> {name = fundec.svar.vname; global_t = Fun} @@ -18,34 +21,39 @@ module GlobalMap = Map.Make(struct (* hack: CIL generates new type names for anonymous types - we want to ignore these *) -let compare_name a b = +let compare_name (a: string) (b: string) = let anon_struct = "__anonstruct_" in let anon_union = "__anonunion_" in + let _ = Printf.printf "Comparing names: %s = %s\n" a b in if a = b then true else BatString.(starts_with a anon_struct && starts_with b anon_struct || starts_with a anon_union && starts_with b anon_union) -let rec eq_constant (a: constant) (b: constant) = match a, b with +let rec eq_constant (context: context) (a: constant) (b: constant) = + match a, b with | CInt (val1, kind1, str1), CInt (val2, kind2, str2) -> Cilint.compare_cilint val1 val2 = 0 && kind1 = kind2 (* Ignore string representation, i.e. 0x2 == 2 *) - | CEnum (exp1, str1, enuminfo1), CEnum (exp2, str2, enuminfo2) -> eq_exp exp1 exp2 (* Ignore name and enuminfo *) + | CEnum (exp1, str1, enuminfo1), CEnum (exp2, str2, enuminfo2) -> eq_exp exp1 exp2 context (* Ignore name and enuminfo *) | a, b -> a = b -and eq_exp (a: exp) (b: exp) = match a,b with - | Const c1, Const c2 -> eq_constant c1 c2 - | Lval lv1, Lval lv2 -> eq_lval lv1 lv2 - | SizeOf typ1, SizeOf typ2 -> eq_typ typ1 typ2 - | SizeOfE exp1, SizeOfE exp2 -> eq_exp exp1 exp2 +and eq_exp2 (context: context) (a: exp) (b: exp) = eq_exp a b context + +and eq_exp (a: exp) (b: exp) (context: context) = + match a, b with + | Const c1, Const c2 -> eq_constant context c1 c2 + | Lval lv1, Lval lv2 -> eq_lval lv1 lv2 context + | SizeOf typ1, SizeOf typ2 -> eq_typ typ1 typ2 context + | SizeOfE exp1, SizeOfE exp2 -> eq_exp exp1 exp2 context | SizeOfStr str1, SizeOfStr str2 -> str1 = str2 (* possibly, having the same length would suffice *) - | AlignOf typ1, AlignOf typ2 -> eq_typ typ1 typ2 - | AlignOfE exp1, AlignOfE exp2 -> eq_exp exp1 exp2 - | UnOp (op1, exp1, typ1), UnOp (op2, exp2, typ2) -> op1 == op2 && eq_exp exp1 exp2 && eq_typ typ1 typ2 - | BinOp (op1, left1, right1, typ1), BinOp (op2, left2, right2, typ2) -> op1 = op2 && eq_exp left1 left2 && eq_exp right1 right2 && eq_typ typ1 typ2 - | CastE (typ1, exp1), CastE (typ2, exp2) -> eq_typ typ1 typ2 && eq_exp exp1 exp2 - | AddrOf lv1, AddrOf lv2 -> eq_lval lv1 lv2 - | StartOf lv1, StartOf lv2 -> eq_lval lv1 lv2 + | AlignOf typ1, AlignOf typ2 -> eq_typ typ1 typ2 context + | AlignOfE exp1, AlignOfE exp2 -> eq_exp exp1 exp2 context + | UnOp (op1, exp1, typ1), UnOp (op2, exp2, typ2) -> op1 == op2 && eq_exp exp1 exp2 context && eq_typ typ1 typ2 context + | BinOp (op1, left1, right1, typ1), BinOp (op2, left2, right2, typ2) -> op1 = op2 && eq_exp left1 left2 context && eq_exp right1 right2 context && eq_typ typ1 typ2 context + | CastE (typ1, exp1), CastE (typ2, exp2) -> eq_typ typ1 typ2 context && eq_exp exp1 exp2 context + | AddrOf lv1, AddrOf lv2 -> eq_lval lv1 lv2 context + | StartOf lv1, StartOf lv2 -> eq_lval lv1 lv2 context | _, _ -> false -and eq_lhost (a: lhost) (b: lhost) = match a, b with - Var v1, Var v2 -> eq_varinfo v1 v2 - | Mem exp1, Mem exp2 -> eq_exp exp1 exp2 +and eq_lhost (a: lhost) (b: lhost) (context: context) = match a, b with + Var v1, Var v2 -> eq_varinfo v1 v2 context + | Mem exp1, Mem exp2 -> eq_exp exp1 exp2 context | _, _ -> false and global_typ_acc: (typ * typ) list ref = ref [] (* TODO: optimize with physical Hashtbl? *) @@ -54,21 +62,21 @@ and mem_typ_acc (a: typ) (b: typ) acc = List.exists (fun p -> match p with (x, y and pretty_length () l = Pretty.num (List.length l) -and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) = +and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (context: context) = 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 *) let r = match a, b with - | TPtr (typ1, attr1), TPtr (typ2, attr2) -> eq_typ_acc typ1 typ2 acc && GobList.equal eq_attribute attr1 attr2 - | TArray (typ1, (Some lenExp1), attr1), TArray (typ2, (Some lenExp2), attr2) -> eq_typ_acc typ1 typ2 acc && eq_exp lenExp1 lenExp2 && GobList.equal eq_attribute attr1 attr2 - | TArray (typ1, None, attr1), TArray (typ2, None, attr2) -> eq_typ_acc typ1 typ2 acc && GobList.equal eq_attribute attr1 attr2 + | TPtr (typ1, attr1), TPtr (typ2, attr2) -> eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_attribute context) attr1 attr2 + | TArray (typ1, (Some lenExp1), attr1), TArray (typ2, (Some lenExp2), attr2) -> eq_typ_acc typ1 typ2 acc context && eq_exp lenExp1 lenExp2 context && GobList.equal (eq_attribute context) attr1 attr2 + | TArray (typ1, None, attr1), TArray (typ2, None, attr2) -> eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_attribute context) attr1 attr2 | TFun (typ1, (Some list1), varArg1, attr1), TFun (typ2, (Some list2), varArg2, attr2) - -> eq_typ_acc typ1 typ2 acc && GobList.equal (eq_args acc) list1 list2 && varArg1 = varArg2 && - GobList.equal eq_attribute attr1 attr2 + -> eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_args context acc) list1 list2 && varArg1 = varArg2 && + GobList.equal (eq_attribute context) attr1 attr2 | TFun (typ1, None, varArg1, attr1), TFun (typ2, None, varArg2, attr2) - -> eq_typ_acc typ1 typ2 acc && varArg1 = varArg2 && - GobList.equal eq_attribute attr1 attr2 - | TNamed (typinfo1, attr1), TNamed (typeinfo2, attr2) -> eq_typ_acc typinfo1.ttype typeinfo2.ttype acc && GobList.equal eq_attribute attr1 attr2 (* Ignore tname, treferenced *) - | TNamed (tinf, attr), b -> eq_typ_acc tinf.ttype b acc (* Ignore tname, treferenced. TODO: dismiss attributes, or not? *) - | a, TNamed (tinf, attr) -> eq_typ_acc a tinf.ttype acc (* Ignore tname, treferenced . TODO: dismiss attributes, or not? *) + -> eq_typ_acc typ1 typ2 acc context && varArg1 = varArg2 && + GobList.equal (eq_attribute context) attr1 attr2 + | TNamed (typinfo1, attr1), TNamed (typeinfo2, attr2) -> eq_typ_acc typinfo1.ttype typeinfo2.ttype acc context && GobList.equal (eq_attribute context) attr1 attr2 (* Ignore tname, treferenced *) + | TNamed (tinf, attr), b -> eq_typ_acc tinf.ttype b acc context (* Ignore tname, treferenced. TODO: dismiss attributes, or not? *) + | a, TNamed (tinf, attr) -> eq_typ_acc a tinf.ttype acc context (* Ignore tname, treferenced . TODO: dismiss attributes, or not? *) (* 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 ( @@ -77,91 +85,106 @@ and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) = ) else ( let acc = (a, b) :: acc in - let res = eq_compinfo compinfo1 compinfo2 acc && GobList.equal eq_attribute attr1 attr2 in + let res = eq_compinfo compinfo1 compinfo2 acc context && GobList.equal (eq_attribute context) attr1 attr2 in if res && compinfo1.cname <> compinfo2.cname then compinfo2.cname <- compinfo1.cname; if res then global_typ_acc := (a, b) :: !global_typ_acc; res ) - | TEnum (enuminfo1, attr1), TEnum (enuminfo2, attr2) -> let res = eq_enuminfo enuminfo1 enuminfo2 && GobList.equal eq_attribute attr1 attr2 in (if res && enuminfo1.ename <> enuminfo2.ename then enuminfo2.ename <- enuminfo1.ename); res - | TBuiltin_va_list attr1, TBuiltin_va_list attr2 -> GobList.equal eq_attribute attr1 attr2 - | TVoid attr1, TVoid attr2 -> GobList.equal eq_attribute attr1 attr2 - | TInt (ik1, attr1), TInt (ik2, attr2) -> ik1 = ik2 && GobList.equal eq_attribute attr1 attr2 - | TFloat (fk1, attr1), TFloat (fk2, attr2) -> fk1 = fk2 && GobList.equal eq_attribute attr1 attr2 + | TEnum (enuminfo1, attr1), TEnum (enuminfo2, attr2) -> let res = eq_enuminfo enuminfo1 enuminfo2 context && GobList.equal (eq_attribute context) attr1 attr2 in (if res && enuminfo1.ename <> enuminfo2.ename then enuminfo2.ename <- enuminfo1.ename); res + | TBuiltin_va_list attr1, TBuiltin_va_list attr2 -> GobList.equal (eq_attribute context) attr1 attr2 + | TVoid attr1, TVoid attr2 -> GobList.equal (eq_attribute context) attr1 attr2 + | TInt (ik1, attr1), TInt (ik2, attr2) -> ik1 = ik2 && GobList.equal (eq_attribute context) attr1 attr2 + | TFloat (fk1, attr1), TFloat (fk2, attr2) -> fk1 = fk2 && GobList.equal (eq_attribute context) attr1 attr2 | _, _ -> false in if Messages.tracing then Messages.traceu "compareast" "eq_typ_acc %a vs %a\n" d_type a d_type b; r -and eq_typ (a: typ) (b: typ) = eq_typ_acc a b [] +and eq_typ (a: typ) (b: typ) (context: context) = eq_typ_acc a b [] context -and eq_eitems (a: string * exp * location) (b: string * exp * location) = match a, b with - (name1, exp1, _l1), (name2, exp2, _l2) -> name1 = name2 && eq_exp exp1 exp2 +and eq_eitems (context: context) (a: string * exp * location) (b: string * exp * location) = match a, b with + (name1, exp1, _l1), (name2, exp2, _l2) -> name1 = name2 && eq_exp exp1 exp2 context (* Ignore location *) -and eq_enuminfo (a: enuminfo) (b: enuminfo) = +and eq_enuminfo (a: enuminfo) (b: enuminfo) (context: context) = compare_name a.ename b.ename && - GobList.equal eq_attribute a.eattr b.eattr && - GobList.equal eq_eitems a.eitems b.eitems + GobList.equal (eq_attribute context) a.eattr b.eattr && + GobList.equal (eq_eitems context) a.eitems b.eitems (* Ignore ereferenced *) -and eq_args (acc: (typ * typ) list) (a: string * typ * attributes) (b: string * typ * attributes) = match a, b with - (name1, typ1, attr1), (name2, typ2, attr2) -> name1 = name2 && eq_typ_acc typ1 typ2 acc && GobList.equal eq_attribute attr1 attr2 +and eq_args (context: context) (acc: (typ * typ) list) (a: string * typ * attributes) (b: string * typ * attributes) = match a, b with + (name1, typ1, attr1), (name2, typ2, attr2) -> name1 = name2 && eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_attribute context) attr1 attr2 -and eq_attrparam (a: attrparam) (b: attrparam) = match a, b with - | ACons (str1, attrparams1), ACons (str2, attrparams2) -> str1 = str2 && GobList.equal eq_attrparam attrparams1 attrparams2 - | ASizeOf typ1, ASizeOf typ2 -> eq_typ typ1 typ2 - | ASizeOfE attrparam1, ASizeOfE attrparam2 -> eq_attrparam attrparam1 attrparam2 +and eq_attrparam (context: context) (a: attrparam) (b: attrparam) = match a, b with + | ACons (str1, attrparams1), ACons (str2, attrparams2) -> str1 = str2 && GobList.equal (eq_attrparam context) attrparams1 attrparams2 + | ASizeOf typ1, ASizeOf typ2 -> eq_typ typ1 typ2 context + | ASizeOfE attrparam1, ASizeOfE attrparam2 -> eq_attrparam context attrparam1 attrparam2 | ASizeOfS typsig1, ASizeOfS typsig2 -> typsig1 = typsig2 - | AAlignOf typ1, AAlignOf typ2 -> eq_typ typ1 typ2 - | AAlignOfE attrparam1, AAlignOfE attrparam2 -> eq_attrparam attrparam1 attrparam2 + | AAlignOf typ1, AAlignOf typ2 -> eq_typ typ1 typ2 context + | AAlignOfE attrparam1, AAlignOfE attrparam2 -> eq_attrparam context attrparam1 attrparam2 | AAlignOfS typsig1, AAlignOfS typsig2 -> typsig1 = typsig2 - | AUnOp (op1, attrparam1), AUnOp (op2, attrparam2) -> op1 = op2 && eq_attrparam attrparam1 attrparam2 - | ABinOp (op1, left1, right1), ABinOp (op2, left2, right2) -> op1 = op2 && eq_attrparam left1 left2 && eq_attrparam right1 right2 - | ADot (attrparam1, str1), ADot (attrparam2, str2) -> eq_attrparam attrparam1 attrparam2 && str1 = str2 - | AStar attrparam1, AStar attrparam2 -> eq_attrparam attrparam1 attrparam2 - | AAddrOf attrparam1, AAddrOf attrparam2 -> eq_attrparam attrparam1 attrparam2 - | AIndex (left1, right1), AIndex (left2, right2) -> eq_attrparam left1 left2 && eq_attrparam right1 right2 - | AQuestion (left1, middle1, right1), AQuestion (left2, middle2, right2) -> eq_attrparam left1 left2 && eq_attrparam middle1 middle2 && eq_attrparam right1 right2 + | AUnOp (op1, attrparam1), AUnOp (op2, attrparam2) -> op1 = op2 && eq_attrparam context attrparam1 attrparam2 + | ABinOp (op1, left1, right1), ABinOp (op2, left2, right2) -> op1 = op2 && eq_attrparam context left1 left2 && eq_attrparam context right1 right2 + | ADot (attrparam1, str1), ADot (attrparam2, str2) -> eq_attrparam context attrparam1 attrparam2 && str1 = str2 + | AStar attrparam1, AStar attrparam2 -> eq_attrparam context attrparam1 attrparam2 + | AAddrOf attrparam1, AAddrOf attrparam2 -> eq_attrparam context attrparam1 attrparam2 + | AIndex (left1, right1), AIndex (left2, right2) -> eq_attrparam context left1 left2 && eq_attrparam context right1 right2 + | AQuestion (left1, middle1, right1), AQuestion (left2, middle2, right2) -> eq_attrparam context left1 left2 && eq_attrparam context middle1 middle2 && eq_attrparam context right1 right2 | a, b -> a = b -and eq_attribute (a: attribute) (b: attribute) = match a, b with - Attr (name1, params1), Attr (name2, params2) -> name1 = name2 && GobList.equal eq_attrparam params1 params2 +and eq_attribute (context: context) (a: attribute) (b: attribute) = match a, b with + | Attr (name1, params1), Attr (name2, params2) -> name1 = name2 && GobList.equal (eq_attrparam context) params1 params2 + +and eq_varinfo2 (context: context) (a: varinfo) (b: varinfo) = eq_varinfo a b context -and eq_varinfo (a: varinfo) (b: varinfo) = a.vname = b.vname && eq_typ a.vtype b.vtype && GobList.equal eq_attribute a.vattr b.vattr && - a.vstorage = b.vstorage && a.vglob = b.vglob && a.vaddrof = b.vaddrof +and bla (context: context) = List.exists (fun x -> match x with (a, b) -> a == "") context + +and eq_varinfo (a: varinfo) (b: varinfo) (context: context) = + let isNamingOk = if a.vname != b.vname then + let existingAssumption: (string*string) option = List.find_opt (fun x -> match x with (original, now) -> original = a.vname) context in + + match existingAssumption with + | Some (original, now) -> now = b.vname + | None -> true (*Var names differ, but there is no assumption, so this can't be good*) + + else true in + + let _ = Printf.printf "Comparing vars: %s = %s\n" a.vname b.vname in + (*a.vname = b.vname*) isNamingOk && eq_typ a.vtype b.vtype context && GobList.equal (eq_attribute context) a.vattr b.vattr && + a.vstorage = b.vstorage && a.vglob = b.vglob && a.vaddrof = b.vaddrof (* Ignore the location, vid, vreferenced, vdescr, vdescrpure, vinline *) (* Accumulator is needed because of recursive types: we have to assume that two types we already encountered in a previous step of the recursion are equivalent *) -and eq_compinfo (a: compinfo) (b: compinfo) (acc: (typ * typ) list) = +and eq_compinfo (a: compinfo) (b: compinfo) (acc: (typ * typ) list) (context: context) = a.cstruct = b.cstruct && compare_name a.cname b.cname && - GobList.equal (fun a b-> eq_fieldinfo a b acc) a.cfields b.cfields && - GobList.equal eq_attribute a.cattr b.cattr && + GobList.equal (fun a b-> eq_fieldinfo a b acc context) a.cfields b.cfields && + GobList.equal (eq_attribute context) a.cattr b.cattr && a.cdefined = b.cdefined (* Ignore ckey, and ignore creferenced *) -and eq_fieldinfo (a: fieldinfo) (b: fieldinfo) (acc: (typ * typ) list)= +and eq_fieldinfo (a: fieldinfo) (b: fieldinfo) (acc: (typ * typ) list) (context: context) = if Messages.tracing then Messages.tracei "compareast" "fieldinfo %s vs %s\n" a.fname b.fname; - let r = a.fname = b.fname && eq_typ_acc a.ftype b.ftype acc && a.fbitfield = b.fbitfield && GobList.equal eq_attribute a.fattr b.fattr in + let r = a.fname = b.fname && eq_typ_acc a.ftype b.ftype acc context && a.fbitfield = b.fbitfield && GobList.equal (eq_attribute context) a.fattr b.fattr in if Messages.tracing then Messages.traceu "compareast" "fieldinfo %s vs %s\n" a.fname b.fname; r -and eq_offset (a: offset) (b: offset) = match a, b with +and eq_offset (a: offset) (b: offset) (context: context) = match a, b with NoOffset, NoOffset -> true - | Field (info1, offset1), Field (info2, offset2) -> eq_fieldinfo info1 info2 [] && eq_offset offset1 offset2 - | Index (exp1, offset1), Index (exp2, offset2) -> eq_exp exp1 exp2 && eq_offset offset1 offset2 + | Field (info1, offset1), Field (info2, offset2) -> eq_fieldinfo info1 info2 [] context && eq_offset offset1 offset2 context + | Index (exp1, offset1), Index (exp2, offset2) -> eq_exp exp1 exp2 context && eq_offset offset1 offset2 context | _, _ -> false -and eq_lval (a: lval) (b: lval) = match a, b with - (host1, off1), (host2, off2) -> eq_lhost host1 host2 && eq_offset off1 off2 +and eq_lval (a: lval) (b: lval) (context: context) = match a, b with + (host1, off1), (host2, off2) -> eq_lhost host1 host2 context && eq_offset off1 off2 context -let eq_instr (a: instr) (b: instr) = match a, b with - | Set (lv1, exp1, _l1, _el1), Set (lv2, exp2, _l2, _el2) -> eq_lval lv1 lv2 && eq_exp exp1 exp2 - | Call (Some lv1, f1, args1, _l1, _el1), Call (Some lv2, f2, args2, _l2, _el2) -> eq_lval lv1 lv2 && eq_exp f1 f2 && GobList.equal eq_exp args1 args2 - | Call (None, f1, args1, _l1, _el1), Call (None, f2, args2, _l2, _el2) -> eq_exp f1 f2 && GobList.equal eq_exp args1 args2 - | Asm (attr1, tmp1, ci1, dj1, rk1, l1), Asm (attr2, tmp2, ci2, dj2, rk2, l2) -> GobList.equal String.equal tmp1 tmp2 && GobList.equal(fun (x1,y1,z1) (x2,y2,z2)-> x1 = x2 && y1 = y2 && eq_lval z1 z2) ci1 ci2 && GobList.equal(fun (x1,y1,z1) (x2,y2,z2)-> x1 = x2 && y1 = y2 && eq_exp z1 z2) dj1 dj2 && GobList.equal String.equal rk1 rk2(* ignore attributes and locations *) - | VarDecl (v1, _l1), VarDecl (v2, _l2) -> eq_varinfo v1 v2 +let eq_instr (context: context) (a: instr) (b: instr) = match a, b with + | Set (lv1, exp1, _l1, _el1), Set (lv2, exp2, _l2, _el2) -> eq_lval lv1 lv2 context && eq_exp exp1 exp2 context + | Call (Some lv1, f1, args1, _l1, _el1), Call (Some lv2, f2, args2, _l2, _el2) -> eq_lval lv1 lv2 context && eq_exp f1 f2 context && GobList.equal (eq_exp2 context) args1 args2 + | Call (None, f1, args1, _l1, _el1), Call (None, f2, args2, _l2, _el2) -> eq_exp f1 f2 context && GobList.equal (eq_exp2 context) args1 args2 + | Asm (attr1, tmp1, ci1, dj1, rk1, l1), Asm (attr2, tmp2, ci2, dj2, rk2, l2) -> GobList.equal String.equal tmp1 tmp2 && GobList.equal(fun (x1,y1,z1) (x2,y2,z2)-> x1 = x2 && y1 = y2 && eq_lval z1 z2 context) ci1 ci2 && GobList.equal(fun (x1,y1,z1) (x2,y2,z2)-> x1 = x2 && y1 = y2 && eq_exp z1 z2 context) dj1 dj2 && GobList.equal String.equal rk1 rk2(* ignore attributes and locations *) + | VarDecl (v1, _l1), VarDecl (v2, _l2) -> eq_varinfo v1 v2 context | _, _ -> false let eq_label (a: label) (b: label) = match a, b with @@ -180,35 +203,42 @@ let eq_stmt_with_location ((a, af): stmt * fundec) ((b, bf): stmt * fundec) = through the cfg and only compares the currently visited node (The cil blocks inside an if statement should not be compared together with its condition to avoid a to early and not precise detection of a changed node inside). Switch, break and continue statements are removed during cfg preparation and therefore need not to be handeled *) -let rec eq_stmtkind ?(cfg_comp = false) ((a, af): stmtkind * fundec) ((b, bf): stmtkind * fundec) = - let eq_block' = fun x y -> if cfg_comp then true else eq_block (x, af) (y, bf) in +let rec eq_stmtkind ?(cfg_comp = false) ((a, af): stmtkind * fundec) ((b, bf): stmtkind * fundec) (context: context) = + let eq_block' = fun x y -> if cfg_comp then true else eq_block (x, af) (y, bf) context in match a, b with - | Instr is1, Instr is2 -> GobList.equal eq_instr is1 is2 - | Return (Some exp1, _l1), Return (Some exp2, _l2) -> eq_exp exp1 exp2 + | Instr is1, Instr is2 -> GobList.equal (eq_instr context) is1 is2 + | Return (Some exp1, _l1), Return (Some exp2, _l2) -> eq_exp exp1 exp2 context | Return (None, _l1), Return (None, _l2) -> true | Return _, Return _ -> false | Goto (st1, _l1), Goto (st2, _l2) -> eq_stmt_with_location (!st1, af) (!st2, bf) | Break _, Break _ -> if cfg_comp then failwith "CompareCFG: Invalid stmtkind in CFG" else true | Continue _, Continue _ -> if cfg_comp then failwith "CompareCFG: Invalid stmtkind in CFG" else true - | If (exp1, then1, else1, _l1, _el1), If (exp2, then2, else2, _l2, _el2) -> eq_exp exp1 exp2 && eq_block' then1 then2 && eq_block' else1 else2 - | Switch (exp1, block1, stmts1, _l1, _el1), Switch (exp2, block2, stmts2, _l2, _el2) -> if cfg_comp then failwith "CompareCFG: Invalid stmtkind in CFG" else eq_exp exp1 exp2 && eq_block' block1 block2 && GobList.equal (fun a b -> eq_stmt (a,af) (b,bf)) stmts1 stmts2 + | If (exp1, then1, else1, _l1, _el1), If (exp2, then2, else2, _l2, _el2) -> eq_exp exp1 exp2 context && eq_block' then1 then2 && eq_block' else1 else2 + | Switch (exp1, block1, stmts1, _l1, _el1), Switch (exp2, block2, stmts2, _l2, _el2) -> if cfg_comp then failwith "CompareCFG: Invalid stmtkind in CFG" else eq_exp exp1 exp2 context && eq_block' block1 block2 && GobList.equal (fun a b -> eq_stmt (a,af) (b,bf) context) stmts1 stmts2 | Loop (block1, _l1, _el1, _con1, _br1), Loop (block2, _l2, _el2, _con2, _br2) -> eq_block' block1 block2 | Block block1, Block block2 -> eq_block' block1 block2 | _, _ -> false -and eq_stmt ?(cfg_comp = false) ((a, af): stmt * fundec) ((b, bf): stmt * fundec) = +and eq_stmt ?(cfg_comp = false) ((a, af): stmt * fundec) ((b, bf): stmt * fundec) (context: context) = GobList.equal eq_label a.labels b.labels && - eq_stmtkind ~cfg_comp (a.skind, af) (b.skind, bf) + eq_stmtkind ~cfg_comp (a.skind, af) (b.skind, bf) context -and eq_block ((a, af): Cil.block * fundec) ((b, bf): Cil.block * fundec) = - a.battrs = b.battrs && GobList.equal (fun x y -> eq_stmt (x, af) (y, bf)) a.bstmts b.bstmts +and eq_block ((a, af): Cil.block * fundec) ((b, bf): Cil.block * fundec) (context: context) = + a.battrs = b.battrs && GobList.equal (fun x y -> eq_stmt (x, af) (y, bf) context) a.bstmts b.bstmts -let rec eq_init (a: init) (b: init) = match a, b with - | SingleInit e1, SingleInit e2 -> eq_exp e1 e2 - | CompoundInit (t1, l1), CompoundInit (t2, l2) -> eq_typ t1 t2 && GobList.equal (fun (o1, i1) (o2, i2) -> eq_offset o1 o2 && eq_init i1 i2) l1 l2 +let rec eq_init (a: init) (b: init) (context: context) = match a, b with + | SingleInit e1, SingleInit e2 -> eq_exp e1 e2 context + | CompoundInit (t1, l1), CompoundInit (t2, l2) -> eq_typ t1 t2 context && GobList.equal (fun (o1, i1) (o2, i2) -> eq_offset o1 o2 context && eq_init i1 i2 context) l1 l2 | _, _ -> false -let eq_initinfo (a: initinfo) (b: initinfo) = match a.init, b.init with - | (Some init_a), (Some init_b) -> eq_init init_a init_b +let eq_initinfo (a: initinfo) (b: initinfo) (context: context) = match a.init, b.init with + | (Some init_a), (Some init_b) -> eq_init init_a init_b context | None, None -> true | _, _ -> false + +let context_to_string (context: context) = "[" ^ (match context with + | [] -> "" + | contextList -> + let elementsAsString = List.map (fun x -> match x with (originalName, nowName) -> "(" ^ originalName ^ " -> " ^ nowName ^ ")") contextList in + List.fold_left (fun a b -> a ^ ", " ^ b) (List.hd elementsAsString) (List.tl elementsAsString) + ) ^ "]" \ No newline at end of file diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index 7f8e5aaa4d..f99b112e37 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -5,24 +5,24 @@ include CompareAST let eq_node (x, fun1) (y, fun2) = match x,y with - | Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) - | Function f1, Function f2 -> eq_varinfo f1.svar f2.svar - | FunctionEntry f1, FunctionEntry f2 -> eq_varinfo f1.svar f2.svar + | Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) [] + | Function f1, Function f2 -> eq_varinfo f1.svar f2.svar [] + | FunctionEntry f1, FunctionEntry f2 -> eq_varinfo f1.svar f2.svar [] | _ -> false (* TODO: compare ASMs properly instead of simply always assuming that they are not the same *) let eq_edge x y = match x, y with - | Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 && eq_exp rv1 rv2 - | Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 && GobList.equal eq_exp ars1 ars2 + | Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 [] && eq_exp rv1 rv2 [] + | Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 [] && GobList.equal (eq_exp2 []) ars1 ars2 | Proc (Some r1,f1,ars1), Proc (Some r2,f2,ars2) -> - eq_lval r1 r2 && eq_exp f1 f2 && GobList.equal eq_exp ars1 ars2 - | Entry f1, Entry f2 -> eq_varinfo f1.svar f2.svar - | Ret (None,fd1), Ret (None,fd2) -> eq_varinfo fd1.svar fd2.svar - | Ret (Some r1,fd1), Ret (Some r2,fd2) -> eq_exp r1 r2 && eq_varinfo fd1.svar fd2.svar - | Test (p1,b1), Test (p2,b2) -> eq_exp p1 p2 && b1 = b2 + eq_lval r1 r2 [] && eq_exp f1 f2 [] && GobList.equal (eq_exp2 []) ars1 ars2 + | Entry f1, Entry f2 -> eq_varinfo f1.svar f2.svar [] + | Ret (None,fd1), Ret (None,fd2) -> eq_varinfo fd1.svar fd2.svar [] + | Ret (Some r1,fd1), Ret (Some r2,fd2) -> eq_exp r1 r2 [] && eq_varinfo fd1.svar fd2.svar [] + | Test (p1,b1), Test (p2,b2) -> eq_exp p1 p2 [] && b1 = b2 | ASM _, ASM _ -> false | Skip, Skip -> true - | VDecl v1, VDecl v2 -> eq_varinfo v1 v2 + | VDecl v1, VDecl v2 -> eq_varinfo v1 v2 [] | SelfLoop, SelfLoop -> true | _ -> false @@ -44,6 +44,8 @@ module NTH = Hashtbl.Make( * process on their successors. If a node from the old CFG can not be matched, it is added to diff and no further * comparison is done for its successors. The two function entry nodes make up the tuple to start the comparison from. *) let compareCfgs (module CfgOld : CfgForward) (module CfgNew : CfgForward) fun1 fun2 = + let _ = Printf.printf "ComparingCfgs" in + let diff = NH.create 113 in let same = NTH.create 113 in let waitingList : (node * node) t = Queue.create () in @@ -127,6 +129,7 @@ let reexamine f1 f2 (same : unit NTH.t) (diffNodes1 : unit NH.t) (module CfgOld (NTH.to_seq_keys same, NH.to_seq_keys diffNodes1, NH.to_seq_keys diffNodes2) let compareFun (module CfgOld : CfgForward) (module CfgNew : CfgForward) fun1 fun2 = + let _ = Printf.printf "Comparing funs" in let same, diff = compareCfgs (module CfgOld) (module CfgNew) fun1 fun2 in let unchanged, diffNodes1, diffNodes2 = reexamine fun1 fun2 same diff (module CfgOld) (module CfgNew) in List.of_seq unchanged, List.of_seq diffNodes1, List.of_seq diffNodes2 diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 26bd27b5a4..808b1ee706 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -32,18 +32,34 @@ let should_reanalyze (fdec: Cil.fundec) = * 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) option) = - let unchangedHeader = eq_varinfo a.svar b.svar && GobList.equal eq_varinfo a.sformals b.sformals in + let unchangedHeader = eq_varinfo a.svar b.svar [] && GobList.equal (eq_varinfo2 []) 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 rec context_aware_compare (alocals: varinfo list) (blocals: varinfo list) (context: context) = match alocals, blocals with + | [], [] -> true, context + | origLocal :: als, nowLocal :: bls -> + let newContext = if origLocal.vname = nowLocal.vname then context else context @ [(origLocal.vname, nowLocal.vname)] in + (*TODO: also call eq_varinfo*) + context_aware_compare als bls newContext + | _, _ -> false, context + in + + let sizeEqual, context = context_aware_compare a.slocals b.slocals [] in + + let _ = Printf.printf "Context=%s\n" (CompareAST.context_to_string context) in + let _ = Printf.printf "SizeEqual=%b; unchangedHeader=%b\n" sizeEqual unchangedHeader 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) context, None | Some (cfgOld, cfgNew) -> + let _ = Printf.printf "compareCIL.eqF: Compaing 2 cfgs now\n" in let module CfgOld : MyCFG.CfgForward = struct let next = cfgOld end in let module CfgNew : MyCFG.CfgForward = struct let next = cfgNew end in let matches, diffNodes1, diffNodes2 = compareFun (module CfgOld) (module CfgNew) a b in @@ -53,12 +69,16 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * cfg) option) = identical, unchangedHeader, diffOpt let eq_glob (a: global) (b: global) (cfgs : (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 + | GFun (f,_), GFun (g,_) -> + let _ = Printf.printf "Comparing funs %s with %s\n" f.svar.vname g.svar.vname in + 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 | _ -> 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 _ = Printf.printf "Comparing Cil files\n" in + let cfgs = if GobConfig.get_string "incremental.compare" = "cfg" then Some (CfgTools.getCFG oldAST |> fst, CfgTools.getCFG newAST |> fst) else None in diff --git a/tests/incremental/04-var-rename/02-rename_and_shuffle.c b/tests/incremental/04-var-rename/02-rename_and_shuffle.c new file mode 100644 index 0000000000..9917738055 --- /dev/null +++ b/tests/incremental/04-var-rename/02-rename_and_shuffle.c @@ -0,0 +1,11 @@ +#include + +//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; +} diff --git a/tests/incremental/04-var-rename/02-rename_and_shuffle.json b/tests/incremental/04-var-rename/02-rename_and_shuffle.json new file mode 100644 index 0000000000..544b7b4ddd --- /dev/null +++ b/tests/incremental/04-var-rename/02-rename_and_shuffle.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/02-rename_and_shuffle.patch b/tests/incremental/04-var-rename/02-rename_and_shuffle.patch new file mode 100644 index 0000000000..5c1dc4785e --- /dev/null +++ b/tests/incremental/04-var-rename/02-rename_and_shuffle.patch @@ -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; + } diff --git a/tests/incremental/04-var-rename/03-rename_with_usage.c b/tests/incremental/04-var-rename/03-rename_with_usage.c new file mode 100644 index 0000000000..2c93c487d8 --- /dev/null +++ b/tests/incremental/04-var-rename/03-rename_with_usage.c @@ -0,0 +1,11 @@ +#include + +//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; +} diff --git a/tests/incremental/04-var-rename/03-rename_with_usage.json b/tests/incremental/04-var-rename/03-rename_with_usage.json new file mode 100644 index 0000000000..544b7b4ddd --- /dev/null +++ b/tests/incremental/04-var-rename/03-rename_with_usage.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/03-rename_with_usage.patch b/tests/incremental/04-var-rename/03-rename_with_usage.patch new file mode 100644 index 0000000000..26fb98b340 --- /dev/null +++ b/tests/incremental/04-var-rename/03-rename_with_usage.patch @@ -0,0 +1,15 @@ +--- tests/incremental/04-var-rename/03-rename_with_usage.c ++++ tests/incremental/04-var-rename/03-rename_with_usage.c +@@ -2,10 +2,10 @@ + + //a is renamed to c, but its usages stay the same + int main() { +- int a = 0; ++ int c = 0; + int b = 1; + +- printf("Print %d", a); ++ printf("Print %d", c); + + return 0; + } diff --git a/tests/incremental/04-var-rename/diffs/02-rename_and_shuffle.c b/tests/incremental/04-var-rename/diffs/02-rename_and_shuffle.c new file mode 100644 index 0000000000..eb54a5c0aa --- /dev/null +++ b/tests/incremental/04-var-rename/diffs/02-rename_and_shuffle.c @@ -0,0 +1,11 @@ +#include + +//a is renamed to c, but the usage of a is replaced by b +int main() { + int c = 0; + int b = 1; + + printf("Print %d", b); + + return 0; +} diff --git a/tests/incremental/04-var-rename/diffs/03-rename_with_usage.c b/tests/incremental/04-var-rename/diffs/03-rename_with_usage.c new file mode 100644 index 0000000000..4676e03447 --- /dev/null +++ b/tests/incremental/04-var-rename/diffs/03-rename_with_usage.c @@ -0,0 +1,11 @@ +#include + +//a is renamed to c, but its usages stay the same +int main() { + int c = 0; + int b = 1; + + printf("Print %d", c); + + return 0; +} From 6bc0fca62df82fd265cbe520be8a352d6c8c6efc Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Sun, 3 Apr 2022 15:58:36 +0200 Subject: [PATCH 03/90] Rename detection for method parameters, too --- src/incremental/compareAST.ml | 7 ++---- src/incremental/compareCIL.ml | 23 ++++++++++++------- .../04-var-rename/04-overwritten_var.c | 9 ++++++++ .../04-var-rename/05-renamed-param.patch | 10 ++++++++ .../04-var-rename/05-renamed_param.c | 8 +++++++ .../04-var-rename/05-renamed_param.json | 3 +++ .../04-var-rename/diffs/04-overwritten_var.c | 11 +++++++++ .../04-var-rename/diffs/05-renamed_param.c | 8 +++++++ 8 files changed, 66 insertions(+), 13 deletions(-) create mode 100644 tests/incremental/04-var-rename/04-overwritten_var.c create mode 100644 tests/incremental/04-var-rename/05-renamed-param.patch create mode 100644 tests/incremental/04-var-rename/05-renamed_param.c create mode 100644 tests/incremental/04-var-rename/05-renamed_param.json create mode 100644 tests/incremental/04-var-rename/diffs/04-overwritten_var.c create mode 100644 tests/incremental/04-var-rename/diffs/05-renamed_param.c diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index ba484693a0..344ff1781b 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -24,7 +24,6 @@ module GlobalMap = Map.Make(struct let compare_name (a: string) (b: string) = let anon_struct = "__anonstruct_" in let anon_union = "__anonunion_" in - let _ = Printf.printf "Comparing names: %s = %s\n" a b in if a = b then true else BatString.(starts_with a anon_struct && starts_with b anon_struct || starts_with a anon_union && starts_with b anon_union) let rec eq_constant (context: context) (a: constant) (b: constant) = @@ -35,7 +34,7 @@ let rec eq_constant (context: context) (a: constant) (b: constant) = and eq_exp2 (context: context) (a: exp) (b: exp) = eq_exp a b context -and eq_exp (a: exp) (b: exp) (context: context) = +and eq_exp (a: exp) (b: exp) (context: context) = match a, b with | Const c1, Const c2 -> eq_constant context c1 c2 | Lval lv1, Lval lv2 -> eq_lval lv1 lv2 context @@ -139,8 +138,6 @@ and eq_attribute (context: context) (a: attribute) (b: attribute) = match a, b w and eq_varinfo2 (context: context) (a: varinfo) (b: varinfo) = eq_varinfo a b context -and bla (context: context) = List.exists (fun x -> match x with (a, b) -> a == "") context - and eq_varinfo (a: varinfo) (b: varinfo) (context: context) = let isNamingOk = if a.vname != b.vname then let existingAssumption: (string*string) option = List.find_opt (fun x -> match x with (original, now) -> original = a.vname) context in @@ -151,7 +148,7 @@ and eq_varinfo (a: varinfo) (b: varinfo) (context: context) = else true in - let _ = Printf.printf "Comparing vars: %s = %s\n" a.vname b.vname in + (*let _ = Printf.printf "Comparing vars: %s = %s\n" a.vname b.vname in *) (*a.vname = b.vname*) isNamingOk && eq_typ a.vtype b.vtype context && GobList.equal (eq_attribute context) a.vattr b.vattr && a.vstorage = b.vstorage && a.vglob = b.vglob && a.vaddrof = b.vaddrof (* Ignore the location, vid, vreferenced, vdescr, vdescrpure, vinline *) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 808b1ee706..65d462dac2 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -32,13 +32,10 @@ let should_reanalyze (fdec: Cil.fundec) = * 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) option) = - let unchangedHeader = eq_varinfo a.svar b.svar [] && GobList.equal (eq_varinfo2 []) a.sformals b.sformals in - let identical, diffOpt = - if should_reanalyze a then - false, None - else - (* Here the local variables are checked to be equal *) - let rec context_aware_compare (alocals: varinfo list) (blocals: varinfo list) (context: context) = match alocals, blocals with + + (* Compares the two varinfo lists, returning as a first element, if the size of the two lists are equal, + * and as a second a context, holding the rename assumptions *) + let rec context_aware_compare (alocals: varinfo list) (blocals: varinfo list) (context: context) = match alocals, blocals with | [], [] -> true, context | origLocal :: als, nowLocal :: bls -> let newContext = if origLocal.vname = nowLocal.vname then context else context @ [(origLocal.vname, nowLocal.vname)] in @@ -47,7 +44,17 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * cfg) option) = | _, _ -> false, context in - let sizeEqual, context = context_aware_compare a.slocals b.slocals [] in + let headerSizeEqual, headerContext = context_aware_compare a.sformals b.sformals [] in + + let unchangedHeader = eq_varinfo a.svar b.svar headerContext && GobList.equal (eq_varinfo2 []) a.sformals b.sformals in + let identical, diffOpt = + if should_reanalyze a then + false, None + else + (* Here the local variables are checked to be equal *) + + + let sizeEqual, context = context_aware_compare a.slocals b.slocals headerContext in let _ = Printf.printf "Context=%s\n" (CompareAST.context_to_string context) in let _ = Printf.printf "SizeEqual=%b; unchangedHeader=%b\n" sizeEqual unchangedHeader in diff --git a/tests/incremental/04-var-rename/04-overwritten_var.c b/tests/incremental/04-var-rename/04-overwritten_var.c new file mode 100644 index 0000000000..80956dea76 --- /dev/null +++ b/tests/incremental/04-var-rename/04-overwritten_var.c @@ -0,0 +1,9 @@ +int main() { + int i = 0; + + for(int i = 0; i < 10; i++) { + + } + + return 0; +} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/05-renamed-param.patch b/tests/incremental/04-var-rename/05-renamed-param.patch new file mode 100644 index 0000000000..944566b05c --- /dev/null +++ b/tests/incremental/04-var-rename/05-renamed-param.patch @@ -0,0 +1,10 @@ +--- tests/incremental/04-var-rename/05-renamed_param.c ++++ tests/incremental/04-var-rename/05-renamed_param.c +@@ -1,5 +1,5 @@ +-void method(int a) { +- int c = a; ++void method(int b) { ++ int c = b; + } + + int main() { diff --git a/tests/incremental/04-var-rename/05-renamed_param.c b/tests/incremental/04-var-rename/05-renamed_param.c new file mode 100644 index 0000000000..72fdfaf0e9 --- /dev/null +++ b/tests/incremental/04-var-rename/05-renamed_param.c @@ -0,0 +1,8 @@ +void method(int a) { + int c = a; +} + +int main() { + method(0); + return 0; +} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/05-renamed_param.json b/tests/incremental/04-var-rename/05-renamed_param.json new file mode 100644 index 0000000000..544b7b4ddd --- /dev/null +++ b/tests/incremental/04-var-rename/05-renamed_param.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/diffs/04-overwritten_var.c b/tests/incremental/04-var-rename/diffs/04-overwritten_var.c new file mode 100644 index 0000000000..240bdbb8ad --- /dev/null +++ b/tests/incremental/04-var-rename/diffs/04-overwritten_var.c @@ -0,0 +1,11 @@ +int main() { + int i = 0; + + for(int a = 0; a < 10; a++) { + i++; + } + + assert(i < 11); + + return 0; +} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/diffs/05-renamed_param.c b/tests/incremental/04-var-rename/diffs/05-renamed_param.c new file mode 100644 index 0000000000..198bd82496 --- /dev/null +++ b/tests/incremental/04-var-rename/diffs/05-renamed_param.c @@ -0,0 +1,8 @@ +void method(int b) { + int c = b; +} + +int main() { + method(0); + return 0; +} \ No newline at end of file From ca6670b3323fe0117eb0d498945d467cc611bcfc Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Tue, 5 Apr 2022 16:53:57 +0200 Subject: [PATCH 04/90] Renaming of method params should work now. --- src/incremental/compareAST.ml | 101 ++++++++++++++---- src/incremental/compareCFG.ml | 27 ++--- src/incremental/compareCIL.ml | 66 +++++++++--- src/util/server.ml | 2 +- ...med-param.patch => 05-renamed_param.patch} | 0 .../06-renamed_param_usage_changed.c | 11 ++ .../06-renamed_param_usage_changed.json | 3 + .../06-renamed_param_usage_changed.patch | 10 ++ .../diffs/06-renamed_param_usage_changed.c | 11 ++ 9 files changed, 183 insertions(+), 48 deletions(-) rename tests/incremental/04-var-rename/{05-renamed-param.patch => 05-renamed_param.patch} (100%) create mode 100644 tests/incremental/04-var-rename/06-renamed_param_usage_changed.c create mode 100644 tests/incremental/04-var-rename/06-renamed_param_usage_changed.json create mode 100644 tests/incremental/04-var-rename/06-renamed_param_usage_changed.patch create mode 100644 tests/incremental/04-var-rename/diffs/06-renamed_param_usage_changed.c diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 344ff1781b..f471cfa61d 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -5,8 +5,41 @@ type global_type = Fun | Decl | Var and global_identifier = {name: string ; global_t: global_type} [@@deriving ord] +type local_rename = string * string +(**) +type method_context = {original_method_name: string; new_method_name: string; parameter_renames: (string * string) list} + (*context is carried through the stack when comparing the AST. Holds a list of rename assumptions.*) -type context = (string * string) list +type context = (local_rename list) * (method_context list) + +(*Compares two names, being aware of the context. Returns true iff: + 1. there is a rename for name1 -> name2 = rename(name1) + 2. there is no rename for name1 -> name1 = name2*) +let context_aware_name_comparison (name1: string) (name2: string) (context: context) = + let (local_c, method_c) = context in + let existingAssumption: (string*string) option = List.find_opt (fun x -> match x with (original, now) -> original = name1) local_c in + + match existingAssumption with + | Some (original, now) -> + (*Printf.printf "Assumption is: %s -> %s\n" original now;*) + now = name2 + | None -> + (*Printf.printf "No assumption when %s, %s, %b\n" name1 name2 (name1 = name2);*) + name1 = name2 (*Var names differ, but there is no assumption, so this can't be good*) + +let string_tuple_to_string (tuple: (string * string) list) = "[" ^ (tuple |> + List.map (fun x -> match x with (first, second) -> "(" ^ first ^ " -> " ^ second ^ ")") |> + String.concat ", ") ^ "]" + +let context_to_string (context: context) = + let (local, methods) = context in + let local_string = string_tuple_to_string local in + let methods_string: string = methods |> + List.map (fun x -> match x with {original_method_name; new_method_name; parameter_renames} -> + "(methodName: " ^ original_method_name ^ " -> " ^ new_method_name ^ + "; renamed_params=" ^ string_tuple_to_string parameter_renames ^ ")") |> + String.concat ", " in + "(local=" ^ local_string ^ "; methods=[" ^ methods_string ^ "])" let identifier_of_global glob = match glob with @@ -68,7 +101,9 @@ and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (context: context) = | TArray (typ1, (Some lenExp1), attr1), TArray (typ2, (Some lenExp2), attr2) -> eq_typ_acc typ1 typ2 acc context && eq_exp lenExp1 lenExp2 context && GobList.equal (eq_attribute context) attr1 attr2 | TArray (typ1, None, attr1), TArray (typ2, None, attr2) -> eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_attribute context) attr1 attr2 | TFun (typ1, (Some list1), varArg1, attr1), TFun (typ2, (Some list2), varArg2, attr2) - -> eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_args context acc) list1 list2 && varArg1 = varArg2 && + -> + Printf.printf "eq_typ_acc=1:%b;2:%b;3:%b;4:%b;\n" ( eq_typ_acc typ1 typ2 acc context) (GobList.equal (eq_args context acc) list1 list2) (varArg1 = varArg2) (GobList.equal (eq_attribute context) attr1 attr2); + eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_args context acc) list1 list2 && varArg1 = varArg2 && GobList.equal (eq_attribute context) attr1 attr2 | TFun (typ1, None, varArg1, attr1), TFun (typ2, None, varArg2, attr2) -> eq_typ_acc typ1 typ2 acc context && varArg1 = varArg2 && @@ -114,7 +149,13 @@ and eq_enuminfo (a: enuminfo) (b: enuminfo) (context: context) = (* Ignore ereferenced *) and eq_args (context: context) (acc: (typ * typ) list) (a: string * typ * attributes) (b: string * typ * attributes) = match a, b with - (name1, typ1, attr1), (name2, typ2, attr2) -> name1 = name2 && eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_attribute context) attr1 attr2 + (name1, typ1, attr1), (name2, typ2, attr2) -> + Printf.printf "Comparing args: %s <-> %s\n" name1 name2; + Printf.printf "Current context: %s\n" (context_to_string context); + let result = context_aware_name_comparison name1 name2 context && eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_attribute context) attr1 attr2 in + + Printf.printf "Back\n"; + result and eq_attrparam (context: context) (a: attrparam) (b: attrparam) = match a, b with | ACons (str1, attrparams1), ACons (str2, attrparams2) -> str1 = str2 && GobList.equal (eq_attrparam context) attrparams1 attrparams2 @@ -139,18 +180,41 @@ and eq_attribute (context: context) (a: attribute) (b: attribute) = match a, b w and eq_varinfo2 (context: context) (a: varinfo) (b: varinfo) = eq_varinfo a b context and eq_varinfo (a: varinfo) (b: varinfo) (context: context) = - let isNamingOk = if a.vname != b.vname then - let existingAssumption: (string*string) option = List.find_opt (fun x -> match x with (original, now) -> original = a.vname) context in + Printf.printf "Comp %s with %s\n" a.vname b.vname; + let isNamingOk = context_aware_name_comparison a.vname b.vname context in + + (*If the following is a method call, we need to check if we have a mapping for that method call. *) + let (_, method_contexts) = context in + let typ_context, did_context_switch = match b.vtype with + | TFun(_, _, _, _) -> ( + let new_locals = List.find_opt (fun x -> match x with + | {original_method_name; new_method_name; parameter_renames} -> original_method_name = a.vname && new_method_name = b.vname + ) method_contexts in + + match new_locals with + | Some locals -> + Printf.printf "Performing context switch. New context=%s\n" (context_to_string (locals.parameter_renames, method_contexts)); + (locals.parameter_renames, method_contexts), true + | None -> ([], method_contexts), false + ) + | _ -> context, false + in + + let typeCheck = eq_typ a.vtype b.vtype typ_context in + let attrCheck = GobList.equal (eq_attribute context) a.vattr b.vattr in - match existingAssumption with - | Some (original, now) -> now = b.vname - | None -> true (*Var names differ, but there is no assumption, so this can't be good*) + let _ = Printf.printf "eq_varinfo: 0=%b;1=%b;2=%b;3=%b;4=%b;5=%b\n" isNamingOk typeCheck attrCheck (a.vstorage = b.vstorage) (a.vglob = b.vglob) (a.vaddrof = b.vaddrof) in - else true in + + (*let _ = if isNamingOk then a.vname <- b.vname in*) (*let _ = Printf.printf "Comparing vars: %s = %s\n" a.vname b.vname in *) - (*a.vname = b.vname*) isNamingOk && eq_typ a.vtype b.vtype context && GobList.equal (eq_attribute context) a.vattr b.vattr && - a.vstorage = b.vstorage && a.vglob = b.vglob && a.vaddrof = b.vaddrof + (*a.vname = b.vname*) + let result = isNamingOk && typeCheck && attrCheck && + a.vstorage = b.vstorage && a.vglob = b.vglob && a.vaddrof = b.vaddrof in + if did_context_switch then Printf.printf "Undo context switch \n"; + + result (* Ignore the location, vid, vreferenced, vdescr, vdescrpure, vinline *) (* Accumulator is needed because of recursive types: we have to assume that two types we already encountered in a previous step of the recursion are equivalent *) @@ -178,8 +242,10 @@ and eq_lval (a: lval) (b: lval) (context: context) = match a, b with let eq_instr (context: context) (a: instr) (b: instr) = match a, b with | Set (lv1, exp1, _l1, _el1), Set (lv2, exp2, _l2, _el2) -> eq_lval lv1 lv2 context && eq_exp exp1 exp2 context - | Call (Some lv1, f1, args1, _l1, _el1), Call (Some lv2, f2, args2, _l2, _el2) -> eq_lval lv1 lv2 context && eq_exp f1 f2 context && GobList.equal (eq_exp2 context) args1 args2 - | Call (None, f1, args1, _l1, _el1), Call (None, f2, args2, _l2, _el2) -> eq_exp f1 f2 context && GobList.equal (eq_exp2 context) args1 args2 + | Call (Some lv1, f1, args1, _l1, _el1), Call (Some lv2, f2, args2, _l2, _el2) -> + eq_lval lv1 lv2 context && eq_exp f1 f2 context && GobList.equal (eq_exp2 context) args1 args2 + | Call (None, f1, args1, _l1, _el1), Call (None, f2, args2, _l2, _el2) -> + eq_exp f1 f2 context && GobList.equal (eq_exp2 context) args1 args2 | Asm (attr1, tmp1, ci1, dj1, rk1, l1), Asm (attr2, tmp2, ci2, dj2, rk2, l2) -> GobList.equal String.equal tmp1 tmp2 && GobList.equal(fun (x1,y1,z1) (x2,y2,z2)-> x1 = x2 && y1 = y2 && eq_lval z1 z2 context) ci1 ci2 && GobList.equal(fun (x1,y1,z1) (x2,y2,z2)-> x1 = x2 && y1 = y2 && eq_exp z1 z2 context) dj1 dj2 && GobList.equal String.equal rk1 rk2(* ignore attributes and locations *) | VarDecl (v1, _l1), VarDecl (v2, _l2) -> eq_varinfo v1 v2 context | _, _ -> false @@ -231,11 +297,4 @@ let rec eq_init (a: init) (b: init) (context: context) = match a, b with let eq_initinfo (a: initinfo) (b: initinfo) (context: context) = match a.init, b.init with | (Some init_a), (Some init_b) -> eq_init init_a init_b context | None, None -> true - | _, _ -> false - -let context_to_string (context: context) = "[" ^ (match context with - | [] -> "" - | contextList -> - let elementsAsString = List.map (fun x -> match x with (originalName, nowName) -> "(" ^ originalName ^ " -> " ^ nowName ^ ")") contextList in - List.fold_left (fun a b -> a ^ ", " ^ b) (List.hd elementsAsString) (List.tl elementsAsString) - ) ^ "]" \ No newline at end of file + | _, _ -> false \ No newline at end of file diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index f99b112e37..25b5f64ccf 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -4,25 +4,28 @@ open Cil include CompareAST let eq_node (x, fun1) (y, fun2) = + let empty_context: context = ([], []) in match x,y with - | Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) [] - | Function f1, Function f2 -> eq_varinfo f1.svar f2.svar [] - | FunctionEntry f1, FunctionEntry f2 -> eq_varinfo f1.svar f2.svar [] + | Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) empty_context + | Function f1, Function f2 -> eq_varinfo f1.svar f2.svar empty_context + | FunctionEntry f1, FunctionEntry f2 -> eq_varinfo f1.svar f2.svar empty_context | _ -> false (* TODO: compare ASMs properly instead of simply always assuming that they are not the same *) -let eq_edge x y = match x, y with - | Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 [] && eq_exp rv1 rv2 [] - | Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 [] && GobList.equal (eq_exp2 []) ars1 ars2 +let eq_edge x y = + let empty_context: context = ([], []) in + match x, y with + | Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 empty_context && eq_exp rv1 rv2 empty_context + | Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 empty_context && GobList.equal (eq_exp2 empty_context) ars1 ars2 | Proc (Some r1,f1,ars1), Proc (Some r2,f2,ars2) -> - eq_lval r1 r2 [] && eq_exp f1 f2 [] && GobList.equal (eq_exp2 []) ars1 ars2 - | Entry f1, Entry f2 -> eq_varinfo f1.svar f2.svar [] - | Ret (None,fd1), Ret (None,fd2) -> eq_varinfo fd1.svar fd2.svar [] - | Ret (Some r1,fd1), Ret (Some r2,fd2) -> eq_exp r1 r2 [] && eq_varinfo fd1.svar fd2.svar [] - | Test (p1,b1), Test (p2,b2) -> eq_exp p1 p2 [] && b1 = b2 + eq_lval r1 r2 empty_context && eq_exp f1 f2 empty_context && GobList.equal (eq_exp2 empty_context) ars1 ars2 + | Entry f1, Entry f2 -> eq_varinfo f1.svar f2.svar empty_context + | Ret (None,fd1), Ret (None,fd2) -> eq_varinfo fd1.svar fd2.svar empty_context + | Ret (Some r1,fd1), Ret (Some r2,fd2) -> eq_exp r1 r2 empty_context && eq_varinfo fd1.svar fd2.svar empty_context + | Test (p1,b1), Test (p2,b2) -> eq_exp p1 p2 empty_context && b1 = b2 | ASM _, ASM _ -> false | Skip, Skip -> true - | VDecl v1, VDecl v2 -> eq_varinfo v1 v2 [] + | VDecl v1, VDecl v2 -> eq_varinfo v1 v2 empty_context | SelfLoop, SelfLoop -> true | _ -> false diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 65d462dac2..013ba21248 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -31,11 +31,11 @@ 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) option) = +let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * cfg) option) (global_context: method_context list) = (* Compares the two varinfo lists, returning as a first element, if the size of the two lists are equal, * and as a second a context, holding the rename assumptions *) - let rec context_aware_compare (alocals: varinfo list) (blocals: varinfo list) (context: context) = match alocals, blocals with + let rec context_aware_compare (alocals: varinfo list) (blocals: varinfo list) (context: local_rename list) = match alocals, blocals with | [], [] -> true, context | origLocal :: als, nowLocal :: bls -> let newContext = if origLocal.vname = nowLocal.vname then context else context @ [(origLocal.vname, nowLocal.vname)] in @@ -45,8 +45,12 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * cfg) option) = in let headerSizeEqual, headerContext = context_aware_compare a.sformals b.sformals [] in + let actHeaderContext = (headerContext, global_context) in + Printf.printf "Header context=%s\n" (context_to_string actHeaderContext); - let unchangedHeader = eq_varinfo a.svar b.svar headerContext && GobList.equal (eq_varinfo2 []) a.sformals b.sformals in + let unchangedHeader = eq_varinfo a.svar b.svar actHeaderContext && GobList.equal (eq_varinfo2 actHeaderContext) a.sformals b.sformals in + let _ = Printf.printf "unchangedHeader=%b\n" unchangedHeader in + let _ = Printf.printf "part1=%b; part2=%b \n" (eq_varinfo a.svar b.svar actHeaderContext) (GobList.equal (eq_varinfo2 actHeaderContext) a.sformals b.sformals) in let identical, diffOpt = if should_reanalyze a then false, None @@ -54,19 +58,21 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * cfg) option) = (* Here the local variables are checked to be equal *) - let sizeEqual, context = context_aware_compare a.slocals b.slocals headerContext in + let sizeEqual, local_rename = context_aware_compare a.slocals b.slocals headerContext in + let context: context = (local_rename, global_context) in let _ = Printf.printf "Context=%s\n" (CompareAST.context_to_string context) in - let _ = Printf.printf "SizeEqual=%b; unchangedHeader=%b\n" sizeEqual unchangedHeader 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) context, None + | None -> + Printf.printf "Comparing blocks\n"; + eq_block (a.sbody, a) (b.sbody, b) context, None | Some (cfgOld, cfgNew) -> - let _ = Printf.printf "compareCIL.eqF: Compaing 2 cfgs now\n" in + Printf.printf "compareCIL.eqF: Compaing 2 cfgs now\n"; let module CfgOld : MyCFG.CfgForward = struct let next = cfgOld end in let module CfgNew : MyCFG.CfgForward = struct let next = cfgNew end in let matches, diffNodes1, diffNodes2 = compareFun (module CfgOld) (module CfgNew) a b in @@ -75,12 +81,16 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * cfg) option) = in identical, unchangedHeader, diffOpt -let eq_glob (a: global) (b: global) (cfgs : (cfg * cfg) option) = match a, b with +let eq_glob (a: global) (b: global) (cfgs : (cfg * cfg) option) (global_context: method_context list) = match a, b with | GFun (f,_), GFun (g,_) -> let _ = Printf.printf "Comparing funs %s with %s\n" f.svar.vname g.svar.vname in - 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 identical, unchangedHeader, diffOpt = eqF f g cfgs global_context in + + let _ = Printf.printf "identical=%b; unchangedHeader=%b\n" identical unchangedHeader in + + identical, unchangedHeader, diffOpt + | 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 | _ -> 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) = @@ -90,6 +100,26 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = then Some (CfgTools.getCFG oldAST |> fst, CfgTools.getCFG newAST |> fst) else None in + let generate_global_context 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 * string) list = if (List.length f.sformals) = (List.length g.sformals) then + 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)) + else [] in + + if not (f.svar.vname = g.svar.vname) || (List.length renamed_params) > 0 then + Option.some {original_method_name=f.svar.vname; new_method_name=g.svar.vname; parameter_renames=renamed_params} + else Option.none + | _, _ -> Option.none + with Not_found -> Option.none + in + let addGlobal map global = try let gid = identifier_of_global global in @@ -101,14 +131,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 checkUnchanged map global global_context = 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_context in if identical then changes.unchanged <- global :: changes.unchanged else changes.changed <- {current = global; old = old_global; unchangedHeader; diff} :: changes.changed @@ -122,10 +153,17 @@ 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_context: method_context list = Cil.foldGlobals newAST (fun (current_global_context: method_context list) global -> + match generate_global_context oldMap global with + | Some context -> current_global_context @ [context] + | None -> current_global_context + ) [] 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 -> checkUnchanged oldMap glob global_context); (* 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)); diff --git a/src/util/server.ml b/src/util/server.ml index e64d4b36e6..3c3cad9e60 100644 --- a/src/util/server.ml +++ b/src/util/server.ml @@ -116,7 +116,7 @@ let reparse (s: t) = (* Only called when the file has not been reparsed, so we can skip the expensive CFG comparison. *) let virtual_changes file = - let eq (glob: Cil.global) _ _ = match glob with + let eq (glob: Cil.global) _ _ _ = match glob with | GFun (fdec, _) -> CompareCIL.should_reanalyze fdec, false, None | _ -> false, false, None in diff --git a/tests/incremental/04-var-rename/05-renamed-param.patch b/tests/incremental/04-var-rename/05-renamed_param.patch similarity index 100% rename from tests/incremental/04-var-rename/05-renamed-param.patch rename to tests/incremental/04-var-rename/05-renamed_param.patch diff --git a/tests/incremental/04-var-rename/06-renamed_param_usage_changed.c b/tests/incremental/04-var-rename/06-renamed_param_usage_changed.c new file mode 100644 index 0000000000..aed642566c --- /dev/null +++ b/tests/incremental/04-var-rename/06-renamed_param_usage_changed.c @@ -0,0 +1,11 @@ +//This test should mark foo and main as changed + +void foo(int a, int b) { + int x = a; + int y = b; +} + +int main() { + foo(3, 4); + return 0; +} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/06-renamed_param_usage_changed.json b/tests/incremental/04-var-rename/06-renamed_param_usage_changed.json new file mode 100644 index 0000000000..544b7b4ddd --- /dev/null +++ b/tests/incremental/04-var-rename/06-renamed_param_usage_changed.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/06-renamed_param_usage_changed.patch b/tests/incremental/04-var-rename/06-renamed_param_usage_changed.patch new file mode 100644 index 0000000000..a93e45c4c5 --- /dev/null +++ b/tests/incremental/04-var-rename/06-renamed_param_usage_changed.patch @@ -0,0 +1,10 @@ +--- tests/incremental/04-var-rename/06-renamed_param_usage_changed.c ++++ tests/incremental/04-var-rename/06-renamed_param_usage_changed.c +@@ -1,6 +1,6 @@ + //This test should mark foo and main as changed + +-void foo(int a, int b) { ++void foo(int b, int a) { + int x = a; + int y = b; + } diff --git a/tests/incremental/04-var-rename/diffs/06-renamed_param_usage_changed.c b/tests/incremental/04-var-rename/diffs/06-renamed_param_usage_changed.c new file mode 100644 index 0000000000..0bf42f645e --- /dev/null +++ b/tests/incremental/04-var-rename/diffs/06-renamed_param_usage_changed.c @@ -0,0 +1,11 @@ +//This test should mark foo and main as changed + +void foo(int b, int a) { + int x = a; + int y = b; +} + +int main() { + foo(3, 4); + return 0; +} \ No newline at end of file From c1e165cb9553291b4d7c4db818a2af703e1f0ba7 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Mon, 18 Apr 2022 15:52:27 +0200 Subject: [PATCH 05/90] Renaming of results does work for the log files. --- src/analyses/base.ml | 35 ++++++++++++++++++- src/incremental/compareAST.ml | 35 ++++++++++--------- src/incremental/compareCIL.ml | 34 ++++++++++++------ src/util/server.ml | 1 + .../04-var-rename/04-overwritten_var.c | 9 ----- .../04-var-rename/04-renamed_assert.c | 7 ++++ .../04-var-rename/04-renamed_assert.json | 3 ++ .../04-var-rename/04-renamed_assert.patch | 13 +++++++ .../04-var-rename/07-method_rename.c | 10 ++++++ .../04-var-rename/07-method_rename.json | 3 ++ .../04-var-rename/07-method_rename.patch | 15 ++++++++ .../04-var-rename/diffs/04-overwritten_var.c | 11 ------ .../04-var-rename/diffs/04-renamed_assert.c | 7 ++++ .../04-var-rename/diffs/07-method_rename.c | 10 ++++++ tests/incremental/04-var-rename/test.c | 15 ++++++++ 15 files changed, 160 insertions(+), 48 deletions(-) delete mode 100644 tests/incremental/04-var-rename/04-overwritten_var.c create mode 100644 tests/incremental/04-var-rename/04-renamed_assert.c create mode 100644 tests/incremental/04-var-rename/04-renamed_assert.json create mode 100644 tests/incremental/04-var-rename/04-renamed_assert.patch create mode 100644 tests/incremental/04-var-rename/07-method_rename.c create mode 100644 tests/incremental/04-var-rename/07-method_rename.json create mode 100644 tests/incremental/04-var-rename/07-method_rename.patch delete mode 100644 tests/incremental/04-var-rename/diffs/04-overwritten_var.c create mode 100644 tests/incremental/04-var-rename/diffs/04-renamed_assert.c create mode 100644 tests/incremental/04-var-rename/diffs/07-method_rename.c create mode 100644 tests/incremental/04-var-rename/test.c diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 2e846da692..202a15c5ab 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2015,6 +2015,36 @@ struct | _ -> [] let assert_fn ctx e should_warn change = + let parent_function: fundec = Node.find_fundec ctx.node in + + (*Performs the actual rename on lvals for renamed local variables.*) + let rename_lval lhost offset = + let new_lhost = match lhost with + | Var varinfo -> + varinfo.vname <- CompareCIL.get_local_rename parent_function.svar.vname varinfo.vname; + Var varinfo + | _ -> lhost + in + (new_lhost, offset) + in + + (*Recusivly go through the expression and rename all occurences of local variables. TODO: What happens with global vars*) + let rec rename_exp (exp: exp) = match exp with + | Lval (lhost, offset) -> Lval (rename_lval lhost offset) + | Real e -> Real (rename_exp e) + | Imag e -> Imag (rename_exp e) + | SizeOfE e -> SizeOfE (rename_exp e) + | AlignOfE e -> AlignOfE (rename_exp e) + | UnOp (unop, e, typ) -> UnOp (unop, rename_exp e, typ) + | BinOp (binop, e1, e2, typ) -> BinOp (binop, rename_exp e1, rename_exp e2, typ) + | Question (e1, e2, e3, typ) -> Question (rename_exp e1, rename_exp e2, rename_exp e3, typ) + | CastE (typ, e) -> CastE (typ, rename_exp e) + | AddrOf (lhost, offset) -> AddrOf (rename_lval lhost offset) + | StartOf (lhost, offset) -> StartOf (rename_lval lhost offset) + (*TODO: AddrOfLabel?*) + | _ -> exp + in + let check_assert e st = match eval_rv (Analyses.ask_of_ctx ctx) ctx.global st e with @@ -2027,7 +2057,7 @@ struct | `Bot -> `Bot | _ -> `Top in - let expr = sprint d_exp e in + let expr = sprint d_exp (rename_exp e) in let warn warn_fn ?annot msg = if should_warn then if get_bool "dbg.regression" then ( (* This only prints unexpected results (with the difference) as indicated by the comment behind the assert (same as used by the regression test script). *) let loc = !M.current_loc in @@ -2087,6 +2117,9 @@ struct invalidate ~ctx (Analyses.ask_of_ctx ctx) gs st addrs let special ctx (lv:lval option) (f: varinfo) (args: exp list) = + Printf.printf "special: varinfo=%s\n" f.vname; + List.iter (fun x -> ignore @@ Pretty.printf "%a\n" Cil.d_exp x;) args; + 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; diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index f471cfa61d..d9361ec082 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -101,9 +101,7 @@ and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (context: context) = | TArray (typ1, (Some lenExp1), attr1), TArray (typ2, (Some lenExp2), attr2) -> eq_typ_acc typ1 typ2 acc context && eq_exp lenExp1 lenExp2 context && GobList.equal (eq_attribute context) attr1 attr2 | TArray (typ1, None, attr1), TArray (typ2, None, attr2) -> eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_attribute context) attr1 attr2 | TFun (typ1, (Some list1), varArg1, attr1), TFun (typ2, (Some list2), varArg2, attr2) - -> - Printf.printf "eq_typ_acc=1:%b;2:%b;3:%b;4:%b;\n" ( eq_typ_acc typ1 typ2 acc context) (GobList.equal (eq_args context acc) list1 list2) (varArg1 = varArg2) (GobList.equal (eq_attribute context) attr1 attr2); - eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_args context acc) list1 list2 && varArg1 = varArg2 && + -> eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_args context acc) list1 list2 && varArg1 = varArg2 && GobList.equal (eq_attribute context) attr1 attr2 | TFun (typ1, None, varArg1, attr1), TFun (typ2, None, varArg2, attr2) -> eq_typ_acc typ1 typ2 acc context && varArg1 = varArg2 && @@ -150,12 +148,7 @@ and eq_enuminfo (a: enuminfo) (b: enuminfo) (context: context) = and eq_args (context: context) (acc: (typ * typ) list) (a: string * typ * attributes) (b: string * typ * attributes) = match a, b with (name1, typ1, attr1), (name2, typ2, attr2) -> - Printf.printf "Comparing args: %s <-> %s\n" name1 name2; - Printf.printf "Current context: %s\n" (context_to_string context); - let result = context_aware_name_comparison name1 name2 context && eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_attribute context) attr1 attr2 in - - Printf.printf "Back\n"; - result + context_aware_name_comparison name1 name2 context && eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_attribute context) attr1 attr2 and eq_attrparam (context: context) (a: attrparam) (b: attrparam) = match a, b with | ACons (str1, attrparams1), ACons (str2, attrparams2) -> str1 = str2 && GobList.equal (eq_attrparam context) attrparams1 attrparams2 @@ -180,11 +173,24 @@ and eq_attribute (context: context) (a: attribute) (b: attribute) = match a, b w and eq_varinfo2 (context: context) (a: varinfo) (b: varinfo) = eq_varinfo a b context and eq_varinfo (a: varinfo) (b: varinfo) (context: context) = - Printf.printf "Comp %s with %s\n" a.vname b.vname; - let isNamingOk = context_aware_name_comparison a.vname b.vname context in + (*Printf.printf "Comp %s with %s\n" a.vname b.vname;*) - (*If the following is a method call, we need to check if we have a mapping for that method call. *) let (_, method_contexts) = context in + + (*When we compare function names, we can directly compare the naming from the context if it exists.*) + let isNamingOk = match b.vtype with + | TFun(_, _, _, _) -> ( + let specific_method_context = List.find_opt (fun x -> match x with + | {original_method_name; new_method_name; parameter_renames} -> original_method_name = a.vname && new_method_name = b.vname + ) method_contexts in + match specific_method_context with + | Some method_context -> method_context.original_method_name = a.vname && method_context.new_method_name = b.vname + | None -> a.vname = b.vname + ) + | _ -> context_aware_name_comparison a.vname b.vname context + in + + (*If the following is a method call, we need to check if we have a mapping for that method call. *) let typ_context, did_context_switch = match b.vtype with | TFun(_, _, _, _) -> ( let new_locals = List.find_opt (fun x -> match x with @@ -193,7 +199,7 @@ and eq_varinfo (a: varinfo) (b: varinfo) (context: context) = match new_locals with | Some locals -> - Printf.printf "Performing context switch. New context=%s\n" (context_to_string (locals.parameter_renames, method_contexts)); + (*Printf.printf "Performing context switch. New context=%s\n" (context_to_string (locals.parameter_renames, method_contexts));*) (locals.parameter_renames, method_contexts), true | None -> ([], method_contexts), false ) @@ -203,9 +209,6 @@ and eq_varinfo (a: varinfo) (b: varinfo) (context: context) = let typeCheck = eq_typ a.vtype b.vtype typ_context in let attrCheck = GobList.equal (eq_attribute context) a.vattr b.vattr in - let _ = Printf.printf "eq_varinfo: 0=%b;1=%b;2=%b;3=%b;4=%b;5=%b\n" isNamingOk typeCheck attrCheck (a.vstorage = b.vstorage) (a.vglob = b.vglob) (a.vaddrof = b.vaddrof) in - - (*let _ = if isNamingOk then a.vname <- b.vname in*) (*let _ = Printf.printf "Comparing vars: %s = %s\n" a.vname b.vname in *) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 013ba21248..b95c56694a 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -3,6 +3,8 @@ open MyCFG include CompareAST include CompareCFG +let rename_map: (string, (string, string) Hashtbl.t) Hashtbl.t ref = ref (Hashtbl.create 100) + type nodes_diff = { unchangedNodes: (node * node) list; primObsoleteNodes: node list; (** primary obsolete nodes -> all obsolete nodes are reachable from these *) @@ -23,6 +25,14 @@ type change_info = { mutable added: global list } +let store_local_rename (function_name: string) (rename_table: (string, string) Hashtbl.t) = + Hashtbl.add !rename_map function_name rename_table + +(*Returnes the rename if one exists, or param_name when no entry exists.*) +let get_local_rename (function_name: string) (param_name: string) = match (Hashtbl.find_opt !rename_map function_name) with + | Some (local_map) -> Option.value (Hashtbl.find_opt local_map param_name) ~default:param_name + | None -> param_name + let empty_change_info () : change_info = {added = []; removed = []; changed = []; unchanged = []} let should_reanalyze (fdec: Cil.fundec) = @@ -32,6 +42,12 @@ let should_reanalyze (fdec: Cil.fundec) = * 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) option) (global_context: method_context list) = + let local_rename_map: (string, string) Hashtbl.t = Hashtbl.create (List.length a.slocals) in + + 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.add 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 context, holding the rename assumptions *) @@ -46,11 +62,8 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * cfg) option) (global_cont let headerSizeEqual, headerContext = context_aware_compare a.sformals b.sformals [] in let actHeaderContext = (headerContext, global_context) in - Printf.printf "Header context=%s\n" (context_to_string actHeaderContext); let unchangedHeader = eq_varinfo a.svar b.svar actHeaderContext && GobList.equal (eq_varinfo2 actHeaderContext) a.sformals b.sformals in - let _ = Printf.printf "unchangedHeader=%b\n" unchangedHeader in - let _ = Printf.printf "part1=%b; part2=%b \n" (eq_varinfo a.svar b.svar actHeaderContext) (GobList.equal (eq_varinfo2 actHeaderContext) a.sformals b.sformals) in let identical, diffOpt = if should_reanalyze a then false, None @@ -69,7 +82,6 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * cfg) option) (global_cont else match cfgs with | None -> - Printf.printf "Comparing blocks\n"; eq_block (a.sbody, a) (b.sbody, b) context, None | Some (cfgOld, cfgNew) -> Printf.printf "compareCIL.eqF: Compaing 2 cfgs now\n"; @@ -79,15 +91,15 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * cfg) option) (global_cont if diffNodes1 = [] && diffNodes2 = [] then (true, None) else (false, Some {unchangedNodes = matches; primObsoleteNodes = diffNodes1; primNewNodes = diffNodes2}) in + + if (identical) then store_local_rename a.svar.vname local_rename_map; + identical, unchangedHeader, diffOpt let eq_glob (a: global) (b: global) (cfgs : (cfg * cfg) option) (global_context: method_context list) = match a, b with | GFun (f,_), GFun (g,_) -> - let _ = Printf.printf "Comparing funs %s with %s\n" f.svar.vname g.svar.vname in let identical, unchangedHeader, diffOpt = eqF f g cfgs global_context in - let _ = Printf.printf "identical=%b; unchangedHeader=%b\n" identical unchangedHeader in - identical, unchangedHeader, diffOpt | 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 @@ -114,10 +126,10 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = else [] in if not (f.svar.vname = g.svar.vname) || (List.length renamed_params) > 0 then - Option.some {original_method_name=f.svar.vname; new_method_name=g.svar.vname; parameter_renames=renamed_params} - else Option.none - | _, _ -> Option.none - with Not_found -> Option.none + Some {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 = diff --git a/src/util/server.ml b/src/util/server.ml index 3c3cad9e60..dc7115b828 100644 --- a/src/util/server.ml +++ b/src/util/server.ml @@ -124,6 +124,7 @@ let virtual_changes file = let increment_data (s: t) file reparsed = match !Serialize.server_solver_data with | Some solver_data when reparsed -> + Printf.printf "Increment_data\n"; let _, changes = VersionLookup.updateMap s.file file s.version_map in let old_data = Some { Analyses.cil_file = s.file; solver_data } in s.max_ids <- UpdateCil.update_ids s.file s.max_ids file s.version_map changes; diff --git a/tests/incremental/04-var-rename/04-overwritten_var.c b/tests/incremental/04-var-rename/04-overwritten_var.c deleted file mode 100644 index 80956dea76..0000000000 --- a/tests/incremental/04-var-rename/04-overwritten_var.c +++ /dev/null @@ -1,9 +0,0 @@ -int main() { - int i = 0; - - for(int i = 0; i < 10; i++) { - - } - - return 0; -} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/04-renamed_assert.c b/tests/incremental/04-var-rename/04-renamed_assert.c new file mode 100644 index 0000000000..55d83e7229 --- /dev/null +++ b/tests/incremental/04-var-rename/04-renamed_assert.c @@ -0,0 +1,7 @@ +int main() { + int myVar = 0; + + assert(myVar < 11); + + return 0; +} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/04-renamed_assert.json b/tests/incremental/04-var-rename/04-renamed_assert.json new file mode 100644 index 0000000000..544b7b4ddd --- /dev/null +++ b/tests/incremental/04-var-rename/04-renamed_assert.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/04-renamed_assert.patch b/tests/incremental/04-var-rename/04-renamed_assert.patch new file mode 100644 index 0000000000..9644dcf6a1 --- /dev/null +++ b/tests/incremental/04-var-rename/04-renamed_assert.patch @@ -0,0 +1,13 @@ +--- tests/incremental/04-var-rename/04-renamed_assert.c ++++ tests/incremental/04-var-rename/04-renamed_assert.c +@@ -1,7 +1,7 @@ + int main() { +- int myVar = 0; ++ int myRenamedVar = 0; + +- assert(myVar < 11); ++ assert(myRenamedVar < 11); + + return 0; + } +\ Kein Zeilenumbruch am Dateiende. diff --git a/tests/incremental/04-var-rename/07-method_rename.c b/tests/incremental/04-var-rename/07-method_rename.c new file mode 100644 index 0000000000..84ce2d8621 --- /dev/null +++ b/tests/incremental/04-var-rename/07-method_rename.c @@ -0,0 +1,10 @@ +//Method is renamed with all of its usages. Test should say no changes. + +int foo() { + return 12; +} + +int main() { + foo(); + return 0; +} diff --git a/tests/incremental/04-var-rename/07-method_rename.json b/tests/incremental/04-var-rename/07-method_rename.json new file mode 100644 index 0000000000..544b7b4ddd --- /dev/null +++ b/tests/incremental/04-var-rename/07-method_rename.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/07-method_rename.patch b/tests/incremental/04-var-rename/07-method_rename.patch new file mode 100644 index 0000000000..e55d61e986 --- /dev/null +++ b/tests/incremental/04-var-rename/07-method_rename.patch @@ -0,0 +1,15 @@ +--- tests/incremental/04-var-rename/07-method_rename.c ++++ tests/incremental/04-var-rename/07-method_rename.c +@@ -1,10 +1,10 @@ + //Method is renamed with all of its usages. Test should say no changes. + +-int foo() { ++int bar() { + return 12; + } + + int main() { +- foo(); ++ bar(); + return 0; + } diff --git a/tests/incremental/04-var-rename/diffs/04-overwritten_var.c b/tests/incremental/04-var-rename/diffs/04-overwritten_var.c deleted file mode 100644 index 240bdbb8ad..0000000000 --- a/tests/incremental/04-var-rename/diffs/04-overwritten_var.c +++ /dev/null @@ -1,11 +0,0 @@ -int main() { - int i = 0; - - for(int a = 0; a < 10; a++) { - i++; - } - - assert(i < 11); - - return 0; -} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/diffs/04-renamed_assert.c b/tests/incremental/04-var-rename/diffs/04-renamed_assert.c new file mode 100644 index 0000000000..8f74e36a13 --- /dev/null +++ b/tests/incremental/04-var-rename/diffs/04-renamed_assert.c @@ -0,0 +1,7 @@ +int main() { + int j = 0; + + assert(j < 11); + + return 0; +} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/diffs/07-method_rename.c b/tests/incremental/04-var-rename/diffs/07-method_rename.c new file mode 100644 index 0000000000..0d6c2aa9b9 --- /dev/null +++ b/tests/incremental/04-var-rename/diffs/07-method_rename.c @@ -0,0 +1,10 @@ +//Method is renamed with all of its usages. Test should say no changes. + +int bar() { + return 12; +} + +int main() { + bar(); + return 0; +} diff --git a/tests/incremental/04-var-rename/test.c b/tests/incremental/04-var-rename/test.c new file mode 100644 index 0000000000..f51eb0d6f7 --- /dev/null +++ b/tests/incremental/04-var-rename/test.c @@ -0,0 +1,15 @@ +void foo() { + int i = 0; + + for(int i = 0; i < 10; i++); +} + +void bar() { + int i = 0; +} + +int main() { + foo(); + bar(); + return 0; +} \ No newline at end of file From d589278c780659e583205de0144f6da312aff7e2 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Tue, 19 Apr 2022 11:44:07 +0200 Subject: [PATCH 06/90] Added multiple incremental runs test --- scripts/test-incremental-multiple.sh | 36 +++++++++++++ src/analyses/base.ml | 8 +++ src/incremental/compareCIL.ml | 50 ++++++++++++++++--- .../04-var-rename/08-2_incremental_runs.json | 3 ++ .../04-var-rename/08-2_incremental_runs_1.c | 8 +++ .../08-2_incremental_runs_1.patch | 14 ++++++ .../04-var-rename/08-2_incremental_runs_2.c | 8 +++ .../08-2_incremental_runs_2.patch | 14 ++++++ .../04-var-rename/08-2_incremental_runs_3.c | 8 +++ .../04-var-rename/09-2_ir_with_changes.json | 3 ++ .../04-var-rename/09-2_ir_with_changes_1.c | 17 +++++++ .../09-2_ir_with_changes_1.patch | 22 ++++++++ .../04-var-rename/09-2_ir_with_changes_2.c | 17 +++++++ .../09-2_ir_with_changes_2.patch | 22 ++++++++ .../04-var-rename/09-2_ir_with_changes_3.c | 18 +++++++ 15 files changed, 240 insertions(+), 8 deletions(-) create mode 100644 scripts/test-incremental-multiple.sh create mode 100644 tests/incremental/04-var-rename/08-2_incremental_runs.json create mode 100644 tests/incremental/04-var-rename/08-2_incremental_runs_1.c create mode 100644 tests/incremental/04-var-rename/08-2_incremental_runs_1.patch create mode 100644 tests/incremental/04-var-rename/08-2_incremental_runs_2.c create mode 100644 tests/incremental/04-var-rename/08-2_incremental_runs_2.patch create mode 100644 tests/incremental/04-var-rename/08-2_incremental_runs_3.c create mode 100644 tests/incremental/04-var-rename/09-2_ir_with_changes.json create mode 100644 tests/incremental/04-var-rename/09-2_ir_with_changes_1.c create mode 100644 tests/incremental/04-var-rename/09-2_ir_with_changes_1.patch create mode 100644 tests/incremental/04-var-rename/09-2_ir_with_changes_2.c create mode 100644 tests/incremental/04-var-rename/09-2_ir_with_changes_2.patch create mode 100644 tests/incremental/04-var-rename/09-2_ir_with_changes_3.c diff --git a/scripts/test-incremental-multiple.sh b/scripts/test-incremental-multiple.sh new file mode 100644 index 0000000000..87b7e150ce --- /dev/null +++ b/scripts/test-incremental-multiple.sh @@ -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 --set save_run $base/$test-incrementalrun $source &> $base/$test.after.incr1.log --html + +patch -p0 <$patch2 + +cat $source + +./goblint --conf $conf $args --enable incremental.load --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 diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 202a15c5ab..6e218bbfa0 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2015,6 +2015,14 @@ struct | _ -> [] let assert_fn ctx e should_warn change = + let _ = Hashtbl.iter (fun fun_name map -> + begin + Printf.printf "%s: [" fun_name; + Hashtbl.iter (fun from tox -> Printf.printf "%s -> %s; " from tox) map; + Printf.printf "]\n"; + end + ) !CompareCIL.rename_map in + let parent_function: fundec = Node.find_fundec ctx.node in (*Performs the actual rename on lvals for renamed local variables.*) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index b95c56694a..762d6fbac5 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -3,8 +3,12 @@ open MyCFG include CompareAST include CompareCFG +(*Maps the function name to a table of it's local variable and parameter renames. The rename table has key of original name and value of renamed name.*) let rename_map: (string, (string, string) Hashtbl.t) Hashtbl.t ref = ref (Hashtbl.create 100) +(*Same as rename_map, but maps renamed name to original name instead.*) +let reverse_rename_map: (string, (string, string) Hashtbl.t) Hashtbl.t ref = ref (Hashtbl.create 100) + type nodes_diff = { unchangedNodes: (node * node) list; primObsoleteNodes: node list; (** primary obsolete nodes -> all obsolete nodes are reachable from these *) @@ -26,13 +30,36 @@ type change_info = { } let store_local_rename (function_name: string) (rename_table: (string, string) Hashtbl.t) = - Hashtbl.add !rename_map function_name rename_table + begin + Hashtbl.add !rename_map function_name rename_table; + let reverse_rename_table = Hashtbl.create (Hashtbl.length !rename_map) in + Hashtbl.iter (fun original_name new_name -> Hashtbl.add reverse_rename_table new_name original_name) rename_table; + Hashtbl.add !reverse_rename_map function_name reverse_rename_table; + end (*Returnes the rename if one exists, or param_name when no entry exists.*) let get_local_rename (function_name: string) (param_name: string) = match (Hashtbl.find_opt !rename_map function_name) with | Some (local_map) -> Option.value (Hashtbl.find_opt local_map param_name) ~default:param_name | None -> param_name +let get_orignal_name (function_name: string) (new_var_name: string) = match (Hashtbl.find_opt !reverse_rename_map function_name) with + | Some (reverse_map) -> Option.value (Hashtbl.find_opt reverse_map new_var_name) ~default:new_var_name + |None -> new_var_name + +let show_rename_map = + let show_local_rename_map (local_rename_map: (string, string) Hashtbl.t) = + let rename_string = Seq.map (fun (orig, new_name) -> orig ^ " -> " ^ new_name) (Hashtbl.to_seq local_rename_map) |> + List.of_seq in + String.concat ", " rename_string + in + + Hashtbl.to_seq !rename_map |> + Seq.iter (fun (fun_name, map) -> Printf.printf "%s=%d" fun_name (Hashtbl.length map)); + + let function_strings = Seq.map (fun (fun_name, map) -> fun_name ^ ": [" ^ (show_local_rename_map map) ^ "]") (Hashtbl.to_seq !rename_map) |> List.of_seq in + + String.concat ", " function_strings + let empty_change_info () : change_info = {added = []; removed = []; changed = []; unchanged = []} let should_reanalyze (fdec: Cil.fundec) = @@ -44,9 +71,10 @@ let should_reanalyze (fdec: Cil.fundec) = let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * cfg) option) (global_context: method_context list) = let local_rename_map: (string, string) Hashtbl.t = Hashtbl.create (List.length a.slocals) in - 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.add local_rename_map a b); + 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.add local_rename_map a b); (* Compares the two varinfo lists, returning as a first element, if the size of the two lists are equal, @@ -146,14 +174,20 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = let changes = empty_change_info () in global_typ_acc := []; - let checkUnchanged map global global_context = + let findChanges map global global_context = 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 global_context in - if identical - then changes.unchanged <- global :: changes.unchanged + if identical then + (*Rename*) + (*match global with + | GFun (fundec, _) -> fundec.slocals |> + List.iter (fun local -> local.vname <- get_orignal_name fundec.svar.vname local.vname); + | _ -> ();*) + + changes.unchanged <- global :: changes.unchanged else changes.changed <- {current = global; old = old_global; unchangedHeader; diff} :: changes.changed with Not_found -> () (* Global was no variable or function, it does not belong into the map *) in @@ -175,7 +209,7 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = (* 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 global_context); + (fun glob -> findChanges oldMap glob global_context); (* 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)); diff --git a/tests/incremental/04-var-rename/08-2_incremental_runs.json b/tests/incremental/04-var-rename/08-2_incremental_runs.json new file mode 100644 index 0000000000..544b7b4ddd --- /dev/null +++ b/tests/incremental/04-var-rename/08-2_incremental_runs.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/08-2_incremental_runs_1.c b/tests/incremental/04-var-rename/08-2_incremental_runs_1.c new file mode 100644 index 0000000000..d9b5afdd19 --- /dev/null +++ b/tests/incremental/04-var-rename/08-2_incremental_runs_1.c @@ -0,0 +1,8 @@ +int main() { + int varFirstIteration = 0; + + varFirstIteration++; + + assert(varFirstIteration < 10); + return 0; +} diff --git a/tests/incremental/04-var-rename/08-2_incremental_runs_1.patch b/tests/incremental/04-var-rename/08-2_incremental_runs_1.patch new file mode 100644 index 0000000000..191b335f3c --- /dev/null +++ b/tests/incremental/04-var-rename/08-2_incremental_runs_1.patch @@ -0,0 +1,14 @@ +--- tests/incremental/04-var-rename/08-2_incremental_runs_1.c ++++ tests/incremental/04-var-rename/08-2_incremental_runs_1.c +@@ -1,8 +1,8 @@ + int main() { +- int varFirstIteration = 0; ++ int varSecondIteration = 0; + +- varFirstIteration++; ++ varSecondIteration++; + +- assert(varFirstIteration < 10); ++ assert(varSecondIteration < 10); + return 0; + } diff --git a/tests/incremental/04-var-rename/08-2_incremental_runs_2.c b/tests/incremental/04-var-rename/08-2_incremental_runs_2.c new file mode 100644 index 0000000000..1190fdb14c --- /dev/null +++ b/tests/incremental/04-var-rename/08-2_incremental_runs_2.c @@ -0,0 +1,8 @@ +int main() { + int varSecondIteration = 0; + + varSecondIteration++; + + assert(varSecondIteration < 10); + return 0; +} diff --git a/tests/incremental/04-var-rename/08-2_incremental_runs_2.patch b/tests/incremental/04-var-rename/08-2_incremental_runs_2.patch new file mode 100644 index 0000000000..0952f3a4bf --- /dev/null +++ b/tests/incremental/04-var-rename/08-2_incremental_runs_2.patch @@ -0,0 +1,14 @@ +--- tests/incremental/04-var-rename/08-2_incremental_runs_1.c ++++ tests/incremental/04-var-rename/08-2_incremental_runs_1.c +@@ -1,8 +1,8 @@ + int main() { +- int varSecondIteration = 0; ++ int varThirdIteration = 0; + +- varSecondIteration++; ++ varThirdIteration++; + +- assert(varSecondIteration < 10); ++ assert(varThirdIteration < 10); + return 0; + } diff --git a/tests/incremental/04-var-rename/08-2_incremental_runs_3.c b/tests/incremental/04-var-rename/08-2_incremental_runs_3.c new file mode 100644 index 0000000000..9ff7105ebb --- /dev/null +++ b/tests/incremental/04-var-rename/08-2_incremental_runs_3.c @@ -0,0 +1,8 @@ +int main() { + int varThirdIteration = 0; + + varThirdIteration++; + + assert(varThirdIteration < 10); + return 0; +} diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes.json b/tests/incremental/04-var-rename/09-2_ir_with_changes.json new file mode 100644 index 0000000000..544b7b4ddd --- /dev/null +++ b/tests/incremental/04-var-rename/09-2_ir_with_changes.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes_1.c b/tests/incremental/04-var-rename/09-2_ir_with_changes_1.c new file mode 100644 index 0000000000..535d3c21fc --- /dev/null +++ b/tests/incremental/04-var-rename/09-2_ir_with_changes_1.c @@ -0,0 +1,17 @@ +void foo() { + int fooOne = 1; + fooOne++; + assert(fooOne == 2); +} + +void bar() { + int barOne = 10; + if (barOne < 11) barOne = 20; + assert(barOne == 20); +} + +int main() { + foo(); + bar(); + return 0; +} diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes_1.patch b/tests/incremental/04-var-rename/09-2_ir_with_changes_1.patch new file mode 100644 index 0000000000..4f2d38927c --- /dev/null +++ b/tests/incremental/04-var-rename/09-2_ir_with_changes_1.patch @@ -0,0 +1,22 @@ +--- tests/incremental/04-var-rename/09-2_ir_with_changes_1.c ++++ tests/incremental/04-var-rename/09-2_ir_with_changes_1.c +@@ -1,13 +1,13 @@ + void foo() { +- int fooOne = 1; +- fooOne++; +- assert(fooOne == 2); ++ int fooTwo = 1; ++ fooTwo++; ++ assert(fooTwo == 2); + } + + void bar() { +- int barOne = 10; +- if (barOne < 11) barOne = 20; +- assert(barOne == 20); ++ int barTwo = 10; ++ if (barTwo < 11) barTwo = 20; ++ assert(barTwo == 20); + } + + int main() { diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes_2.c b/tests/incremental/04-var-rename/09-2_ir_with_changes_2.c new file mode 100644 index 0000000000..6469a06781 --- /dev/null +++ b/tests/incremental/04-var-rename/09-2_ir_with_changes_2.c @@ -0,0 +1,17 @@ +void foo() { + int fooTwo = 1; + fooTwo++; + assert(fooTwo == 2); +} + +void bar() { + int barTwo = 10; + if (barTwo < 11) barTwo = 20; + assert(barTwo == 20); +} + +int main() { + foo(); + bar(); + return 0; +} diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes_2.patch b/tests/incremental/04-var-rename/09-2_ir_with_changes_2.patch new file mode 100644 index 0000000000..823bbd7a0e --- /dev/null +++ b/tests/incremental/04-var-rename/09-2_ir_with_changes_2.patch @@ -0,0 +1,22 @@ +--- tests/incremental/04-var-rename/09-2_ir_with_changes_1.c ++++ tests/incremental/04-var-rename/09-2_ir_with_changes_1.c +@@ -1,13 +1,14 @@ + void foo() { +- int fooTwo = 1; +- fooTwo++; +- assert(fooTwo == 2); ++ int fooThree = 1; ++ fooThree++; ++ assert(fooThree == 2); + } + + void bar() { + int barTwo = 10; +- if (barTwo < 11) barTwo = 20; +- assert(barTwo == 20); ++ int x = 3; ++ if (x < 11) barTwo = 13; ++ assert(x > 1); + } + + int main() { diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes_3.c b/tests/incremental/04-var-rename/09-2_ir_with_changes_3.c new file mode 100644 index 0000000000..eaf77e72d1 --- /dev/null +++ b/tests/incremental/04-var-rename/09-2_ir_with_changes_3.c @@ -0,0 +1,18 @@ +void foo() { + int fooThree = 1; + fooThree++; + assert(fooThree == 2); +} + +void bar() { + int barTwo = 10; + int x = 3; + if (x < 11) barTwo = 13; + assert(x > 1); +} + +int main() { + foo(); + bar(); + return 0; +} From 9eb3f875f9767424e20f627e725f1d33210ec615 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 20 Apr 2022 15:53:26 +0200 Subject: [PATCH 07/90] Renamed local vars are now also shown in g2html. --- src/cdomains/baseDomain.ml | 6 ++++-- src/framework/analyses.ml | 8 ++++++++ .../04-var-rename/09-2_ir_with_changes_1.patch | 7 ++++--- .../04-var-rename/09-2_ir_with_changes_2.c | 5 +++-- .../04-var-rename/09-2_ir_with_changes_2.patch | 11 +---------- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/cdomains/baseDomain.ml b/src/cdomains/baseDomain.ml index 472de2a66f..dc2b63a95f 100644 --- a/src/cdomains/baseDomain.ml +++ b/src/cdomains/baseDomain.ml @@ -108,9 +108,11 @@ struct ++ text ")" let printXml f r = + CPA.iter (fun key value -> key.vname <- (CompareCIL.get_local_rename (!Analyses.currentFunctionName) key.vname)) r.cpa; + let e = XmlUtil.escape in - BatPrintf.fprintf f "\n\n\n%s\n\n%a\n%s\n\n%a\n%s\n\n%a\n\n%s\n\n%a\n\n" - (e @@ CPA.name ()) CPA.printXml r.cpa + BatPrintf.fprintf f "\n\n\n%s\n\n%a\n%s\n\n%a\n%s\n\n%a\n\n%s\n\n%a\n\n" + (e @@ (CPA.name () ^ "ASSSSSSS")) CPA.printXml r.cpa (e @@ PartDeps.name ()) PartDeps.printXml r.deps (e @@ WeakUpdates.name ()) WeakUpdates.printXml r.weak (e @@ PrivD.name ()) PrivD.printXml r.priv diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index d6244d60e1..0d00ac672a 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -8,6 +8,8 @@ open GobConfig module GU = Goblintutil module M = Messages +let currentFunctionName: string ref = ref "" + (** Analysis starts from lists of functions: start functions, exit functions, and * other functions. *) type fundecs = fundec list * fundec list * fundec list @@ -150,6 +152,10 @@ struct (* Not using Node.location here to have updated locations in incremental analysis. See: https://github.com/goblint/analyzer/issues/290#issuecomment-881258091. *) let loc = UpdateCil.getLoc n in + + let parentNode = Node.find_fundec n in + currentFunctionName.contents <- parentNode.svar.vname; + BatPrintf.fprintf f "\n" (Node.show_id n) loc.file loc.line loc.byte loc.column; BatPrintf.fprintf f "%a\n" Range.printXml v in @@ -185,6 +191,8 @@ struct match get_string "result" with | "pretty" -> ignore (fprintf out "%a\n" pretty (Lazy.force table)) | "fast_xml" -> + Printf.printf "%s" (Printexc.get_callstack 15 |> Printexc.raw_backtrace_to_string); + let module SH = BatHashtbl.Make (Basetype.RawStrings) in let file2funs = SH.create 100 in let funs2node = SH.create 100 in diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes_1.patch b/tests/incremental/04-var-rename/09-2_ir_with_changes_1.patch index 4f2d38927c..c640034ea4 100644 --- a/tests/incremental/04-var-rename/09-2_ir_with_changes_1.patch +++ b/tests/incremental/04-var-rename/09-2_ir_with_changes_1.patch @@ -1,6 +1,6 @@ --- tests/incremental/04-var-rename/09-2_ir_with_changes_1.c +++ tests/incremental/04-var-rename/09-2_ir_with_changes_1.c -@@ -1,13 +1,13 @@ +@@ -1,13 +1,14 @@ void foo() { - int fooOne = 1; - fooOne++; @@ -15,8 +15,9 @@ - if (barOne < 11) barOne = 20; - assert(barOne == 20); + int barTwo = 10; -+ if (barTwo < 11) barTwo = 20; -+ assert(barTwo == 20); ++ int x = 3; ++ if (x < 11) barTwo = 13; ++ assert(x > 1); } int main() { diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes_2.c b/tests/incremental/04-var-rename/09-2_ir_with_changes_2.c index 6469a06781..6c4f789066 100644 --- a/tests/incremental/04-var-rename/09-2_ir_with_changes_2.c +++ b/tests/incremental/04-var-rename/09-2_ir_with_changes_2.c @@ -6,8 +6,9 @@ void foo() { void bar() { int barTwo = 10; - if (barTwo < 11) barTwo = 20; - assert(barTwo == 20); + int x = 3; + if (x < 11) barTwo = 13; + assert(x > 1); } int main() { diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes_2.patch b/tests/incremental/04-var-rename/09-2_ir_with_changes_2.patch index 823bbd7a0e..ad44fd2303 100644 --- a/tests/incremental/04-var-rename/09-2_ir_with_changes_2.patch +++ b/tests/incremental/04-var-rename/09-2_ir_with_changes_2.patch @@ -1,6 +1,6 @@ --- tests/incremental/04-var-rename/09-2_ir_with_changes_1.c +++ tests/incremental/04-var-rename/09-2_ir_with_changes_1.c -@@ -1,13 +1,14 @@ +@@ -1,7 +1,7 @@ void foo() { - int fooTwo = 1; - fooTwo++; @@ -11,12 +11,3 @@ } void bar() { - int barTwo = 10; -- if (barTwo < 11) barTwo = 20; -- assert(barTwo == 20); -+ int x = 3; -+ if (x < 11) barTwo = 13; -+ assert(x > 1); - } - - int main() { From d652715f577295a231ec8bcf7d7b6b5a365ace2b Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Tue, 3 May 2022 15:51:11 +0200 Subject: [PATCH 08/90] Added incremental aware print statements and replaced traditional print statements with those in many places --- src/analyses/base.ml | 91 +++++++++++++++++--------------- src/analyses/basePriv.ml | 12 ++--- src/cdomains/baseDomain.ml | 6 +-- src/cdomains/exp.ml | 2 +- src/cdomains/lval.ml | 4 +- src/framework/analyses.ml | 16 +++--- src/framework/constraints.ml | 6 +-- src/framework/edge.ml | 11 ++-- src/framework/node.ml | 16 +++--- src/incremental/compareAST.ml | 4 ++ src/incremental/compareCIL.ml | 39 +------------- src/incremental/renameMapping.ml | 62 ++++++++++++++++++++++ src/util/cilType.ml | 1 + src/util/cilfacade.ml | 4 +- 14 files changed, 155 insertions(+), 119 deletions(-) create mode 100644 src/incremental/renameMapping.ml diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 6e218bbfa0..65dff1a699 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -346,7 +346,7 @@ struct * which part of an array is involved. *) let rec get ?(full=false) a (gs: glob_fun) (st: store) (addrs:address) (exp:exp option): value = let at = AD.get_type addrs in - let firstvar = if M.tracing then match AD.to_var_may addrs with [] -> "" | x :: _ -> x.vname else "" in + let firstvar = if M.tracing then match AD.to_var_may addrs with [] -> "" | x :: _ -> RenameMapping.show_varinfo x else "" in if M.tracing then M.traceli "get" ~var:firstvar "Address: %a\nState: %a\n" AD.pretty addrs CPA.pretty st.cpa; (* Finding a single varinfo*offset pair *) let res = @@ -576,7 +576,7 @@ struct (* The evaluation function as mutually recursive eval_lv & eval_rv *) let rec eval_rv (a: Q.ask) (gs:glob_fun) (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\n" RenameMapping.d_exp exp; let r = (* we have a special expression that should evaluate to top ... *) if exp = MyCFG.unknown_exp then @@ -584,7 +584,7 @@ struct else eval_rv_ask_evalint a gs 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\n" RenameMapping.d_exp exp VD.pretty r; r (** Evaluate expression using EvalInt query. @@ -593,13 +593,13 @@ struct Non-integer expression just delegate to next eval_rv function. *) and eval_rv_ask_evalint a gs st exp = let eval_next () = eval_rv_no_ask_evalint a gs 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\n" RenameMapping.d_exp exp; let r = 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\n" RenameMapping.d_exp exp; let a = a.f (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\n" RenameMapping.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) *) @@ -609,7 +609,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\n" RenameMapping.d_exp exp VD.pretty r; r (** Evaluate expression without EvalInt query on outermost expression. @@ -622,11 +622,11 @@ struct Otherwise just delegate to next eval_rv function. *) and eval_rv_ask_mustbeequal a gs st exp = let eval_next () = eval_rv_base a gs st exp in - if M.tracing then M.traceli "evalint" "base eval_rv_ask_mustbeequal %a\n" d_exp exp; + if M.tracing then M.traceli "evalint" "base eval_rv_ask_mustbeequal %a\n" RenameMapping.d_exp exp; let binop op e1 e2 = let must_be_equal () = let r = a.f (Q.MustBeEqual (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\n" RenameMapping.d_exp e1 RenameMapping.d_exp e2 r; r in match op with @@ -654,14 +654,14 @@ struct | BinOp (op,arg1,arg2,_) -> binop op arg1 arg2 | _ -> eval_next () in - if M.tracing then M.traceu "evalint" "base eval_rv_ask_mustbeequal %a -> %a\n" d_exp exp VD.pretty r; + if M.tracing then M.traceu "evalint" "base eval_rv_ask_mustbeequal %a -> %a\n" RenameMapping.d_exp exp VD.pretty r; r (** Evaluate expression structurally by base. This handles constants directly and variables using CPA. Subexpressions delegate to [eval_rv], which may use queries on them. *) and eval_rv_base (a: Q.ask) (gs:glob_fun) (st: store) (exp:exp): value = - 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\n" RenameMapping.d_exp exp; let rec do_offs def = function (* for types that only have one value *) | Field (fd, offs) -> begin match Goblintutil.is_blessed (TComp (fd.fcomp, [])) with @@ -741,7 +741,7 @@ struct let te2 = Cilfacade.typeOf e2 in let both_arith_type = isArithmeticType te1 && isArithmeticType te2 in let is_safe = (VD.equal a1 a2 || 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; + M.tracel "cast" "remove cast on both sides for %a? -> %b\n" RenameMapping.d_exp exp is_safe; if is_safe then ( (* we can ignore the casts if the values are equal anyway, or 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 @@ -779,7 +779,7 @@ struct VD.cast ~torg:(Cilfacade.typeOf exp) t v | _ -> 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\n" RenameMapping.d_exp exp VD.pretty r; r (* A hackish evaluation of expressions that should immediately yield an * address, e.g. when calling functions. *) @@ -857,20 +857,20 @@ struct let eval_rv (a: Q.ask) (gs:glob_fun) (st: store) (exp:exp): value = try let r = eval_rv a gs 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\n" RenameMapping.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 ask gs 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\n" RenameMapping.d_exp e; let r = match eval_rv_no_ask_evalint ask gs st e with | `Int i -> `Lifted i (* cast should be unnecessary, eval_rv should guarantee right ikind already *) | `Bot -> Queries.ID.bot () (* TODO: remove? *) (* | v -> M.warn ("Query function answered " ^ (VD.show v)); Queries.Result.top q *) - | v -> M.debug ~category:Analyzer "Base EvalInt %a query answering bot instead of %a" d_exp e VD.pretty v; Queries.ID.bot () + | v -> M.debug ~category:Analyzer "Base EvalInt %a query answering bot instead of %a" RenameMapping.d_exp e VD.pretty v; Queries.ID.bot () 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\n" RenameMapping.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. *) @@ -892,12 +892,12 @@ struct try let fp = eval_fv (Analyses.ask_of_ctx ctx) ctx.global ctx.local fval in if AD.mem Addr.UnknownPtr fp then begin - M.warn "Function pointer %a may contain unknown functions." d_exp fval; + M.warn "Function pointer %a may contain unknown functions." RenameMapping.d_exp fval; dummyFunDec.svar :: AD.to_var_may fp end else AD.to_var_may fp with SetDomain.Unsupported _ -> - M.warn "Unknown call to function %a." d_exp fval; + M.warn "Unknown call to function %a." RenameMapping.d_exp fval; [dummyFunDec.svar] (** Evaluate expression as address. @@ -1000,7 +1000,7 @@ struct (* check if we have an array of chars that form a string *) (* TODO return may-points-to-set of strings *) | `Address a when List.compare_length_with (AD.to_string a) 1 > 0 -> (* oh oh *) - M.debug "EvalStr (%a) returned %a" d_exp e AD.pretty a; + M.debug "EvalStr (%a) returned %a" RenameMapping.d_exp e AD.pretty a; Queries.Result.top q | `Address a when List.compare_length_with (AD.to_var_may a) 1 = 0 -> (* some other address *) (* Cil.varinfo * (AD.Addr.field, AD.Addr.idx) Lval.offs *) @@ -1101,12 +1101,14 @@ struct * precise information about arrays. *) let set (a: Q.ask) ?(ctx=None) ?(invariant=false) ?lval_raw ?rval_raw ?t_override (gs:glob_fun) (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 "setosek" ~var:x.vname "update_variable: start '%s' '%a'\nto\n%a\n\n" x.vname VD.pretty y CPA.pretty z; + let x_vname = RenameMapping.show_varinfo x in + + if M.tracing then M.tracel "setosek" ~var:x_vname "update_variable: start '%s' '%a'\nto\n%a\n\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 "setosek" ~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 "setosek" ~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; r in - let firstvar = if M.tracing then match AD.to_var_may lval with [] -> "" | x :: _ -> x.vname else "" in + let firstvar = if M.tracing then match AD.to_var_may lval with [] -> "" | x :: _ -> RenameMapping.show_varinfo x 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; (* Updating a single varinfo*offset pair. NB! This function's type does @@ -1150,10 +1152,12 @@ struct if M.tracing then M.tracel "setosek" ~var:firstvar "update_one_addr: BAD? exp.globs_are_top is set \n"; { st with cpa = CPA.add x `Top st.cpa } end else + let x_vname = RenameMapping.show_varinfo x in + (* Check if we need to side-effect this one. We no longer generate * side-effects here, but the code still distinguishes these cases. *) if (!GU.earlyglobs || ThreadFlag.is_multi a) && is_global a x then begin - if M.tracing then M.tracel "setosek" ~var:x.vname "update_one_addr: update a global var '%s' ...\n" x.vname; + if M.tracing then M.tracel "setosek" ~var:x_vname "update_one_addr: update a global var '%s' ...\n" x_vname; let priv_getg = priv_getg gs 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. @@ -1165,10 +1169,10 @@ struct in let new_value = update_offset old_value in let r = Priv.write_global ~invariant a priv_getg (priv_sideg (Option.get ctx).sideg) st x new_value in - if M.tracing then M.tracel "setosek" ~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 "setosek" ~var:x_vname "update_one_addr: updated a global var '%s' \nstate:%a\n\n" x_vname D.pretty r; r end else begin - if M.tracing then M.tracel "setosek" ~var:x.vname "update_one_addr: update a local var '%s' ...\n" x.vname; + if M.tracing then M.tracel "setosek" ~var:x_vname "update_one_addr: update a local var '%s' ...\n" 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 - @@ -1376,7 +1380,7 @@ struct if M.tracing then M.trace "invariant" "Failed! (operation not supported)\n\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\n" RenameMapping.d_exp exp tv; let null_val typ = match Cil.unrollType typ with | TPtr _ -> `Address AD.null_ptr @@ -1598,12 +1602,12 @@ struct | BinOp(op, CastE (t1, c1), CastE (t2, c2), t) when (op = Eq || op = Ne) && typeSig (Cilfacade.typeOf c1) = typeSig (Cilfacade.typeOf c2) && VD.is_safe_cast t1 (Cilfacade.typeOf c1) && VD.is_safe_cast t2 (Cilfacade.typeOf c2) -> inv_exp c (BinOp (op, c1, c2, t)) st | BinOp (op, e1, e2, _) as e -> - 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) ID.pretty c; + if M.tracing then M.tracel "inv" "binop %a with %a %a %a == %a\n" RenameMapping.d_exp e VD.pretty (eval e1 st) d_binop op VD.pretty (eval e2 st) ID.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 a', b' = inv_bin_int (a, b) ikind c op in - if M.tracing then M.tracel "inv" "binop: %a, a': %a, b': %a\n" d_exp e ID.pretty a' ID.pretty b'; + if M.tracing then M.tracel "inv" "binop: %a, a': %a, b': %a\n" RenameMapping.d_exp e ID.pretty a' ID.pretty b'; let st' = inv_exp a' e1 st in let st'' = inv_exp b' e2 st' in st'' @@ -1788,23 +1792,23 @@ struct let valu = eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local exp in let refine () = let res = invariant ctx (Analyses.ask_of_ctx ctx) ctx.global 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.tracec "branch" "EqualSet result for expression %a is %a\n" RenameMapping.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" RenameMapping.d_exp exp Queries.ES.pretty (ctx.ask (Queries.CondVars exp)); if M.tracing then M.traceu "branch" "Invariant enforced!\n"; match ctx.ask (Queries.CondVars exp) with | s when Queries.ES.cardinal s = 1 -> let e = Queries.ES.choose s in - M.debug "CondVars result for expression %a is %a" d_exp exp d_exp e; + M.debug "CondVars result for expression %a is %a" RenameMapping.d_exp exp RenameMapping.d_exp e; invariant ctx (Analyses.ask_of_ctx ctx) ctx.global 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.tracel "branchosek" "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\n" RenameMapping.d_exp exp VD.pretty valu; + if M.tracing then M.tracel "branchosek" "Evaluating branch for expression %a with value %a\n" RenameMapping.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 when (ID.is_bool 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\n" RenameMapping.d_exp exp ID.pretty value; (* to suppress pattern matching warnings: *) let fromJust x = match x with Some x -> x | None -> assert false in let v = fromJust (ID.to_bool value) in @@ -1980,7 +1984,7 @@ struct in Some (lval, v, args) else ( - M.debug ~category:Analyzer "Not creating a thread from %s because its type is %a" v.vname d_type v.vtype; + M.debug ~category:Analyzer "Not creating a thread from %s because its type is %a" (RenameMapping.show_varinfo v) d_type v.vtype; None ) in @@ -2015,6 +2019,7 @@ struct | _ -> [] let assert_fn ctx e should_warn change = + (* let _ = Hashtbl.iter (fun fun_name map -> begin Printf.printf "%s: [" fun_name; @@ -2052,6 +2057,7 @@ struct (*TODO: AddrOfLabel?*) | _ -> exp in + *) let check_assert e st = @@ -2065,7 +2071,7 @@ struct | `Bot -> `Bot | _ -> `Top in - let expr = sprint d_exp (rename_exp e) in + let expr = sprint RenameMapping.d_exp e in let warn warn_fn ?annot msg = if should_warn then if get_bool "dbg.regression" then ( (* This only prints unexpected results (with the difference) as indicated by the comment behind the assert (same as used by the regression test script). *) let loc = !M.current_loc in @@ -2104,7 +2110,7 @@ struct end let special_unknown_invalidate ctx ask gs st f args = - (if not (CilType.Varinfo.equal f dummyFunDec.svar) && not (LF.use_special f.vname) then M.error ~category:Imprecise ~tags:[Category Unsound] "Function definition missing for %s" f.vname); + (if not (CilType.Varinfo.equal f dummyFunDec.svar) && not (LF.use_special f.vname) then M.error ~category:Imprecise ~tags:[Category Unsound] "Function definition missing for %s" (RenameMapping.show_varinfo f)); (if CilType.Varinfo.equal f dummyFunDec.svar then M.warn "Unknown function ptr called"); let addrs = if get_bool "sem.unknown_function.invalidate.globals" then ( @@ -2125,17 +2131,16 @@ struct invalidate ~ctx (Analyses.ask_of_ctx ctx) gs st addrs let special ctx (lv:lval option) (f: varinfo) (args: exp list) = - Printf.printf "special: varinfo=%s\n" f.vname; - List.iter (fun x -> ignore @@ Pretty.printf "%a\n" Cil.d_exp x;) args; + List.iter (fun x -> ignore @@ Pretty.printf "%a\n" RenameMapping.d_exp x;) args; 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\n" d_plainlval lv (RenameMapping.show_varinfo f); invalidate ~ctx (Analyses.ask_of_ctx ctx) ctx.global st [Cil.mkAddrOrStartOf lv] | None -> st 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 "," d_varinfo) (List.map BatTuple.Tuple3.second forks); + if M.tracing then if not (List.is_empty forks) then M.tracel "spawn" "Base.special %s: spawning functions %a\n" (RenameMapping.show_varinfo f) (d_list "," d_varinfo) (List.map BatTuple.Tuple3.second forks); List.iter (BatTuple.Tuple3.uncurry ctx.spawn) forks; let st: store = ctx.local in let gs = ctx.global in @@ -2379,7 +2384,7 @@ struct | _, v -> VD.show v in let args_short = List.map short_fun f.sformals in - Printable.get_short_list (f.svar.vname ^ "(") ")" args_short + Printable.get_short_list (RenameMapping.show_varinfo f.svar ^ "(") ")" args_short let threadenter ctx (lval: lval option) (f: varinfo) (args: exp list): D.t list = match Cilfacade.find_varinfo_fundec f with diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 7d8d7179f2..e0c5cec32e 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -94,7 +94,7 @@ struct let write_global ?(invariant=false) ask getg sideg (st: BaseComponents (D).t) x v = if invariant && not (is_private ask x) then ( - if M.tracing then M.tracel "setosek" ~var:x.vname "update_one_addr: BAD! effect = '%B', or else is private! \n" (not invariant); + if M.tracing then M.tracel "setosek" ~var:(RenameMapping.show_varinfo x) "update_one_addr: BAD! effect = '%B', or else is private! \n" (not invariant); st ) else ( @@ -110,7 +110,7 @@ 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:(RenameMapping.show_varinfo v) "Tracing for %s\n" (RenameMapping.show_varinfo v); let res = if is_global ask v then begin if M.tracing then M.tracec "globalize" "Publishing its value: %a\n" VD.pretty value; @@ -151,7 +151,7 @@ struct let write_global ?(invariant=false) ask getg sideg (st: BaseComponents (D).t) x v = if invariant && not (is_private ask x) then ( - if M.tracing then M.tracel "setosek" ~var:x.vname "update_one_addr: BAD! effect = '%B', or else is private! \n" (not invariant); + if M.tracing then M.tracel "setosek" ~var:(RenameMapping.show_varinfo x) "update_one_addr: BAD! effect = '%B', or else is private! \n" (not invariant); st ) else ( @@ -170,7 +170,7 @@ struct if M.tracing then M.tracel "sync" "OldPriv: %a\n" BaseComponents.pretty st; (* For each global variable, we create the side effect *) let side_var (v: varinfo) (value) (st: BaseComponents.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:(RenameMapping.show_varinfo v) "Tracing for %s\n" (RenameMapping.show_varinfo v); let res = if is_global ask v && ((privates && not (is_precious_glob v)) || not (is_private ask v)) then begin if M.tracing then M.tracec "globalize" "Publishing its value: %a\n" VD.pretty value; @@ -427,7 +427,7 @@ struct let write_global ?(invariant=false) ask getg sideg (st: BaseComponents (D).t) x v = if invariant && not (is_private ask x) then ( - if M.tracing then M.tracel "setosek" ~var:x.vname "update_one_addr: BAD! effect = '%B', or else is private! \n" (not invariant); + if M.tracing then M.tracel "setosek" ~var:(RenameMapping.show_varinfo x) "update_one_addr: BAD! effect = '%B', or else is private! \n" (not invariant); st ) else ( @@ -448,7 +448,7 @@ struct let privates = sync_privates reason ask in (* 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:(RenameMapping.show_varinfo v) "Tracing for %s\n" (RenameMapping.show_varinfo v); let res = if is_global ask v then let protected = is_protected ask v in diff --git a/src/cdomains/baseDomain.ml b/src/cdomains/baseDomain.ml index dc2b63a95f..472de2a66f 100644 --- a/src/cdomains/baseDomain.ml +++ b/src/cdomains/baseDomain.ml @@ -108,11 +108,9 @@ struct ++ text ")" let printXml f r = - CPA.iter (fun key value -> key.vname <- (CompareCIL.get_local_rename (!Analyses.currentFunctionName) key.vname)) r.cpa; - let e = XmlUtil.escape in - BatPrintf.fprintf f "\n\n\n%s\n\n%a\n%s\n\n%a\n%s\n\n%a\n\n%s\n\n%a\n\n" - (e @@ (CPA.name () ^ "ASSSSSSS")) CPA.printXml r.cpa + BatPrintf.fprintf f "\n\n\n%s\n\n%a\n%s\n\n%a\n%s\n\n%a\n\n%s\n\n%a\n\n" + (e @@ CPA.name ()) CPA.printXml r.cpa (e @@ PartDeps.name ()) PartDeps.printXml r.deps (e @@ WeakUpdates.name ()) WeakUpdates.printXml r.weak (e @@ PrivD.name ()) PrivD.printXml r.priv diff --git a/src/cdomains/exp.ml b/src/cdomains/exp.ml index 35c585f8ef..1ff23b5448 100644 --- a/src/cdomains/exp.ml +++ b/src/cdomains/exp.ml @@ -260,7 +260,7 @@ struct let ee_to_str x = match x with - | EVar v -> v.vname + | EVar v -> RenameMapping.show_varinfo v | EAddr -> "&" | EDeref -> "*" | EField f -> f.fname diff --git a/src/cdomains/lval.ml b/src/cdomains/lval.ml index c7037594c5..74d467777b 100644 --- a/src/cdomains/lval.ml +++ b/src/cdomains/lval.ml @@ -220,8 +220,8 @@ struct let short_addr (x, o) = if RichVarinfo.BiVarinfoMap.Collection.mem_varinfo x then let description = RichVarinfo.BiVarinfoMap.Collection.describe_varinfo x in - "(" ^ x.vname ^ ", " ^ description ^ ")" ^ short_offs o - else x.vname ^ short_offs o + "(" ^ RenameMapping.show_varinfo x ^ ", " ^ description ^ ")" ^ short_offs o + else RenameMapping.show_varinfo x ^ short_offs o let show = function | Addr (x, o)-> short_addr (x, o) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 0d00ac672a..21d015c512 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -37,7 +37,7 @@ struct let printXml f n = let l = Node.location n in - BatPrintf.fprintf f "\n" (Node.show_id n) l.file (Node.find_fundec n).svar.vname l.line l.byte l.column + BatPrintf.fprintf f "\n" (Node.show_id n) l.file (RenameMapping.show_varinfo (Node.find_fundec n).svar) l.line l.byte l.column let var_id = Node.show_id let node n = n @@ -117,7 +117,7 @@ struct See: https://github.com/goblint/analyzer/issues/290#issuecomment-881258091. *) let x = UpdateCil.getLoc a in let f = Node.find_fundec a in - CilType.Location.show x ^ "(" ^ f.svar.vname ^ ")" + CilType.Location.show x ^ "(" ^ RenameMapping.show_varinfo f.svar ^ ")" include Printable.SimpleShow ( struct @@ -154,7 +154,7 @@ struct let loc = UpdateCil.getLoc n in let parentNode = Node.find_fundec n in - currentFunctionName.contents <- parentNode.svar.vname; + currentFunctionName.contents <- RenameMapping.show_varinfo parentNode.svar; BatPrintf.fprintf f "\n" (Node.show_id n) loc.file loc.line loc.byte loc.column; BatPrintf.fprintf f "%a\n" Range.printXml v @@ -196,9 +196,9 @@ struct let module SH = BatHashtbl.Make (Basetype.RawStrings) in let file2funs = SH.create 100 in let funs2node = SH.create 100 in - iter (fun n _ -> SH.add funs2node (Node.find_fundec n).svar.vname n) (Lazy.force table); + iter (fun n _ -> SH.add funs2node (RenameMapping.show_varinfo (Node.find_fundec n).svar) n) (Lazy.force table); iterGlobals file (function - | GFun (fd,loc) -> SH.add file2funs loc.file fd.svar.vname + | GFun (fd,loc) -> SH.add file2funs loc.file (RenameMapping.show_varinfo fd.svar) | _ -> () ); let p_node f n = BatPrintf.fprintf f "%s" (Node.show_id n) in @@ -244,9 +244,9 @@ struct let module SH = BatHashtbl.Make (Basetype.RawStrings) in let file2funs = SH.create 100 in let funs2node = SH.create 100 in - iter (fun n _ -> SH.add funs2node (Node.find_fundec n).svar.vname n) (Lazy.force table); + iter (fun n _ -> SH.add funs2node (RenameMapping.show_varinfo (Node.find_fundec n).svar) n) (Lazy.force table); iterGlobals file (function - | GFun (fd,loc) -> SH.add file2funs loc.file fd.svar.vname + | GFun (fd,loc) -> SH.add file2funs loc.file (RenameMapping.show_varinfo fd.svar) | _ -> () ); let p_enum p f xs = BatEnum.print ~first:"[\n " ~last:"\n]" ~sep:",\n " p f xs in @@ -547,7 +547,7 @@ struct your analysis to be path sensitive, do override this. To obtain a behavior where all paths are kept apart, set this to D.equal x y *) - let call_descr f _ = f.svar.vname + let call_descr f _ = RenameMapping.show_varinfo f.svar (* prettier name for equation variables --- currently base can do this and MCP just forwards it to Base.*) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index ed3acc309f..53b3897039 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -371,7 +371,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\n" (RenameMapping.show_varinfo f.svar)); v_new, M.add f.svar v_new m ) else @@ -512,7 +512,7 @@ struct ignore (getl (Function fd, c)) | exception Not_found -> (* unknown function *) - M.error ~category:Imprecise ~tags:[Category Unsound] "Created a thread from unknown function %s" f.vname + M.error ~category:Imprecise ~tags:[Category Unsound] "Created a thread from unknown function %s" (RenameMapping.show_varinfo f) (* actual implementation (e.g. invalidation) is done by threadenter *) ) ds in @@ -646,7 +646,7 @@ struct let one_function f = match Cilfacade.find_varinfo_fundec f with | fd when LibraryFunctions.use_special f.vname -> - M.warn "Using special for defined function %s" f.vname; + M.warn "Using special for defined function %s" (RenameMapping.show_varinfo f); tf_special_call ctx lv f args | fd -> tf_normal_call ctx lv e fd args getl sidel getg sideg diff --git a/src/framework/edge.ml b/src/framework/edge.ml index 0118eabb09..376934510b 100644 --- a/src/framework/edge.ml +++ b/src/framework/edge.ml @@ -33,6 +33,7 @@ type t = (** This for interrupt edges.! *) [@@deriving eq, to_yojson] +let dn_exp = RenameMapping.dn_exp let pretty () = function | Test (exp, b) -> if b then Pretty.dprintf "Pos(%a)" dn_exp exp else Pretty.dprintf "Neg(%a)" dn_exp exp @@ -47,15 +48,17 @@ let pretty () = function | VDecl v -> Cil.defaultCilPrinter#pVDecl () v | SelfLoop -> Pretty.text "SelfLoop" +let d_exp = RenameMapping.d_exp + let pretty_plain () = function | Assign (lv,rv) -> dprintf "Assign '%a = %a' " d_lval lv d_exp rv | Proc (None ,f,ars) -> dprintf "Proc '%a(%a)'" d_exp f (d_list ", " d_exp) ars | Proc (Some r,f,ars) -> dprintf "Proc '%a = %a(%a)'" d_lval r d_exp f (d_list ", " d_exp) ars - | Entry f -> dprintf "Entry %s" f.svar.vname - | Ret (None,fd) -> dprintf "Ret (None, %s)" fd.svar.vname - | Ret (Some r,fd) -> dprintf "Ret (Some %a, %s)" d_exp r fd.svar.vname + | Entry f -> dprintf "Entry %s" (RenameMapping.show_varinfo f.svar) + | Ret (None,fd) -> dprintf "Ret (None, %s)" (RenameMapping.show_varinfo fd.svar) + | Ret (Some r,fd) -> dprintf "Ret (Some %a, %s)" d_exp r (RenameMapping.show_varinfo fd.svar) | Test (p,b) -> dprintf "Test (%a,%b)" d_exp p b | ASM _ -> text "ASM ..." | Skip -> text "Skip" - | VDecl v -> dprintf "VDecl '%a %s;'" d_type v.vtype v.vname + | VDecl v -> dprintf "VDecl '%a %s;'" d_type v.vtype (RenameMapping.show_varinfo v) | SelfLoop -> text "SelfLoop" diff --git a/src/framework/node.ml b/src/framework/node.ml index 1d5a8291f9..cc1d32a018 100644 --- a/src/framework/node.ml +++ b/src/framework/node.ml @@ -22,21 +22,21 @@ let name () = "node" (** Pretty node plainly with entire stmt. *) let pretty_plain () = function | Statement s -> text "Statement " ++ dn_stmt () s - | Function f -> text "Function " ++ text f.svar.vname - | FunctionEntry f -> text "FunctionEntry " ++ text f.svar.vname + | Function f -> text "Function " ++ text (RenameMapping.show_varinfo f.svar) + | FunctionEntry f -> text "FunctionEntry " ++ text (RenameMapping.show_varinfo f.svar) (* TODO: remove this? *) (** Pretty node plainly with stmt location. *) let pretty_plain_short () = function | Statement s -> text "Statement @ " ++ CilType.Location.pretty () (Cilfacade.get_stmtLoc s) - | Function f -> text "Function " ++ text f.svar.vname - | FunctionEntry f -> text "FunctionEntry " ++ text f.svar.vname + | Function f -> text "Function " ++ text (RenameMapping.show_varinfo f.svar) + | FunctionEntry f -> text "FunctionEntry " ++ text (RenameMapping.show_varinfo f.svar) (** Pretty node for solver variable tracing with short stmt. *) let pretty_trace () = function | Statement stmt -> dprintf "node %d \"%a\"" stmt.sid Cilfacade.stmt_pretty_short stmt - | Function fd -> dprintf "call of %s" fd.svar.vname - | FunctionEntry fd -> dprintf "entry state of %s" fd.svar.vname + | Function fd -> dprintf "call of %s" (RenameMapping.show_varinfo fd.svar) + | FunctionEntry fd -> dprintf "entry state of %s" (RenameMapping.show_varinfo fd.svar) (** Output functions for Printable interface *) let pretty () x = pretty_trace () x @@ -56,8 +56,8 @@ let show_id = function (** Show node label for CFG. *) let show_cfg = function | Statement stmt -> string_of_int stmt.sid (* doesn't use this but defaults to no label and uses ID from show_id instead *) - | Function fd -> "return of " ^ fd.svar.vname ^ "()" - | FunctionEntry fd -> fd.svar.vname ^ "()" + | Function fd -> "return of " ^ (RenameMapping.show_varinfo fd.svar) ^ "()" + | FunctionEntry fd -> (RenameMapping.show_varinfo fd.svar) ^ "()" let location (node: t) = diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index d9361ec082..1d1456bdf4 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -217,6 +217,10 @@ and eq_varinfo (a: varinfo) (b: varinfo) (context: context) = a.vstorage = b.vstorage && a.vglob = b.vglob && a.vaddrof = b.vaddrof in if did_context_switch then Printf.printf "Undo context switch \n"; + (*Save rename mapping for future usage. If this function later turns out to actually being changed, the new varinfo id will be used anyway + and this mapping has no effect*) + if a.vname <> b.vname && result then RenameMapping.store_update_varinfo_name a b.vname; + result (* Ignore the location, vid, vreferenced, vdescr, vdescrpure, vinline *) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 762d6fbac5..643673829a 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -3,12 +3,6 @@ open MyCFG include CompareAST include CompareCFG -(*Maps the function name to a table of it's local variable and parameter renames. The rename table has key of original name and value of renamed name.*) -let rename_map: (string, (string, string) Hashtbl.t) Hashtbl.t ref = ref (Hashtbl.create 100) - -(*Same as rename_map, but maps renamed name to original name instead.*) -let reverse_rename_map: (string, (string, string) Hashtbl.t) Hashtbl.t ref = ref (Hashtbl.create 100) - type nodes_diff = { unchangedNodes: (node * node) list; primObsoleteNodes: node list; (** primary obsolete nodes -> all obsolete nodes are reachable from these *) @@ -29,37 +23,6 @@ type change_info = { mutable added: global list } -let store_local_rename (function_name: string) (rename_table: (string, string) Hashtbl.t) = - begin - Hashtbl.add !rename_map function_name rename_table; - let reverse_rename_table = Hashtbl.create (Hashtbl.length !rename_map) in - Hashtbl.iter (fun original_name new_name -> Hashtbl.add reverse_rename_table new_name original_name) rename_table; - Hashtbl.add !reverse_rename_map function_name reverse_rename_table; - end - -(*Returnes the rename if one exists, or param_name when no entry exists.*) -let get_local_rename (function_name: string) (param_name: string) = match (Hashtbl.find_opt !rename_map function_name) with - | Some (local_map) -> Option.value (Hashtbl.find_opt local_map param_name) ~default:param_name - | None -> param_name - -let get_orignal_name (function_name: string) (new_var_name: string) = match (Hashtbl.find_opt !reverse_rename_map function_name) with - | Some (reverse_map) -> Option.value (Hashtbl.find_opt reverse_map new_var_name) ~default:new_var_name - |None -> new_var_name - -let show_rename_map = - let show_local_rename_map (local_rename_map: (string, string) Hashtbl.t) = - let rename_string = Seq.map (fun (orig, new_name) -> orig ^ " -> " ^ new_name) (Hashtbl.to_seq local_rename_map) |> - List.of_seq in - String.concat ", " rename_string - in - - Hashtbl.to_seq !rename_map |> - Seq.iter (fun (fun_name, map) -> Printf.printf "%s=%d" fun_name (Hashtbl.length map)); - - let function_strings = Seq.map (fun (fun_name, map) -> fun_name ^ ": [" ^ (show_local_rename_map map) ^ "]") (Hashtbl.to_seq !rename_map) |> List.of_seq in - - String.concat ", " function_strings - let empty_change_info () : change_info = {added = []; removed = []; changed = []; unchanged = []} let should_reanalyze (fdec: Cil.fundec) = @@ -120,7 +83,7 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * cfg) option) (global_cont else (false, Some {unchangedNodes = matches; primObsoleteNodes = diffNodes1; primNewNodes = diffNodes2}) in - if (identical) then store_local_rename a.svar.vname local_rename_map; + (*if (identical) then store_local_rename a.svar.vname local_rename_map;*) identical, unchangedHeader, diffOpt diff --git a/src/incremental/renameMapping.ml b/src/incremental/renameMapping.ml new file mode 100644 index 0000000000..2251d45899 --- /dev/null +++ b/src/incremental/renameMapping.ml @@ -0,0 +1,62 @@ +open Cil + +module IncrementallyUpdatedVarinfoMap = Hashtbl.Make (CilType.Varinfo) + +(*Mapps a varinfo to its updated name*) +let renamedVarinfoMap: string IncrementallyUpdatedVarinfoMap.t ref = ref (IncrementallyUpdatedVarinfoMap.create 100) + +let get_old_or_updated_varinfo_name (old_varinfo: varinfo) = + let r: string option = IncrementallyUpdatedVarinfoMap.find_opt !renamedVarinfoMap old_varinfo in + Option.value r ~default:old_varinfo.vname + +let store_update_varinfo_name (old_varinfo: varinfo) (new_name: string) = + Printf.printf "Storing renamed name: %s -> %s\n" old_varinfo.vname new_name; + IncrementallyUpdatedVarinfoMap.add !renamedVarinfoMap old_varinfo new_name + +(* + Incremental rename aware version of show. Returns the renamed name of the varinfo if it has been updated by an incremental build, or vname if nothing has changed. + + Dev Note: Putting this into CilType.Varinfo results in a cyclic dependency. It should not be put into CilType anyway, as CilType only defines types based on the types themselves, not implement any logic based on other components outside its own definitions. So I think it's cleaner this way. +*) +let show_varinfo (varinfo: varinfo) = + Printf.printf "Accessing renamed: %s -> %s\n" varinfo.vname (get_old_or_updated_varinfo_name varinfo); + get_old_or_updated_varinfo_name varinfo + + +class incremental_printer : Cil.cilPrinter = object(self) + inherit Cil.defaultCilPrinterClass + method pVar (v:varinfo) = Pretty.text (show_varinfo v) + end;; + +class plain_incremental_printer : Cil.cilPrinter = object(self) + inherit Cil.plainCilPrinterClass + method pVar (v:varinfo) = Pretty.text (show_varinfo v) +end;; + +let incremental_aware_printer = new incremental_printer +let plain_incremental_aware_printer = new plain_incremental_printer + +let d_exp () e = printExp incremental_aware_printer () e + +(* A hack to allow forward reference of d_exp. Copy from Cil. *) +let pd_exp : (unit -> exp -> Pretty.doc) ref = + ref (fun _ -> failwith "") + +let _ = pd_exp := d_exp + +(*Fixme: Im a copy of Cil.dn_obj because i couldnt figure out why I couldn't access Cil.dn_obj*) +let dn_obj (func: unit -> 'a -> Pretty.doc) : (unit -> 'a -> Pretty.doc) = + begin + (* construct the closure to return *) + let theFunc () (obj:'a) : Pretty.doc = + begin + let prevStyle = !lineDirectiveStyle in + lineDirectiveStyle := None; + let ret = (func () obj) in (* call underlying printer *) + lineDirectiveStyle := prevStyle; + ret + end in + theFunc + end + +let dn_exp = (dn_obj d_exp) diff --git a/src/util/cilType.ml b/src/util/cilType.ml index 577b307904..436239f080 100644 --- a/src/util/cilType.ml +++ b/src/util/cilType.ml @@ -99,6 +99,7 @@ struct let show = show end ) + let pp fmt x = Format.fprintf fmt "%s" x.vname (* for deriving show *) end diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index 01951ac5cd..6c22c57977 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -320,8 +320,8 @@ let getFirstStmt fd = List.hd fd.sbody.bstmts let pstmt stmt = dumpStmt defaultCilPrinter stdout 0 stmt; print_newline () -let p_expr exp = Pretty.printf "%a\n" (printExp defaultCilPrinter) exp -let d_expr exp = Pretty.printf "%a\n" (printExp plainCilPrinter) exp +let p_expr exp = Pretty.printf "%a\n" (printExp RenameMapping.incremental_aware_printer) exp +let d_expr exp = Pretty.printf "%a\n" (printExp RenameMapping.plain_incremental_aware_printer) exp (* Returns the ikind of a TInt(_) and TEnum(_). Unrolls typedefs. Warns if a a different type is put in and return IInt *) let rec get_ikind t = From 08da3fb35383bc066de0bd5254f92047ef59f3cc Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 4 May 2022 15:38:57 +0200 Subject: [PATCH 09/90] Renamed variable names are now displayed with their new name in g2html --- src/analyses/apron/apronAnalysis.apron.ml | 2 +- src/analyses/arinc.ml | 14 ++++---- src/cdomains/basetype.ml | 4 +-- src/framework/edge.ml | 4 ++- src/incremental/renameMapping.ml | 41 +++++++++++++++++++++-- 5 files changed, 52 insertions(+), 13 deletions(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 8fbe0660e4..d73bda5533 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -146,7 +146,7 @@ struct if !GU.global_initialization && e = MyCFG.unknown_exp then st (* ignore extern inits because there's no body before assign, so the apron env is empty... *) else ( - if M.tracing then M.traceli "apron" "assign %a = %a\n" d_lval lv d_exp e; + if M.tracing then M.traceli "apron" "assign %a = %a\n" RenameMapping.d_lval lv d_exp 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 e (fun apr' e' -> diff --git a/src/analyses/arinc.ml b/src/analyses/arinc.ml index fe2679cc50..03aa2ecea0 100644 --- a/src/analyses/arinc.ml +++ b/src/analyses/arinc.ml @@ -137,7 +137,7 @@ struct let return_code_is_success z = Cilint.is_zero_cilint z || Cilint.compare_cilint z Cilint.one_cilint = 0 let str_return_code i = if return_code_is_success i then "SUCCESS" else "ERROR" let str_return_dlval (v,o as dlval) = - sprint d_lval (Lval.CilLval.to_lval dlval) ^ "_" ^ string_of_int v.vdecl.line |> + sprint RenameMapping.d_lval (Lval.CilLval.to_lval dlval) ^ "_" ^ string_of_int v.vdecl.line |> Str.global_replace (Str.regexp "[^a-zA-Z0-9]") "_" let add_return_dlval env kind dlval = ArincUtil.add_return_var env.procid kind (str_return_dlval dlval) @@ -152,17 +152,17 @@ struct | a when not (Queries.LS.is_top a) && Queries.LS.cardinal a > 0 -> let top_elt = (dummyFunDec.svar, `NoOffset) in let a' = if Queries.LS.mem top_elt a then ( - M.debug "mayPointTo: query result for %a contains TOP!" d_exp exp; (* UNSOUND *) + M.debug "mayPointTo: query result for %a contains TOP!" RenameMapping.d_exp exp; (* UNSOUND *) Queries.LS.remove top_elt a ) else a in Queries.LS.elements a' | v -> - M.debug "mayPointTo: query result for %a is %a" d_exp exp Queries.LS.pretty v; + M.debug "mayPointTo: query result for %a is %a" RenameMapping.d_exp exp Queries.LS.pretty v; (*failwith "mayPointTo"*) [] let iterMayPointTo ctx exp f = mayPointTo ctx exp |> List.iter f - let debugMayPointTo ctx exp = M.debug "%a mayPointTo %a" d_exp exp (Pretty.d_list ", " Lval.CilLval.pretty) (mayPointTo ctx exp) + let debugMayPointTo ctx exp = M.debug "%a mayPointTo %a" RenameMapping.d_exp exp (Pretty.d_list ", " Lval.CilLval.pretty) (mayPointTo ctx exp) (* transfer functions *) @@ -184,7 +184,7 @@ struct let edges_added = ref false in let f dlval = (* M.debug @@ "assign: MayPointTo " ^ sprint d_plainlval lval ^ ": " ^ sprint d_plainexp (Lval.CilLval.to_exp dlval); *) - let is_ret_type = try is_return_code_type @@ Lval.CilLval.to_exp dlval with Cilfacade.TypeOfError Index_NonArray -> M.debug "assign: Cilfacade.typeOf %a threw exception Errormsg.Error \"Bug: typeOffset: Index on a non-array\". Will assume this is a return type to remain sound." d_exp (Lval.CilLval.to_exp dlval); true in + let is_ret_type = try is_return_code_type @@ Lval.CilLval.to_exp dlval with Cilfacade.TypeOfError Index_NonArray -> M.debug "assign: Cilfacade.typeOf %a threw exception Errormsg.Error \"Bug: typeOffset: Index on a non-array\". Will assume this is a return type to remain sound." RenameMapping.d_exp (Lval.CilLval.to_exp dlval); true in if (not is_ret_type) || Lval.CilLval.has_index dlval then () else let dlval = global_dlval dlval "assign" in edges_added := true; @@ -320,7 +320,7 @@ struct let is_creating_fun = startsWith (Functions.prefix^"Create") f.vname in if M.tracing && is_arinc_fun then ( (* M.tracel "arinc" "found %s(%s)\n" f.vname args_str *) - M.debug "found %s(%a) in %s" f.vname (Pretty.d_list ", " d_exp) arglist env.fundec.svar.vname + M.debug "found %s(%a) in %s" f.vname (Pretty.d_list ", " RenameMapping.d_exp) arglist env.fundec.svar.vname ); let is_error_handler = env.pname = pname_ErrorHandler in let eval_int exp = @@ -339,7 +339,7 @@ struct (* call assign for all analyses (we only need base)! *) | AddrOf lval -> ctx.emit (Assign {lval; exp = mkAddrOf @@ var id}) (* TODO not needed for the given code, but we could use Queries.MayPointTo exp in this case *) - | _ -> failwith @@ "Could not assign id. Expected &id. Found "^sprint d_exp exp + | _ -> failwith @@ "Could not assign id. Expected &id. Found "^sprint RenameMapping.d_exp exp in let assign_id_by_name resource_type name id = assign_id id (get_id (resource_type, eval_str name)) diff --git a/src/cdomains/basetype.ml b/src/cdomains/basetype.ml index 3d48c74292..138d60216d 100644 --- a/src/cdomains/basetype.ml +++ b/src/cdomains/basetype.ml @@ -26,8 +26,8 @@ struct let show x = if RichVarinfo.BiVarinfoMap.Collection.mem_varinfo x then let description = RichVarinfo.BiVarinfoMap.Collection.describe_varinfo x in - "(" ^ x.vname ^ ", " ^ description ^ ")" - else x.vname + "(" ^ RenameMapping.show_varinfo x ^ ", " ^ description ^ ")" + else RenameMapping.show_varinfo x let pretty () x = Pretty.text (show x) let pretty_trace () x = Pretty.dprintf "%s on %a" x.vname CilType.Location.pretty x.vdecl let get_location x = x.vdecl diff --git a/src/framework/edge.ml b/src/framework/edge.ml index 376934510b..22ae3efb78 100644 --- a/src/framework/edge.ml +++ b/src/framework/edge.ml @@ -34,6 +34,7 @@ type t = [@@deriving eq, to_yojson] let dn_exp = RenameMapping.dn_exp +let dn_lval = RenameMapping.dn_lval let pretty () = function | Test (exp, b) -> if b then Pretty.dprintf "Pos(%a)" dn_exp exp else Pretty.dprintf "Neg(%a)" dn_exp exp @@ -45,10 +46,11 @@ let pretty () = function | Ret (None,f) -> Pretty.dprintf "return" | ASM (_,_,_) -> Pretty.text "ASM ..." | Skip -> Pretty.text "skip" - | VDecl v -> Cil.defaultCilPrinter#pVDecl () v + | VDecl v -> RenameMapping.incremental_aware_printer#pVDecl () v | SelfLoop -> Pretty.text "SelfLoop" let d_exp = RenameMapping.d_exp +let d_lval = RenameMapping.d_lval let pretty_plain () = function | Assign (lv,rv) -> dprintf "Assign '%a = %a' " d_lval lv d_exp rv diff --git a/src/incremental/renameMapping.ml b/src/incremental/renameMapping.ml index 2251d45899..ed5cfdcea7 100644 --- a/src/incremental/renameMapping.ml +++ b/src/incremental/renameMapping.ml @@ -22,21 +22,48 @@ let show_varinfo (varinfo: varinfo) = Printf.printf "Accessing renamed: %s -> %s\n" varinfo.vname (get_old_or_updated_varinfo_name varinfo); get_old_or_updated_varinfo_name varinfo +(*in original Cil v.vname is hardcoded*) +let pVDeclImpl () (v:varinfo) (pType) (pAttrs) = + (* First the storage modifiers *) + Pretty.(text (if v.vinline then "__inline " else "") + ++ d_storage () v.vstorage + ++ (pType (Some (Pretty.text (show_varinfo v))) () v.vtype) + ++ Pretty.text " " + ++ pAttrs () v.vattr) class incremental_printer : Cil.cilPrinter = object(self) inherit Cil.defaultCilPrinterClass method pVar (v:varinfo) = Pretty.text (show_varinfo v) - end;; + + (* variable declaration *) + method pVDecl () (v:varinfo) = pVDeclImpl () v self#pType self#pAttrs +end;; class plain_incremental_printer : Cil.cilPrinter = object(self) inherit Cil.plainCilPrinterClass method pVar (v:varinfo) = Pretty.text (show_varinfo v) + + method pVDecl () (v:varinfo) = pVDeclImpl () v self#pType self#pAttrs end;; let incremental_aware_printer = new incremental_printer let plain_incremental_aware_printer = new plain_incremental_printer -let d_exp () e = printExp incremental_aware_printer () e +let d_exp () e = + let _ = Pretty.printf "Printing Exp: %a\n" (printExp incremental_aware_printer) e in + let _ = match e with + | BinOp (_, exp1, exp2, _) -> + ignore@@Pretty.printf "BinOp: %a and %a\n" (printExp incremental_aware_printer) exp1 (printExp incremental_aware_printer) exp2; + Pretty.printf "%s\n" (Printexc.get_callstack 15 |> Printexc.raw_backtrace_to_string); + | _ -> + Pretty.printf ""; + in + + printExp incremental_aware_printer () e + +let d_lval () l = printLval incremental_aware_printer () l + +let d_stmt () s = printStmt incremental_aware_printer () s (* A hack to allow forward reference of d_exp. Copy from Cil. *) let pd_exp : (unit -> exp -> Pretty.doc) ref = @@ -44,6 +71,12 @@ let pd_exp : (unit -> exp -> Pretty.doc) ref = let _ = pd_exp := d_exp +let pd_lval : (unit -> lval -> Pretty.doc) ref = ref (fun _ -> failwith "") +let _ = pd_lval := d_lval + +let pd_stmt : (unit -> stmt -> Pretty.doc) ref = ref (fun _ -> failwith "") +let _ = pd_stmt := d_stmt + (*Fixme: Im a copy of Cil.dn_obj because i couldnt figure out why I couldn't access Cil.dn_obj*) let dn_obj (func: unit -> 'a -> Pretty.doc) : (unit -> 'a -> Pretty.doc) = begin @@ -60,3 +93,7 @@ let dn_obj (func: unit -> 'a -> Pretty.doc) : (unit -> 'a -> Pretty.doc) = end let dn_exp = (dn_obj d_exp) + +let dn_lval = (dn_obj d_lval) + +let dn_stmt = (dn_obj d_stmt) \ No newline at end of file From 3a11fb917c1ee8ef88d3a6d7abaa6856d25e985c Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Mon, 9 May 2022 10:49:21 +0200 Subject: [PATCH 10/90] Cleanup print statements and added some docu. --- scripts/test-incremental-multiple.sh | 4 +- src/analyses/base.ml | 63 +++++----------------------- src/analyses/spec.ml | 2 +- src/framework/analyses.ml | 2 - src/incremental/compareAST.ml | 13 ++---- src/incremental/compareCFG.ml | 3 -- src/incremental/compareCIL.ml | 7 ---- src/incremental/renameMapping.ml | 35 +++++++--------- 8 files changed, 32 insertions(+), 97 deletions(-) diff --git a/scripts/test-incremental-multiple.sh b/scripts/test-incremental-multiple.sh index 87b7e150ce..a910e8498a 100644 --- a/scripts/test-incremental-multiple.sh +++ b/scripts/test-incremental-multiple.sh @@ -16,13 +16,13 @@ patch -p0 -b <$patch1 cat $source -./goblint --conf $conf $args --enable incremental.load --set save_run $base/$test-incrementalrun $source &> $base/$test.after.incr1.log --html +./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 --set save_run $base/$test-incrementalrun $source &> $base/$test.after.incr2.log --html +./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 diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 65dff1a699..3c8f2a6b00 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -845,7 +845,7 @@ struct do_offs (AD.map (add_offset_varinfo (convert_offset a gs st ofs)) adr) ofs | `Bot -> AD.bot () | _ -> - M.debug ~category:Analyzer "Failed evaluating %a to lvalue" d_lval lval; do_offs AD.unknown_ptr ofs + M.debug ~category:Analyzer "Failed evaluating %a to lvalue" RenameMapping.d_lval lval; do_offs AD.unknown_ptr ofs end (* run eval_rv from above and keep a result that is bottom *) @@ -1128,7 +1128,7 @@ struct with Cilfacade.TypeOfError _ -> (* If we cannot determine the correct type here, we go with the one of the LVal *) (* This will usually lead to a type mismatch in the ValueDomain (and hence supertop) *) - M.warn "Cilfacade.typeOfLval failed Could not obtain the type of %a" d_lval (Var x, cil_offset); + M.warn "Cilfacade.typeOfLval failed Could not obtain the type of %a" RenameMapping.d_lval (Var x, cil_offset); lval_type in let update_offset old_value = @@ -1307,7 +1307,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\n" RenameMapping.d_lval x VD.pretty value; (match value with | `Int n -> let ikind = Cilfacade.get_ikind_exp (Lval lval) in @@ -1320,13 +1320,13 @@ 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 %s\n" d_lval x (BI.to_string n); + if M.tracing then M.tracec "invariant" "Yes, %a is not %s\n" RenameMapping.d_lval x (BI.to_string 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\n" RenameMapping.d_lval x AD.pretty n; match eval_rv_address a gs st (Lval x) with | `Address a when AD.is_definite n -> Some (x, `Address (AD.diff a n)) @@ -1353,7 +1353,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 %s\n\n" d_lval x (BI.to_string n); + if M.tracing then M.tracec "invariant" "Yes, success! %a is not %s\n\n" RenameMapping.d_lval x (BI.to_string n); Some (x, `Int (range_from n)) | None -> None end @@ -1368,7 +1368,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 %s\n\n" d_lval x (BI.to_string n); + if M.tracing then M.tracec "invariant" "Yes, success! %a is not %s\n\n" RenameMapping.d_lval x (BI.to_string n); Some (x, `Int (range_from n)) | None -> None end @@ -1428,12 +1428,12 @@ struct in match derived_invariant exp tv with | Some (lval, value) -> - 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\n" RenameMapping.d_lval lval VD.pretty value; let addr = eval_lv a gs st lval in if (AD.is_top addr) then st else let oldval = get a gs st addr None in (* None is ok here, we could try to get more precise, but this is ok (reading at unknown position in array) *) - let oldval = if is_some_bot oldval then (M.tracec "invariant" "%a is bot! This should not happen. Will continue with top!" d_lval lval; VD.top ()) else oldval in + let oldval = if is_some_bot oldval then (M.tracec "invariant" "%a is bot! This should not happen. Will continue with top!" RenameMapping.d_lval lval; VD.top ()) else oldval in let t_lval = Cilfacade.typeOfLval lval in let state_with_excluded = set a gs st addr t_lval value ~invariant:true ~ctx:(Some ctx) in let value = get a gs state_with_excluded addr None in @@ -1639,7 +1639,7 @@ struct let v = VD.meet oldv c' in if is_some_bot v then raise Deadcode 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 oldv VD.pretty v ID.pretty c VD.pretty c'; + if M.tracing then M.tracel "inv" "improve lval %a from %a to %a (c = %a, c' = %a)\n" RenameMapping.d_lval x VD.pretty oldv VD.pretty v ID.pretty c VD.pretty c'; set' x v st )) | Const _ -> st (* nothing to do *) @@ -2019,47 +2019,6 @@ struct | _ -> [] let assert_fn ctx e should_warn change = - (* - let _ = Hashtbl.iter (fun fun_name map -> - begin - Printf.printf "%s: [" fun_name; - Hashtbl.iter (fun from tox -> Printf.printf "%s -> %s; " from tox) map; - Printf.printf "]\n"; - end - ) !CompareCIL.rename_map in - - let parent_function: fundec = Node.find_fundec ctx.node in - - (*Performs the actual rename on lvals for renamed local variables.*) - let rename_lval lhost offset = - let new_lhost = match lhost with - | Var varinfo -> - varinfo.vname <- CompareCIL.get_local_rename parent_function.svar.vname varinfo.vname; - Var varinfo - | _ -> lhost - in - (new_lhost, offset) - in - - (*Recusivly go through the expression and rename all occurences of local variables. TODO: What happens with global vars*) - let rec rename_exp (exp: exp) = match exp with - | Lval (lhost, offset) -> Lval (rename_lval lhost offset) - | Real e -> Real (rename_exp e) - | Imag e -> Imag (rename_exp e) - | SizeOfE e -> SizeOfE (rename_exp e) - | AlignOfE e -> AlignOfE (rename_exp e) - | UnOp (unop, e, typ) -> UnOp (unop, rename_exp e, typ) - | BinOp (binop, e1, e2, typ) -> BinOp (binop, rename_exp e1, rename_exp e2, typ) - | Question (e1, e2, e3, typ) -> Question (rename_exp e1, rename_exp e2, rename_exp e3, typ) - | CastE (typ, e) -> CastE (typ, rename_exp e) - | AddrOf (lhost, offset) -> AddrOf (rename_lval lhost offset) - | StartOf (lhost, offset) -> StartOf (rename_lval lhost offset) - (*TODO: AddrOfLabel?*) - | _ -> exp - in - *) - - let check_assert e st = match eval_rv (Analyses.ask_of_ctx ctx) ctx.global st e with | `Int v when ID.is_bool v -> @@ -2131,8 +2090,6 @@ struct invalidate ~ctx (Analyses.ask_of_ctx ctx) gs st addrs let special ctx (lv:lval option) (f: varinfo) (args: exp list) = - List.iter (fun x -> ignore @@ Pretty.printf "%a\n" RenameMapping.d_exp x;) args; - 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 (RenameMapping.show_varinfo f); diff --git a/src/analyses/spec.ml b/src/analyses/spec.ml index 38be505f5d..9fcfd7bb61 100644 --- a/src/analyses/spec.ml +++ b/src/analyses/spec.ml @@ -256,7 +256,7 @@ struct 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 "assign (none in D): %a = %a [%a]" d_lval lval d_exp rval d_plainexp rval; + M.debug "assign (none in D): %a = %a [%a]" RenameMapping.d_lval lval d_exp rval d_plainexp rval; m (* diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 21d015c512..ddb76ccfd6 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -191,8 +191,6 @@ struct match get_string "result" with | "pretty" -> ignore (fprintf out "%a\n" pretty (Lazy.force table)) | "fast_xml" -> - Printf.printf "%s" (Printexc.get_callstack 15 |> Printexc.raw_backtrace_to_string); - let module SH = BatHashtbl.Make (Basetype.RawStrings) in let file2funs = SH.create 100 in let funs2node = SH.create 100 in diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 1d1456bdf4..c4330d25a3 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -191,7 +191,7 @@ and eq_varinfo (a: varinfo) (b: varinfo) (context: context) = in (*If the following is a method call, we need to check if we have a mapping for that method call. *) - let typ_context, did_context_switch = match b.vtype with + let typ_context = match b.vtype with | TFun(_, _, _, _) -> ( let new_locals = List.find_opt (fun x -> match x with | {original_method_name; new_method_name; parameter_renames} -> original_method_name = a.vname && new_method_name = b.vname @@ -200,22 +200,17 @@ and eq_varinfo (a: varinfo) (b: varinfo) (context: context) = match new_locals with | Some locals -> (*Printf.printf "Performing context switch. New context=%s\n" (context_to_string (locals.parameter_renames, method_contexts));*) - (locals.parameter_renames, method_contexts), true - | None -> ([], method_contexts), false + (locals.parameter_renames, method_contexts) + | None -> ([], method_contexts) ) - | _ -> context, false + | _ -> context in let typeCheck = eq_typ a.vtype b.vtype typ_context in let attrCheck = GobList.equal (eq_attribute context) a.vattr b.vattr in - (*let _ = if isNamingOk then a.vname <- b.vname in*) - - (*let _ = Printf.printf "Comparing vars: %s = %s\n" a.vname b.vname in *) - (*a.vname = b.vname*) let result = isNamingOk && typeCheck && attrCheck && a.vstorage = b.vstorage && a.vglob = b.vglob && a.vaddrof = b.vaddrof in - if did_context_switch then Printf.printf "Undo context switch \n"; (*Save rename mapping for future usage. If this function later turns out to actually being changed, the new varinfo id will be used anyway and this mapping has no effect*) diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index 25b5f64ccf..e87df4f832 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -47,8 +47,6 @@ module NTH = Hashtbl.Make( * process on their successors. If a node from the old CFG can not be matched, it is added to diff and no further * comparison is done for its successors. The two function entry nodes make up the tuple to start the comparison from. *) let compareCfgs (module CfgOld : CfgForward) (module CfgNew : CfgForward) fun1 fun2 = - let _ = Printf.printf "ComparingCfgs" in - let diff = NH.create 113 in let same = NTH.create 113 in let waitingList : (node * node) t = Queue.create () in @@ -132,7 +130,6 @@ let reexamine f1 f2 (same : unit NTH.t) (diffNodes1 : unit NH.t) (module CfgOld (NTH.to_seq_keys same, NH.to_seq_keys diffNodes1, NH.to_seq_keys diffNodes2) let compareFun (module CfgOld : CfgForward) (module CfgNew : CfgForward) fun1 fun2 = - let _ = Printf.printf "Comparing funs" in let same, diff = compareCfgs (module CfgOld) (module CfgNew) fun1 fun2 in let unchanged, diffNodes1, diffNodes2 = reexamine fun1 fun2 same diff (module CfgOld) (module CfgNew) in List.of_seq unchanged, List.of_seq diffNodes1, List.of_seq diffNodes2 diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 643673829a..01a3672d51 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -60,13 +60,9 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * cfg) option) (global_cont false, None else (* Here the local variables are checked to be equal *) - - let sizeEqual, local_rename = context_aware_compare a.slocals b.slocals headerContext in let context: context = (local_rename, global_context) in - let _ = Printf.printf "Context=%s\n" (CompareAST.context_to_string context) in - let sameDef = unchangedHeader && sizeEqual in if not sameDef then (false, None) @@ -75,7 +71,6 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * cfg) option) (global_cont | None -> eq_block (a.sbody, a) (b.sbody, b) context, None | Some (cfgOld, cfgNew) -> - Printf.printf "compareCIL.eqF: Compaing 2 cfgs now\n"; let module CfgOld : MyCFG.CfgForward = struct let next = cfgOld end in let module CfgNew : MyCFG.CfgForward = struct let next = cfgNew end in let matches, diffNodes1, diffNodes2 = compareFun (module CfgOld) (module CfgNew) a b in @@ -97,8 +92,6 @@ let eq_glob (a: global) (b: global) (cfgs : (cfg * cfg) option) (global_context: | _ -> 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 _ = Printf.printf "Comparing Cil files\n" in - let cfgs = if GobConfig.get_string "incremental.compare" = "cfg" then Some (CfgTools.getCFG oldAST |> fst, CfgTools.getCFG newAST |> fst) else None in diff --git a/src/incremental/renameMapping.ml b/src/incremental/renameMapping.ml index ed5cfdcea7..e3f332e555 100644 --- a/src/incremental/renameMapping.ml +++ b/src/incremental/renameMapping.ml @@ -1,5 +1,13 @@ open Cil +(* + This file remembers which varinfos were renamed in the process of incremental analysis. + If the functions of this file are used to pretty print varinfos and their names, the correct updated name + will be shown instead of the old varinfo name that was used when the analysis result was created. + + The rename entries are filled up by CompareAST.ml while the comparison takes place. +*) + module IncrementallyUpdatedVarinfoMap = Hashtbl.Make (CilType.Varinfo) (*Mapps a varinfo to its updated name*) @@ -10,7 +18,6 @@ let get_old_or_updated_varinfo_name (old_varinfo: varinfo) = Option.value r ~default:old_varinfo.vname let store_update_varinfo_name (old_varinfo: varinfo) (new_name: string) = - Printf.printf "Storing renamed name: %s -> %s\n" old_varinfo.vname new_name; IncrementallyUpdatedVarinfoMap.add !renamedVarinfoMap old_varinfo new_name (* @@ -18,9 +25,7 @@ let store_update_varinfo_name (old_varinfo: varinfo) (new_name: string) = Dev Note: Putting this into CilType.Varinfo results in a cyclic dependency. It should not be put into CilType anyway, as CilType only defines types based on the types themselves, not implement any logic based on other components outside its own definitions. So I think it's cleaner this way. *) -let show_varinfo (varinfo: varinfo) = - Printf.printf "Accessing renamed: %s -> %s\n" varinfo.vname (get_old_or_updated_varinfo_name varinfo); - get_old_or_updated_varinfo_name varinfo +let show_varinfo = get_old_or_updated_varinfo_name (*in original Cil v.vname is hardcoded*) let pVDeclImpl () (v:varinfo) (pType) (pAttrs) = @@ -33,33 +38,23 @@ let pVDeclImpl () (v:varinfo) (pType) (pAttrs) = class incremental_printer : Cil.cilPrinter = object(self) inherit Cil.defaultCilPrinterClass - method pVar (v:varinfo) = Pretty.text (show_varinfo v) + method! pVar (v:varinfo) = Pretty.text (show_varinfo v) (* variable declaration *) - method pVDecl () (v:varinfo) = pVDeclImpl () v self#pType self#pAttrs + method! pVDecl () (v:varinfo) = pVDeclImpl () v self#pType self#pAttrs end;; class plain_incremental_printer : Cil.cilPrinter = object(self) inherit Cil.plainCilPrinterClass - method pVar (v:varinfo) = Pretty.text (show_varinfo v) + method! pVar (v:varinfo) = Pretty.text (show_varinfo v) - method pVDecl () (v:varinfo) = pVDeclImpl () v self#pType self#pAttrs + method! pVDecl () (v:varinfo) = pVDeclImpl () v self#pType self#pAttrs end;; let incremental_aware_printer = new incremental_printer let plain_incremental_aware_printer = new plain_incremental_printer -let d_exp () e = - let _ = Pretty.printf "Printing Exp: %a\n" (printExp incremental_aware_printer) e in - let _ = match e with - | BinOp (_, exp1, exp2, _) -> - ignore@@Pretty.printf "BinOp: %a and %a\n" (printExp incremental_aware_printer) exp1 (printExp incremental_aware_printer) exp2; - Pretty.printf "%s\n" (Printexc.get_callstack 15 |> Printexc.raw_backtrace_to_string); - | _ -> - Pretty.printf ""; - in - - printExp incremental_aware_printer () e +let d_exp () e = printExp incremental_aware_printer () e let d_lval () l = printLval incremental_aware_printer () l @@ -77,7 +72,7 @@ let _ = pd_lval := d_lval let pd_stmt : (unit -> stmt -> Pretty.doc) ref = ref (fun _ -> failwith "") let _ = pd_stmt := d_stmt -(*Fixme: Im a copy of Cil.dn_obj because i couldnt figure out why I couldn't access Cil.dn_obj*) +(*Fixme: Im a copy of Cil.dn_obj but Cil.dn_obj is not exported. So export Cil.dn_obj and then replace me.*) let dn_obj (func: unit -> 'a -> Pretty.doc) : (unit -> 'a -> Pretty.doc) = begin (* construct the closure to return *) From b7fac8970b55ec40f148f7a9042e856d2d8ee9cb Mon Sep 17 00:00:00 2001 From: Tim ORtel <100865202+TimOrtel@users.noreply.github.com> Date: Mon, 9 May 2022 15:06:07 +0200 Subject: [PATCH 11/90] Renamed context to rename_mapping --- src/incremental/compareAST.ml | 230 +++++++++++++++++----------------- src/incremental/compareCFG.ml | 26 ++-- src/incremental/compareCIL.ml | 46 +++---- 3 files changed, 151 insertions(+), 151 deletions(-) diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index c4330d25a3..bc9ac84552 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -5,18 +5,18 @@ type global_type = Fun | Decl | Var and global_identifier = {name: string ; global_t: global_type} [@@deriving ord] -type local_rename = string * string +type local_rename_assumption = string * string (**) -type method_context = {original_method_name: string; new_method_name: string; parameter_renames: (string * string) list} +type method_rename_assumption = {original_method_name: string; new_method_name: string; parameter_renames: (string * string) list} -(*context is carried through the stack when comparing the AST. Holds a list of rename assumptions.*) -type context = (local_rename list) * (method_context list) +(*rename_mapping is carried through the stack when comparing the AST. Holds a list of rename assumptions.*) +type rename_mapping = (local_rename_assumption list) * (method_rename_assumption list) -(*Compares two names, being aware of the context. Returns true iff: +(*Compares two names, being aware of the rename_mapping. Returns true iff: 1. there is a rename for name1 -> name2 = rename(name1) 2. there is no rename for name1 -> name1 = name2*) -let context_aware_name_comparison (name1: string) (name2: string) (context: context) = - let (local_c, method_c) = context in +let rename_mapping_aware_name_comparison (name1: string) (name2: string) (rename_mapping: rename_mapping) = + let (local_c, method_c) = rename_mapping in let existingAssumption: (string*string) option = List.find_opt (fun x -> match x with (original, now) -> original = name1) local_c in match existingAssumption with @@ -31,8 +31,8 @@ let string_tuple_to_string (tuple: (string * string) list) = "[" ^ (tuple |> List.map (fun x -> match x with (first, second) -> "(" ^ first ^ " -> " ^ second ^ ")") |> String.concat ", ") ^ "]" -let context_to_string (context: context) = - let (local, methods) = context in +let rename_mapping_to_string (rename_mapping: rename_mapping) = + let (local, methods) = rename_mapping in let local_string = string_tuple_to_string local in let methods_string: string = methods |> List.map (fun x -> match x with {original_method_name; new_method_name; parameter_renames} -> @@ -59,33 +59,33 @@ let compare_name (a: string) (b: string) = let anon_union = "__anonunion_" in if a = b then true else BatString.(starts_with a anon_struct && starts_with b anon_struct || starts_with a anon_union && starts_with b anon_union) -let rec eq_constant (context: context) (a: constant) (b: constant) = +let rec eq_constant (rename_mapping: rename_mapping) (a: constant) (b: constant) = match a, b with | CInt (val1, kind1, str1), CInt (val2, kind2, str2) -> Cilint.compare_cilint val1 val2 = 0 && kind1 = kind2 (* Ignore string representation, i.e. 0x2 == 2 *) - | CEnum (exp1, str1, enuminfo1), CEnum (exp2, str2, enuminfo2) -> eq_exp exp1 exp2 context (* Ignore name and enuminfo *) + | CEnum (exp1, str1, enuminfo1), CEnum (exp2, str2, enuminfo2) -> eq_exp exp1 exp2 rename_mapping (* Ignore name and enuminfo *) | a, b -> a = b -and eq_exp2 (context: context) (a: exp) (b: exp) = eq_exp a b context +and eq_exp2 (rename_mapping: rename_mapping) (a: exp) (b: exp) = eq_exp a b rename_mapping -and eq_exp (a: exp) (b: exp) (context: context) = +and eq_exp (a: exp) (b: exp) (rename_mapping: rename_mapping) = match a, b with - | Const c1, Const c2 -> eq_constant context c1 c2 - | Lval lv1, Lval lv2 -> eq_lval lv1 lv2 context - | SizeOf typ1, SizeOf typ2 -> eq_typ typ1 typ2 context - | SizeOfE exp1, SizeOfE exp2 -> eq_exp exp1 exp2 context + | Const c1, Const c2 -> eq_constant rename_mapping c1 c2 + | Lval lv1, Lval lv2 -> eq_lval lv1 lv2 rename_mapping + | SizeOf typ1, SizeOf typ2 -> eq_typ typ1 typ2 rename_mapping + | SizeOfE exp1, SizeOfE exp2 -> eq_exp exp1 exp2 rename_mapping | SizeOfStr str1, SizeOfStr str2 -> str1 = str2 (* possibly, having the same length would suffice *) - | AlignOf typ1, AlignOf typ2 -> eq_typ typ1 typ2 context - | AlignOfE exp1, AlignOfE exp2 -> eq_exp exp1 exp2 context - | UnOp (op1, exp1, typ1), UnOp (op2, exp2, typ2) -> op1 == op2 && eq_exp exp1 exp2 context && eq_typ typ1 typ2 context - | BinOp (op1, left1, right1, typ1), BinOp (op2, left2, right2, typ2) -> op1 = op2 && eq_exp left1 left2 context && eq_exp right1 right2 context && eq_typ typ1 typ2 context - | CastE (typ1, exp1), CastE (typ2, exp2) -> eq_typ typ1 typ2 context && eq_exp exp1 exp2 context - | AddrOf lv1, AddrOf lv2 -> eq_lval lv1 lv2 context - | StartOf lv1, StartOf lv2 -> eq_lval lv1 lv2 context + | AlignOf typ1, AlignOf typ2 -> eq_typ typ1 typ2 rename_mapping + | AlignOfE exp1, AlignOfE exp2 -> eq_exp exp1 exp2 rename_mapping + | UnOp (op1, exp1, typ1), UnOp (op2, exp2, typ2) -> op1 == op2 && eq_exp exp1 exp2 rename_mapping && eq_typ typ1 typ2 rename_mapping + | BinOp (op1, left1, right1, typ1), BinOp (op2, left2, right2, typ2) -> op1 = op2 && eq_exp left1 left2 rename_mapping && eq_exp right1 right2 rename_mapping && eq_typ typ1 typ2 rename_mapping + | CastE (typ1, exp1), CastE (typ2, exp2) -> eq_typ typ1 typ2 rename_mapping && eq_exp exp1 exp2 rename_mapping + | AddrOf lv1, AddrOf lv2 -> eq_lval lv1 lv2 rename_mapping + | StartOf lv1, StartOf lv2 -> eq_lval lv1 lv2 rename_mapping | _, _ -> false -and eq_lhost (a: lhost) (b: lhost) (context: context) = match a, b with - Var v1, Var v2 -> eq_varinfo v1 v2 context - | Mem exp1, Mem exp2 -> eq_exp exp1 exp2 context +and eq_lhost (a: lhost) (b: lhost) (rename_mapping: rename_mapping) = match a, b with + Var v1, Var v2 -> eq_varinfo v1 v2 rename_mapping + | Mem exp1, Mem exp2 -> eq_exp exp1 exp2 rename_mapping | _, _ -> false and global_typ_acc: (typ * typ) list ref = ref [] (* TODO: optimize with physical Hashtbl? *) @@ -94,21 +94,21 @@ and mem_typ_acc (a: typ) (b: typ) acc = List.exists (fun p -> match p with (x, y and pretty_length () l = Pretty.num (List.length l) -and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (context: context) = +and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (rename_mapping: rename_mapping) = 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 *) let r = match a, b with - | TPtr (typ1, attr1), TPtr (typ2, attr2) -> eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_attribute context) attr1 attr2 - | TArray (typ1, (Some lenExp1), attr1), TArray (typ2, (Some lenExp2), attr2) -> eq_typ_acc typ1 typ2 acc context && eq_exp lenExp1 lenExp2 context && GobList.equal (eq_attribute context) attr1 attr2 - | TArray (typ1, None, attr1), TArray (typ2, None, attr2) -> eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_attribute context) attr1 attr2 + | TPtr (typ1, attr1), TPtr (typ2, attr2) -> eq_typ_acc typ1 typ2 acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 + | TArray (typ1, (Some lenExp1), attr1), TArray (typ2, (Some lenExp2), attr2) -> eq_typ_acc typ1 typ2 acc rename_mapping && eq_exp lenExp1 lenExp2 rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 + | TArray (typ1, None, attr1), TArray (typ2, None, attr2) -> eq_typ_acc typ1 typ2 acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 | TFun (typ1, (Some list1), varArg1, attr1), TFun (typ2, (Some list2), varArg2, attr2) - -> eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_args context acc) list1 list2 && varArg1 = varArg2 && - GobList.equal (eq_attribute context) attr1 attr2 + -> eq_typ_acc typ1 typ2 acc rename_mapping && GobList.equal (eq_args rename_mapping acc) list1 list2 && varArg1 = varArg2 && + GobList.equal (eq_attribute rename_mapping) attr1 attr2 | TFun (typ1, None, varArg1, attr1), TFun (typ2, None, varArg2, attr2) - -> eq_typ_acc typ1 typ2 acc context && varArg1 = varArg2 && - GobList.equal (eq_attribute context) attr1 attr2 - | TNamed (typinfo1, attr1), TNamed (typeinfo2, attr2) -> eq_typ_acc typinfo1.ttype typeinfo2.ttype acc context && GobList.equal (eq_attribute context) attr1 attr2 (* Ignore tname, treferenced *) - | TNamed (tinf, attr), b -> eq_typ_acc tinf.ttype b acc context (* Ignore tname, treferenced. TODO: dismiss attributes, or not? *) - | a, TNamed (tinf, attr) -> eq_typ_acc a tinf.ttype acc context (* Ignore tname, treferenced . TODO: dismiss attributes, or not? *) + -> eq_typ_acc typ1 typ2 acc rename_mapping && varArg1 = varArg2 && + GobList.equal (eq_attribute rename_mapping) attr1 attr2 + | TNamed (typinfo1, attr1), TNamed (typeinfo2, attr2) -> eq_typ_acc typinfo1.ttype typeinfo2.ttype acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 (* Ignore tname, treferenced *) + | TNamed (tinf, attr), b -> eq_typ_acc tinf.ttype b acc rename_mapping (* Ignore tname, treferenced. TODO: dismiss attributes, or not? *) + | a, TNamed (tinf, attr) -> eq_typ_acc a tinf.ttype acc rename_mapping (* Ignore tname, treferenced . TODO: dismiss attributes, or not? *) (* 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 ( @@ -117,97 +117,97 @@ and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (context: context) = ) else ( let acc = (a, b) :: acc in - let res = eq_compinfo compinfo1 compinfo2 acc context && GobList.equal (eq_attribute context) attr1 attr2 in + let res = eq_compinfo compinfo1 compinfo2 acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 in if res && compinfo1.cname <> compinfo2.cname then compinfo2.cname <- compinfo1.cname; if res then global_typ_acc := (a, b) :: !global_typ_acc; res ) - | TEnum (enuminfo1, attr1), TEnum (enuminfo2, attr2) -> let res = eq_enuminfo enuminfo1 enuminfo2 context && GobList.equal (eq_attribute context) attr1 attr2 in (if res && enuminfo1.ename <> enuminfo2.ename then enuminfo2.ename <- enuminfo1.ename); res - | TBuiltin_va_list attr1, TBuiltin_va_list attr2 -> GobList.equal (eq_attribute context) attr1 attr2 - | TVoid attr1, TVoid attr2 -> GobList.equal (eq_attribute context) attr1 attr2 - | TInt (ik1, attr1), TInt (ik2, attr2) -> ik1 = ik2 && GobList.equal (eq_attribute context) attr1 attr2 - | TFloat (fk1, attr1), TFloat (fk2, attr2) -> fk1 = fk2 && GobList.equal (eq_attribute context) attr1 attr2 + | TEnum (enuminfo1, attr1), TEnum (enuminfo2, attr2) -> let res = eq_enuminfo enuminfo1 enuminfo2 rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 in (if res && enuminfo1.ename <> enuminfo2.ename then enuminfo2.ename <- enuminfo1.ename); res + | TBuiltin_va_list attr1, TBuiltin_va_list attr2 -> GobList.equal (eq_attribute rename_mapping) attr1 attr2 + | TVoid attr1, TVoid attr2 -> GobList.equal (eq_attribute rename_mapping) attr1 attr2 + | TInt (ik1, attr1), TInt (ik2, attr2) -> ik1 = ik2 && GobList.equal (eq_attribute rename_mapping) attr1 attr2 + | TFloat (fk1, attr1), TFloat (fk2, attr2) -> fk1 = fk2 && GobList.equal (eq_attribute rename_mapping) attr1 attr2 | _, _ -> false in if Messages.tracing then Messages.traceu "compareast" "eq_typ_acc %a vs %a\n" d_type a d_type b; r -and eq_typ (a: typ) (b: typ) (context: context) = eq_typ_acc a b [] context +and eq_typ (a: typ) (b: typ) (rename_mapping: rename_mapping) = eq_typ_acc a b [] rename_mapping -and eq_eitems (context: context) (a: string * exp * location) (b: string * exp * location) = match a, b with - (name1, exp1, _l1), (name2, exp2, _l2) -> name1 = name2 && eq_exp exp1 exp2 context +and eq_eitems (rename_mapping: rename_mapping) (a: string * exp * location) (b: string * exp * location) = match a, b with + (name1, exp1, _l1), (name2, exp2, _l2) -> name1 = name2 && eq_exp exp1 exp2 rename_mapping (* Ignore location *) -and eq_enuminfo (a: enuminfo) (b: enuminfo) (context: context) = +and eq_enuminfo (a: enuminfo) (b: enuminfo) (rename_mapping: rename_mapping) = compare_name a.ename b.ename && - GobList.equal (eq_attribute context) a.eattr b.eattr && - GobList.equal (eq_eitems context) a.eitems b.eitems + GobList.equal (eq_attribute rename_mapping) a.eattr b.eattr && + GobList.equal (eq_eitems rename_mapping) a.eitems b.eitems (* Ignore ereferenced *) -and eq_args (context: context) (acc: (typ * typ) list) (a: string * typ * attributes) (b: string * typ * attributes) = match a, b with +and eq_args (rename_mapping: rename_mapping) (acc: (typ * typ) list) (a: string * typ * attributes) (b: string * typ * attributes) = match a, b with (name1, typ1, attr1), (name2, typ2, attr2) -> - context_aware_name_comparison name1 name2 context && eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_attribute context) attr1 attr2 + rename_mapping_aware_name_comparison name1 name2 rename_mapping && eq_typ_acc typ1 typ2 acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 -and eq_attrparam (context: context) (a: attrparam) (b: attrparam) = match a, b with - | ACons (str1, attrparams1), ACons (str2, attrparams2) -> str1 = str2 && GobList.equal (eq_attrparam context) attrparams1 attrparams2 - | ASizeOf typ1, ASizeOf typ2 -> eq_typ typ1 typ2 context - | ASizeOfE attrparam1, ASizeOfE attrparam2 -> eq_attrparam context attrparam1 attrparam2 +and eq_attrparam (rename_mapping: rename_mapping) (a: attrparam) (b: attrparam) = match a, b with + | ACons (str1, attrparams1), ACons (str2, attrparams2) -> str1 = str2 && GobList.equal (eq_attrparam rename_mapping) attrparams1 attrparams2 + | ASizeOf typ1, ASizeOf typ2 -> eq_typ typ1 typ2 rename_mapping + | ASizeOfE attrparam1, ASizeOfE attrparam2 -> eq_attrparam rename_mapping attrparam1 attrparam2 | ASizeOfS typsig1, ASizeOfS typsig2 -> typsig1 = typsig2 - | AAlignOf typ1, AAlignOf typ2 -> eq_typ typ1 typ2 context - | AAlignOfE attrparam1, AAlignOfE attrparam2 -> eq_attrparam context attrparam1 attrparam2 + | AAlignOf typ1, AAlignOf typ2 -> eq_typ typ1 typ2 rename_mapping + | AAlignOfE attrparam1, AAlignOfE attrparam2 -> eq_attrparam rename_mapping attrparam1 attrparam2 | AAlignOfS typsig1, AAlignOfS typsig2 -> typsig1 = typsig2 - | AUnOp (op1, attrparam1), AUnOp (op2, attrparam2) -> op1 = op2 && eq_attrparam context attrparam1 attrparam2 - | ABinOp (op1, left1, right1), ABinOp (op2, left2, right2) -> op1 = op2 && eq_attrparam context left1 left2 && eq_attrparam context right1 right2 - | ADot (attrparam1, str1), ADot (attrparam2, str2) -> eq_attrparam context attrparam1 attrparam2 && str1 = str2 - | AStar attrparam1, AStar attrparam2 -> eq_attrparam context attrparam1 attrparam2 - | AAddrOf attrparam1, AAddrOf attrparam2 -> eq_attrparam context attrparam1 attrparam2 - | AIndex (left1, right1), AIndex (left2, right2) -> eq_attrparam context left1 left2 && eq_attrparam context right1 right2 - | AQuestion (left1, middle1, right1), AQuestion (left2, middle2, right2) -> eq_attrparam context left1 left2 && eq_attrparam context middle1 middle2 && eq_attrparam context right1 right2 + | AUnOp (op1, attrparam1), AUnOp (op2, attrparam2) -> op1 = op2 && eq_attrparam rename_mapping attrparam1 attrparam2 + | ABinOp (op1, left1, right1), ABinOp (op2, left2, right2) -> op1 = op2 && eq_attrparam rename_mapping left1 left2 && eq_attrparam rename_mapping right1 right2 + | ADot (attrparam1, str1), ADot (attrparam2, str2) -> eq_attrparam rename_mapping attrparam1 attrparam2 && str1 = str2 + | AStar attrparam1, AStar attrparam2 -> eq_attrparam rename_mapping attrparam1 attrparam2 + | AAddrOf attrparam1, AAddrOf attrparam2 -> eq_attrparam rename_mapping attrparam1 attrparam2 + | AIndex (left1, right1), AIndex (left2, right2) -> eq_attrparam rename_mapping left1 left2 && eq_attrparam rename_mapping right1 right2 + | AQuestion (left1, middle1, right1), AQuestion (left2, middle2, right2) -> eq_attrparam rename_mapping left1 left2 && eq_attrparam rename_mapping middle1 middle2 && eq_attrparam rename_mapping right1 right2 | a, b -> a = b -and eq_attribute (context: context) (a: attribute) (b: attribute) = match a, b with - | Attr (name1, params1), Attr (name2, params2) -> name1 = name2 && GobList.equal (eq_attrparam context) params1 params2 +and eq_attribute (rename_mapping: rename_mapping) (a: attribute) (b: attribute) = match a, b with + | Attr (name1, params1), Attr (name2, params2) -> name1 = name2 && GobList.equal (eq_attrparam rename_mapping) params1 params2 -and eq_varinfo2 (context: context) (a: varinfo) (b: varinfo) = eq_varinfo a b context +and eq_varinfo2 (rename_mapping: rename_mapping) (a: varinfo) (b: varinfo) = eq_varinfo a b rename_mapping -and eq_varinfo (a: varinfo) (b: varinfo) (context: context) = +and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) = (*Printf.printf "Comp %s with %s\n" a.vname b.vname;*) - let (_, method_contexts) = context in + let (_, method_rename_mappings) = rename_mapping in - (*When we compare function names, we can directly compare the naming from the context if it exists.*) + (*When we compare function names, we can directly compare the naming from the rename_mapping if it exists.*) let isNamingOk = match b.vtype with | TFun(_, _, _, _) -> ( - let specific_method_context = List.find_opt (fun x -> match x with + let specific_method_rename_mapping = List.find_opt (fun x -> match x with | {original_method_name; new_method_name; parameter_renames} -> original_method_name = a.vname && new_method_name = b.vname - ) method_contexts in - match specific_method_context with - | Some method_context -> method_context.original_method_name = a.vname && method_context.new_method_name = b.vname + ) method_rename_mappings in + match specific_method_rename_mapping with + | Some method_rename_mapping -> method_rename_mapping.original_method_name = a.vname && method_rename_mapping.new_method_name = b.vname | None -> a.vname = b.vname ) - | _ -> context_aware_name_comparison a.vname b.vname context + | _ -> rename_mapping_aware_name_comparison a.vname b.vname rename_mapping in (*If the following is a method call, we need to check if we have a mapping for that method call. *) - let typ_context = match b.vtype with + let typ_rename_mapping = match b.vtype with | TFun(_, _, _, _) -> ( let new_locals = List.find_opt (fun x -> match x with | {original_method_name; new_method_name; parameter_renames} -> original_method_name = a.vname && new_method_name = b.vname - ) method_contexts in + ) method_rename_mappings in match new_locals with | Some locals -> - (*Printf.printf "Performing context switch. New context=%s\n" (context_to_string (locals.parameter_renames, method_contexts));*) - (locals.parameter_renames, method_contexts) - | None -> ([], method_contexts) + (*Printf.printf "Performing rename_mapping switch. New rename_mapping=%s\n" (rename_mapping_to_string (locals.parameter_renames, method_rename_mappings));*) + (locals.parameter_renames, method_rename_mappings) + | None -> ([], method_rename_mappings) ) - | _ -> context + | _ -> rename_mapping in - let typeCheck = eq_typ a.vtype b.vtype typ_context in - let attrCheck = GobList.equal (eq_attribute context) a.vattr b.vattr in + let typeCheck = eq_typ a.vtype b.vtype typ_rename_mapping in + let attrCheck = GobList.equal (eq_attribute rename_mapping) a.vattr b.vattr in let result = isNamingOk && typeCheck && attrCheck && a.vstorage = b.vstorage && a.vglob = b.vglob && a.vaddrof = b.vaddrof in @@ -220,36 +220,36 @@ and eq_varinfo (a: varinfo) (b: varinfo) (context: context) = (* Ignore the location, vid, vreferenced, vdescr, vdescrpure, vinline *) (* Accumulator is needed because of recursive types: we have to assume that two types we already encountered in a previous step of the recursion are equivalent *) -and eq_compinfo (a: compinfo) (b: compinfo) (acc: (typ * typ) list) (context: context) = +and eq_compinfo (a: compinfo) (b: compinfo) (acc: (typ * typ) list) (rename_mapping: rename_mapping) = a.cstruct = b.cstruct && compare_name a.cname b.cname && - GobList.equal (fun a b-> eq_fieldinfo a b acc context) a.cfields b.cfields && - GobList.equal (eq_attribute context) a.cattr b.cattr && + GobList.equal (fun a b-> eq_fieldinfo a b acc rename_mapping) a.cfields b.cfields && + GobList.equal (eq_attribute rename_mapping) a.cattr b.cattr && a.cdefined = b.cdefined (* Ignore ckey, and ignore creferenced *) -and eq_fieldinfo (a: fieldinfo) (b: fieldinfo) (acc: (typ * typ) list) (context: context) = +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; - let r = a.fname = b.fname && eq_typ_acc a.ftype b.ftype acc context && a.fbitfield = b.fbitfield && GobList.equal (eq_attribute context) a.fattr b.fattr in + let r = a.fname = b.fname && eq_typ_acc a.ftype b.ftype acc rename_mapping && a.fbitfield = b.fbitfield && GobList.equal (eq_attribute rename_mapping) a.fattr b.fattr in if Messages.tracing then Messages.traceu "compareast" "fieldinfo %s vs %s\n" a.fname b.fname; r -and eq_offset (a: offset) (b: offset) (context: context) = match a, b with +and eq_offset (a: offset) (b: offset) (rename_mapping: rename_mapping) = match a, b with NoOffset, NoOffset -> true - | Field (info1, offset1), Field (info2, offset2) -> eq_fieldinfo info1 info2 [] context && eq_offset offset1 offset2 context - | Index (exp1, offset1), Index (exp2, offset2) -> eq_exp exp1 exp2 context && eq_offset offset1 offset2 context + | Field (info1, offset1), Field (info2, offset2) -> eq_fieldinfo info1 info2 [] rename_mapping && eq_offset offset1 offset2 rename_mapping + | Index (exp1, offset1), Index (exp2, offset2) -> eq_exp exp1 exp2 rename_mapping && eq_offset offset1 offset2 rename_mapping | _, _ -> false -and eq_lval (a: lval) (b: lval) (context: context) = match a, b with - (host1, off1), (host2, off2) -> eq_lhost host1 host2 context && eq_offset off1 off2 context +and eq_lval (a: lval) (b: lval) (rename_mapping: rename_mapping) = match a, b with + (host1, off1), (host2, off2) -> eq_lhost host1 host2 rename_mapping && eq_offset off1 off2 rename_mapping -let eq_instr (context: context) (a: instr) (b: instr) = match a, b with - | Set (lv1, exp1, _l1, _el1), Set (lv2, exp2, _l2, _el2) -> eq_lval lv1 lv2 context && eq_exp exp1 exp2 context +let eq_instr (rename_mapping: rename_mapping) (a: instr) (b: instr) = match a, b with + | Set (lv1, exp1, _l1, _el1), Set (lv2, exp2, _l2, _el2) -> eq_lval lv1 lv2 rename_mapping && eq_exp exp1 exp2 rename_mapping | Call (Some lv1, f1, args1, _l1, _el1), Call (Some lv2, f2, args2, _l2, _el2) -> - eq_lval lv1 lv2 context && eq_exp f1 f2 context && GobList.equal (eq_exp2 context) args1 args2 + eq_lval lv1 lv2 rename_mapping && eq_exp f1 f2 rename_mapping && GobList.equal (eq_exp2 rename_mapping) args1 args2 | Call (None, f1, args1, _l1, _el1), Call (None, f2, args2, _l2, _el2) -> - eq_exp f1 f2 context && GobList.equal (eq_exp2 context) args1 args2 - | Asm (attr1, tmp1, ci1, dj1, rk1, l1), Asm (attr2, tmp2, ci2, dj2, rk2, l2) -> GobList.equal String.equal tmp1 tmp2 && GobList.equal(fun (x1,y1,z1) (x2,y2,z2)-> x1 = x2 && y1 = y2 && eq_lval z1 z2 context) ci1 ci2 && GobList.equal(fun (x1,y1,z1) (x2,y2,z2)-> x1 = x2 && y1 = y2 && eq_exp z1 z2 context) dj1 dj2 && GobList.equal String.equal rk1 rk2(* ignore attributes and locations *) - | VarDecl (v1, _l1), VarDecl (v2, _l2) -> eq_varinfo v1 v2 context + eq_exp f1 f2 rename_mapping && GobList.equal (eq_exp2 rename_mapping) args1 args2 + | Asm (attr1, tmp1, ci1, dj1, rk1, l1), Asm (attr2, tmp2, ci2, dj2, rk2, l2) -> GobList.equal String.equal tmp1 tmp2 && GobList.equal(fun (x1,y1,z1) (x2,y2,z2)-> x1 = x2 && y1 = y2 && eq_lval z1 z2 rename_mapping) ci1 ci2 && GobList.equal(fun (x1,y1,z1) (x2,y2,z2)-> x1 = x2 && y1 = y2 && eq_exp z1 z2 rename_mapping) dj1 dj2 && GobList.equal String.equal rk1 rk2(* ignore attributes and locations *) + | VarDecl (v1, _l1), VarDecl (v2, _l2) -> eq_varinfo v1 v2 rename_mapping | _, _ -> false let eq_label (a: label) (b: label) = match a, b with @@ -268,35 +268,35 @@ let eq_stmt_with_location ((a, af): stmt * fundec) ((b, bf): stmt * fundec) = through the cfg and only compares the currently visited node (The cil blocks inside an if statement should not be compared together with its condition to avoid a to early and not precise detection of a changed node inside). Switch, break and continue statements are removed during cfg preparation and therefore need not to be handeled *) -let rec eq_stmtkind ?(cfg_comp = false) ((a, af): stmtkind * fundec) ((b, bf): stmtkind * fundec) (context: context) = - let eq_block' = fun x y -> if cfg_comp then true else eq_block (x, af) (y, bf) context in +let rec eq_stmtkind ?(cfg_comp = false) ((a, af): stmtkind * fundec) ((b, bf): stmtkind * fundec) (rename_mapping: rename_mapping) = + let eq_block' = fun x y -> if cfg_comp then true else eq_block (x, af) (y, bf) rename_mapping in match a, b with - | Instr is1, Instr is2 -> GobList.equal (eq_instr context) is1 is2 - | Return (Some exp1, _l1), Return (Some exp2, _l2) -> eq_exp exp1 exp2 context + | Instr is1, Instr is2 -> GobList.equal (eq_instr rename_mapping) is1 is2 + | Return (Some exp1, _l1), Return (Some exp2, _l2) -> eq_exp exp1 exp2 rename_mapping | Return (None, _l1), Return (None, _l2) -> true | Return _, Return _ -> false | Goto (st1, _l1), Goto (st2, _l2) -> eq_stmt_with_location (!st1, af) (!st2, bf) | Break _, Break _ -> if cfg_comp then failwith "CompareCFG: Invalid stmtkind in CFG" else true | Continue _, Continue _ -> if cfg_comp then failwith "CompareCFG: Invalid stmtkind in CFG" else true - | If (exp1, then1, else1, _l1, _el1), If (exp2, then2, else2, _l2, _el2) -> eq_exp exp1 exp2 context && eq_block' then1 then2 && eq_block' else1 else2 - | Switch (exp1, block1, stmts1, _l1, _el1), Switch (exp2, block2, stmts2, _l2, _el2) -> if cfg_comp then failwith "CompareCFG: Invalid stmtkind in CFG" else eq_exp exp1 exp2 context && eq_block' block1 block2 && GobList.equal (fun a b -> eq_stmt (a,af) (b,bf) context) stmts1 stmts2 + | If (exp1, then1, else1, _l1, _el1), If (exp2, then2, else2, _l2, _el2) -> eq_exp exp1 exp2 rename_mapping && eq_block' then1 then2 && eq_block' else1 else2 + | Switch (exp1, block1, stmts1, _l1, _el1), Switch (exp2, block2, stmts2, _l2, _el2) -> if cfg_comp then failwith "CompareCFG: Invalid stmtkind in CFG" else eq_exp exp1 exp2 rename_mapping && eq_block' block1 block2 && GobList.equal (fun a b -> eq_stmt (a,af) (b,bf) rename_mapping) stmts1 stmts2 | Loop (block1, _l1, _el1, _con1, _br1), Loop (block2, _l2, _el2, _con2, _br2) -> eq_block' block1 block2 | Block block1, Block block2 -> eq_block' block1 block2 | _, _ -> false -and eq_stmt ?(cfg_comp = false) ((a, af): stmt * fundec) ((b, bf): stmt * fundec) (context: context) = +and eq_stmt ?(cfg_comp = false) ((a, af): stmt * fundec) ((b, bf): stmt * fundec) (rename_mapping: rename_mapping) = GobList.equal eq_label a.labels b.labels && - eq_stmtkind ~cfg_comp (a.skind, af) (b.skind, bf) context + eq_stmtkind ~cfg_comp (a.skind, af) (b.skind, bf) rename_mapping -and eq_block ((a, af): Cil.block * fundec) ((b, bf): Cil.block * fundec) (context: context) = - a.battrs = b.battrs && GobList.equal (fun x y -> eq_stmt (x, af) (y, bf) context) a.bstmts b.bstmts +and eq_block ((a, af): Cil.block * fundec) ((b, bf): Cil.block * fundec) (rename_mapping: rename_mapping) = + a.battrs = b.battrs && GobList.equal (fun x y -> eq_stmt (x, af) (y, bf) rename_mapping) a.bstmts b.bstmts -let rec eq_init (a: init) (b: init) (context: context) = match a, b with - | SingleInit e1, SingleInit e2 -> eq_exp e1 e2 context - | CompoundInit (t1, l1), CompoundInit (t2, l2) -> eq_typ t1 t2 context && GobList.equal (fun (o1, i1) (o2, i2) -> eq_offset o1 o2 context && eq_init i1 i2 context) l1 l2 +let rec eq_init (a: init) (b: init) (rename_mapping: rename_mapping) = match a, b with + | SingleInit e1, SingleInit e2 -> eq_exp e1 e2 rename_mapping + | CompoundInit (t1, l1), CompoundInit (t2, l2) -> eq_typ t1 t2 rename_mapping && GobList.equal (fun (o1, i1) (o2, i2) -> eq_offset o1 o2 rename_mapping && eq_init i1 i2 rename_mapping) l1 l2 | _, _ -> false -let eq_initinfo (a: initinfo) (b: initinfo) (context: context) = match a.init, b.init with - | (Some init_a), (Some init_b) -> eq_init init_a init_b context +let eq_initinfo (a: initinfo) (b: initinfo) (rename_mapping: rename_mapping) = match a.init, b.init with + | (Some init_a), (Some init_b) -> eq_init init_a init_b rename_mapping | None, None -> true | _, _ -> false \ No newline at end of file diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index a6fb5230d9..11bb246d01 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -4,28 +4,28 @@ open Cil include CompareAST let eq_node (x, fun1) (y, fun2) = - let empty_context: context = ([], []) in + let empty_rename_mapping: rename_mapping = ([], []) in match x,y with - | Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) empty_context - | Function f1, Function f2 -> eq_varinfo f1.svar f2.svar empty_context - | FunctionEntry f1, FunctionEntry f2 -> eq_varinfo f1.svar f2.svar empty_context + | Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) empty_rename_mapping + | Function f1, Function f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping + | FunctionEntry f1, FunctionEntry f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping | _ -> false (* TODO: compare ASMs properly instead of simply always assuming that they are not the same *) let eq_edge x y = - let empty_context: context = ([], []) in + let empty_rename_mapping: rename_mapping = ([], []) in match x, y with - | Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 empty_context && eq_exp rv1 rv2 empty_context - | Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 empty_context && GobList.equal (eq_exp2 empty_context) ars1 ars2 + | Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 empty_rename_mapping && eq_exp rv1 rv2 empty_rename_mapping + | Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 empty_rename_mapping && GobList.equal (eq_exp2 empty_rename_mapping) ars1 ars2 | Proc (Some r1,f1,ars1), Proc (Some r2,f2,ars2) -> - eq_lval r1 r2 empty_context && eq_exp f1 f2 empty_context && GobList.equal (eq_exp2 empty_context) ars1 ars2 - | Entry f1, Entry f2 -> eq_varinfo f1.svar f2.svar empty_context - | Ret (None,fd1), Ret (None,fd2) -> eq_varinfo fd1.svar fd2.svar empty_context - | Ret (Some r1,fd1), Ret (Some r2,fd2) -> eq_exp r1 r2 empty_context && eq_varinfo fd1.svar fd2.svar empty_context - | Test (p1,b1), Test (p2,b2) -> eq_exp p1 p2 empty_context && b1 = b2 + eq_lval r1 r2 empty_rename_mapping && eq_exp f1 f2 empty_rename_mapping && GobList.equal (eq_exp2 empty_rename_mapping) ars1 ars2 + | Entry f1, Entry f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping + | Ret (None,fd1), Ret (None,fd2) -> eq_varinfo fd1.svar fd2.svar empty_rename_mapping + | Ret (Some r1,fd1), Ret (Some r2,fd2) -> eq_exp r1 r2 empty_rename_mapping && eq_varinfo fd1.svar fd2.svar empty_rename_mapping + | Test (p1,b1), Test (p2,b2) -> eq_exp p1 p2 empty_rename_mapping && b1 = b2 | ASM _, ASM _ -> false | Skip, Skip -> true - | VDecl v1, VDecl v2 -> eq_varinfo v1 v2 empty_context + | VDecl v1, VDecl v2 -> eq_varinfo v1 v2 empty_rename_mapping | SelfLoop, SelfLoop -> true | _ -> false diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index b8a64ae045..40fd0b877a 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -36,7 +36,7 @@ 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) (global_context: method_context list) = +let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (global_rename_mapping: method_rename_assumption list) = 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 @@ -46,34 +46,34 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo (* Compares the two varinfo lists, returning as a first element, if the size of the two lists are equal, - * and as a second a context, holding the rename assumptions *) - let rec context_aware_compare (alocals: varinfo list) (blocals: varinfo list) (context: local_rename list) = match alocals, blocals with - | [], [] -> true, context + * 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: local_rename_assumption list) = match alocals, blocals with + | [], [] -> true, rename_mapping | origLocal :: als, nowLocal :: bls -> - let newContext = if origLocal.vname = nowLocal.vname then context else context @ [(origLocal.vname, nowLocal.vname)] in + let new_rename_mapping = if origLocal.vname = nowLocal.vname then rename_mapping else rename_mapping @ [(origLocal.vname, nowLocal.vname)] in (*TODO: also call eq_varinfo*) - context_aware_compare als bls newContext - | _, _ -> false, context + rename_mapping_aware_compare als bls new_rename_mapping + | _, _ -> false, rename_mapping in - let headerSizeEqual, headerContext = context_aware_compare a.sformals b.sformals [] in - let actHeaderContext = (headerContext, global_context) in + let headerSizeEqual, headerRenameMapping = rename_mapping_aware_compare a.sformals b.sformals [] in + let actHeaderRenameMapping = (headerRenameMapping, global_rename_mapping) in - let unchangedHeader = eq_varinfo a.svar b.svar actHeaderContext && GobList.equal (eq_varinfo2 actHeaderContext) a.sformals b.sformals 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 (* Here the local variables are checked to be equal *) - let sizeEqual, local_rename = context_aware_compare a.slocals b.slocals headerContext in - let context: context = (local_rename, global_context) in + 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) context, 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 @@ -83,9 +83,9 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo in identical, unchangedHeader, diffOpt -let eq_glob (a: global) (b: global) (cfgs : (cfg * (cfg * cfg)) option) (global_context: method_context list) = match a, b with +let eq_glob (a: global) (b: global) (cfgs : (cfg * (cfg * cfg)) option) (global_rename_ammping: method_rename_assumption list) = match a, b with | GFun (f,_), GFun (g,_) -> - let identical, unchangedHeader, diffOpt = eqF f g cfgs global_context in + let identical, unchangedHeader, diffOpt = eqF f g cfgs global_rename_ammping in identical, unchangedHeader, diffOpt | 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 *) @@ -97,7 +97,7 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = then Some (CfgTools.getCFG oldAST |> fst, CfgTools.getCFG newAST) else None in - let generate_global_context map global = + let generate_global_rename_mapping map global = try let ident = identifier_of_global global in let old_global = GlobalMap.find ident map in @@ -131,12 +131,12 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = let changes = empty_change_info () in global_typ_acc := []; - let findChanges map global global_context = + 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 global_context 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 @@ -151,16 +151,16 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = let oldMap = Cil.foldGlobals oldAST addGlobal GlobalMap.empty in let newMap = Cil.foldGlobals newAST addGlobal GlobalMap.empty in - let global_context: method_context list = Cil.foldGlobals newAST (fun (current_global_context: method_context list) global -> - match generate_global_context oldMap global with - | Some context -> current_global_context @ [context] - | None -> current_global_context + let global_rename_mapping: method_rename_assumption list = Cil.foldGlobals newAST (fun (current_global_rename_mapping: method_rename_assumption list) global -> + match generate_global_rename_mapping oldMap global with + | Some rename_mapping -> current_global_rename_mapping @ [rename_mapping] + | None -> current_global_rename_mapping ) [] 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 -> findChanges oldMap glob global_context); + (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)); From abf871b201b310f7901d55fbcc1f2e7beedcdde4 Mon Sep 17 00:00:00 2001 From: Tim ORtel <100865202+TimOrtel@users.noreply.github.com> Date: Mon, 9 May 2022 16:09:32 +0200 Subject: [PATCH 12/90] Replaced rename mapping lists with Hashtbls for increased performance --- src/incremental/compareAST.ml | 27 +++++++++------------ src/incremental/compareCFG.ml | 4 ++-- src/incremental/compareCIL.ml | 45 ++++++++++++++++++++++------------- 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index bc9ac84552..07e5d6ab83 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -5,22 +5,21 @@ type global_type = Fun | Decl | Var and global_identifier = {name: string ; global_t: global_type} [@@deriving ord] -type local_rename_assumption = string * string -(**) -type method_rename_assumption = {original_method_name: string; new_method_name: string; parameter_renames: (string * string) list} +type method_rename_assumption = {original_method_name: string; new_method_name: string; parameter_renames: (string, string) Hashtbl.t} +type method_rename_assumptions = (string, method_rename_assumption) Hashtbl.t (*rename_mapping is carried through the stack when comparing the AST. Holds a list of rename assumptions.*) -type rename_mapping = (local_rename_assumption list) * (method_rename_assumption list) +type rename_mapping = ((string, string) Hashtbl.t) * (method_rename_assumptions) (*Compares two names, being aware of the rename_mapping. Returns true iff: 1. there is a rename for name1 -> name2 = rename(name1) 2. there is no rename for name1 -> name1 = name2*) let rename_mapping_aware_name_comparison (name1: string) (name2: string) (rename_mapping: rename_mapping) = let (local_c, method_c) = rename_mapping in - let existingAssumption: (string*string) option = List.find_opt (fun x -> match x with (original, now) -> original = name1) local_c in + let existingAssumption: string option = Hashtbl.find_opt local_c name1 in match existingAssumption with - | Some (original, now) -> + | Some now -> (*Printf.printf "Assumption is: %s -> %s\n" original now;*) now = name2 | None -> @@ -33,11 +32,11 @@ let string_tuple_to_string (tuple: (string * string) list) = "[" ^ (tuple |> let rename_mapping_to_string (rename_mapping: rename_mapping) = let (local, methods) = rename_mapping in - let local_string = string_tuple_to_string local in - let methods_string: string = methods |> + let local_string = string_tuple_to_string (List.of_seq (Hashtbl.to_seq local)) in + let methods_string: string = List.of_seq (Hashtbl.to_seq_values methods) |> List.map (fun x -> match x with {original_method_name; new_method_name; parameter_renames} -> "(methodName: " ^ original_method_name ^ " -> " ^ new_method_name ^ - "; renamed_params=" ^ string_tuple_to_string parameter_renames ^ ")") |> + "; renamed_params=" ^ string_tuple_to_string (List.of_seq (Hashtbl.to_seq parameter_renames)) ^ ")") |> String.concat ", " in "(local=" ^ local_string ^ "; methods=[" ^ methods_string ^ "])" @@ -180,9 +179,7 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) = (*When we compare function names, we can directly compare the naming from the rename_mapping if it exists.*) let isNamingOk = match b.vtype with | TFun(_, _, _, _) -> ( - let specific_method_rename_mapping = List.find_opt (fun x -> match x with - | {original_method_name; new_method_name; parameter_renames} -> original_method_name = a.vname && new_method_name = b.vname - ) method_rename_mappings in + let specific_method_rename_mapping = Hashtbl.find_opt method_rename_mappings a.vname in match specific_method_rename_mapping with | Some method_rename_mapping -> method_rename_mapping.original_method_name = a.vname && method_rename_mapping.new_method_name = b.vname | None -> a.vname = b.vname @@ -193,15 +190,13 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) = (*If the following is a method call, we need to check if we have a mapping for that method call. *) let typ_rename_mapping = match b.vtype with | TFun(_, _, _, _) -> ( - let new_locals = List.find_opt (fun x -> match x with - | {original_method_name; new_method_name; parameter_renames} -> original_method_name = a.vname && new_method_name = b.vname - ) method_rename_mappings in + let new_locals = Hashtbl.find_opt method_rename_mappings a.vname in match new_locals with | Some locals -> (*Printf.printf "Performing rename_mapping switch. New rename_mapping=%s\n" (rename_mapping_to_string (locals.parameter_renames, method_rename_mappings));*) (locals.parameter_renames, method_rename_mappings) - | None -> ([], method_rename_mappings) + | None -> (Hashtbl.create 0, method_rename_mappings) ) | _ -> rename_mapping in diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index 11bb246d01..f03176122a 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -4,7 +4,7 @@ open Cil include CompareAST let eq_node (x, fun1) (y, fun2) = - let empty_rename_mapping: rename_mapping = ([], []) in + let empty_rename_mapping: rename_mapping = (Hashtbl.create 0, Hashtbl.create 0) in match x,y with | Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) empty_rename_mapping | Function f1, Function f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping @@ -13,7 +13,7 @@ let eq_node (x, fun1) (y, fun2) = (* TODO: compare ASMs properly instead of simply always assuming that they are not the same *) let eq_edge x y = - let empty_rename_mapping: rename_mapping = ([], []) in + let empty_rename_mapping: rename_mapping = (Hashtbl.create 0, Hashtbl.create 0) in match x, y with | Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 empty_rename_mapping && eq_exp rv1 rv2 empty_rename_mapping | Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 empty_rename_mapping && GobList.equal (eq_exp2 empty_rename_mapping) ars1 ars2 diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 40fd0b877a..f5817ae76e 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -36,7 +36,7 @@ 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) (global_rename_mapping: method_rename_assumption list) = +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 @@ -47,16 +47,17 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo (* 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: local_rename_assumption list) = match alocals, blocals with + let rec rename_mapping_aware_compare (alocals: varinfo list) (blocals: varinfo list) (rename_mapping: (string, string) Hashtbl.t) = match alocals, blocals with | [], [] -> true, rename_mapping | origLocal :: als, nowLocal :: bls -> - let new_rename_mapping = if origLocal.vname = nowLocal.vname then rename_mapping else rename_mapping @ [(origLocal.vname, nowLocal.vname)] in - (*TODO: also call eq_varinfo*) - rename_mapping_aware_compare als bls new_rename_mapping + if origLocal.vname <> nowLocal.vname then Hashtbl.add rename_mapping origLocal.vname nowLocal.vname; + + (*TODO: maybe optimize this with eq_varinfo*) + rename_mapping_aware_compare als bls rename_mapping | _, _ -> false, rename_mapping in - let headerSizeEqual, headerRenameMapping = rename_mapping_aware_compare a.sformals b.sformals [] in + let headerSizeEqual, headerRenameMapping = rename_mapping_aware_compare a.sformals b.sformals (Hashtbl.create 0) 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 @@ -83,13 +84,13 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo in identical, unchangedHeader, diffOpt -let eq_glob (a: global) (b: global) (cfgs : (cfg * (cfg * cfg)) option) (global_rename_ammping: method_rename_assumption list) = match a, b with +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_ammping in + let identical, unchangedHeader, diffOpt = eqF f g cfgs global_rename_mapping in identical, unchangedHeader, diffOpt - | 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 + | GVar (x, init_x, _), GVar (y, init_y, _) -> eq_varinfo x y (Hashtbl.create 0, Hashtbl.create 0), 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 (Hashtbl.create 0, Hashtbl.create 0), 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) = @@ -104,13 +105,18 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = match old_global, global with | GFun(f, _), GFun (g, _) -> - let renamed_params: (string * string) list = if (List.length f.sformals) = (List.length g.sformals) then + let renamed_params: (string, string) Hashtbl.t = if (List.length f.sformals) = (List.length g.sformals) then 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)) - else [] in - - if not (f.svar.vname = g.svar.vname) || (List.length renamed_params) > 0 then + List.map (fun (original, now) -> (original.vname, now.vname)) |> + (fun list -> + let table: (string, string) Hashtbl.t = Hashtbl.create (List.length list) in + List.iter (fun mapping -> Hashtbl.add table (fst mapping) (snd mapping)) list; + table + ) + else Hashtbl.create 0 in + + if not (f.svar.vname = g.svar.vname) || (Hashtbl.length renamed_params) > 0 then Some {original_method_name=f.svar.vname; new_method_name=g.svar.vname; parameter_renames=renamed_params} else None | _, _ -> None @@ -151,11 +157,16 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = let oldMap = Cil.foldGlobals oldAST addGlobal GlobalMap.empty in let newMap = Cil.foldGlobals newAST addGlobal GlobalMap.empty in - let global_rename_mapping: method_rename_assumption list = Cil.foldGlobals newAST (fun (current_global_rename_mapping: method_rename_assumption list) global -> + let global_rename_mapping: method_rename_assumptions = Cil.foldGlobals newAST (fun (current_global_rename_mapping: method_rename_assumption list) global -> match generate_global_rename_mapping oldMap global with | Some rename_mapping -> current_global_rename_mapping @ [rename_mapping] | None -> current_global_rename_mapping - ) [] in + ) [] |> + (fun mappings -> + let table = Hashtbl.create (List.length mappings) in + List.iter (fun mapping -> Hashtbl.add table mapping.original_method_name mapping) mappings; + table + ) 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. *) From 4745a3fc8292ac8fe9091b9a7f793b9fbe991652 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Sat, 14 May 2022 14:14:30 +0200 Subject: [PATCH 13/90] cleanup print statements and code. --- src/analyses/base.ml | 278 +++++++++++++++------------------- src/cdomains/baseDomain.ml | 6 +- src/framework/analyses.ml | 2 - src/incremental/compareAST.ml | 9 +- src/incremental/compareCFG.ml | 3 - src/incremental/compareCIL.ml | 5 +- 6 files changed, 125 insertions(+), 178 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 6e218bbfa0..8a15ec008c 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -650,9 +650,9 @@ struct | _ -> eval_next () in let r = - match exp with - | BinOp (op,arg1,arg2,_) -> binop op arg1 arg2 - | _ -> eval_next () + match exp with + | BinOp (op,arg1,arg2,_) -> binop op arg1 arg2 + | _ -> eval_next () in if M.tracing then M.traceu "evalint" "base eval_rv_ask_mustbeequal %a -> %a\n" d_exp exp VD.pretty r; r @@ -860,7 +860,7 @@ struct if M.tracing then M.tracel "eval" "eval_rv %a = %a\n" 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) + ValueDomain.Compound.top_value (Cilfacade.typeOf exp) let query_evalint ask gs st e = if M.tracing then M.traceli "evalint" "base query_evalint %a\n" d_exp e; @@ -961,9 +961,9 @@ struct | _ -> Queries.Result.top q end | Q.EvalThread e -> begin - let v = eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local e in - (* ignore (Pretty.eprintf "evalthread %a (%a): %a" d_exp e d_plainexp e VD.pretty v); *) - match v with + let v = eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local e in + (* ignore (Pretty.eprintf "evalthread %a (%a): %a" d_exp e d_plainexp e VD.pretty v); *) + match v with | `Thread a -> a | `Bot -> Queries.Result.bot q (* TODO: remove *) | _ -> Queries.Result.top q @@ -1028,7 +1028,7 @@ struct match ID.to_int i1, ID.to_int i2 with | Some i1', Some i2' when Z.equal i1' i2' -> true | _ -> false - end + end | _ -> false end | Q.MayBeEqual (e1, e2) -> begin @@ -1097,8 +1097,8 @@ struct | _ -> st (** [set st addr val] returns a state where [addr] is set to [val] - * it is always ok to put None for lval_raw and rval_raw, this amounts to not using/maintaining - * precise information about arrays. *) + * it is always ok to put None for lval_raw and rval_raw, this amounts to not using/maintaining + * precise information about arrays. *) let set (a: Q.ask) ?(ctx=None) ?(invariant=false) ?lval_raw ?rval_raw ?t_override (gs:glob_fun) (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 "setosek" ~var:x.vname "update_variable: start '%s' '%a'\nto\n%a\n\n" x.vname VD.pretty y CPA.pretty z; @@ -1208,20 +1208,20 @@ struct VD.affect_move a v x (fun x -> None) else let patched_ask = - match ctx with - | Some ctx -> - (* The usual recursion trick for ctx. *) - (* Must change ctx used by ask to also use new st (not ctx.local), otherwise recursive EvalInt queries use outdated state. *) - (* Note: query is just called on base, but not any other analyses. Potentially imprecise, but seems to be sufficient for now. *) - let rec ctx' = - { ctx with - ask = (fun (type a) (q: a Queries.t) -> query ctx' q) - ; local = st - } - in - Analyses.ask_of_ctx ctx' - | _ -> - a + match ctx with + | Some ctx -> + (* The usual recursion trick for ctx. *) + (* Must change ctx used by ask to also use new st (not ctx.local), otherwise recursive EvalInt queries use outdated state. *) + (* Note: query is just called on base, but not any other analyses. Potentially imprecise, but seems to be sufficient for now. *) + let rec ctx' = + { ctx with + ask = (fun (type a) (q: a Queries.t) -> query ctx' q) + ; local = st + } + in + Analyses.ask_of_ctx ctx' + | _ -> + a in let moved_by = fun x -> Some 0 in (* this is ok, the information is not provided if it *) VD.affect_move patched_ask v x moved_by (* was a set call caused e.g. by a guard *) @@ -1287,9 +1287,9 @@ struct let f s v = rem_partitioning a s v in List.fold_left f st v_list - (************************************************************************** - * Auxillary functions - **************************************************************************) + (************************************************************************** + * Auxillary functions + **************************************************************************) let is_some_bot x = match x with @@ -1305,10 +1305,10 @@ struct | Eq, x, value, true -> if M.tracing then M.tracec "invariant" "Yes, %a equals %a\n" d_lval x VD.pretty value; (match value with - | `Int n -> - let ikind = Cilfacade.get_ikind_exp (Lval lval) in - Some (x, `Int (ID.cast_to ikind n)) - | _ -> Some(x, value)) + | `Int n -> + let ikind = Cilfacade.get_ikind_exp (Lval lval) in + Some (x, `Int (ID.cast_to ikind n)) + | _ -> Some(x, value)) (* The false-branch for x == value: *) | Eq, x, value, false -> begin match value with @@ -1343,25 +1343,25 @@ struct | Lt, x, value, _ -> begin match value with | `Int n -> begin - let ikind = Cilfacade.get_ikind_exp (Lval lval) in - let n = ID.cast_to ikind n in - let range_from x = if tv then ID.ending ikind (BI.sub x BI.one) else ID.starting ikind x in - 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 %s\n\n" d_lval x (BI.to_string n); - Some (x, `Int (range_from n)) - | None -> None + let ikind = Cilfacade.get_ikind_exp (Lval lval) in + let n = ID.cast_to ikind n in + let range_from x = if tv then ID.ending ikind (BI.sub x BI.one) else ID.starting ikind x in + 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 %s\n\n" d_lval x (BI.to_string n); + Some (x, `Int (range_from n)) + | None -> None end | _ -> None end | Le, x, value, _ -> begin match value with | `Int n -> begin - let ikind = Cilfacade.get_ikind_exp (Lval lval) in - let n = ID.cast_to ikind n in - let range_from x = if tv then ID.ending ikind x else ID.starting ikind (BI.add x BI.one) in - let limit_from = if tv then ID.maximal else ID.minimal in + let ikind = Cilfacade.get_ikind_exp (Lval lval) in + let n = ID.cast_to ikind n in + let range_from x = if tv then ID.ending ikind x else ID.starting ikind (BI.add x BI.one) in + 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 %s\n\n" d_lval x (BI.to_string n); @@ -1393,15 +1393,15 @@ struct -> derived_invariant (BinOp (op, c1, c2, t)) tv | BinOp(op, CastE (TInt (ik, _) as t1, Lval x), rval, typ) -> (match eval_rv a gs st (Lval x) with - | `Int v -> - (* This is tricky: It it is not sufficient to check that ID.cast_to_ik v = v - * If there is one domain that knows this to be true and the other does not, we - * should still impose the invariant. E.g. i -> ([1,5]; Not {0}[byte]) *) - if VD.is_safe_cast t1 (Cilfacade.typeOfLval x) then - derived_invariant (BinOp (op, Lval x, rval, typ)) tv - else - None - | _ -> None) + | `Int v -> + (* This is tricky: It it is not sufficient to check that ID.cast_to_ik v = v + * If there is one domain that knows this to be true and the other does not, we + * should still impose the invariant. E.g. i -> ([1,5]; Not {0}[byte]) *) + if VD.is_safe_cast t1 (Cilfacade.typeOfLval x) then + derived_invariant (BinOp (op, Lval x, rval, typ)) tv + else + None + | _ -> None) | BinOp(op, rval, CastE (TInt (_, _) as ti, Lval x), typ) -> derived_invariant (BinOp (switchedOp op, CastE(ti, Lval x), rval, typ)) tv (* Cases like if (x) are treated like if (x != 0) *) @@ -1459,7 +1459,7 @@ struct let warn_and_top_on_zero x = if GobOption.exists (BI.equal BI.zero) (ID.to_int x) then (M.warn "Must Undefined Behavior: Second argument of div or mod is 0, continuing with top"; - ID.top_of ikind) + ID.top_of ikind) else x in @@ -1479,9 +1479,9 @@ struct (* Only multiplication with odd numbers is an invertible operation in (mod 2^n) *) (* refine x by information about y, using x * y == c *) let refine_by x y = (match ID.to_int y with - | None -> x - | Some v when BI.equal (BI.rem v (BI.of_int 2)) BI.zero (* v % 2 = 0 *) -> x (* A refinement would still be possible here, but has to take non-injectivity into account. *) - | Some v (* when Int64.rem v 2L = 1L *) -> ID.meet x (ID.div c y)) (* Div is ok here, c must be divisible by a and b *) + | None -> x + | Some v when BI.equal (BI.rem v (BI.of_int 2)) BI.zero (* v % 2 = 0 *) -> x (* A refinement would still be possible here, but has to take non-injectivity into account. *) + | Some v (* when Int64.rem v 2L = 1L *) -> ID.meet x (ID.div c y)) (* Div is ok here, c must be divisible by a and b *) in (refine_by a b, refine_by b a) | MinusA -> meet_non ID.add ID.sub @@ -1536,37 +1536,37 @@ struct let both x = x, x in let m = ID.meet a b in (match op, ID.to_bool c with - | Eq, Some true - | Ne, Some false -> both m (* def. equal: if they compare equal, both values must be from the meet *) - | Eq, Some false - | Ne, Some true -> (* def. unequal *) - (* Both values can not be in the meet together, but it's not sound to exclude the meet from both. - * e.g. a=[0,1], b=[1,2], meet a b = [1,1], but (a != b) does not imply a=[0,0], b=[2,2] since others are possible: a=[1,1], b=[2,2] - * Only if a is a definite value, we can exclude it from b: *) - let excl a b = match ID.to_int a with Some x -> ID.of_excl_list ikind [x] | None -> b 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'; - meet_bin a' b' - | _, _ -> a, b + | Eq, Some true + | Ne, Some false -> both m (* def. equal: if they compare equal, both values must be from the meet *) + | Eq, Some false + | Ne, Some true -> (* def. unequal *) + (* Both values can not be in the meet together, but it's not sound to exclude the meet from both. + * e.g. a=[0,1], b=[1,2], meet a b = [1,1], but (a != b) does not imply a=[0,0], b=[2,2] since others are possible: a=[1,1], b=[2,2] + * Only if a is a definite value, we can exclude it from b: *) + let excl a b = match ID.to_int a with Some x -> ID.of_excl_list ikind [x] | None -> b 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'; + meet_bin a' b' + | _, _ -> a, b ) | Lt | Le | Ge | Gt as op -> let pred x = BI.sub x BI.one in let succ x = BI.add x BI.one in (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; *) - (match op, ID.to_bool c with - | Le, Some true - | Gt, Some false -> meet_bin (ID.ending ikind u2) (ID.starting ikind l1) - | Ge, Some true - | Lt, Some false -> meet_bin (ID.starting ikind l2) (ID.ending ikind u1) - | Lt, Some true - | Ge, Some false -> meet_bin (ID.ending ikind (pred u2)) (ID.starting ikind (succ l1)) - | Gt, Some true - | Le, Some false -> meet_bin (ID.starting ikind (succ l2)) (ID.ending ikind (pred u1)) - | _, _ -> a, b) - | _ -> a, b) + | 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; *) + (match op, ID.to_bool c with + | Le, Some true + | Gt, Some false -> meet_bin (ID.ending ikind u2) (ID.starting ikind l1) + | Ge, Some true + | Lt, Some false -> meet_bin (ID.starting ikind l2) (ID.ending ikind u1) + | Lt, Some true + | Ge, Some false -> meet_bin (ID.ending ikind (pred u2)) (ID.starting ikind (succ l1)) + | Gt, Some true + | Le, Some false -> meet_bin (ID.starting ikind (succ l2)) (ID.ending ikind (pred u1)) + | _, _ -> a, b) + | _ -> a, b) | BOr | BXor as op-> if M.tracing then M.tracel "inv" "Unhandled operator %a\n" d_binop op; (* Be careful: inv_exp performs a meet on both arguments of the BOr / BXor. *) @@ -1600,15 +1600,15 @@ struct | BinOp (op, e1, e2, _) as e -> 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) ID.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 a', b' = inv_bin_int (a, b) ikind c op in - if M.tracing then M.tracel "inv" "binop: %a, a': %a, b': %a\n" d_exp e ID.pretty a' ID.pretty b'; - let st' = inv_exp a' e1 st in - let st'' = inv_exp b' e2 st' in - st'' - (* | `Address a, `Address b -> ... *) - | a1, a2 -> fallback ("binop: got abstract values that are not `Int: " ^ sprint VD.pretty a1 ^ " and " ^ sprint VD.pretty a2) st) + | `Int a, `Int b -> + let ikind = Cilfacade.get_ikind_exp e1 in (* both operands have the same type (except for Shiftlt, Shiftrt)! *) + let a', b' = inv_bin_int (a, b) ikind c op in + if M.tracing then M.tracel "inv" "binop: %a, a': %a, b': %a\n" d_exp e ID.pretty a' ID.pretty b'; + let st' = inv_exp a' e1 st in + let st'' = inv_exp b' e2 st' in + st'' + (* | `Address a, `Address b -> ... *) + | a1, a2 -> fallback ("binop: got abstract values that are not `Int: " ^ sprint VD.pretty a1 ^ " and " ^ sprint VD.pretty a2) st) | Lval x -> (* meet x with c *) let t = Cil.unrollType (Cilfacade.typeOfLval x) in (* unroll type to deal with TNamed *) let c' = match t with @@ -1642,18 +1642,18 @@ struct | CastE ((TInt (ik, _)) as t, e) | CastE ((TEnum ({ekind = ik; _ }, _)) as t, e) -> (* Can only meet the t part of an Lval in e with c (unless we meet with all overflow possibilities)! Since there is no good way to do this, we only continue if e has no values outside of t. *) (match eval e st with - | `Int i -> - if ID.leq i (ID.cast_to ik i) then + | `Int i -> + if ID.leq i (ID.cast_to ik i) then match Cilfacade.typeOf e with - | TInt(ik_e, _) - | TEnum ({ekind = ik_e; _ }, _) -> - let c' = ID.cast_to ik_e c in - 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'; - inv_exp c' e st - | x -> fallback ("CastE: e did evaluate to `Int, but the type did not match" ^ sprint d_type t) st - else - fallback ("CastE: " ^ sprint d_plainexp e ^ " evaluates to " ^ sprint ID.pretty i ^ " which is bigger than the type it is cast to which is " ^ sprint d_type t) st - | v -> fallback ("CastE: e did not evaluate to `Int, but " ^ sprint VD.pretty v) st) + | TInt(ik_e, _) + | TEnum ({ekind = ik_e; _ }, _) -> + let c' = ID.cast_to ik_e c in + 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'; + inv_exp c' e st + | x -> fallback ("CastE: e did evaluate to `Int, but the type did not match" ^ sprint d_type t) st + else + fallback ("CastE: " ^ sprint d_plainexp e ^ " evaluates to " ^ sprint ID.pretty i ^ " which is bigger than the type it is cast to which is " ^ sprint d_type t) st + | v -> fallback ("CastE: e did not evaluate to `Int, but " ^ sprint VD.pretty v) st) | e -> fallback (sprint d_plainexp e ^ " not implemented") st in if eval_bool exp st = Some (not tv) then raise Deadcode (* we already know that the branch is dead *) @@ -1728,7 +1728,7 @@ struct in match is_list_init () with | Some a when (get_bool "exp.list-type") -> - set ~ctx:(Some ctx) (Analyses.ask_of_ctx ctx) ctx.global ctx.local (AD.singleton (Addr.from_var a)) lval_t (`List (ValueDomain.Lists.bot ())) + set ~ctx:(Some ctx) (Analyses.ask_of_ctx ctx) ctx.global ctx.local (AD.singleton (Addr.from_var a)) lval_t (`List (ValueDomain.Lists.bot ())) | _ -> let rval_val = eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local rval in let lval_val = eval_lv (Analyses.ask_of_ctx ctx) ctx.global ctx.local lval in @@ -1743,16 +1743,16 @@ struct AD.is_top xs || AD.exists not_local xs in (match rval_val, lval_val with - | `Address adrs, lval - when (not !GU.global_initialization) && get_bool "kernel" && not_local lval && not (AD.is_top adrs) -> - let find_fps e xs = match Addr.to_var_must e with - | Some x -> x :: xs - | None -> xs - in - let vars = AD.fold find_fps adrs [] in (* filter_map from AD to list *) - let funs = List.filter (fun x -> isFunctionType x.vtype) vars in - List.iter (fun x -> ctx.spawn None x []) funs - | _ -> () + | `Address adrs, lval + when (not !GU.global_initialization) && get_bool "kernel" && not_local lval && not (AD.is_top adrs) -> + let find_fps e xs = match Addr.to_var_must e with + | Some x -> x :: xs + | None -> xs + in + let vars = AD.fold find_fps adrs [] in (* filter_map from AD to list *) + let funs = List.filter (fun x -> isFunctionType x.vtype) vars in + List.iter (fun x -> ctx.spawn None x []) funs + | _ -> () ); match lval with (* this section ensure global variables contain bottom values of the proper type before setting them *) | (Var v, offs) when AD.is_definite lval_val && v.vglob -> @@ -1861,7 +1861,7 @@ struct | _ -> () end; set ~ctx:(Some ctx) ~t_override (Analyses.ask_of_ctx ctx) ctx.global nst (return_var ()) t_override rv - (* lval_raw:None, and rval_raw:None is correct here *) + (* lval_raw:None, and rval_raw:None is correct here *) let vdecl ctx (v:varinfo) = if not (Cil.isArrayType v.vtype) then @@ -2015,45 +2015,6 @@ struct | _ -> [] let assert_fn ctx e should_warn change = - let _ = Hashtbl.iter (fun fun_name map -> - begin - Printf.printf "%s: [" fun_name; - Hashtbl.iter (fun from tox -> Printf.printf "%s -> %s; " from tox) map; - Printf.printf "]\n"; - end - ) !CompareCIL.rename_map in - - let parent_function: fundec = Node.find_fundec ctx.node in - - (*Performs the actual rename on lvals for renamed local variables.*) - let rename_lval lhost offset = - let new_lhost = match lhost with - | Var varinfo -> - varinfo.vname <- CompareCIL.get_local_rename parent_function.svar.vname varinfo.vname; - Var varinfo - | _ -> lhost - in - (new_lhost, offset) - in - - (*Recusivly go through the expression and rename all occurences of local variables. TODO: What happens with global vars*) - let rec rename_exp (exp: exp) = match exp with - | Lval (lhost, offset) -> Lval (rename_lval lhost offset) - | Real e -> Real (rename_exp e) - | Imag e -> Imag (rename_exp e) - | SizeOfE e -> SizeOfE (rename_exp e) - | AlignOfE e -> AlignOfE (rename_exp e) - | UnOp (unop, e, typ) -> UnOp (unop, rename_exp e, typ) - | BinOp (binop, e1, e2, typ) -> BinOp (binop, rename_exp e1, rename_exp e2, typ) - | Question (e1, e2, e3, typ) -> Question (rename_exp e1, rename_exp e2, rename_exp e3, typ) - | CastE (typ, e) -> CastE (typ, rename_exp e) - | AddrOf (lhost, offset) -> AddrOf (rename_lval lhost offset) - | StartOf (lhost, offset) -> StartOf (rename_lval lhost offset) - (*TODO: AddrOfLabel?*) - | _ -> exp - in - - let check_assert e st = match eval_rv (Analyses.ask_of_ctx ctx) ctx.global st e with | `Int v when ID.is_bool v -> @@ -2065,7 +2026,7 @@ struct | `Bot -> `Bot | _ -> `Top in - let expr = sprint d_exp (rename_exp e) in + let expr = sprint d_exp e in let warn warn_fn ?annot msg = if should_warn then if get_bool "dbg.regression" then ( (* This only prints unexpected results (with the difference) as indicated by the comment behind the assert (same as used by the regression test script). *) let loc = !M.current_loc in @@ -2125,9 +2086,6 @@ struct invalidate ~ctx (Analyses.ask_of_ctx ctx) gs st addrs let special ctx (lv:lval option) (f: varinfo) (args: exp list) = - Printf.printf "special: varinfo=%s\n" f.vname; - List.iter (fun x -> ignore @@ Pretty.printf "%a\n" Cil.d_exp x;) args; - 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; @@ -2274,7 +2232,7 @@ struct in (* ignore @@ printf "malloc will allocate %a bytes\n" ID.pretty (eval_int ctx.ask gs st size); *) set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(heap_var, TVoid [], `Blob (VD.bot (), eval_int (Analyses.ask_of_ctx ctx) gs st size, true)); - (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), `Address heap_var)] + (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), `Address heap_var)] | _ -> st end | `Calloc (n, size) -> @@ -2287,7 +2245,7 @@ struct else addr 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 (Analyses.ask_of_ctx ctx) gs st [(add_null (AD.from_var heap_var), TVoid [], `Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.one) (`Blob (VD.bot (), eval_int (Analyses.ask_of_ctx ctx) gs st size, false)))); - (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), `Address (add_null (AD.from_var_offset (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.zero, `NoOffset)))))] + (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), `Address (add_null (AD.from_var_offset (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.zero, `NoOffset)))))] | _ -> st end | `Unknown "__goblint_unknown" -> diff --git a/src/cdomains/baseDomain.ml b/src/cdomains/baseDomain.ml index dc2b63a95f..3533f0e8a2 100644 --- a/src/cdomains/baseDomain.ml +++ b/src/cdomains/baseDomain.ml @@ -108,11 +108,9 @@ struct ++ text ")" let printXml f r = - CPA.iter (fun key value -> key.vname <- (CompareCIL.get_local_rename (!Analyses.currentFunctionName) key.vname)) r.cpa; - let e = XmlUtil.escape in - BatPrintf.fprintf f "\n\n\n%s\n\n%a\n%s\n\n%a\n%s\n\n%a\n\n%s\n\n%a\n\n" - (e @@ (CPA.name () ^ "ASSSSSSS")) CPA.printXml r.cpa + BatPrintf.fprintf f "\n\n\n%s\n\n%a\n%s\n\n%a\n%s\n\n%a\n\n%s\n\n%a\n\n" + (e @@ (CPA.name ())) CPA.printXml r.cpa (e @@ PartDeps.name ()) PartDeps.printXml r.deps (e @@ WeakUpdates.name ()) WeakUpdates.printXml r.weak (e @@ PrivD.name ()) PrivD.printXml r.priv diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 0d00ac672a..7314427034 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -191,8 +191,6 @@ struct match get_string "result" with | "pretty" -> ignore (fprintf out "%a\n" pretty (Lazy.force table)) | "fast_xml" -> - Printf.printf "%s" (Printexc.get_callstack 15 |> Printexc.raw_backtrace_to_string); - let module SH = BatHashtbl.Make (Basetype.RawStrings) in let file2funs = SH.create 100 in let funs2node = SH.create 100 in diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index d9361ec082..e226f92b99 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -191,7 +191,7 @@ and eq_varinfo (a: varinfo) (b: varinfo) (context: context) = in (*If the following is a method call, we need to check if we have a mapping for that method call. *) - let typ_context, did_context_switch = match b.vtype with + let typ_context = match b.vtype with | TFun(_, _, _, _) -> ( let new_locals = List.find_opt (fun x -> match x with | {original_method_name; new_method_name; parameter_renames} -> original_method_name = a.vname && new_method_name = b.vname @@ -200,10 +200,10 @@ and eq_varinfo (a: varinfo) (b: varinfo) (context: context) = match new_locals with | Some locals -> (*Printf.printf "Performing context switch. New context=%s\n" (context_to_string (locals.parameter_renames, method_contexts));*) - (locals.parameter_renames, method_contexts), true - | None -> ([], method_contexts), false + (locals.parameter_renames, method_contexts) + | None -> ([], method_contexts) ) - | _ -> context, false + | _ -> context in let typeCheck = eq_typ a.vtype b.vtype typ_context in @@ -215,7 +215,6 @@ and eq_varinfo (a: varinfo) (b: varinfo) (context: context) = (*a.vname = b.vname*) let result = isNamingOk && typeCheck && attrCheck && a.vstorage = b.vstorage && a.vglob = b.vglob && a.vaddrof = b.vaddrof in - if did_context_switch then Printf.printf "Undo context switch \n"; result (* Ignore the location, vid, vreferenced, vdescr, vdescrpure, vinline *) diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index 25b5f64ccf..e87df4f832 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -47,8 +47,6 @@ module NTH = Hashtbl.Make( * process on their successors. If a node from the old CFG can not be matched, it is added to diff and no further * comparison is done for its successors. The two function entry nodes make up the tuple to start the comparison from. *) let compareCfgs (module CfgOld : CfgForward) (module CfgNew : CfgForward) fun1 fun2 = - let _ = Printf.printf "ComparingCfgs" in - let diff = NH.create 113 in let same = NTH.create 113 in let waitingList : (node * node) t = Queue.create () in @@ -132,7 +130,6 @@ let reexamine f1 f2 (same : unit NTH.t) (diffNodes1 : unit NH.t) (module CfgOld (NTH.to_seq_keys same, NH.to_seq_keys diffNodes1, NH.to_seq_keys diffNodes2) let compareFun (module CfgOld : CfgForward) (module CfgNew : CfgForward) fun1 fun2 = - let _ = Printf.printf "Comparing funs" in let same, diff = compareCfgs (module CfgOld) (module CfgNew) fun1 fun2 in let unchanged, diffNodes1, diffNodes2 = reexamine fun1 fun2 same diff (module CfgOld) (module CfgNew) in List.of_seq unchanged, List.of_seq diffNodes1, List.of_seq diffNodes2 diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 762d6fbac5..0b1ea5329b 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -102,7 +102,7 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * cfg) option) (global_cont let sizeEqual, local_rename = context_aware_compare a.slocals b.slocals headerContext in let context: context = (local_rename, global_context) in - let _ = Printf.printf "Context=%s\n" (CompareAST.context_to_string context) in + (*let _ = Printf.printf "Context=%s\n" (CompareAST.context_to_string context) in*) let sameDef = unchangedHeader && sizeEqual in if not sameDef then @@ -112,7 +112,6 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * cfg) option) (global_cont | None -> eq_block (a.sbody, a) (b.sbody, b) context, None | Some (cfgOld, cfgNew) -> - Printf.printf "compareCIL.eqF: Compaing 2 cfgs now\n"; let module CfgOld : MyCFG.CfgForward = struct let next = cfgOld end in let module CfgNew : MyCFG.CfgForward = struct let next = cfgNew end in let matches, diffNodes1, diffNodes2 = compareFun (module CfgOld) (module CfgNew) a b in @@ -134,8 +133,6 @@ let eq_glob (a: global) (b: global) (cfgs : (cfg * cfg) option) (global_context: | _ -> 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 _ = Printf.printf "Comparing Cil files\n" in - let cfgs = if GobConfig.get_string "incremental.compare" = "cfg" then Some (CfgTools.getCFG oldAST |> fst, CfgTools.getCFG newAST |> fst) else None in From c645c682184e5bcc4df762a565fa040da0d592b9 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Sat, 14 May 2022 15:01:28 +0200 Subject: [PATCH 14/90] cherry picked context -> rename mapping --- src/incremental/compareAST.ml | 230 +++++++++++++++++----------------- src/incremental/compareCFG.ml | 28 ++--- src/incremental/compareCIL.ml | 47 +++---- 3 files changed, 147 insertions(+), 158 deletions(-) diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index e226f92b99..36662c8c81 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -5,18 +5,18 @@ type global_type = Fun | Decl | Var and global_identifier = {name: string ; global_t: global_type} [@@deriving ord] -type local_rename = string * string +type local_rename_assumption = string * string (**) -type method_context = {original_method_name: string; new_method_name: string; parameter_renames: (string * string) list} +type method_rename_assumption = {original_method_name: string; new_method_name: string; parameter_renames: (string * string) list} -(*context is carried through the stack when comparing the AST. Holds a list of rename assumptions.*) -type context = (local_rename list) * (method_context list) +(*rename_mapping is carried through the stack when comparing the AST. Holds a list of rename assumptions.*) +type rename_mapping = (local_rename_assumption list) * (method_rename_assumption list) -(*Compares two names, being aware of the context. Returns true iff: +(*Compares two names, being aware of the rename_mapping. Returns true iff: 1. there is a rename for name1 -> name2 = rename(name1) 2. there is no rename for name1 -> name1 = name2*) -let context_aware_name_comparison (name1: string) (name2: string) (context: context) = - let (local_c, method_c) = context in +let rename_mapping_aware_name_comparison (name1: string) (name2: string) (rename_mapping: rename_mapping) = + let (local_c, method_c) = rename_mapping in let existingAssumption: (string*string) option = List.find_opt (fun x -> match x with (original, now) -> original = name1) local_c in match existingAssumption with @@ -31,8 +31,8 @@ let string_tuple_to_string (tuple: (string * string) list) = "[" ^ (tuple |> List.map (fun x -> match x with (first, second) -> "(" ^ first ^ " -> " ^ second ^ ")") |> String.concat ", ") ^ "]" -let context_to_string (context: context) = - let (local, methods) = context in +let rename_mapping_to_string (rename_mapping: rename_mapping) = + let (local, methods) = rename_mapping in let local_string = string_tuple_to_string local in let methods_string: string = methods |> List.map (fun x -> match x with {original_method_name; new_method_name; parameter_renames} -> @@ -59,33 +59,33 @@ let compare_name (a: string) (b: string) = let anon_union = "__anonunion_" in if a = b then true else BatString.(starts_with a anon_struct && starts_with b anon_struct || starts_with a anon_union && starts_with b anon_union) -let rec eq_constant (context: context) (a: constant) (b: constant) = +let rec eq_constant (rename_mapping: rename_mapping) (a: constant) (b: constant) = match a, b with | CInt (val1, kind1, str1), CInt (val2, kind2, str2) -> Cilint.compare_cilint val1 val2 = 0 && kind1 = kind2 (* Ignore string representation, i.e. 0x2 == 2 *) - | CEnum (exp1, str1, enuminfo1), CEnum (exp2, str2, enuminfo2) -> eq_exp exp1 exp2 context (* Ignore name and enuminfo *) + | CEnum (exp1, str1, enuminfo1), CEnum (exp2, str2, enuminfo2) -> eq_exp exp1 exp2 rename_mapping (* Ignore name and enuminfo *) | a, b -> a = b -and eq_exp2 (context: context) (a: exp) (b: exp) = eq_exp a b context +and eq_exp2 (rename_mapping: rename_mapping) (a: exp) (b: exp) = eq_exp a b rename_mapping -and eq_exp (a: exp) (b: exp) (context: context) = +and eq_exp (a: exp) (b: exp) (rename_mapping: rename_mapping) = match a, b with - | Const c1, Const c2 -> eq_constant context c1 c2 - | Lval lv1, Lval lv2 -> eq_lval lv1 lv2 context - | SizeOf typ1, SizeOf typ2 -> eq_typ typ1 typ2 context - | SizeOfE exp1, SizeOfE exp2 -> eq_exp exp1 exp2 context + | Const c1, Const c2 -> eq_constant rename_mapping c1 c2 + | Lval lv1, Lval lv2 -> eq_lval lv1 lv2 rename_mapping + | SizeOf typ1, SizeOf typ2 -> eq_typ typ1 typ2 rename_mapping + | SizeOfE exp1, SizeOfE exp2 -> eq_exp exp1 exp2 rename_mapping | SizeOfStr str1, SizeOfStr str2 -> str1 = str2 (* possibly, having the same length would suffice *) - | AlignOf typ1, AlignOf typ2 -> eq_typ typ1 typ2 context - | AlignOfE exp1, AlignOfE exp2 -> eq_exp exp1 exp2 context - | UnOp (op1, exp1, typ1), UnOp (op2, exp2, typ2) -> op1 == op2 && eq_exp exp1 exp2 context && eq_typ typ1 typ2 context - | BinOp (op1, left1, right1, typ1), BinOp (op2, left2, right2, typ2) -> op1 = op2 && eq_exp left1 left2 context && eq_exp right1 right2 context && eq_typ typ1 typ2 context - | CastE (typ1, exp1), CastE (typ2, exp2) -> eq_typ typ1 typ2 context && eq_exp exp1 exp2 context - | AddrOf lv1, AddrOf lv2 -> eq_lval lv1 lv2 context - | StartOf lv1, StartOf lv2 -> eq_lval lv1 lv2 context + | AlignOf typ1, AlignOf typ2 -> eq_typ typ1 typ2 rename_mapping + | AlignOfE exp1, AlignOfE exp2 -> eq_exp exp1 exp2 rename_mapping + | UnOp (op1, exp1, typ1), UnOp (op2, exp2, typ2) -> op1 == op2 && eq_exp exp1 exp2 rename_mapping && eq_typ typ1 typ2 rename_mapping + | BinOp (op1, left1, right1, typ1), BinOp (op2, left2, right2, typ2) -> op1 = op2 && eq_exp left1 left2 rename_mapping && eq_exp right1 right2 rename_mapping && eq_typ typ1 typ2 rename_mapping + | CastE (typ1, exp1), CastE (typ2, exp2) -> eq_typ typ1 typ2 rename_mapping && eq_exp exp1 exp2 rename_mapping + | AddrOf lv1, AddrOf lv2 -> eq_lval lv1 lv2 rename_mapping + | StartOf lv1, StartOf lv2 -> eq_lval lv1 lv2 rename_mapping | _, _ -> false -and eq_lhost (a: lhost) (b: lhost) (context: context) = match a, b with - Var v1, Var v2 -> eq_varinfo v1 v2 context - | Mem exp1, Mem exp2 -> eq_exp exp1 exp2 context +and eq_lhost (a: lhost) (b: lhost) (rename_mapping: rename_mapping) = match a, b with + Var v1, Var v2 -> eq_varinfo v1 v2 rename_mapping + | Mem exp1, Mem exp2 -> eq_exp exp1 exp2 rename_mapping | _, _ -> false and global_typ_acc: (typ * typ) list ref = ref [] (* TODO: optimize with physical Hashtbl? *) @@ -94,21 +94,21 @@ and mem_typ_acc (a: typ) (b: typ) acc = List.exists (fun p -> match p with (x, y and pretty_length () l = Pretty.num (List.length l) -and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (context: context) = +and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (rename_mapping: rename_mapping) = 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 *) let r = match a, b with - | TPtr (typ1, attr1), TPtr (typ2, attr2) -> eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_attribute context) attr1 attr2 - | TArray (typ1, (Some lenExp1), attr1), TArray (typ2, (Some lenExp2), attr2) -> eq_typ_acc typ1 typ2 acc context && eq_exp lenExp1 lenExp2 context && GobList.equal (eq_attribute context) attr1 attr2 - | TArray (typ1, None, attr1), TArray (typ2, None, attr2) -> eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_attribute context) attr1 attr2 + | TPtr (typ1, attr1), TPtr (typ2, attr2) -> eq_typ_acc typ1 typ2 acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 + | TArray (typ1, (Some lenExp1), attr1), TArray (typ2, (Some lenExp2), attr2) -> eq_typ_acc typ1 typ2 acc rename_mapping && eq_exp lenExp1 lenExp2 rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 + | TArray (typ1, None, attr1), TArray (typ2, None, attr2) -> eq_typ_acc typ1 typ2 acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 | TFun (typ1, (Some list1), varArg1, attr1), TFun (typ2, (Some list2), varArg2, attr2) - -> eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_args context acc) list1 list2 && varArg1 = varArg2 && - GobList.equal (eq_attribute context) attr1 attr2 + -> eq_typ_acc typ1 typ2 acc rename_mapping && GobList.equal (eq_args rename_mapping acc) list1 list2 && varArg1 = varArg2 && + GobList.equal (eq_attribute rename_mapping) attr1 attr2 | TFun (typ1, None, varArg1, attr1), TFun (typ2, None, varArg2, attr2) - -> eq_typ_acc typ1 typ2 acc context && varArg1 = varArg2 && - GobList.equal (eq_attribute context) attr1 attr2 - | TNamed (typinfo1, attr1), TNamed (typeinfo2, attr2) -> eq_typ_acc typinfo1.ttype typeinfo2.ttype acc context && GobList.equal (eq_attribute context) attr1 attr2 (* Ignore tname, treferenced *) - | TNamed (tinf, attr), b -> eq_typ_acc tinf.ttype b acc context (* Ignore tname, treferenced. TODO: dismiss attributes, or not? *) - | a, TNamed (tinf, attr) -> eq_typ_acc a tinf.ttype acc context (* Ignore tname, treferenced . TODO: dismiss attributes, or not? *) + -> eq_typ_acc typ1 typ2 acc rename_mapping && varArg1 = varArg2 && + GobList.equal (eq_attribute rename_mapping) attr1 attr2 + | TNamed (typinfo1, attr1), TNamed (typeinfo2, attr2) -> eq_typ_acc typinfo1.ttype typeinfo2.ttype acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 (* Ignore tname, treferenced *) + | TNamed (tinf, attr), b -> eq_typ_acc tinf.ttype b acc rename_mapping (* Ignore tname, treferenced. TODO: dismiss attributes, or not? *) + | a, TNamed (tinf, attr) -> eq_typ_acc a tinf.ttype acc rename_mapping (* Ignore tname, treferenced . TODO: dismiss attributes, or not? *) (* 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 ( @@ -117,97 +117,97 @@ and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (context: context) = ) else ( let acc = (a, b) :: acc in - let res = eq_compinfo compinfo1 compinfo2 acc context && GobList.equal (eq_attribute context) attr1 attr2 in + let res = eq_compinfo compinfo1 compinfo2 acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 in if res && compinfo1.cname <> compinfo2.cname then compinfo2.cname <- compinfo1.cname; if res then global_typ_acc := (a, b) :: !global_typ_acc; res ) - | TEnum (enuminfo1, attr1), TEnum (enuminfo2, attr2) -> let res = eq_enuminfo enuminfo1 enuminfo2 context && GobList.equal (eq_attribute context) attr1 attr2 in (if res && enuminfo1.ename <> enuminfo2.ename then enuminfo2.ename <- enuminfo1.ename); res - | TBuiltin_va_list attr1, TBuiltin_va_list attr2 -> GobList.equal (eq_attribute context) attr1 attr2 - | TVoid attr1, TVoid attr2 -> GobList.equal (eq_attribute context) attr1 attr2 - | TInt (ik1, attr1), TInt (ik2, attr2) -> ik1 = ik2 && GobList.equal (eq_attribute context) attr1 attr2 - | TFloat (fk1, attr1), TFloat (fk2, attr2) -> fk1 = fk2 && GobList.equal (eq_attribute context) attr1 attr2 + | TEnum (enuminfo1, attr1), TEnum (enuminfo2, attr2) -> let res = eq_enuminfo enuminfo1 enuminfo2 rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 in (if res && enuminfo1.ename <> enuminfo2.ename then enuminfo2.ename <- enuminfo1.ename); res + | TBuiltin_va_list attr1, TBuiltin_va_list attr2 -> GobList.equal (eq_attribute rename_mapping) attr1 attr2 + | TVoid attr1, TVoid attr2 -> GobList.equal (eq_attribute rename_mapping) attr1 attr2 + | TInt (ik1, attr1), TInt (ik2, attr2) -> ik1 = ik2 && GobList.equal (eq_attribute rename_mapping) attr1 attr2 + | TFloat (fk1, attr1), TFloat (fk2, attr2) -> fk1 = fk2 && GobList.equal (eq_attribute rename_mapping) attr1 attr2 | _, _ -> false in if Messages.tracing then Messages.traceu "compareast" "eq_typ_acc %a vs %a\n" d_type a d_type b; r -and eq_typ (a: typ) (b: typ) (context: context) = eq_typ_acc a b [] context +and eq_typ (a: typ) (b: typ) (rename_mapping: rename_mapping) = eq_typ_acc a b [] rename_mapping -and eq_eitems (context: context) (a: string * exp * location) (b: string * exp * location) = match a, b with - (name1, exp1, _l1), (name2, exp2, _l2) -> name1 = name2 && eq_exp exp1 exp2 context +and eq_eitems (rename_mapping: rename_mapping) (a: string * exp * location) (b: string * exp * location) = match a, b with + (name1, exp1, _l1), (name2, exp2, _l2) -> name1 = name2 && eq_exp exp1 exp2 rename_mapping (* Ignore location *) -and eq_enuminfo (a: enuminfo) (b: enuminfo) (context: context) = +and eq_enuminfo (a: enuminfo) (b: enuminfo) (rename_mapping: rename_mapping) = compare_name a.ename b.ename && - GobList.equal (eq_attribute context) a.eattr b.eattr && - GobList.equal (eq_eitems context) a.eitems b.eitems + GobList.equal (eq_attribute rename_mapping) a.eattr b.eattr && + GobList.equal (eq_eitems rename_mapping) a.eitems b.eitems (* Ignore ereferenced *) -and eq_args (context: context) (acc: (typ * typ) list) (a: string * typ * attributes) (b: string * typ * attributes) = match a, b with +and eq_args (rename_mapping: rename_mapping) (acc: (typ * typ) list) (a: string * typ * attributes) (b: string * typ * attributes) = match a, b with (name1, typ1, attr1), (name2, typ2, attr2) -> - context_aware_name_comparison name1 name2 context && eq_typ_acc typ1 typ2 acc context && GobList.equal (eq_attribute context) attr1 attr2 + rename_mapping_aware_name_comparison name1 name2 rename_mapping && eq_typ_acc typ1 typ2 acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 -and eq_attrparam (context: context) (a: attrparam) (b: attrparam) = match a, b with - | ACons (str1, attrparams1), ACons (str2, attrparams2) -> str1 = str2 && GobList.equal (eq_attrparam context) attrparams1 attrparams2 - | ASizeOf typ1, ASizeOf typ2 -> eq_typ typ1 typ2 context - | ASizeOfE attrparam1, ASizeOfE attrparam2 -> eq_attrparam context attrparam1 attrparam2 +and eq_attrparam (rename_mapping: rename_mapping) (a: attrparam) (b: attrparam) = match a, b with + | ACons (str1, attrparams1), ACons (str2, attrparams2) -> str1 = str2 && GobList.equal (eq_attrparam rename_mapping) attrparams1 attrparams2 + | ASizeOf typ1, ASizeOf typ2 -> eq_typ typ1 typ2 rename_mapping + | ASizeOfE attrparam1, ASizeOfE attrparam2 -> eq_attrparam rename_mapping attrparam1 attrparam2 | ASizeOfS typsig1, ASizeOfS typsig2 -> typsig1 = typsig2 - | AAlignOf typ1, AAlignOf typ2 -> eq_typ typ1 typ2 context - | AAlignOfE attrparam1, AAlignOfE attrparam2 -> eq_attrparam context attrparam1 attrparam2 + | AAlignOf typ1, AAlignOf typ2 -> eq_typ typ1 typ2 rename_mapping + | AAlignOfE attrparam1, AAlignOfE attrparam2 -> eq_attrparam rename_mapping attrparam1 attrparam2 | AAlignOfS typsig1, AAlignOfS typsig2 -> typsig1 = typsig2 - | AUnOp (op1, attrparam1), AUnOp (op2, attrparam2) -> op1 = op2 && eq_attrparam context attrparam1 attrparam2 - | ABinOp (op1, left1, right1), ABinOp (op2, left2, right2) -> op1 = op2 && eq_attrparam context left1 left2 && eq_attrparam context right1 right2 - | ADot (attrparam1, str1), ADot (attrparam2, str2) -> eq_attrparam context attrparam1 attrparam2 && str1 = str2 - | AStar attrparam1, AStar attrparam2 -> eq_attrparam context attrparam1 attrparam2 - | AAddrOf attrparam1, AAddrOf attrparam2 -> eq_attrparam context attrparam1 attrparam2 - | AIndex (left1, right1), AIndex (left2, right2) -> eq_attrparam context left1 left2 && eq_attrparam context right1 right2 - | AQuestion (left1, middle1, right1), AQuestion (left2, middle2, right2) -> eq_attrparam context left1 left2 && eq_attrparam context middle1 middle2 && eq_attrparam context right1 right2 + | AUnOp (op1, attrparam1), AUnOp (op2, attrparam2) -> op1 = op2 && eq_attrparam rename_mapping attrparam1 attrparam2 + | ABinOp (op1, left1, right1), ABinOp (op2, left2, right2) -> op1 = op2 && eq_attrparam rename_mapping left1 left2 && eq_attrparam rename_mapping right1 right2 + | ADot (attrparam1, str1), ADot (attrparam2, str2) -> eq_attrparam rename_mapping attrparam1 attrparam2 && str1 = str2 + | AStar attrparam1, AStar attrparam2 -> eq_attrparam rename_mapping attrparam1 attrparam2 + | AAddrOf attrparam1, AAddrOf attrparam2 -> eq_attrparam rename_mapping attrparam1 attrparam2 + | AIndex (left1, right1), AIndex (left2, right2) -> eq_attrparam rename_mapping left1 left2 && eq_attrparam rename_mapping right1 right2 + | AQuestion (left1, middle1, right1), AQuestion (left2, middle2, right2) -> eq_attrparam rename_mapping left1 left2 && eq_attrparam rename_mapping middle1 middle2 && eq_attrparam rename_mapping right1 right2 | a, b -> a = b -and eq_attribute (context: context) (a: attribute) (b: attribute) = match a, b with - | Attr (name1, params1), Attr (name2, params2) -> name1 = name2 && GobList.equal (eq_attrparam context) params1 params2 +and eq_attribute (rename_mapping: rename_mapping) (a: attribute) (b: attribute) = match a, b with + | Attr (name1, params1), Attr (name2, params2) -> name1 = name2 && GobList.equal (eq_attrparam rename_mapping) params1 params2 -and eq_varinfo2 (context: context) (a: varinfo) (b: varinfo) = eq_varinfo a b context +and eq_varinfo2 (rename_mapping: rename_mapping) (a: varinfo) (b: varinfo) = eq_varinfo a b rename_mapping -and eq_varinfo (a: varinfo) (b: varinfo) (context: context) = +and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) = (*Printf.printf "Comp %s with %s\n" a.vname b.vname;*) - let (_, method_contexts) = context in + let (_, method_rename_mappings) = rename_mapping in - (*When we compare function names, we can directly compare the naming from the context if it exists.*) + (*When we compare function names, we can directly compare the naming from the rename_mapping if it exists.*) let isNamingOk = match b.vtype with | TFun(_, _, _, _) -> ( - let specific_method_context = List.find_opt (fun x -> match x with + let specific_method_rename_mapping = List.find_opt (fun x -> match x with | {original_method_name; new_method_name; parameter_renames} -> original_method_name = a.vname && new_method_name = b.vname - ) method_contexts in - match specific_method_context with - | Some method_context -> method_context.original_method_name = a.vname && method_context.new_method_name = b.vname + ) method_rename_mappings in + match specific_method_rename_mapping with + | Some method_rename_mapping -> method_rename_mapping.original_method_name = a.vname && method_rename_mapping.new_method_name = b.vname | None -> a.vname = b.vname ) - | _ -> context_aware_name_comparison a.vname b.vname context + | _ -> rename_mapping_aware_name_comparison a.vname b.vname rename_mapping in (*If the following is a method call, we need to check if we have a mapping for that method call. *) - let typ_context = match b.vtype with + let typ_rename_mapping = match b.vtype with | TFun(_, _, _, _) -> ( let new_locals = List.find_opt (fun x -> match x with | {original_method_name; new_method_name; parameter_renames} -> original_method_name = a.vname && new_method_name = b.vname - ) method_contexts in + ) method_rename_mappings in match new_locals with | Some locals -> - (*Printf.printf "Performing context switch. New context=%s\n" (context_to_string (locals.parameter_renames, method_contexts));*) - (locals.parameter_renames, method_contexts) - | None -> ([], method_contexts) + (*Printf.printf "Performing rename_mapping switch. New rename_mapping=%s\n" (rename_mapping_to_string (locals.parameter_renames, method_rename_mappings));*) + (locals.parameter_renames, method_rename_mappings) + | None -> ([], method_rename_mappings) ) - | _ -> context + | _ -> rename_mapping in - let typeCheck = eq_typ a.vtype b.vtype typ_context in - let attrCheck = GobList.equal (eq_attribute context) a.vattr b.vattr in + let typeCheck = eq_typ a.vtype b.vtype typ_rename_mapping in + let attrCheck = GobList.equal (eq_attribute rename_mapping) a.vattr b.vattr in (*let _ = if isNamingOk then a.vname <- b.vname in*) @@ -220,36 +220,36 @@ and eq_varinfo (a: varinfo) (b: varinfo) (context: context) = (* Ignore the location, vid, vreferenced, vdescr, vdescrpure, vinline *) (* Accumulator is needed because of recursive types: we have to assume that two types we already encountered in a previous step of the recursion are equivalent *) -and eq_compinfo (a: compinfo) (b: compinfo) (acc: (typ * typ) list) (context: context) = +and eq_compinfo (a: compinfo) (b: compinfo) (acc: (typ * typ) list) (rename_mapping: rename_mapping) = a.cstruct = b.cstruct && compare_name a.cname b.cname && - GobList.equal (fun a b-> eq_fieldinfo a b acc context) a.cfields b.cfields && - GobList.equal (eq_attribute context) a.cattr b.cattr && + GobList.equal (fun a b-> eq_fieldinfo a b acc rename_mapping) a.cfields b.cfields && + GobList.equal (eq_attribute rename_mapping) a.cattr b.cattr && a.cdefined = b.cdefined (* Ignore ckey, and ignore creferenced *) -and eq_fieldinfo (a: fieldinfo) (b: fieldinfo) (acc: (typ * typ) list) (context: context) = +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; - let r = a.fname = b.fname && eq_typ_acc a.ftype b.ftype acc context && a.fbitfield = b.fbitfield && GobList.equal (eq_attribute context) a.fattr b.fattr in + let r = a.fname = b.fname && eq_typ_acc a.ftype b.ftype acc rename_mapping && a.fbitfield = b.fbitfield && GobList.equal (eq_attribute rename_mapping) a.fattr b.fattr in if Messages.tracing then Messages.traceu "compareast" "fieldinfo %s vs %s\n" a.fname b.fname; r -and eq_offset (a: offset) (b: offset) (context: context) = match a, b with +and eq_offset (a: offset) (b: offset) (rename_mapping: rename_mapping) = match a, b with NoOffset, NoOffset -> true - | Field (info1, offset1), Field (info2, offset2) -> eq_fieldinfo info1 info2 [] context && eq_offset offset1 offset2 context - | Index (exp1, offset1), Index (exp2, offset2) -> eq_exp exp1 exp2 context && eq_offset offset1 offset2 context + | Field (info1, offset1), Field (info2, offset2) -> eq_fieldinfo info1 info2 [] rename_mapping && eq_offset offset1 offset2 rename_mapping + | Index (exp1, offset1), Index (exp2, offset2) -> eq_exp exp1 exp2 rename_mapping && eq_offset offset1 offset2 rename_mapping | _, _ -> false -and eq_lval (a: lval) (b: lval) (context: context) = match a, b with - (host1, off1), (host2, off2) -> eq_lhost host1 host2 context && eq_offset off1 off2 context +and eq_lval (a: lval) (b: lval) (rename_mapping: rename_mapping) = match a, b with + (host1, off1), (host2, off2) -> eq_lhost host1 host2 rename_mapping && eq_offset off1 off2 rename_mapping -let eq_instr (context: context) (a: instr) (b: instr) = match a, b with - | Set (lv1, exp1, _l1, _el1), Set (lv2, exp2, _l2, _el2) -> eq_lval lv1 lv2 context && eq_exp exp1 exp2 context +let eq_instr (rename_mapping: rename_mapping) (a: instr) (b: instr) = match a, b with + | Set (lv1, exp1, _l1, _el1), Set (lv2, exp2, _l2, _el2) -> eq_lval lv1 lv2 rename_mapping && eq_exp exp1 exp2 rename_mapping | Call (Some lv1, f1, args1, _l1, _el1), Call (Some lv2, f2, args2, _l2, _el2) -> - eq_lval lv1 lv2 context && eq_exp f1 f2 context && GobList.equal (eq_exp2 context) args1 args2 + eq_lval lv1 lv2 rename_mapping && eq_exp f1 f2 rename_mapping && GobList.equal (eq_exp2 rename_mapping) args1 args2 | Call (None, f1, args1, _l1, _el1), Call (None, f2, args2, _l2, _el2) -> - eq_exp f1 f2 context && GobList.equal (eq_exp2 context) args1 args2 - | Asm (attr1, tmp1, ci1, dj1, rk1, l1), Asm (attr2, tmp2, ci2, dj2, rk2, l2) -> GobList.equal String.equal tmp1 tmp2 && GobList.equal(fun (x1,y1,z1) (x2,y2,z2)-> x1 = x2 && y1 = y2 && eq_lval z1 z2 context) ci1 ci2 && GobList.equal(fun (x1,y1,z1) (x2,y2,z2)-> x1 = x2 && y1 = y2 && eq_exp z1 z2 context) dj1 dj2 && GobList.equal String.equal rk1 rk2(* ignore attributes and locations *) - | VarDecl (v1, _l1), VarDecl (v2, _l2) -> eq_varinfo v1 v2 context + eq_exp f1 f2 rename_mapping && GobList.equal (eq_exp2 rename_mapping) args1 args2 + | Asm (attr1, tmp1, ci1, dj1, rk1, l1), Asm (attr2, tmp2, ci2, dj2, rk2, l2) -> GobList.equal String.equal tmp1 tmp2 && GobList.equal(fun (x1,y1,z1) (x2,y2,z2)-> x1 = x2 && y1 = y2 && eq_lval z1 z2 rename_mapping) ci1 ci2 && GobList.equal(fun (x1,y1,z1) (x2,y2,z2)-> x1 = x2 && y1 = y2 && eq_exp z1 z2 rename_mapping) dj1 dj2 && GobList.equal String.equal rk1 rk2(* ignore attributes and locations *) + | VarDecl (v1, _l1), VarDecl (v2, _l2) -> eq_varinfo v1 v2 rename_mapping | _, _ -> false let eq_label (a: label) (b: label) = match a, b with @@ -268,35 +268,35 @@ let eq_stmt_with_location ((a, af): stmt * fundec) ((b, bf): stmt * fundec) = through the cfg and only compares the currently visited node (The cil blocks inside an if statement should not be compared together with its condition to avoid a to early and not precise detection of a changed node inside). Switch, break and continue statements are removed during cfg preparation and therefore need not to be handeled *) -let rec eq_stmtkind ?(cfg_comp = false) ((a, af): stmtkind * fundec) ((b, bf): stmtkind * fundec) (context: context) = - let eq_block' = fun x y -> if cfg_comp then true else eq_block (x, af) (y, bf) context in +let rec eq_stmtkind ?(cfg_comp = false) ((a, af): stmtkind * fundec) ((b, bf): stmtkind * fundec) (rename_mapping: rename_mapping) = + let eq_block' = fun x y -> if cfg_comp then true else eq_block (x, af) (y, bf) rename_mapping in match a, b with - | Instr is1, Instr is2 -> GobList.equal (eq_instr context) is1 is2 - | Return (Some exp1, _l1), Return (Some exp2, _l2) -> eq_exp exp1 exp2 context + | Instr is1, Instr is2 -> GobList.equal (eq_instr rename_mapping) is1 is2 + | Return (Some exp1, _l1), Return (Some exp2, _l2) -> eq_exp exp1 exp2 rename_mapping | Return (None, _l1), Return (None, _l2) -> true | Return _, Return _ -> false | Goto (st1, _l1), Goto (st2, _l2) -> eq_stmt_with_location (!st1, af) (!st2, bf) | Break _, Break _ -> if cfg_comp then failwith "CompareCFG: Invalid stmtkind in CFG" else true | Continue _, Continue _ -> if cfg_comp then failwith "CompareCFG: Invalid stmtkind in CFG" else true - | If (exp1, then1, else1, _l1, _el1), If (exp2, then2, else2, _l2, _el2) -> eq_exp exp1 exp2 context && eq_block' then1 then2 && eq_block' else1 else2 - | Switch (exp1, block1, stmts1, _l1, _el1), Switch (exp2, block2, stmts2, _l2, _el2) -> if cfg_comp then failwith "CompareCFG: Invalid stmtkind in CFG" else eq_exp exp1 exp2 context && eq_block' block1 block2 && GobList.equal (fun a b -> eq_stmt (a,af) (b,bf) context) stmts1 stmts2 + | If (exp1, then1, else1, _l1, _el1), If (exp2, then2, else2, _l2, _el2) -> eq_exp exp1 exp2 rename_mapping && eq_block' then1 then2 && eq_block' else1 else2 + | Switch (exp1, block1, stmts1, _l1, _el1), Switch (exp2, block2, stmts2, _l2, _el2) -> if cfg_comp then failwith "CompareCFG: Invalid stmtkind in CFG" else eq_exp exp1 exp2 rename_mapping && eq_block' block1 block2 && GobList.equal (fun a b -> eq_stmt (a,af) (b,bf) rename_mapping) stmts1 stmts2 | Loop (block1, _l1, _el1, _con1, _br1), Loop (block2, _l2, _el2, _con2, _br2) -> eq_block' block1 block2 | Block block1, Block block2 -> eq_block' block1 block2 | _, _ -> false -and eq_stmt ?(cfg_comp = false) ((a, af): stmt * fundec) ((b, bf): stmt * fundec) (context: context) = +and eq_stmt ?(cfg_comp = false) ((a, af): stmt * fundec) ((b, bf): stmt * fundec) (rename_mapping: rename_mapping) = GobList.equal eq_label a.labels b.labels && - eq_stmtkind ~cfg_comp (a.skind, af) (b.skind, bf) context + eq_stmtkind ~cfg_comp (a.skind, af) (b.skind, bf) rename_mapping -and eq_block ((a, af): Cil.block * fundec) ((b, bf): Cil.block * fundec) (context: context) = - a.battrs = b.battrs && GobList.equal (fun x y -> eq_stmt (x, af) (y, bf) context) a.bstmts b.bstmts +and eq_block ((a, af): Cil.block * fundec) ((b, bf): Cil.block * fundec) (rename_mapping: rename_mapping) = + a.battrs = b.battrs && GobList.equal (fun x y -> eq_stmt (x, af) (y, bf) rename_mapping) a.bstmts b.bstmts -let rec eq_init (a: init) (b: init) (context: context) = match a, b with - | SingleInit e1, SingleInit e2 -> eq_exp e1 e2 context - | CompoundInit (t1, l1), CompoundInit (t2, l2) -> eq_typ t1 t2 context && GobList.equal (fun (o1, i1) (o2, i2) -> eq_offset o1 o2 context && eq_init i1 i2 context) l1 l2 +let rec eq_init (a: init) (b: init) (rename_mapping: rename_mapping) = match a, b with + | SingleInit e1, SingleInit e2 -> eq_exp e1 e2 rename_mapping + | CompoundInit (t1, l1), CompoundInit (t2, l2) -> eq_typ t1 t2 rename_mapping && GobList.equal (fun (o1, i1) (o2, i2) -> eq_offset o1 o2 rename_mapping && eq_init i1 i2 rename_mapping) l1 l2 | _, _ -> false -let eq_initinfo (a: initinfo) (b: initinfo) (context: context) = match a.init, b.init with - | (Some init_a), (Some init_b) -> eq_init init_a init_b context +let eq_initinfo (a: initinfo) (b: initinfo) (rename_mapping: rename_mapping) = match a.init, b.init with + | (Some init_a), (Some init_b) -> eq_init init_a init_b rename_mapping | None, None -> true | _, _ -> false \ No newline at end of file diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index 69cdb1a471..e2492de086 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -4,28 +4,28 @@ open Cil include CompareAST let eq_node (x, fun1) (y, fun2) = - let empty_context: context = ([], []) in + let empty_rename_mapping: rename_mapping = ([], []) in match x,y with - | Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) empty_context - | Function f1, Function f2 -> eq_varinfo f1.svar f2.svar empty_context - | FunctionEntry f1, FunctionEntry f2 -> eq_varinfo f1.svar f2.svar empty_context + | Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) empty_rename_mapping + | Function f1, Function f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping + | FunctionEntry f1, FunctionEntry f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping | _ -> false (* TODO: compare ASMs properly instead of simply always assuming that they are not the same *) -let eq_edge x y = - let empty_context: context = ([], []) in +let eq_edge x y = + let empty_rename_mapping: rename_mapping = ([], []) in match x, y with - | Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 empty_context && eq_exp rv1 rv2 empty_context - | Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 empty_context && GobList.equal (eq_exp2 empty_context) ars1 ars2 + | Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 empty_rename_mapping && eq_exp rv1 rv2 empty_rename_mapping + | Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 empty_rename_mapping && GobList.equal (eq_exp2 empty_rename_mapping) ars1 ars2 | Proc (Some r1,f1,ars1), Proc (Some r2,f2,ars2) -> - eq_lval r1 r2 empty_context && eq_exp f1 f2 empty_context && GobList.equal (eq_exp2 empty_context) ars1 ars2 - | Entry f1, Entry f2 -> eq_varinfo f1.svar f2.svar empty_context - | Ret (None,fd1), Ret (None,fd2) -> eq_varinfo fd1.svar fd2.svar empty_context - | Ret (Some r1,fd1), Ret (Some r2,fd2) -> eq_exp r1 r2 empty_context && eq_varinfo fd1.svar fd2.svar empty_context - | Test (p1,b1), Test (p2,b2) -> eq_exp p1 p2 empty_context && b1 = b2 + eq_lval r1 r2 empty_rename_mapping && eq_exp f1 f2 empty_rename_mapping && GobList.equal (eq_exp2 empty_rename_mapping) ars1 ars2 + | Entry f1, Entry f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping + | Ret (None,fd1), Ret (None,fd2) -> eq_varinfo fd1.svar fd2.svar empty_rename_mapping + | Ret (Some r1,fd1), Ret (Some r2,fd2) -> eq_exp r1 r2 empty_rename_mapping && eq_varinfo fd1.svar fd2.svar empty_rename_mapping + | Test (p1,b1), Test (p2,b2) -> eq_exp p1 p2 empty_rename_mapping && b1 = b2 | ASM _, ASM _ -> false | Skip, Skip -> true - | VDecl v1, VDecl v2 -> eq_varinfo v1 v2 empty_context + | VDecl v1, VDecl v2 -> eq_varinfo v1 v2 empty_rename_mapping | _ -> false (* The order of the edges in the list is relevant. Therefore compare them one to one without sorting first *) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 28bd0806b1..40fd0b877a 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -36,7 +36,7 @@ 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) (global_rename_mapping: method_rename_assumptions) = +let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (global_rename_mapping: method_rename_assumption list) = 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 @@ -47,17 +47,16 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo (* 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, string) Hashtbl.t) = match alocals, blocals with + let rec rename_mapping_aware_compare (alocals: varinfo list) (blocals: varinfo list) (rename_mapping: local_rename_assumption list) = match alocals, blocals with | [], [] -> true, rename_mapping | origLocal :: als, nowLocal :: bls -> - if origLocal.vname <> nowLocal.vname then Hashtbl.add rename_mapping origLocal.vname nowLocal.vname; - - (*TODO: maybe optimize this with eq_varinfo*) - rename_mapping_aware_compare als bls rename_mapping + let new_rename_mapping = if origLocal.vname = nowLocal.vname then rename_mapping else rename_mapping @ [(origLocal.vname, nowLocal.vname)] in + (*TODO: also call eq_varinfo*) + rename_mapping_aware_compare als bls new_rename_mapping | _, _ -> false, rename_mapping in - let headerSizeEqual, headerRenameMapping = rename_mapping_aware_compare a.sformals b.sformals (Hashtbl.create 0) in + let headerSizeEqual, headerRenameMapping = rename_mapping_aware_compare a.sformals b.sformals [] 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 @@ -84,13 +83,13 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo in identical, unchangedHeader, diffOpt -let eq_glob (a: global) (b: global) (cfgs : (cfg * (cfg * cfg)) option) (global_rename_mapping: method_rename_assumptions) = match a, b with +let eq_glob (a: global) (b: global) (cfgs : (cfg * (cfg * cfg)) option) (global_rename_ammping: method_rename_assumption list) = match a, b with | GFun (f,_), GFun (g,_) -> - let identical, unchangedHeader, diffOpt = eqF f g cfgs global_rename_mapping in + let identical, unchangedHeader, diffOpt = eqF f g cfgs global_rename_ammping in identical, unchangedHeader, diffOpt - | GVar (x, init_x, _), GVar (y, init_y, _) -> eq_varinfo x y (Hashtbl.create 0, Hashtbl.create 0), 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 (Hashtbl.create 0, Hashtbl.create 0), false, None + | 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 | _ -> 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) = @@ -105,18 +104,13 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = match old_global, global with | GFun(f, _), GFun (g, _) -> - let renamed_params: (string, string) Hashtbl.t = if (List.length f.sformals) = (List.length g.sformals) then + let renamed_params: (string * string) list = if (List.length f.sformals) = (List.length g.sformals) then 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)) |> - (fun list -> - let table: (string, string) Hashtbl.t = Hashtbl.create (List.length list) in - List.iter (fun mapping -> Hashtbl.add table (fst mapping) (snd mapping)) list; - table - ) - else Hashtbl.create 0 in - - if not (f.svar.vname = g.svar.vname) || (Hashtbl.length renamed_params) > 0 then + List.map (fun (original, now) -> (original.vname, now.vname)) + else [] in + + if not (f.svar.vname = g.svar.vname) || (List.length renamed_params) > 0 then Some {original_method_name=f.svar.vname; new_method_name=g.svar.vname; parameter_renames=renamed_params} else None | _, _ -> None @@ -157,16 +151,11 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = 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 list) global -> + let global_rename_mapping: method_rename_assumption list = Cil.foldGlobals newAST (fun (current_global_rename_mapping: method_rename_assumption list) global -> match generate_global_rename_mapping oldMap global with | Some rename_mapping -> current_global_rename_mapping @ [rename_mapping] | None -> current_global_rename_mapping - ) [] |> - (fun mappings -> - let table = Hashtbl.create (List.length mappings) in - List.iter (fun mapping -> Hashtbl.add table mapping.original_method_name mapping) mappings; - table - ) in + ) [] 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. *) @@ -181,4 +170,4 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = (** Given an (optional) equality function between [Cil.global]s, an old and a new [Cil.file], this function computes a [change_info], which describes which [global]s are changed, unchanged, removed and added. *) let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = - Stats.time "compareCilFiles" (compareCilFiles ~eq oldAST) newAST \ No newline at end of file + Stats.time "compareCilFiles" (compareCilFiles ~eq oldAST) newAST From aed7a3aa875f6c3482fe2f7db4ffe2d37cfdc18c Mon Sep 17 00:00:00 2001 From: Tim ORtel <100865202+TimOrtel@users.noreply.github.com> Date: Mon, 9 May 2022 16:09:32 +0200 Subject: [PATCH 15/90] Replaced rename mapping lists with Hashtbls for increased performance --- src/incremental/compareAST.ml | 27 +++++++++------------ src/incremental/compareCFG.ml | 4 ++-- src/incremental/compareCIL.ml | 45 ++++++++++++++++++++++------------- 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 36662c8c81..e42e3539d2 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -5,22 +5,21 @@ type global_type = Fun | Decl | Var and global_identifier = {name: string ; global_t: global_type} [@@deriving ord] -type local_rename_assumption = string * string -(**) -type method_rename_assumption = {original_method_name: string; new_method_name: string; parameter_renames: (string * string) list} +type method_rename_assumption = {original_method_name: string; new_method_name: string; parameter_renames: (string, string) Hashtbl.t} +type method_rename_assumptions = (string, method_rename_assumption) Hashtbl.t (*rename_mapping is carried through the stack when comparing the AST. Holds a list of rename assumptions.*) -type rename_mapping = (local_rename_assumption list) * (method_rename_assumption list) +type rename_mapping = ((string, string) Hashtbl.t) * (method_rename_assumptions) (*Compares two names, being aware of the rename_mapping. Returns true iff: 1. there is a rename for name1 -> name2 = rename(name1) 2. there is no rename for name1 -> name1 = name2*) let rename_mapping_aware_name_comparison (name1: string) (name2: string) (rename_mapping: rename_mapping) = let (local_c, method_c) = rename_mapping in - let existingAssumption: (string*string) option = List.find_opt (fun x -> match x with (original, now) -> original = name1) local_c in + let existingAssumption: string option = Hashtbl.find_opt local_c name1 in match existingAssumption with - | Some (original, now) -> + | Some now -> (*Printf.printf "Assumption is: %s -> %s\n" original now;*) now = name2 | None -> @@ -33,11 +32,11 @@ let string_tuple_to_string (tuple: (string * string) list) = "[" ^ (tuple |> let rename_mapping_to_string (rename_mapping: rename_mapping) = let (local, methods) = rename_mapping in - let local_string = string_tuple_to_string local in - let methods_string: string = methods |> + let local_string = string_tuple_to_string (List.of_seq (Hashtbl.to_seq local)) in + let methods_string: string = List.of_seq (Hashtbl.to_seq_values methods) |> List.map (fun x -> match x with {original_method_name; new_method_name; parameter_renames} -> "(methodName: " ^ original_method_name ^ " -> " ^ new_method_name ^ - "; renamed_params=" ^ string_tuple_to_string parameter_renames ^ ")") |> + "; renamed_params=" ^ string_tuple_to_string (List.of_seq (Hashtbl.to_seq parameter_renames)) ^ ")") |> String.concat ", " in "(local=" ^ local_string ^ "; methods=[" ^ methods_string ^ "])" @@ -180,9 +179,7 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) = (*When we compare function names, we can directly compare the naming from the rename_mapping if it exists.*) let isNamingOk = match b.vtype with | TFun(_, _, _, _) -> ( - let specific_method_rename_mapping = List.find_opt (fun x -> match x with - | {original_method_name; new_method_name; parameter_renames} -> original_method_name = a.vname && new_method_name = b.vname - ) method_rename_mappings in + let specific_method_rename_mapping = Hashtbl.find_opt method_rename_mappings a.vname in match specific_method_rename_mapping with | Some method_rename_mapping -> method_rename_mapping.original_method_name = a.vname && method_rename_mapping.new_method_name = b.vname | None -> a.vname = b.vname @@ -193,15 +190,13 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) = (*If the following is a method call, we need to check if we have a mapping for that method call. *) let typ_rename_mapping = match b.vtype with | TFun(_, _, _, _) -> ( - let new_locals = List.find_opt (fun x -> match x with - | {original_method_name; new_method_name; parameter_renames} -> original_method_name = a.vname && new_method_name = b.vname - ) method_rename_mappings in + let new_locals = Hashtbl.find_opt method_rename_mappings a.vname in match new_locals with | Some locals -> (*Printf.printf "Performing rename_mapping switch. New rename_mapping=%s\n" (rename_mapping_to_string (locals.parameter_renames, method_rename_mappings));*) (locals.parameter_renames, method_rename_mappings) - | None -> ([], method_rename_mappings) + | None -> (Hashtbl.create 0, method_rename_mappings) ) | _ -> rename_mapping in diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index e2492de086..4557cb88b3 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -4,7 +4,7 @@ open Cil include CompareAST let eq_node (x, fun1) (y, fun2) = - let empty_rename_mapping: rename_mapping = ([], []) in + let empty_rename_mapping: rename_mapping = (Hashtbl.create 0, Hashtbl.create 0) in match x,y with | Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) empty_rename_mapping | Function f1, Function f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping @@ -13,7 +13,7 @@ let eq_node (x, fun1) (y, fun2) = (* TODO: compare ASMs properly instead of simply always assuming that they are not the same *) let eq_edge x y = - let empty_rename_mapping: rename_mapping = ([], []) in + let empty_rename_mapping: rename_mapping = (Hashtbl.create 0, Hashtbl.create 0) in match x, y with | Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 empty_rename_mapping && eq_exp rv1 rv2 empty_rename_mapping | Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 empty_rename_mapping && GobList.equal (eq_exp2 empty_rename_mapping) ars1 ars2 diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 40fd0b877a..f5817ae76e 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -36,7 +36,7 @@ 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) (global_rename_mapping: method_rename_assumption list) = +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 @@ -47,16 +47,17 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo (* 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: local_rename_assumption list) = match alocals, blocals with + let rec rename_mapping_aware_compare (alocals: varinfo list) (blocals: varinfo list) (rename_mapping: (string, string) Hashtbl.t) = match alocals, blocals with | [], [] -> true, rename_mapping | origLocal :: als, nowLocal :: bls -> - let new_rename_mapping = if origLocal.vname = nowLocal.vname then rename_mapping else rename_mapping @ [(origLocal.vname, nowLocal.vname)] in - (*TODO: also call eq_varinfo*) - rename_mapping_aware_compare als bls new_rename_mapping + if origLocal.vname <> nowLocal.vname then Hashtbl.add rename_mapping origLocal.vname nowLocal.vname; + + (*TODO: maybe optimize this with eq_varinfo*) + rename_mapping_aware_compare als bls rename_mapping | _, _ -> false, rename_mapping in - let headerSizeEqual, headerRenameMapping = rename_mapping_aware_compare a.sformals b.sformals [] in + let headerSizeEqual, headerRenameMapping = rename_mapping_aware_compare a.sformals b.sformals (Hashtbl.create 0) 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 @@ -83,13 +84,13 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo in identical, unchangedHeader, diffOpt -let eq_glob (a: global) (b: global) (cfgs : (cfg * (cfg * cfg)) option) (global_rename_ammping: method_rename_assumption list) = match a, b with +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_ammping in + let identical, unchangedHeader, diffOpt = eqF f g cfgs global_rename_mapping in identical, unchangedHeader, diffOpt - | 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 + | GVar (x, init_x, _), GVar (y, init_y, _) -> eq_varinfo x y (Hashtbl.create 0, Hashtbl.create 0), 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 (Hashtbl.create 0, Hashtbl.create 0), 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) = @@ -104,13 +105,18 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = match old_global, global with | GFun(f, _), GFun (g, _) -> - let renamed_params: (string * string) list = if (List.length f.sformals) = (List.length g.sformals) then + let renamed_params: (string, string) Hashtbl.t = if (List.length f.sformals) = (List.length g.sformals) then 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)) - else [] in - - if not (f.svar.vname = g.svar.vname) || (List.length renamed_params) > 0 then + List.map (fun (original, now) -> (original.vname, now.vname)) |> + (fun list -> + let table: (string, string) Hashtbl.t = Hashtbl.create (List.length list) in + List.iter (fun mapping -> Hashtbl.add table (fst mapping) (snd mapping)) list; + table + ) + else Hashtbl.create 0 in + + if not (f.svar.vname = g.svar.vname) || (Hashtbl.length renamed_params) > 0 then Some {original_method_name=f.svar.vname; new_method_name=g.svar.vname; parameter_renames=renamed_params} else None | _, _ -> None @@ -151,11 +157,16 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = let oldMap = Cil.foldGlobals oldAST addGlobal GlobalMap.empty in let newMap = Cil.foldGlobals newAST addGlobal GlobalMap.empty in - let global_rename_mapping: method_rename_assumption list = Cil.foldGlobals newAST (fun (current_global_rename_mapping: method_rename_assumption list) global -> + let global_rename_mapping: method_rename_assumptions = Cil.foldGlobals newAST (fun (current_global_rename_mapping: method_rename_assumption list) global -> match generate_global_rename_mapping oldMap global with | Some rename_mapping -> current_global_rename_mapping @ [rename_mapping] | None -> current_global_rename_mapping - ) [] in + ) [] |> + (fun mappings -> + let table = Hashtbl.create (List.length mappings) in + List.iter (fun mapping -> Hashtbl.add table mapping.original_method_name mapping) mappings; + table + ) 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. *) From 9e95ddbde4805fd32df2d568672e47c4a6a270de Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Sat, 14 May 2022 15:16:43 +0200 Subject: [PATCH 16/90] Old locals are now renamed to the new local names. --- src/incremental/updateCil.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/incremental/updateCil.ml b/src/incremental/updateCil.ml index b2655f9d54..2cf28ba329 100644 --- a/src/incremental/updateCil.ml +++ b/src/incremental/updateCil.ml @@ -43,7 +43,7 @@ let update_ids (old_file: file) (ids: max_ids) (new_file: file) (changes: change in let reset_fun (f: fundec) (old_f: fundec) = f.svar.vid <- old_f.svar.vid; - List.iter2 (fun l o_l -> l.vid <- o_l.vid) f.slocals old_f.slocals; + List.iter2 (fun l o_l -> l.vid <- o_l.vid; o_l.vname <- l.vname) f.slocals old_f.slocals; List.iter2 (fun lo o_f -> lo.vid <- o_f.vid) f.sformals old_f.sformals; List.iter2 (fun s o_s -> s.sid <- o_s.sid) f.sallstmts old_f.sallstmts; List.iter (fun s -> store_node_location (Statement s) (Cilfacade.get_stmtLoc s)) f.sallstmts; From 7e89ec2d1ae1fac5d0fd07433df21a3671ca6a3b Mon Sep 17 00:00:00 2001 From: Tim ORtel <100865202+TimOrtel@users.noreply.github.com> Date: Mon, 16 May 2022 13:46:14 +0200 Subject: [PATCH 17/90] Fixed duplicate id tests --- .../04-var-rename/{ => diffs}/08-2_incremental_runs_2.c | 4 ++-- .../04-var-rename/{ => diffs}/08-2_incremental_runs_3.c | 0 .../04-var-rename/{ => diffs}/09-2_ir_with_changes_2.c | 0 .../04-var-rename/{ => diffs}/09-2_ir_with_changes_3.c | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename tests/incremental/04-var-rename/{ => diffs}/08-2_incremental_runs_2.c (92%) rename tests/incremental/04-var-rename/{ => diffs}/08-2_incremental_runs_3.c (100%) rename tests/incremental/04-var-rename/{ => diffs}/09-2_ir_with_changes_2.c (100%) rename tests/incremental/04-var-rename/{ => diffs}/09-2_ir_with_changes_3.c (100%) diff --git a/tests/incremental/04-var-rename/08-2_incremental_runs_2.c b/tests/incremental/04-var-rename/diffs/08-2_incremental_runs_2.c similarity index 92% rename from tests/incremental/04-var-rename/08-2_incremental_runs_2.c rename to tests/incremental/04-var-rename/diffs/08-2_incremental_runs_2.c index 1190fdb14c..43205a976e 100644 --- a/tests/incremental/04-var-rename/08-2_incremental_runs_2.c +++ b/tests/incremental/04-var-rename/diffs/08-2_incremental_runs_2.c @@ -1,8 +1,8 @@ int main() { int varSecondIteration = 0; - + varSecondIteration++; - + assert(varSecondIteration < 10); return 0; } diff --git a/tests/incremental/04-var-rename/08-2_incremental_runs_3.c b/tests/incremental/04-var-rename/diffs/08-2_incremental_runs_3.c similarity index 100% rename from tests/incremental/04-var-rename/08-2_incremental_runs_3.c rename to tests/incremental/04-var-rename/diffs/08-2_incremental_runs_3.c diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes_2.c b/tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_2.c similarity index 100% rename from tests/incremental/04-var-rename/09-2_ir_with_changes_2.c rename to tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_2.c diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes_3.c b/tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_3.c similarity index 100% rename from tests/incremental/04-var-rename/09-2_ir_with_changes_3.c rename to tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_3.c From e4df2cab546726a25d6a2de42a82981f04c2878b Mon Sep 17 00:00:00 2001 From: Tim ORtel <100865202+TimOrtel@users.noreply.github.com> Date: Mon, 16 May 2022 15:19:32 +0200 Subject: [PATCH 18/90] Added some test cases --- .../05-method-rename/00-simple_rename.c | 10 ++++++++ .../05-method-rename/00-simple_rename.patch | 15 ++++++++++++ .../05-method-rename/01-dependent_rename.c | 14 +++++++++++ .../01-dependent_rename.patch | 21 +++++++++++++++++ .../05-method-rename/02-rename_and_swap.c | 19 +++++++++++++++ .../03-cyclic_rename_dependency.c | 17 ++++++++++++++ .../05-method-rename/04-cyclic_with_swap.c | 16 +++++++++++++ .../05-method-rename/diffs/00-simple_rename.c | 10 ++++++++ .../diffs/01-dependent_rename.c | 14 +++++++++++ .../diffs/02-rename_and_swap.c | 23 +++++++++++++++++++ .../diffs/03-cyclic_rename_dependency.c | 17 ++++++++++++++ .../diffs/04-cyclic_with_swap.c | 20 ++++++++++++++++ 12 files changed, 196 insertions(+) create mode 100644 tests/incremental/05-method-rename/00-simple_rename.c create mode 100644 tests/incremental/05-method-rename/00-simple_rename.patch create mode 100644 tests/incremental/05-method-rename/01-dependent_rename.c create mode 100644 tests/incremental/05-method-rename/01-dependent_rename.patch create mode 100644 tests/incremental/05-method-rename/02-rename_and_swap.c create mode 100644 tests/incremental/05-method-rename/03-cyclic_rename_dependency.c create mode 100644 tests/incremental/05-method-rename/04-cyclic_with_swap.c create mode 100644 tests/incremental/05-method-rename/diffs/00-simple_rename.c create mode 100644 tests/incremental/05-method-rename/diffs/01-dependent_rename.c create mode 100644 tests/incremental/05-method-rename/diffs/02-rename_and_swap.c create mode 100644 tests/incremental/05-method-rename/diffs/03-cyclic_rename_dependency.c create mode 100644 tests/incremental/05-method-rename/diffs/04-cyclic_with_swap.c diff --git a/tests/incremental/05-method-rename/00-simple_rename.c b/tests/incremental/05-method-rename/00-simple_rename.c new file mode 100644 index 0000000000..5d1e6fe872 --- /dev/null +++ b/tests/incremental/05-method-rename/00-simple_rename.c @@ -0,0 +1,10 @@ +#include + +void foo() { + printf("foo"); +} + +int main() { + foo(); + return 0; +} diff --git a/tests/incremental/05-method-rename/00-simple_rename.patch b/tests/incremental/05-method-rename/00-simple_rename.patch new file mode 100644 index 0000000000..407a5a9bbf --- /dev/null +++ b/tests/incremental/05-method-rename/00-simple_rename.patch @@ -0,0 +1,15 @@ +--- tests/incremental/05-method_rename/00-simple_rename.c ++++ tests/incremental/05-method_rename/00-simple_rename.c +@@ -1,10 +1,10 @@ + #include + +-void foo() { ++void bar() { + printf("foo"); + } + + int main() { +- foo(); ++ bar(); + return 0; + } diff --git a/tests/incremental/05-method-rename/01-dependent_rename.c b/tests/incremental/05-method-rename/01-dependent_rename.c new file mode 100644 index 0000000000..66c1a5a634 --- /dev/null +++ b/tests/incremental/05-method-rename/01-dependent_rename.c @@ -0,0 +1,14 @@ +#include + +void fun1() { + printf("fun1"); +} + +void fun2() { + fun1(); +} + +int main() { + fun2(); + return 0; +} diff --git a/tests/incremental/05-method-rename/01-dependent_rename.patch b/tests/incremental/05-method-rename/01-dependent_rename.patch new file mode 100644 index 0000000000..5eedfd814b --- /dev/null +++ b/tests/incremental/05-method-rename/01-dependent_rename.patch @@ -0,0 +1,21 @@ +--- tests/incremental/05-method_rename/01-dependent_rename.c ++++ tests/incremental/05-method_rename/01-dependent_rename.c +@@ -1,14 +1,14 @@ + #include + +-void fun1() { ++void bar1() { + printf("fun1"); + } + +-void fun2() { +- fun1(); ++void bar2() { ++ bar1(); + } + + int main() { +- fun2(); ++ bar2(); + return 0; + } diff --git a/tests/incremental/05-method-rename/02-rename_and_swap.c b/tests/incremental/05-method-rename/02-rename_and_swap.c new file mode 100644 index 0000000000..f62edd44a4 --- /dev/null +++ b/tests/incremental/05-method-rename/02-rename_and_swap.c @@ -0,0 +1,19 @@ +#include + +void foo1() { + printf("foo1"); +} + +void foo2() { + foo1(); +} + +void foo3() { + foo1(); +} + +int main() { + foo2(); + foo3(); + return 0; +} diff --git a/tests/incremental/05-method-rename/03-cyclic_rename_dependency.c b/tests/incremental/05-method-rename/03-cyclic_rename_dependency.c new file mode 100644 index 0000000000..2509cfbcd5 --- /dev/null +++ b/tests/incremental/05-method-rename/03-cyclic_rename_dependency.c @@ -0,0 +1,17 @@ +#include + +//Unchanged. + +void foo1(int c) { + if (c < 10) foo2(c + 1); +} + +void foo2(int c) { + if (c < 10) foo1(c + 1); +} + +int main() { + foo1(0); + foo2(0); + return 0; +} diff --git a/tests/incremental/05-method-rename/04-cyclic_with_swap.c b/tests/incremental/05-method-rename/04-cyclic_with_swap.c new file mode 100644 index 0000000000..74123d5a14 --- /dev/null +++ b/tests/incremental/05-method-rename/04-cyclic_with_swap.c @@ -0,0 +1,16 @@ +#include + +//Changed. + +void foo1(int c) { + if (c < 10) foo2(c + 1); +} + +void foo2(int c) { + if (c < 10) foo1(c + 1); +} + +int main() { + foo1(0); + return 0; +} diff --git a/tests/incremental/05-method-rename/diffs/00-simple_rename.c b/tests/incremental/05-method-rename/diffs/00-simple_rename.c new file mode 100644 index 0000000000..79a05fe8d4 --- /dev/null +++ b/tests/incremental/05-method-rename/diffs/00-simple_rename.c @@ -0,0 +1,10 @@ +#include + +void bar() { + printf("foo"); +} + +int main() { + bar(); + return 0; +} diff --git a/tests/incremental/05-method-rename/diffs/01-dependent_rename.c b/tests/incremental/05-method-rename/diffs/01-dependent_rename.c new file mode 100644 index 0000000000..a2c5d48fea --- /dev/null +++ b/tests/incremental/05-method-rename/diffs/01-dependent_rename.c @@ -0,0 +1,14 @@ +#include + +void bar1() { + printf("fun1"); +} + +void bar2() { + bar1(); +} + +int main() { + bar2(); + return 0; +} diff --git a/tests/incremental/05-method-rename/diffs/02-rename_and_swap.c b/tests/incremental/05-method-rename/diffs/02-rename_and_swap.c new file mode 100644 index 0000000000..eae4b77001 --- /dev/null +++ b/tests/incremental/05-method-rename/diffs/02-rename_and_swap.c @@ -0,0 +1,23 @@ +#include + +void newFun() { + printf("newFun"); +} + +void bar1() { + printf("foo1"); +} + +void foo2() { + bar1(); +} + +void foo3() { + newFun(); +} + +int main() { + foo2(); + foo3(); + return 0; +} diff --git a/tests/incremental/05-method-rename/diffs/03-cyclic_rename_dependency.c b/tests/incremental/05-method-rename/diffs/03-cyclic_rename_dependency.c new file mode 100644 index 0000000000..a720f8025e --- /dev/null +++ b/tests/incremental/05-method-rename/diffs/03-cyclic_rename_dependency.c @@ -0,0 +1,17 @@ +#include + +//Unchanged. + +void bar1(int c) { + if (c < 10) bar2(c + 1); +} + +void bar2(int c) { + if (c < 10) bar1(c + 1); +} + +int main() { + bar1(0); + bar2(0); + return 0; +} diff --git a/tests/incremental/05-method-rename/diffs/04-cyclic_with_swap.c b/tests/incremental/05-method-rename/diffs/04-cyclic_with_swap.c new file mode 100644 index 0000000000..67cb349429 --- /dev/null +++ b/tests/incremental/05-method-rename/diffs/04-cyclic_with_swap.c @@ -0,0 +1,20 @@ +#include + +//Changed. + +void newFun(int c) { + printf("newfun"); +} + +void bar1(int c) { + if (c < 10) bar2(c + 1); +} + +void bar2(int c) { + if (c < 10) newFun(c + 1); +} + +int main() { + bar1(0); + return 0; +} From 2c8641185f6fc90e2c273a98544274ed9f460844 Mon Sep 17 00:00:00 2001 From: Tim ORtel <100865202+TimOrtel@users.noreply.github.com> Date: Tue, 17 May 2022 16:36:59 +0200 Subject: [PATCH 19/90] Added first functions for rename detection. --- src/incremental/detectRenamedFunctions.ml | 43 +++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/incremental/detectRenamedFunctions.ml diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml new file mode 100644 index 0000000000..c4fe19674e --- /dev/null +++ b/src/incremental/detectRenamedFunctions.ml @@ -0,0 +1,43 @@ +open Cil +open MyCFG +include CompareAST +include CompareCFG + +(*Maps the function name as keys*) +module FundecMap = Map.Make(String);; + +type functionStatus = Identical | Renamed | Changed | New | Deleted +type results = (fundec, functionStatus) Hashtbl.t + +(*A dependency mapps the function it depends on to the name the function has to be changed to*) +type dependencies = (fundec, string) Hashtbl.t + +type earlyFunctionStatus = Unchanged of dependencies | Unknown + +let getFunctionMap (ast: file) : fundec FundecMap.t = + Cil.foldGlobals ast (fun map global -> + match global with + | GFun (fundec, _) -> FundecMap.add fundec.svar.vname fundec map + | _ -> map + ) FundecMap.empty + +let seperateUnchangedFunctions (oldFunctionMap: fundec FundecMap.t) (newFunctionMap: fundec FundecMap.t) : earlyFunctionStatus FundecMap.t = + FundecMap.map (fun f -> + let matchingNewFundec = FundecMap.find_opt f.svar.vname newFunctionMap in + match matchingNewFundec with + | Some newFun -> + (*Compare if they are similar*) + let result = CompareCIL.eqF f newFun None (Hashtbl.create 0) in + Unknown + | None -> Unknown + ) oldFunctionMap + +let detectRenamedFunctions (oldAST: file) (newAST: file) : results = begin + let oldFunctionMap = getFunctionMap oldAST in + let newFunctionMap = getFunctionMap newAST in + + (*1. detect function which names have not changed*) + let unchangedNameFunctions = FundecMap.map (fun _ fundec -> ) oldFunctionMap in + + Hashtbl.create 0 +end From 182db42f257b333bf6f8853861e69422fcbd23f2 Mon Sep 17 00:00:00 2001 From: Tim ORtel <100865202+TimOrtel@users.noreply.github.com> Date: Tue, 17 May 2022 17:01:29 +0200 Subject: [PATCH 20/90] Replaced Hashtbl in compare functions with Map --- src/incremental/compareAST.ml | 56 ++++++++++++++++++----------------- src/incremental/compareCFG.ml | 6 ++-- src/incremental/compareCIL.ml | 30 ++++++++----------- 3 files changed, 45 insertions(+), 47 deletions(-) diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index e42e3539d2..4f268d387a 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -5,24 +5,26 @@ type global_type = Fun | Decl | Var and global_identifier = {name: string ; global_t: global_type} [@@deriving ord] -type method_rename_assumption = {original_method_name: string; new_method_name: string; parameter_renames: (string, string) Hashtbl.t} -type method_rename_assumptions = (string, method_rename_assumption) Hashtbl.t +module StringMap = Map.Make(String) + +type method_rename_assumption = {original_method_name: string; new_method_name: string; parameter_renames: string StringMap.t} +type method_rename_assumptions = method_rename_assumption StringMap.t (*rename_mapping is carried through the stack when comparing the AST. Holds a list of rename assumptions.*) -type rename_mapping = ((string, string) Hashtbl.t) * (method_rename_assumptions) +type rename_mapping = (string StringMap.t) * (method_rename_assumptions) (*Compares two names, being aware of the rename_mapping. Returns true iff: 1. there is a rename for name1 -> name2 = rename(name1) 2. there is no rename for name1 -> name1 = name2*) -let rename_mapping_aware_name_comparison (name1: string) (name2: string) (rename_mapping: rename_mapping) = +let rename_mapping_aware_name_comparison (name1: string) (name2: string) (rename_mapping: rename_mapping) = let (local_c, method_c) = rename_mapping in - let existingAssumption: string option = Hashtbl.find_opt local_c name1 in + let existingAssumption: string option = StringMap.find_opt name1 local_c in match existingAssumption with - | Some now -> + | Some now -> (*Printf.printf "Assumption is: %s -> %s\n" original now;*) now = name2 - | None -> + | None -> (*Printf.printf "No assumption when %s, %s, %b\n" name1 name2 (name1 = name2);*) name1 = name2 (*Var names differ, but there is no assumption, so this can't be good*) @@ -30,13 +32,13 @@ let string_tuple_to_string (tuple: (string * string) list) = "[" ^ (tuple |> List.map (fun x -> match x with (first, second) -> "(" ^ first ^ " -> " ^ second ^ ")") |> String.concat ", ") ^ "]" -let rename_mapping_to_string (rename_mapping: rename_mapping) = +let rename_mapping_to_string (rename_mapping: rename_mapping) = let (local, methods) = rename_mapping in - let local_string = string_tuple_to_string (List.of_seq (Hashtbl.to_seq local)) in - let methods_string: string = List.of_seq (Hashtbl.to_seq_values methods) |> - List.map (fun x -> match x with {original_method_name; new_method_name; parameter_renames} -> - "(methodName: " ^ original_method_name ^ " -> " ^ new_method_name ^ - "; renamed_params=" ^ string_tuple_to_string (List.of_seq (Hashtbl.to_seq parameter_renames)) ^ ")") |> + let local_string = string_tuple_to_string (List.of_seq (StringMap.to_seq local)) in + let methods_string: string = List.of_seq (StringMap.to_seq methods |> Seq.map snd) |> + List.map (fun x -> match x with {original_method_name; new_method_name; parameter_renames} -> + "(methodName: " ^ original_method_name ^ " -> " ^ new_method_name ^ + "; renamed_params=" ^ string_tuple_to_string (List.of_seq (StringMap.to_seq parameter_renames)) ^ ")") |> String.concat ", " in "(local=" ^ local_string ^ "; methods=[" ^ methods_string ^ "])" @@ -58,7 +60,7 @@ let compare_name (a: string) (b: string) = let anon_union = "__anonunion_" in if a = b then true else BatString.(starts_with a anon_struct && starts_with b anon_struct || starts_with a anon_union && starts_with b anon_union) -let rec eq_constant (rename_mapping: rename_mapping) (a: constant) (b: constant) = +let rec eq_constant (rename_mapping: rename_mapping) (a: constant) (b: constant) = match a, b with | CInt (val1, kind1, str1), CInt (val2, kind2, str2) -> Cilint.compare_cilint val1 val2 = 0 && kind1 = kind2 (* Ignore string representation, i.e. 0x2 == 2 *) | CEnum (exp1, str1, enuminfo1), CEnum (exp2, str2, enuminfo2) -> eq_exp exp1 exp2 rename_mapping (* Ignore name and enuminfo *) @@ -66,9 +68,9 @@ let rec eq_constant (rename_mapping: rename_mapping) (a: constant) (b: constant) and eq_exp2 (rename_mapping: rename_mapping) (a: exp) (b: exp) = eq_exp a b rename_mapping -and eq_exp (a: exp) (b: exp) (rename_mapping: rename_mapping) = +and eq_exp (a: exp) (b: exp) (rename_mapping: rename_mapping) = match a, b with - | Const c1, Const c2 -> eq_constant rename_mapping c1 c2 + | Const c1, Const c2 -> eq_constant rename_mapping c1 c2 | Lval lv1, Lval lv2 -> eq_lval lv1 lv2 rename_mapping | SizeOf typ1, SizeOf typ2 -> eq_typ typ1 typ2 rename_mapping | SizeOfE exp1, SizeOfE exp2 -> eq_exp exp1 exp2 rename_mapping @@ -146,7 +148,7 @@ and eq_enuminfo (a: enuminfo) (b: enuminfo) (rename_mapping: rename_mapping) = (* Ignore ereferenced *) and eq_args (rename_mapping: rename_mapping) (acc: (typ * typ) list) (a: string * typ * attributes) (b: string * typ * attributes) = match a, b with - (name1, typ1, attr1), (name2, typ2, attr2) -> + (name1, typ1, attr1), (name2, typ2, attr2) -> rename_mapping_aware_name_comparison name1 name2 rename_mapping && eq_typ_acc typ1 typ2 acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 and eq_attrparam (rename_mapping: rename_mapping) (a: attrparam) (b: attrparam) = match a, b with @@ -171,7 +173,7 @@ and eq_attribute (rename_mapping: rename_mapping) (a: attribute) (b: attribute) and eq_varinfo2 (rename_mapping: rename_mapping) (a: varinfo) (b: varinfo) = eq_varinfo a b rename_mapping -and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) = +and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) = (*Printf.printf "Comp %s with %s\n" a.vname b.vname;*) let (_, method_rename_mappings) = rename_mapping in @@ -179,24 +181,24 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) = (*When we compare function names, we can directly compare the naming from the rename_mapping if it exists.*) let isNamingOk = match b.vtype with | TFun(_, _, _, _) -> ( - let specific_method_rename_mapping = Hashtbl.find_opt method_rename_mappings a.vname in + let specific_method_rename_mapping = StringMap.find_opt a.vname method_rename_mappings in match specific_method_rename_mapping with | Some method_rename_mapping -> method_rename_mapping.original_method_name = a.vname && method_rename_mapping.new_method_name = b.vname | None -> a.vname = b.vname ) | _ -> rename_mapping_aware_name_comparison a.vname b.vname rename_mapping - in + in (*If the following is a method call, we need to check if we have a mapping for that method call. *) let typ_rename_mapping = match b.vtype with | TFun(_, _, _, _) -> ( - let new_locals = Hashtbl.find_opt method_rename_mappings a.vname in + let new_locals = StringMap.find_opt a.vname method_rename_mappings in match new_locals with - | Some locals -> + | Some locals -> (*Printf.printf "Performing rename_mapping switch. New rename_mapping=%s\n" (rename_mapping_to_string (locals.parameter_renames, method_rename_mappings));*) (locals.parameter_renames, method_rename_mappings) - | None -> (Hashtbl.create 0, method_rename_mappings) + | None -> (StringMap.empty, method_rename_mappings) ) | _ -> rename_mapping in @@ -207,7 +209,7 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) = (*let _ = if isNamingOk then a.vname <- b.vname in*) (*let _ = Printf.printf "Comparing vars: %s = %s\n" a.vname b.vname in *) - (*a.vname = b.vname*) + (*a.vname = b.vname*) let result = isNamingOk && typeCheck && attrCheck && a.vstorage = b.vstorage && a.vglob = b.vglob && a.vaddrof = b.vaddrof in @@ -239,9 +241,9 @@ and eq_lval (a: lval) (b: lval) (rename_mapping: rename_mapping) = match a, b wi let eq_instr (rename_mapping: rename_mapping) (a: instr) (b: instr) = match a, b with | Set (lv1, exp1, _l1, _el1), Set (lv2, exp2, _l2, _el2) -> eq_lval lv1 lv2 rename_mapping && eq_exp exp1 exp2 rename_mapping - | Call (Some lv1, f1, args1, _l1, _el1), Call (Some lv2, f2, args2, _l2, _el2) -> + | Call (Some lv1, f1, args1, _l1, _el1), Call (Some lv2, f2, args2, _l2, _el2) -> eq_lval lv1 lv2 rename_mapping && eq_exp f1 f2 rename_mapping && GobList.equal (eq_exp2 rename_mapping) args1 args2 - | Call (None, f1, args1, _l1, _el1), Call (None, f2, args2, _l2, _el2) -> + | Call (None, f1, args1, _l1, _el1), Call (None, f2, args2, _l2, _el2) -> eq_exp f1 f2 rename_mapping && GobList.equal (eq_exp2 rename_mapping) args1 args2 | Asm (attr1, tmp1, ci1, dj1, rk1, l1), Asm (attr2, tmp2, ci2, dj2, rk2, l2) -> GobList.equal String.equal tmp1 tmp2 && GobList.equal(fun (x1,y1,z1) (x2,y2,z2)-> x1 = x2 && y1 = y2 && eq_lval z1 z2 rename_mapping) ci1 ci2 && GobList.equal(fun (x1,y1,z1) (x2,y2,z2)-> x1 = x2 && y1 = y2 && eq_exp z1 z2 rename_mapping) dj1 dj2 && GobList.equal String.equal rk1 rk2(* ignore attributes and locations *) | VarDecl (v1, _l1), VarDecl (v2, _l2) -> eq_varinfo v1 v2 rename_mapping @@ -294,4 +296,4 @@ let rec eq_init (a: init) (b: init) (rename_mapping: rename_mapping) = match a, let eq_initinfo (a: initinfo) (b: initinfo) (rename_mapping: rename_mapping) = match a.init, b.init with | (Some init_a), (Some init_b) -> eq_init init_a init_b rename_mapping | None, None -> true - | _, _ -> false \ No newline at end of file + | _, _ -> false diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index 4557cb88b3..2a52e6eafe 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -4,7 +4,7 @@ open Cil include CompareAST let eq_node (x, fun1) (y, fun2) = - let empty_rename_mapping: rename_mapping = (Hashtbl.create 0, Hashtbl.create 0) in + let empty_rename_mapping: rename_mapping = (StringMap.empty, StringMap.empty) in match x,y with | Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) empty_rename_mapping | Function f1, Function f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping @@ -12,8 +12,8 @@ let eq_node (x, fun1) (y, fun2) = | _ -> false (* TODO: compare ASMs properly instead of simply always assuming that they are not the same *) -let eq_edge x y = - let empty_rename_mapping: rename_mapping = (Hashtbl.create 0, Hashtbl.create 0) in +let eq_edge x y = + let empty_rename_mapping: rename_mapping = (StringMap.empty, StringMap.empty) in match x, y with | Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 empty_rename_mapping && eq_exp rv1 rv2 empty_rename_mapping | Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 empty_rename_mapping && GobList.equal (eq_exp2 empty_rename_mapping) ars1 ars2 diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index f5817ae76e..f6abd0c9f3 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -47,17 +47,17 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo (* 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, string) Hashtbl.t) = match alocals, blocals with + 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 -> - if origLocal.vname <> nowLocal.vname then Hashtbl.add rename_mapping origLocal.vname nowLocal.vname; + let new_mapping = if origLocal.vname <> nowLocal.vname then StringMap.add origLocal.vname nowLocal.vname rename_mapping else rename_mapping in (*TODO: maybe optimize this with eq_varinfo*) - rename_mapping_aware_compare als bls rename_mapping + rename_mapping_aware_compare als bls new_mapping | _, _ -> false, rename_mapping in - let headerSizeEqual, headerRenameMapping = rename_mapping_aware_compare a.sformals b.sformals (Hashtbl.create 0) 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 @@ -89,8 +89,8 @@ let eq_glob (a: global) (b: global) (cfgs : (cfg * (cfg * cfg)) option) (global_ let identical, unchangedHeader, diffOpt = eqF f g cfgs global_rename_mapping in identical, unchangedHeader, diffOpt - | GVar (x, init_x, _), GVar (y, init_y, _) -> eq_varinfo x y (Hashtbl.create 0, Hashtbl.create 0), 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 (Hashtbl.create 0, Hashtbl.create 0), false, None + | GVar (x, init_x, _), GVar (y, init_y, _) -> eq_varinfo x y (StringMap.empty, StringMap.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, StringMap.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) = @@ -105,18 +105,16 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = match old_global, global with | GFun(f, _), GFun (g, _) -> - let renamed_params: (string, string) Hashtbl.t = if (List.length f.sformals) = (List.length g.sformals) then + let renamed_params: string StringMap.t = if (List.length f.sformals) = (List.length g.sformals) then 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)) |> - (fun list -> - let table: (string, string) Hashtbl.t = Hashtbl.create (List.length list) in - List.iter (fun mapping -> Hashtbl.add table (fst mapping) (snd mapping)) list; - table + (fun list -> + List.fold_left (fun map mapping -> StringMap.add (fst mapping) (snd mapping) map) StringMap.empty list ) - else Hashtbl.create 0 in + else StringMap.empty in - if not (f.svar.vname = g.svar.vname) || (Hashtbl.length renamed_params) > 0 then + if not (f.svar.vname = g.svar.vname) || not (StringMap.is_empty renamed_params) then Some {original_method_name=f.svar.vname; new_method_name=g.svar.vname; parameter_renames=renamed_params} else None | _, _ -> None @@ -162,10 +160,8 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = | Some rename_mapping -> current_global_rename_mapping @ [rename_mapping] | None -> current_global_rename_mapping ) [] |> - (fun mappings -> - let table = Hashtbl.create (List.length mappings) in - List.iter (fun mapping -> Hashtbl.add table mapping.original_method_name mapping) mappings; - table + (fun mappings -> + List.fold_left (fun map mapping -> StringMap.add mapping.original_method_name mapping map) StringMap.empty mappings ) in (* For each function in the new file, check whether a function with the same name From 11de365781e3ac4a6b1217910d9553bc99936228 Mon Sep 17 00:00:00 2001 From: Tim ORtel <100865202+TimOrtel@users.noreply.github.com> Date: Tue, 17 May 2022 19:30:06 +0200 Subject: [PATCH 21/90] CompareAST functions now propagate updated rename_mappings in their return type --- src/incremental/compareAST.ml | 328 ++++++++++++++++++++-------------- src/incremental/compareCFG.ml | 36 ++-- src/incremental/compareCIL.ml | 12 +- 3 files changed, 224 insertions(+), 152 deletions(-) diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 4f268d387a..4834796d2b 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -14,8 +14,8 @@ type method_rename_assumptions = method_rename_assumption StringMap.t type rename_mapping = (string StringMap.t) * (method_rename_assumptions) (*Compares two names, being aware of the rename_mapping. Returns true iff: - 1. there is a rename for name1 -> name2 = rename(name1) - 2. there is no rename for name1 -> name1 = name2*) + 1. there is a rename for name1 -> name2 = rename(name1) + 2. there is no rename for name1 -> name1 = name2*) let rename_mapping_aware_name_comparison (name1: string) (name2: string) (rename_mapping: rename_mapping) = let (local_c, method_c) = rename_mapping in let existingAssumption: string option = StringMap.find_opt name1 local_c in @@ -28,18 +28,29 @@ let rename_mapping_aware_name_comparison (name1: string) (name2: string) (rename (*Printf.printf "No assumption when %s, %s, %b\n" name1 name2 (name1 = name2);*) name1 = name2 (*Var names differ, but there is no assumption, so this can't be good*) +(*Creates the mapping of local renames. If the locals do not match in size, an empty mapping is returned.*) +let create_locals_rename_mapping (originalLocalNames: string list) (updatedLocalNames: string list): string StringMap.t = + if (List.length originalLocalNames) = (List.length updatedLocalNames) then + List.combine originalLocalNames updatedLocalNames |> + List.filter (fun (original, now) -> not (original = now)) |> + List.map (fun (original, now) -> (original, now)) |> + (fun list -> + List.fold_left (fun map mapping -> StringMap.add (fst mapping) (snd mapping) map) StringMap.empty list + ) + else StringMap.empty + let string_tuple_to_string (tuple: (string * string) list) = "[" ^ (tuple |> - List.map (fun x -> match x with (first, second) -> "(" ^ first ^ " -> " ^ second ^ ")") |> - String.concat ", ") ^ "]" + List.map (fun x -> match x with (first, second) -> "(" ^ first ^ " -> " ^ second ^ ")") |> + String.concat ", ") ^ "]" let rename_mapping_to_string (rename_mapping: rename_mapping) = let (local, methods) = rename_mapping in let local_string = string_tuple_to_string (List.of_seq (StringMap.to_seq local)) in let methods_string: string = List.of_seq (StringMap.to_seq methods |> Seq.map snd) |> - List.map (fun x -> match x with {original_method_name; new_method_name; parameter_renames} -> - "(methodName: " ^ original_method_name ^ " -> " ^ new_method_name ^ - "; renamed_params=" ^ string_tuple_to_string (List.of_seq (StringMap.to_seq parameter_renames)) ^ ")") |> - String.concat ", " in + List.map (fun x -> match x with {original_method_name; new_method_name; parameter_renames} -> + "(methodName: " ^ original_method_name ^ " -> " ^ new_method_name ^ + "; renamed_params=" ^ string_tuple_to_string (List.of_seq (StringMap.to_seq parameter_renames)) ^ ")") |> + String.concat ", " in "(local=" ^ local_string ^ "; methods=[" ^ methods_string ^ "])" let identifier_of_global glob = @@ -53,6 +64,20 @@ module GlobalMap = Map.Make(struct type t = global_identifier [@@deriving ord] end) +(*rename mapping forward propagation, takes the result from a call and propagates the rename mapping to the next call. + the second call is only executed if the previous call returned true*) +let (&&>>) (prev_result: bool * rename_mapping) f : bool * rename_mapping = + let (prev_equal, updated_rename_mapping) = prev_result in + if prev_equal then f updated_rename_mapping else false, updated_rename_mapping + +(*Same as && but propagates the rename mapping*) +let (&&>) (prev_result: bool * rename_mapping) (b: bool) : bool * rename_mapping = + let (prev_equal, rename_mapping) = prev_result in + (prev_equal && b, rename_mapping) + +(*Same as Goblist.eq but propagates the rename_mapping*) +let forward_list_equal f l1 l2 (prev_result: rename_mapping) : bool * rename_mapping = + List.fold_left2 (fun (b, r) x y -> if b then f x y r else (b, r)) (true, prev_result) l1 l2 (* hack: CIL generates new type names for anonymous types - we want to ignore these *) let compare_name (a: string) (b: string) = @@ -60,34 +85,35 @@ let compare_name (a: string) (b: string) = let anon_union = "__anonunion_" in if a = b then true else BatString.(starts_with a anon_struct && starts_with b anon_struct || starts_with a anon_union && starts_with b anon_union) -let rec eq_constant (rename_mapping: rename_mapping) (a: constant) (b: constant) = +let rec eq_constant (rename_mapping: rename_mapping) (a: constant) (b: constant) : bool * rename_mapping = match a, b with - | CInt (val1, kind1, str1), CInt (val2, kind2, str2) -> Cilint.compare_cilint val1 val2 = 0 && kind1 = kind2 (* Ignore string representation, i.e. 0x2 == 2 *) + | CInt (val1, kind1, str1), CInt (val2, kind2, str2) -> Cilint.compare_cilint val1 val2 = 0 && kind1 = kind2, rename_mapping (* Ignore string representation, i.e. 0x2 == 2 *) | CEnum (exp1, str1, enuminfo1), CEnum (exp2, str2, enuminfo2) -> eq_exp exp1 exp2 rename_mapping (* Ignore name and enuminfo *) - | a, b -> a = b + | a, b -> a = b, rename_mapping and eq_exp2 (rename_mapping: rename_mapping) (a: exp) (b: exp) = eq_exp a b rename_mapping -and eq_exp (a: exp) (b: exp) (rename_mapping: rename_mapping) = +and eq_exp (a: exp) (b: exp) (rename_mapping: rename_mapping) : bool * rename_mapping = match a, b with | Const c1, Const c2 -> eq_constant rename_mapping c1 c2 | Lval lv1, Lval lv2 -> eq_lval lv1 lv2 rename_mapping | SizeOf typ1, SizeOf typ2 -> eq_typ typ1 typ2 rename_mapping | SizeOfE exp1, SizeOfE exp2 -> eq_exp exp1 exp2 rename_mapping - | SizeOfStr str1, SizeOfStr str2 -> str1 = str2 (* possibly, having the same length would suffice *) + | SizeOfStr str1, SizeOfStr str2 -> str1 = str2, rename_mapping (* possibly, having the same length would suffice *) | AlignOf typ1, AlignOf typ2 -> eq_typ typ1 typ2 rename_mapping | AlignOfE exp1, AlignOfE exp2 -> eq_exp exp1 exp2 rename_mapping - | UnOp (op1, exp1, typ1), UnOp (op2, exp2, typ2) -> op1 == op2 && eq_exp exp1 exp2 rename_mapping && eq_typ typ1 typ2 rename_mapping - | BinOp (op1, left1, right1, typ1), BinOp (op2, left2, right2, typ2) -> op1 = op2 && eq_exp left1 left2 rename_mapping && eq_exp right1 right2 rename_mapping && eq_typ typ1 typ2 rename_mapping - | CastE (typ1, exp1), CastE (typ2, exp2) -> eq_typ typ1 typ2 rename_mapping && eq_exp exp1 exp2 rename_mapping + | UnOp (op1, exp1, typ1), UnOp (op2, exp2, typ2) -> + (op1 == op2, rename_mapping) &&>> eq_exp exp1 exp2 &&>> eq_typ typ1 typ2 + | BinOp (op1, left1, right1, typ1), BinOp (op2, left2, right2, typ2) -> (op1 = op2, rename_mapping) &&>> eq_exp left1 left2 &&>> eq_exp right1 right2 &&>> eq_typ typ1 typ2 + | CastE (typ1, exp1), CastE (typ2, exp2) -> eq_typ typ1 typ2 rename_mapping &&>> eq_exp exp1 exp2 | AddrOf lv1, AddrOf lv2 -> eq_lval lv1 lv2 rename_mapping | StartOf lv1, StartOf lv2 -> eq_lval lv1 lv2 rename_mapping - | _, _ -> false + | _, _ -> false, rename_mapping and eq_lhost (a: lhost) (b: lhost) (rename_mapping: rename_mapping) = match a, b with Var v1, Var v2 -> eq_varinfo v1 v2 rename_mapping | Mem exp1, Mem exp2 -> eq_exp exp1 exp2 rename_mapping - | _, _ -> false + | _, _ -> false, rename_mapping and global_typ_acc: (typ * typ) list ref = ref [] (* TODO: optimize with physical Hashtbl? *) @@ -95,159 +121,191 @@ and mem_typ_acc (a: typ) (b: typ) acc = List.exists (fun p -> match p with (x, y and pretty_length () l = Pretty.num (List.length l) -and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (rename_mapping: rename_mapping) = +and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (rename_mapping: rename_mapping) : bool * rename_mapping = 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 *) - let r = match a, b with - | TPtr (typ1, attr1), TPtr (typ2, attr2) -> eq_typ_acc typ1 typ2 acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 - | TArray (typ1, (Some lenExp1), attr1), TArray (typ2, (Some lenExp2), attr2) -> eq_typ_acc typ1 typ2 acc rename_mapping && eq_exp lenExp1 lenExp2 rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 - | TArray (typ1, None, attr1), TArray (typ2, None, attr2) -> eq_typ_acc typ1 typ2 acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 - | TFun (typ1, (Some list1), varArg1, attr1), TFun (typ2, (Some list2), varArg2, attr2) - -> eq_typ_acc typ1 typ2 acc rename_mapping && GobList.equal (eq_args rename_mapping acc) list1 list2 && varArg1 = varArg2 && - GobList.equal (eq_attribute rename_mapping) attr1 attr2 - | TFun (typ1, None, varArg1, attr1), TFun (typ2, None, varArg2, attr2) - -> eq_typ_acc typ1 typ2 acc rename_mapping && varArg1 = varArg2 && - GobList.equal (eq_attribute rename_mapping) attr1 attr2 - | TNamed (typinfo1, attr1), TNamed (typeinfo2, attr2) -> eq_typ_acc typinfo1.ttype typeinfo2.ttype acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 (* Ignore tname, treferenced *) + let r, updated_rename_mapping = match a, b with + | TPtr (typ1, attr1), TPtr (typ2, attr2) -> + eq_typ_acc typ1 typ2 acc rename_mapping &&>> forward_list_equal eq_attribute attr1 attr2 + | TArray (typ1, (Some lenExp1), attr1), TArray (typ2, (Some lenExp2), attr2) -> eq_typ_acc typ1 typ2 acc rename_mapping &&>> eq_exp lenExp1 lenExp2 &&>> forward_list_equal (eq_attribute) attr1 attr2 + | TArray (typ1, None, attr1), TArray (typ2, None, attr2) -> eq_typ_acc typ1 typ2 acc rename_mapping &&>> forward_list_equal eq_attribute attr1 attr2 + | TFun (typ1, (Some list1), varArg1, attr1), TFun (typ2, (Some list2), varArg2, attr2) -> + eq_typ_acc typ1 typ2 acc rename_mapping &&>> + forward_list_equal (eq_args acc) list1 list2 &&> + (varArg1 = varArg2) &&>> + forward_list_equal eq_attribute attr1 attr2 + | TFun (typ1, None, varArg1, attr1), TFun (typ2, None, varArg2, attr2) -> + eq_typ_acc typ1 typ2 acc rename_mapping &&> + (varArg1 = varArg2) &&>> + forward_list_equal eq_attribute attr1 attr2 + | TNamed (typinfo1, attr1), TNamed (typeinfo2, attr2) -> + eq_typ_acc typinfo1.ttype typeinfo2.ttype acc rename_mapping &&>> forward_list_equal eq_attribute attr1 attr2 (* Ignore tname, treferenced *) | TNamed (tinf, attr), b -> eq_typ_acc tinf.ttype b acc rename_mapping (* Ignore tname, treferenced. TODO: dismiss attributes, or not? *) | a, TNamed (tinf, attr) -> eq_typ_acc a tinf.ttype acc rename_mapping (* Ignore tname, treferenced . TODO: dismiss attributes, or not? *) (* 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"; - true + true, rename_mapping ) else ( let acc = (a, b) :: acc in - let res = eq_compinfo compinfo1 compinfo2 acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 in + let (res, rm) = eq_compinfo compinfo1 compinfo2 acc rename_mapping &&>> forward_list_equal eq_attribute attr1 attr2 in if res && compinfo1.cname <> compinfo2.cname then compinfo2.cname <- compinfo1.cname; if res then global_typ_acc := (a, b) :: !global_typ_acc; - res + res, rm ) - | TEnum (enuminfo1, attr1), TEnum (enuminfo2, attr2) -> let res = eq_enuminfo enuminfo1 enuminfo2 rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 in (if res && enuminfo1.ename <> enuminfo2.ename then enuminfo2.ename <- enuminfo1.ename); res - | TBuiltin_va_list attr1, TBuiltin_va_list attr2 -> GobList.equal (eq_attribute rename_mapping) attr1 attr2 - | TVoid attr1, TVoid attr2 -> GobList.equal (eq_attribute rename_mapping) attr1 attr2 - | TInt (ik1, attr1), TInt (ik2, attr2) -> ik1 = ik2 && GobList.equal (eq_attribute rename_mapping) attr1 attr2 - | TFloat (fk1, attr1), TFloat (fk2, attr2) -> fk1 = fk2 && GobList.equal (eq_attribute rename_mapping) attr1 attr2 - | _, _ -> false + | TEnum (enuminfo1, attr1), TEnum (enuminfo2, attr2) -> + let (res, rm) = eq_enuminfo enuminfo1 enuminfo2 rename_mapping &&>> forward_list_equal eq_attribute attr1 attr2 in + (if res && enuminfo1.ename <> enuminfo2.ename then enuminfo2.ename <- enuminfo1.ename); + res, rm + | TBuiltin_va_list attr1, TBuiltin_va_list attr2 -> forward_list_equal eq_attribute attr1 attr2 rename_mapping + | TVoid attr1, TVoid attr2 -> forward_list_equal eq_attribute attr1 attr2 rename_mapping + | TInt (ik1, attr1), TInt (ik2, attr2) -> (ik1 = ik2, rename_mapping) &&>> forward_list_equal eq_attribute attr1 attr2 + | TFloat (fk1, attr1), TFloat (fk2, attr2) -> (fk1 = fk2, rename_mapping) &&>> forward_list_equal eq_attribute 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; - r + (r, updated_rename_mapping) -and eq_typ (a: typ) (b: typ) (rename_mapping: rename_mapping) = eq_typ_acc a b [] rename_mapping +and eq_typ (a: typ) (b: typ) (rename_mapping: rename_mapping) : bool * rename_mapping = eq_typ_acc a b [] rename_mapping -and eq_eitems (rename_mapping: rename_mapping) (a: string * exp * location) (b: string * exp * location) = match a, b with - (name1, exp1, _l1), (name2, exp2, _l2) -> name1 = name2 && eq_exp exp1 exp2 rename_mapping +and eq_eitems (a: string * exp * location) (b: string * exp * location) (rename_mapping: rename_mapping) = match a, b with + (name1, exp1, _l1), (name2, exp2, _l2) -> (name1 = name2, rename_mapping) &&>> eq_exp exp1 exp2 (* Ignore location *) and eq_enuminfo (a: enuminfo) (b: enuminfo) (rename_mapping: rename_mapping) = - compare_name a.ename b.ename && - GobList.equal (eq_attribute rename_mapping) a.eattr b.eattr && - GobList.equal (eq_eitems rename_mapping) a.eitems b.eitems + (compare_name a.ename b.ename, rename_mapping) &&>> + forward_list_equal eq_attribute a.eattr b.eattr &&>> + forward_list_equal eq_eitems a.eitems b.eitems (* Ignore ereferenced *) -and eq_args (rename_mapping: rename_mapping) (acc: (typ * typ) list) (a: string * typ * attributes) (b: string * typ * attributes) = match a, b with +and eq_args (acc: (typ * typ) list) (a: string * typ * attributes) (b: string * typ * attributes) (rename_mapping: rename_mapping) : bool * rename_mapping = match a, b with (name1, typ1, attr1), (name2, typ2, attr2) -> - rename_mapping_aware_name_comparison name1 name2 rename_mapping && eq_typ_acc typ1 typ2 acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 + (rename_mapping_aware_name_comparison name1 name2 rename_mapping, rename_mapping) &&>> + eq_typ_acc typ1 typ2 acc &&>> + forward_list_equal eq_attribute attr1 attr2 -and eq_attrparam (rename_mapping: rename_mapping) (a: attrparam) (b: attrparam) = match a, b with - | ACons (str1, attrparams1), ACons (str2, attrparams2) -> str1 = str2 && GobList.equal (eq_attrparam rename_mapping) attrparams1 attrparams2 +and eq_attrparam (a: attrparam) (b: attrparam) (rename_mapping: rename_mapping) : bool * rename_mapping = match a, b with + | ACons (str1, attrparams1), ACons (str2, attrparams2) -> (str1 = str2, rename_mapping) &&>> forward_list_equal eq_attrparam attrparams1 attrparams2 | ASizeOf typ1, ASizeOf typ2 -> eq_typ typ1 typ2 rename_mapping - | ASizeOfE attrparam1, ASizeOfE attrparam2 -> eq_attrparam rename_mapping attrparam1 attrparam2 - | ASizeOfS typsig1, ASizeOfS typsig2 -> typsig1 = typsig2 + | ASizeOfE attrparam1, ASizeOfE attrparam2 -> eq_attrparam attrparam1 attrparam2 rename_mapping + | ASizeOfS typsig1, ASizeOfS typsig2 -> typsig1 = typsig2, rename_mapping | AAlignOf typ1, AAlignOf typ2 -> eq_typ typ1 typ2 rename_mapping - | AAlignOfE attrparam1, AAlignOfE attrparam2 -> eq_attrparam rename_mapping attrparam1 attrparam2 - | AAlignOfS typsig1, AAlignOfS typsig2 -> typsig1 = typsig2 - | AUnOp (op1, attrparam1), AUnOp (op2, attrparam2) -> op1 = op2 && eq_attrparam rename_mapping attrparam1 attrparam2 - | ABinOp (op1, left1, right1), ABinOp (op2, left2, right2) -> op1 = op2 && eq_attrparam rename_mapping left1 left2 && eq_attrparam rename_mapping right1 right2 - | ADot (attrparam1, str1), ADot (attrparam2, str2) -> eq_attrparam rename_mapping attrparam1 attrparam2 && str1 = str2 - | AStar attrparam1, AStar attrparam2 -> eq_attrparam rename_mapping attrparam1 attrparam2 - | AAddrOf attrparam1, AAddrOf attrparam2 -> eq_attrparam rename_mapping attrparam1 attrparam2 - | AIndex (left1, right1), AIndex (left2, right2) -> eq_attrparam rename_mapping left1 left2 && eq_attrparam rename_mapping right1 right2 - | AQuestion (left1, middle1, right1), AQuestion (left2, middle2, right2) -> eq_attrparam rename_mapping left1 left2 && eq_attrparam rename_mapping middle1 middle2 && eq_attrparam rename_mapping right1 right2 - | a, b -> a = b - -and eq_attribute (rename_mapping: rename_mapping) (a: attribute) (b: attribute) = match a, b with - | Attr (name1, params1), Attr (name2, params2) -> name1 = name2 && GobList.equal (eq_attrparam rename_mapping) params1 params2 + | AAlignOfE attrparam1, AAlignOfE attrparam2 -> eq_attrparam attrparam1 attrparam2 rename_mapping + | AAlignOfS typsig1, AAlignOfS typsig2 -> typsig1 = typsig2, rename_mapping + | AUnOp (op1, attrparam1), AUnOp (op2, attrparam2) -> (op1 = op2, rename_mapping) &&>> eq_attrparam attrparam1 attrparam2 + | ABinOp (op1, left1, right1), ABinOp (op2, left2, right2) -> (op1 = op2, rename_mapping) &&>> eq_attrparam left1 left2 &&>> eq_attrparam right1 right2 + | ADot (attrparam1, str1), ADot (attrparam2, str2) -> eq_attrparam attrparam1 attrparam2 rename_mapping &&> (str1 = str2) + | AStar attrparam1, AStar attrparam2 -> eq_attrparam attrparam1 attrparam2 rename_mapping + | AAddrOf attrparam1, AAddrOf attrparam2 -> eq_attrparam attrparam1 attrparam2 rename_mapping + | AIndex (left1, right1), AIndex (left2, right2) -> eq_attrparam left1 left2 rename_mapping &&>> eq_attrparam right1 right2 + | AQuestion (left1, middle1, right1), AQuestion (left2, middle2, right2) -> + eq_attrparam left1 left2 rename_mapping &&>> + eq_attrparam middle1 middle2 &&>> + eq_attrparam right1 right2 + | a, b -> a = b, rename_mapping + +and eq_attribute (a: attribute) (b: attribute) (rename_mapping: rename_mapping) : bool * rename_mapping = match a, b with + | Attr (name1, params1), Attr (name2, params2) -> (name1 = name2, rename_mapping) &&>> forward_list_equal eq_attrparam params1 params2 and eq_varinfo2 (rename_mapping: rename_mapping) (a: varinfo) (b: varinfo) = eq_varinfo a b rename_mapping -and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) = +and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) : bool * rename_mapping = (*Printf.printf "Comp %s with %s\n" a.vname b.vname;*) - let (_, method_rename_mappings) = rename_mapping in + let (locals_renames, method_rename_mappings) = rename_mapping in (*When we compare function names, we can directly compare the naming from the rename_mapping if it exists.*) - let isNamingOk = match b.vtype with - | TFun(_, _, _, _) -> ( + let isNamingOk, updated_method_rename_mappings = match a.vtype, b.vtype with + | TFun(_, aParamSpec, _, _), TFun(_, bParamSpec, _, _) -> ( let specific_method_rename_mapping = StringMap.find_opt a.vname method_rename_mappings in match specific_method_rename_mapping with - | Some method_rename_mapping -> method_rename_mapping.original_method_name = a.vname && method_rename_mapping.new_method_name = b.vname - | None -> a.vname = b.vname + | Some method_rename_mapping -> + let is_naming_ok = method_rename_mapping.original_method_name = a.vname && method_rename_mapping.new_method_name = b.vname in + is_naming_ok, method_rename_mappings + | None -> + if a.vname <> b.vname then + (*Function that extracts the names from the param spec of the TFun*) + let extract_names_from_params param_spec = + Option.map (fun list -> List.map (fun (name, _, _) -> name) list) param_spec |> + Option.value ~default:[] + in + + (*No mapping exists yet. Create one.*) + let aParamNames = extract_names_from_params aParamSpec in + let bParamNames = extract_names_from_params bParamSpec in + + let assumption = + {original_method_name = a.vname; new_method_name = b.vname; parameter_renames = create_locals_rename_mapping aParamNames bParamNames} in + + true, StringMap.add a.vname assumption method_rename_mappings + else true, method_rename_mappings ) - | _ -> rename_mapping_aware_name_comparison a.vname b.vname rename_mapping - in + | _, _ -> rename_mapping_aware_name_comparison a.vname b.vname rename_mapping, method_rename_mappings + in (*If the following is a method call, we need to check if we have a mapping for that method call. *) let typ_rename_mapping = match b.vtype with - | TFun(_, _, _, _) -> ( - let new_locals = StringMap.find_opt a.vname method_rename_mappings in + | TFun(_, _, _, _) -> ( + let new_locals = StringMap.find_opt a.vname updated_method_rename_mappings in match new_locals with - | Some locals -> - (*Printf.printf "Performing rename_mapping switch. New rename_mapping=%s\n" (rename_mapping_to_string (locals.parameter_renames, method_rename_mappings));*) - (locals.parameter_renames, method_rename_mappings) - | None -> (StringMap.empty, method_rename_mappings) - ) - | _ -> rename_mapping - in - - let typeCheck = eq_typ a.vtype b.vtype typ_rename_mapping in - let attrCheck = GobList.equal (eq_attribute rename_mapping) a.vattr b.vattr in - - (*let _ = if isNamingOk then a.vname <- b.vname in*) + | Some locals -> + (*Printf.printf "Performing rename_mapping switch. New rename_mapping=%s\n" (rename_mapping_to_string (locals.parameter_renames, method_rename_mappings));*) + (locals.parameter_renames, updated_method_rename_mappings) + | None -> (StringMap.empty, updated_method_rename_mappings) + ) + | _ -> (locals_renames, updated_method_rename_mappings) + in - (*let _ = Printf.printf "Comparing vars: %s = %s\n" a.vname b.vname in *) - (*a.vname = b.vname*) - let result = isNamingOk && typeCheck && attrCheck && - a.vstorage = b.vstorage && a.vglob = b.vglob && a.vaddrof = b.vaddrof in + (*Ignore rename mapping for type check, as it doesn't change anyway*) + let (typeCheck, _) = eq_typ a.vtype b.vtype typ_rename_mapping in - result + (typeCheck, (locals_renames, updated_method_rename_mappings)) &&>> + forward_list_equal eq_attribute a.vattr b.vattr &&> + (a.vstorage = b.vstorage) &&> (a.vglob = b.vglob) &&> (a.vaddrof = b.vaddrof) (* Ignore the location, vid, vreferenced, vdescr, vdescrpure, vinline *) (* Accumulator is needed because of recursive types: we have to assume that two types we already encountered in a previous step of the recursion are equivalent *) -and eq_compinfo (a: compinfo) (b: compinfo) (acc: (typ * typ) list) (rename_mapping: rename_mapping) = - a.cstruct = b.cstruct && - compare_name a.cname b.cname && - GobList.equal (fun a b-> eq_fieldinfo a b acc rename_mapping) a.cfields b.cfields && - GobList.equal (eq_attribute rename_mapping) a.cattr b.cattr && - a.cdefined = b.cdefined (* Ignore ckey, and ignore creferenced *) +and eq_compinfo (a: compinfo) (b: compinfo) (acc: (typ * typ) list) (rename_mapping: rename_mapping) : bool * rename_mapping = + (a.cstruct = b.cstruct, rename_mapping) &&> + compare_name a.cname b.cname &&>> + forward_list_equal (fun a b -> eq_fieldinfo a b acc) a.cfields b.cfields &&>> + forward_list_equal eq_attribute a.cattr b.cattr &&> + (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; - let r = a.fname = b.fname && eq_typ_acc a.ftype b.ftype acc rename_mapping && a.fbitfield = b.fbitfield && GobList.equal (eq_attribute rename_mapping) a.fattr b.fattr in + 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 a.fattr b.fattr in if Messages.tracing then Messages.traceu "compareast" "fieldinfo %s vs %s\n" a.fname b.fname; - r + (r, rm) -and eq_offset (a: offset) (b: offset) (rename_mapping: rename_mapping) = match a, b with - NoOffset, NoOffset -> true - | Field (info1, offset1), Field (info2, offset2) -> eq_fieldinfo info1 info2 [] rename_mapping && eq_offset offset1 offset2 rename_mapping - | Index (exp1, offset1), Index (exp2, offset2) -> eq_exp exp1 exp2 rename_mapping && eq_offset offset1 offset2 rename_mapping - | _, _ -> false +and eq_offset (a: offset) (b: offset) (rename_mapping: rename_mapping) : bool * rename_mapping = match a, b with + NoOffset, NoOffset -> true, rename_mapping + | Field (info1, offset1), Field (info2, offset2) -> eq_fieldinfo info1 info2 [] rename_mapping &&>> eq_offset offset1 offset2 + | Index (exp1, offset1), Index (exp2, offset2) -> eq_exp exp1 exp2 rename_mapping &&>> eq_offset offset1 offset2 + | _, _ -> false, rename_mapping -and eq_lval (a: lval) (b: lval) (rename_mapping: rename_mapping) = match a, b with - (host1, off1), (host2, off2) -> eq_lhost host1 host2 rename_mapping && eq_offset off1 off2 rename_mapping +and eq_lval (a: lval) (b: lval) (rename_mapping: rename_mapping) : bool * rename_mapping = match a, b with + (host1, off1), (host2, off2) -> eq_lhost host1 host2 rename_mapping &&>> eq_offset off1 off2 -let eq_instr (rename_mapping: rename_mapping) (a: instr) (b: instr) = match a, b with - | Set (lv1, exp1, _l1, _el1), Set (lv2, exp2, _l2, _el2) -> eq_lval lv1 lv2 rename_mapping && eq_exp exp1 exp2 rename_mapping +let eq_instr (a: instr) (b: instr) (rename_mapping: rename_mapping) = match a, b with + | Set (lv1, exp1, _l1, _el1), Set (lv2, exp2, _l2, _el2) -> eq_lval lv1 lv2 rename_mapping &&>> eq_exp exp1 exp2 | Call (Some lv1, f1, args1, _l1, _el1), Call (Some lv2, f2, args2, _l2, _el2) -> - eq_lval lv1 lv2 rename_mapping && eq_exp f1 f2 rename_mapping && GobList.equal (eq_exp2 rename_mapping) args1 args2 + eq_lval lv1 lv2 rename_mapping &&>> eq_exp f1 f2 &&>> forward_list_equal eq_exp args1 args2 | Call (None, f1, args1, _l1, _el1), Call (None, f2, args2, _l2, _el2) -> - eq_exp f1 f2 rename_mapping && GobList.equal (eq_exp2 rename_mapping) args1 args2 - | Asm (attr1, tmp1, ci1, dj1, rk1, l1), Asm (attr2, tmp2, ci2, dj2, rk2, l2) -> GobList.equal String.equal tmp1 tmp2 && GobList.equal(fun (x1,y1,z1) (x2,y2,z2)-> x1 = x2 && y1 = y2 && eq_lval z1 z2 rename_mapping) ci1 ci2 && GobList.equal(fun (x1,y1,z1) (x2,y2,z2)-> x1 = x2 && y1 = y2 && eq_exp z1 z2 rename_mapping) dj1 dj2 && GobList.equal String.equal rk1 rk2(* ignore attributes and locations *) + eq_exp f1 f2 rename_mapping &&>> forward_list_equal eq_exp args1 args2 + | Asm (attr1, tmp1, ci1, dj1, rk1, l1), Asm (attr2, tmp2, ci2, dj2, rk2, l2) -> + (GobList.equal String.equal tmp1 tmp2, rename_mapping) &&>> + forward_list_equal (fun (x1,y1,z1) (x2,y2,z2) x-> (x1 = x2, x) &&> (y1 = y2) &&>> eq_lval z1 z2) ci1 ci2 &&>> + forward_list_equal (fun (x1,y1,z1) (x2,y2,z2) x-> (x1 = x2, x) &&> (y1 = y2) &&>> eq_exp z1 z2) dj1 dj2 &&> + GobList.equal String.equal rk1 rk2(* ignore attributes and locations *) | VarDecl (v1, _l1), VarDecl (v2, _l2) -> eq_varinfo v1 v2 rename_mapping - | _, _ -> false + | _, _ -> false, rename_mapping let eq_label (a: label) (b: label) = match a, b with Label (lb1, _l1, s1), Label (lb2, _l2, s2) -> lb1 = lb2 && s1 = s2 @@ -266,34 +324,38 @@ let eq_stmt_with_location ((a, af): stmt * fundec) ((b, bf): stmt * fundec) = compared together with its condition to avoid a to early and not precise detection of a changed node inside). Switch, break and continue statements are removed during cfg preparation and therefore need not to be handeled *) let rec eq_stmtkind ?(cfg_comp = false) ((a, af): stmtkind * fundec) ((b, bf): stmtkind * fundec) (rename_mapping: rename_mapping) = - let eq_block' = fun x y -> if cfg_comp then true else eq_block (x, af) (y, bf) rename_mapping in + let eq_block' = fun x y rm -> if cfg_comp then true, rm else eq_block (x, af) (y, bf) rm in match a, b with - | Instr is1, Instr is2 -> GobList.equal (eq_instr rename_mapping) is1 is2 + | Instr is1, Instr is2 -> forward_list_equal eq_instr is1 is2 rename_mapping | Return (Some exp1, _l1), Return (Some exp2, _l2) -> eq_exp exp1 exp2 rename_mapping - | Return (None, _l1), Return (None, _l2) -> true - | Return _, Return _ -> false - | Goto (st1, _l1), Goto (st2, _l2) -> eq_stmt_with_location (!st1, af) (!st2, bf) - | Break _, Break _ -> if cfg_comp then failwith "CompareCFG: Invalid stmtkind in CFG" else true - | Continue _, Continue _ -> if cfg_comp then failwith "CompareCFG: Invalid stmtkind in CFG" else true - | If (exp1, then1, else1, _l1, _el1), If (exp2, then2, else2, _l2, _el2) -> eq_exp exp1 exp2 rename_mapping && eq_block' then1 then2 && eq_block' else1 else2 - | Switch (exp1, block1, stmts1, _l1, _el1), Switch (exp2, block2, stmts2, _l2, _el2) -> if cfg_comp then failwith "CompareCFG: Invalid stmtkind in CFG" else eq_exp exp1 exp2 rename_mapping && eq_block' block1 block2 && GobList.equal (fun a b -> eq_stmt (a,af) (b,bf) rename_mapping) stmts1 stmts2 - | Loop (block1, _l1, _el1, _con1, _br1), Loop (block2, _l2, _el2, _con2, _br2) -> eq_block' block1 block2 - | Block block1, Block block2 -> eq_block' block1 block2 - | _, _ -> false + | Return (None, _l1), Return (None, _l2) -> true, rename_mapping + | Return _, Return _ -> false, rename_mapping + | Goto (st1, _l1), Goto (st2, _l2) -> eq_stmt_with_location (!st1, af) (!st2, bf), rename_mapping + | Break _, Break _ -> if cfg_comp then failwith "CompareCFG: Invalid stmtkind in CFG" else true, rename_mapping + | Continue _, Continue _ -> if cfg_comp then failwith "CompareCFG: Invalid stmtkind in CFG" else true, rename_mapping + | If (exp1, then1, else1, _l1, _el1), If (exp2, then2, else2, _l2, _el2) -> eq_exp exp1 exp2 rename_mapping &&>> + eq_block' then1 then2 &&>> + eq_block' else1 else2 + | Switch (exp1, block1, stmts1, _l1, _el1), Switch (exp2, block2, stmts2, _l2, _el2) -> if cfg_comp then failwith "CompareCFG: Invalid stmtkind in CFG" else eq_exp exp1 exp2 rename_mapping &&>> eq_block' block1 block2 &&>> forward_list_equal (fun a b -> eq_stmt (a,af) (b,bf)) stmts1 stmts2 + | Loop (block1, _l1, _el1, _con1, _br1), Loop (block2, _l2, _el2, _con2, _br2) -> eq_block' block1 block2 rename_mapping + | Block block1, Block block2 -> eq_block' block1 block2 rename_mapping + | _, _ -> false, rename_mapping and eq_stmt ?(cfg_comp = false) ((a, af): stmt * fundec) ((b, bf): stmt * fundec) (rename_mapping: rename_mapping) = - GobList.equal eq_label a.labels b.labels && - eq_stmtkind ~cfg_comp (a.skind, af) (b.skind, bf) rename_mapping + (GobList.equal eq_label a.labels b.labels, rename_mapping) &&>> + eq_stmtkind ~cfg_comp (a.skind, af) (b.skind, bf) -and eq_block ((a, af): Cil.block * fundec) ((b, bf): Cil.block * fundec) (rename_mapping: rename_mapping) = - a.battrs = b.battrs && GobList.equal (fun x y -> eq_stmt (x, af) (y, bf) rename_mapping) a.bstmts b.bstmts +and eq_block ((a, af): Cil.block * fundec) ((b, bf): Cil.block * fundec) (rename_mapping: rename_mapping) : bool * rename_mapping = + (a.battrs = b.battrs, rename_mapping) &&>> forward_list_equal (fun x y -> eq_stmt (x, af) (y, bf)) a.bstmts b.bstmts let rec eq_init (a: init) (b: init) (rename_mapping: rename_mapping) = match a, b with | SingleInit e1, SingleInit e2 -> eq_exp e1 e2 rename_mapping - | CompoundInit (t1, l1), CompoundInit (t2, l2) -> eq_typ t1 t2 rename_mapping && GobList.equal (fun (o1, i1) (o2, i2) -> eq_offset o1 o2 rename_mapping && eq_init i1 i2 rename_mapping) l1 l2 - | _, _ -> false + | CompoundInit (t1, l1), CompoundInit (t2, l2) -> + eq_typ t1 t2 rename_mapping &&>> + forward_list_equal (fun (o1, i1) (o2, i2) x -> eq_offset o1 o2 x &&>> eq_init i1 i2) l1 l2 + | _, _ -> false, rename_mapping let eq_initinfo (a: initinfo) (b: initinfo) (rename_mapping: rename_mapping) = match a.init, b.init with | (Some init_a), (Some init_b) -> eq_init init_a init_b rename_mapping - | None, None -> true - | _, _ -> false + | None, None -> true, rename_mapping + | _, _ -> false, rename_mapping diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index 2a52e6eafe..4f2c37223f 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -3,30 +3,40 @@ open Queue open Cil include CompareAST -let eq_node (x, fun1) (y, fun2) = +(*Non propagating version of &&>>. Discords the new rename_mapping and alwas propagates the one in prev_result*) +let (&&<>) (prev_result: bool * rename_mapping) f : bool * rename_mapping = + let (prev_equal, prev_rm) = prev_result in + if prev_equal then + let (r, _) = f prev_rm in + (r, prev_rm) + else false, prev_rm + +let eq_node (x, fun1) (y, fun2) : bool = let empty_rename_mapping: rename_mapping = (StringMap.empty, StringMap.empty) in match x,y with - | Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) empty_rename_mapping - | Function f1, Function f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping - | FunctionEntry f1, FunctionEntry f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping + | Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) empty_rename_mapping |> fst + | Function f1, Function f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping |> fst + | FunctionEntry f1, FunctionEntry f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping |> fst | _ -> false (* TODO: compare ASMs properly instead of simply always assuming that they are not the same *) let eq_edge x y = let empty_rename_mapping: rename_mapping = (StringMap.empty, StringMap.empty) in - match x, y with - | Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 empty_rename_mapping && eq_exp rv1 rv2 empty_rename_mapping - | Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 empty_rename_mapping && GobList.equal (eq_exp2 empty_rename_mapping) ars1 ars2 + let (r, _) = match x, y with + | Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 empty_rename_mapping &&<> eq_exp rv1 rv2 + | Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 empty_rename_mapping &&<> forward_list_equal eq_exp ars1 ars2 | Proc (Some r1,f1,ars1), Proc (Some r2,f2,ars2) -> - eq_lval r1 r2 empty_rename_mapping && eq_exp f1 f2 empty_rename_mapping && GobList.equal (eq_exp2 empty_rename_mapping) ars1 ars2 + eq_lval r1 r2 empty_rename_mapping &&<> eq_exp f1 f2 &&<> forward_list_equal eq_exp ars1 ars2 | Entry f1, Entry f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping | Ret (None,fd1), Ret (None,fd2) -> eq_varinfo fd1.svar fd2.svar empty_rename_mapping - | Ret (Some r1,fd1), Ret (Some r2,fd2) -> eq_exp r1 r2 empty_rename_mapping && eq_varinfo fd1.svar fd2.svar empty_rename_mapping - | Test (p1,b1), Test (p2,b2) -> eq_exp p1 p2 empty_rename_mapping && b1 = b2 - | ASM _, ASM _ -> false - | Skip, Skip -> true + | Ret (Some r1,fd1), Ret (Some r2,fd2) -> eq_exp r1 r2 empty_rename_mapping &&<> eq_varinfo fd1.svar fd2.svar + | Test (p1,b1), Test (p2,b2) -> eq_exp p1 p2 empty_rename_mapping &&> (b1 = b2) + | ASM _, ASM _ -> false, empty_rename_mapping + | Skip, Skip -> true, empty_rename_mapping | VDecl v1, VDecl v2 -> eq_varinfo v1 v2 empty_rename_mapping - | _ -> false + | _ -> false, empty_rename_mapping + in + r (* The order of the edges in the list is relevant. Therefore compare them one to one without sorting first *) let eq_edge_list xs ys = GobList.equal eq_edge xs ys diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index f6abd0c9f3..a62459068d 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -60,7 +60,7 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo 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 unchangedHeader = eq_varinfo a.svar b.svar actHeaderRenameMapping &&>> forward_list_equal eq_varinfo a.sformals b.sformals in let identical, diffOpt = if should_reanalyze a then false, None @@ -69,12 +69,12 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo 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 + let sameDef = unchangedHeader &&> sizeEqual |> fst in if not sameDef then (false, None) else match cfgs with - | None -> eq_block (a.sbody, a) (b.sbody, b) rename_mapping, None + | None -> eq_block (a.sbody, a) (b.sbody, b) rename_mapping |> fst, 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 @@ -88,9 +88,9 @@ let eq_glob (a: global) (b: global) (cfgs : (cfg * (cfg * cfg)) option) (global_ | GFun (f,_), GFun (g,_) -> let identical, unchangedHeader, diffOpt = eqF f g cfgs global_rename_mapping in - identical, unchangedHeader, diffOpt - | GVar (x, init_x, _), GVar (y, init_y, _) -> eq_varinfo x y (StringMap.empty, StringMap.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, StringMap.empty), false, None + identical, unchangedHeader |> fst, diffOpt + | GVar (x, init_x, _), GVar (y, init_y, _) -> eq_varinfo x y (StringMap.empty, StringMap.empty) |> fst, 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, StringMap.empty) |> fst, 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) = From d900e7e48ed6c90d492212deb3bbc2ed802be120 Mon Sep 17 00:00:00 2001 From: Tim ORtel <100865202+TimOrtel@users.noreply.github.com> Date: Tue, 17 May 2022 19:40:35 +0200 Subject: [PATCH 22/90] eqF now returns the method rename dependencies --- src/incremental/compareCIL.ml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index a62459068d..8a16eb2d08 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -61,9 +61,9 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo let actHeaderRenameMapping = (headerRenameMapping, global_rename_mapping) in let unchangedHeader = eq_varinfo a.svar b.svar actHeaderRenameMapping &&>> forward_list_equal eq_varinfo a.sformals b.sformals in - let identical, diffOpt = + let identical, diffOpt, rename_mapping = if should_reanalyze a then - false, None + false, None, (StringMap.empty, StringMap.empty) else (* Here the local variables are checked to be equal *) let sizeEqual, local_rename = rename_mapping_aware_compare a.slocals b.slocals headerRenameMapping in @@ -71,24 +71,26 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo let sameDef = unchangedHeader &&> sizeEqual |> fst in if not sameDef then - (false, None) + (false, None, (StringMap.empty, StringMap.empty)) else match cfgs with - | None -> eq_block (a.sbody, a) (b.sbody, b) rename_mapping |> fst, None + | None -> + let (identical, new_rename_mapping) = eq_block (a.sbody, a) (b.sbody, b) rename_mapping in + identical, None, new_rename_mapping | 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 let matches, diffNodes1 = compareFun (module CfgOld) (module CfgNew) a b in - if diffNodes1 = [] then (true, None) - else (false, Some {unchangedNodes = matches; primObsoleteNodes = diffNodes1}) + if diffNodes1 = [] then (true, None, (StringMap.empty, StringMap.empty)) + else (false, Some {unchangedNodes = matches; primObsoleteNodes = diffNodes1}, (StringMap.empty, StringMap.empty)) in - identical, unchangedHeader, diffOpt + identical, unchangedHeader |> fst, diffOpt 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 |> fst, diffOpt + identical, unchangedHeader, diffOpt | GVar (x, init_x, _), GVar (y, init_y, _) -> eq_varinfo x y (StringMap.empty, StringMap.empty) |> fst, 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, StringMap.empty) |> fst, false, None | _ -> ignore @@ Pretty.printf "Not comparable: %a and %a\n" Cil.d_global a Cil.d_global b; false, false, None From 49caf8c1485fa643f1cb67d6e56531065bdb9106 Mon Sep 17 00:00:00 2001 From: Tim ORtel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 18 May 2022 16:09:17 +0200 Subject: [PATCH 23/90] Implemented rename detection of method. Not tested. --- src/framework/analyses.ml | 2 +- src/incremental/compareCIL.ml | 152 ++-------- src/incremental/compareGlobals.ml | 85 ++++++ src/incremental/detectRenamedFunctions.ml | 266 ++++++++++++++++-- src/incremental/updateCil.ml | 1 + src/solvers/td3.ml | 2 +- src/util/server.ml | 4 +- .../05-method-rename/05-deep_change.c | 18 ++ .../05-method-rename/05-deep_change.json | 3 + .../05-method-rename/05-deep_change.patch | 11 + .../05-method-rename/diffs/05-deep-change.c | 18 ++ 11 files changed, 415 insertions(+), 147 deletions(-) create mode 100644 src/incremental/compareGlobals.ml create mode 100644 tests/incremental/05-method-rename/05-deep_change.c create mode 100644 tests/incremental/05-method-rename/05-deep_change.json create mode 100644 tests/incremental/05-method-rename/05-deep_change.patch create mode 100644 tests/incremental/05-method-rename/diffs/05-deep-change.c diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index a605d8e8ed..1b4969dba7 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -407,7 +407,7 @@ type increment_data = { old_data: analyzed_data option; new_file: Cil.file; - changes: CompareCIL.change_info + changes: CompareGlobals.change_info } let empty_increment_data ?(server=false) file = { diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 8a16eb2d08..c038dd42f5 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -1,94 +1,15 @@ open Cil open MyCFG +open CompareGlobals +include DetectRenamedFunctions include CompareAST include CompareCFG -type nodes_diff = { - unchangedNodes: (node * node) list; - primObsoleteNodes: node list; (** primary obsolete nodes -> all obsolete nodes are reachable from these *) -} - -type unchanged_global = { - old: global; - current: global -} -(** For semantically unchanged globals, still keep old and current version of global for resetting current to old. *) - -type changed_global = { - old: global; - current: global; - unchangedHeader: bool; - diff: nodes_diff option -} - -type change_info = { - mutable changed: changed_global list; - mutable unchanged: unchanged_global list; - mutable removed: global list; - mutable added: global list -} - let empty_change_info () : change_info = {added = []; removed = []; changed = []; unchanged = []} -let should_reanalyze (fdec: Cil.fundec) = - List.mem fdec.svar.vname (GobConfig.get_string_list "incremental.force-reanalyze.funs") - -(* 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) (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.add 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 = if origLocal.vname <> nowLocal.vname then StringMap.add origLocal.vname nowLocal.vname rename_mapping else 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 &&>> forward_list_equal eq_varinfo a.sformals b.sformals in - let identical, diffOpt, rename_mapping = - if should_reanalyze a then - false, None, (StringMap.empty, StringMap.empty) - else - (* 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 |> fst in - if not sameDef then - (false, None, (StringMap.empty, StringMap.empty)) - else - match cfgs with - | None -> - let (identical, new_rename_mapping) = eq_block (a.sbody, a) (b.sbody, b) rename_mapping in - identical, None, new_rename_mapping - | 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 - let matches, diffNodes1 = compareFun (module CfgOld) (module CfgNew) a b in - if diffNodes1 = [] then (true, None, (StringMap.empty, StringMap.empty)) - else (false, Some {unchangedNodes = matches; primObsoleteNodes = diffNodes1}, (StringMap.empty, StringMap.empty)) - in - identical, unchangedHeader |> fst, diffOpt - -let eq_glob (a: global) (b: global) (cfgs : (cfg * (cfg * cfg)) option) (global_rename_mapping: method_rename_assumptions) = match a, b with +let eq_glob (a: global) (b: global) (cfgs : (cfg * (cfg * cfg)) option) = match a, b with | GFun (f,_), GFun (g,_) -> - let identical, unchangedHeader, diffOpt = eqF f g cfgs global_rename_mapping in + let identical, unchangedHeader, diffOpt, _ = CompareGlobals.eqF f g cfgs StringMap.empty in identical, unchangedHeader, diffOpt | GVar (x, init_x, _), GVar (y, init_y, _) -> eq_varinfo x y (StringMap.empty, StringMap.empty) |> fst, false, None (* ignore the init_info - a changed init of a global will lead to a different start state *) @@ -100,29 +21,6 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = 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 - 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)) |> - (fun list -> - List.fold_left (fun map mapping -> StringMap.add (fst mapping) (snd mapping) map) StringMap.empty list - ) - else StringMap.empty in - - if not (f.svar.vname = g.svar.vname) || not (StringMap.is_empty renamed_params) then - Some {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 @@ -137,15 +35,21 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = let changes = empty_change_info () in global_typ_acc := []; - let findChanges map global global_rename_mapping = + let findChanges map global = 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 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 + let isGFun = match global with + | GFun _-> false (* set to true later to disable finding changes for funs*) + | _ -> false + in + + if not isGFun then + 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 + if identical + then changes.unchanged <- {current = global; old = old_global} :: changes.unchanged + else changes.changed <- {current = global; old = old_global; unchangedHeader; diff} :: changes.changed with Not_found -> () (* Global was no variable or function, it does not belong into the map *) in let checkExists map global = @@ -157,19 +61,21 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = 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 list) global -> - match generate_global_rename_mapping oldMap global with - | Some rename_mapping -> current_global_rename_mapping @ [rename_mapping] - | None -> current_global_rename_mapping - ) [] |> - (fun mappings -> - List.fold_left (fun map mapping -> StringMap.add mapping.original_method_name mapping map) StringMap.empty mappings - ) in + let renameDetectionResults = detectRenamedFunctions oldAST newAST in + FundecMap.to_seq renameDetectionResults |> + Seq.iter + (fun (fundec, (functionGlobal, status)) -> + Printf.printf "Function satus of %s is=" fundec.svar.vname; + match status with + | SameName _ -> Printf.printf "Same Name\n"; + | Renamed rd -> Printf.printf "Renamed to %s" rd.nowName; + | ChangedOrNewOrDeleted -> Printf.printf "Changed or new or deleted." + ); (* 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 -> findChanges oldMap glob global_rename_mapping); + (fun glob -> findChanges oldMap glob); (* 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)); diff --git a/src/incremental/compareGlobals.ml b/src/incremental/compareGlobals.ml new file mode 100644 index 0000000000..e0cde8735a --- /dev/null +++ b/src/incremental/compareGlobals.ml @@ -0,0 +1,85 @@ +open Cil +open MyCFG +include CompareAST +include CompareCFG + +type nodes_diff = { + unchangedNodes: (node * node) list; + primObsoleteNodes: node list; (** primary obsolete nodes -> all obsolete nodes are reachable from these *) +} + +type unchanged_global = { + old: global; + current: global +} +(** For semantically unchanged globals, still keep old and current version of global for resetting current to old. *) + +type changed_global = { + old: global; + current: global; + unchangedHeader: bool; + diff: nodes_diff option +} + +type change_info = { + mutable changed: changed_global list; + mutable unchanged: unchanged_global list; + mutable removed: global list; + mutable added: global list +} + +let should_reanalyze (fdec: Cil.fundec) = + List.mem fdec.svar.vname (GobConfig.get_string_list "incremental.force-reanalyze.funs") + +(* 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) (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.add 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 = if origLocal.vname <> nowLocal.vname then StringMap.add origLocal.vname nowLocal.vname rename_mapping else 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 &&>> forward_list_equal eq_varinfo a.sformals b.sformals in + let identical, diffOpt, (_, renamed_method_dependencies) = + if should_reanalyze a then + false, None, (StringMap.empty, StringMap.empty) + else + (* 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 |> fst in + if not sameDef then + (false, None, (StringMap.empty, StringMap.empty)) + else + match cfgs with + | None -> + let (identical, new_rename_mapping) = eq_block (a.sbody, a) (b.sbody, b) rename_mapping in + identical, None, new_rename_mapping + | 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 + let matches, diffNodes1 = compareFun (module CfgOld) (module CfgNew) a b in + if diffNodes1 = [] then (true, None, (StringMap.empty, StringMap.empty)) + else (false, Some {unchangedNodes = matches; primObsoleteNodes = diffNodes1}, (StringMap.empty, StringMap.empty)) + in + identical, unchangedHeader |> fst, diffOpt, renamed_method_dependencies diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml index c4fe19674e..9ff36b69cd 100644 --- a/src/incremental/detectRenamedFunctions.ml +++ b/src/incremental/detectRenamedFunctions.ml @@ -3,41 +3,267 @@ open MyCFG include CompareAST include CompareCFG -(*Maps the function name as keys*) -module FundecMap = Map.Make(String);; +module StringSet = Set.Make(String) -type functionStatus = Identical | Renamed | Changed | New | Deleted -type results = (fundec, functionStatus) Hashtbl.t +type f = fundec * location -(*A dependency mapps the function it depends on to the name the function has to be changed to*) -type dependencies = (fundec, string) Hashtbl.t +type dependencyPointer = {oldName: string; dependencyCount: int} -type earlyFunctionStatus = Unchanged of dependencies | Unknown +module OldFunNameWithDependencyCount = struct + type t = dependencyPointer + let compare x y = Int.compare x.dependencyCount y.dependencyCount +end + +module FundecForMap = struct + type t = Cil.fundec + + let compare x y = Int.compare x.svar.vid y.svar.vid +end + +module DependencyHeap = BatHeap.Make(OldFunNameWithDependencyCount) + +module FundecMap = Map.Make(FundecForMap) + +(*A dependency maps the function it depends on to the name the function has to be changed to*) +type dependencies = string StringMap.t + +(*The dependents map the functions that depend with the name they need it to be changed to*) +type dependents = string StringMap.t + +(*hadWrongAssumption: set to true, if one of the depencies this node had, was wrong. Thus this node is changed.*) +type nodeData = {nowName: string; dependencies: dependencies; dependents: dependents; hadWrongAssumption: bool} + +type renameData = {nowName: string; dependencies: dependencies} + +(*A direct match means that the function name stayed the same and the old and new function match (with contraints defined by dependencies). *) +type earlyFunctionStatus = DirectMatch of dependencies | Changed | Unknown + +(*Renamed: newName * dependencies*) +type functionStatus = SameName of dependencies | Renamed of renameData | ChangedOrNewOrDeleted +type results = functionStatus StringMap.t -let getFunctionMap (ast: file) : fundec FundecMap.t = +type output = global * functionStatus + + +let getFunctionMap (ast: file) : f StringMap.t = Cil.foldGlobals ast (fun map global -> match global with - | GFun (fundec, _) -> FundecMap.add fundec.svar.vname fundec map + | GFun (fundec, location) -> StringMap.add fundec.svar.vname (fundec, location) map | _ -> map - ) FundecMap.empty + ) StringMap.empty + +let getDependencies fromEq = StringMap.map (fun assumption -> assumption.new_method_name) fromEq -let seperateUnchangedFunctions (oldFunctionMap: fundec FundecMap.t) (newFunctionMap: fundec FundecMap.t) : earlyFunctionStatus FundecMap.t = - FundecMap.map (fun f -> - let matchingNewFundec = FundecMap.find_opt f.svar.vname newFunctionMap in +(*Split the functions up in those which have not been renamed, and such which have been renamed, are new or have been deleted*) +let seperateUnchangedFunctions (oldFunctionMap: f StringMap.t) (nowFunctionMap: f StringMap.t) : earlyFunctionStatus StringMap.t = + StringMap.map (fun (f, _) -> + let matchingNewFundec = StringMap.find_opt f.svar.vname nowFunctionMap in match matchingNewFundec with - | Some newFun -> + | Some (newFun, _) -> (*Compare if they are similar*) - let result = CompareCIL.eqF f newFun None (Hashtbl.create 0) in - Unknown + let doMatch, _, _, dependencies = CompareGlobals.eqF f newFun None StringMap.empty in + if doMatch then DirectMatch(getDependencies dependencies) + else Unknown | None -> Unknown ) oldFunctionMap -let detectRenamedFunctions (oldAST: file) (newAST: file) : results = begin +(* +Tries to find a partner for each method that is not a direct match. +Returns the found partner for each unknown function with the rename dependencies or ChangedOrNewOrDeleted if no partner was found. +Use sets instead of lists, because member lookups are faster in sets.*) +let categorizeUnknownFunctions + (unknownFunctions: StringSet.t) + (directMatchFunctions: StringSet.t) + (oldFunctionMap: f StringMap.t) + (nowFunctionMap: f StringMap.t) : functionStatus StringMap.t = + let nowFunctionMapWithoutDirectMatchFunctions = StringMap.filter (fun key _ -> not (StringSet.mem key directMatchFunctions)) nowFunctionMap in + + StringSet.fold (fun functionWithUnknownStatusName map -> + (*The unknown functions directly come from the oldFunctionMap, so there has to be an entry.*) + let (functionWithUnknownStatusFundec, _) = StringMap.find functionWithUnknownStatusName oldFunctionMap in + + (*Find the first match in all new unknown functions: O(all_functions - direct_functions)*) + let foundFunctionMatch = + StringMap.to_seq nowFunctionMapWithoutDirectMatchFunctions |> + Seq.map (fun (name, (f, _)) -> name, f) |> + Seq.find_map (fun (nowFunName, nowFunFundec) -> + let doMatch, _, _, dependencies = CompareGlobals.eqF functionWithUnknownStatusFundec nowFunFundec None StringMap.empty in + if doMatch then Option.some ( + {nowName = nowFunName; dependencies = getDependencies dependencies} + ) else None + ) in + + match foundFunctionMatch with + | Some renameData -> StringMap.add functionWithUnknownStatusName (Renamed(renameData)) map + | None -> StringMap.add functionWithUnknownStatusName ChangedOrNewOrDeleted map + ) unknownFunctions StringMap.empty + + +(*Marks the changed node as changed in the results and also marks all nodes that depend on that node as changed. *) +let rec propagateChangedNode (changedNodeOldName: string) + (nodeMap: nodeData StringMap.t) + (dependencyHeap: DependencyHeap.t) + (currentResults: results) : (nodeData StringMap.t) * (DependencyHeap.t) * (results) = + let resultsWithChangedNode = StringMap.add changedNodeOldName ChangedOrNewOrDeleted currentResults in + let changedNodeData = StringMap.find changedNodeOldName nodeMap in + (*BatHeap does not support removing an element directly. Maybe we should use a different implementation.*) + let dependencyHeapWithoutChangedNode: DependencyHeap.t = + dependencyHeap |> + DependencyHeap.to_list |> + List.filter (fun pointer -> pointer.oldName <> changedNodeOldName) |> + DependencyHeap.of_list in + + changedNodeData.dependents |> + StringMap.to_seq |> + Seq.fold_left (fun (nodeMap, dependencyHeap, currentResults) (dependentName, _) -> + propagateChangedNode dependentName nodeMap dependencyHeap currentResults + ) (nodeMap, dependencyHeapWithoutChangedNode, resultsWithChangedNode) + +(* Takes the node with the currently least dependencies and tries to reduce the graph from that node. + Cyclic dependency graphs are currently not supported. If a cyclic dependency is found, all remaining nodes are marked as changed. + + Function is applied recursivly until no nodes remain in the graph. +*) +let rec reduceNodeGraph (nodeMap: nodeData StringMap.t) (dependencyHeap: DependencyHeap.t) (currentResults: results) : results = + if DependencyHeap.size dependencyHeap = 0 then currentResults + else + let topDependencyPointer = DependencyHeap.find_min dependencyHeap in + let currentNode = StringMap.find topDependencyPointer.oldName nodeMap in + + let newDependencyHeap = DependencyHeap.del_min dependencyHeap in + + if topDependencyPointer.dependencyCount = 0 then + (*Remove this node from the dependecies of the nodes that depend on it. + The nodes that depend on the wrong name are set to be changed.*) + let newNodeMap = currentNode.dependents |> + StringMap.to_seq |> + Seq.fold_left (fun nodeMap (dependingFun, dependingOnName) -> + let dependeeNodeData: nodeData = StringMap.find dependingFun nodeMap in + + (*Remove the dependency of current node from the dependencies of the dependee*) + let newDependencies = dependeeNodeData.dependencies |> + StringMap.filter (fun dependingName _ -> dependingName <> topDependencyPointer.oldName) + in + + let hadWrongAssumption = if currentNode.nowName <> dependingOnName then true + else dependeeNodeData.hadWrongAssumption + in + + let newNodeData = { + nowName = dependeeNodeData.nowName; + dependencies = newDependencies; + dependents = dependeeNodeData.dependents; + hadWrongAssumption = hadWrongAssumption + } in + + (*Replace node data in map*) + let newNodeMap = StringMap.add dependingFun newNodeData nodeMap in + + newNodeMap + + ) nodeMap in + + let status = if currentNode.hadWrongAssumption then ChangedOrNewOrDeleted else Renamed({nowName=currentNode.nowName; dependencies=currentNode.dependencies}) in + + let newResults = StringMap.add topDependencyPointer.oldName status currentResults in + + reduceNodeGraph newNodeMap newDependencyHeap newResults + else + (*Cyclic dependency found. *) + (*Mark all remaining nodes with dependencies as changed.*) + DependencyHeap.to_list dependencyHeap |> + List.fold_left (fun results dependencyPointer -> + StringMap.add dependencyPointer.oldName ChangedOrNewOrDeleted results + ) currentResults + +let detectRenamedFunctions (oldAST: file) (newAST: file) : output FundecMap.t = begin let oldFunctionMap = getFunctionMap oldAST in - let newFunctionMap = getFunctionMap newAST in + let nowFunctionMap = getFunctionMap newAST in (*1. detect function which names have not changed*) - let unchangedNameFunctions = FundecMap.map (fun _ fundec -> ) oldFunctionMap in + let statusForFunction = seperateUnchangedFunctions oldFunctionMap nowFunctionMap in + + let directMatchFunctions, knownChangedFunctions, unknownFunctions, initialCategorization = StringMap.fold ( + fun funName earlyStatus (directMatchFunctions, knownChangedFunctions, unknownFunctions, initialCategorization) -> match earlyStatus with + | DirectMatch d -> ( + StringSet.add funName directMatchFunctions, + knownChangedFunctions, + unknownFunctions, + StringMap.add funName (SameName(d)) initialCategorization + ) + | Changed -> ( + directMatchFunctions, + StringSet.add funName knownChangedFunctions, + unknownFunctions, + StringMap.add funName ChangedOrNewOrDeleted initialCategorization + ) + | Unknown -> ( + directMatchFunctions, + knownChangedFunctions, + StringSet.add funName unknownFunctions, + initialCategorization + ) + ) statusForFunction (StringSet.empty, StringSet.empty, StringSet.empty, StringMap.empty) in + + (*2. get dependencies of those functions we did match in 1. + These function statuses are just early guesses. They still need to be checked and adapted in the graph analysis.*) + let categorizationResults = categorizeUnknownFunctions unknownFunctions directMatchFunctions oldFunctionMap nowFunctionMap in + + (*3. build dependency graph*) + let categorizationMap = StringMap.union (fun _ _ _ -> None) initialCategorization categorizationResults in + + (*dependentsMap>*) + (*Generate the dependents map now, so it does not have to be done when generating the node map*) + let dependentsMap: string StringMap.t StringMap.t = StringMap.fold (fun oldFunName functionStatus dependentsMap -> + (*Go through all dependencies and add itself to the list of dependents*) + let addDependents dependencies = StringMap.fold (fun dependingOn hasToBeNamed dependentsMap -> + let currentDependents = StringMap.find_opt dependingOn dependentsMap |> + Option.value ~default:StringMap.empty in + + let newDependents = StringMap.add oldFunName hasToBeNamed currentDependents in + + StringMap.add dependingOn newDependents dependentsMap + ) dependencies dependentsMap + in + + match functionStatus with + | SameName dependencies -> addDependents dependencies + | Renamed renameData -> addDependents renameData.dependencies + | ChangedOrNewOrDeleted -> dependentsMap + ) categorizationMap StringMap.empty in + + (*The nodes are represented in the node map. The node data contains the nowName, + and the nodes it depends on as well as the nodes that depend on that node. + The dependencyHeap points to the function name with the currently least dependencies.*) + let (nodeMap: nodeData StringMap.t), (dependencyHeap: DependencyHeap.t) = + StringMap.fold (fun oldFunName functionStatus (nodeMap, dependencyHeap) -> + let dependents = StringMap.find_opt oldFunName dependentsMap |> + Option.value ~default:StringMap.empty in + + let getNodeEntry dependencies = {nowName=oldFunName; dependencies = dependencies; dependents = dependents; hadWrongAssumption = false} in + let getDependencyPointer dependencies = {oldName=oldFunName; dependencyCount=StringMap.cardinal dependencies} in + + match functionStatus with + | SameName dependencies -> + ( + StringMap.add oldFunName (getNodeEntry dependencies) nodeMap, + DependencyHeap.add (getDependencyPointer dependencies) dependencyHeap + ) + | Renamed renameData -> + ( + StringMap.add oldFunName (getNodeEntry renameData.dependencies) nodeMap, + DependencyHeap.add (getDependencyPointer renameData.dependencies) dependencyHeap + ) + | ChangedOrNewOrDeleted -> (nodeMap, dependencyHeap) + ) categorizationMap (StringMap.empty, DependencyHeap.empty) in + + + let result = reduceNodeGraph nodeMap dependencyHeap StringMap.empty in + + let x = StringMap.to_seq result |> + Seq.map (fun (oldName, status) -> + let (f, l) = StringMap.find oldName oldFunctionMap in + f, (GFun(f, l), status)) in - Hashtbl.create 0 + FundecMap.add_seq x FundecMap.empty end diff --git a/src/incremental/updateCil.ml b/src/incremental/updateCil.ml index 2cf28ba329..90bca36304 100644 --- a/src/incremental/updateCil.ml +++ b/src/incremental/updateCil.ml @@ -2,6 +2,7 @@ open Cil open CompareCIL open MaxIdUtil open MyCFG +open CompareGlobals module NodeMap = Hashtbl.Make(Node) diff --git a/src/solvers/td3.ml b/src/solvers/td3.ml index 254d2fcd85..a0e95e1d65 100644 --- a/src/solvers/td3.ml +++ b/src/solvers/td3.ml @@ -13,7 +13,7 @@ open Prelude open Analyses open Constraints open Messages -open CompareCIL +open CompareGlobals open Cil module WP = diff --git a/src/util/server.ml b/src/util/server.ml index c9cba4c664..380acf6ebf 100644 --- a/src/util/server.ml +++ b/src/util/server.ml @@ -120,8 +120,8 @@ let reparse (s: t) = (* Only called when the file has not been reparsed, so we can skip the expensive CFG comparison. *) let virtual_changes file = - let eq (glob: Cil.global) _ _ _ = match glob with - | GFun (fdec, _) -> not (CompareCIL.should_reanalyze fdec), false, None + let eq (glob: Cil.global) _ _ = match glob with + | GFun (fdec, _) -> not (CompareGlobals.should_reanalyze fdec), false, None | _ -> true, false, None in CompareCIL.compareCilFiles ~eq file file diff --git a/tests/incremental/05-method-rename/05-deep_change.c b/tests/incremental/05-method-rename/05-deep_change.c new file mode 100644 index 0000000000..80037f934d --- /dev/null +++ b/tests/incremental/05-method-rename/05-deep_change.c @@ -0,0 +1,18 @@ +#include + +void zap() { + printf("zap"); +} + +void bar() { + zap(); +} + +void foo() { + bar(); +} + +int main() { + foo(); + return 0; +} diff --git a/tests/incremental/05-method-rename/05-deep_change.json b/tests/incremental/05-method-rename/05-deep_change.json new file mode 100644 index 0000000000..0db3279e44 --- /dev/null +++ b/tests/incremental/05-method-rename/05-deep_change.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/tests/incremental/05-method-rename/05-deep_change.patch b/tests/incremental/05-method-rename/05-deep_change.patch new file mode 100644 index 0000000000..0374da2fb6 --- /dev/null +++ b/tests/incremental/05-method-rename/05-deep_change.patch @@ -0,0 +1,11 @@ +--- tests/incremental/05-method-rename/05-deep_change.c ++++ tests/incremental/05-method-rename/05-deep_change.c +@@ -1,7 +1,7 @@ + #include + + void zap() { +- printf("zap"); ++ printf("drap"); + } + + void bar() { diff --git a/tests/incremental/05-method-rename/diffs/05-deep-change.c b/tests/incremental/05-method-rename/diffs/05-deep-change.c new file mode 100644 index 0000000000..57ad90457b --- /dev/null +++ b/tests/incremental/05-method-rename/diffs/05-deep-change.c @@ -0,0 +1,18 @@ +#include + +void zap() { + printf("drap"); +} + +void bar() { + zap(); +} + +void foo() { + bar(); +} + +int main() { + foo(); + return 0; +} From abef1169bb6315a4dce4f262436c4bb7c21df45c Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 25 May 2022 14:01:22 +0200 Subject: [PATCH 24/90] Removed obsolete method and smaller changes --- src/incremental/detectRenamedFunctions.ml | 31 +++++------------------ 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml index 9ff36b69cd..6d7ba79126 100644 --- a/src/incremental/detectRenamedFunctions.ml +++ b/src/incremental/detectRenamedFunctions.ml @@ -98,27 +98,6 @@ let categorizeUnknownFunctions | None -> StringMap.add functionWithUnknownStatusName ChangedOrNewOrDeleted map ) unknownFunctions StringMap.empty - -(*Marks the changed node as changed in the results and also marks all nodes that depend on that node as changed. *) -let rec propagateChangedNode (changedNodeOldName: string) - (nodeMap: nodeData StringMap.t) - (dependencyHeap: DependencyHeap.t) - (currentResults: results) : (nodeData StringMap.t) * (DependencyHeap.t) * (results) = - let resultsWithChangedNode = StringMap.add changedNodeOldName ChangedOrNewOrDeleted currentResults in - let changedNodeData = StringMap.find changedNodeOldName nodeMap in - (*BatHeap does not support removing an element directly. Maybe we should use a different implementation.*) - let dependencyHeapWithoutChangedNode: DependencyHeap.t = - dependencyHeap |> - DependencyHeap.to_list |> - List.filter (fun pointer -> pointer.oldName <> changedNodeOldName) |> - DependencyHeap.of_list in - - changedNodeData.dependents |> - StringMap.to_seq |> - Seq.fold_left (fun (nodeMap, dependencyHeap, currentResults) (dependentName, _) -> - propagateChangedNode dependentName nodeMap dependencyHeap currentResults - ) (nodeMap, dependencyHeapWithoutChangedNode, resultsWithChangedNode) - (* Takes the node with the currently least dependencies and tries to reduce the graph from that node. Cyclic dependency graphs are currently not supported. If a cyclic dependency is found, all remaining nodes are marked as changed. @@ -135,9 +114,9 @@ let rec reduceNodeGraph (nodeMap: nodeData StringMap.t) (dependencyHeap: Depende if topDependencyPointer.dependencyCount = 0 then (*Remove this node from the dependecies of the nodes that depend on it. The nodes that depend on the wrong name are set to be changed.*) - let newNodeMap = currentNode.dependents |> + let (newNodeMap, updatedDependencyHeap) = currentNode.dependents |> StringMap.to_seq |> - Seq.fold_left (fun nodeMap (dependingFun, dependingOnName) -> + Seq.fold_left (fun (nodeMap, dependencyHeap) (dependingFun, dependingOnName) -> let dependeeNodeData: nodeData = StringMap.find dependingFun nodeMap in (*Remove the dependency of current node from the dependencies of the dependee*) @@ -145,6 +124,8 @@ let rec reduceNodeGraph (nodeMap: nodeData StringMap.t) (dependencyHeap: Depende StringMap.filter (fun dependingName _ -> dependingName <> topDependencyPointer.oldName) in + (*TODO: Update dependencyheap by decreasing the dependency count by 1*) + let hadWrongAssumption = if currentNode.nowName <> dependingOnName then true else dependeeNodeData.hadWrongAssumption in @@ -159,9 +140,9 @@ let rec reduceNodeGraph (nodeMap: nodeData StringMap.t) (dependencyHeap: Depende (*Replace node data in map*) let newNodeMap = StringMap.add dependingFun newNodeData nodeMap in - newNodeMap + newNodeMap, dependencyHeap - ) nodeMap in + ) (nodeMap, newDependencyHeap) in let status = if currentNode.hadWrongAssumption then ChangedOrNewOrDeleted else Renamed({nowName=currentNode.nowName; dependencies=currentNode.dependencies}) in From b9ad6dbbc29b6752b5d14bd02bfbc8a0f6c4f165 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 25 May 2022 18:30:13 +0200 Subject: [PATCH 25/90] Implemented simple rename detection for methods --- src/incremental/compareCIL.ml | 46 ++- src/incremental/detectRenamedFunctions.ml | 361 ++++++++---------- src/incremental/updateCil.ml | 1 - .../05-method-rename/00-simple_rename.json | 3 + .../05-method-rename/00-simple_rename.patch | 4 +- .../05-method-rename/01-dependent_rename.json | 3 + .../01-dependent_rename.patch | 4 +- 7 files changed, 189 insertions(+), 233 deletions(-) create mode 100644 tests/incremental/05-method-rename/00-simple_rename.json create mode 100644 tests/incremental/05-method-rename/01-dependent_rename.json diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index c038dd42f5..13e1f42069 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -40,7 +40,7 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = let isGFun = match global with | GFun _-> false (* set to true later to disable finding changes for funs*) | _ -> false - in + in if not isGFun then let ident = identifier_of_global global in @@ -52,34 +52,46 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = else changes.changed <- {current = global; old = old_global; unchangedHeader; diff} :: changes.changed with Not_found -> () (* Global was no variable or function, it does not belong into the map *) in - let checkExists map global = - match identifier_of_global global with - | name -> GlobalMap.mem name map - | exception Not_found -> true (* return true, so isn't considered a change *) - in + (* 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 renameDetectionResults = detectRenamedFunctions oldAST newAST in FundecMap.to_seq renameDetectionResults |> - Seq.iter + Seq.iter (fun (fundec, (functionGlobal, status)) -> - Printf.printf "Function satus of %s is=" fundec.svar.vname; - match status with - | SameName _ -> Printf.printf "Same Name\n"; - | Renamed rd -> Printf.printf "Renamed to %s" rd.nowName; - | ChangedOrNewOrDeleted -> Printf.printf "Changed or new or deleted." - ); + Printf.printf "Function status of %s is=" fundec.svar.vname; + match status with + | Unchanged _ -> Printf.printf "Same Name\n"; + | Added -> Printf.printf "Added\n"; + | Removed -> Printf.printf "Removed\n"; + | Changed _ -> Printf.printf "Changed\n"; + | UnchangedButRenamed toFrom -> + match toFrom with + | GFun (f, _) -> Printf.printf "Renamed to %s\n" f.svar.vname; + | _ -> Printf.printf "TODO"; + ); (* 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 -> findChanges oldMap glob); - (* 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)); - Cil.iterGlobals oldAST (fun glob -> if not (checkExists newMap glob) then changes.removed <- (glob::changes.removed)); + let unchanged, changed, added, removed = FundecMap.fold (fun _ (global, status) (u, c, a, r) -> + match status with + | Unchanged now -> (u @ [{old=global; current=now}], c, a, r) + | UnchangedButRenamed now -> (u @ [{old=global; current=now}], c, a, r) + | Added -> (u, c, a @ [global], r) + | Removed -> (u, c, a, r @ [global]) + | Changed (now, unchangedHeader) -> (u, c @ [{old=global; current=now; unchangedHeader=unchangedHeader; diff=None}], a, r) + ) renameDetectionResults (changes.unchanged, changes.changed, changes.added, changes.removed) + in + + changes.added <- added; + changes.removed <- removed; + changes.changed <- changed; + changes.unchanged <- unchanged; + changes (** Given an (optional) equality function between [Cil.global]s, an old and a new [Cil.file], this function computes a [change_info], diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml index 6d7ba79126..99bc4a6f68 100644 --- a/src/incremental/detectRenamedFunctions.ml +++ b/src/incremental/detectRenamedFunctions.ml @@ -7,43 +7,36 @@ module StringSet = Set.Make(String) type f = fundec * location -type dependencyPointer = {oldName: string; dependencyCount: int} - -module OldFunNameWithDependencyCount = struct - type t = dependencyPointer - let compare x y = Int.compare x.dependencyCount y.dependencyCount -end - module FundecForMap = struct type t = Cil.fundec - let compare x y = Int.compare x.svar.vid y.svar.vid + let compare x y = String.compare x.svar.vname y.svar.vname end -module DependencyHeap = BatHeap.Make(OldFunNameWithDependencyCount) - module FundecMap = Map.Make(FundecForMap) (*A dependency maps the function it depends on to the name the function has to be changed to*) type dependencies = string StringMap.t -(*The dependents map the functions that depend with the name they need it to be changed to*) -type dependents = string StringMap.t - -(*hadWrongAssumption: set to true, if one of the depencies this node had, was wrong. Thus this node is changed.*) -type nodeData = {nowName: string; dependencies: dependencies; dependents: dependents; hadWrongAssumption: bool} - -type renameData = {nowName: string; dependencies: dependencies} +(*Renamed: newName * dependencies; Modified=now*unchangedHeader*) +type functionStatus = SameName of fundec | Renamed of fundec | Created | Deleted | Modified of fundec * bool +type outputFunctionStatus = Unchanged of global | UnchangedButRenamed of global | Added | Removed | Changed of global * bool -(*A direct match means that the function name stayed the same and the old and new function match (with contraints defined by dependencies). *) -type earlyFunctionStatus = DirectMatch of dependencies | Changed | Unknown +type output = global * outputFunctionStatus -(*Renamed: newName * dependencies*) -type functionStatus = SameName of dependencies | Renamed of renameData | ChangedOrNewOrDeleted -type results = functionStatus StringMap.t - -type output = global * functionStatus +let pretty (f: functionStatus) = + match f with + | SameName _ -> "SameName" + | Renamed x -> "Renamed to " ^ x.svar.vname + | Created -> "Added" + | Deleted -> "Removed" + | Modified _ -> "Changed" +let printFundecMap elemToString map = begin + Seq.iter (fun (f, e) -> + ignore@@Pretty.printf "%s->%s;" f.svar.vname (elemToString e); + ) (FundecMap.to_seq map) +end let getFunctionMap (ast: file) : f StringMap.t = Cil.foldGlobals ast (fun map global -> @@ -54,197 +47,143 @@ let getFunctionMap (ast: file) : f StringMap.t = let getDependencies fromEq = StringMap.map (fun assumption -> assumption.new_method_name) fromEq -(*Split the functions up in those which have not been renamed, and such which have been renamed, are new or have been deleted*) -let seperateUnchangedFunctions (oldFunctionMap: f StringMap.t) (nowFunctionMap: f StringMap.t) : earlyFunctionStatus StringMap.t = - StringMap.map (fun (f, _) -> - let matchingNewFundec = StringMap.find_opt f.svar.vname nowFunctionMap in - match matchingNewFundec with - | Some (newFun, _) -> - (*Compare if they are similar*) - let doMatch, _, _, dependencies = CompareGlobals.eqF f newFun None StringMap.empty in - if doMatch then DirectMatch(getDependencies dependencies) - else Unknown - | None -> Unknown - ) oldFunctionMap - -(* -Tries to find a partner for each method that is not a direct match. -Returns the found partner for each unknown function with the rename dependencies or ChangedOrNewOrDeleted if no partner was found. -Use sets instead of lists, because member lookups are faster in sets.*) -let categorizeUnknownFunctions - (unknownFunctions: StringSet.t) - (directMatchFunctions: StringSet.t) - (oldFunctionMap: f StringMap.t) - (nowFunctionMap: f StringMap.t) : functionStatus StringMap.t = - let nowFunctionMapWithoutDirectMatchFunctions = StringMap.filter (fun key _ -> not (StringSet.mem key directMatchFunctions)) nowFunctionMap in - - StringSet.fold (fun functionWithUnknownStatusName map -> - (*The unknown functions directly come from the oldFunctionMap, so there has to be an entry.*) - let (functionWithUnknownStatusFundec, _) = StringMap.find functionWithUnknownStatusName oldFunctionMap in - - (*Find the first match in all new unknown functions: O(all_functions - direct_functions)*) - let foundFunctionMatch = - StringMap.to_seq nowFunctionMapWithoutDirectMatchFunctions |> - Seq.map (fun (name, (f, _)) -> name, f) |> - Seq.find_map (fun (nowFunName, nowFunFundec) -> - let doMatch, _, _, dependencies = CompareGlobals.eqF functionWithUnknownStatusFundec nowFunFundec None StringMap.empty in - if doMatch then Option.some ( - {nowName = nowFunName; dependencies = getDependencies dependencies} - ) else None - ) in - - match foundFunctionMatch with - | Some renameData -> StringMap.add functionWithUnknownStatusName (Renamed(renameData)) map - | None -> StringMap.add functionWithUnknownStatusName ChangedOrNewOrDeleted map - ) unknownFunctions StringMap.empty - -(* Takes the node with the currently least dependencies and tries to reduce the graph from that node. - Cyclic dependency graphs are currently not supported. If a cyclic dependency is found, all remaining nodes are marked as changed. - - Function is applied recursivly until no nodes remain in the graph. -*) -let rec reduceNodeGraph (nodeMap: nodeData StringMap.t) (dependencyHeap: DependencyHeap.t) (currentResults: results) : results = - if DependencyHeap.size dependencyHeap = 0 then currentResults - else - let topDependencyPointer = DependencyHeap.find_min dependencyHeap in - let currentNode = StringMap.find topDependencyPointer.oldName nodeMap in - - let newDependencyHeap = DependencyHeap.del_min dependencyHeap in - - if topDependencyPointer.dependencyCount = 0 then - (*Remove this node from the dependecies of the nodes that depend on it. - The nodes that depend on the wrong name are set to be changed.*) - let (newNodeMap, updatedDependencyHeap) = currentNode.dependents |> - StringMap.to_seq |> - Seq.fold_left (fun (nodeMap, dependencyHeap) (dependingFun, dependingOnName) -> - let dependeeNodeData: nodeData = StringMap.find dependingFun nodeMap in - - (*Remove the dependency of current node from the dependencies of the dependee*) - let newDependencies = dependeeNodeData.dependencies |> - StringMap.filter (fun dependingName _ -> dependingName <> topDependencyPointer.oldName) - in - - (*TODO: Update dependencyheap by decreasing the dependency count by 1*) - - let hadWrongAssumption = if currentNode.nowName <> dependingOnName then true - else dependeeNodeData.hadWrongAssumption - in - - let newNodeData = { - nowName = dependeeNodeData.nowName; - dependencies = newDependencies; - dependents = dependeeNodeData.dependents; - hadWrongAssumption = hadWrongAssumption - } in - - (*Replace node data in map*) - let newNodeMap = StringMap.add dependingFun newNodeData nodeMap in - - newNodeMap, dependencyHeap - - ) (nodeMap, newDependencyHeap) in - - let status = if currentNode.hadWrongAssumption then ChangedOrNewOrDeleted else Renamed({nowName=currentNode.nowName; dependencies=currentNode.dependencies}) in - - let newResults = StringMap.add topDependencyPointer.oldName status currentResults in - - reduceNodeGraph newNodeMap newDependencyHeap newResults +type carryType = { + statusForOldFunction: functionStatus FundecMap.t; + statusForNowFunction: functionStatus FundecMap.t; + methodMapping: fundec FundecMap.t; + reverseMethodMapping: fundec FundecMap.t} + +let registerStatusForOldF f status data = + {statusForOldFunction = FundecMap.add f status data.statusForOldFunction; + statusForNowFunction=data.statusForNowFunction; + methodMapping=data.methodMapping; + reverseMethodMapping=data.reverseMethodMapping} + +let registerStatusForNowF f status data = + {statusForOldFunction = data.statusForOldFunction; + statusForNowFunction=FundecMap.add f status data.statusForNowFunction; + methodMapping=data.methodMapping; + reverseMethodMapping=data.reverseMethodMapping} + +let registerBiStatus (oldF: fundec) (nowF: fundec) (status: functionStatus) data = + {statusForOldFunction=FundecMap.add oldF status data.statusForOldFunction; + statusForNowFunction=FundecMap.add nowF status data.statusForNowFunction; + methodMapping=data.methodMapping; + reverseMethodMapping=data.reverseMethodMapping} + +let registerMapping oldF nowF data = + {statusForOldFunction=data.statusForOldFunction; + statusForNowFunction=data.statusForNowFunction; + methodMapping=FundecMap.add oldF nowF data.methodMapping; + reverseMethodMapping=FundecMap.add nowF oldF data.reverseMethodMapping} + +(*returns true iff for all dependencies it is true, that the dependency has a corresponding function with the new name and matches the without having dependencies itself and the new name is not already present on the old AST. *) +let doAllDependenciesMatch (dependencies: dependencies) (oldFunctionMap: f StringMap.t) (newFunctionMap: f StringMap.t) (data: carryType) : bool * carryType = + StringMap.fold (fun oldName newName (allEqual, data) -> + (*Early cutoff if a previous dependency returned false or the newName is already present in the old map*) + if allEqual && not (StringMap.mem newName oldFunctionMap) then + + let (oldFundec, _) = StringMap.find oldName oldFunctionMap in + + let knownMapping = FundecMap.find_opt oldFundec data.methodMapping in + + (*To avoid inconsitencies, if a function has already been mapped to a function, that mapping is reused again.*) + match knownMapping with + | Some(knownFundec) -> + (*This function has already been mapped*) + knownFundec.svar.vname = newName, data + | None -> + let newFundecOption = StringMap.find_opt newName newFunctionMap in + + match newFundecOption with + | Some((newFundec, _)) -> + let doMatch, _, _, dependencies = CompareGlobals.eqF oldFundec newFundec None StringMap.empty in + + if doMatch && StringMap.is_empty dependencies then + true, registerMapping oldFundec newFundec data + else false, data + + | None -> false, data + else false, data + ) dependencies (true, data) + +(*Check if f has already been assigned a status. If yes do nothing. + If not, check if the function took part in the mapping, then register it to have been renamed. Otherwise register it as the supplied status*) +let assignStatusToUnassignedFunction data f registerStatus statusMap mapping status = + if not (FundecMap.mem f statusMap) then + if (FundecMap.mem f mapping) then + registerStatus f (Renamed(FundecMap.find f mapping)) data else - (*Cyclic dependency found. *) - (*Mark all remaining nodes with dependencies as changed.*) - DependencyHeap.to_list dependencyHeap |> - List.fold_left (fun results dependencyPointer -> - StringMap.add dependencyPointer.oldName ChangedOrNewOrDeleted results - ) currentResults + (*this function has been added/removed*) + registerStatus f status data + else + data let detectRenamedFunctions (oldAST: file) (newAST: file) : output FundecMap.t = begin let oldFunctionMap = getFunctionMap oldAST in let nowFunctionMap = getFunctionMap newAST in - (*1. detect function which names have not changed*) - let statusForFunction = seperateUnchangedFunctions oldFunctionMap nowFunctionMap in - - let directMatchFunctions, knownChangedFunctions, unknownFunctions, initialCategorization = StringMap.fold ( - fun funName earlyStatus (directMatchFunctions, knownChangedFunctions, unknownFunctions, initialCategorization) -> match earlyStatus with - | DirectMatch d -> ( - StringSet.add funName directMatchFunctions, - knownChangedFunctions, - unknownFunctions, - StringMap.add funName (SameName(d)) initialCategorization - ) - | Changed -> ( - directMatchFunctions, - StringSet.add funName knownChangedFunctions, - unknownFunctions, - StringMap.add funName ChangedOrNewOrDeleted initialCategorization - ) - | Unknown -> ( - directMatchFunctions, - knownChangedFunctions, - StringSet.add funName unknownFunctions, - initialCategorization - ) - ) statusForFunction (StringSet.empty, StringSet.empty, StringSet.empty, StringMap.empty) in - - (*2. get dependencies of those functions we did match in 1. - These function statuses are just early guesses. They still need to be checked and adapted in the graph analysis.*) - let categorizationResults = categorizeUnknownFunctions unknownFunctions directMatchFunctions oldFunctionMap nowFunctionMap in - - (*3. build dependency graph*) - let categorizationMap = StringMap.union (fun _ _ _ -> None) initialCategorization categorizationResults in - - (*dependentsMap>*) - (*Generate the dependents map now, so it does not have to be done when generating the node map*) - let dependentsMap: string StringMap.t StringMap.t = StringMap.fold (fun oldFunName functionStatus dependentsMap -> - (*Go through all dependencies and add itself to the list of dependents*) - let addDependents dependencies = StringMap.fold (fun dependingOn hasToBeNamed dependentsMap -> - let currentDependents = StringMap.find_opt dependingOn dependentsMap |> - Option.value ~default:StringMap.empty in - - let newDependents = StringMap.add oldFunName hasToBeNamed currentDependents in - - StringMap.add dependingOn newDependents dependentsMap - ) dependencies dependentsMap - in - - match functionStatus with - | SameName dependencies -> addDependents dependencies - | Renamed renameData -> addDependents renameData.dependencies - | ChangedOrNewOrDeleted -> dependentsMap - ) categorizationMap StringMap.empty in - - (*The nodes are represented in the node map. The node data contains the nowName, - and the nodes it depends on as well as the nodes that depend on that node. - The dependencyHeap points to the function name with the currently least dependencies.*) - let (nodeMap: nodeData StringMap.t), (dependencyHeap: DependencyHeap.t) = - StringMap.fold (fun oldFunName functionStatus (nodeMap, dependencyHeap) -> - let dependents = StringMap.find_opt oldFunName dependentsMap |> - Option.value ~default:StringMap.empty in - - let getNodeEntry dependencies = {nowName=oldFunName; dependencies = dependencies; dependents = dependents; hadWrongAssumption = false} in - let getDependencyPointer dependencies = {oldName=oldFunName; dependencyCount=StringMap.cardinal dependencies} in - - match functionStatus with - | SameName dependencies -> - ( - StringMap.add oldFunName (getNodeEntry dependencies) nodeMap, - DependencyHeap.add (getDependencyPointer dependencies) dependencyHeap - ) - | Renamed renameData -> - ( - StringMap.add oldFunName (getNodeEntry renameData.dependencies) nodeMap, - DependencyHeap.add (getDependencyPointer renameData.dependencies) dependencyHeap - ) - | ChangedOrNewOrDeleted -> (nodeMap, dependencyHeap) - ) categorizationMap (StringMap.empty, DependencyHeap.empty) in - - - let result = reduceNodeGraph nodeMap dependencyHeap StringMap.empty in - - let x = StringMap.to_seq result |> - Seq.map (fun (oldName, status) -> - let (f, l) = StringMap.find oldName oldFunctionMap in - f, (GFun(f, l), status)) in - - FundecMap.add_seq x FundecMap.empty + let initialData: carryType = {statusForOldFunction = FundecMap.empty; + statusForNowFunction = FundecMap.empty; + methodMapping=FundecMap.empty; + reverseMethodMapping=FundecMap.empty} in + + (*Go through all functions, for all that have not been renamed *) + let finalData = + StringMap.fold (fun _ (f, _) (data: carryType) -> + let matchingNewFundec = StringMap.find_opt f.svar.vname nowFunctionMap in + match matchingNewFundec with + | Some (newFun, _) -> + (*Compare if they are similar*) + let doMatch, unchangedHeader, _, dependencies = CompareGlobals.eqF f newFun None StringMap.empty in + + let actDependencies = getDependencies dependencies in + + if doMatch then + let doDependenciesMatch, updatedData = doAllDependenciesMatch actDependencies oldFunctionMap nowFunctionMap data in + if doDependenciesMatch then + registerBiStatus f newFun (SameName(newFun)) updatedData + else + registerStatusForOldF f (Modified(newFun, unchangedHeader)) data |> + registerStatusForNowF newFun (Modified(f, unchangedHeader)) + else + registerStatusForOldF f (Modified(newFun, unchangedHeader)) data |> + registerStatusForNowF newFun (Modified(f, unchangedHeader)) + | None -> data + ) oldFunctionMap initialData |> + (*Now go through all old functions again. Those who have not been assigned a status are removed*) + StringMap.fold (fun _ (f, _) (data: carryType) -> + assignStatusToUnassignedFunction data f registerStatusForOldF data.statusForOldFunction data.methodMapping Deleted + ) oldFunctionMap |> + (*now go through all new functions. Those have have not been assigned a mapping are added.*) + StringMap.fold (fun _ (nowF, _) (data: carryType) -> + assignStatusToUnassignedFunction data nowF registerStatusForNowF data.statusForNowFunction data.reverseMethodMapping Created + ) nowFunctionMap + + in + + (*Map back to GFun and exposed function status*) + let extractOutput funMap invertedFunMap f (s: functionStatus) = + let getGFun f2 map = + let (f, l) = StringMap.find f2.svar.vname map in + GFun(f, l) + in + + let outputS = match s with + | SameName x -> Unchanged(getGFun x invertedFunMap) + | Renamed x -> UnchangedButRenamed(getGFun x invertedFunMap) + | Created -> Added + | Deleted -> Removed + | Modified (x, unchangedHeader) -> Changed(getGFun x invertedFunMap, unchangedHeader) + in + getGFun f funMap, outputS + in + + FundecMap.merge (fun _ a b -> + if Option.is_some a then a + else if Option.is_some b then b + else None + ) + (FundecMap.mapi (extractOutput oldFunctionMap nowFunctionMap) finalData.statusForOldFunction) + (FundecMap.mapi (extractOutput nowFunctionMap oldFunctionMap) finalData.statusForNowFunction) end diff --git a/src/incremental/updateCil.ml b/src/incremental/updateCil.ml index 90bca36304..aa2df5447a 100644 --- a/src/incremental/updateCil.ml +++ b/src/incremental/updateCil.ml @@ -1,5 +1,4 @@ open Cil -open CompareCIL open MaxIdUtil open MyCFG open CompareGlobals diff --git a/tests/incremental/05-method-rename/00-simple_rename.json b/tests/incremental/05-method-rename/00-simple_rename.json new file mode 100644 index 0000000000..0db3279e44 --- /dev/null +++ b/tests/incremental/05-method-rename/00-simple_rename.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/tests/incremental/05-method-rename/00-simple_rename.patch b/tests/incremental/05-method-rename/00-simple_rename.patch index 407a5a9bbf..ed7b40014c 100644 --- a/tests/incremental/05-method-rename/00-simple_rename.patch +++ b/tests/incremental/05-method-rename/00-simple_rename.patch @@ -1,5 +1,5 @@ ---- tests/incremental/05-method_rename/00-simple_rename.c -+++ tests/incremental/05-method_rename/00-simple_rename.c +--- tests/incremental/05-method-rename/00-simple_rename.c ++++ tests/incremental/05-method-rename/00-simple_rename.c @@ -1,10 +1,10 @@ #include diff --git a/tests/incremental/05-method-rename/01-dependent_rename.json b/tests/incremental/05-method-rename/01-dependent_rename.json new file mode 100644 index 0000000000..0db3279e44 --- /dev/null +++ b/tests/incremental/05-method-rename/01-dependent_rename.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/tests/incremental/05-method-rename/01-dependent_rename.patch b/tests/incremental/05-method-rename/01-dependent_rename.patch index 5eedfd814b..f3a4a9a3f8 100644 --- a/tests/incremental/05-method-rename/01-dependent_rename.patch +++ b/tests/incremental/05-method-rename/01-dependent_rename.patch @@ -1,5 +1,5 @@ ---- tests/incremental/05-method_rename/01-dependent_rename.c -+++ tests/incremental/05-method_rename/01-dependent_rename.c +--- tests/incremental/05-method-rename/01-dependent_rename.c ++++ tests/incremental/05-method-rename/01-dependent_rename.c @@ -1,14 +1,14 @@ #include From 1855a5a16dd3138d2a5cc592e0124db758476811 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 25 May 2022 18:38:07 +0200 Subject: [PATCH 26/90] Added more docu --- src/incremental/detectRenamedFunctions.ml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml index 99bc4a6f68..30cb5fb35b 100644 --- a/src/incremental/detectRenamedFunctions.ml +++ b/src/incremental/detectRenamedFunctions.ml @@ -10,6 +10,7 @@ type f = fundec * location module FundecForMap = struct type t = Cil.fundec + (*x.svar.uid cannot be used, as they may overlap between old and now AST*) let compare x y = String.compare x.svar.vname y.svar.vname end @@ -47,12 +48,20 @@ let getFunctionMap (ast: file) : f StringMap.t = let getDependencies fromEq = StringMap.map (fun assumption -> assumption.new_method_name) fromEq +(*Data type that holds the important data while checking for renames. + statusForOldFunction: Status we have already figured out for a fundec from oldAST; + statusForNowFunction: see statusForOldFunction; + methodMapping: Mappings from (fundec of old AST) -> (fundec of now AST) we have already figured out to hold. + reverseMethodMapping: see method mapping, but from now -> old + *) type carryType = { statusForOldFunction: functionStatus FundecMap.t; statusForNowFunction: functionStatus FundecMap.t; methodMapping: fundec FundecMap.t; reverseMethodMapping: fundec FundecMap.t} +(*Carry type manipulation functions.*) + let registerStatusForOldF f status data = {statusForOldFunction = FundecMap.add f status data.statusForOldFunction; statusForNowFunction=data.statusForNowFunction; @@ -108,7 +117,7 @@ let doAllDependenciesMatch (dependencies: dependencies) (oldFunctionMap: f Strin ) dependencies (true, data) (*Check if f has already been assigned a status. If yes do nothing. - If not, check if the function took part in the mapping, then register it to have been renamed. Otherwise register it as the supplied status*) + If not, check if the function took part in the mapping, then register it to have been renamed. Otherwise register it as the supplied status.*) let assignStatusToUnassignedFunction data f registerStatus statusMap mapping status = if not (FundecMap.mem f statusMap) then if (FundecMap.mem f mapping) then @@ -151,6 +160,8 @@ let detectRenamedFunctions (oldAST: file) (newAST: file) : output FundecMap.t = registerStatusForNowF newFun (Modified(f, unchangedHeader)) | None -> data ) oldFunctionMap initialData |> + (*At this point we already know of the functions that have changed and stayed the same. We now assign the correct status to all the functions that + have been mapped. The functions that have not been mapped are added/removed.*) (*Now go through all old functions again. Those who have not been assigned a status are removed*) StringMap.fold (fun _ (f, _) (data: carryType) -> assignStatusToUnassignedFunction data f registerStatusForOldF data.statusForOldFunction data.methodMapping Deleted @@ -162,6 +173,8 @@ let detectRenamedFunctions (oldAST: file) (newAST: file) : output FundecMap.t = in + (*Done with the analyis, the following just adjusts the output types.*) + (*Map back to GFun and exposed function status*) let extractOutput funMap invertedFunMap f (s: functionStatus) = let getGFun f2 map = @@ -179,6 +192,7 @@ let detectRenamedFunctions (oldAST: file) (newAST: file) : output FundecMap.t = getGFun f funMap, outputS in + (*Merge together old and now functions*) FundecMap.merge (fun _ a b -> if Option.is_some a then a else if Option.is_some b then b From c71e29ceeea41b8dc45312c4042f1a58c48043e2 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 25 May 2022 18:47:41 +0200 Subject: [PATCH 27/90] Added more test cases for method rename --- .../05-method-rename/06-common_rename.c | 20 ++++++++++++ .../05-method-rename/06-common_rename.json | 3 ++ .../05-method-rename/06-common_rename.patch | 27 ++++++++++++++++ .../07-common_rename_refactored.c | 20 ++++++++++++ .../07-common_rename_refactored.json | 3 ++ .../07-common_rename_refactored.patch | 31 +++++++++++++++++++ .../05-method-rename/diffs/06-common_rename.c | 20 ++++++++++++ .../diffs/07-common_rename_refactored.c | 24 ++++++++++++++ 8 files changed, 148 insertions(+) create mode 100644 tests/incremental/05-method-rename/06-common_rename.c create mode 100644 tests/incremental/05-method-rename/06-common_rename.json create mode 100644 tests/incremental/05-method-rename/06-common_rename.patch create mode 100644 tests/incremental/05-method-rename/07-common_rename_refactored.c create mode 100644 tests/incremental/05-method-rename/07-common_rename_refactored.json create mode 100644 tests/incremental/05-method-rename/07-common_rename_refactored.patch create mode 100644 tests/incremental/05-method-rename/diffs/06-common_rename.c create mode 100644 tests/incremental/05-method-rename/diffs/07-common_rename_refactored.c diff --git a/tests/incremental/05-method-rename/06-common_rename.c b/tests/incremental/05-method-rename/06-common_rename.c new file mode 100644 index 0000000000..ce72a6dda1 --- /dev/null +++ b/tests/incremental/05-method-rename/06-common_rename.c @@ -0,0 +1,20 @@ +#include + +void foo() { + printf("foo"); +} + +void fun1() { + foo(); +} + +void fun2() { + foo(); +} + +int main() { + fun1(); + fun2(); + foo(); + return 0; +} diff --git a/tests/incremental/05-method-rename/06-common_rename.json b/tests/incremental/05-method-rename/06-common_rename.json new file mode 100644 index 0000000000..0db3279e44 --- /dev/null +++ b/tests/incremental/05-method-rename/06-common_rename.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/tests/incremental/05-method-rename/06-common_rename.patch b/tests/incremental/05-method-rename/06-common_rename.patch new file mode 100644 index 0000000000..15afbce9ce --- /dev/null +++ b/tests/incremental/05-method-rename/06-common_rename.patch @@ -0,0 +1,27 @@ +--- tests/incremental/05-method-rename/06-common_rename.c ++++ tests/incremental/05-method-rename/06-common_rename.c +@@ -1,20 +1,20 @@ + #include + +-void foo() { ++void bar() { + printf("foo"); + } + + void fun1() { +- foo(); ++ bar(); + } + + void fun2() { +- foo(); ++ bar(); + } + + int main() { + fun1(); + fun2(); +- foo(); ++ bar(); + return 0; + } diff --git a/tests/incremental/05-method-rename/07-common_rename_refactored.c b/tests/incremental/05-method-rename/07-common_rename_refactored.c new file mode 100644 index 0000000000..ce72a6dda1 --- /dev/null +++ b/tests/incremental/05-method-rename/07-common_rename_refactored.c @@ -0,0 +1,20 @@ +#include + +void foo() { + printf("foo"); +} + +void fun1() { + foo(); +} + +void fun2() { + foo(); +} + +int main() { + fun1(); + fun2(); + foo(); + return 0; +} diff --git a/tests/incremental/05-method-rename/07-common_rename_refactored.json b/tests/incremental/05-method-rename/07-common_rename_refactored.json new file mode 100644 index 0000000000..0db3279e44 --- /dev/null +++ b/tests/incremental/05-method-rename/07-common_rename_refactored.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/tests/incremental/05-method-rename/07-common_rename_refactored.patch b/tests/incremental/05-method-rename/07-common_rename_refactored.patch new file mode 100644 index 0000000000..4c3d9fa1d6 --- /dev/null +++ b/tests/incremental/05-method-rename/07-common_rename_refactored.patch @@ -0,0 +1,31 @@ +--- tests/incremental/05-method-rename/07-common_rename_refactored.c ++++ tests/incremental/05-method-rename/07-common_rename_refactored.c +@@ -1,20 +1,24 @@ + #include + +-void foo() { ++void bar() { + printf("foo"); + } + ++void baz() { ++ printf("baz"); ++} ++ + void fun1() { +- foo(); ++ bar(); + } + + void fun2() { +- foo(); ++ bar(); + } + + int main() { + fun1(); + fun2(); +- foo(); ++ baz(); + return 0; + } diff --git a/tests/incremental/05-method-rename/diffs/06-common_rename.c b/tests/incremental/05-method-rename/diffs/06-common_rename.c new file mode 100644 index 0000000000..6a96b84747 --- /dev/null +++ b/tests/incremental/05-method-rename/diffs/06-common_rename.c @@ -0,0 +1,20 @@ +#include + +void bar() { + printf("foo"); +} + +void fun1() { + bar(); +} + +void fun2() { + bar(); +} + +int main() { + fun1(); + fun2(); + bar(); + return 0; +} diff --git a/tests/incremental/05-method-rename/diffs/07-common_rename_refactored.c b/tests/incremental/05-method-rename/diffs/07-common_rename_refactored.c new file mode 100644 index 0000000000..170cdfb6de --- /dev/null +++ b/tests/incremental/05-method-rename/diffs/07-common_rename_refactored.c @@ -0,0 +1,24 @@ +#include + +void bar() { + printf("foo"); +} + +void baz() { + printf("baz"); +} + +void fun1() { + bar(); +} + +void fun2() { + bar(); +} + +int main() { + fun1(); + fun2(); + baz(); + return 0; +} From 874519c74ff26839364088b44f88b8823643fd71 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Mon, 6 Jun 2022 12:52:15 +0200 Subject: [PATCH 28/90] Added json and patch files for method rename tests. --- .../05-method-rename/02-rename_and_swap.json | 3 +++ .../05-method-rename/02-rename_and_swap.patch | 25 +++++++++++++++++ .../03-cyclic_rename_dependency.json | 3 +++ .../03-cyclic_rename_dependency.patch | 25 +++++++++++++++++ .../05-method-rename/04-cyclic_with_swap.json | 3 +++ .../04-cyclic_with_swap.patch | 27 +++++++++++++++++++ 6 files changed, 86 insertions(+) create mode 100644 tests/incremental/05-method-rename/02-rename_and_swap.json create mode 100644 tests/incremental/05-method-rename/02-rename_and_swap.patch create mode 100644 tests/incremental/05-method-rename/03-cyclic_rename_dependency.json create mode 100644 tests/incremental/05-method-rename/03-cyclic_rename_dependency.patch create mode 100644 tests/incremental/05-method-rename/04-cyclic_with_swap.json create mode 100644 tests/incremental/05-method-rename/04-cyclic_with_swap.patch diff --git a/tests/incremental/05-method-rename/02-rename_and_swap.json b/tests/incremental/05-method-rename/02-rename_and_swap.json new file mode 100644 index 0000000000..0db3279e44 --- /dev/null +++ b/tests/incremental/05-method-rename/02-rename_and_swap.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/tests/incremental/05-method-rename/02-rename_and_swap.patch b/tests/incremental/05-method-rename/02-rename_and_swap.patch new file mode 100644 index 0000000000..ab39c2dc4b --- /dev/null +++ b/tests/incremental/05-method-rename/02-rename_and_swap.patch @@ -0,0 +1,25 @@ +--- tests/incremental/05-method-rename/02-rename_and_swap.c ++++ tests/incremental/05-method-rename/02-rename_and_swap.c +@@ -1,15 +1,19 @@ + #include + +-void foo1() { ++void newFun() { ++ printf("newFun"); ++} ++ ++void bar1() { + printf("foo1"); + } + + void foo2() { +- foo1(); ++ bar1(); + } + + void foo3() { +- foo1(); ++ newFun(); + } + + int main() { diff --git a/tests/incremental/05-method-rename/03-cyclic_rename_dependency.json b/tests/incremental/05-method-rename/03-cyclic_rename_dependency.json new file mode 100644 index 0000000000..0db3279e44 --- /dev/null +++ b/tests/incremental/05-method-rename/03-cyclic_rename_dependency.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/tests/incremental/05-method-rename/03-cyclic_rename_dependency.patch b/tests/incremental/05-method-rename/03-cyclic_rename_dependency.patch new file mode 100644 index 0000000000..ae32544efd --- /dev/null +++ b/tests/incremental/05-method-rename/03-cyclic_rename_dependency.patch @@ -0,0 +1,25 @@ +--- tests/incremental/05-method-rename/03-cyclic_rename_dependency.c ++++ tests/incremental/05-method-rename/03-cyclic_rename_dependency.c +@@ -2,16 +2,16 @@ + + //Unchanged. + +-void foo1(int c) { +- if (c < 10) foo2(c + 1); ++void bar1(int c) { ++ if (c < 10) bar2(c + 1); + } + +-void foo2(int c) { +- if (c < 10) foo1(c + 1); ++void bar2(int c) { ++ if (c < 10) bar1(c + 1); + } + + int main() { +- foo1(0); +- foo2(0); ++ bar1(0); ++ bar2(0); + return 0; + } diff --git a/tests/incremental/05-method-rename/04-cyclic_with_swap.json b/tests/incremental/05-method-rename/04-cyclic_with_swap.json new file mode 100644 index 0000000000..0db3279e44 --- /dev/null +++ b/tests/incremental/05-method-rename/04-cyclic_with_swap.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/tests/incremental/05-method-rename/04-cyclic_with_swap.patch b/tests/incremental/05-method-rename/04-cyclic_with_swap.patch new file mode 100644 index 0000000000..7e96afd8e0 --- /dev/null +++ b/tests/incremental/05-method-rename/04-cyclic_with_swap.patch @@ -0,0 +1,27 @@ +--- tests/incremental/05-method-rename/04-cyclic_with_swap.c ++++ tests/incremental/05-method-rename/04-cyclic_with_swap.c +@@ -2,15 +2,19 @@ + + //Changed. + +-void foo1(int c) { +- if (c < 10) foo2(c + 1); ++void newFun(int c) { ++ printf("newfun"); + } + +-void foo2(int c) { +- if (c < 10) foo1(c + 1); ++void bar1(int c) { ++ if (c < 10) bar2(c + 1); ++} ++ ++void bar2(int c) { ++ if (c < 10) newFun(c + 1); + } + + int main() { +- foo1(0); ++ bar1(0); + return 0; + } From b75a19f63bf1a604895886b2cc698835accbe0f3 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Mon, 6 Jun 2022 15:19:47 +0200 Subject: [PATCH 29/90] Global var renames are now also detected in compareAST --- src/incremental/compareAST.ml | 70 +++++++++++++++---- src/incremental/compareCFG.ml | 4 +- src/incremental/compareCIL.ml | 6 +- src/incremental/compareGlobals.ml | 18 ++--- src/incremental/detectRenamedFunctions.ml | 17 +++-- .../06-glob-var-rename/00-simple_rename.c | 9 +++ .../06-glob-var-rename/00-simple_rename.json | 3 + .../06-glob-var-rename/00-simple_rename.patch | 14 ++++ .../01-duplicate_local_global.c | 14 ++++ .../01-duplicate_local_global.json | 3 + .../01-duplicate_local_global.patch | 21 ++++++ .../diffs/00-simple_rename.c | 9 +++ .../diffs/01-duplicate_local_global.c | 14 ++++ 13 files changed, 166 insertions(+), 36 deletions(-) create mode 100644 tests/incremental/06-glob-var-rename/00-simple_rename.c create mode 100644 tests/incremental/06-glob-var-rename/00-simple_rename.json create mode 100644 tests/incremental/06-glob-var-rename/00-simple_rename.patch create mode 100644 tests/incremental/06-glob-var-rename/01-duplicate_local_global.c create mode 100644 tests/incremental/06-glob-var-rename/01-duplicate_local_global.json create mode 100644 tests/incremental/06-glob-var-rename/01-duplicate_local_global.patch create mode 100644 tests/incremental/06-glob-var-rename/diffs/00-simple_rename.c create mode 100644 tests/incremental/06-glob-var-rename/diffs/01-duplicate_local_global.c diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 4834796d2b..4176c85bf5 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -7,17 +7,30 @@ and global_identifier = {name: string ; global_t: global_type} [@@deriving ord] module StringMap = Map.Make(String) +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) + type method_rename_assumption = {original_method_name: string; new_method_name: string; parameter_renames: string StringMap.t} type method_rename_assumptions = method_rename_assumption StringMap.t +type glob_var_rename_assumptions = string VarinfoMap.t (*rename_mapping is carried through the stack when comparing the AST. Holds a list of rename assumptions.*) -type rename_mapping = (string StringMap.t) * (method_rename_assumptions) +type rename_mapping = (string StringMap.t) * (method_rename_assumptions) * glob_var_rename_assumptions + +let emptyRenameMapping = (StringMap.empty, StringMap.empty, VarinfoMap.empty) (*Compares two names, being aware of the rename_mapping. Returns true iff: 1. there is a rename for name1 -> name2 = rename(name1) 2. there is no rename for name1 -> name1 = name2*) let rename_mapping_aware_name_comparison (name1: string) (name2: string) (rename_mapping: rename_mapping) = - let (local_c, method_c) = rename_mapping in + let (local_c, method_c, _) = rename_mapping in let existingAssumption: string option = StringMap.find_opt name1 local_c in match existingAssumption with @@ -44,14 +57,18 @@ let string_tuple_to_string (tuple: (string * string) list) = "[" ^ (tuple |> String.concat ", ") ^ "]" let rename_mapping_to_string (rename_mapping: rename_mapping) = - let (local, methods) = rename_mapping in + let (local, methods, glob_vars) = rename_mapping in let local_string = string_tuple_to_string (List.of_seq (StringMap.to_seq local)) in let methods_string: string = List.of_seq (StringMap.to_seq methods |> Seq.map snd) |> List.map (fun x -> match x with {original_method_name; new_method_name; parameter_renames} -> "(methodName: " ^ original_method_name ^ " -> " ^ new_method_name ^ "; renamed_params=" ^ string_tuple_to_string (List.of_seq (StringMap.to_seq parameter_renames)) ^ ")") |> String.concat ", " in - "(local=" ^ local_string ^ "; methods=[" ^ methods_string ^ "])" + + let global_var_string: string = string_tuple_to_string (List.of_seq (VarinfoMap.to_seq glob_vars) |> + List.map (fun (v, nowName) -> v.vname, nowName)) in + + "(local=" ^ local_string ^ "; methods=[" ^ methods_string ^ "]; glob_vars=" ^ global_var_string ^ ")" let identifier_of_global glob = match glob with @@ -215,16 +232,30 @@ and eq_varinfo2 (rename_mapping: rename_mapping) (a: varinfo) (b: varinfo) = eq_ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) : bool * rename_mapping = (*Printf.printf "Comp %s with %s\n" a.vname b.vname;*) - let (locals_renames, method_rename_mappings) = rename_mapping in + let (locals_renames, method_rename_mappings, glob_vars) = rename_mapping in + + let compare_local_and_global_var = fun old_varinfo now_varinfo -> + let is_local = StringMap.mem old_varinfo.vname locals_renames in + if not is_local then + let present_mapping = VarinfoMap.find_opt old_varinfo glob_vars in + + match present_mapping with + | Some (knownNowName) -> now_varinfo.vname = knownNowName, method_rename_mappings, glob_vars + | None -> ( + let update_glob_vars = VarinfoMap.add old_varinfo now_varinfo.vname glob_vars in + true, method_rename_mappings, update_glob_vars + ) + else rename_mapping_aware_name_comparison old_varinfo.vname now_varinfo.vname rename_mapping, method_rename_mappings, glob_vars + in (*When we compare function names, we can directly compare the naming from the rename_mapping if it exists.*) - let isNamingOk, updated_method_rename_mappings = match a.vtype, b.vtype with + let isNamingOk, updated_method_rename_mappings, updatedGlobVarMapping = match a.vtype, b.vtype with | TFun(_, aParamSpec, _, _), TFun(_, bParamSpec, _, _) -> ( let specific_method_rename_mapping = StringMap.find_opt a.vname method_rename_mappings in match specific_method_rename_mapping with | Some method_rename_mapping -> let is_naming_ok = method_rename_mapping.original_method_name = a.vname && method_rename_mapping.new_method_name = b.vname in - is_naming_ok, method_rename_mappings + is_naming_ok, method_rename_mappings, glob_vars | None -> if a.vname <> b.vname then (*Function that extracts the names from the param spec of the TFun*) @@ -240,10 +271,13 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) : bool let assumption = {original_method_name = a.vname; new_method_name = b.vname; parameter_renames = create_locals_rename_mapping aParamNames bParamNames} in - true, StringMap.add a.vname assumption method_rename_mappings - else true, method_rename_mappings + true, StringMap.add a.vname assumption method_rename_mappings, glob_vars + else true, method_rename_mappings, glob_vars ) - | _, _ -> rename_mapping_aware_name_comparison a.vname b.vname rename_mapping, method_rename_mappings + | TInt (_, _), TInt (_, _) -> compare_local_and_global_var a b + | TFloat (_, _), TFloat (_, _) -> compare_local_and_global_var a b + | TPtr (_, _), TPtr(_, _) -> compare_local_and_global_var a b + | _, _ -> rename_mapping_aware_name_comparison a.vname b.vname rename_mapping, method_rename_mappings, glob_vars in (*If the following is a method call, we need to check if we have a mapping for that method call. *) @@ -253,17 +287,23 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) : bool match new_locals with | Some locals -> - (*Printf.printf "Performing rename_mapping switch. New rename_mapping=%s\n" (rename_mapping_to_string (locals.parameter_renames, method_rename_mappings));*) - (locals.parameter_renames, updated_method_rename_mappings) - | None -> (StringMap.empty, updated_method_rename_mappings) + (locals.parameter_renames, updated_method_rename_mappings, updatedGlobVarMapping) + | None -> (StringMap.empty, updated_method_rename_mappings, updatedGlobVarMapping) ) - | _ -> (locals_renames, updated_method_rename_mappings) + (*| GVar (_, _, _) -> ( + let new_local = VarinfoMap.find_opt a glob_vars in + + match new_local with + | Some now_name -> (StringMap.add a.vname now_name StringMap.empty, updated_method_rename_mappings, updatedGlobVarMapping) + | None -> (StringMap.empty, updated_method_rename_mappings, updatedGlobVarMapping) + )*) + | _ -> (locals_renames, updated_method_rename_mappings, updatedGlobVarMapping) in (*Ignore rename mapping for type check, as it doesn't change anyway*) let (typeCheck, _) = eq_typ a.vtype b.vtype typ_rename_mapping in - (typeCheck, (locals_renames, updated_method_rename_mappings)) &&>> + (typeCheck, (locals_renames, updated_method_rename_mappings, updatedGlobVarMapping)) &&>> forward_list_equal eq_attribute a.vattr b.vattr &&> (a.vstorage = b.vstorage) &&> (a.vglob = b.vglob) &&> (a.vaddrof = b.vaddrof) (* Ignore the location, vid, vreferenced, vdescr, vdescrpure, vinline *) diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index 4f2c37223f..62ea88e875 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -12,7 +12,7 @@ let (&&<>) (prev_result: bool * rename_mapping) f : bool * rename_mapping = else false, prev_rm let eq_node (x, fun1) (y, fun2) : bool = - let empty_rename_mapping: rename_mapping = (StringMap.empty, StringMap.empty) in + let empty_rename_mapping: rename_mapping = emptyRenameMapping in match x,y with | Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) empty_rename_mapping |> fst | Function f1, Function f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping |> fst @@ -21,7 +21,7 @@ let eq_node (x, fun1) (y, fun2) : bool = (* TODO: compare ASMs properly instead of simply always assuming that they are not the same *) let eq_edge x y = - let empty_rename_mapping: rename_mapping = (StringMap.empty, StringMap.empty) in + let empty_rename_mapping: rename_mapping = emptyRenameMapping in let (r, _) = match x, y with | Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 empty_rename_mapping &&<> eq_exp rv1 rv2 | Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 empty_rename_mapping &&<> forward_list_equal eq_exp ars1 ars2 diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 13e1f42069..816ff623be 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -9,11 +9,11 @@ let empty_change_info () : change_info = {added = []; removed = []; changed = [] let eq_glob (a: global) (b: global) (cfgs : (cfg * (cfg * cfg)) option) = match a, b with | GFun (f,_), GFun (g,_) -> - let identical, unchangedHeader, diffOpt, _ = CompareGlobals.eqF f g cfgs StringMap.empty in + let identical, unchangedHeader, diffOpt, _, _ = CompareGlobals.eqF f g cfgs StringMap.empty VarinfoMap.empty in identical, unchangedHeader, diffOpt - | GVar (x, init_x, _), GVar (y, init_y, _) -> eq_varinfo x y (StringMap.empty, StringMap.empty) |> fst, 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, StringMap.empty) |> fst, false, None + | GVar (x, init_x, _), GVar (y, init_y, _) -> eq_varinfo x y emptyRenameMapping |> fst, 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 emptyRenameMapping |> fst, 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) = diff --git a/src/incremental/compareGlobals.ml b/src/incremental/compareGlobals.ml index e0cde8735a..c491372314 100644 --- a/src/incremental/compareGlobals.ml +++ b/src/incremental/compareGlobals.ml @@ -34,7 +34,7 @@ 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) (global_rename_mapping: method_rename_assumptions) = + let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (global_function_rename_mapping: method_rename_assumptions) (global_var_rename_mapping: glob_var_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 @@ -56,20 +56,20 @@ let should_reanalyze (fdec: Cil.fundec) = in let headerSizeEqual, headerRenameMapping = rename_mapping_aware_compare a.sformals b.sformals (StringMap.empty) in - let actHeaderRenameMapping = (headerRenameMapping, global_rename_mapping) in + let actHeaderRenameMapping = (headerRenameMapping, global_function_rename_mapping, global_var_rename_mapping) in let unchangedHeader = eq_varinfo a.svar b.svar actHeaderRenameMapping &&>> forward_list_equal eq_varinfo a.sformals b.sformals in - let identical, diffOpt, (_, renamed_method_dependencies) = + let identical, diffOpt, (_, renamed_method_dependencies, renamed_global_vars_dependencies) = if should_reanalyze a then - false, None, (StringMap.empty, StringMap.empty) + false, None, emptyRenameMapping else (* 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 rename_mapping: rename_mapping = (local_rename, global_function_rename_mapping, global_var_rename_mapping) in let sameDef = unchangedHeader &&> sizeEqual |> fst in if not sameDef then - (false, None, (StringMap.empty, StringMap.empty)) + (false, None, emptyRenameMapping) else match cfgs with | None -> @@ -79,7 +79,7 @@ let should_reanalyze (fdec: Cil.fundec) = 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 let matches, diffNodes1 = compareFun (module CfgOld) (module CfgNew) a b in - if diffNodes1 = [] then (true, None, (StringMap.empty, StringMap.empty)) - else (false, Some {unchangedNodes = matches; primObsoleteNodes = diffNodes1}, (StringMap.empty, StringMap.empty)) + if diffNodes1 = [] then (true, None, emptyRenameMapping) + else (false, Some {unchangedNodes = matches; primObsoleteNodes = diffNodes1}, emptyRenameMapping) in - identical, unchangedHeader |> fst, diffOpt, renamed_method_dependencies + identical, unchangedHeader |> fst, diffOpt, renamed_method_dependencies, renamed_global_vars_dependencies diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml index 30cb5fb35b..ba6be0dcf1 100644 --- a/src/incremental/detectRenamedFunctions.ml +++ b/src/incremental/detectRenamedFunctions.ml @@ -17,7 +17,7 @@ end module FundecMap = Map.Make(FundecForMap) (*A dependency maps the function it depends on to the name the function has to be changed to*) -type dependencies = string StringMap.t +type functionDependencies = string StringMap.t (*Renamed: newName * dependencies; Modified=now*unchangedHeader*) type functionStatus = SameName of fundec | Renamed of fundec | Created | Deleted | Modified of fundec * bool @@ -87,7 +87,7 @@ let registerMapping oldF nowF data = reverseMethodMapping=FundecMap.add nowF oldF data.reverseMethodMapping} (*returns true iff for all dependencies it is true, that the dependency has a corresponding function with the new name and matches the without having dependencies itself and the new name is not already present on the old AST. *) -let doAllDependenciesMatch (dependencies: dependencies) (oldFunctionMap: f StringMap.t) (newFunctionMap: f StringMap.t) (data: carryType) : bool * carryType = +let doAllDependenciesMatch (dependencies: functionDependencies) (global_var_dependencies: glob_var_rename_assumptions) (oldFunctionMap: f StringMap.t) (newFunctionMap: f StringMap.t) (data: carryType) : bool * carryType = StringMap.fold (fun oldName newName (allEqual, data) -> (*Early cutoff if a previous dependency returned false or the newName is already present in the old map*) if allEqual && not (StringMap.mem newName oldFunctionMap) then @@ -106,9 +106,9 @@ let doAllDependenciesMatch (dependencies: dependencies) (oldFunctionMap: f Strin match newFundecOption with | Some((newFundec, _)) -> - let doMatch, _, _, dependencies = CompareGlobals.eqF oldFundec newFundec None StringMap.empty in + let doMatch, _, _, function_dependencies, global_var_dependencies = CompareGlobals.eqF oldFundec newFundec None StringMap.empty VarinfoMap.empty in - if doMatch && StringMap.is_empty dependencies then + if doMatch && StringMap.is_empty function_dependencies && VarinfoMap.is_empty global_var_dependencies then true, registerMapping oldFundec newFundec data else false, data @@ -144,12 +144,15 @@ let detectRenamedFunctions (oldAST: file) (newAST: file) : output FundecMap.t = match matchingNewFundec with | Some (newFun, _) -> (*Compare if they are similar*) - let doMatch, unchangedHeader, _, dependencies = CompareGlobals.eqF f newFun None StringMap.empty in + let doMatch, unchangedHeader, _, function_dependencies, global_var_dependencies = + CompareGlobals.eqF f newFun None StringMap.empty VarinfoMap.empty in - let actDependencies = getDependencies dependencies in + let _ = Pretty.printf "%s\n" (rename_mapping_to_string (StringMap.empty, function_dependencies, global_var_dependencies)) in + + let actDependencies = getDependencies function_dependencies in if doMatch then - let doDependenciesMatch, updatedData = doAllDependenciesMatch actDependencies oldFunctionMap nowFunctionMap data in + let doDependenciesMatch, updatedData = doAllDependenciesMatch actDependencies global_var_dependencies oldFunctionMap nowFunctionMap data in if doDependenciesMatch then registerBiStatus f newFun (SameName(newFun)) updatedData else diff --git a/tests/incremental/06-glob-var-rename/00-simple_rename.c b/tests/incremental/06-glob-var-rename/00-simple_rename.c new file mode 100644 index 0000000000..56650e98ed --- /dev/null +++ b/tests/incremental/06-glob-var-rename/00-simple_rename.c @@ -0,0 +1,9 @@ +#include + +int foo = 1; + +int main() { + printf("%d", foo); + + return 0; +} diff --git a/tests/incremental/06-glob-var-rename/00-simple_rename.json b/tests/incremental/06-glob-var-rename/00-simple_rename.json new file mode 100644 index 0000000000..0db3279e44 --- /dev/null +++ b/tests/incremental/06-glob-var-rename/00-simple_rename.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/tests/incremental/06-glob-var-rename/00-simple_rename.patch b/tests/incremental/06-glob-var-rename/00-simple_rename.patch new file mode 100644 index 0000000000..1e0f3b2565 --- /dev/null +++ b/tests/incremental/06-glob-var-rename/00-simple_rename.patch @@ -0,0 +1,14 @@ +--- tests/incremental/06-glob-var-rename/00-simple_rename.c ++++ tests/incremental/06-glob-var-rename/00-simple_rename.c +@@ -1,9 +1,9 @@ + #include + +-int foo = 1; ++int bar = 1; + + int main() { +- printf("%d", foo); ++ printf("%d", bar); + + return 0; + } diff --git a/tests/incremental/06-glob-var-rename/01-duplicate_local_global.c b/tests/incremental/06-glob-var-rename/01-duplicate_local_global.c new file mode 100644 index 0000000000..9ad715e50d --- /dev/null +++ b/tests/incremental/06-glob-var-rename/01-duplicate_local_global.c @@ -0,0 +1,14 @@ +#include + +int foo = 1; + +int main() { + + printf("%d", foo); + + int foo = 2; + + printf("%d", foo); + + return 0; +} diff --git a/tests/incremental/06-glob-var-rename/01-duplicate_local_global.json b/tests/incremental/06-glob-var-rename/01-duplicate_local_global.json new file mode 100644 index 0000000000..0db3279e44 --- /dev/null +++ b/tests/incremental/06-glob-var-rename/01-duplicate_local_global.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/tests/incremental/06-glob-var-rename/01-duplicate_local_global.patch b/tests/incremental/06-glob-var-rename/01-duplicate_local_global.patch new file mode 100644 index 0000000000..1d65c5672a --- /dev/null +++ b/tests/incremental/06-glob-var-rename/01-duplicate_local_global.patch @@ -0,0 +1,21 @@ +--- tests/incremental/06-glob-var-rename/01-duplicate_local_global.c ++++ tests/incremental/06-glob-var-rename/01-duplicate_local_global.c +@@ -1,14 +1,14 @@ + #include + +-int foo = 1; ++int bar = 1; + + int main() { + +- printf("%d", foo); ++ printf("%d", bar); + +- int foo = 2; ++ int bar = 2; + +- printf("%d", foo); ++ printf("%d", bar); + + return 0; + } diff --git a/tests/incremental/06-glob-var-rename/diffs/00-simple_rename.c b/tests/incremental/06-glob-var-rename/diffs/00-simple_rename.c new file mode 100644 index 0000000000..bfe71f0522 --- /dev/null +++ b/tests/incremental/06-glob-var-rename/diffs/00-simple_rename.c @@ -0,0 +1,9 @@ +#include + +int bar = 1; + +int main() { + printf("%d", bar); + + return 0; +} diff --git a/tests/incremental/06-glob-var-rename/diffs/01-duplicate_local_global.c b/tests/incremental/06-glob-var-rename/diffs/01-duplicate_local_global.c new file mode 100644 index 0000000000..0e4ebf3fd7 --- /dev/null +++ b/tests/incremental/06-glob-var-rename/diffs/01-duplicate_local_global.c @@ -0,0 +1,14 @@ +#include + +int bar = 1; + +int main() { + + printf("%d", bar); + + int bar = 2; + + printf("%d", bar); + + return 0; +} From 9e80fc1971b8d2d9895f2de3c46113b5da1badaa Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Tue, 7 Jun 2022 14:43:15 +0200 Subject: [PATCH 30/90] Cleaned up and improved detectRenamedFunctions. --- src/incremental/compareAST.ml | 27 +- src/incremental/compareCIL.ml | 11 +- src/incremental/compareGlobals.ml | 20 +- src/incremental/detectRenamedFunctions.ml | 290 ++++++++++++------ .../06-glob-var-rename/02-add_new_gvar.c | 8 + .../06-glob-var-rename/02-add_new_gvar.json | 3 + .../06-glob-var-rename/02-add_new_gvar.patch | 13 + .../diffs/02-add_new_gvar.c | 9 + 8 files changed, 259 insertions(+), 122 deletions(-) create mode 100644 tests/incremental/06-glob-var-rename/02-add_new_gvar.c create mode 100644 tests/incremental/06-glob-var-rename/02-add_new_gvar.json create mode 100644 tests/incremental/06-glob-var-rename/02-add_new_gvar.patch create mode 100644 tests/incremental/06-glob-var-rename/diffs/02-add_new_gvar.c diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 4176c85bf5..598bc03418 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -66,7 +66,7 @@ let rename_mapping_to_string (rename_mapping: rename_mapping) = String.concat ", " in let global_var_string: string = string_tuple_to_string (List.of_seq (VarinfoMap.to_seq glob_vars) |> - List.map (fun (v, nowName) -> v.vname, nowName)) in + List.map (fun (v, nowName) -> v.vname, nowName)) in "(local=" ^ local_string ^ "; methods=[" ^ methods_string ^ "]; glob_vars=" ^ global_var_string ^ ")" @@ -234,18 +234,19 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) : bool let (locals_renames, method_rename_mappings, glob_vars) = rename_mapping in - let compare_local_and_global_var = fun old_varinfo now_varinfo -> - let is_local = StringMap.mem old_varinfo.vname locals_renames in + let compare_local_and_global_var = + let is_local = StringMap.mem a.vname locals_renames in if not is_local then - let present_mapping = VarinfoMap.find_opt old_varinfo glob_vars in + let present_mapping = VarinfoMap.find_opt a glob_vars in match present_mapping with - | Some (knownNowName) -> now_varinfo.vname = knownNowName, method_rename_mappings, glob_vars - | None -> ( - let update_glob_vars = VarinfoMap.add old_varinfo now_varinfo.vname glob_vars in + | Some (knownNowName) -> + b.vname = knownNowName, method_rename_mappings, glob_vars + | None -> ( + let update_glob_vars = VarinfoMap.add a b.vname glob_vars in true, method_rename_mappings, update_glob_vars ) - else rename_mapping_aware_name_comparison old_varinfo.vname now_varinfo.vname rename_mapping, method_rename_mappings, glob_vars + else rename_mapping_aware_name_comparison a.vname b.vname rename_mapping, method_rename_mappings, glob_vars in (*When we compare function names, we can directly compare the naming from the rename_mapping if it exists.*) @@ -274,9 +275,9 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) : bool true, StringMap.add a.vname assumption method_rename_mappings, glob_vars else true, method_rename_mappings, glob_vars ) - | TInt (_, _), TInt (_, _) -> compare_local_and_global_var a b - | TFloat (_, _), TFloat (_, _) -> compare_local_and_global_var a b - | TPtr (_, _), TPtr(_, _) -> compare_local_and_global_var a b + | TInt (_, _), TInt (_, _) -> compare_local_and_global_var + | TFloat (_, _), TFloat (_, _) -> compare_local_and_global_var + | TPtr (_, _), TPtr(_, _) -> compare_local_and_global_var | _, _ -> rename_mapping_aware_name_comparison a.vname b.vname rename_mapping, method_rename_mappings, glob_vars in @@ -296,14 +297,14 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) : bool match new_local with | Some now_name -> (StringMap.add a.vname now_name StringMap.empty, updated_method_rename_mappings, updatedGlobVarMapping) | None -> (StringMap.empty, updated_method_rename_mappings, updatedGlobVarMapping) - )*) + )*) | _ -> (locals_renames, updated_method_rename_mappings, updatedGlobVarMapping) in (*Ignore rename mapping for type check, as it doesn't change anyway*) let (typeCheck, _) = eq_typ a.vtype b.vtype typ_rename_mapping in - (typeCheck, (locals_renames, updated_method_rename_mappings, updatedGlobVarMapping)) &&>> + (isNamingOk && typeCheck, (locals_renames, updated_method_rename_mappings, updatedGlobVarMapping)) &&>> forward_list_equal eq_attribute a.vattr b.vattr &&> (a.vstorage = b.vstorage) &&> (a.vglob = b.vglob) &&> (a.vaddrof = b.vaddrof) (* Ignore the location, vid, vreferenced, vdescr, vdescrpure, vinline *) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 816ff623be..0deca77de2 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -38,7 +38,7 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = let findChanges map global = try let isGFun = match global with - | GFun _-> false (* set to true later to disable finding changes for funs*) + | GFun _-> true (* set to true later to disable finding changes for funs*) | _ -> false in @@ -57,10 +57,10 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = let oldMap = Cil.foldGlobals oldAST addGlobal GlobalMap.empty in let renameDetectionResults = detectRenamedFunctions oldAST newAST in - FundecMap.to_seq renameDetectionResults |> + GlobalElemMap.to_seq renameDetectionResults |> Seq.iter - (fun (fundec, (functionGlobal, status)) -> - Printf.printf "Function status of %s is=" fundec.svar.vname; + (fun (gT, (functionGlobal, status)) -> + Printf.printf "Function status of %s is=" (globalElemName gT); match status with | Unchanged _ -> Printf.printf "Same Name\n"; | Added -> Printf.printf "Added\n"; @@ -69,6 +69,7 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = | UnchangedButRenamed toFrom -> match toFrom with | GFun (f, _) -> Printf.printf "Renamed to %s\n" f.svar.vname; + | GVar(v, _, _) -> Printf.printf "Renamed to %s\n" v.vname; | _ -> Printf.printf "TODO"; ); @@ -77,7 +78,7 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = Cil.iterGlobals newAST (fun glob -> findChanges oldMap glob); - let unchanged, changed, added, removed = FundecMap.fold (fun _ (global, status) (u, c, a, r) -> + let unchanged, changed, added, removed = GlobalElemMap.fold (fun _ (global, status) (u, c, a, r) -> match status with | Unchanged now -> (u @ [{old=global; current=now}], c, a, r) | UnchangedButRenamed now -> (u @ [{old=global; current=now}], c, a, r) diff --git a/src/incremental/compareGlobals.ml b/src/incremental/compareGlobals.ml index c491372314..76a98bd58e 100644 --- a/src/incremental/compareGlobals.ml +++ b/src/incremental/compareGlobals.ml @@ -34,26 +34,26 @@ 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) (global_function_rename_mapping: method_rename_assumptions) (global_var_rename_mapping: glob_var_rename_assumptions) = +let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (global_function_rename_mapping: method_rename_assumptions) (global_var_rename_mapping: glob_var_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.add local_rename_map a b); + List.map (fun x -> match x with (a, b) -> (a.vname, b.vname)) |> + List.iter (fun pair -> match pair with (a, b) -> Hashtbl.add 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 = if origLocal.vname <> nowLocal.vname then StringMap.add origLocal.vname nowLocal.vname rename_mapping else rename_mapping in + | [], [] -> 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 + (*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_function_rename_mapping, global_var_rename_mapping) in diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml index ba6be0dcf1..44f64f6d5c 100644 --- a/src/incremental/detectRenamedFunctions.ml +++ b/src/incremental/detectRenamedFunctions.ml @@ -1,11 +1,23 @@ open Cil -open MyCFG include CompareAST include CompareCFG module StringSet = Set.Make(String) type f = fundec * location +type v = varinfo * initinfo * location + +type globalElem = Fundec of fundec | GlobalVar of varinfo + +let globalElemName elem = match elem with + | Fundec(f) -> f.svar.vname + | GlobalVar(v) -> v.vname + +module GlobalElemForMap = struct + type t = globalElem + + let compare x y = String.compare (globalElemName x) (globalElemName y) +end module FundecForMap = struct type t = Cil.fundec @@ -16,19 +28,24 @@ end module FundecMap = Map.Make(FundecForMap) +module GlobalElemMap = Map.Make(GlobalElemForMap) + (*A dependency maps the function it depends on to the name the function has to be changed to*) type functionDependencies = string StringMap.t + (*Renamed: newName * dependencies; Modified=now*unchangedHeader*) -type functionStatus = SameName of fundec | Renamed of fundec | Created | Deleted | Modified of fundec * bool +type status = SameName of globalElem | Renamed of globalElem | Created | Deleted | Modified of globalElem * bool type outputFunctionStatus = Unchanged of global | UnchangedButRenamed of global | Added | Removed | Changed of global * bool type output = global * outputFunctionStatus -let pretty (f: functionStatus) = + + +let pretty (f: status) = match f with | SameName _ -> "SameName" - | Renamed x -> "Renamed to " ^ x.svar.vname + | Renamed x -> ("Renamed to " ^ globalElemName x) | Created -> "Added" | Deleted -> "Removed" | Modified _ -> "Changed" @@ -39,103 +56,170 @@ let printFundecMap elemToString map = begin ) (FundecMap.to_seq map) end -let getFunctionMap (ast: file) : f StringMap.t = - Cil.foldGlobals ast (fun map global -> +let getFunctionAndGVarMap (ast: file) : f StringMap.t * v StringMap.t = + Cil.foldGlobals ast (fun (functionMap, gvarMap) global -> match global with - | GFun (fundec, location) -> StringMap.add fundec.svar.vname (fundec, location) map - | _ -> map - ) StringMap.empty + | GFun (fundec, location) -> (StringMap.add fundec.svar.vname (fundec, location) functionMap, gvarMap) + | GVar (varinfo, initinfo, location) -> (functionMap, StringMap.add varinfo.vname (varinfo, initinfo, location) gvarMap) + | _ -> functionMap, gvarMap + ) (StringMap.empty, StringMap.empty) + let getDependencies fromEq = StringMap.map (fun assumption -> assumption.new_method_name) fromEq (*Data type that holds the important data while checking for renames. - statusForOldFunction: Status we have already figured out for a fundec from oldAST; - statusForNowFunction: see statusForOldFunction; - methodMapping: Mappings from (fundec of old AST) -> (fundec of now AST) we have already figured out to hold. - reverseMethodMapping: see method mapping, but from now -> old - *) + statusForOldElem: Status we have already figured out for a fundec from oldAST; + statusForNowElem: see statusForOldElem; + mapping: Mappings from (fundec of old AST) -> (fundec of now AST) we have already figured out to hold. + reversemapping: see method mapping, but from now -> old +*) type carryType = { - statusForOldFunction: functionStatus FundecMap.t; - statusForNowFunction: functionStatus FundecMap.t; - methodMapping: fundec FundecMap.t; - reverseMethodMapping: fundec FundecMap.t} + statusForOldElem: status GlobalElemMap.t; + statusForNowElem: status GlobalElemMap.t; + mapping: globalElem GlobalElemMap.t; + reverseMapping: globalElem GlobalElemMap.t; +} (*Carry type manipulation functions.*) let registerStatusForOldF f status data = - {statusForOldFunction = FundecMap.add f status data.statusForOldFunction; - statusForNowFunction=data.statusForNowFunction; - methodMapping=data.methodMapping; - reverseMethodMapping=data.reverseMethodMapping} + {statusForOldElem = GlobalElemMap.add f status data.statusForOldElem; + statusForNowElem=data.statusForNowElem; + mapping=data.mapping; + reverseMapping=data.reverseMapping; + } let registerStatusForNowF f status data = - {statusForOldFunction = data.statusForOldFunction; - statusForNowFunction=FundecMap.add f status data.statusForNowFunction; - methodMapping=data.methodMapping; - reverseMethodMapping=data.reverseMethodMapping} - -let registerBiStatus (oldF: fundec) (nowF: fundec) (status: functionStatus) data = - {statusForOldFunction=FundecMap.add oldF status data.statusForOldFunction; - statusForNowFunction=FundecMap.add nowF status data.statusForNowFunction; - methodMapping=data.methodMapping; - reverseMethodMapping=data.reverseMethodMapping} + {statusForOldElem = data.statusForOldElem; + statusForNowElem=GlobalElemMap.add f status data.statusForNowElem; + mapping=data.mapping; + reverseMapping=data.reverseMapping; + } + +let registerBiStatus (oldF: globalElem) (nowF: globalElem) (status: status) data = + {statusForOldElem=GlobalElemMap.add oldF status data.statusForOldElem; + statusForNowElem=GlobalElemMap.add nowF status data.statusForNowElem; + mapping=data.mapping; + reverseMapping=data.reverseMapping; + } let registerMapping oldF nowF data = - {statusForOldFunction=data.statusForOldFunction; - statusForNowFunction=data.statusForNowFunction; - methodMapping=FundecMap.add oldF nowF data.methodMapping; - reverseMethodMapping=FundecMap.add nowF oldF data.reverseMethodMapping} + {statusForOldElem=data.statusForOldElem; + statusForNowElem=data.statusForNowElem; + mapping=GlobalElemMap.add oldF nowF data.mapping; + reverseMapping=GlobalElemMap.add nowF oldF data.reverseMapping; + } -(*returns true iff for all dependencies it is true, that the dependency has a corresponding function with the new name and matches the without having dependencies itself and the new name is not already present on the old AST. *) -let doAllDependenciesMatch (dependencies: functionDependencies) (global_var_dependencies: glob_var_rename_assumptions) (oldFunctionMap: f StringMap.t) (newFunctionMap: f StringMap.t) (data: carryType) : bool * carryType = - StringMap.fold (fun oldName newName (allEqual, data) -> - (*Early cutoff if a previous dependency returned false or the newName is already present in the old map*) - if allEqual && not (StringMap.mem newName oldFunctionMap) then +let registerGVarMapping oldV nowV data = { + statusForOldElem=data.statusForOldElem; + statusForNowElem=data.statusForNowElem; + mapping=data.mapping; + reverseMapping=data.reverseMapping; +} - let (oldFundec, _) = StringMap.find oldName oldFunctionMap in - let knownMapping = FundecMap.find_opt oldFundec data.methodMapping in +(*returns true iff for all dependencies it is true, that the dependency has a corresponding function with the new name and matches the without having dependencies itself and the new name is not already present on the old AST. *) +let doAllDependenciesMatch (dependencies: functionDependencies) (global_var_dependencies: glob_var_rename_assumptions) (oldFunctionMap: f StringMap.t) (nowFunctionMap: f StringMap.t) (oldGVarMap: v StringMap.t) (nowGVarMap: v StringMap.t) (data: carryType) : bool * carryType = + + let isConsistent = fun old nowName allEqual getName getGlobal oldMap nowMap getNowOption data -> + (*Early cutoff if a previous dependency returned false. + We never create a mapping between globs where the now name was already part of the old set or the old name is part of the now set. + But only if now and old differ. + *) + if allEqual && (getName old = nowName || (not (StringMap.mem nowName oldMap) && not (StringMap.mem (getName old) nowMap))) then + let globalElem = getGlobal old in + let knownMapping = GlobalElemMap.find_opt globalElem data.mapping in + + (*To avoid inconsitencies, if a function has already been mapped to a function, that mapping is reused again.*) + match knownMapping with + | Some(knownElem) -> + (*This function has already been mapped*) + globalElemName knownElem = nowName, data + | None -> + let nowElemOption = getNowOption nowName in + + match nowElemOption with + | Some(nowElem) -> + let compare = fun old now -> + match (old, now) with + | Fundec(oF), Fundec(nF) -> + let doMatch, _, _, function_dependencies, global_var_dependencies = CompareGlobals.eqF oF nF None StringMap.empty VarinfoMap.empty in + doMatch, function_dependencies, global_var_dependencies + | GlobalVar(oV), GlobalVar(nV) -> + let (equal, (_, function_dependencies, global_var_dependencies)) = eq_varinfo oV nV emptyRenameMapping in + (*eq_varinfo always comes back with a self dependency. We need to filter that out.*) + equal, function_dependencies, (VarinfoMap.filter (fun vi name -> not (vi.vname = oV.vname && name = nowName)) global_var_dependencies) + | _, _ -> failwith "Unknown or incompatible global types" + in + + + let doMatch, function_dependencies, global_var_dependencies = compare globalElem nowElem in + + (*let _ = Printf.printf "%s <-> %s: %b %b %b\n" (getName old) (globalElemName nowElem) doMatch (StringMap.is_empty function_dependencies) (VarinfoMap.is_empty global_var_dependencies) in + + let _ = Printf.printf "%s\n" (rename_mapping_to_string (StringMap.empty, function_dependencies, global_var_dependencies)) in + *) + if doMatch && StringMap.is_empty function_dependencies && VarinfoMap.is_empty global_var_dependencies then + true, registerMapping globalElem nowElem data + else false, data - (*To avoid inconsitencies, if a function has already been mapped to a function, that mapping is reused again.*) - match knownMapping with - | Some(knownFundec) -> - (*This function has already been mapped*) - knownFundec.svar.vname = newName, data | None -> - let newFundecOption = StringMap.find_opt newName newFunctionMap in - - match newFundecOption with - | Some((newFundec, _)) -> - let doMatch, _, _, function_dependencies, global_var_dependencies = CompareGlobals.eqF oldFundec newFundec None StringMap.empty VarinfoMap.empty in - - if doMatch && StringMap.is_empty function_dependencies && VarinfoMap.is_empty global_var_dependencies then - true, registerMapping oldFundec newFundec data - else false, data + false, data + else false, data + in - | None -> false, data - else false, data - ) dependencies (true, data) + StringMap.fold (fun oldName nowName (allEqual, data) -> + let (old, _) = StringMap.find oldName oldFunctionMap in + isConsistent + old + nowName + allEqual + (fun x -> x.svar.vname) + (fun x -> Fundec(x)) + oldFunctionMap + nowFunctionMap + (fun x -> + Option.bind (StringMap.find_opt x nowFunctionMap) (fun (x, _) -> Some(Fundec(x))) + ) + data + ) dependencies (true, data) |> + VarinfoMap.fold (fun oldVarinfo nowName (allEqual, data) -> + isConsistent + oldVarinfo + nowName + allEqual + (fun x -> x.vname) + (fun x -> GlobalVar(x)) + oldGVarMap + nowGVarMap + (fun x -> + Option.bind (StringMap.find_opt x nowGVarMap) (fun (x, _, _) -> Some(GlobalVar(x))) + ) + data + ) + global_var_dependencies (*Check if f has already been assigned a status. If yes do nothing. If not, check if the function took part in the mapping, then register it to have been renamed. Otherwise register it as the supplied status.*) -let assignStatusToUnassignedFunction data f registerStatus statusMap mapping status = - if not (FundecMap.mem f statusMap) then - if (FundecMap.mem f mapping) then - registerStatus f (Renamed(FundecMap.find f mapping)) data +let assignStatusToUnassignedElem data f registerStatus statusMap mapping status = + if not (GlobalElemMap.mem f statusMap) then + if (GlobalElemMap.mem f mapping) then + registerStatus f (Renamed (GlobalElemMap.find f mapping)) data else (*this function has been added/removed*) registerStatus f status data else data -let detectRenamedFunctions (oldAST: file) (newAST: file) : output FundecMap.t = begin - let oldFunctionMap = getFunctionMap oldAST in - let nowFunctionMap = getFunctionMap newAST in +let detectRenamedFunctions (oldAST: file) (newAST: file) : output GlobalElemMap.t = begin + let oldFunctionMap, oldGVarMap = getFunctionAndGVarMap oldAST in + let nowFunctionMap, nowGVarMap = getFunctionAndGVarMap newAST in - let initialData: carryType = {statusForOldFunction = FundecMap.empty; - statusForNowFunction = FundecMap.empty; - methodMapping=FundecMap.empty; - reverseMethodMapping=FundecMap.empty} in + let initialData: carryType = {statusForOldElem = GlobalElemMap.empty; + statusForNowElem = GlobalElemMap.empty; + mapping=GlobalElemMap.empty; + reverseMapping=GlobalElemMap.empty; + } in (*Go through all functions, for all that have not been renamed *) let finalData = @@ -145,62 +229,80 @@ let detectRenamedFunctions (oldAST: file) (newAST: file) : output FundecMap.t = | Some (newFun, _) -> (*Compare if they are similar*) let doMatch, unchangedHeader, _, function_dependencies, global_var_dependencies = - CompareGlobals.eqF f newFun None StringMap.empty VarinfoMap.empty in + CompareGlobals.eqF f newFun None StringMap.empty VarinfoMap.empty in + + let _ = Pretty.printf "%s <-> %s: %b %s\n" f.svar.vname newFun.svar.vname doMatch (rename_mapping_to_string (StringMap.empty, function_dependencies, global_var_dependencies)) in + + let _ = Pretty.printf "old locals: %s\n" (String.concat ", " (List.map (fun x -> x.vname) f.slocals)) in + let _ = Pretty.printf "now locals: %s\n" (String.concat ", " (List.map (fun x -> x.vname) newFun.slocals)) in - let _ = Pretty.printf "%s\n" (rename_mapping_to_string (StringMap.empty, function_dependencies, global_var_dependencies)) in let actDependencies = getDependencies function_dependencies in + let oldG = Fundec(f) in + let nowG = Fundec(newFun) in + + if doMatch then - let doDependenciesMatch, updatedData = doAllDependenciesMatch actDependencies global_var_dependencies oldFunctionMap nowFunctionMap data in + let doDependenciesMatch, updatedData = doAllDependenciesMatch actDependencies global_var_dependencies oldFunctionMap nowFunctionMap oldGVarMap nowGVarMap data in if doDependenciesMatch then - registerBiStatus f newFun (SameName(newFun)) updatedData + registerBiStatus oldG nowG (SameName(oldG)) updatedData else - registerStatusForOldF f (Modified(newFun, unchangedHeader)) data |> - registerStatusForNowF newFun (Modified(f, unchangedHeader)) + registerStatusForOldF oldG (Modified(nowG, unchangedHeader)) data |> + registerStatusForNowF nowG (Modified(oldG, unchangedHeader)) else - registerStatusForOldF f (Modified(newFun, unchangedHeader)) data |> - registerStatusForNowF newFun (Modified(f, unchangedHeader)) + registerStatusForOldF oldG (Modified(nowG, unchangedHeader)) data |> + registerStatusForNowF nowG (Modified(oldG, unchangedHeader)) | None -> data ) oldFunctionMap initialData |> (*At this point we already know of the functions that have changed and stayed the same. We now assign the correct status to all the functions that have been mapped. The functions that have not been mapped are added/removed.*) (*Now go through all old functions again. Those who have not been assigned a status are removed*) StringMap.fold (fun _ (f, _) (data: carryType) -> - assignStatusToUnassignedFunction data f registerStatusForOldF data.statusForOldFunction data.methodMapping Deleted + assignStatusToUnassignedElem data (Fundec(f)) registerStatusForOldF data.statusForOldElem data.mapping Deleted ) oldFunctionMap |> (*now go through all new functions. Those have have not been assigned a mapping are added.*) StringMap.fold (fun _ (nowF, _) (data: carryType) -> - assignStatusToUnassignedFunction data nowF registerStatusForNowF data.statusForNowFunction data.reverseMethodMapping Created - ) nowFunctionMap - + assignStatusToUnassignedElem data (Fundec(nowF)) registerStatusForNowF data.statusForNowElem data.reverseMapping Created + ) nowFunctionMap |> + StringMap.fold (fun _ (v, _, _) data -> + assignStatusToUnassignedElem data (GlobalVar(v)) registerStatusForOldF data.statusForOldElem data.mapping Deleted + ) oldGVarMap |> + StringMap.fold (fun _ (nowV, _, _) (data: carryType) -> + assignStatusToUnassignedElem data (GlobalVar(nowV)) registerStatusForNowF data.statusForNowElem data.reverseMapping Created + ) nowGVarMap in (*Done with the analyis, the following just adjusts the output types.*) (*Map back to GFun and exposed function status*) - let extractOutput funMap invertedFunMap f (s: functionStatus) = - let getGFun f2 map = - let (f, l) = StringMap.find f2.svar.vname map in - GFun(f, l) + let extractOutput funMap invertedFunMap gvarMap invertedGvarMap f (s: status) = + let getGlobal gT fundecMap gVarMap = + match gT with + | Fundec(f2) -> + let (f, l) = StringMap.find f2.svar.vname fundecMap in + GFun(f, l) + | GlobalVar(v2) -> + let (v, i, l) = StringMap.find v2.vname gVarMap in + GVar(v, i, l) in let outputS = match s with - | SameName x -> Unchanged(getGFun x invertedFunMap) - | Renamed x -> UnchangedButRenamed(getGFun x invertedFunMap) + | SameName x -> Unchanged(getGlobal x invertedFunMap invertedGvarMap) + | Renamed x -> UnchangedButRenamed(getGlobal x invertedFunMap invertedGvarMap) | Created -> Added | Deleted -> Removed - | Modified (x, unchangedHeader) -> Changed(getGFun x invertedFunMap, unchangedHeader) + | Modified (x, unchangedHeader) -> Changed(getGlobal x invertedFunMap invertedGvarMap, unchangedHeader) in - getGFun f funMap, outputS + getGlobal f funMap gvarMap, outputS in (*Merge together old and now functions*) - FundecMap.merge (fun _ a b -> + GlobalElemMap.merge (fun _ a b -> if Option.is_some a then a else if Option.is_some b then b else None ) - (FundecMap.mapi (extractOutput oldFunctionMap nowFunctionMap) finalData.statusForOldFunction) - (FundecMap.mapi (extractOutput nowFunctionMap oldFunctionMap) finalData.statusForNowFunction) + (GlobalElemMap.mapi (extractOutput oldFunctionMap nowFunctionMap oldGVarMap nowGVarMap) finalData.statusForOldElem) + (GlobalElemMap.mapi (extractOutput nowFunctionMap oldFunctionMap nowGVarMap oldGVarMap) finalData.statusForNowElem) end diff --git a/tests/incremental/06-glob-var-rename/02-add_new_gvar.c b/tests/incremental/06-glob-var-rename/02-add_new_gvar.c new file mode 100644 index 0000000000..5efe319981 --- /dev/null +++ b/tests/incremental/06-glob-var-rename/02-add_new_gvar.c @@ -0,0 +1,8 @@ +#include + +int myVar = 1; + +int main() { + printf("%d", myVar); + printf("%d", myVar); +} diff --git a/tests/incremental/06-glob-var-rename/02-add_new_gvar.json b/tests/incremental/06-glob-var-rename/02-add_new_gvar.json new file mode 100644 index 0000000000..0db3279e44 --- /dev/null +++ b/tests/incremental/06-glob-var-rename/02-add_new_gvar.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/tests/incremental/06-glob-var-rename/02-add_new_gvar.patch b/tests/incremental/06-glob-var-rename/02-add_new_gvar.patch new file mode 100644 index 0000000000..f0c145107a --- /dev/null +++ b/tests/incremental/06-glob-var-rename/02-add_new_gvar.patch @@ -0,0 +1,13 @@ +--- tests/incremental/06-glob-var-rename/02-add_new_gvar.c ++++ tests/incremental/06-glob-var-rename/02-add_new_gvar.c +@@ -1,8 +1,9 @@ + #include + + int myVar = 1; ++int foo = 1; + + int main() { + printf("%d", myVar); +- printf("%d", myVar); ++ printf("%d", foo); + } diff --git a/tests/incremental/06-glob-var-rename/diffs/02-add_new_gvar.c b/tests/incremental/06-glob-var-rename/diffs/02-add_new_gvar.c new file mode 100644 index 0000000000..3841a59b11 --- /dev/null +++ b/tests/incremental/06-glob-var-rename/diffs/02-add_new_gvar.c @@ -0,0 +1,9 @@ +#include + +int myVar = 1; +int foo = 1; + +int main() { + printf("%d", myVar); + printf("%d", foo); +} From fae1e5e2964d5961aa371390e3360e6e9a14dba9 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 8 Jun 2022 14:07:17 +0200 Subject: [PATCH 31/90] Moved multiple incremental run tests to subdirectory. --- .../{ => multiple_incremental_runs}/08-2_incremental_runs.json | 0 .../{ => multiple_incremental_runs}/08-2_incremental_runs_1.c | 0 .../{ => multiple_incremental_runs}/08-2_incremental_runs_1.patch | 0 .../{ => multiple_incremental_runs}/08-2_incremental_runs_2.patch | 0 .../{ => multiple_incremental_runs}/09-2_ir_with_changes.json | 0 .../{ => multiple_incremental_runs}/09-2_ir_with_changes_1.c | 0 .../{ => multiple_incremental_runs}/09-2_ir_with_changes_1.patch | 0 .../{ => multiple_incremental_runs}/09-2_ir_with_changes_2.patch | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename tests/incremental/04-var-rename/{ => multiple_incremental_runs}/08-2_incremental_runs.json (100%) rename tests/incremental/04-var-rename/{ => multiple_incremental_runs}/08-2_incremental_runs_1.c (100%) rename tests/incremental/04-var-rename/{ => multiple_incremental_runs}/08-2_incremental_runs_1.patch (100%) rename tests/incremental/04-var-rename/{ => multiple_incremental_runs}/08-2_incremental_runs_2.patch (100%) rename tests/incremental/04-var-rename/{ => multiple_incremental_runs}/09-2_ir_with_changes.json (100%) rename tests/incremental/04-var-rename/{ => multiple_incremental_runs}/09-2_ir_with_changes_1.c (100%) rename tests/incremental/04-var-rename/{ => multiple_incremental_runs}/09-2_ir_with_changes_1.patch (100%) rename tests/incremental/04-var-rename/{ => multiple_incremental_runs}/09-2_ir_with_changes_2.patch (100%) diff --git a/tests/incremental/04-var-rename/08-2_incremental_runs.json b/tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs.json similarity index 100% rename from tests/incremental/04-var-rename/08-2_incremental_runs.json rename to tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs.json diff --git a/tests/incremental/04-var-rename/08-2_incremental_runs_1.c b/tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs_1.c similarity index 100% rename from tests/incremental/04-var-rename/08-2_incremental_runs_1.c rename to tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs_1.c diff --git a/tests/incremental/04-var-rename/08-2_incremental_runs_1.patch b/tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs_1.patch similarity index 100% rename from tests/incremental/04-var-rename/08-2_incremental_runs_1.patch rename to tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs_1.patch diff --git a/tests/incremental/04-var-rename/08-2_incremental_runs_2.patch b/tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs_2.patch similarity index 100% rename from tests/incremental/04-var-rename/08-2_incremental_runs_2.patch rename to tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs_2.patch diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes.json b/tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes.json similarity index 100% rename from tests/incremental/04-var-rename/09-2_ir_with_changes.json rename to tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes.json diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes_1.c b/tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes_1.c similarity index 100% rename from tests/incremental/04-var-rename/09-2_ir_with_changes_1.c rename to tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes_1.c diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes_1.patch b/tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes_1.patch similarity index 100% rename from tests/incremental/04-var-rename/09-2_ir_with_changes_1.patch rename to tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes_1.patch diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes_2.patch b/tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes_2.patch similarity index 100% rename from tests/incremental/04-var-rename/09-2_ir_with_changes_2.patch rename to tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes_2.patch From 0c6b9c42902ba3e12792311ff430b77f485bb6fb Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 8 Jun 2022 14:07:17 +0200 Subject: [PATCH 32/90] Moved multiple incremental run tests to subdirectory. --- .../{ => multiple_incremental_runs}/08-2_incremental_runs.json | 0 .../{ => multiple_incremental_runs}/08-2_incremental_runs_1.c | 0 .../{ => multiple_incremental_runs}/08-2_incremental_runs_1.patch | 0 .../{ => multiple_incremental_runs}/08-2_incremental_runs_2.patch | 0 .../{ => multiple_incremental_runs}/09-2_ir_with_changes.json | 0 .../{ => multiple_incremental_runs}/09-2_ir_with_changes_1.c | 0 .../{ => multiple_incremental_runs}/09-2_ir_with_changes_1.patch | 0 .../{ => multiple_incremental_runs}/09-2_ir_with_changes_2.patch | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename tests/incremental/04-var-rename/{ => multiple_incremental_runs}/08-2_incremental_runs.json (100%) rename tests/incremental/04-var-rename/{ => multiple_incremental_runs}/08-2_incremental_runs_1.c (100%) rename tests/incremental/04-var-rename/{ => multiple_incremental_runs}/08-2_incremental_runs_1.patch (100%) rename tests/incremental/04-var-rename/{ => multiple_incremental_runs}/08-2_incremental_runs_2.patch (100%) rename tests/incremental/04-var-rename/{ => multiple_incremental_runs}/09-2_ir_with_changes.json (100%) rename tests/incremental/04-var-rename/{ => multiple_incremental_runs}/09-2_ir_with_changes_1.c (100%) rename tests/incremental/04-var-rename/{ => multiple_incremental_runs}/09-2_ir_with_changes_1.patch (100%) rename tests/incremental/04-var-rename/{ => multiple_incremental_runs}/09-2_ir_with_changes_2.patch (100%) diff --git a/tests/incremental/04-var-rename/08-2_incremental_runs.json b/tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs.json similarity index 100% rename from tests/incremental/04-var-rename/08-2_incremental_runs.json rename to tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs.json diff --git a/tests/incremental/04-var-rename/08-2_incremental_runs_1.c b/tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs_1.c similarity index 100% rename from tests/incremental/04-var-rename/08-2_incremental_runs_1.c rename to tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs_1.c diff --git a/tests/incremental/04-var-rename/08-2_incremental_runs_1.patch b/tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs_1.patch similarity index 100% rename from tests/incremental/04-var-rename/08-2_incremental_runs_1.patch rename to tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs_1.patch diff --git a/tests/incremental/04-var-rename/08-2_incremental_runs_2.patch b/tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs_2.patch similarity index 100% rename from tests/incremental/04-var-rename/08-2_incremental_runs_2.patch rename to tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs_2.patch diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes.json b/tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes.json similarity index 100% rename from tests/incremental/04-var-rename/09-2_ir_with_changes.json rename to tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes.json diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes_1.c b/tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes_1.c similarity index 100% rename from tests/incremental/04-var-rename/09-2_ir_with_changes_1.c rename to tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes_1.c diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes_1.patch b/tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes_1.patch similarity index 100% rename from tests/incremental/04-var-rename/09-2_ir_with_changes_1.patch rename to tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes_1.patch diff --git a/tests/incremental/04-var-rename/09-2_ir_with_changes_2.patch b/tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes_2.patch similarity index 100% rename from tests/incremental/04-var-rename/09-2_ir_with_changes_2.patch rename to tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes_2.patch From a9a2e65d68b7e2b4689639a1e4e9c42f63316169 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Tue, 14 Jun 2022 19:31:58 +0200 Subject: [PATCH 33/90] Now also uses VarinfoMap for method rename assumptions --- src/incremental/compareAST.ml | 23 +++++++--------------- src/incremental/compareCIL.ml | 2 +- src/incremental/detectRenamedFunctions.ml | 24 ++++++++--------------- src/util/cilMaps.ml | 20 +++++++++++++++++++ 4 files changed, 36 insertions(+), 33 deletions(-) create mode 100644 src/util/cilMaps.ml diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 598bc03418..43388c9bad 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -1,4 +1,5 @@ open Cil +open CilMaps (* global_type and global_t are implicitly used by GlobalMap to keep GVarDecl apart from GVar and GFun, so do not remove! *) type global_type = Fun | Decl | Var @@ -7,24 +8,14 @@ and global_identifier = {name: string ; global_t: global_type} [@@deriving ord] module StringMap = Map.Make(String) -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) - type method_rename_assumption = {original_method_name: string; new_method_name: string; parameter_renames: string StringMap.t} -type method_rename_assumptions = method_rename_assumption StringMap.t +type method_rename_assumptions = method_rename_assumption VarinfoMap.t type glob_var_rename_assumptions = string VarinfoMap.t (*rename_mapping is carried through the stack when comparing the AST. Holds a list of rename assumptions.*) type rename_mapping = (string StringMap.t) * (method_rename_assumptions) * glob_var_rename_assumptions -let emptyRenameMapping = (StringMap.empty, StringMap.empty, VarinfoMap.empty) +let emptyRenameMapping = (StringMap.empty, VarinfoMap.empty, VarinfoMap.empty) (*Compares two names, being aware of the rename_mapping. Returns true iff: 1. there is a rename for name1 -> name2 = rename(name1) @@ -59,7 +50,7 @@ let string_tuple_to_string (tuple: (string * string) list) = "[" ^ (tuple |> let rename_mapping_to_string (rename_mapping: rename_mapping) = let (local, methods, glob_vars) = rename_mapping in let local_string = string_tuple_to_string (List.of_seq (StringMap.to_seq local)) in - let methods_string: string = List.of_seq (StringMap.to_seq methods |> Seq.map snd) |> + let methods_string: string = List.of_seq (VarinfoMap.to_seq methods |> Seq.map snd) |> List.map (fun x -> match x with {original_method_name; new_method_name; parameter_renames} -> "(methodName: " ^ original_method_name ^ " -> " ^ new_method_name ^ "; renamed_params=" ^ string_tuple_to_string (List.of_seq (StringMap.to_seq parameter_renames)) ^ ")") |> @@ -252,7 +243,7 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) : bool (*When we compare function names, we can directly compare the naming from the rename_mapping if it exists.*) let isNamingOk, updated_method_rename_mappings, updatedGlobVarMapping = match a.vtype, b.vtype with | TFun(_, aParamSpec, _, _), TFun(_, bParamSpec, _, _) -> ( - let specific_method_rename_mapping = StringMap.find_opt a.vname method_rename_mappings in + let specific_method_rename_mapping = VarinfoMap.find_opt a method_rename_mappings in match specific_method_rename_mapping with | Some method_rename_mapping -> let is_naming_ok = method_rename_mapping.original_method_name = a.vname && method_rename_mapping.new_method_name = b.vname in @@ -272,7 +263,7 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) : bool let assumption = {original_method_name = a.vname; new_method_name = b.vname; parameter_renames = create_locals_rename_mapping aParamNames bParamNames} in - true, StringMap.add a.vname assumption method_rename_mappings, glob_vars + true, VarinfoMap.add a assumption method_rename_mappings, glob_vars else true, method_rename_mappings, glob_vars ) | TInt (_, _), TInt (_, _) -> compare_local_and_global_var @@ -284,7 +275,7 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) : bool (*If the following is a method call, we need to check if we have a mapping for that method call. *) let typ_rename_mapping = match b.vtype with | TFun(_, _, _, _) -> ( - let new_locals = StringMap.find_opt a.vname updated_method_rename_mappings in + let new_locals = VarinfoMap.find_opt a updated_method_rename_mappings in match new_locals with | Some locals -> diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 0deca77de2..9e3475e665 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -9,7 +9,7 @@ let empty_change_info () : change_info = {added = []; removed = []; changed = [] let eq_glob (a: global) (b: global) (cfgs : (cfg * (cfg * cfg)) option) = match a, b with | GFun (f,_), GFun (g,_) -> - let identical, unchangedHeader, diffOpt, _, _ = CompareGlobals.eqF f g cfgs StringMap.empty VarinfoMap.empty in + let identical, unchangedHeader, diffOpt, _, _ = CompareGlobals.eqF f g cfgs VarinfoMap.empty VarinfoMap.empty in identical, unchangedHeader, diffOpt | GVar (x, init_x, _), GVar (y, init_y, _) -> eq_varinfo x y emptyRenameMapping |> fst, false, None (* ignore the init_info - a changed init of a global will lead to a different start state *) diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml index 44f64f6d5c..11222849a6 100644 --- a/src/incremental/detectRenamedFunctions.ml +++ b/src/incremental/detectRenamedFunctions.ml @@ -1,6 +1,7 @@ open Cil include CompareAST include CompareCFG +open CilMaps module StringSet = Set.Make(String) @@ -19,19 +20,10 @@ module GlobalElemForMap = struct let compare x y = String.compare (globalElemName x) (globalElemName y) end -module FundecForMap = struct - type t = Cil.fundec - - (*x.svar.uid cannot be used, as they may overlap between old and now AST*) - let compare x y = String.compare x.svar.vname y.svar.vname -end - -module FundecMap = Map.Make(FundecForMap) - module GlobalElemMap = Map.Make(GlobalElemForMap) (*A dependency maps the function it depends on to the name the function has to be changed to*) -type functionDependencies = string StringMap.t +type functionDependencies = string VarinfoMap.t (*Renamed: newName * dependencies; Modified=now*unchangedHeader*) @@ -65,7 +57,7 @@ let getFunctionAndGVarMap (ast: file) : f StringMap.t * v StringMap.t = ) (StringMap.empty, StringMap.empty) -let getDependencies fromEq = StringMap.map (fun assumption -> assumption.new_method_name) fromEq +let getDependencies fromEq = VarinfoMap.map (fun assumption -> assumption.new_method_name) fromEq (*Data type that holds the important data while checking for renames. statusForOldElem: Status we have already figured out for a fundec from oldAST; @@ -143,7 +135,7 @@ let doAllDependenciesMatch (dependencies: functionDependencies) (global_var_depe let compare = fun old now -> match (old, now) with | Fundec(oF), Fundec(nF) -> - let doMatch, _, _, function_dependencies, global_var_dependencies = CompareGlobals.eqF oF nF None StringMap.empty VarinfoMap.empty in + let doMatch, _, _, function_dependencies, global_var_dependencies = CompareGlobals.eqF oF nF None VarinfoMap.empty VarinfoMap.empty in doMatch, function_dependencies, global_var_dependencies | GlobalVar(oV), GlobalVar(nV) -> let (equal, (_, function_dependencies, global_var_dependencies)) = eq_varinfo oV nV emptyRenameMapping in @@ -159,7 +151,7 @@ let doAllDependenciesMatch (dependencies: functionDependencies) (global_var_depe let _ = Printf.printf "%s\n" (rename_mapping_to_string (StringMap.empty, function_dependencies, global_var_dependencies)) in *) - if doMatch && StringMap.is_empty function_dependencies && VarinfoMap.is_empty global_var_dependencies then + if doMatch && VarinfoMap.is_empty function_dependencies && VarinfoMap.is_empty global_var_dependencies then true, registerMapping globalElem nowElem data else false, data @@ -168,8 +160,8 @@ let doAllDependenciesMatch (dependencies: functionDependencies) (global_var_depe else false, data in - StringMap.fold (fun oldName nowName (allEqual, data) -> - let (old, _) = StringMap.find oldName oldFunctionMap in + VarinfoMap.fold (fun old nowName (allEqual, data) -> + let (old, _) = StringMap.find old.vname oldFunctionMap in isConsistent old nowName @@ -229,7 +221,7 @@ let detectRenamedFunctions (oldAST: file) (newAST: file) : output GlobalElemMap. | Some (newFun, _) -> (*Compare if they are similar*) let doMatch, unchangedHeader, _, function_dependencies, global_var_dependencies = - CompareGlobals.eqF f newFun None StringMap.empty VarinfoMap.empty in + CompareGlobals.eqF f newFun None VarinfoMap.empty VarinfoMap.empty in let _ = Pretty.printf "%s <-> %s: %b %s\n" f.svar.vname newFun.svar.vname doMatch (rename_mapping_to_string (StringMap.empty, function_dependencies, global_var_dependencies)) in diff --git a/src/util/cilMaps.ml b/src/util/cilMaps.ml new file mode 100644 index 0000000000..5a23328151 --- /dev/null +++ b/src/util/cilMaps.ml @@ -0,0 +1,20 @@ +open Cil + +module FundecForMap = struct + type t = Cil.fundec + + (*x.svar.uid cannot be used, as they may overlap between old and now AST*) + let compare x y = String.compare x.svar.vname y.svar.vname +end + +module FundecMap = Map.Make(FundecForMap) + +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) From ed605ff3fd0606248b71423a906da977b1d9ac88 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Tue, 14 Jun 2022 19:34:19 +0200 Subject: [PATCH 34/90] Now uses varinfo#vGlob to check if a variable is global --- src/incremental/compareAST.ml | 3 +-- src/incremental/compareCIL.ml | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 43388c9bad..a6af65fcde 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -226,8 +226,7 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) : bool let (locals_renames, method_rename_mappings, glob_vars) = rename_mapping in let compare_local_and_global_var = - let is_local = StringMap.mem a.vname locals_renames in - if not is_local then + if a.vglob then let present_mapping = VarinfoMap.find_opt a glob_vars in match present_mapping with diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 9e3475e665..4a9452dc4e 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -4,6 +4,7 @@ open CompareGlobals include DetectRenamedFunctions include CompareAST include CompareCFG +open CilMaps let empty_change_info () : change_info = {added = []; removed = []; changed = []; unchanged = []} From 3b60fb7754de74efa98cf1afbe45dd2535d5e208 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 15 Jun 2022 13:07:27 +0200 Subject: [PATCH 35/90] Removed unused currentFunctionName global state. --- src/framework/analyses.ml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 506d350d5a..5a8a1f51c9 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -8,8 +8,6 @@ open GobConfig module GU = Goblintutil module M = Messages -let currentFunctionName: string ref = ref "" - (** Analysis starts from lists of functions: start functions, exit functions, and * other functions. *) type fundecs = fundec list * fundec list * fundec list @@ -143,9 +141,6 @@ struct See: https://github.com/goblint/analyzer/issues/290#issuecomment-881258091. *) let loc = UpdateCil.getLoc n in - let parentNode = Node.find_fundec n in - currentFunctionName.contents <- RenameMapping.show_varinfo parentNode.svar; - BatPrintf.fprintf f "\n" (Node.show_id n) loc.file loc.line loc.byte loc.column; BatPrintf.fprintf f "%a\n" Range.printXml v in From e9494eb21a4fccf1c2e1ca61a6aa157b2960cb15 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 15 Jun 2022 13:10:14 +0200 Subject: [PATCH 36/90] Removed useless global state cherry pick --- src/framework/analyses.ml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 1b4969dba7..de19700109 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -8,8 +8,6 @@ open GobConfig module GU = Goblintutil module M = Messages -let currentFunctionName: string ref = ref "" - (** Analysis starts from lists of functions: start functions, exit functions, and * other functions. *) type fundecs = fundec list * fundec list * fundec list From 1eae9c326a5e8f45aa2740f8bf9ab1d010af6411 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 15 Jun 2022 13:18:37 +0200 Subject: [PATCH 37/90] Removed nothing test case --- tests/incremental/04-var-rename/00-unused_rename.patch | 8 -------- tests/incremental/04-var-rename/01-nothing.c | 4 ---- tests/incremental/04-var-rename/01-nothing.json | 3 --- tests/incremental/04-var-rename/01-nothing.patch | 8 -------- .../{00-unused_rename.c => 01-unused_rename.c} | 0 .../{00-unused_rename.json => 01-unused_rename.json} | 0 tests/incremental/04-var-rename/01-unused_rename.patch | 8 ++++++++ tests/incremental/04-var-rename/diffs/01-nothing.c | 5 ----- .../diffs/{00-unused_rename.c => 01-unused_rename.c} | 0 9 files changed, 8 insertions(+), 28 deletions(-) delete mode 100644 tests/incremental/04-var-rename/00-unused_rename.patch delete mode 100644 tests/incremental/04-var-rename/01-nothing.c delete mode 100644 tests/incremental/04-var-rename/01-nothing.json delete mode 100644 tests/incremental/04-var-rename/01-nothing.patch rename tests/incremental/04-var-rename/{00-unused_rename.c => 01-unused_rename.c} (100%) rename tests/incremental/04-var-rename/{00-unused_rename.json => 01-unused_rename.json} (100%) create mode 100644 tests/incremental/04-var-rename/01-unused_rename.patch delete mode 100644 tests/incremental/04-var-rename/diffs/01-nothing.c rename tests/incremental/04-var-rename/diffs/{00-unused_rename.c => 01-unused_rename.c} (100%) diff --git a/tests/incremental/04-var-rename/00-unused_rename.patch b/tests/incremental/04-var-rename/00-unused_rename.patch deleted file mode 100644 index d3d15e3bc7..0000000000 --- a/tests/incremental/04-var-rename/00-unused_rename.patch +++ /dev/null @@ -1,8 +0,0 @@ ---- tests/incremental/04-var-rename/00-unused_rename.c -+++ tests/incremental/04-var-rename/00-unused_rename.c -@@ -1,4 +1,4 @@ - int main() { -- int a = 0; -+ int b = 0; - return 0; - } diff --git a/tests/incremental/04-var-rename/01-nothing.c b/tests/incremental/04-var-rename/01-nothing.c deleted file mode 100644 index 3dc9c8f6e6..0000000000 --- a/tests/incremental/04-var-rename/01-nothing.c +++ /dev/null @@ -1,4 +0,0 @@ -int main() { - int x = 0; - return 0; -} diff --git a/tests/incremental/04-var-rename/01-nothing.json b/tests/incremental/04-var-rename/01-nothing.json deleted file mode 100644 index 544b7b4ddd..0000000000 --- a/tests/incremental/04-var-rename/01-nothing.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/01-nothing.patch b/tests/incremental/04-var-rename/01-nothing.patch deleted file mode 100644 index 663c19abfc..0000000000 --- a/tests/incremental/04-var-rename/01-nothing.patch +++ /dev/null @@ -1,8 +0,0 @@ ---- tests/incremental/04-var-rename/01-nothing.c -+++ tests/incremental/04-var-rename/01-nothing.c -@@ -1,4 +1,5 @@ - int main() { - int x = 0; -+ - return 0; - } diff --git a/tests/incremental/04-var-rename/00-unused_rename.c b/tests/incremental/04-var-rename/01-unused_rename.c similarity index 100% rename from tests/incremental/04-var-rename/00-unused_rename.c rename to tests/incremental/04-var-rename/01-unused_rename.c diff --git a/tests/incremental/04-var-rename/00-unused_rename.json b/tests/incremental/04-var-rename/01-unused_rename.json similarity index 100% rename from tests/incremental/04-var-rename/00-unused_rename.json rename to tests/incremental/04-var-rename/01-unused_rename.json diff --git a/tests/incremental/04-var-rename/01-unused_rename.patch b/tests/incremental/04-var-rename/01-unused_rename.patch new file mode 100644 index 0000000000..977470ad53 --- /dev/null +++ b/tests/incremental/04-var-rename/01-unused_rename.patch @@ -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; + } diff --git a/tests/incremental/04-var-rename/diffs/01-nothing.c b/tests/incremental/04-var-rename/diffs/01-nothing.c deleted file mode 100644 index 3c9e6cafd7..0000000000 --- a/tests/incremental/04-var-rename/diffs/01-nothing.c +++ /dev/null @@ -1,5 +0,0 @@ -int main() { - int x = 0; - - return 0; -} diff --git a/tests/incremental/04-var-rename/diffs/00-unused_rename.c b/tests/incremental/04-var-rename/diffs/01-unused_rename.c similarity index 100% rename from tests/incremental/04-var-rename/diffs/00-unused_rename.c rename to tests/incremental/04-var-rename/diffs/01-unused_rename.c From 192129905f66a52b9e0c56a8775333250be5d4df Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 15 Jun 2022 13:19:12 +0200 Subject: [PATCH 38/90] Fixed analysis.ml --- src/framework/analyses.ml | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index de19700109..355344d89e 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -151,9 +151,6 @@ struct See: https://github.com/goblint/analyzer/issues/290#issuecomment-881258091. *) let loc = UpdateCil.getLoc n in - let parentNode = Node.find_fundec n in - currentFunctionName.contents <- parentNode.svar.vname; - BatPrintf.fprintf f "\n" (Node.show_id n) loc.file loc.line loc.byte loc.column; BatPrintf.fprintf f "%a\n" Range.printXml v in From fd691c5570a19d92551b84b7155acac988584558 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 15 Jun 2022 13:18:37 +0200 Subject: [PATCH 39/90] Removed nothing test case --- tests/incremental/04-var-rename/00-unused_rename.patch | 8 -------- tests/incremental/04-var-rename/01-nothing.c | 4 ---- tests/incremental/04-var-rename/01-nothing.json | 3 --- tests/incremental/04-var-rename/01-nothing.patch | 8 -------- .../{00-unused_rename.c => 01-unused_rename.c} | 0 .../{00-unused_rename.json => 01-unused_rename.json} | 0 tests/incremental/04-var-rename/01-unused_rename.patch | 8 ++++++++ tests/incremental/04-var-rename/diffs/01-nothing.c | 5 ----- .../diffs/{00-unused_rename.c => 01-unused_rename.c} | 0 9 files changed, 8 insertions(+), 28 deletions(-) delete mode 100644 tests/incremental/04-var-rename/00-unused_rename.patch delete mode 100644 tests/incremental/04-var-rename/01-nothing.c delete mode 100644 tests/incremental/04-var-rename/01-nothing.json delete mode 100644 tests/incremental/04-var-rename/01-nothing.patch rename tests/incremental/04-var-rename/{00-unused_rename.c => 01-unused_rename.c} (100%) rename tests/incremental/04-var-rename/{00-unused_rename.json => 01-unused_rename.json} (100%) create mode 100644 tests/incremental/04-var-rename/01-unused_rename.patch delete mode 100644 tests/incremental/04-var-rename/diffs/01-nothing.c rename tests/incremental/04-var-rename/diffs/{00-unused_rename.c => 01-unused_rename.c} (100%) diff --git a/tests/incremental/04-var-rename/00-unused_rename.patch b/tests/incremental/04-var-rename/00-unused_rename.patch deleted file mode 100644 index d3d15e3bc7..0000000000 --- a/tests/incremental/04-var-rename/00-unused_rename.patch +++ /dev/null @@ -1,8 +0,0 @@ ---- tests/incremental/04-var-rename/00-unused_rename.c -+++ tests/incremental/04-var-rename/00-unused_rename.c -@@ -1,4 +1,4 @@ - int main() { -- int a = 0; -+ int b = 0; - return 0; - } diff --git a/tests/incremental/04-var-rename/01-nothing.c b/tests/incremental/04-var-rename/01-nothing.c deleted file mode 100644 index 3dc9c8f6e6..0000000000 --- a/tests/incremental/04-var-rename/01-nothing.c +++ /dev/null @@ -1,4 +0,0 @@ -int main() { - int x = 0; - return 0; -} diff --git a/tests/incremental/04-var-rename/01-nothing.json b/tests/incremental/04-var-rename/01-nothing.json deleted file mode 100644 index 544b7b4ddd..0000000000 --- a/tests/incremental/04-var-rename/01-nothing.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/01-nothing.patch b/tests/incremental/04-var-rename/01-nothing.patch deleted file mode 100644 index 663c19abfc..0000000000 --- a/tests/incremental/04-var-rename/01-nothing.patch +++ /dev/null @@ -1,8 +0,0 @@ ---- tests/incremental/04-var-rename/01-nothing.c -+++ tests/incremental/04-var-rename/01-nothing.c -@@ -1,4 +1,5 @@ - int main() { - int x = 0; -+ - return 0; - } diff --git a/tests/incremental/04-var-rename/00-unused_rename.c b/tests/incremental/04-var-rename/01-unused_rename.c similarity index 100% rename from tests/incremental/04-var-rename/00-unused_rename.c rename to tests/incremental/04-var-rename/01-unused_rename.c diff --git a/tests/incremental/04-var-rename/00-unused_rename.json b/tests/incremental/04-var-rename/01-unused_rename.json similarity index 100% rename from tests/incremental/04-var-rename/00-unused_rename.json rename to tests/incremental/04-var-rename/01-unused_rename.json diff --git a/tests/incremental/04-var-rename/01-unused_rename.patch b/tests/incremental/04-var-rename/01-unused_rename.patch new file mode 100644 index 0000000000..977470ad53 --- /dev/null +++ b/tests/incremental/04-var-rename/01-unused_rename.patch @@ -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; + } diff --git a/tests/incremental/04-var-rename/diffs/01-nothing.c b/tests/incremental/04-var-rename/diffs/01-nothing.c deleted file mode 100644 index 3c9e6cafd7..0000000000 --- a/tests/incremental/04-var-rename/diffs/01-nothing.c +++ /dev/null @@ -1,5 +0,0 @@ -int main() { - int x = 0; - - return 0; -} diff --git a/tests/incremental/04-var-rename/diffs/00-unused_rename.c b/tests/incremental/04-var-rename/diffs/01-unused_rename.c similarity index 100% rename from tests/incremental/04-var-rename/diffs/00-unused_rename.c rename to tests/incremental/04-var-rename/diffs/01-unused_rename.c From 686a3da496760afd2320eb825e396f06f81ff62e Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 15 Jun 2022 13:48:29 +0200 Subject: [PATCH 40/90] Added include assert to tests. Removed useless test.c --- .../incremental/04-var-rename/04-renamed_assert.c | 2 ++ .../04-var-rename/diffs/04-renamed_assert.c | 2 ++ .../08-2_incremental_runs_1.c | 2 ++ .../09-2_ir_with_changes_1.c | 2 ++ tests/incremental/04-var-rename/test.c | 15 --------------- 5 files changed, 8 insertions(+), 15 deletions(-) delete mode 100644 tests/incremental/04-var-rename/test.c diff --git a/tests/incremental/04-var-rename/04-renamed_assert.c b/tests/incremental/04-var-rename/04-renamed_assert.c index 55d83e7229..4a4a9e7f21 100644 --- a/tests/incremental/04-var-rename/04-renamed_assert.c +++ b/tests/incremental/04-var-rename/04-renamed_assert.c @@ -1,3 +1,5 @@ +#include + int main() { int myVar = 0; diff --git a/tests/incremental/04-var-rename/diffs/04-renamed_assert.c b/tests/incremental/04-var-rename/diffs/04-renamed_assert.c index 8f74e36a13..642609580c 100644 --- a/tests/incremental/04-var-rename/diffs/04-renamed_assert.c +++ b/tests/incremental/04-var-rename/diffs/04-renamed_assert.c @@ -1,3 +1,5 @@ +#include + int main() { int j = 0; diff --git a/tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs_1.c b/tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs_1.c index d9b5afdd19..e522ad239a 100644 --- a/tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs_1.c +++ b/tests/incremental/04-var-rename/multiple_incremental_runs/08-2_incremental_runs_1.c @@ -1,3 +1,5 @@ +#include + int main() { int varFirstIteration = 0; diff --git a/tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes_1.c b/tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes_1.c index 535d3c21fc..e50f6d9beb 100644 --- a/tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes_1.c +++ b/tests/incremental/04-var-rename/multiple_incremental_runs/09-2_ir_with_changes_1.c @@ -1,3 +1,5 @@ +#include + void foo() { int fooOne = 1; fooOne++; diff --git a/tests/incremental/04-var-rename/test.c b/tests/incremental/04-var-rename/test.c deleted file mode 100644 index f51eb0d6f7..0000000000 --- a/tests/incremental/04-var-rename/test.c +++ /dev/null @@ -1,15 +0,0 @@ -void foo() { - int i = 0; - - for(int i = 0; i < 10; i++); -} - -void bar() { - int i = 0; -} - -int main() { - foo(); - bar(); - return 0; -} \ No newline at end of file From 0a0ee34aea61364808da4be56542a68dc7cd2a27 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 15 Jun 2022 13:49:45 +0200 Subject: [PATCH 41/90] Replaced tupletostring with fancy syntax. --- src/incremental/compareAST.ml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 2b72de178e..42e019b86d 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -26,17 +26,13 @@ let rename_mapping_aware_name_comparison (name1: string) (name2: string) (rename (*Printf.printf "No assumption when %s, %s, %b\n" name1 name2 (name1 = name2);*) name1 = name2 (*Var names differ, but there is no assumption, so this can't be good*) -let string_tuple_to_string (tuple: (string * string) list) = "[" ^ (tuple |> - List.map (fun x -> match x with (first, second) -> "(" ^ first ^ " -> " ^ second ^ ")") |> - String.concat ", ") ^ "]" - let rename_mapping_to_string (rename_mapping: rename_mapping) = let (local, methods) = rename_mapping in - let local_string = string_tuple_to_string (List.of_seq (Hashtbl.to_seq local)) in + let local_string = [%show: (string * string) list] (List.of_seq (Hashtbl.to_seq local)) in let methods_string: string = List.of_seq (Hashtbl.to_seq_values methods) |> List.map (fun x -> match x with {original_method_name; new_method_name; parameter_renames} -> "(methodName: " ^ original_method_name ^ " -> " ^ new_method_name ^ - "; renamed_params=" ^ string_tuple_to_string (List.of_seq (Hashtbl.to_seq parameter_renames)) ^ ")") |> + "; renamed_params=" ^ [%show: (string * string) list] (List.of_seq (Hashtbl.to_seq parameter_renames)) ^ ")") |> String.concat ", " in "(local=" ^ local_string ^ "; methods=[" ^ methods_string ^ "])" From 8b28e892257fc473331fd57da797775f752237ca Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 15 Jun 2022 13:52:56 +0200 Subject: [PATCH 42/90] Hashtbl.add is now replaced by Hashtbl.replace in many places. --- src/incremental/compareCIL.ml | 52 +++++++++++++++++------------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 77fd210f73..197f61e123 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -41,21 +41,21 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo 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.add local_rename_map a b); + 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, string) Hashtbl.t) = match alocals, blocals with - | [], [] -> true, rename_mapping - | origLocal :: als, nowLocal :: bls -> - if origLocal.vname <> nowLocal.vname then Hashtbl.add rename_mapping origLocal.vname nowLocal.vname; + | [], [] -> true, rename_mapping + | origLocal :: als, nowLocal :: bls -> + if origLocal.vname <> nowLocal.vname then Hashtbl.replace rename_mapping origLocal.vname nowLocal.vname; - (*TODO: maybe optimize this with eq_varinfo*) - rename_mapping_aware_compare als bls rename_mapping - | _, _ -> false, rename_mapping - in + (*TODO: maybe optimize this with eq_varinfo*) + rename_mapping_aware_compare als bls rename_mapping + | _, _ -> false, rename_mapping + in let headerSizeEqual, headerRenameMapping = rename_mapping_aware_compare a.sformals b.sformals (Hashtbl.create 0) in let actHeaderRenameMapping = (headerRenameMapping, global_rename_mapping) in @@ -104,22 +104,22 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = let old_global = GlobalMap.find ident map in match old_global, global with - | GFun(f, _), GFun (g, _) -> - let renamed_params: (string, string) Hashtbl.t = if (List.length f.sformals) = (List.length g.sformals) then + | GFun(f, _), GFun (g, _) -> + let renamed_params: (string, string) Hashtbl.t = if (List.length f.sformals) = (List.length g.sformals) then 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)) |> - (fun list -> - let table: (string, string) Hashtbl.t = Hashtbl.create (List.length list) in - List.iter (fun mapping -> Hashtbl.add table (fst mapping) (snd mapping)) list; - table + (fun list -> + let table: (string, string) Hashtbl.t = Hashtbl.create (List.length list) in + List.iter (fun mapping -> Hashtbl.add table (fst mapping) (snd mapping)) list; + table ) else Hashtbl.create 0 in - if not (f.svar.vname = g.svar.vname) || (Hashtbl.length renamed_params) > 0 then - Some {original_method_name=f.svar.vname; new_method_name=g.svar.vname; parameter_renames=renamed_params} - else None - | _, _ -> None + if not (f.svar.vname = g.svar.vname) || (Hashtbl.length renamed_params) > 0 then + Some {original_method_name=f.svar.vname; new_method_name=g.svar.vname; parameter_renames=renamed_params} + else None + | _, _ -> None with Not_found -> None in @@ -158,15 +158,15 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = 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 list) global -> - match generate_global_rename_mapping oldMap global with + match generate_global_rename_mapping oldMap global with | Some rename_mapping -> current_global_rename_mapping @ [rename_mapping] | None -> current_global_rename_mapping - ) [] |> - (fun mappings -> - let table = Hashtbl.create (List.length mappings) in - List.iter (fun mapping -> Hashtbl.add table mapping.original_method_name mapping) mappings; - table - ) in + ) [] |> + (fun mappings -> + let table = Hashtbl.create (List.length mappings) in + List.iter (fun mapping -> Hashtbl.replace table mapping.original_method_name mapping) mappings; + table + ) 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. *) From 77bd92632fca62725fe90bd16b89e95741b4061e Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 15 Jun 2022 13:55:21 +0200 Subject: [PATCH 43/90] List optimization. --- src/incremental/compareCIL.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 197f61e123..780bfaccf3 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -159,7 +159,7 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = let global_rename_mapping: method_rename_assumptions = Cil.foldGlobals newAST (fun (current_global_rename_mapping: method_rename_assumption list) global -> match generate_global_rename_mapping oldMap global with - | Some rename_mapping -> current_global_rename_mapping @ [rename_mapping] + | Some rename_mapping -> rename_mapping::current_global_rename_mapping | None -> current_global_rename_mapping ) [] |> (fun mappings -> From 938fcb0bc02943ceef3cc3d0f128dd9b7b15ad8a Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Fri, 17 Jun 2022 15:50:16 +0200 Subject: [PATCH 44/90] Removed compinfo and enum rename hack from compareAST and replaced it with a functional alternative --- src/incremental/compareAST.ml | 56 ++++++++++++++++------- src/incremental/compareCIL.ml | 5 +- src/incremental/compareGlobals.ml | 12 ++--- src/incremental/detectRenamedFunctions.ml | 35 ++++++++++---- src/incremental/updateCil.ml | 2 + 5 files changed, 78 insertions(+), 32 deletions(-) diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 598bc03418..4e017707d8 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -21,16 +21,19 @@ type method_rename_assumption = {original_method_name: string; new_method_name: type method_rename_assumptions = method_rename_assumption StringMap.t type glob_var_rename_assumptions = string VarinfoMap.t +(*On a successful match, these compinfo and enuminfo names have to be set to the snd element of the tuple. *) +type renamesOnSuccess = (compinfo * compinfo) list * (enuminfo * enuminfo) list + (*rename_mapping is carried through the stack when comparing the AST. Holds a list of rename assumptions.*) -type rename_mapping = (string StringMap.t) * (method_rename_assumptions) * glob_var_rename_assumptions +type rename_mapping = (string StringMap.t) * (method_rename_assumptions) * glob_var_rename_assumptions * renamesOnSuccess -let emptyRenameMapping = (StringMap.empty, StringMap.empty, VarinfoMap.empty) +let emptyRenameMapping: rename_mapping = (StringMap.empty, StringMap.empty, VarinfoMap.empty, ([], [])) (*Compares two names, being aware of the rename_mapping. Returns true iff: 1. there is a rename for name1 -> name2 = rename(name1) 2. there is no rename for name1 -> name1 = name2*) let rename_mapping_aware_name_comparison (name1: string) (name2: string) (rename_mapping: rename_mapping) = - let (local_c, method_c, _) = rename_mapping in + let (local_c, method_c, _, _) = rename_mapping in let existingAssumption: string option = StringMap.find_opt name1 local_c in match existingAssumption with @@ -57,7 +60,7 @@ let string_tuple_to_string (tuple: (string * string) list) = "[" ^ (tuple |> String.concat ", ") ^ "]" let rename_mapping_to_string (rename_mapping: rename_mapping) = - let (local, methods, glob_vars) = rename_mapping in + let (local, methods, glob_vars, _) = rename_mapping in let local_string = string_tuple_to_string (List.of_seq (StringMap.to_seq local)) in let methods_string: string = List.of_seq (StringMap.to_seq methods |> Seq.map snd) |> List.map (fun x -> match x with {original_method_name; new_method_name; parameter_renames} -> @@ -139,6 +142,21 @@ and mem_typ_acc (a: typ) (b: typ) acc = List.exists (fun p -> match p with (x, y and pretty_length () l = Pretty.num (List.length l) and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (rename_mapping: rename_mapping) : bool * rename_mapping = + (* Registers a compinfo rename or a enum rename*) + let register_rename_on_success = fun rename_mapping compinfo_option enum_option -> + let maybeAddTuple = fun list option -> + Option.value ~default:list (Option.bind option (fun elem -> Some(elem :: list))) + in + + let (a, b, c, renames_on_success) = rename_mapping in + let (compinfoRenames, enumRenames) = renames_on_success in + + let updatedCompinfoRenames = maybeAddTuple compinfoRenames compinfo_option in + let updatedEnumRenames = maybeAddTuple enumRenames enum_option in + + 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 *) let r, updated_rename_mapping = match a, b with | TPtr (typ1, attr1), TPtr (typ2, attr2) -> @@ -167,16 +185,22 @@ and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (rename_mapping: rename else ( let acc = (a, b) :: acc in let (res, rm) = eq_compinfo compinfo1 compinfo2 acc rename_mapping &&>> forward_list_equal eq_attribute attr1 attr2 in - if res && compinfo1.cname <> compinfo2.cname then - compinfo2.cname <- compinfo1.cname; + let updated_rm: rename_mapping = if res && compinfo1.cname <> compinfo2.cname then + (* This renaming now only takes place when the comparison was successful.*) + (*compinfo2.cname <- compinfo1.cname;*) + + register_rename_on_success rm (Some((compinfo2, compinfo1))) None + else rm + in if res then global_typ_acc := (a, b) :: !global_typ_acc; - res, rm + res, updated_rm ) | TEnum (enuminfo1, attr1), TEnum (enuminfo2, attr2) -> let (res, rm) = eq_enuminfo enuminfo1 enuminfo2 rename_mapping &&>> forward_list_equal eq_attribute attr1 attr2 in - (if res && enuminfo1.ename <> enuminfo2.ename then enuminfo2.ename <- enuminfo1.ename); - res, rm + if res && enuminfo1.ename <> enuminfo2.ename then + res, register_rename_on_success rm None (Some((enuminfo2, enuminfo1))) + else res, rm | TBuiltin_va_list attr1, TBuiltin_va_list attr2 -> forward_list_equal eq_attribute attr1 attr2 rename_mapping | TVoid attr1, TVoid attr2 -> forward_list_equal eq_attribute attr1 attr2 rename_mapping | TInt (ik1, attr1), TInt (ik2, attr2) -> (ik1 = ik2, rename_mapping) &&>> forward_list_equal eq_attribute attr1 attr2 @@ -232,7 +256,7 @@ and eq_varinfo2 (rename_mapping: rename_mapping) (a: varinfo) (b: varinfo) = eq_ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) : bool * rename_mapping = (*Printf.printf "Comp %s with %s\n" a.vname b.vname;*) - let (locals_renames, method_rename_mappings, glob_vars) = rename_mapping in + let (locals_renames, method_rename_mappings, glob_vars, renames_on_success) = rename_mapping in let compare_local_and_global_var = let is_local = StringMap.mem a.vname locals_renames in @@ -288,8 +312,8 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) : bool match new_locals with | Some locals -> - (locals.parameter_renames, updated_method_rename_mappings, updatedGlobVarMapping) - | None -> (StringMap.empty, updated_method_rename_mappings, updatedGlobVarMapping) + (locals.parameter_renames, updated_method_rename_mappings, updatedGlobVarMapping, renames_on_success) + | None -> (StringMap.empty, updated_method_rename_mappings, updatedGlobVarMapping, renames_on_success) ) (*| GVar (_, _, _) -> ( let new_local = VarinfoMap.find_opt a glob_vars in @@ -298,13 +322,13 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) : bool | Some now_name -> (StringMap.add a.vname now_name StringMap.empty, updated_method_rename_mappings, updatedGlobVarMapping) | None -> (StringMap.empty, updated_method_rename_mappings, updatedGlobVarMapping) )*) - | _ -> (locals_renames, updated_method_rename_mappings, updatedGlobVarMapping) + | _ -> (locals_renames, updated_method_rename_mappings, updatedGlobVarMapping, renames_on_success) in - (*Ignore rename mapping for type check, as it doesn't change anyway*) - let (typeCheck, _) = eq_typ a.vtype b.vtype typ_rename_mapping in + (*Ignore rename mapping for type check, as it doesn't change anyway. We only need the renames_on_success*) + let (typeCheck, (_, _, _, updated_renames_on_success)) = eq_typ a.vtype b.vtype typ_rename_mapping in - (isNamingOk && typeCheck, (locals_renames, updated_method_rename_mappings, updatedGlobVarMapping)) &&>> + (isNamingOk && typeCheck, (locals_renames, updated_method_rename_mappings, updatedGlobVarMapping, updated_renames_on_success)) &&>> forward_list_equal eq_attribute a.vattr b.vattr &&> (a.vstorage = b.vstorage) &&> (a.vglob = b.vglob) &&> (a.vaddrof = b.vaddrof) (* Ignore the location, vid, vreferenced, vdescr, vdescrpure, vinline *) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 0deca77de2..7acb59b258 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -9,7 +9,10 @@ let empty_change_info () : change_info = {added = []; removed = []; changed = [] let eq_glob (a: global) (b: global) (cfgs : (cfg * (cfg * cfg)) option) = match a, b with | GFun (f,_), GFun (g,_) -> - let identical, unchangedHeader, diffOpt, _, _ = CompareGlobals.eqF f g cfgs StringMap.empty VarinfoMap.empty in + let identical, unchangedHeader, diffOpt, _, _, renamesOnSuccess = CompareGlobals.eqF f g cfgs StringMap.empty VarinfoMap.empty in + (*Perform renames no matter what.*) + let _ = performRenames renamesOnSuccess in + identical, unchangedHeader, diffOpt | GVar (x, init_x, _), GVar (y, init_y, _) -> eq_varinfo x y emptyRenameMapping |> fst, false, None (* ignore the init_info - a changed init of a global will lead to a different start state *) diff --git a/src/incremental/compareGlobals.ml b/src/incremental/compareGlobals.ml index 76a98bd58e..cac98eefda 100644 --- a/src/incremental/compareGlobals.ml +++ b/src/incremental/compareGlobals.ml @@ -56,18 +56,18 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo in let headerSizeEqual, headerRenameMapping = rename_mapping_aware_compare a.sformals b.sformals (StringMap.empty) in - let actHeaderRenameMapping = (headerRenameMapping, global_function_rename_mapping, global_var_rename_mapping) in + let actHeaderRenameMapping: rename_mapping = (headerRenameMapping, global_function_rename_mapping, global_var_rename_mapping, ([], [])) in - let unchangedHeader = eq_varinfo a.svar b.svar actHeaderRenameMapping &&>> forward_list_equal eq_varinfo a.sformals b.sformals in - let identical, diffOpt, (_, renamed_method_dependencies, renamed_global_vars_dependencies) = + let (unchangedHeader, (_, _, _, renamesOnSuccessHeader)) = eq_varinfo a.svar b.svar actHeaderRenameMapping &&>> forward_list_equal eq_varinfo a.sformals b.sformals in + let identical, diffOpt, (_, renamed_method_dependencies, renamed_global_vars_dependencies, renamesOnSuccess) = if should_reanalyze a then false, None, emptyRenameMapping else (* 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_function_rename_mapping, global_var_rename_mapping) in + let rename_mapping: rename_mapping = (local_rename, global_function_rename_mapping, global_var_rename_mapping, renamesOnSuccessHeader) in - let sameDef = unchangedHeader &&> sizeEqual |> fst in + let sameDef = unchangedHeader && sizeEqual in if not sameDef then (false, None, emptyRenameMapping) else @@ -82,4 +82,4 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo if diffNodes1 = [] then (true, None, emptyRenameMapping) else (false, Some {unchangedNodes = matches; primObsoleteNodes = diffNodes1}, emptyRenameMapping) in - identical, unchangedHeader |> fst, diffOpt, renamed_method_dependencies, renamed_global_vars_dependencies + identical, unchangedHeader, diffOpt, renamed_method_dependencies, renamed_global_vars_dependencies, renamesOnSuccess diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml index 44f64f6d5c..f0fc490257 100644 --- a/src/incremental/detectRenamedFunctions.ml +++ b/src/incremental/detectRenamedFunctions.ml @@ -64,6 +64,12 @@ let getFunctionAndGVarMap (ast: file) : f StringMap.t * v StringMap.t = | _ -> functionMap, gvarMap ) (StringMap.empty, StringMap.empty) +let performRenames (renamesOnSuccess: renamesOnSuccess) = + begin + let (compinfoRenames, enumRenames) = renamesOnSuccess in + List.iter (fun (compinfo2, compinfo1) -> compinfo2.cname <- compinfo1.cname) compinfoRenames; + List.iter (fun (enum2, enum1) -> enum2.ename <- enum1.ename) enumRenames; + end let getDependencies fromEq = StringMap.map (fun assumption -> assumption.new_method_name) fromEq @@ -119,7 +125,12 @@ let registerGVarMapping oldV nowV data = { (*returns true iff for all dependencies it is true, that the dependency has a corresponding function with the new name and matches the without having dependencies itself and the new name is not already present on the old AST. *) -let doAllDependenciesMatch (dependencies: functionDependencies) (global_var_dependencies: glob_var_rename_assumptions) (oldFunctionMap: f StringMap.t) (nowFunctionMap: f StringMap.t) (oldGVarMap: v StringMap.t) (nowGVarMap: v StringMap.t) (data: carryType) : bool * carryType = +let doAllDependenciesMatch (dependencies: functionDependencies) +(global_var_dependencies: glob_var_rename_assumptions) +(oldFunctionMap: f StringMap.t) +(nowFunctionMap: f StringMap.t) +(oldGVarMap: v StringMap.t) +(nowGVarMap: v StringMap.t) (data: carryType) : bool * carryType = let isConsistent = fun old nowName allEqual getName getGlobal oldMap nowMap getNowOption data -> (*Early cutoff if a previous dependency returned false. @@ -143,23 +154,24 @@ let doAllDependenciesMatch (dependencies: functionDependencies) (global_var_depe let compare = fun old now -> match (old, now) with | Fundec(oF), Fundec(nF) -> - let doMatch, _, _, function_dependencies, global_var_dependencies = CompareGlobals.eqF oF nF None StringMap.empty VarinfoMap.empty in - doMatch, function_dependencies, global_var_dependencies + let doMatch, _, _, function_dependencies, global_var_dependencies, renamesOnSuccess = CompareGlobals.eqF oF nF None StringMap.empty VarinfoMap.empty in + doMatch, function_dependencies, global_var_dependencies, renamesOnSuccess | GlobalVar(oV), GlobalVar(nV) -> - let (equal, (_, function_dependencies, global_var_dependencies)) = eq_varinfo oV nV emptyRenameMapping in + let (equal, (_, function_dependencies, global_var_dependencies, renamesOnSuccess)) = eq_varinfo oV nV emptyRenameMapping in (*eq_varinfo always comes back with a self dependency. We need to filter that out.*) - equal, function_dependencies, (VarinfoMap.filter (fun vi name -> not (vi.vname = oV.vname && name = nowName)) global_var_dependencies) + equal, function_dependencies, (VarinfoMap.filter (fun vi name -> not (vi.vname = oV.vname && name = nowName)) global_var_dependencies), renamesOnSuccess | _, _ -> failwith "Unknown or incompatible global types" in - let doMatch, function_dependencies, global_var_dependencies = compare globalElem nowElem in + let doMatch, function_dependencies, global_var_dependencies, renamesOnSuccess = compare globalElem nowElem in (*let _ = Printf.printf "%s <-> %s: %b %b %b\n" (getName old) (globalElemName nowElem) doMatch (StringMap.is_empty function_dependencies) (VarinfoMap.is_empty global_var_dependencies) in let _ = Printf.printf "%s\n" (rename_mapping_to_string (StringMap.empty, function_dependencies, global_var_dependencies)) in *) if doMatch && StringMap.is_empty function_dependencies && VarinfoMap.is_empty global_var_dependencies then + let _ = performRenames renamesOnSuccess in true, registerMapping globalElem nowElem data else false, data @@ -228,13 +240,18 @@ let detectRenamedFunctions (oldAST: file) (newAST: file) : output GlobalElemMap. match matchingNewFundec with | Some (newFun, _) -> (*Compare if they are similar*) - let doMatch, unchangedHeader, _, function_dependencies, global_var_dependencies = + let doMatch, unchangedHeader, _, function_dependencies, global_var_dependencies, renamesOnSuccess = CompareGlobals.eqF f newFun None StringMap.empty VarinfoMap.empty in - let _ = Pretty.printf "%s <-> %s: %b %s\n" f.svar.vname newFun.svar.vname doMatch (rename_mapping_to_string (StringMap.empty, function_dependencies, global_var_dependencies)) in + (*Before renamesOnSuccess, functions with the same name have always been compared. + In this comparison, the renaming on compinfo and enum was always performed, no matter if the comparison + was a success or not. This call mimics this behaviour.*) + let _ = performRenames renamesOnSuccess in + + (*let _ = Pretty.printf "%s <-> %s: %b %s\n" f.svar.vname newFun.svar.vname doMatch (rename_mapping_to_string (StringMap.empty, function_dependencies, global_var_dependencies)) in let _ = Pretty.printf "old locals: %s\n" (String.concat ", " (List.map (fun x -> x.vname) f.slocals)) in - let _ = Pretty.printf "now locals: %s\n" (String.concat ", " (List.map (fun x -> x.vname) newFun.slocals)) in + let _ = Pretty.printf "now locals: %s\n" (String.concat ", " (List.map (fun x -> x.vname) newFun.slocals)) in*) let actDependencies = getDependencies function_dependencies in diff --git a/src/incremental/updateCil.ml b/src/incremental/updateCil.ml index aa2df5447a..5aa756804a 100644 --- a/src/incremental/updateCil.ml +++ b/src/incremental/updateCil.ml @@ -42,6 +42,7 @@ let update_ids (old_file: file) (ids: max_ids) (new_file: file) (changes: change target.svar <- src.svar; in let reset_fun (f: fundec) (old_f: fundec) = + old_f.svar.vname <- f.svar.vname; f.svar.vid <- old_f.svar.vid; List.iter2 (fun l o_l -> l.vid <- o_l.vid; o_l.vname <- l.vname) f.slocals old_f.slocals; List.iter2 (fun lo o_f -> lo.vid <- o_f.vid) f.sformals old_f.sformals; @@ -58,6 +59,7 @@ let update_ids (old_file: file) (ids: max_ids) (new_file: file) (changes: change in let reset_var (v: varinfo) (old_v: varinfo)= v.vid <- old_v.vid; + old_v.vname <- v.vname; update_vid_max v.vid; in let reset_globals (glob: unchanged_global) = From 7371dc97fb56692403273856c200c42204f16381 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Mon, 20 Jun 2022 15:32:25 +0200 Subject: [PATCH 45/90] method_rename_assumptions now uses varinfo map instead of string hashtable. --- src/incremental/compareAST.ml | 61 ++++++++++++++++++----------------- src/incremental/compareCFG.ml | 7 ++-- src/incremental/compareCIL.ml | 50 +++++++++++++--------------- src/util/cilMaps.ml | 11 +++++++ 4 files changed, 70 insertions(+), 59 deletions(-) create mode 100644 src/util/cilMaps.ml diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 42e019b86d..e8e17d8f97 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -1,22 +1,25 @@ open Cil +open CilMaps (* global_type and global_t are implicitly used by GlobalMap to keep GVarDecl apart from GVar and GFun, so do not remove! *) type global_type = Fun | Decl | Var and global_identifier = {name: string ; global_t: global_type} [@@deriving ord] -type method_rename_assumption = {original_method_name: string; new_method_name: string; parameter_renames: (string, string) Hashtbl.t} -type method_rename_assumptions = (string, method_rename_assumption) Hashtbl.t +module StringMap = Map.Make(String) + +type method_rename_assumption = {original_method_name: string; new_method_name: string; parameter_renames: string StringMap.t} +type method_rename_assumptions = method_rename_assumption VarinfoMap.t (*rename_mapping is carried through the stack when comparing the AST. Holds a list of rename assumptions.*) -type rename_mapping = ((string, string) Hashtbl.t) * (method_rename_assumptions) +type rename_mapping = (string StringMap.t) * (method_rename_assumptions) (*Compares two names, being aware of the rename_mapping. Returns true iff: - 1. there is a rename for name1 -> name2 = rename(name1) - 2. there is no rename for name1 -> name1 = name2*) + 1. there is a rename for name1 -> name2 = rename(name1) + 2. there is no rename for name1 -> name1 = name2*) let rename_mapping_aware_name_comparison (name1: string) (name2: string) (rename_mapping: rename_mapping) = let (local_c, method_c) = rename_mapping in - let existingAssumption: string option = Hashtbl.find_opt local_c name1 in + let existingAssumption: string option = StringMap.find_opt name1 local_c in match existingAssumption with | Some now -> @@ -28,12 +31,12 @@ let rename_mapping_aware_name_comparison (name1: string) (name2: string) (rename let rename_mapping_to_string (rename_mapping: rename_mapping) = let (local, methods) = rename_mapping in - let local_string = [%show: (string * string) list] (List.of_seq (Hashtbl.to_seq local)) in - let methods_string: string = List.of_seq (Hashtbl.to_seq_values methods) |> - List.map (fun x -> match x with {original_method_name; new_method_name; parameter_renames} -> - "(methodName: " ^ original_method_name ^ " -> " ^ new_method_name ^ - "; renamed_params=" ^ [%show: (string * string) list] (List.of_seq (Hashtbl.to_seq parameter_renames)) ^ ")") |> - String.concat ", " in + let local_string = [%show: (string * string) list] (List.of_seq (StringMap.to_seq local)) in + let methods_string: string = List.of_seq (VarinfoMap.to_seq methods |> Seq.map snd) |> + List.map (fun x -> match x with {original_method_name; new_method_name; parameter_renames} -> + "(methodName: " ^ original_method_name ^ " -> " ^ new_method_name ^ + "; renamed_params=" ^ [%show: (string * string) list] (List.of_seq (StringMap.to_seq parameter_renames)) ^ ")") |> + String.concat ", " in "(local=" ^ local_string ^ "; methods=[" ^ methods_string ^ "])" let identifier_of_global glob = @@ -101,7 +104,7 @@ and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (rename_mapping: rename | TArray (typ1, None, attr1), TArray (typ2, None, attr2) -> eq_typ_acc typ1 typ2 acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 | TFun (typ1, (Some list1), varArg1, attr1), TFun (typ2, (Some list2), varArg2, attr2) -> eq_typ_acc typ1 typ2 acc rename_mapping && GobList.equal (eq_args rename_mapping acc) list1 list2 && varArg1 = varArg2 && - GobList.equal (eq_attribute rename_mapping) attr1 attr2 + GobList.equal (eq_attribute rename_mapping) attr1 attr2 | TFun (typ1, None, varArg1, attr1), TFun (typ2, None, varArg2, attr2) -> eq_typ_acc typ1 typ2 acc rename_mapping && varArg1 = varArg2 && GobList.equal (eq_attribute rename_mapping) attr1 attr2 @@ -147,7 +150,7 @@ and eq_enuminfo (a: enuminfo) (b: enuminfo) (rename_mapping: rename_mapping) = and eq_args (rename_mapping: rename_mapping) (acc: (typ * typ) list) (a: string * typ * attributes) (b: string * typ * attributes) = match a, b with (name1, typ1, attr1), (name2, typ2, attr2) -> - rename_mapping_aware_name_comparison name1 name2 rename_mapping && eq_typ_acc typ1 typ2 acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 + rename_mapping_aware_name_comparison name1 name2 rename_mapping && eq_typ_acc typ1 typ2 acc rename_mapping && GobList.equal (eq_attribute rename_mapping) attr1 attr2 and eq_attrparam (rename_mapping: rename_mapping) (a: attrparam) (b: attrparam) = match a, b with | ACons (str1, attrparams1), ACons (str2, attrparams2) -> str1 = str2 && GobList.equal (eq_attrparam rename_mapping) attrparams1 attrparams2 @@ -179,37 +182,37 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) = (*When we compare function names, we can directly compare the naming from the rename_mapping if it exists.*) let isNamingOk = match b.vtype with | TFun(_, _, _, _) -> ( - let specific_method_rename_mapping = Hashtbl.find_opt method_rename_mappings a.vname in + let specific_method_rename_mapping = VarinfoMap.find_opt a method_rename_mappings in match specific_method_rename_mapping with - | Some method_rename_mapping -> method_rename_mapping.original_method_name = a.vname && method_rename_mapping.new_method_name = b.vname - | None -> a.vname = b.vname + | Some method_rename_mapping -> method_rename_mapping.original_method_name = a.vname && method_rename_mapping.new_method_name = b.vname + | None -> a.vname = b.vname ) | _ -> rename_mapping_aware_name_comparison a.vname b.vname rename_mapping - in + in (*If the following is a method call, we need to check if we have a mapping for that method call. *) let typ_rename_mapping = match b.vtype with - | TFun(_, _, _, _) -> ( - let new_locals = Hashtbl.find_opt method_rename_mappings a.vname in + | TFun(_, _, _, _) -> ( + let new_locals = VarinfoMap.find_opt a method_rename_mappings in match new_locals with - | Some locals -> - (*Printf.printf "Performing rename_mapping switch. New rename_mapping=%s\n" (rename_mapping_to_string (locals.parameter_renames, method_rename_mappings));*) - (locals.parameter_renames, method_rename_mappings) - | None -> (Hashtbl.create 0, method_rename_mappings) - ) - | _ -> rename_mapping - in + | Some locals -> + (*Printf.printf "Performing rename_mapping switch. New rename_mapping=%s\n" (rename_mapping_to_string (locals.parameter_renames, method_rename_mappings));*) + (locals.parameter_renames, method_rename_mappings) + | None -> (StringMap.empty, method_rename_mappings) + ) + | _ -> rename_mapping + in let typeCheck = eq_typ a.vtype b.vtype typ_rename_mapping in let attrCheck = GobList.equal (eq_attribute rename_mapping) a.vattr b.vattr in - (*let _ = if isNamingOk then a.vname <- b.vname in*) + (*let _ = if isNamingOk then a.vname <- b.vname in*) (*let _ = Printf.printf "Comparing vars: %s = %s\n" a.vname b.vname in *) (*a.vname = b.vname*) let result = isNamingOk && typeCheck && attrCheck && - a.vstorage = b.vstorage && a.vglob = b.vglob && a.vaddrof = b.vaddrof in + a.vstorage = b.vstorage && a.vglob = b.vglob && a.vaddrof = b.vaddrof in result (* Ignore the location, vid, vreferenced, vdescr, vdescrpure, vinline *) diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index 4557cb88b3..78a182a291 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -1,10 +1,11 @@ open MyCFG open Queue open Cil +open CilMaps include CompareAST let eq_node (x, fun1) (y, fun2) = - let empty_rename_mapping: rename_mapping = (Hashtbl.create 0, Hashtbl.create 0) in + let empty_rename_mapping: rename_mapping = (StringMap.empty, VarinfoMap.empty) in match x,y with | Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) empty_rename_mapping | Function f1, Function f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping @@ -12,8 +13,8 @@ let eq_node (x, fun1) (y, fun2) = | _ -> false (* TODO: compare ASMs properly instead of simply always assuming that they are not the same *) -let eq_edge x y = - let empty_rename_mapping: rename_mapping = (Hashtbl.create 0, Hashtbl.create 0) in +let eq_edge x y = + let empty_rename_mapping: rename_mapping = (StringMap.empty, VarinfoMap.empty) in match x, y with | Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 empty_rename_mapping && eq_exp rv1 rv2 empty_rename_mapping | Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 empty_rename_mapping && GobList.equal (eq_exp2 empty_rename_mapping) ars1 ars2 diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 780bfaccf3..474cbcb5b3 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -1,5 +1,6 @@ open Cil open MyCFG +open CilMaps include CompareAST include CompareCFG @@ -47,17 +48,17 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo (* 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, string) Hashtbl.t) = match alocals, blocals with + 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 -> - if origLocal.vname <> nowLocal.vname then Hashtbl.replace rename_mapping origLocal.vname nowLocal.vname; + 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 rename_mapping + rename_mapping_aware_compare als bls new_mapping | _, _ -> false, rename_mapping in - let headerSizeEqual, headerRenameMapping = rename_mapping_aware_compare a.sformals b.sformals (Hashtbl.create 0) 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 @@ -89,8 +90,8 @@ let eq_glob (a: global) (b: global) (cfgs : (cfg * (cfg * cfg)) option) (global_ let identical, unchangedHeader, diffOpt = eqF f g cfgs global_rename_mapping in identical, unchangedHeader, diffOpt - | GVar (x, init_x, _), GVar (y, init_y, _) -> eq_varinfo x y (Hashtbl.create 0, Hashtbl.create 0), 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 (Hashtbl.create 0, Hashtbl.create 0), false, None + | 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) = @@ -105,19 +106,18 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = match old_global, global with | GFun(f, _), GFun (g, _) -> - let renamed_params: (string, string) Hashtbl.t = if (List.length f.sformals) = (List.length g.sformals) then - 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)) |> - (fun list -> - let table: (string, string) Hashtbl.t = Hashtbl.create (List.length list) in - List.iter (fun mapping -> Hashtbl.add table (fst mapping) (snd mapping)) list; - table - ) - else Hashtbl.create 0 in - - if not (f.svar.vname = g.svar.vname) || (Hashtbl.length renamed_params) > 0 then - Some {original_method_name=f.svar.vname; new_method_name=g.svar.vname; parameter_renames=renamed_params} + 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 @@ -157,16 +157,12 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = 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 list) global -> + 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 rename_mapping -> rename_mapping::current_global_rename_mapping + | Some (funVar, rename_mapping) -> VarinfoMap.add funVar rename_mapping current_global_rename_mapping | None -> current_global_rename_mapping - ) [] |> - (fun mappings -> - let table = Hashtbl.create (List.length mappings) in - List.iter (fun mapping -> Hashtbl.replace table mapping.original_method_name mapping) mappings; - table - ) in + ) 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. *) diff --git a/src/util/cilMaps.ml b/src/util/cilMaps.ml new file mode 100644 index 0000000000..d776020fc2 --- /dev/null +++ b/src/util/cilMaps.ml @@ -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) From 7b320c993fd001d480d279a5d796b483fb50303b Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Mon, 20 Jun 2022 16:03:23 +0200 Subject: [PATCH 46/90] Removed RenameMapping. --- src/analyses/apron/apronAnalysis.apron.ml | 2 +- src/analyses/arinc.ml | 14 ++-- src/analyses/spec.ml | 2 +- src/cdomains/basetype.ml | 4 +- src/cdomains/lval.ml | 4 +- src/cdomains/symbLocksDomain.ml | 2 +- src/framework/analyses.ml | 14 ++-- src/framework/constraints.ml | 6 +- src/framework/node.ml | 16 ++-- src/incremental/renameMapping.ml | 94 ----------------------- 10 files changed, 32 insertions(+), 126 deletions(-) delete mode 100644 src/incremental/renameMapping.ml diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 3dacf59ab6..09b86e9615 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -146,7 +146,7 @@ struct if !GU.global_initialization && e = MyCFG.unknown_exp then st (* ignore extern inits because there's no body before assign, so the apron env is empty... *) else ( - if M.tracing then M.traceli "apron" "assign %a = %a\n" RenameMapping.d_lval lv d_exp e; + if M.tracing then M.traceli "apron" "assign %a = %a\n" d_lval lv d_exp 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 e (fun apr' e' -> diff --git a/src/analyses/arinc.ml b/src/analyses/arinc.ml index 79974d8434..8397e8d63e 100644 --- a/src/analyses/arinc.ml +++ b/src/analyses/arinc.ml @@ -137,7 +137,7 @@ struct let return_code_is_success z = Cilint.is_zero_cilint z || Cilint.compare_cilint z Cilint.one_cilint = 0 let str_return_code i = if return_code_is_success i then "SUCCESS" else "ERROR" let str_return_dlval (v,o as dlval) = - sprint RenameMapping.d_lval (Lval.CilLval.to_lval dlval) ^ "_" ^ string_of_int v.vdecl.line |> + sprint d_lval (Lval.CilLval.to_lval dlval) ^ "_" ^ string_of_int v.vdecl.line |> Str.global_replace (Str.regexp "[^a-zA-Z0-9]") "_" let add_return_dlval env kind dlval = ArincUtil.add_return_var env.procid kind (str_return_dlval dlval) @@ -152,17 +152,17 @@ struct | a when not (Queries.LS.is_top a) && Queries.LS.cardinal a > 0 -> let top_elt = (dummyFunDec.svar, `NoOffset) in let a' = if Queries.LS.mem top_elt a then ( - M.debug "mayPointTo: query result for %a contains TOP!" RenameMapping.d_exp exp; (* UNSOUND *) + M.debug "mayPointTo: query result for %a contains TOP!" d_exp exp; (* UNSOUND *) Queries.LS.remove top_elt a ) else a in Queries.LS.elements a' | v -> - M.debug "mayPointTo: query result for %a is %a" RenameMapping.d_exp exp Queries.LS.pretty v; + M.debug "mayPointTo: query result for %a is %a" d_exp exp Queries.LS.pretty v; (*failwith "mayPointTo"*) [] let iterMayPointTo ctx exp f = mayPointTo ctx exp |> List.iter f - let debugMayPointTo ctx exp = M.debug "%a mayPointTo %a" RenameMapping.d_exp exp (Pretty.d_list ", " Lval.CilLval.pretty) (mayPointTo ctx exp) + let debugMayPointTo ctx exp = M.debug "%a mayPointTo %a" d_exp exp (Pretty.d_list ", " Lval.CilLval.pretty) (mayPointTo ctx exp) (* transfer functions *) @@ -184,7 +184,7 @@ struct let edges_added = ref false in let f dlval = (* M.debug @@ "assign: MayPointTo " ^ sprint d_plainlval lval ^ ": " ^ sprint d_plainexp (Lval.CilLval.to_exp dlval); *) - let is_ret_type = try is_return_code_type @@ Lval.CilLval.to_exp dlval with Cilfacade.TypeOfError Index_NonArray -> M.debug "assign: Cilfacade.typeOf %a threw exception Errormsg.Error \"Bug: typeOffset: Index on a non-array\". Will assume this is a return type to remain sound." RenameMapping.d_exp (Lval.CilLval.to_exp dlval); true in + let is_ret_type = try is_return_code_type @@ Lval.CilLval.to_exp dlval with Cilfacade.TypeOfError Index_NonArray -> M.debug "assign: Cilfacade.typeOf %a threw exception Errormsg.Error \"Bug: typeOffset: Index on a non-array\". Will assume this is a return type to remain sound." d_exp (Lval.CilLval.to_exp dlval); true in if (not is_ret_type) || Lval.CilLval.has_index dlval then () else let dlval = global_dlval dlval "assign" in edges_added := true; @@ -320,7 +320,7 @@ struct let is_creating_fun = startsWith (Functions.prefix^"Create") f.vname in if M.tracing && is_arinc_fun then ( (* M.tracel "arinc" "found %s(%s)\n" f.vname args_str *) - M.debug "found %s(%a) in %s" f.vname (Pretty.d_list ", " RenameMapping.d_exp) arglist env.fundec.svar.vname + M.debug "found %s(%a) in %s" f.vname (Pretty.d_list ", " d_exp) arglist env.fundec.svar.vname ); let is_error_handler = env.pname = pname_ErrorHandler in let eval_int exp = @@ -339,7 +339,7 @@ struct (* call assign for all analyses (we only need base)! *) | AddrOf lval -> ctx.emit (Assign {lval; exp = mkAddrOf @@ var id}) (* TODO not needed for the given code, but we could use Queries.MayPointTo exp in this case *) - | _ -> failwith @@ "Could not assign id. Expected &id. Found "^sprint RenameMapping.d_exp exp + | _ -> failwith @@ "Could not assign id. Expected &id. Found "^sprint d_exp exp in let assign_id_by_name resource_type name id = assign_id id (get_id (resource_type, eval_str name)) diff --git a/src/analyses/spec.ml b/src/analyses/spec.ml index 9fcfd7bb61..38be505f5d 100644 --- a/src/analyses/spec.ml +++ b/src/analyses/spec.ml @@ -256,7 +256,7 @@ struct 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 "assign (none in D): %a = %a [%a]" RenameMapping.d_lval lval d_exp rval d_plainexp rval; + M.debug "assign (none in D): %a = %a [%a]" d_lval lval d_exp rval d_plainexp rval; m (* diff --git a/src/cdomains/basetype.ml b/src/cdomains/basetype.ml index 263e61f130..d2c5f2b3be 100644 --- a/src/cdomains/basetype.ml +++ b/src/cdomains/basetype.ml @@ -26,8 +26,8 @@ struct let show x = if RichVarinfo.BiVarinfoMap.Collection.mem_varinfo x then let description = RichVarinfo.BiVarinfoMap.Collection.describe_varinfo x in - "(" ^ RenameMapping.show_varinfo x ^ ", " ^ description ^ ")" - else RenameMapping.show_varinfo x + "(" ^ x.vname ^ ", " ^ description ^ ")" + else x.vname let pretty () x = Pretty.text (show x) type group = Global | Local | Parameter | Temp [@@deriving show { with_path = false }] let (%) = Batteries.(%) diff --git a/src/cdomains/lval.ml b/src/cdomains/lval.ml index 6d10606f26..d13cbe9f93 100644 --- a/src/cdomains/lval.ml +++ b/src/cdomains/lval.ml @@ -223,8 +223,8 @@ struct let short_addr (x, o) = if RichVarinfo.BiVarinfoMap.Collection.mem_varinfo x then let description = RichVarinfo.BiVarinfoMap.Collection.describe_varinfo x in - "(" ^ RenameMapping.show_varinfo x ^ ", " ^ description ^ ")" ^ short_offs o - else RenameMapping.show_varinfo x ^ short_offs o + "(" ^ x.vname ^ ", " ^ description ^ ")" ^ short_offs o + else x.vname ^ short_offs o let show = function | Addr (x, o)-> short_addr (x, o) diff --git a/src/cdomains/symbLocksDomain.ml b/src/cdomains/symbLocksDomain.ml index 454b7c666e..1471749871 100644 --- a/src/cdomains/symbLocksDomain.ml +++ b/src/cdomains/symbLocksDomain.ml @@ -184,7 +184,7 @@ struct let ee_to_str x = match x with - | EVar v -> RenameMapping.show_varinfo v + | EVar v -> v.vname | EAddr -> "&" | EDeref -> "*" | EField f -> f.fname diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 5a8a1f51c9..63e149ef7a 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -31,7 +31,7 @@ struct let printXml f n = let l = Node.location n in - BatPrintf.fprintf f "\n" (Node.show_id n) l.file (RenameMapping.show_varinfo (Node.find_fundec n).svar) l.line l.byte l.column + BatPrintf.fprintf f "\n" (Node.show_id n) l.file (Node.find_fundec n).svar.vname l.line l.byte l.column let var_id = Node.show_id end @@ -105,7 +105,7 @@ struct See: https://github.com/goblint/analyzer/issues/290#issuecomment-881258091. *) let x = UpdateCil.getLoc a in let f = Node.find_fundec a in - CilType.Location.show x ^ "(" ^ RenameMapping.show_varinfo f.svar ^ ")" + CilType.Location.show x ^ "(" ^ f.svar.vname ^ ")" include Printable.SimpleShow ( struct @@ -179,9 +179,9 @@ struct let module SH = BatHashtbl.Make (Basetype.RawStrings) in let file2funs = SH.create 100 in let funs2node = SH.create 100 in - iter (fun n _ -> SH.add funs2node (RenameMapping.show_varinfo (Node.find_fundec n).svar) n) (Lazy.force table); + iter (fun n _ -> SH.add funs2node (Node.find_fundec n).svar.vname n) (Lazy.force table); iterGlobals file (function - | GFun (fd,loc) -> SH.add file2funs loc.file (RenameMapping.show_varinfo fd.svar) + | GFun (fd,loc) -> SH.add file2funs loc.file fd.svar.vname | _ -> () ); let p_node f n = BatPrintf.fprintf f "%s" (Node.show_id n) in @@ -227,9 +227,9 @@ struct let module SH = BatHashtbl.Make (Basetype.RawStrings) in let file2funs = SH.create 100 in let funs2node = SH.create 100 in - iter (fun n _ -> SH.add funs2node (RenameMapping.show_varinfo (Node.find_fundec n).svar) n) (Lazy.force table); + iter (fun n _ -> SH.add funs2node (Node.find_fundec n).svar.vname n) (Lazy.force table); iterGlobals file (function - | GFun (fd,loc) -> SH.add file2funs loc.file (RenameMapping.show_varinfo fd.svar) + | GFun (fd,loc) -> SH.add file2funs loc.file fd.svar.vname | _ -> () ); let p_enum p f xs = BatEnum.print ~first:"[\n " ~last:"\n]" ~sep:",\n " p f xs in @@ -519,7 +519,7 @@ struct your analysis to be path sensitive, do override this. To obtain a behavior where all paths are kept apart, set this to D.equal x y *) - let call_descr f _ = RenameMapping.show_varinfo f.svar + let call_descr f _ = f.svar.vname (* prettier name for equation variables --- currently base can do this and MCP just forwards it to Base.*) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index aac3e5b644..209f89423e 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -363,7 +363,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" (RenameMapping.show_varinfo f.svar)); + 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); v_new, M.add f.svar v_new m ) else @@ -501,7 +501,7 @@ struct ignore (getl (Function fd, c)) | exception Not_found -> (* unknown function *) - M.error ~category:Imprecise ~tags:[Category Unsound] "Created a thread from unknown function %s" (RenameMapping.show_varinfo f) + M.error ~category:Imprecise ~tags:[Category Unsound] "Created a thread from unknown function %s" f.vname (* actual implementation (e.g. invalidation) is done by threadenter *) ) ds in @@ -631,7 +631,7 @@ struct let one_function f = match Cilfacade.find_varinfo_fundec f with | fd when LibraryFunctions.use_special f.vname -> - M.warn "Using special for defined function %s" (RenameMapping.show_varinfo f); + M.warn "Using special for defined function %s" f.vname; tf_special_call ctx lv f args | fd -> tf_normal_call ctx lv e fd args getl sidel getg sideg diff --git a/src/framework/node.ml b/src/framework/node.ml index cc1d32a018..84f8dea1ea 100644 --- a/src/framework/node.ml +++ b/src/framework/node.ml @@ -22,21 +22,21 @@ let name () = "node" (** Pretty node plainly with entire stmt. *) let pretty_plain () = function | Statement s -> text "Statement " ++ dn_stmt () s - | Function f -> text "Function " ++ text (RenameMapping.show_varinfo f.svar) - | FunctionEntry f -> text "FunctionEntry " ++ text (RenameMapping.show_varinfo f.svar) + | Function f -> text "Function " ++ text (f.svar.vname) + | FunctionEntry f -> text "FunctionEntry " ++ text (f.svar.vname) (* TODO: remove this? *) (** Pretty node plainly with stmt location. *) let pretty_plain_short () = function | Statement s -> text "Statement @ " ++ CilType.Location.pretty () (Cilfacade.get_stmtLoc s) - | Function f -> text "Function " ++ text (RenameMapping.show_varinfo f.svar) - | FunctionEntry f -> text "FunctionEntry " ++ text (RenameMapping.show_varinfo f.svar) + | Function f -> text "Function " ++ text (f.svar.vname) + | FunctionEntry f -> text "FunctionEntry " ++ text (f.svar.vname) (** Pretty node for solver variable tracing with short stmt. *) let pretty_trace () = function | Statement stmt -> dprintf "node %d \"%a\"" stmt.sid Cilfacade.stmt_pretty_short stmt - | Function fd -> dprintf "call of %s" (RenameMapping.show_varinfo fd.svar) - | FunctionEntry fd -> dprintf "entry state of %s" (RenameMapping.show_varinfo fd.svar) + | Function fd -> dprintf "call of %s" (fd.svar.vname) + | FunctionEntry fd -> dprintf "entry state of %s" (fd.svar.vname) (** Output functions for Printable interface *) let pretty () x = pretty_trace () x @@ -56,8 +56,8 @@ let show_id = function (** Show node label for CFG. *) let show_cfg = function | Statement stmt -> string_of_int stmt.sid (* doesn't use this but defaults to no label and uses ID from show_id instead *) - | Function fd -> "return of " ^ (RenameMapping.show_varinfo fd.svar) ^ "()" - | FunctionEntry fd -> (RenameMapping.show_varinfo fd.svar) ^ "()" + | Function fd -> "return of " ^ (fd.svar.vname) ^ "()" + | FunctionEntry fd -> (fd.svar.vname) ^ "()" let location (node: t) = diff --git a/src/incremental/renameMapping.ml b/src/incremental/renameMapping.ml deleted file mode 100644 index e3f332e555..0000000000 --- a/src/incremental/renameMapping.ml +++ /dev/null @@ -1,94 +0,0 @@ -open Cil - -(* - This file remembers which varinfos were renamed in the process of incremental analysis. - If the functions of this file are used to pretty print varinfos and their names, the correct updated name - will be shown instead of the old varinfo name that was used when the analysis result was created. - - The rename entries are filled up by CompareAST.ml while the comparison takes place. -*) - -module IncrementallyUpdatedVarinfoMap = Hashtbl.Make (CilType.Varinfo) - -(*Mapps a varinfo to its updated name*) -let renamedVarinfoMap: string IncrementallyUpdatedVarinfoMap.t ref = ref (IncrementallyUpdatedVarinfoMap.create 100) - -let get_old_or_updated_varinfo_name (old_varinfo: varinfo) = - let r: string option = IncrementallyUpdatedVarinfoMap.find_opt !renamedVarinfoMap old_varinfo in - Option.value r ~default:old_varinfo.vname - -let store_update_varinfo_name (old_varinfo: varinfo) (new_name: string) = - IncrementallyUpdatedVarinfoMap.add !renamedVarinfoMap old_varinfo new_name - -(* - Incremental rename aware version of show. Returns the renamed name of the varinfo if it has been updated by an incremental build, or vname if nothing has changed. - - Dev Note: Putting this into CilType.Varinfo results in a cyclic dependency. It should not be put into CilType anyway, as CilType only defines types based on the types themselves, not implement any logic based on other components outside its own definitions. So I think it's cleaner this way. -*) -let show_varinfo = get_old_or_updated_varinfo_name - -(*in original Cil v.vname is hardcoded*) -let pVDeclImpl () (v:varinfo) (pType) (pAttrs) = - (* First the storage modifiers *) - Pretty.(text (if v.vinline then "__inline " else "") - ++ d_storage () v.vstorage - ++ (pType (Some (Pretty.text (show_varinfo v))) () v.vtype) - ++ Pretty.text " " - ++ pAttrs () v.vattr) - -class incremental_printer : Cil.cilPrinter = object(self) - inherit Cil.defaultCilPrinterClass - method! pVar (v:varinfo) = Pretty.text (show_varinfo v) - - (* variable declaration *) - method! pVDecl () (v:varinfo) = pVDeclImpl () v self#pType self#pAttrs -end;; - -class plain_incremental_printer : Cil.cilPrinter = object(self) - inherit Cil.plainCilPrinterClass - method! pVar (v:varinfo) = Pretty.text (show_varinfo v) - - method! pVDecl () (v:varinfo) = pVDeclImpl () v self#pType self#pAttrs -end;; - -let incremental_aware_printer = new incremental_printer -let plain_incremental_aware_printer = new plain_incremental_printer - -let d_exp () e = printExp incremental_aware_printer () e - -let d_lval () l = printLval incremental_aware_printer () l - -let d_stmt () s = printStmt incremental_aware_printer () s - -(* A hack to allow forward reference of d_exp. Copy from Cil. *) -let pd_exp : (unit -> exp -> Pretty.doc) ref = - ref (fun _ -> failwith "") - -let _ = pd_exp := d_exp - -let pd_lval : (unit -> lval -> Pretty.doc) ref = ref (fun _ -> failwith "") -let _ = pd_lval := d_lval - -let pd_stmt : (unit -> stmt -> Pretty.doc) ref = ref (fun _ -> failwith "") -let _ = pd_stmt := d_stmt - -(*Fixme: Im a copy of Cil.dn_obj but Cil.dn_obj is not exported. So export Cil.dn_obj and then replace me.*) -let dn_obj (func: unit -> 'a -> Pretty.doc) : (unit -> 'a -> Pretty.doc) = - begin - (* construct the closure to return *) - let theFunc () (obj:'a) : Pretty.doc = - begin - let prevStyle = !lineDirectiveStyle in - lineDirectiveStyle := None; - let ret = (func () obj) in (* call underlying printer *) - lineDirectiveStyle := prevStyle; - ret - end in - theFunc - end - -let dn_exp = (dn_obj d_exp) - -let dn_lval = (dn_obj d_lval) - -let dn_stmt = (dn_obj d_stmt) \ No newline at end of file From 18938a393a3fb068a7249de541996a254ceb56b4 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Tue, 21 Jun 2022 11:45:21 +0200 Subject: [PATCH 47/90] Fixed crash in forward_list_equal on lists with altering list lengths. --- src/incremental/compareAST.ml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 95af1f37ca..cb9163c11a 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -88,7 +88,9 @@ let (&&>) (prev_result: bool * rename_mapping) (b: bool) : bool * rename_mapping (*Same as Goblist.eq but propagates the rename_mapping*) let forward_list_equal f l1 l2 (prev_result: rename_mapping) : bool * rename_mapping = - List.fold_left2 (fun (b, r) x y -> if b then f x y r else (b, r)) (true, prev_result) l1 l2 + if ((List.compare_lengths l1 l2) = 0) then + List.fold_left2 (fun (b, r) x y -> if b then f x y r else (b, r)) (true, prev_result) l1 l2 + else false, prev_result (* hack: CIL generates new type names for anonymous types - we want to ignore these *) let compare_name (a: string) (b: string) = @@ -181,7 +183,7 @@ and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (rename_mapping: rename (*compinfo2.cname <- compinfo1.cname;*) register_rename_on_success rm (Some((compinfo2, compinfo1))) None - else rm + else rm in if res then global_typ_acc := (a, b) :: !global_typ_acc; From 40281fe99685a1ed446a3b7b6d402ff63134c44f Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Tue, 21 Jun 2022 13:50:15 +0200 Subject: [PATCH 48/90] Added documentation to tests in 04-var-rename --- .../04-var-rename/01-unused_rename.txt | 3 +++ .../04-var-rename/02-rename_and_shuffle.txt | 2 ++ .../04-var-rename/03-rename_with_usage.txt | 2 ++ .../04-var-rename/04-renamed_assert.txt | 2 ++ .../04-var-rename/05-renamed_param.txt | 2 ++ .../06-renamed_param_usage_changed.txt | 2 ++ .../incremental/04-var-rename/07-method_rename.c | 10 ---------- .../04-var-rename/07-method_rename.json | 3 --- .../04-var-rename/07-method_rename.patch | 15 --------------- .../04-var-rename/diffs/04-renamed_assert.c | 4 ++-- .../04-var-rename/diffs/07-method_rename.c | 10 ---------- 11 files changed, 15 insertions(+), 40 deletions(-) create mode 100644 tests/incremental/04-var-rename/01-unused_rename.txt create mode 100644 tests/incremental/04-var-rename/02-rename_and_shuffle.txt create mode 100644 tests/incremental/04-var-rename/03-rename_with_usage.txt create mode 100644 tests/incremental/04-var-rename/04-renamed_assert.txt create mode 100644 tests/incremental/04-var-rename/05-renamed_param.txt create mode 100644 tests/incremental/04-var-rename/06-renamed_param_usage_changed.txt delete mode 100644 tests/incremental/04-var-rename/07-method_rename.c delete mode 100644 tests/incremental/04-var-rename/07-method_rename.json delete mode 100644 tests/incremental/04-var-rename/07-method_rename.patch delete mode 100644 tests/incremental/04-var-rename/diffs/07-method_rename.c diff --git a/tests/incremental/04-var-rename/01-unused_rename.txt b/tests/incremental/04-var-rename/01-unused_rename.txt new file mode 100644 index 0000000000..a317916ad1 --- /dev/null +++ b/tests/incremental/04-var-rename/01-unused_rename.txt @@ -0,0 +1,3 @@ +local variable a is renamed to b. +a/b is not used. +No semantic changes. diff --git a/tests/incremental/04-var-rename/02-rename_and_shuffle.txt b/tests/incremental/04-var-rename/02-rename_and_shuffle.txt new file mode 100644 index 0000000000..8c0ab5ac05 --- /dev/null +++ b/tests/incremental/04-var-rename/02-rename_and_shuffle.txt @@ -0,0 +1,2 @@ +a is renamed to c, but the usage of a is replaced by b. +Semantic changes. diff --git a/tests/incremental/04-var-rename/03-rename_with_usage.txt b/tests/incremental/04-var-rename/03-rename_with_usage.txt new file mode 100644 index 0000000000..18ff7e94d4 --- /dev/null +++ b/tests/incremental/04-var-rename/03-rename_with_usage.txt @@ -0,0 +1,2 @@ +a is renamed to c, but the usage stays the same. +No semantic changes. diff --git a/tests/incremental/04-var-rename/04-renamed_assert.txt b/tests/incremental/04-var-rename/04-renamed_assert.txt new file mode 100644 index 0000000000..1afc289347 --- /dev/null +++ b/tests/incremental/04-var-rename/04-renamed_assert.txt @@ -0,0 +1,2 @@ +local var used in assert is renamed. +No semantic changes. diff --git a/tests/incremental/04-var-rename/05-renamed_param.txt b/tests/incremental/04-var-rename/05-renamed_param.txt new file mode 100644 index 0000000000..09bca47979 --- /dev/null +++ b/tests/incremental/04-var-rename/05-renamed_param.txt @@ -0,0 +1,2 @@ +Function param is renamed. +No semantic changes. diff --git a/tests/incremental/04-var-rename/06-renamed_param_usage_changed.txt b/tests/incremental/04-var-rename/06-renamed_param_usage_changed.txt new file mode 100644 index 0000000000..0dc90594c7 --- /dev/null +++ b/tests/incremental/04-var-rename/06-renamed_param_usage_changed.txt @@ -0,0 +1,2 @@ +function parameters a and b and swapped in the function header. But the function body stays the same. +Semantic changes. diff --git a/tests/incremental/04-var-rename/07-method_rename.c b/tests/incremental/04-var-rename/07-method_rename.c deleted file mode 100644 index 84ce2d8621..0000000000 --- a/tests/incremental/04-var-rename/07-method_rename.c +++ /dev/null @@ -1,10 +0,0 @@ -//Method is renamed with all of its usages. Test should say no changes. - -int foo() { - return 12; -} - -int main() { - foo(); - return 0; -} diff --git a/tests/incremental/04-var-rename/07-method_rename.json b/tests/incremental/04-var-rename/07-method_rename.json deleted file mode 100644 index 544b7b4ddd..0000000000 --- a/tests/incremental/04-var-rename/07-method_rename.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/07-method_rename.patch b/tests/incremental/04-var-rename/07-method_rename.patch deleted file mode 100644 index e55d61e986..0000000000 --- a/tests/incremental/04-var-rename/07-method_rename.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- tests/incremental/04-var-rename/07-method_rename.c -+++ tests/incremental/04-var-rename/07-method_rename.c -@@ -1,10 +1,10 @@ - //Method is renamed with all of its usages. Test should say no changes. - --int foo() { -+int bar() { - return 12; - } - - int main() { -- foo(); -+ bar(); - return 0; - } diff --git a/tests/incremental/04-var-rename/diffs/04-renamed_assert.c b/tests/incremental/04-var-rename/diffs/04-renamed_assert.c index 642609580c..ef95920fd5 100644 --- a/tests/incremental/04-var-rename/diffs/04-renamed_assert.c +++ b/tests/incremental/04-var-rename/diffs/04-renamed_assert.c @@ -2,8 +2,8 @@ int main() { int j = 0; - + assert(j < 11); return 0; -} \ No newline at end of file +} diff --git a/tests/incremental/04-var-rename/diffs/07-method_rename.c b/tests/incremental/04-var-rename/diffs/07-method_rename.c deleted file mode 100644 index 0d6c2aa9b9..0000000000 --- a/tests/incremental/04-var-rename/diffs/07-method_rename.c +++ /dev/null @@ -1,10 +0,0 @@ -//Method is renamed with all of its usages. Test should say no changes. - -int bar() { - return 12; -} - -int main() { - bar(); - return 0; -} From 60468d43e5f0e175c9a412f81d1039e8a5fd84a9 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 15 Jun 2022 13:07:27 +0200 Subject: [PATCH 49/90] Add comment to test-incremental-multiple.sh --- scripts/test-incremental-multiple.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/test-incremental-multiple.sh b/scripts/test-incremental-multiple.sh index a910e8498a..0dee9c117d 100644 --- a/scripts/test-incremental-multiple.sh +++ b/scripts/test-incremental-multiple.sh @@ -1,3 +1,4 @@ +#This file runs 3 incremental tests in total. As such it is similar to test-incremental.sh but performs an additional incremental run on top of it. test=$1 base=./tests/incremental From a9d297cb7b852bc9f7acd9a19f738cf4edc43edc Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 22 Jun 2022 11:14:53 +0200 Subject: [PATCH 50/90] Removed syntactic noise introduced by addition and removal of RenameMapping --- src/cdomains/baseDomain.ml | 2 +- src/framework/analyses.ml | 1 - src/framework/node.ml | 16 ++++++++-------- src/util/cilType.ml | 1 - 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/cdomains/baseDomain.ml b/src/cdomains/baseDomain.ml index 2002a1e1a4..35daecaf01 100644 --- a/src/cdomains/baseDomain.ml +++ b/src/cdomains/baseDomain.ml @@ -115,7 +115,7 @@ struct let printXml f r = let e = XmlUtil.escape in BatPrintf.fprintf f "\n\n\n%s\n\n%a\n%s\n\n%a\n%s\n\n%a\n\n%s\n\n%a\n\n" - (e @@ (CPA.name ())) CPA.printXml r.cpa + (e @@ CPA.name ()) CPA.printXml r.cpa (e @@ PartDeps.name ()) PartDeps.printXml r.deps (e @@ WeakUpdates.name ()) WeakUpdates.printXml r.weak (e @@ PrivD.name ()) PrivD.printXml r.priv diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 63e149ef7a..f2b99a68e4 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -140,7 +140,6 @@ struct (* Not using Node.location here to have updated locations in incremental analysis. See: https://github.com/goblint/analyzer/issues/290#issuecomment-881258091. *) let loc = UpdateCil.getLoc n in - BatPrintf.fprintf f "\n" (Node.show_id n) loc.file loc.line loc.byte loc.column; BatPrintf.fprintf f "%a\n" Range.printXml v in diff --git a/src/framework/node.ml b/src/framework/node.ml index 84f8dea1ea..1d5a8291f9 100644 --- a/src/framework/node.ml +++ b/src/framework/node.ml @@ -22,21 +22,21 @@ let name () = "node" (** Pretty node plainly with entire stmt. *) let pretty_plain () = function | Statement s -> text "Statement " ++ dn_stmt () s - | Function f -> text "Function " ++ text (f.svar.vname) - | FunctionEntry f -> text "FunctionEntry " ++ text (f.svar.vname) + | Function f -> text "Function " ++ text f.svar.vname + | FunctionEntry f -> text "FunctionEntry " ++ text f.svar.vname (* TODO: remove this? *) (** Pretty node plainly with stmt location. *) let pretty_plain_short () = function | Statement s -> text "Statement @ " ++ CilType.Location.pretty () (Cilfacade.get_stmtLoc s) - | Function f -> text "Function " ++ text (f.svar.vname) - | FunctionEntry f -> text "FunctionEntry " ++ text (f.svar.vname) + | Function f -> text "Function " ++ text f.svar.vname + | FunctionEntry f -> text "FunctionEntry " ++ text f.svar.vname (** Pretty node for solver variable tracing with short stmt. *) let pretty_trace () = function | Statement stmt -> dprintf "node %d \"%a\"" stmt.sid Cilfacade.stmt_pretty_short stmt - | Function fd -> dprintf "call of %s" (fd.svar.vname) - | FunctionEntry fd -> dprintf "entry state of %s" (fd.svar.vname) + | Function fd -> dprintf "call of %s" fd.svar.vname + | FunctionEntry fd -> dprintf "entry state of %s" fd.svar.vname (** Output functions for Printable interface *) let pretty () x = pretty_trace () x @@ -56,8 +56,8 @@ let show_id = function (** Show node label for CFG. *) let show_cfg = function | Statement stmt -> string_of_int stmt.sid (* doesn't use this but defaults to no label and uses ID from show_id instead *) - | Function fd -> "return of " ^ (fd.svar.vname) ^ "()" - | FunctionEntry fd -> (fd.svar.vname) ^ "()" + | Function fd -> "return of " ^ fd.svar.vname ^ "()" + | FunctionEntry fd -> fd.svar.vname ^ "()" let location (node: t) = diff --git a/src/util/cilType.ml b/src/util/cilType.ml index bd714c31e8..16b3ad75fe 100644 --- a/src/util/cilType.ml +++ b/src/util/cilType.ml @@ -117,7 +117,6 @@ struct let show = show end ) - let pp fmt x = Format.fprintf fmt "%s" x.vname (* for deriving show *) end From 31283c9aafb2e002e8d34bcc7d18dec2e0161b12 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 22 Jun 2022 11:16:25 +0200 Subject: [PATCH 51/90] Removed diffs directory in tests/incremental/04-var-rename --- .../04-var-rename/diffs/01-unused_rename.c | 4 ---- .../diffs/02-rename_and_shuffle.c | 11 ----------- .../04-var-rename/diffs/03-rename_with_usage.c | 11 ----------- .../04-var-rename/diffs/04-renamed_assert.c | 9 --------- .../04-var-rename/diffs/05-renamed_param.c | 8 -------- .../diffs/06-renamed_param_usage_changed.c | 11 ----------- .../diffs/08-2_incremental_runs_2.c | 8 -------- .../diffs/08-2_incremental_runs_3.c | 8 -------- .../diffs/09-2_ir_with_changes_2.c | 18 ------------------ .../diffs/09-2_ir_with_changes_3.c | 18 ------------------ 10 files changed, 106 deletions(-) delete mode 100644 tests/incremental/04-var-rename/diffs/01-unused_rename.c delete mode 100644 tests/incremental/04-var-rename/diffs/02-rename_and_shuffle.c delete mode 100644 tests/incremental/04-var-rename/diffs/03-rename_with_usage.c delete mode 100644 tests/incremental/04-var-rename/diffs/04-renamed_assert.c delete mode 100644 tests/incremental/04-var-rename/diffs/05-renamed_param.c delete mode 100644 tests/incremental/04-var-rename/diffs/06-renamed_param_usage_changed.c delete mode 100644 tests/incremental/04-var-rename/diffs/08-2_incremental_runs_2.c delete mode 100644 tests/incremental/04-var-rename/diffs/08-2_incremental_runs_3.c delete mode 100644 tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_2.c delete mode 100644 tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_3.c diff --git a/tests/incremental/04-var-rename/diffs/01-unused_rename.c b/tests/incremental/04-var-rename/diffs/01-unused_rename.c deleted file mode 100644 index 1fbd3f6638..0000000000 --- a/tests/incremental/04-var-rename/diffs/01-unused_rename.c +++ /dev/null @@ -1,4 +0,0 @@ -int main() { - int b = 0; - return 0; -} diff --git a/tests/incremental/04-var-rename/diffs/02-rename_and_shuffle.c b/tests/incremental/04-var-rename/diffs/02-rename_and_shuffle.c deleted file mode 100644 index eb54a5c0aa..0000000000 --- a/tests/incremental/04-var-rename/diffs/02-rename_and_shuffle.c +++ /dev/null @@ -1,11 +0,0 @@ -#include - -//a is renamed to c, but the usage of a is replaced by b -int main() { - int c = 0; - int b = 1; - - printf("Print %d", b); - - return 0; -} diff --git a/tests/incremental/04-var-rename/diffs/03-rename_with_usage.c b/tests/incremental/04-var-rename/diffs/03-rename_with_usage.c deleted file mode 100644 index 4676e03447..0000000000 --- a/tests/incremental/04-var-rename/diffs/03-rename_with_usage.c +++ /dev/null @@ -1,11 +0,0 @@ -#include - -//a is renamed to c, but its usages stay the same -int main() { - int c = 0; - int b = 1; - - printf("Print %d", c); - - return 0; -} diff --git a/tests/incremental/04-var-rename/diffs/04-renamed_assert.c b/tests/incremental/04-var-rename/diffs/04-renamed_assert.c deleted file mode 100644 index ef95920fd5..0000000000 --- a/tests/incremental/04-var-rename/diffs/04-renamed_assert.c +++ /dev/null @@ -1,9 +0,0 @@ -#include - -int main() { - int j = 0; - - assert(j < 11); - - return 0; -} diff --git a/tests/incremental/04-var-rename/diffs/05-renamed_param.c b/tests/incremental/04-var-rename/diffs/05-renamed_param.c deleted file mode 100644 index 198bd82496..0000000000 --- a/tests/incremental/04-var-rename/diffs/05-renamed_param.c +++ /dev/null @@ -1,8 +0,0 @@ -void method(int b) { - int c = b; -} - -int main() { - method(0); - return 0; -} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/diffs/06-renamed_param_usage_changed.c b/tests/incremental/04-var-rename/diffs/06-renamed_param_usage_changed.c deleted file mode 100644 index 0bf42f645e..0000000000 --- a/tests/incremental/04-var-rename/diffs/06-renamed_param_usage_changed.c +++ /dev/null @@ -1,11 +0,0 @@ -//This test should mark foo and main as changed - -void foo(int b, int a) { - int x = a; - int y = b; -} - -int main() { - foo(3, 4); - return 0; -} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/diffs/08-2_incremental_runs_2.c b/tests/incremental/04-var-rename/diffs/08-2_incremental_runs_2.c deleted file mode 100644 index 43205a976e..0000000000 --- a/tests/incremental/04-var-rename/diffs/08-2_incremental_runs_2.c +++ /dev/null @@ -1,8 +0,0 @@ -int main() { - int varSecondIteration = 0; - - varSecondIteration++; - - assert(varSecondIteration < 10); - return 0; -} diff --git a/tests/incremental/04-var-rename/diffs/08-2_incremental_runs_3.c b/tests/incremental/04-var-rename/diffs/08-2_incremental_runs_3.c deleted file mode 100644 index 9ff7105ebb..0000000000 --- a/tests/incremental/04-var-rename/diffs/08-2_incremental_runs_3.c +++ /dev/null @@ -1,8 +0,0 @@ -int main() { - int varThirdIteration = 0; - - varThirdIteration++; - - assert(varThirdIteration < 10); - return 0; -} diff --git a/tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_2.c b/tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_2.c deleted file mode 100644 index 6c4f789066..0000000000 --- a/tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_2.c +++ /dev/null @@ -1,18 +0,0 @@ -void foo() { - int fooTwo = 1; - fooTwo++; - assert(fooTwo == 2); -} - -void bar() { - int barTwo = 10; - int x = 3; - if (x < 11) barTwo = 13; - assert(x > 1); -} - -int main() { - foo(); - bar(); - return 0; -} diff --git a/tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_3.c b/tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_3.c deleted file mode 100644 index eaf77e72d1..0000000000 --- a/tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_3.c +++ /dev/null @@ -1,18 +0,0 @@ -void foo() { - int fooThree = 1; - fooThree++; - assert(fooThree == 2); -} - -void bar() { - int barTwo = 10; - int x = 3; - if (x < 11) barTwo = 13; - assert(x > 1); -} - -int main() { - foo(); - bar(); - return 0; -} From f04651b20e72343f761f6f449686f8ad82f023c6 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 22 Jun 2022 11:16:25 +0200 Subject: [PATCH 52/90] Removed diffs directory in tests/incremental/04-var-rename --- .../04-var-rename/diffs/01-unused_rename.c | 4 ---- .../diffs/02-rename_and_shuffle.c | 11 ----------- .../04-var-rename/diffs/03-rename_with_usage.c | 11 ----------- .../04-var-rename/diffs/04-renamed_assert.c | 7 ------- .../04-var-rename/diffs/05-renamed_param.c | 8 -------- .../diffs/06-renamed_param_usage_changed.c | 11 ----------- .../diffs/08-2_incremental_runs_2.c | 8 -------- .../diffs/08-2_incremental_runs_3.c | 8 -------- .../diffs/09-2_ir_with_changes_2.c | 18 ------------------ .../diffs/09-2_ir_with_changes_3.c | 18 ------------------ 10 files changed, 104 deletions(-) delete mode 100644 tests/incremental/04-var-rename/diffs/01-unused_rename.c delete mode 100644 tests/incremental/04-var-rename/diffs/02-rename_and_shuffle.c delete mode 100644 tests/incremental/04-var-rename/diffs/03-rename_with_usage.c delete mode 100644 tests/incremental/04-var-rename/diffs/04-renamed_assert.c delete mode 100644 tests/incremental/04-var-rename/diffs/05-renamed_param.c delete mode 100644 tests/incremental/04-var-rename/diffs/06-renamed_param_usage_changed.c delete mode 100644 tests/incremental/04-var-rename/diffs/08-2_incremental_runs_2.c delete mode 100644 tests/incremental/04-var-rename/diffs/08-2_incremental_runs_3.c delete mode 100644 tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_2.c delete mode 100644 tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_3.c diff --git a/tests/incremental/04-var-rename/diffs/01-unused_rename.c b/tests/incremental/04-var-rename/diffs/01-unused_rename.c deleted file mode 100644 index 1fbd3f6638..0000000000 --- a/tests/incremental/04-var-rename/diffs/01-unused_rename.c +++ /dev/null @@ -1,4 +0,0 @@ -int main() { - int b = 0; - return 0; -} diff --git a/tests/incremental/04-var-rename/diffs/02-rename_and_shuffle.c b/tests/incremental/04-var-rename/diffs/02-rename_and_shuffle.c deleted file mode 100644 index eb54a5c0aa..0000000000 --- a/tests/incremental/04-var-rename/diffs/02-rename_and_shuffle.c +++ /dev/null @@ -1,11 +0,0 @@ -#include - -//a is renamed to c, but the usage of a is replaced by b -int main() { - int c = 0; - int b = 1; - - printf("Print %d", b); - - return 0; -} diff --git a/tests/incremental/04-var-rename/diffs/03-rename_with_usage.c b/tests/incremental/04-var-rename/diffs/03-rename_with_usage.c deleted file mode 100644 index 4676e03447..0000000000 --- a/tests/incremental/04-var-rename/diffs/03-rename_with_usage.c +++ /dev/null @@ -1,11 +0,0 @@ -#include - -//a is renamed to c, but its usages stay the same -int main() { - int c = 0; - int b = 1; - - printf("Print %d", c); - - return 0; -} diff --git a/tests/incremental/04-var-rename/diffs/04-renamed_assert.c b/tests/incremental/04-var-rename/diffs/04-renamed_assert.c deleted file mode 100644 index 8f74e36a13..0000000000 --- a/tests/incremental/04-var-rename/diffs/04-renamed_assert.c +++ /dev/null @@ -1,7 +0,0 @@ -int main() { - int j = 0; - - assert(j < 11); - - return 0; -} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/diffs/05-renamed_param.c b/tests/incremental/04-var-rename/diffs/05-renamed_param.c deleted file mode 100644 index 198bd82496..0000000000 --- a/tests/incremental/04-var-rename/diffs/05-renamed_param.c +++ /dev/null @@ -1,8 +0,0 @@ -void method(int b) { - int c = b; -} - -int main() { - method(0); - return 0; -} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/diffs/06-renamed_param_usage_changed.c b/tests/incremental/04-var-rename/diffs/06-renamed_param_usage_changed.c deleted file mode 100644 index 0bf42f645e..0000000000 --- a/tests/incremental/04-var-rename/diffs/06-renamed_param_usage_changed.c +++ /dev/null @@ -1,11 +0,0 @@ -//This test should mark foo and main as changed - -void foo(int b, int a) { - int x = a; - int y = b; -} - -int main() { - foo(3, 4); - return 0; -} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/diffs/08-2_incremental_runs_2.c b/tests/incremental/04-var-rename/diffs/08-2_incremental_runs_2.c deleted file mode 100644 index 43205a976e..0000000000 --- a/tests/incremental/04-var-rename/diffs/08-2_incremental_runs_2.c +++ /dev/null @@ -1,8 +0,0 @@ -int main() { - int varSecondIteration = 0; - - varSecondIteration++; - - assert(varSecondIteration < 10); - return 0; -} diff --git a/tests/incremental/04-var-rename/diffs/08-2_incremental_runs_3.c b/tests/incremental/04-var-rename/diffs/08-2_incremental_runs_3.c deleted file mode 100644 index 9ff7105ebb..0000000000 --- a/tests/incremental/04-var-rename/diffs/08-2_incremental_runs_3.c +++ /dev/null @@ -1,8 +0,0 @@ -int main() { - int varThirdIteration = 0; - - varThirdIteration++; - - assert(varThirdIteration < 10); - return 0; -} diff --git a/tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_2.c b/tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_2.c deleted file mode 100644 index 6c4f789066..0000000000 --- a/tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_2.c +++ /dev/null @@ -1,18 +0,0 @@ -void foo() { - int fooTwo = 1; - fooTwo++; - assert(fooTwo == 2); -} - -void bar() { - int barTwo = 10; - int x = 3; - if (x < 11) barTwo = 13; - assert(x > 1); -} - -int main() { - foo(); - bar(); - return 0; -} diff --git a/tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_3.c b/tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_3.c deleted file mode 100644 index eaf77e72d1..0000000000 --- a/tests/incremental/04-var-rename/diffs/09-2_ir_with_changes_3.c +++ /dev/null @@ -1,18 +0,0 @@ -void foo() { - int fooThree = 1; - fooThree++; - assert(fooThree == 2); -} - -void bar() { - int barTwo = 10; - int x = 3; - if (x < 11) barTwo = 13; - assert(x > 1); -} - -int main() { - foo(); - bar(); - return 0; -} From 4d8098e84f3a373ced44940f27d33a1a59a83052 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 15 Jun 2022 13:07:27 +0200 Subject: [PATCH 53/90] Add comment to test-incremental-multiple.sh --- scripts/test-incremental-multiple.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/test-incremental-multiple.sh b/scripts/test-incremental-multiple.sh index 87b7e150ce..eb0dbd9128 100644 --- a/scripts/test-incremental-multiple.sh +++ b/scripts/test-incremental-multiple.sh @@ -1,3 +1,4 @@ +#This file runs 3 incremental tests in total. As such it is similar to test-incremental.sh but performs an additional incremental run on top of it. test=$1 base=./tests/incremental From 45350394dbda2535f216023d00841e0eb99e62a5 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Tue, 21 Jun 2022 13:50:15 +0200 Subject: [PATCH 54/90] Added documentation to tests in 04-var-rename --- .../04-var-rename/01-unused_rename.txt | 3 +++ .../04-var-rename/02-rename_and_shuffle.txt | 2 ++ .../04-var-rename/03-rename_with_usage.txt | 2 ++ .../04-var-rename/04-renamed_assert.txt | 2 ++ .../04-var-rename/05-renamed_param.txt | 2 ++ .../06-renamed_param_usage_changed.txt | 2 ++ .../incremental/04-var-rename/07-method_rename.c | 10 ---------- .../04-var-rename/07-method_rename.json | 3 --- .../04-var-rename/07-method_rename.patch | 15 --------------- .../04-var-rename/diffs/04-renamed_assert.c | 9 +++++++++ .../04-var-rename/diffs/07-method_rename.c | 10 ---------- 11 files changed, 22 insertions(+), 38 deletions(-) create mode 100644 tests/incremental/04-var-rename/01-unused_rename.txt create mode 100644 tests/incremental/04-var-rename/02-rename_and_shuffle.txt create mode 100644 tests/incremental/04-var-rename/03-rename_with_usage.txt create mode 100644 tests/incremental/04-var-rename/04-renamed_assert.txt create mode 100644 tests/incremental/04-var-rename/05-renamed_param.txt create mode 100644 tests/incremental/04-var-rename/06-renamed_param_usage_changed.txt delete mode 100644 tests/incremental/04-var-rename/07-method_rename.c delete mode 100644 tests/incremental/04-var-rename/07-method_rename.json delete mode 100644 tests/incremental/04-var-rename/07-method_rename.patch create mode 100644 tests/incremental/04-var-rename/diffs/04-renamed_assert.c delete mode 100644 tests/incremental/04-var-rename/diffs/07-method_rename.c diff --git a/tests/incremental/04-var-rename/01-unused_rename.txt b/tests/incremental/04-var-rename/01-unused_rename.txt new file mode 100644 index 0000000000..a317916ad1 --- /dev/null +++ b/tests/incremental/04-var-rename/01-unused_rename.txt @@ -0,0 +1,3 @@ +local variable a is renamed to b. +a/b is not used. +No semantic changes. diff --git a/tests/incremental/04-var-rename/02-rename_and_shuffle.txt b/tests/incremental/04-var-rename/02-rename_and_shuffle.txt new file mode 100644 index 0000000000..8c0ab5ac05 --- /dev/null +++ b/tests/incremental/04-var-rename/02-rename_and_shuffle.txt @@ -0,0 +1,2 @@ +a is renamed to c, but the usage of a is replaced by b. +Semantic changes. diff --git a/tests/incremental/04-var-rename/03-rename_with_usage.txt b/tests/incremental/04-var-rename/03-rename_with_usage.txt new file mode 100644 index 0000000000..18ff7e94d4 --- /dev/null +++ b/tests/incremental/04-var-rename/03-rename_with_usage.txt @@ -0,0 +1,2 @@ +a is renamed to c, but the usage stays the same. +No semantic changes. diff --git a/tests/incremental/04-var-rename/04-renamed_assert.txt b/tests/incremental/04-var-rename/04-renamed_assert.txt new file mode 100644 index 0000000000..1afc289347 --- /dev/null +++ b/tests/incremental/04-var-rename/04-renamed_assert.txt @@ -0,0 +1,2 @@ +local var used in assert is renamed. +No semantic changes. diff --git a/tests/incremental/04-var-rename/05-renamed_param.txt b/tests/incremental/04-var-rename/05-renamed_param.txt new file mode 100644 index 0000000000..09bca47979 --- /dev/null +++ b/tests/incremental/04-var-rename/05-renamed_param.txt @@ -0,0 +1,2 @@ +Function param is renamed. +No semantic changes. diff --git a/tests/incremental/04-var-rename/06-renamed_param_usage_changed.txt b/tests/incremental/04-var-rename/06-renamed_param_usage_changed.txt new file mode 100644 index 0000000000..0dc90594c7 --- /dev/null +++ b/tests/incremental/04-var-rename/06-renamed_param_usage_changed.txt @@ -0,0 +1,2 @@ +function parameters a and b and swapped in the function header. But the function body stays the same. +Semantic changes. diff --git a/tests/incremental/04-var-rename/07-method_rename.c b/tests/incremental/04-var-rename/07-method_rename.c deleted file mode 100644 index 84ce2d8621..0000000000 --- a/tests/incremental/04-var-rename/07-method_rename.c +++ /dev/null @@ -1,10 +0,0 @@ -//Method is renamed with all of its usages. Test should say no changes. - -int foo() { - return 12; -} - -int main() { - foo(); - return 0; -} diff --git a/tests/incremental/04-var-rename/07-method_rename.json b/tests/incremental/04-var-rename/07-method_rename.json deleted file mode 100644 index 544b7b4ddd..0000000000 --- a/tests/incremental/04-var-rename/07-method_rename.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/07-method_rename.patch b/tests/incremental/04-var-rename/07-method_rename.patch deleted file mode 100644 index e55d61e986..0000000000 --- a/tests/incremental/04-var-rename/07-method_rename.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- tests/incremental/04-var-rename/07-method_rename.c -+++ tests/incremental/04-var-rename/07-method_rename.c -@@ -1,10 +1,10 @@ - //Method is renamed with all of its usages. Test should say no changes. - --int foo() { -+int bar() { - return 12; - } - - int main() { -- foo(); -+ bar(); - return 0; - } diff --git a/tests/incremental/04-var-rename/diffs/04-renamed_assert.c b/tests/incremental/04-var-rename/diffs/04-renamed_assert.c new file mode 100644 index 0000000000..ef95920fd5 --- /dev/null +++ b/tests/incremental/04-var-rename/diffs/04-renamed_assert.c @@ -0,0 +1,9 @@ +#include + +int main() { + int j = 0; + + assert(j < 11); + + return 0; +} diff --git a/tests/incremental/04-var-rename/diffs/07-method_rename.c b/tests/incremental/04-var-rename/diffs/07-method_rename.c deleted file mode 100644 index 0d6c2aa9b9..0000000000 --- a/tests/incremental/04-var-rename/diffs/07-method_rename.c +++ /dev/null @@ -1,10 +0,0 @@ -//Method is renamed with all of its usages. Test should say no changes. - -int bar() { - return 12; -} - -int main() { - bar(); - return 0; -} From 55133d21e47e914cfb93727cc79089dc10f16f7a Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 22 Jun 2022 14:55:21 +0200 Subject: [PATCH 55/90] Replaced printf with tracing in compareCIL --- src/incremental/compareCIL.ml | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index d65bff45a0..ea59d48a1c 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -61,21 +61,23 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = let oldMap = Cil.foldGlobals oldAST addGlobal GlobalMap.empty in let renameDetectionResults = detectRenamedFunctions oldAST newAST in - GlobalElemMap.to_seq renameDetectionResults |> - Seq.iter - (fun (gT, (functionGlobal, status)) -> - Printf.printf "Function status of %s is=" (globalElemName gT); - match status with - | Unchanged _ -> Printf.printf "Same Name\n"; - | Added -> Printf.printf "Added\n"; - | Removed -> Printf.printf "Removed\n"; - | Changed _ -> Printf.printf "Changed\n"; - | UnchangedButRenamed toFrom -> - match toFrom with - | GFun (f, _) -> Printf.printf "Renamed to %s\n" f.svar.vname; - | GVar(v, _, _) -> Printf.printf "Renamed to %s\n" v.vname; - | _ -> Printf.printf "TODO"; - ); + + if Messages.tracing then + GlobalElemMap.to_seq renameDetectionResults |> + Seq.iter + (fun (gT, (functionGlobal, status)) -> + Messages.trace "compareCIL" "Function status of %s is=" (globalElemName gT); + match status with + | Unchanged _ -> Messages.trace "compareCIL" "Same Name\n"; + | Added -> Messages.trace "compareCIL" "Added\n"; + | Removed -> Messages.trace "compareCIL" "Removed\n"; + | Changed _ -> Messages.trace "compareCIL" "Changed\n"; + | UnchangedButRenamed toFrom -> + match toFrom with + | GFun (f, _) -> Messages.trace "compareCIL" "Renamed to %s\n" f.svar.vname; + | GVar(v, _, _) -> Messages.trace "compareCIL" "Renamed to %s\n" v.vname; + | _ -> (); + ); (* 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. *) From 51590ee7763e5fd7cbb0a9051df6457d9a539cbc Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Fri, 1 Jul 2022 17:57:04 +0200 Subject: [PATCH 56/90] Removed parameter renames and instead disabled the name checking for function parameters on function parameters. --- src/incremental/compareAST.ml | 61 +++++++------------ src/incremental/compareGlobals.ml | 8 --- .../04-var-rename/diffs/04-renamed_assert.c | 9 --- 3 files changed, 22 insertions(+), 56 deletions(-) delete mode 100644 tests/incremental/04-var-rename/diffs/04-renamed_assert.c diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index cb9163c11a..e6d310eeac 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -8,7 +8,7 @@ and global_identifier = {name: string ; global_t: global_type} [@@deriving ord] module StringMap = Map.Make(String) -type method_rename_assumption = {original_method_name: string; new_method_name: string; parameter_renames: string StringMap.t} +type method_rename_assumption = {original_method_name: string; new_method_name: string} type method_rename_assumptions = method_rename_assumption VarinfoMap.t type glob_var_rename_assumptions = string VarinfoMap.t @@ -54,9 +54,8 @@ let rename_mapping_to_string (rename_mapping: rename_mapping) = let (local, methods, glob_vars, _) = rename_mapping in let local_string = string_tuple_to_string (List.of_seq (StringMap.to_seq local)) in let methods_string: string = List.of_seq (VarinfoMap.to_seq methods |> Seq.map snd) |> - List.map (fun x -> match x with {original_method_name; new_method_name; parameter_renames} -> - "(methodName: " ^ original_method_name ^ " -> " ^ new_method_name ^ - "; renamed_params=" ^ string_tuple_to_string (List.of_seq (StringMap.to_seq parameter_renames)) ^ ")") |> + List.map (fun x -> match x with {original_method_name; new_method_name} -> + "(methodName: " ^ original_method_name ^ " -> " ^ new_method_name ^ ")") |> String.concat ", " in let global_var_string: string = string_tuple_to_string (List.of_seq (VarinfoMap.to_seq glob_vars) |> @@ -134,7 +133,7 @@ and mem_typ_acc (a: typ) (b: typ) acc = List.exists (fun p -> match p with (x, y and pretty_length () l = Pretty.num (List.length l) -and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (rename_mapping: rename_mapping) : bool * rename_mapping = +and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) ?(fun_parameter_name_comparison_enabled: bool = true) (rename_mapping: rename_mapping) : bool * rename_mapping = (* Registers a compinfo rename or a enum rename*) let register_rename_on_success = fun rename_mapping compinfo_option enum_option -> let maybeAddTuple = fun list option -> @@ -142,12 +141,12 @@ and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (rename_mapping: rename in let (a, b, c, renames_on_success) = rename_mapping in - let (compinfoRenames, enumRenames) = renames_on_success in + let (compinfoRenames, enumRenames) = renames_on_success in - let updatedCompinfoRenames = maybeAddTuple compinfoRenames compinfo_option in - let updatedEnumRenames = maybeAddTuple enumRenames enum_option in + let updatedCompinfoRenames = maybeAddTuple compinfoRenames compinfo_option in + let updatedEnumRenames = maybeAddTuple enumRenames enum_option in - a, b, c, (updatedCompinfoRenames, updatedEnumRenames) + 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 *) @@ -158,7 +157,7 @@ and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (rename_mapping: rename | TArray (typ1, None, attr1), TArray (typ2, None, attr2) -> eq_typ_acc typ1 typ2 acc rename_mapping &&>> forward_list_equal eq_attribute attr1 attr2 | TFun (typ1, (Some list1), varArg1, attr1), TFun (typ2, (Some list2), varArg2, attr2) -> eq_typ_acc typ1 typ2 acc rename_mapping &&>> - forward_list_equal (eq_args acc) list1 list2 &&> + forward_list_equal (eq_args acc ~fun_parameter_name_comparison_enabled:fun_parameter_name_comparison_enabled) list1 list2 &&> (varArg1 = varArg2) &&>> forward_list_equal eq_attribute attr1 attr2 | TFun (typ1, None, varArg1, attr1), TFun (typ2, None, varArg2, attr2) -> @@ -179,11 +178,11 @@ and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (rename_mapping: rename let acc = (a, b) :: acc in let (res, rm) = eq_compinfo compinfo1 compinfo2 acc rename_mapping &&>> forward_list_equal eq_attribute attr1 attr2 in let updated_rm: rename_mapping = if res && compinfo1.cname <> compinfo2.cname then - (* This renaming now only takes place when the comparison was successful.*) - (*compinfo2.cname <- compinfo1.cname;*) + (* This renaming now only takes place when the comparison was successful.*) + (*compinfo2.cname <- compinfo1.cname;*) - register_rename_on_success rm (Some((compinfo2, compinfo1))) None - else rm + register_rename_on_success rm (Some((compinfo2, compinfo1))) None + else rm in if res then global_typ_acc := (a, b) :: !global_typ_acc; @@ -203,7 +202,7 @@ and eq_typ_acc (a: typ) (b: typ) (acc: (typ * typ) list) (rename_mapping: rename if Messages.tracing then Messages.traceu "compareast" "eq_typ_acc %a vs %a\n" d_type a d_type b; (r, updated_rename_mapping) -and eq_typ (a: typ) (b: typ) (rename_mapping: rename_mapping) : bool * rename_mapping = eq_typ_acc a b [] rename_mapping +and eq_typ (a: typ) (b: typ) ?(fun_parameter_name_comparison_enabled: bool = true) (rename_mapping: rename_mapping) : bool * rename_mapping = eq_typ_acc a b [] ~fun_parameter_name_comparison_enabled:fun_parameter_name_comparison_enabled rename_mapping and eq_eitems (a: string * exp * location) (b: string * exp * location) (rename_mapping: rename_mapping) = match a, b with (name1, exp1, _l1), (name2, exp2, _l2) -> (name1 = name2, rename_mapping) &&>> eq_exp exp1 exp2 @@ -215,9 +214,10 @@ and eq_enuminfo (a: enuminfo) (b: enuminfo) (rename_mapping: rename_mapping) = forward_list_equal eq_eitems a.eitems b.eitems (* Ignore ereferenced *) -and eq_args (acc: (typ * typ) list) (a: string * typ * attributes) (b: string * typ * attributes) (rename_mapping: rename_mapping) : bool * rename_mapping = match a, b with +(*param: fun_parameter_name_comparison_enabled when set to false, skips the comparison of the names*) +and eq_args (acc: (typ * typ) list) (a: string * typ * attributes) (b: string * typ * attributes) ?(fun_parameter_name_comparison_enabled: bool = true) (rename_mapping: rename_mapping) : bool * rename_mapping = match a, b with (name1, typ1, attr1), (name2, typ2, attr2) -> - (rename_mapping_aware_name_comparison name1 name2 rename_mapping, rename_mapping) &&>> + ((not fun_parameter_name_comparison_enabled) || rename_mapping_aware_name_comparison name1 name2 rename_mapping, rename_mapping) &&>> eq_typ_acc typ1 typ2 acc &&>> forward_list_equal eq_attribute attr1 attr2 @@ -275,18 +275,8 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) : bool is_naming_ok, method_rename_mappings, glob_vars | None -> if a.vname <> b.vname then - (*Function that extracts the names from the param spec of the TFun*) - let extract_names_from_params param_spec = - Option.map (fun list -> List.map (fun (name, _, _) -> name) list) param_spec |> - Option.value ~default:[] - in - - (*No mapping exists yet. Create one.*) - let aParamNames = extract_names_from_params aParamSpec in - let bParamNames = extract_names_from_params bParamSpec in - let assumption = - {original_method_name = a.vname; new_method_name = b.vname; parameter_renames = create_locals_rename_mapping aParamNames bParamNames} in + {original_method_name = a.vname; new_method_name = b.vname} in true, VarinfoMap.add a assumption method_rename_mappings, glob_vars else true, method_rename_mappings, glob_vars @@ -298,15 +288,8 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) : bool in (*If the following is a method call, we need to check if we have a mapping for that method call. *) - let typ_rename_mapping = match b.vtype with - | TFun(_, _, _, _) -> ( - let new_locals = VarinfoMap.find_opt a updated_method_rename_mappings in - - match new_locals with - | Some locals -> - (locals.parameter_renames, updated_method_rename_mappings, updatedGlobVarMapping, renames_on_success) - | None -> (StringMap.empty, updated_method_rename_mappings, updatedGlobVarMapping, renames_on_success) - ) + let fun_parameter_name_comparison_enabled = match b.vtype with + | TFun(_, _, _, _) -> false (*| GVar (_, _, _) -> ( let new_local = VarinfoMap.find_opt a glob_vars in @@ -314,11 +297,11 @@ and eq_varinfo (a: varinfo) (b: varinfo) (rename_mapping: rename_mapping) : bool | Some now_name -> (StringMap.add a.vname now_name StringMap.empty, updated_method_rename_mappings, updatedGlobVarMapping) | None -> (StringMap.empty, updated_method_rename_mappings, updatedGlobVarMapping) )*) - | _ -> (locals_renames, updated_method_rename_mappings, updatedGlobVarMapping, renames_on_success) + | _ -> true in (*Ignore rename mapping for type check, as it doesn't change anyway. We only need the renames_on_success*) - let (typeCheck, (_, _, _, updated_renames_on_success)) = eq_typ a.vtype b.vtype typ_rename_mapping in + let (typeCheck, (_, _, _, updated_renames_on_success)) = eq_typ a.vtype b.vtype ~fun_parameter_name_comparison_enabled:fun_parameter_name_comparison_enabled (StringMap.empty, VarinfoMap.empty, VarinfoMap.empty, renames_on_success) in (isNamingOk && typeCheck, (locals_renames, updated_method_rename_mappings, updatedGlobVarMapping, updated_renames_on_success)) &&>> forward_list_equal eq_attribute a.vattr b.vattr &&> diff --git a/src/incremental/compareGlobals.ml b/src/incremental/compareGlobals.ml index cac98eefda..602ad52707 100644 --- a/src/incremental/compareGlobals.ml +++ b/src/incremental/compareGlobals.ml @@ -35,14 +35,6 @@ let should_reanalyze (fdec: Cil.fundec) = * 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) (global_function_rename_mapping: method_rename_assumptions) (global_var_rename_mapping: glob_var_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.add 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 diff --git a/tests/incremental/04-var-rename/diffs/04-renamed_assert.c b/tests/incremental/04-var-rename/diffs/04-renamed_assert.c deleted file mode 100644 index ef95920fd5..0000000000 --- a/tests/incremental/04-var-rename/diffs/04-renamed_assert.c +++ /dev/null @@ -1,9 +0,0 @@ -#include - -int main() { - int j = 0; - - assert(j < 11); - - return 0; -} From 1caaa79035ae362fd36fc4a7cddefc7f99021120 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Mon, 4 Jul 2022 14:42:34 +0200 Subject: [PATCH 57/90] Removed diffs. --- .../05-method-rename/diffs/00-simple_rename.c | 10 -------- .../diffs/01-dependent_rename.c | 14 ----------- .../diffs/02-rename_and_swap.c | 23 ------------------ .../diffs/03-cyclic_rename_dependency.c | 17 ------------- .../diffs/04-cyclic_with_swap.c | 20 ---------------- .../05-method-rename/diffs/05-deep-change.c | 18 -------------- .../05-method-rename/diffs/06-common_rename.c | 20 ---------------- .../diffs/07-common_rename_refactored.c | 24 ------------------- .../diffs/00-simple_rename.c | 9 ------- .../diffs/01-duplicate_local_global.c | 14 ----------- .../diffs/02-add_new_gvar.c | 9 ------- 11 files changed, 178 deletions(-) delete mode 100644 tests/incremental/05-method-rename/diffs/00-simple_rename.c delete mode 100644 tests/incremental/05-method-rename/diffs/01-dependent_rename.c delete mode 100644 tests/incremental/05-method-rename/diffs/02-rename_and_swap.c delete mode 100644 tests/incremental/05-method-rename/diffs/03-cyclic_rename_dependency.c delete mode 100644 tests/incremental/05-method-rename/diffs/04-cyclic_with_swap.c delete mode 100644 tests/incremental/05-method-rename/diffs/05-deep-change.c delete mode 100644 tests/incremental/05-method-rename/diffs/06-common_rename.c delete mode 100644 tests/incremental/05-method-rename/diffs/07-common_rename_refactored.c delete mode 100644 tests/incremental/06-glob-var-rename/diffs/00-simple_rename.c delete mode 100644 tests/incremental/06-glob-var-rename/diffs/01-duplicate_local_global.c delete mode 100644 tests/incremental/06-glob-var-rename/diffs/02-add_new_gvar.c diff --git a/tests/incremental/05-method-rename/diffs/00-simple_rename.c b/tests/incremental/05-method-rename/diffs/00-simple_rename.c deleted file mode 100644 index 79a05fe8d4..0000000000 --- a/tests/incremental/05-method-rename/diffs/00-simple_rename.c +++ /dev/null @@ -1,10 +0,0 @@ -#include - -void bar() { - printf("foo"); -} - -int main() { - bar(); - return 0; -} diff --git a/tests/incremental/05-method-rename/diffs/01-dependent_rename.c b/tests/incremental/05-method-rename/diffs/01-dependent_rename.c deleted file mode 100644 index a2c5d48fea..0000000000 --- a/tests/incremental/05-method-rename/diffs/01-dependent_rename.c +++ /dev/null @@ -1,14 +0,0 @@ -#include - -void bar1() { - printf("fun1"); -} - -void bar2() { - bar1(); -} - -int main() { - bar2(); - return 0; -} diff --git a/tests/incremental/05-method-rename/diffs/02-rename_and_swap.c b/tests/incremental/05-method-rename/diffs/02-rename_and_swap.c deleted file mode 100644 index eae4b77001..0000000000 --- a/tests/incremental/05-method-rename/diffs/02-rename_and_swap.c +++ /dev/null @@ -1,23 +0,0 @@ -#include - -void newFun() { - printf("newFun"); -} - -void bar1() { - printf("foo1"); -} - -void foo2() { - bar1(); -} - -void foo3() { - newFun(); -} - -int main() { - foo2(); - foo3(); - return 0; -} diff --git a/tests/incremental/05-method-rename/diffs/03-cyclic_rename_dependency.c b/tests/incremental/05-method-rename/diffs/03-cyclic_rename_dependency.c deleted file mode 100644 index a720f8025e..0000000000 --- a/tests/incremental/05-method-rename/diffs/03-cyclic_rename_dependency.c +++ /dev/null @@ -1,17 +0,0 @@ -#include - -//Unchanged. - -void bar1(int c) { - if (c < 10) bar2(c + 1); -} - -void bar2(int c) { - if (c < 10) bar1(c + 1); -} - -int main() { - bar1(0); - bar2(0); - return 0; -} diff --git a/tests/incremental/05-method-rename/diffs/04-cyclic_with_swap.c b/tests/incremental/05-method-rename/diffs/04-cyclic_with_swap.c deleted file mode 100644 index 67cb349429..0000000000 --- a/tests/incremental/05-method-rename/diffs/04-cyclic_with_swap.c +++ /dev/null @@ -1,20 +0,0 @@ -#include - -//Changed. - -void newFun(int c) { - printf("newfun"); -} - -void bar1(int c) { - if (c < 10) bar2(c + 1); -} - -void bar2(int c) { - if (c < 10) newFun(c + 1); -} - -int main() { - bar1(0); - return 0; -} diff --git a/tests/incremental/05-method-rename/diffs/05-deep-change.c b/tests/incremental/05-method-rename/diffs/05-deep-change.c deleted file mode 100644 index 57ad90457b..0000000000 --- a/tests/incremental/05-method-rename/diffs/05-deep-change.c +++ /dev/null @@ -1,18 +0,0 @@ -#include - -void zap() { - printf("drap"); -} - -void bar() { - zap(); -} - -void foo() { - bar(); -} - -int main() { - foo(); - return 0; -} diff --git a/tests/incremental/05-method-rename/diffs/06-common_rename.c b/tests/incremental/05-method-rename/diffs/06-common_rename.c deleted file mode 100644 index 6a96b84747..0000000000 --- a/tests/incremental/05-method-rename/diffs/06-common_rename.c +++ /dev/null @@ -1,20 +0,0 @@ -#include - -void bar() { - printf("foo"); -} - -void fun1() { - bar(); -} - -void fun2() { - bar(); -} - -int main() { - fun1(); - fun2(); - bar(); - return 0; -} diff --git a/tests/incremental/05-method-rename/diffs/07-common_rename_refactored.c b/tests/incremental/05-method-rename/diffs/07-common_rename_refactored.c deleted file mode 100644 index 170cdfb6de..0000000000 --- a/tests/incremental/05-method-rename/diffs/07-common_rename_refactored.c +++ /dev/null @@ -1,24 +0,0 @@ -#include - -void bar() { - printf("foo"); -} - -void baz() { - printf("baz"); -} - -void fun1() { - bar(); -} - -void fun2() { - bar(); -} - -int main() { - fun1(); - fun2(); - baz(); - return 0; -} diff --git a/tests/incremental/06-glob-var-rename/diffs/00-simple_rename.c b/tests/incremental/06-glob-var-rename/diffs/00-simple_rename.c deleted file mode 100644 index bfe71f0522..0000000000 --- a/tests/incremental/06-glob-var-rename/diffs/00-simple_rename.c +++ /dev/null @@ -1,9 +0,0 @@ -#include - -int bar = 1; - -int main() { - printf("%d", bar); - - return 0; -} diff --git a/tests/incremental/06-glob-var-rename/diffs/01-duplicate_local_global.c b/tests/incremental/06-glob-var-rename/diffs/01-duplicate_local_global.c deleted file mode 100644 index 0e4ebf3fd7..0000000000 --- a/tests/incremental/06-glob-var-rename/diffs/01-duplicate_local_global.c +++ /dev/null @@ -1,14 +0,0 @@ -#include - -int bar = 1; - -int main() { - - printf("%d", bar); - - int bar = 2; - - printf("%d", bar); - - return 0; -} diff --git a/tests/incremental/06-glob-var-rename/diffs/02-add_new_gvar.c b/tests/incremental/06-glob-var-rename/diffs/02-add_new_gvar.c deleted file mode 100644 index 3841a59b11..0000000000 --- a/tests/incremental/06-glob-var-rename/diffs/02-add_new_gvar.c +++ /dev/null @@ -1,9 +0,0 @@ -#include - -int myVar = 1; -int foo = 1; - -int main() { - printf("%d", myVar); - printf("%d", foo); -} From b9785ab01454e8e12798b46760b38a010ddd071c Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Tue, 12 Jul 2022 15:55:33 +0200 Subject: [PATCH 58/90] Applied changes requested in PR #731. compareCFG now propagates rename_mapping. --- src/incremental/compareAST.ml | 8 ++- src/incremental/compareCFG.ml | 102 ++++++++++++++++-------------- src/incremental/compareGlobals.ml | 43 ++++++++++--- src/incremental/updateCil.ml | 2 +- 4 files changed, 96 insertions(+), 59 deletions(-) diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index be5d987884..00a79e1fb1 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -63,6 +63,10 @@ let rename_mapping_to_string (rename_mapping: rename_mapping) = "(local=" ^ local_string ^ "; methods=[" ^ methods_string ^ "]; glob_vars=" ^ global_var_string ^ ")" +let is_rename_mapping_empty (rename_mapping: rename_mapping) = + let local, methods, glob_vars, _= rename_mapping in + StringMap.is_empty local && VarinfoMap.is_empty methods && VarinfoMap.is_empty glob_vars + let identifier_of_global glob = match glob with | 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 (prev_equal && b, rename_mapping) (*Same as Goblist.eq but propagates the rename_mapping*) -let forward_list_equal f l1 l2 (prev_result: rename_mapping) : bool * rename_mapping = +let forward_list_equal ?(propF = (&&>>)) f l1 l2 (prev_result: rename_mapping) : bool * rename_mapping = if ((List.compare_lengths l1 l2) = 0) then - List.fold_left2 (fun (b, r) x y -> if b then f x y r else (b, r)) (true, prev_result) l1 l2 + List.fold_left2 (fun (b, r) x y -> propF (b, r) (f x y)) (true, prev_result) l1 l2 else false, prev_result (* hack: CIL generates new type names for anonymous types - we want to ignore these *) diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index 33735aa453..5ebcf1aa6a 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -3,43 +3,42 @@ open Queue open Cil include CompareAST -(*Non propagating version of &&>>. Discards the new rename_mapping and alwas propagates the one in prev_result*) +(*Non propagating version of &&>>. Discards the new rename_mapping and alwas propagates the one in prev_result. However propagates the renames_on_success*) let (&&<>) (prev_result: bool * rename_mapping) f : bool * rename_mapping = let (prev_equal, prev_rm) = prev_result in + let (a, b, c, _) = prev_rm in + if prev_equal then - let (r, _) = f prev_rm in - (r, prev_rm) + let (r, (_, _, _, updated_renames_on_success)) = f prev_rm in + (r, (a, b, c, updated_renames_on_success)) else false, prev_rm -let eq_node (x, fun1) (y, fun2) : bool = - let empty_rename_mapping: rename_mapping = emptyRenameMapping in +let eq_node (x, fun1) (y, fun2) rename_mapping = match x,y with - | Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) empty_rename_mapping |> fst - | Function f1, Function f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping |> fst - | FunctionEntry f1, FunctionEntry f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping |> fst - | _ -> false + | Statement s1, Statement s2 -> eq_stmt ~cfg_comp:true (s1, fun1) (s2, fun2) rename_mapping + | Function f1, Function f2 -> eq_varinfo f1.svar f2.svar rename_mapping + | FunctionEntry f1, FunctionEntry f2 -> eq_varinfo f1.svar f2.svar rename_mapping + | _ -> false, rename_mapping (* TODO: compare ASMs properly instead of simply always assuming that they are not the same *) -let eq_edge x y = - let empty_rename_mapping: rename_mapping = emptyRenameMapping in - let (r, _) = match x, y with - | Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 empty_rename_mapping &&<> eq_exp rv1 rv2 - | Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 empty_rename_mapping &&<> forward_list_equal eq_exp ars1 ars2 - | Proc (Some r1,f1,ars1), Proc (Some r2,f2,ars2) -> - eq_lval r1 r2 empty_rename_mapping &&<> eq_exp f1 f2 &&<> forward_list_equal eq_exp ars1 ars2 - | Entry f1, Entry f2 -> eq_varinfo f1.svar f2.svar empty_rename_mapping - | Ret (None,fd1), Ret (None,fd2) -> eq_varinfo fd1.svar fd2.svar empty_rename_mapping - | Ret (Some r1,fd1), Ret (Some r2,fd2) -> eq_exp r1 r2 empty_rename_mapping &&<> eq_varinfo fd1.svar fd2.svar - | Test (p1,b1), Test (p2,b2) -> eq_exp p1 p2 empty_rename_mapping &&> (b1 = b2) - | ASM _, ASM _ -> false, empty_rename_mapping - | Skip, Skip -> true, empty_rename_mapping - | VDecl v1, VDecl v2 -> eq_varinfo v1 v2 empty_rename_mapping - | _ -> false, empty_rename_mapping - in - r +let eq_edge x y rename_mapping = + match x, y with + | Assign (lv1, rv1), Assign (lv2, rv2) -> eq_lval lv1 lv2 rename_mapping &&<> eq_exp rv1 rv2 + | Proc (None,f1,ars1), Proc (None,f2,ars2) -> eq_exp f1 f2 rename_mapping &&<> forward_list_equal eq_exp ars1 ars2 + | Proc (Some r1,f1,ars1), Proc (Some r2,f2,ars2) -> + eq_lval r1 r2 rename_mapping &&<> eq_exp f1 f2 &&<> forward_list_equal eq_exp ars1 ars2 + | Entry f1, Entry f2 -> eq_varinfo f1.svar f2.svar rename_mapping + | Ret (None,fd1), Ret (None,fd2) -> eq_varinfo fd1.svar fd2.svar rename_mapping + | Ret (Some r1,fd1), Ret (Some r2,fd2) -> eq_exp r1 r2 rename_mapping &&<> eq_varinfo fd1.svar fd2.svar + | Test (p1,b1), Test (p2,b2) -> eq_exp p1 p2 rename_mapping &&> (b1 = b2) + | ASM _, ASM _ -> false, rename_mapping + | Skip, Skip -> true, rename_mapping + | VDecl v1, VDecl v2 -> eq_varinfo v1 v2 rename_mapping + | _ -> false, rename_mapping + (* The order of the edges in the list is relevant. Therefore compare them one to one without sorting first *) -let eq_edge_list xs ys = GobList.equal eq_edge xs ys +let eq_edge_list xs ys = forward_list_equal ~propF:(&&<>) eq_edge xs ys let to_edge_list ls = List.map (fun (loc, edge) -> edge) ls @@ -52,13 +51,14 @@ type biDirectionNodeMap = {node1to2: node NH.t; node2to1: node NH.t} * in the succesors of fromNode2 in the new CFG. Matching node tuples are added to the waitingList to repeat the matching * process on their successors. If a node from the old CFG can not be matched, it is added to diff and no further * comparison is done for its successors. The two function entry nodes make up the tuple to start the comparison from. *) -let compareCfgs (module CfgOld : CfgForward) (module CfgNew : CfgForward) fun1 fun2 = + +let compareCfgs (module CfgOld : CfgForward) (module CfgNew : CfgForward) fun1 fun2 rename_mapping : biDirectionNodeMap * unit NH.t * rename_mapping = let diff = NH.create 113 in let same = {node1to2=NH.create 113; node2to1=NH.create 113} in let waitingList : (node * node) t = Queue.create () in - let rec compareNext () = - if Queue.is_empty waitingList then () + let rec compareNext () rename_mapping : rename_mapping = + if Queue.is_empty waitingList then rename_mapping else let fromNode1, fromNode2 = Queue.take waitingList in let outList1 = CfgOld.next fromNode1 in @@ -66,24 +66,26 @@ let compareCfgs (module CfgOld : CfgForward) (module CfgNew : CfgForward) fun1 f (* Find a matching edge and successor node for (edgeList1, toNode1) in the list of successors of fromNode2. * If successful, add the matching node tuple to same, else add toNode1 to the differing nodes. *) - let findMatch (edgeList1, toNode1) = - let rec aux remSuc = match remSuc with - | [] -> NH.replace diff toNode1 () + let findMatch (edgeList1, toNode1) rename_mapping : rename_mapping = + let rec aux remSuc rename_mapping : rename_mapping = match remSuc with + | [] -> NH.replace diff toNode1 (); rename_mapping | (locEdgeList2, toNode2)::remSuc' -> let edgeList2 = to_edge_list locEdgeList2 in (* TODO: don't allow pseudo return node to be equal to normal return node, could make function unchanged, but have different sallstmts *) - if eq_node (toNode1, fun1) (toNode2, fun2) && eq_edge_list edgeList1 edgeList2 then + let (isEq, updatedRenameMapping) = (true, rename_mapping) &&>> eq_node (toNode1, fun1) (toNode2, fun2) &&>> eq_edge_list edgeList1 edgeList2 in + if isEq then begin match NH.find_opt same.node1to2 toNode1 with - | Some n2 -> if not (Node.equal n2 toNode2) then NH.replace diff toNode1 () - | None -> NH.replace same.node1to2 toNode1 toNode2; NH.replace same.node2to1 toNode2 toNode1; Queue.add (toNode1, toNode2) waitingList + | Some n2 -> if not (Node.equal n2 toNode2) then NH.replace diff toNode1 (); updatedRenameMapping + | None -> NH.replace same.node1to2 toNode1 toNode2; NH.replace same.node2to1 toNode2 toNode1; Queue.add (toNode1, toNode2) waitingList; + updatedRenameMapping end - else aux remSuc' in - aux outList2 in + else aux remSuc' updatedRenameMapping in + aux outList2 rename_mapping in (* For a toNode1 from the list of successors of fromNode1, check whether it might have duplicate matches. * In that case declare toNode1 as differing node. Else, try finding a match in the list of successors * of fromNode2 in the new CFG using findMatch. *) - let iterOuts (locEdgeList1, toNode1) = + let iterOuts (locEdgeList1, toNode1) rename_mapping : rename_mapping = let edgeList1 = to_edge_list locEdgeList1 in (* Differentiate between a possibly duplicate Test(1,false) edge and a single occurence. In the first * 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 let posAmbigEdge edgeList = let findTestFalseEdge (ll,_) = testFalseEdge (snd (List.hd ll)) in let numDuplicates l = List.length (List.find_all findTestFalseEdge l) in testFalseEdge (List.hd edgeList) && (numDuplicates outList1 > 1 || numDuplicates outList2 > 1) in - if posAmbigEdge edgeList1 then NH.replace diff toNode1 () - else findMatch (edgeList1, toNode1) in - List.iter iterOuts outList1; compareNext () in + if posAmbigEdge edgeList1 then (NH.replace diff toNode1 (); rename_mapping) + else findMatch (edgeList1, toNode1) rename_mapping in + let updatedRenameMapping = List.fold_left (fun rm e -> iterOuts e rm) rename_mapping outList1 in + compareNext () updatedRenameMapping + in let entryNode1, entryNode2 = (FunctionEntry fun1, FunctionEntry fun2) in - NH.replace same.node1to2 entryNode1 entryNode2; NH.replace same.node2to1 entryNode2 entryNode1; - Queue.push (entryNode1,entryNode2) waitingList; compareNext (); (same, diff) + NH.replace same.node1to2 entryNode1 entryNode2; + NH.replace same.node2to1 entryNode2 entryNode1; + Queue.push (entryNode1,entryNode2) waitingList; + let updatedRenameMapping = compareNext () rename_mapping in + same, diff, updatedRenameMapping (* This is the second phase of the CFG comparison of functions. It removes the nodes from the matching node set 'same' * 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 repeat (); NH.to_seq same.node1to2, NH.to_seq_keys diffNodes1 -let compareFun (module CfgOld : CfgForward) (module CfgNew : CfgBidir) fun1 fun2 = - let same, diff = Stats.time "compare-phase1" (fun () -> compareCfgs (module CfgOld) (module CfgNew) fun1 fun2) () in + +let compareFun (module CfgOld : CfgForward) (module CfgNew : CfgBidir) fun1 fun2 rename_mapping : (node * node) list * node list * rename_mapping = + let same, diff, rename_mapping = Stats.time "compare-phase1" (fun () -> compareCfgs (module CfgOld) (module CfgNew) fun1 fun2 rename_mapping) () in let unchanged, diffNodes1 = Stats.time "compare-phase2" (fun () -> reexamine fun1 fun2 same diff (module CfgOld) (module CfgNew)) () in - List.of_seq unchanged, List.of_seq diffNodes1 + List.of_seq unchanged, List.of_seq diffNodes1, rename_mapping diff --git a/src/incremental/compareGlobals.ml b/src/incremental/compareGlobals.ml index 602ad52707..d91255bbaf 100644 --- a/src/incremental/compareGlobals.ml +++ b/src/incremental/compareGlobals.ml @@ -1,5 +1,6 @@ open Cil open MyCFG +open CilMaps include CompareAST include CompareCFG @@ -47,19 +48,43 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo | _, _ -> false, rename_mapping in - let headerSizeEqual, headerRenameMapping = rename_mapping_aware_compare a.sformals b.sformals (StringMap.empty) in - let actHeaderRenameMapping: rename_mapping = (headerRenameMapping, global_function_rename_mapping, global_var_rename_mapping, ([], [])) in + let unchangedHeader, headerRenameMapping, renamesOnSuccessHeader = match cfgs with + | None -> ( + let headerSizeEqual, headerRenameMapping = rename_mapping_aware_compare a.sformals b.sformals (StringMap.empty) in + let actHeaderRenameMapping: rename_mapping = (headerRenameMapping, global_function_rename_mapping, global_var_rename_mapping, ([], [])) in + + let (unchangedHeader, (_, _, _, renamesOnSuccessHeader)) = eq_varinfo a.svar b.svar actHeaderRenameMapping &&>> + forward_list_equal eq_varinfo a.sformals b.sformals in + unchangedHeader, headerRenameMapping, renamesOnSuccessHeader + ) + | Some _ -> ( + let unchangedHeader, headerRenameMapping = eq_varinfo a.svar b.svar emptyRenameMapping &&>> + forward_list_equal eq_varinfo a.sformals b.sformals in + let (_, _, _, renamesOnSuccessHeader) = headerRenameMapping in + + (unchangedHeader && is_rename_mapping_empty headerRenameMapping), StringMap.empty, renamesOnSuccessHeader + ) + in - let (unchangedHeader, (_, _, _, renamesOnSuccessHeader)) = eq_varinfo a.svar b.svar actHeaderRenameMapping &&>> forward_list_equal eq_varinfo a.sformals b.sformals in let identical, diffOpt, (_, renamed_method_dependencies, renamed_global_vars_dependencies, renamesOnSuccess) = if should_reanalyze a then false, None, emptyRenameMapping else (* 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_function_rename_mapping, global_var_rename_mapping, renamesOnSuccessHeader) in + (*flag: when running on cfg, true iff the locals are identical; on ast: if the size of the locals stayed the same*) + let flag, rename_mapping = + match cfgs with + | None -> ( + let sizeEqual, local_rename = rename_mapping_aware_compare a.slocals b.slocals headerRenameMapping in + sizeEqual, (local_rename, global_function_rename_mapping, global_var_rename_mapping, renamesOnSuccessHeader) + ) + | Some _ -> ( + let isEqual, rename_mapping = forward_list_equal eq_varinfo a.slocals b.slocals (StringMap.empty, VarinfoMap.empty, VarinfoMap.empty, renamesOnSuccessHeader) in + isEqual && is_rename_mapping_empty rename_mapping, rename_mapping + ) + in - let sameDef = unchangedHeader && sizeEqual in + let sameDef = unchangedHeader && flag in if not sameDef then (false, None, emptyRenameMapping) else @@ -70,8 +95,8 @@ let eqF (a: Cil.fundec) (b: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (glo | 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 - let matches, diffNodes1 = compareFun (module CfgOld) (module CfgNew) a b in - if diffNodes1 = [] then (true, None, emptyRenameMapping) - else (false, Some {unchangedNodes = matches; primObsoleteNodes = diffNodes1}, emptyRenameMapping) + let matches, diffNodes1, updated_rename_mapping = compareFun (module CfgOld) (module CfgNew) a b rename_mapping in + if diffNodes1 = [] then (true, None, updated_rename_mapping) + else (false, Some {unchangedNodes = matches; primObsoleteNodes = diffNodes1}, updated_rename_mapping) in identical, unchangedHeader, diffOpt, renamed_method_dependencies, renamed_global_vars_dependencies, renamesOnSuccess diff --git a/src/incremental/updateCil.ml b/src/incremental/updateCil.ml index 5aa756804a..99ff845e38 100644 --- a/src/incremental/updateCil.ml +++ b/src/incremental/updateCil.ml @@ -45,7 +45,7 @@ let update_ids (old_file: file) (ids: max_ids) (new_file: file) (changes: change old_f.svar.vname <- f.svar.vname; f.svar.vid <- old_f.svar.vid; List.iter2 (fun l o_l -> l.vid <- o_l.vid; o_l.vname <- l.vname) f.slocals old_f.slocals; - List.iter2 (fun lo o_f -> lo.vid <- o_f.vid) f.sformals old_f.sformals; + List.iter2 (fun lo o_f -> lo.vid <- o_f.vid; o_f.vname <- lo.vname) f.sformals old_f.sformals; List.iter2 (fun s o_s -> s.sid <- o_s.sid) f.sallstmts old_f.sallstmts; List.iter (fun s -> store_node_location (Statement s) (Cilfacade.get_stmtLoc s)) f.sallstmts; From 1f7b7bb8e9d8a6ac0aeb89edab986ab766ed503f Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Tue, 12 Jul 2022 16:30:36 +0200 Subject: [PATCH 59/90] detecting renames can now be disabled using the option incremental.detect-renames --- src/incremental/compareAST.ml | 23 ++++++----- src/incremental/compareCIL.ml | 75 ++++++++++++++++++----------------- src/util/options.schema.json | 6 +++ 3 files changed, 58 insertions(+), 46 deletions(-) diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 00a79e1fb1..d3ba142cfb 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -24,16 +24,19 @@ let emptyRenameMapping: rename_mapping = (StringMap.empty, VarinfoMap.empty, Var 1. there is a rename for name1 -> name2 = rename(name1) 2. there is no rename for name1 -> name1 = name2*) let rename_mapping_aware_name_comparison (name1: string) (name2: string) (rename_mapping: rename_mapping) = - let (local_c, method_c, _, _) = rename_mapping in - let existingAssumption: string option = StringMap.find_opt name1 local_c in - - match existingAssumption with - | Some now -> - (*Printf.printf "Assumption is: %s -> %s\n" original now;*) - now = name2 - | None -> - (*Printf.printf "No assumption when %s, %s, %b\n" name1 name2 (name1 = name2);*) - name1 = name2 (*Var names differ, but there is no assumption, so this can't be good*) + if GobConfig.get_bool "incremental.detect-renames" then ( + let (local_c, method_c, _, _) = rename_mapping in + let existingAssumption: string option = StringMap.find_opt name1 local_c in + + match existingAssumption with + | Some now -> + (*Printf.printf "Assumption is: %s -> %s\n" original now;*) + now = name2 + | None -> + (*Printf.printf "No assumption when %s, %s, %b\n" name1 name2 (name1 = name2);*) + name1 = name2 (*Var names differ, but there is no assumption, so this can't be good*) + ) + else name1 = name2 (*Creates the mapping of local renames. If the locals do not match in size, an empty mapping is returned.*) let create_locals_rename_mapping (originalLocalNames: string list) (updatedLocalNames: string list): string StringMap.t = diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index e89fc27c16..0e494075d1 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -41,12 +41,13 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = global_typ_acc := []; let findChanges map global = try - let isGFun = match global with - | GFun _-> true (* set to true later to disable finding changes for funs*) + let skipFindChanges = match global with + | GFun _-> true + | GVar _ -> true | _ -> false in - if not isGFun then + if not skipFindChanges || not (GobConfig.get_bool "incremental.detect-renames") then let ident = identifier_of_global global in let old_global = GlobalMap.find ident map in (* Do a (recursive) equal comparison ignoring location information *) @@ -60,45 +61,47 @@ 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 renameDetectionResults = detectRenamedFunctions oldAST newAST in - - if Messages.tracing then - GlobalElemMap.to_seq renameDetectionResults |> - Seq.iter - (fun (gT, (functionGlobal, status)) -> - Messages.trace "compareCIL" "Function status of %s is=" (globalElemName gT); - match status with - | Unchanged _ -> Messages.trace "compareCIL" "Same Name\n"; - | Added -> Messages.trace "compareCIL" "Added\n"; - | Removed -> Messages.trace "compareCIL" "Removed\n"; - | Changed _ -> Messages.trace "compareCIL" "Changed\n"; - | UnchangedButRenamed toFrom -> - match toFrom with - | GFun (f, _) -> Messages.trace "compareCIL" "Renamed to %s\n" f.svar.vname; - | GVar(v, _, _) -> Messages.trace "compareCIL" "Renamed to %s\n" v.vname; - | _ -> (); - ); + if GobConfig.get_bool "incremental.detect-renames" then ( + let renameDetectionResults = detectRenamedFunctions oldAST newAST in + + if Messages.tracing then + GlobalElemMap.to_seq renameDetectionResults |> + Seq.iter + (fun (gT, (functionGlobal, status)) -> + Messages.trace "compareCIL" "Function status of %s is=" (globalElemName gT); + match status with + | Unchanged _ -> Messages.trace "compareCIL" "Same Name\n"; + | Added -> Messages.trace "compareCIL" "Added\n"; + | Removed -> Messages.trace "compareCIL" "Removed\n"; + | Changed _ -> Messages.trace "compareCIL" "Changed\n"; + | UnchangedButRenamed toFrom -> + match toFrom with + | GFun (f, _) -> Messages.trace "compareCIL" "Renamed to %s\n" f.svar.vname; + | GVar(v, _, _) -> Messages.trace "compareCIL" "Renamed to %s\n" v.vname; + | _ -> (); + ); + + let unchanged, changed, added, removed = GlobalElemMap.fold (fun _ (global, status) (u, c, a, r) -> + match status with + | Unchanged now -> (u @ [{old=global; current=now}], c, a, r) + | UnchangedButRenamed now -> (u @ [{old=global; current=now}], c, a, r) + | Added -> (u, c, a @ [global], r) + | Removed -> (u, c, a, r @ [global]) + | Changed (now, unchangedHeader) -> (u, c @ [{old=global; current=now; unchangedHeader=unchangedHeader; diff=None}], a, r) + ) renameDetectionResults (changes.unchanged, changes.changed, changes.added, changes.removed) + in + + changes.added <- added; + changes.removed <- removed; + changes.changed <- changed; + changes.unchanged <- unchanged; + ) else (); (* 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 -> findChanges oldMap glob); - let unchanged, changed, added, removed = GlobalElemMap.fold (fun _ (global, status) (u, c, a, r) -> - match status with - | Unchanged now -> (u @ [{old=global; current=now}], c, a, r) - | UnchangedButRenamed now -> (u @ [{old=global; current=now}], c, a, r) - | Added -> (u, c, a @ [global], r) - | Removed -> (u, c, a, r @ [global]) - | Changed (now, unchangedHeader) -> (u, c @ [{old=global; current=now; unchangedHeader=unchangedHeader; diff=None}], a, r) - ) renameDetectionResults (changes.unchanged, changes.changed, changes.added, changes.removed) - in - - changes.added <- added; - changes.removed <- removed; - changes.changed <- changed; - changes.unchanged <- unchanged; - changes (** Given an (optional) equality function between [Cil.global]s, an old and a new [Cil.file], this function computes a [change_info], diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 326ef3f6e1..b6227adc2e 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -953,6 +953,12 @@ "enum": ["ast", "cfg"], "default": "ast" }, + "detect-renames": { + "title": "incremental.detect-renames", + "description": "If Goblint should try to detect renamed local variables, function parameters, functions and global variables", + "type":"boolean", + "default": true + }, "force-reanalyze": { "title": "incremental.force-reanalyze", "type": "object", From ae506cb0b03eea630c5fd1936e69f91d7a977b61 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Wed, 13 Jul 2022 19:01:28 +0200 Subject: [PATCH 60/90] Added test script. function rename detection now supports renamed recursive functions. --- scripts/test-refactorings-rename.py | 432 ++++++++++++++++++ src/incremental/detectRenamedFunctions.ml | 87 ++-- .../05-method-rename/08-recursive_rename.c | 7 + .../05-method-rename/08-recursive_rename.json | 3 + .../08-recursive_rename.patch | 13 + 5 files changed, 504 insertions(+), 38 deletions(-) create mode 100644 scripts/test-refactorings-rename.py create mode 100644 tests/incremental/05-method-rename/08-recursive_rename.c create mode 100644 tests/incremental/05-method-rename/08-recursive_rename.json create mode 100644 tests/incremental/05-method-rename/08-recursive_rename.patch diff --git a/scripts/test-refactorings-rename.py b/scripts/test-refactorings-rename.py new file mode 100644 index 0000000000..4d3fd70edb --- /dev/null +++ b/scripts/test-refactorings-rename.py @@ -0,0 +1,432 @@ +#!/usr/bin/python +import dataclasses +import os +import pathlib +import re +import shutil +import subprocess +import sys +import tempfile +from os.path import isdir +from pathlib import Path +from pycparser import c_ast, c_parser, parse_file +from pycparser.c_ast import TypeDecl, ArrayDecl, PtrDecl, IdentifierType +from pycparser.c_generator import CGenerator + +parser_errors = 0 +struct_occurrences = 0 +skips = 0 +includes = 0 +includes_only_assert = 0 +invalid_solver = 0 +introduced_changes = 0 +renamed_a_function = 0 + +def main(): + regression_folder = Path("./tests/regression") + + task = TaskRenameFunction() + + # test = regression_folder / "23-partitioned_arrays_last/06-interprocedural.c" + # execute_validation_test(test.parent, test, task) + # return + + excluded = [ + "44-trier_analyzer/33-recA.c", + # Even though the same file is read in, the type of rec#i differes from int * to int?! + "04-mutex/53-kernel-spinlock.c", # Kernel is broken. + "56-witness/01-base-lor-enums.c", # 0evals? + "56-witness/02-base-lor-addr.c", # 0evals? + "56-witness/03-int-log-short.c", # 0evals? + "56-witness/04-base-priv-sync-prune.c", # 0evals? + "44-trier_analyzer/09-G1.c", # Also renamed glob var + "44-trier_analyzer/21-Pproc.c" # renamed function. + ] + + # folder = regression_folder / "07-uninit" + # for testFile in folder.iterdir(): + # filename, extension = os.path.splitext(testFile.name) + # identifier = f"{folder.name}/{testFile.name}" + # + # if extension == ".c" and not (identifier in excluded): + # execute_validation_test(folder, testFile) + + total_tests = 0 + executed_tests = 0 + + for folder in regression_folder.iterdir(): + if isdir(folder): + for testFile in folder.iterdir(): + filename, extension = os.path.splitext(testFile.name) + if extension == ".c" and not (f"{folder.name}/{testFile.name}" in excluded): + total_tests += 1 + if execute_validation_test(folder, testFile, task): + executed_tests += 1 + + global introduced_changes + global renamed_a_function + + print(f"Executed {executed_tests}/{total_tests}") + if isinstance(task, TaskRenameLocals) and task.introduce_changes: + print(f"Introduced changes in {introduced_changes}/{executed_tests}") + + if isinstance(task, TaskRenameFunction): + print(f"Renamed a function in {renamed_a_function}/{executed_tests}") + + global parser_errors + global struct_occurrences + global skips + global includes + global invalid_solver + global includes_only_assert + + print("Skipped due tue:") + print("Parser errors: " + str(parser_errors)) + print("Struct occurrences: " + str(struct_occurrences)) + print("Skips (//Skip): " + str(skips)) + print(f"Includes: {includes}, of those only assert: {includes_only_assert}") + print("Invalid solver: " + str(invalid_solver)) + + +def execute_validation_test(folder: Path, test_file: Path, task): + print(f"Executing test: {folder.name}/{test_file.name}") + + global parser_errors + global struct_occurrences + global skips + global includes + global invalid_solver + global includes_only_assert + global introduced_changes + global renamed_a_function + + extra_params = "" + + with open(test_file, "r") as filehandle: + lines = filehandle.readlines() + if lines[0].startswith("// PARAM:"): + extra_params = lines[0][len("// PARAM:"):-1] + if lines[0].startswith("// SKIP"): + print("Skipped test.") + skips += 1 + return False + if any(x.startswith("#include") for x in lines): + print("Skipped test because of include") + includes += 1 + + include_lines = [x for x in lines if x.startswith("#include")] + + if all("assert.h" in x for x in include_lines): + includes_only_assert += 1 + + return False + if any("struct" in x for x in lines): + print("Skipped because struct") + struct_occurrences += 1 + return False + + if "slr3" in extra_params or "slr4" in extra_params: + print("Aborted test due to invalid solver.") + invalid_solver += 1 + return False + + modified_file_result = create_modified_file(test_file, task) + + if modified_file_result is None: + print("Aborted test due to parsing error.") + parser_errors += 1 + return False + + base = "./" + + args = f"--enable dbg.debug --enable printstats -v {extra_params}" + + subprocess.run(f"./goblint {args} --enable incremental.save {test_file}", shell=True, capture_output=True) + + command = subprocess.run( + f"./goblint {args} --enable incremental.load --set save_run {base}/{test_file}-incrementalrun {modified_file_result.tmp.name}", + shell=True, text=True, capture_output=True) + + found_line = False + + for line in command.stdout.splitlines(): + if line.startswith("change_info = "): + match = re.search("; changed = (\d+)", line) + change_count = int(match.group(1)) + + if modified_file_result.introduced_changes: + invalid_change_count = change_count == 0 + expected = "> 0" + else: + invalid_change_count = change_count != 0 + expected = "= 0" + + if invalid_change_count != 0: + print("-----------------------------------------------------------------") + print(command.stdout) + print("-----------------------------------------------------------------") + print(f"Invalid change count={change_count}. Expected {expected}.") + cleanup(folder, test_file, modified_file_result.tmp) + sys.exit(-1) + found_line = True + break + + if not found_line: + print("Could not find line with change count.") + print(command.stdout) + cleanup(folder, test_file, modified_file_result.tmp) + sys.exit(-1) + + if modified_file_result.introduced_changes: + introduced_changes += 1 + + if modified_file_result.renamed_anything and isinstance(task, TaskRenameFunction): + renamed_a_function += 1 + + cleanup(folder, test_file, modified_file_result.tmp) + + return True + + +def cleanup(folder: Path, test: Path, updated_file): + updated_file.close() + shutil.rmtree(folder / f"{test.name}-incrementalrun") + + +def find_local_vars(node, on_node_found): + if node.body.block_items is not None: + for child in node.body.block_items: + if isinstance(child, c_ast.Decl): + if isinstance(child.type, c_ast.TypeDecl) or isinstance(child.type, c_ast.ArrayDecl): + on_node_found(child) + + +def rename_decl(node, new_name): + if isinstance(node.type, TypeDecl) or isinstance(node.type, ArrayDecl) or isinstance(node.type, PtrDecl): + node.name = new_name + if isinstance(node.type, TypeDecl): + node.type.declname = new_name + if isinstance(node.type, ArrayDecl): + node.type.type.declname = new_name + if isinstance(node.type, PtrDecl): + node.type.type.declname = new_name + +def visit_rest_of_func_def(self, node): + self.visit(node.decl) + if node.param_decls is not None: + self.visit(node.param_decls) + + self.visit(node.body) + +class VarDeclVisitor(c_ast.NodeVisitor): + + def __init__(self): + self.local_variables = {} + self.function_params = {} + + def visit_FuncDef(self, node): + lv = [] + fp = [] + + find_local_vars(node, lambda f: lv.append(f.name)) + if isinstance(node.decl, c_ast.Decl) and isinstance(node.decl.type, c_ast.FuncDecl): + func_decl = node.decl.type + if isinstance(func_decl.args, c_ast.ParamList): + for arg in func_decl.args.params: + if isinstance(arg, c_ast.Decl): + fp.append(arg.name) + + self.local_variables[node.decl.name] = lv + self.function_params[node.decl.name] = fp + + +class RenameVariableVisitor(c_ast.NodeVisitor): + + def __init__(self, rename_mapping): + self.map = rename_mapping + + def visit_ID(self, node): + if node.name in self.map: + node.name = self.map[node.name] + + def visit_Decl(self, node): + if node.name in self.map: + rename_decl(node, self.map[node.name]) + + if node.init is not None: + self.visit(node.init) + + self.visit(node.type) + + +class IntroduceSemanticChangeVisitor(c_ast.NodeVisitor): + + # legal_local_variables: Only these variables may be used to introduce a change + def __init__(self, legal_local_variables): + self.in_fun = False + self.fun_name = None + + self.introduced_change = False + self.found_vars = [] + self.introduced_changes = [] + self.legal_local_variables = legal_local_variables + + def visit_ID(self, node): + if self.in_fun: + if any(found_var for found_var in self.found_vars if found_var.name == node.name): + known_var = [found_var for found_var in self.found_vars if found_var.name == node.name][0] + + # check if we can find another already declared var with the same type + other_decls = [var for var in self.found_vars if + var.type == known_var.type and + var.name != known_var.name and + var.name in self.legal_local_variables[self.fun_name] + ] + + # only introduce change if not already done so for this variable + if len(other_decls) > 0 and known_var.name not in self.introduced_changes: + node.name = other_decls[0].name + self.introduced_change = True + self.introduced_changes.append(known_var.name) + else: + node.name = known_var.name + + + def visit_FuncDef(self, node): + self.in_fun = True + self.fun_name = node.decl.name + self.found_vars = [] + self.introduced_changes = [] + visit_rest_of_func_def(self, node) + self.in_fun = False + self.fun_name = None + + def visit_Decl(self, node): + if self.in_fun and isinstance(node.type, c_ast.TypeDecl) or isinstance(node.type, c_ast.ArrayDecl): + if isinstance(node.type, TypeDecl) and isinstance(node.type.type, IdentifierType): + if len(node.type.type.names) == 1: + self.found_vars.append(LocalVar(node.name, node.type.type.names[0], node.name + "_updated")) + if node.init is not None: + self.visit(node.init) + + self.visit(node.type) + + +# find a single function to rename, but never main +class FindFunctionToRenameVisitor(c_ast.NodeVisitor): + + def __init__(self): + self.fun_name = None + self.updated_fun_name = None + + + def visit_FuncDef(self, node): + fun_name = node.decl.name + if fun_name != "main" and self.fun_name is None: + self.fun_name = fun_name + self.updated_fun_name = fun_name + "_updated" + + +class RenameFunctionVisitor(c_ast.NodeVisitor): + + def __init__(self, function_to_rename_name, updated_name): + self.function_to_rename_name = function_to_rename_name + self.updated_name = updated_name + + def visit_FuncDef(self, node): + fun_name = node.decl.name + if fun_name == self.function_to_rename_name: + node.decl.name = self.updated_name + node.decl.type.type.declname = self.updated_name + + visit_rest_of_func_def(self, node) + + + def visit_ID(self, node): + if node.name == self.function_to_rename_name: + node.name = self.updated_name + + +def create_modified_file(source_file: Path, task): + try: + ast = parse_file(source_file, use_cpp=True) + + introduced_change = False + renamed_anything = False + + if isinstance(task, TaskRenameLocals): + v = VarDeclVisitor() + v.visit(ast) + + rename_mapping = {} + local_vars = [x for xs in (list(v.local_variables.values()) + list(v.function_params.values())) for x in xs] + for local_var in local_vars: + rename_mapping[local_var] = local_var + "_updated" + + if task.introduce_changes: + x = IntroduceSemanticChangeVisitor(v.local_variables) + x.visit(ast) + + # print(CGenerator().visit(ast)) + # print("Introduced change:" + str(x.introduced_change)) + + introduced_change = x.introduced_change + else: + introduced_change = False + + RenameVariableVisitor(rename_mapping).visit(ast) + renamed_anything = len(local_vars) > 0 + + if isinstance(task, TaskRenameFunction): + v = FindFunctionToRenameVisitor() + v.visit(ast) + + renamed_anything = v.fun_name is not None + + if v.fun_name is not None: + v = RenameFunctionVisitor(v.fun_name, v.updated_fun_name) + v.visit(ast) + + introduced_change = False + + print(CGenerator().visit(ast)) + + tmp = tempfile.NamedTemporaryFile() + with open(tmp.name, "w") as f: + f.write(CGenerator().visit(ast)) + + return ModifiedFileResult(tmp, introduced_change, renamed_anything) + except: + return None + + +@dataclasses.dataclass +class ModifiedFileResult: + tmp: tempfile.NamedTemporaryFile + introduced_changes: bool + renamed_anything: bool + + +@dataclasses.dataclass +class LocalVar: + name: str + type: str + new_name: str + + +@dataclasses.dataclass +class TaskRenameLocals: + introduce_changes: bool + + +@dataclasses.dataclass +class TaskRenameFunction: + def __init__(self): + self + + +if __name__ == '__main__': + # result = create_modified_file(Path("scripts/test.c"), TaskRenameFunction()) + # print(result.introduced_changes) + # result.tmp.close() + main() diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml index 0b81b3a718..8a85f54854 100644 --- a/src/incremental/detectRenamedFunctions.ml +++ b/src/incremental/detectRenamedFunctions.ml @@ -56,11 +56,11 @@ let getFunctionAndGVarMap (ast: file) : f StringMap.t * v StringMap.t = | _ -> functionMap, gvarMap ) (StringMap.empty, StringMap.empty) -let performRenames (renamesOnSuccess: renamesOnSuccess) = +let performRenames (renamesOnSuccess: renamesOnSuccess) = begin - let (compinfoRenames, enumRenames) = renamesOnSuccess in - List.iter (fun (compinfo2, compinfo1) -> compinfo2.cname <- compinfo1.cname) compinfoRenames; - List.iter (fun (enum2, enum1) -> enum2.ename <- enum1.ename) enumRenames; + let (compinfoRenames, enumRenames) = renamesOnSuccess in + List.iter (fun (compinfo2, compinfo1) -> compinfo2.cname <- compinfo1.cname) compinfoRenames; + List.iter (fun (enum2, enum1) -> enum2.ename <- enum1.ename) enumRenames; end let getDependencies fromEq = VarinfoMap.map (fun assumption -> assumption.new_method_name) fromEq @@ -115,14 +115,17 @@ let registerGVarMapping oldV nowV data = { reverseMapping=data.reverseMapping; } +(*True iff the global var rename assumptions contains only entries that are identity mappings*) +let areGlobalVarRenameAssumptionsEmpty (mapping: glob_var_rename_assumptions) : bool = + VarinfoMap.for_all (fun varinfo newName -> varinfo.vname = newName) mapping (*returns true iff for all dependencies it is true, that the dependency has a corresponding function with the new name and matches the without having dependencies itself and the new name is not already present on the old AST. *) -let doAllDependenciesMatch (dependencies: functionDependencies) -(global_var_dependencies: glob_var_rename_assumptions) -(oldFunctionMap: f StringMap.t) -(nowFunctionMap: f StringMap.t) -(oldGVarMap: v StringMap.t) -(nowGVarMap: v StringMap.t) (data: carryType) : bool * carryType = +let doAllDependenciesMatch (dependencies: functionDependencies) + (global_var_dependencies: glob_var_rename_assumptions) + (oldFunctionMap: f StringMap.t) + (nowFunctionMap: f StringMap.t) + (oldGVarMap: v StringMap.t) + (nowGVarMap: v StringMap.t) (data: carryType) : bool * carryType = let isConsistent = fun old nowName allEqual getName getGlobal oldMap nowMap getNowOption data -> (*Early cutoff if a previous dependency returned false. @@ -142,31 +145,39 @@ let doAllDependenciesMatch (dependencies: functionDependencies) let nowElemOption = getNowOption nowName in match nowElemOption with - | Some(nowElem) -> - let compare = fun old now -> - match (old, now) with - | Fundec(oF), Fundec(nF) -> - let doMatch, _, _, function_dependencies, global_var_dependencies, renamesOnSuccess = CompareGlobals.eqF oF nF None VarinfoMap.empty VarinfoMap.empty in - doMatch, function_dependencies, global_var_dependencies, renamesOnSuccess - | GlobalVar(oV), GlobalVar(nV) -> - let (equal, (_, function_dependencies, global_var_dependencies, renamesOnSuccess)) = eq_varinfo oV nV emptyRenameMapping in - (*eq_varinfo always comes back with a self dependency. We need to filter that out.*) - equal, function_dependencies, (VarinfoMap.filter (fun vi name -> not (vi.vname = oV.vname && name = nowName)) global_var_dependencies), renamesOnSuccess - | _, _ -> failwith "Unknown or incompatible global types" - in - - - let doMatch, function_dependencies, global_var_dependencies, renamesOnSuccess = compare globalElem nowElem in - - (*let _ = Printf.printf "%s <-> %s: %b %b %b\n" (getName old) (globalElemName nowElem) doMatch (StringMap.is_empty function_dependencies) (VarinfoMap.is_empty global_var_dependencies) in - - let _ = Printf.printf "%s\n" (rename_mapping_to_string (StringMap.empty, function_dependencies, global_var_dependencies)) in - *) - if doMatch && VarinfoMap.is_empty function_dependencies && VarinfoMap.is_empty global_var_dependencies then - let _ = performRenames renamesOnSuccess in - true, registerMapping globalElem nowElem data - else false, data - + | Some(nowElem) -> ( + let compare = fun old now -> + match (old, now) with + | Fundec(oF), Fundec(nF) -> + let doMatch, _, _, function_dependencies, global_var_dependencies, renamesOnSuccess = CompareGlobals.eqF oF nF None VarinfoMap.empty VarinfoMap.empty in + doMatch, function_dependencies, global_var_dependencies, renamesOnSuccess + | GlobalVar(oV), GlobalVar(nV) -> + let (equal, (_, function_dependencies, global_var_dependencies, renamesOnSuccess)) = eq_varinfo oV nV emptyRenameMapping in + (*eq_varinfo always comes back with a self dependency. We need to filter that out.*) + equal, function_dependencies, (VarinfoMap.filter (fun vi name -> not (vi.vname = oV.vname && name = nowName)) global_var_dependencies), renamesOnSuccess + | _, _ -> failwith "Unknown or incompatible global types" + in + + + let doMatch, function_dependencies, global_var_dependencies, renamesOnSuccess = compare globalElem nowElem in + + (*let _ = Printf.printf "%s <-> %s: %b %b %b\n" (getName old) (globalElemName nowElem) doMatch (StringMap.is_empty function_dependencies) (VarinfoMap.is_empty global_var_dependencies) in + + let _ = Printf.printf "%s\n" (rename_mapping_to_string (StringMap.empty, function_dependencies, global_var_dependencies)) in + *) + + (*Having a dependency on yourself is ok.*) + let hasNoExternalDependency = VarinfoMap.is_empty function_dependencies || ( + VarinfoMap.cardinal function_dependencies = 1 && ( + VarinfoMap.fold (fun varinfo dependency _ -> varinfo.vname = globalElemName globalElem && dependency.new_method_name = globalElemName nowElem) function_dependencies true + ) + ) in + + if doMatch && hasNoExternalDependency && areGlobalVarRenameAssumptionsEmpty global_var_dependencies then + let _ = performRenames renamesOnSuccess in + true, registerMapping globalElem nowElem data + else false, data + ) | None -> false, data else false, data @@ -235,15 +246,15 @@ let detectRenamedFunctions (oldAST: file) (newAST: file) : output GlobalElemMap. let doMatch, unchangedHeader, _, function_dependencies, global_var_dependencies, renamesOnSuccess = CompareGlobals.eqF f newFun None VarinfoMap.empty VarinfoMap.empty in - (*Before renamesOnSuccess, functions with the same name have always been compared. + (*Before renamesOnSuccess, functions with the same name have always been compared. In this comparison, the renaming on compinfo and enum was always performed, no matter if the comparison was a success or not. This call mimics this behaviour.*) let _ = performRenames renamesOnSuccess in - (*let _ = Pretty.printf "%s <-> %s: %b %s\n" f.svar.vname newFun.svar.vname doMatch (rename_mapping_to_string (StringMap.empty, function_dependencies, global_var_dependencies)) in + let _ = Pretty.printf "%s <-> %s: %b %s\n" f.svar.vname newFun.svar.vname doMatch (rename_mapping_to_string (StringMap.empty, function_dependencies, global_var_dependencies, ([], []))) in let _ = Pretty.printf "old locals: %s\n" (String.concat ", " (List.map (fun x -> x.vname) f.slocals)) in - let _ = Pretty.printf "now locals: %s\n" (String.concat ", " (List.map (fun x -> x.vname) newFun.slocals)) in*) + let _ = Pretty.printf "now locals: %s\n" (String.concat ", " (List.map (fun x -> x.vname) newFun.slocals)) in let actDependencies = getDependencies function_dependencies in diff --git a/tests/incremental/05-method-rename/08-recursive_rename.c b/tests/incremental/05-method-rename/08-recursive_rename.c new file mode 100644 index 0000000000..dc9ac72e94 --- /dev/null +++ b/tests/incremental/05-method-rename/08-recursive_rename.c @@ -0,0 +1,7 @@ +void foo(int x) { + if(x > 1) foo(x - 1); +} + +int main() { + foo(10); +} diff --git a/tests/incremental/05-method-rename/08-recursive_rename.json b/tests/incremental/05-method-rename/08-recursive_rename.json new file mode 100644 index 0000000000..0db3279e44 --- /dev/null +++ b/tests/incremental/05-method-rename/08-recursive_rename.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/tests/incremental/05-method-rename/08-recursive_rename.patch b/tests/incremental/05-method-rename/08-recursive_rename.patch new file mode 100644 index 0000000000..42469f434c --- /dev/null +++ b/tests/incremental/05-method-rename/08-recursive_rename.patch @@ -0,0 +1,13 @@ +--- tests/incremental/05-method-rename/08-recursive_rename.c ++++ tests/incremental/05-method-rename/08-recursive_rename.c +@@ -1,7 +1,7 @@ +-void foo(int x) { +- if(x > 1) foo(x - 1); ++void bar(int x) { ++ if(x > 1) bar(x - 1); + } + + int main() { +- foo(10); ++ bar(10); + } From f8dba3e8598f2b0cc0b8ea75d4bc50341f027488 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Sat, 16 Jul 2022 16:15:17 +0200 Subject: [PATCH 61/90] Added doc on how to support library headers. --- scripts/test-refactorings-rename.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/scripts/test-refactorings-rename.py b/scripts/test-refactorings-rename.py index 4d3fd70edb..4339113eac 100644 --- a/scripts/test-refactorings-rename.py +++ b/scripts/test-refactorings-rename.py @@ -22,14 +22,17 @@ introduced_changes = 0 renamed_a_function = 0 +# to support library headers, first clone https://github.com/eliben/pycparser to the directory next of the analyzer folder. +# Then comment the lines out and in that are described that way. + def main(): regression_folder = Path("./tests/regression") - task = TaskRenameFunction() + task = TaskRenameLocals(False) - # test = regression_folder / "23-partitioned_arrays_last/06-interprocedural.c" - # execute_validation_test(test.parent, test, task) - # return + test = regression_folder / "25-vla/02-loop.c" + execute_validation_test(test.parent, test, task) + return excluded = [ "44-trier_analyzer/33-recA.c", @@ -110,6 +113,7 @@ def execute_validation_test(folder: Path, test_file: Path, task): print("Skipped test.") skips += 1 return False + # comment this if out if you want to support library headers if any(x.startswith("#include") for x in lines): print("Skipped test because of include") includes += 1 @@ -141,6 +145,16 @@ def execute_validation_test(folder: Path, test_file: Path, task): args = f"--enable dbg.debug --enable printstats -v {extra_params}" + # uncomment to support library headers. + # with tempfile.NamedTemporaryFile() as t: + # subprocess.run(f"cpp -E -I../pycparser/utils/fake_libc_include {test_file} > {t.name}", shell=True) + # + # + # x = subprocess.run(f"./goblint {args} --enable incremental.save {t.name}", shell=True, text=True, capture_output=True) + # if x.returncode != 0: + # includes += 1 + # return False + subprocess.run(f"./goblint {args} --enable incremental.save {test_file}", shell=True, capture_output=True) command = subprocess.run( @@ -349,6 +363,10 @@ def visit_ID(self, node): def create_modified_file(source_file: Path, task): try: + # uncommet to support library headers. + # gcc = subprocess.run(f"cpp -E -I../pycparser/utils/fake_libc_include {source_file}", shell=True, capture_output=True, text=True) + + # ast = c_parser.CParser().parse(gcc.stdout) ast = parse_file(source_file, use_cpp=True) introduced_change = False @@ -389,7 +407,7 @@ def create_modified_file(source_file: Path, task): introduced_change = False - print(CGenerator().visit(ast)) + # print(CGenerator().visit(ast)) tmp = tempfile.NamedTemporaryFile() with open(tmp.name, "w") as f: From d9a7c935f145586f7795dcd69f730ab7b912c01c Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Tue, 26 Jul 2022 19:54:44 +0200 Subject: [PATCH 62/90] Fixed a couple of bugs --- src/incremental/compareCIL.ml | 17 +- src/incremental/detectRenamedFunctions.ml | 191 +++++++++++++--------- 2 files changed, 131 insertions(+), 77 deletions(-) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 0e494075d1..5450ef2934 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -10,12 +10,11 @@ let empty_change_info () : change_info = {added = []; removed = []; changed = [] let eq_glob (a: global) (b: global) (cfgs : (cfg * (cfg * cfg)) option) = match a, b with | GFun (f,_), GFun (g,_) -> - let identical, unchangedHeader, diffOpt, _, _, renamesOnSuccess = CompareGlobals.eqF f g cfgs VarinfoMap.empty VarinfoMap.empty in + let identical, unchangedHeader, diffOpt, funDep, globVarDep, renamesOnSuccess = CompareGlobals.eqF f g cfgs VarinfoMap.empty VarinfoMap.empty in (*Perform renames no matter what.*) let _ = performRenames renamesOnSuccess in - - identical, unchangedHeader, diffOpt + identical && VarinfoMap.is_empty funDep && areGlobalVarRenameAssumptionsEmpty globVarDep, unchangedHeader, diffOpt | GVar (x, init_x, _), GVar (y, init_y, _) -> eq_varinfo x y emptyRenameMapping |> fst, 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 emptyRenameMapping |> fst, false, None | _ -> ignore @@ Pretty.printf "Not comparable: %a and %a\n" Cil.d_global a Cil.d_global b; false, false, None @@ -102,6 +101,18 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = Cil.iterGlobals newAST (fun glob -> findChanges oldMap glob); + if not (GobConfig.get_bool "incremental.detect-global-renames") then ( + let newMap = Cil.foldGlobals newAST addGlobal GlobalMap.empty in + + let checkExists map global = + match identifier_of_global global with + | name -> GlobalMap.mem name map + | exception Not_found -> true (* return true, so isn't considered a change *) + in + + Cil.iterGlobals newAST (fun glob -> if not (checkExists oldMap glob) then (changes.added <- (glob::changes.added))); + Cil.iterGlobals oldAST (fun glob -> if not (checkExists newMap glob) then (changes.removed <- (glob::changes.removed))); + ); changes (** Given an (optional) equality function between [Cil.global]s, an old and a new [Cil.file], this function computes a [change_info], diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml index 8a85f54854..603b7c28ce 100644 --- a/src/incremental/detectRenamedFunctions.ml +++ b/src/incremental/detectRenamedFunctions.ml @@ -14,6 +14,10 @@ let globalElemName elem = match elem with | Fundec(f) -> f.svar.vname | GlobalVar(v) -> v.vname +let globalElemName2 elem = match elem with + | Fundec(f) -> "Fundec(" ^ f.svar.vname ^ ")" + | GlobalVar(v) -> "GlobalVar(" ^ v.vname ^ ")" + module GlobalElemForMap = struct type t = globalElem @@ -78,6 +82,13 @@ type carryType = { reverseMapping: globalElem GlobalElemMap.t; } +let emptyCarryType = { + statusForOldElem = GlobalElemMap.empty; + statusForNowElem = GlobalElemMap.empty; + mapping = GlobalElemMap.empty; + reverseMapping = GlobalElemMap.empty; +} + (*Carry type manipulation functions.*) let registerStatusForOldF f status data = @@ -136,10 +147,13 @@ let doAllDependenciesMatch (dependencies: functionDependencies) let globalElem = getGlobal old in let knownMapping = GlobalElemMap.find_opt globalElem data.mapping in + (*let _ = Printf.printf "Dep: %s -> %s\n" (globalElemName2 globalElem) nowName in*) + (*To avoid inconsitencies, if a function has already been mapped to a function, that mapping is reused again.*) match knownMapping with | Some(knownElem) -> (*This function has already been mapped*) + (*let _ = Printf.printf "Already mapped. %s = %s\n" (globalElemName2 knownElem) nowName in*) globalElemName knownElem = nowName, data | None -> let nowElemOption = getNowOption nowName in @@ -161,11 +175,6 @@ let doAllDependenciesMatch (dependencies: functionDependencies) let doMatch, function_dependencies, global_var_dependencies, renamesOnSuccess = compare globalElem nowElem in - (*let _ = Printf.printf "%s <-> %s: %b %b %b\n" (getName old) (globalElemName nowElem) doMatch (StringMap.is_empty function_dependencies) (VarinfoMap.is_empty global_var_dependencies) in - - let _ = Printf.printf "%s\n" (rename_mapping_to_string (StringMap.empty, function_dependencies, global_var_dependencies)) in - *) - (*Having a dependency on yourself is ok.*) let hasNoExternalDependency = VarinfoMap.is_empty function_dependencies || ( VarinfoMap.cardinal function_dependencies = 1 && ( @@ -173,13 +182,19 @@ let doAllDependenciesMatch (dependencies: functionDependencies) ) ) in + (*let _ = Printf.printf "%s <-> %s: %b %b %b\n" (globalElemName2 globalElem) (globalElemName2 nowElem) doMatch hasNoExternalDependency (VarinfoMap.is_empty global_var_dependencies) in + + let _ = Printf.printf "%s\n" (rename_mapping_to_string (StringMap.empty, function_dependencies, global_var_dependencies, ([], []))) in*) + if doMatch && hasNoExternalDependency && areGlobalVarRenameAssumptionsEmpty global_var_dependencies then let _ = performRenames renamesOnSuccess in true, registerMapping globalElem nowElem data else false, data ) | None -> - false, data + (*Printf.printf "No elem with name %s found \n" nowName;*) + (*Return true assumes external globs never change. Which is ok for now*) + true, data else false, data in @@ -226,75 +241,62 @@ let assignStatusToUnassignedElem data f registerStatus statusMap mapping status else data -let detectRenamedFunctions (oldAST: file) (newAST: file) : output GlobalElemMap.t = begin - let oldFunctionMap, oldGVarMap = getFunctionAndGVarMap oldAST in - let nowFunctionMap, nowGVarMap = getFunctionAndGVarMap newAST in - - let initialData: carryType = {statusForOldElem = GlobalElemMap.empty; - statusForNowElem = GlobalElemMap.empty; - mapping=GlobalElemMap.empty; - reverseMapping=GlobalElemMap.empty; - } in - - (*Go through all functions, for all that have not been renamed *) - let finalData = - StringMap.fold (fun _ (f, _) (data: carryType) -> - let matchingNewFundec = StringMap.find_opt f.svar.vname nowFunctionMap in - match matchingNewFundec with - | Some (newFun, _) -> - (*Compare if they are similar*) - let doMatch, unchangedHeader, _, function_dependencies, global_var_dependencies, renamesOnSuccess = - CompareGlobals.eqF f newFun None VarinfoMap.empty VarinfoMap.empty in - - (*Before renamesOnSuccess, functions with the same name have always been compared. - In this comparison, the renaming on compinfo and enum was always performed, no matter if the comparison - was a success or not. This call mimics this behaviour.*) - let _ = performRenames renamesOnSuccess in - - let _ = Pretty.printf "%s <-> %s: %b %s\n" f.svar.vname newFun.svar.vname doMatch (rename_mapping_to_string (StringMap.empty, function_dependencies, global_var_dependencies, ([], []))) in - - let _ = Pretty.printf "old locals: %s\n" (String.concat ", " (List.map (fun x -> x.vname) f.slocals)) in - let _ = Pretty.printf "now locals: %s\n" (String.concat ", " (List.map (fun x -> x.vname) newFun.slocals)) in - - - let actDependencies = getDependencies function_dependencies in +let findSameNameMatchingGVars oldGVarMap nowGVarMap data = + StringMap.fold (fun _ (v, _, _) (data: carryType) -> + let matchingNowGvar = StringMap.find_opt v.vname nowGVarMap in + match matchingNowGvar with + | Some (nowGvar, _, _) -> ( + let identical, _ = eq_varinfo v nowGvar emptyRenameMapping in - let oldG = Fundec(f) in - let nowG = Fundec(newFun) in + let oldG, nowG = GlobalVar v, GlobalVar nowGvar in - - if doMatch then - let doDependenciesMatch, updatedData = doAllDependenciesMatch actDependencies global_var_dependencies oldFunctionMap nowFunctionMap oldGVarMap nowGVarMap data in - if doDependenciesMatch then - registerBiStatus oldG nowG (SameName(oldG)) updatedData - else - registerStatusForOldF oldG (Modified(nowG, unchangedHeader)) data |> - registerStatusForNowF nowG (Modified(oldG, unchangedHeader)) + if identical then + registerBiStatus (GlobalVar v) (GlobalVar nowGvar) (SameName (GlobalVar nowGvar)) data else - registerStatusForOldF oldG (Modified(nowG, unchangedHeader)) data |> - registerStatusForNowF nowG (Modified(oldG, unchangedHeader)) - | None -> data - ) oldFunctionMap initialData |> - (*At this point we already know of the functions that have changed and stayed the same. We now assign the correct status to all the functions that - have been mapped. The functions that have not been mapped are added/removed.*) - (*Now go through all old functions again. Those who have not been assigned a status are removed*) - StringMap.fold (fun _ (f, _) (data: carryType) -> - assignStatusToUnassignedElem data (Fundec(f)) registerStatusForOldF data.statusForOldElem data.mapping Deleted - ) oldFunctionMap |> - (*now go through all new functions. Those have have not been assigned a mapping are added.*) - StringMap.fold (fun _ (nowF, _) (data: carryType) -> - assignStatusToUnassignedElem data (Fundec(nowF)) registerStatusForNowF data.statusForNowElem data.reverseMapping Created - ) nowFunctionMap |> - StringMap.fold (fun _ (v, _, _) data -> - assignStatusToUnassignedElem data (GlobalVar(v)) registerStatusForOldF data.statusForOldElem data.mapping Deleted - ) oldGVarMap |> - StringMap.fold (fun _ (nowV, _, _) (data: carryType) -> - assignStatusToUnassignedElem data (GlobalVar(nowV)) registerStatusForNowF data.statusForNowElem data.reverseMapping Created - ) nowGVarMap - in - - (*Done with the analyis, the following just adjusts the output types.*) - + registerStatusForOldF oldG (Modified(nowG, false)) data |> + registerStatusForNowF nowG (Modified(oldG, false)) + ) + | None -> data + ) oldGVarMap data + +(*Goes through all old functions and looks for now-functions with the same name. If a pair has been found, onMatch is called with the comparison result. + On match then modifies the carryType. Returns (list of the functions that have the same name and match, the updated carry type)*) +let findSameNameMatchingFunctions + oldFunctionMap + nowFunctionMap + (initialData: 'a) + (onMatch: fundec -> fundec -> bool -> bool -> string VarinfoMap.t -> CompareGlobals.glob_var_rename_assumptions -> CompareGlobals.renamesOnSuccess -> 'a -> 'a) : 'a = + StringMap.fold (fun _ (f, _) (data: 'a) -> + let matchingNewFundec = StringMap.find_opt f.svar.vname nowFunctionMap in + match matchingNewFundec with + | Some (newFun, _) -> + (*Compare if they are similar*) + let doMatch, unchangedHeader, _, function_dependencies, global_var_dependencies, renamesOnSuccess = CompareGlobals.eqF f newFun None VarinfoMap.empty VarinfoMap.empty in + + let actDependencies = getDependencies function_dependencies in + + onMatch f newFun doMatch unchangedHeader actDependencies global_var_dependencies renamesOnSuccess data + | None -> data + ) oldFunctionMap initialData + +let fillStatusForUnassignedElems oldFunctionMap nowFunctionMap oldGVarMap nowGVarMap (data: carryType) = + data |> + (*Now go through all old functions again. Those who have not been assigned a status are removed*) + StringMap.fold (fun _ (f, _) (data: carryType) -> + assignStatusToUnassignedElem data (Fundec f) registerStatusForOldF data.statusForOldElem data.mapping Deleted + ) oldFunctionMap |> + (*now go through all new functions. Those have have not been assigned a mapping are added.*) + StringMap.fold (fun _ (nowF, _) (data: carryType) -> + assignStatusToUnassignedElem data (Fundec nowF) registerStatusForNowF data.statusForNowElem data.reverseMapping Created + ) nowFunctionMap |> + StringMap.fold (fun _ (v, _, _) data -> + assignStatusToUnassignedElem data (GlobalVar(v)) registerStatusForOldF data.statusForOldElem data.mapping Deleted + ) oldGVarMap |> + StringMap.fold (fun _ (nowV, _, _) (data: carryType) -> + assignStatusToUnassignedElem data (GlobalVar(nowV)) registerStatusForNowF data.statusForNowElem data.reverseMapping Created + ) nowGVarMap + +let mapAnalysisResultToOutput oldFunctionMap nowFunctionMap oldGVarMap nowGVarMap (data: carryType) : output GlobalElemMap.t = (*Map back to GFun and exposed function status*) let extractOutput funMap invertedFunMap gvarMap invertedGvarMap f (s: status) = let getGlobal gT fundecMap gVarMap = @@ -323,6 +325,47 @@ let detectRenamedFunctions (oldAST: file) (newAST: file) : output GlobalElemMap. else if Option.is_some b then b else None ) - (GlobalElemMap.mapi (extractOutput oldFunctionMap nowFunctionMap oldGVarMap nowGVarMap) finalData.statusForOldElem) - (GlobalElemMap.mapi (extractOutput nowFunctionMap oldFunctionMap nowGVarMap oldGVarMap) finalData.statusForNowElem) + (GlobalElemMap.mapi (extractOutput oldFunctionMap nowFunctionMap oldGVarMap nowGVarMap) data.statusForOldElem) + (GlobalElemMap.mapi (extractOutput nowFunctionMap oldFunctionMap nowGVarMap oldGVarMap) data.statusForNowElem) + +let detectRenamedFunctions (oldAST: file) (newAST: file) : output GlobalElemMap.t = begin + let oldFunctionMap, oldGVarMap = getFunctionAndGVarMap oldAST in + let nowFunctionMap, nowGVarMap = getFunctionAndGVarMap newAST in + + (*let show x = [%show: (string * string) list] (StringMap.to_seq x |> Seq.map (fun (name, (v, _, _)) -> (name, v.vname)) |> List.of_seq) in + + let _ = Printf.printf "oldGvarMap: %s" (show oldGVarMap) in + let _ = Printf.printf "nowGvarMap: %s" (show nowGVarMap) in*) + + + let initialData: carryType = findSameNameMatchingGVars oldGVarMap nowGVarMap emptyCarryType in + + (*Go through all functions, for all that have not been renamed *) + let finalData = findSameNameMatchingFunctions oldFunctionMap nowFunctionMap initialData (fun oldF nowF doMatch unchangedHeader functionDependencies global_var_dependencies renamesOnSuccess data -> + let oldG = Fundec(oldF) in + let nowG = Fundec(nowF) in + + (*let _ = Printf.printf "1. Same Name: %s <-> %s: %b, %b\n" oldF.svar.vname nowF.svar.vname doMatch unchangedHeader in*) + + if doMatch then + let doDependenciesMatch, updatedData = doAllDependenciesMatch functionDependencies global_var_dependencies oldFunctionMap nowFunctionMap oldGVarMap nowGVarMap data in + + (*let _ = Printf.printf "2. Same Name: %s <-> %s: %b\n" oldF.svar.vname nowF.svar.vname doDependenciesMatch in*) + + if doDependenciesMatch then + registerBiStatus oldG nowG (SameName(oldG)) updatedData + else + registerStatusForOldF oldG (Modified(nowG, unchangedHeader)) data |> + registerStatusForNowF nowG (Modified(oldG, unchangedHeader)) + else + registerStatusForOldF oldG (Modified(nowG, unchangedHeader)) data |> + registerStatusForNowF nowG (Modified(oldG, unchangedHeader)) + ) |> + (*At this point we already know of the functions that have changed and stayed the same. We now assign the correct status to all the functions that + have been mapped. The functions that have not been mapped are added/removed.*) + fillStatusForUnassignedElems oldFunctionMap nowFunctionMap oldGVarMap nowGVarMap + in + + (*Done with the analyis, the following just adjusts the output types.*) + mapAnalysisResultToOutput oldFunctionMap nowFunctionMap oldGVarMap nowGVarMap finalData end From fa9747b0ac7c06b1c849f9c483a33ce25ea93329 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Mon, 6 Mar 2023 16:12:05 +0100 Subject: [PATCH 63/90] remove GlobalElemMap --- src/incremental/compareCIL.ml | 14 +- src/incremental/compareGlobals.ml | 15 ++ src/incremental/detectRenamedFunctions.ml | 263 +++++++++------------- 3 files changed, 126 insertions(+), 166 deletions(-) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 238bf06cb1..c58c34d039 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -46,7 +46,7 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = Not_found -> map in - (* Store a map from functionNames in the old file to the function definition*) + (* Store a map from global names in the old file to the globals declarations and/or definition *) let oldMap = Cil.foldGlobals oldAST addGlobal GlobalMap.empty in let newMap = Cil.foldGlobals newAST addGlobal GlobalMap.empty in @@ -72,13 +72,13 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = in if GobConfig.get_bool "incremental.detect-renames" then ( - let renameDetectionResults = detectRenamedFunctions oldAST newAST in + let renameDetectionResults = detectRenamedFunctions oldMap newMap in if Messages.tracing then - GlobalElemMap.to_seq renameDetectionResults |> + GlobalColMap.to_seq renameDetectionResults |> Seq.iter (fun (gT, (functionGlobal, status)) -> - Messages.trace "compareCIL" "Function status of %s is=" (globalElemName gT); + Messages.trace "compareCIL" "Function status of %s is=" (name_of_global_col gT); match status with | Unchanged _ -> Messages.trace "compareCIL" "Same Name\n"; | Added -> Messages.trace "compareCIL" "Added\n"; @@ -91,7 +91,7 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = | _ -> (); ); - let unchanged, changed, added, removed = GlobalElemMap.fold (fun _ (global, status) (u, c, a, r) -> + let unchanged, changed, added, removed = GlobalColMap.fold (fun _ (global, status) (u, c, a, r) -> match status with | Unchanged now -> (u @ [{old=global; current=now}], c, a, r) | UnchangedButRenamed now -> (u @ [{old=global; current=now}], c, a, r) @@ -105,15 +105,13 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = changes.removed <- removed; changes.changed <- changed; changes.unchanged <- unchanged; - ) else (); + ); (* 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. *) GlobalMap.iter (fun name glob_col -> findChanges oldMap name glob_col) newMap; if not (GobConfig.get_bool "incremental.detect-renames") then ( - let newMap = Cil.foldGlobals newAST addGlobal GlobalMap.empty in - GlobalMap.iter (fun name glob -> if not (GlobalMap.mem name newMap) then changes.removed <- (glob::changes.removed)) oldMap; ); changes diff --git a/src/incremental/compareGlobals.ml b/src/incremental/compareGlobals.ml index 2372a9f4f6..2686e16a86 100644 --- a/src/incremental/compareGlobals.ml +++ b/src/incremental/compareGlobals.ml @@ -9,6 +9,21 @@ module GlobalMap = Map.Make(String) type global_def = Var of varinfo | Fun of fundec type global_col = {decls: varinfo option; def: global_def option} +let name_of_global_col gc = match gc.def with + | Some (Fun f) -> f.svar.vname + | Some (Var v) -> v.vname + | None -> match gc.decls with + | Some v -> v.vname + | None -> raise (Failure "empty global record") + +let compare_global_col gc1 gc2 = compare (name_of_global_col gc1) (name_of_global_col gc2) + +module GlobalColMap = Map.Make( + struct + type t = global_col + let compare = compare_global_col + end) + let name_of_global g = match g with | GVar (v,_,_) -> v.vname | GFun (f,_) -> f.svar.vname diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml index 4454b9c77f..e3ab0328e2 100644 --- a/src/incremental/detectRenamedFunctions.ml +++ b/src/incremental/detectRenamedFunctions.ml @@ -7,40 +7,20 @@ module StringSet = Set.Make(String) type f = fundec * location type v = varinfo * initinfo * location -type globalElem = Fundec of fundec | GlobalVar of varinfo - -let globalElemName elem = match elem with - | Fundec(f) -> f.svar.vname - | GlobalVar(v) -> v.vname - -let globalElemName2 elem = match elem with - | Fundec(f) -> "Fundec(" ^ f.svar.vname ^ ")" - | GlobalVar(v) -> "GlobalVar(" ^ v.vname ^ ")" - -module GlobalElemForMap = struct - type t = globalElem - - let compare x y = String.compare (globalElemName x) (globalElemName y) -end - -module GlobalElemMap = Map.Make(GlobalElemForMap) - (*A dependency maps the function it depends on to the name the function has to be changed to*) type functionDependencies = string VarinfoMap.t (*Renamed: newName * dependencies; Modified=now*unchangedHeader*) -type status = SameName of globalElem | Renamed of globalElem | Created | Deleted | Modified of globalElem * bool +type status = SameName of global_col | Renamed of global_col | Created | Deleted | Modified of global_col * bool type outputFunctionStatus = Unchanged of global_col | UnchangedButRenamed of global_col | Added | Removed | Changed of global_col * bool type output = global_col * outputFunctionStatus - - let pretty (f: status) = match f with | SameName _ -> "SameName" - | Renamed x -> ("Renamed to " ^ globalElemName x) + | Renamed x -> ("Renamed to " ^ CompareGlobals.name_of_global_col x) | Created -> "Added" | Deleted -> "Removed" | Modified _ -> "Changed" @@ -75,23 +55,23 @@ let getDependencies fromEq = VarinfoMap.map (fun assumption -> assumption.new_me reversemapping: see method mapping, but from now -> old *) type carryType = { - statusForOldElem: status GlobalElemMap.t; - statusForNowElem: status GlobalElemMap.t; - mapping: globalElem GlobalElemMap.t; - reverseMapping: globalElem GlobalElemMap.t; + statusForOldElem : status GlobalColMap.t; + statusForNowElem : status GlobalColMap.t; + mapping: global_col GlobalColMap.t; + reverseMapping: global_col GlobalColMap.t; } let emptyCarryType = { - statusForOldElem = GlobalElemMap.empty; - statusForNowElem = GlobalElemMap.empty; - mapping = GlobalElemMap.empty; - reverseMapping = GlobalElemMap.empty; + statusForOldElem = GlobalColMap.empty; + statusForNowElem = GlobalColMap.empty; + mapping = GlobalColMap.empty; + reverseMapping = GlobalColMap.empty; } (*Carry type manipulation functions.*) let registerStatusForOldF f status data = - {statusForOldElem = GlobalElemMap.add f status data.statusForOldElem; + {statusForOldElem = GlobalColMap.add f status data.statusForOldElem; statusForNowElem=data.statusForNowElem; mapping=data.mapping; reverseMapping=data.reverseMapping; @@ -99,14 +79,14 @@ let registerStatusForOldF f status data = let registerStatusForNowF f status data = {statusForOldElem = data.statusForOldElem; - statusForNowElem=GlobalElemMap.add f status data.statusForNowElem; + statusForNowElem=GlobalColMap.add f status data.statusForNowElem; mapping=data.mapping; reverseMapping=data.reverseMapping; } -let registerBiStatus (oldF: globalElem) (nowF: globalElem) (status: status) data = - {statusForOldElem=GlobalElemMap.add oldF status data.statusForOldElem; - statusForNowElem=GlobalElemMap.add nowF status data.statusForNowElem; +let registerBiStatus (oldF: global_col) (nowF: global_col) (status: status) data = + {statusForOldElem=GlobalColMap.add oldF status data.statusForOldElem; + statusForNowElem=GlobalColMap.add nowF status data.statusForNowElem; mapping=data.mapping; reverseMapping=data.reverseMapping; } @@ -114,8 +94,8 @@ let registerBiStatus (oldF: globalElem) (nowF: globalElem) (status: status) data let registerMapping oldF nowF data = {statusForOldElem=data.statusForOldElem; statusForNowElem=data.statusForNowElem; - mapping=GlobalElemMap.add oldF nowF data.mapping; - reverseMapping=GlobalElemMap.add nowF oldF data.reverseMapping; + mapping=GlobalColMap.add oldF nowF data.mapping; + reverseMapping=GlobalColMap.add nowF oldF data.reverseMapping; } let registerGVarMapping oldV nowV data = { @@ -132,19 +112,16 @@ let areGlobalVarRenameAssumptionsEmpty (mapping: glob_var_rename_assumptions) : (*returns true iff for all dependencies it is true, that the dependency has a corresponding function with the new name and matches the without having dependencies itself and the new name is not already present on the old AST. *) let doAllDependenciesMatch (dependencies: functionDependencies) (global_var_dependencies: glob_var_rename_assumptions) - (oldFunctionMap: f StringMap.t) - (nowFunctionMap: f StringMap.t) - (oldGVarMap: v StringMap.t) - (nowGVarMap: v StringMap.t) (data: carryType) : bool * carryType = + (oldMap: global_col StringMap.t) + (newMap: global_col StringMap.t) (data: carryType) : bool * carryType = - let isConsistent = fun old nowName allEqual getName getGlobal oldMap nowMap getNowOption data -> + let isConsistent = fun old nowName allEqual getName oldMap nowMap getNowOption data -> (*Early cutoff if a previous dependency returned false. We never create a mapping between globs where the now name was already part of the old set or the old name is part of the now set. But only if now and old differ. *) if allEqual && (getName old = nowName || (not (StringMap.mem nowName oldMap) && not (StringMap.mem (getName old) nowMap))) then - let globalElem = getGlobal old in - let knownMapping = GlobalElemMap.find_opt globalElem data.mapping in + let knownMapping = GlobalColMap.find_opt old data.mapping in (*let _ = Printf.printf "Dep: %s -> %s\n" (globalElemName2 globalElem) nowName in*) @@ -153,31 +130,34 @@ let doAllDependenciesMatch (dependencies: functionDependencies) | Some(knownElem) -> (*This function has already been mapped*) (*let _ = Printf.printf "Already mapped. %s = %s\n" (globalElemName2 knownElem) nowName in*) - globalElemName knownElem = nowName, data + name_of_global_col knownElem = nowName, data | None -> let nowElemOption = getNowOption nowName in match nowElemOption with | Some(nowElem) -> ( let compare = fun old now -> - match (old, now) with - | Fundec(oF), Fundec(nF) -> - let doMatch, _, function_dependencies, global_var_dependencies, renamesOnSuccess = CompareGlobals.eqF oF nF None VarinfoMap.empty VarinfoMap.empty in - doMatch, function_dependencies, global_var_dependencies, renamesOnSuccess - | GlobalVar(oV), GlobalVar(nV) -> - let (equal, (_, function_dependencies, global_var_dependencies, renamesOnSuccess)) = eq_varinfo oV nV ~rename_mapping:empty_rename_mapping in + let compareVar oV nV = let (equal, (_, function_dependencies, global_var_dependencies, renamesOnSuccess)) = eq_varinfo oV nV ~rename_mapping:empty_rename_mapping in (*eq_varinfo always comes back with a self dependency. We need to filter that out.*) unchanged_to_change_status equal, function_dependencies, (VarinfoMap.filter (fun vi name -> not (vi.vname = oV.vname && name = nowName)) global_var_dependencies), renamesOnSuccess + in + match (old.def, now.def) with + | Some (Fun oF), Some (Fun nF) -> + let doMatch, _, function_dependencies, global_var_dependencies, renamesOnSuccess = CompareGlobals.eqF oF nF None VarinfoMap.empty VarinfoMap.empty in + doMatch, function_dependencies, global_var_dependencies, renamesOnSuccess + | Some (Var oV), Some (Var nV) -> compareVar oV nV + | None, None -> (match old.decls, now.decls with + | Some oV, Some nV -> compareVar oV nV + | _ -> failwith "Unknown or incompatible global types") | _, _ -> failwith "Unknown or incompatible global types" in - - let doMatch, function_dependencies, global_var_dependencies, renamesOnSuccess = compare globalElem nowElem in + let doMatch, function_dependencies, global_var_dependencies, renamesOnSuccess = compare old nowElem in (*Having a dependency on yourself is ok.*) let hasNoExternalDependency = VarinfoMap.is_empty function_dependencies || ( VarinfoMap.cardinal function_dependencies = 1 && ( - VarinfoMap.fold (fun varinfo dependency _ -> varinfo.vname = globalElemName globalElem && dependency.new_method_name = globalElemName nowElem) function_dependencies true + VarinfoMap.fold (fun varinfo dependency _ -> varinfo.vname = name_of_global_col old && dependency.new_method_name = name_of_global_col nowElem) function_dependencies true ) ) in @@ -188,7 +168,7 @@ let doAllDependenciesMatch (dependencies: functionDependencies) match doMatch with | Unchanged when hasNoExternalDependency && areGlobalVarRenameAssumptionsEmpty global_var_dependencies -> let _ = performRenames renamesOnSuccess in - true, registerMapping globalElem nowElem data + true, registerMapping old nowElem data | _ -> false, data ) | None -> @@ -199,32 +179,26 @@ let doAllDependenciesMatch (dependencies: functionDependencies) in VarinfoMap.fold (fun old nowName (allEqual, data) -> - let (old, _) = StringMap.find old.vname oldFunctionMap in + let old = StringMap.find old.vname oldMap in isConsistent old nowName allEqual - (fun x -> x.svar.vname) - (fun x -> Fundec(x)) - oldFunctionMap - nowFunctionMap - (fun x -> - Option.bind (StringMap.find_opt x nowFunctionMap) (fun (x, _) -> Some(Fundec(x))) - ) + (fun x -> name_of_global_col x) + oldMap + newMap + (fun x -> StringMap.find_opt x newMap) data ) dependencies (true, data) |> VarinfoMap.fold (fun oldVarinfo nowName (allEqual, data) -> isConsistent - oldVarinfo + (GlobalMap.find oldVarinfo.vname oldMap) nowName allEqual - (fun x -> x.vname) - (fun x -> GlobalVar(x)) - oldGVarMap - nowGVarMap - (fun x -> - Option.bind (StringMap.find_opt x nowGVarMap) (fun (x, _, _) -> Some(GlobalVar(x))) - ) + (fun x -> name_of_global_col x) + oldMap + newMap + (fun x -> StringMap.find_opt x newMap) data ) global_var_dependencies @@ -232,32 +206,36 @@ let doAllDependenciesMatch (dependencies: functionDependencies) (*Check if f has already been assigned a status. If yes do nothing. If not, check if the function took part in the mapping, then register it to have been renamed. Otherwise register it as the supplied status.*) let assignStatusToUnassignedElem data f registerStatus statusMap mapping status = - if not (GlobalElemMap.mem f statusMap) then - if (GlobalElemMap.mem f mapping) then - registerStatus f (Renamed (GlobalElemMap.find f mapping)) data + if not (GlobalColMap.mem f statusMap) then + if (GlobalColMap.mem f mapping) then + registerStatus f (Renamed (GlobalColMap.find f mapping)) data else (*this function has been added/removed*) registerStatus f status data else data -let findSameNameMatchingGVars oldGVarMap nowGVarMap data = - StringMap.fold (fun _ (v, _, _) (data: carryType) -> - let matchingNowGvar = StringMap.find_opt v.vname nowGVarMap in - match matchingNowGvar with - | Some (nowGvar, _, _) -> ( - let identical, _ = eq_varinfo v nowGvar ~rename_mapping:empty_rename_mapping in - - let oldG, nowG = GlobalVar v, GlobalVar nowGvar in - - if identical then - registerBiStatus (GlobalVar v) (GlobalVar nowGvar) (SameName (GlobalVar nowGvar)) data - else - registerStatusForOldF oldG (Modified(nowG, false)) data |> - registerStatusForNowF nowG (Modified(oldG, false)) - ) - | None -> data - ) oldGVarMap data +let findSameNameMatchingGVars (oldMap : global_col StringMap.t) (newMap : global_col StringMap.t) data = + let compare_varinfo v1 v2 data = + let identical, _ = eq_varinfo v1 v2 ~rename_mapping:empty_rename_mapping in + let oldG, nowG = GlobalMap.find v1.vname oldMap, GlobalMap.find v2.vname newMap in + if identical then + registerBiStatus oldG nowG (SameName nowG) data + else + registerStatusForOldF oldG (Modified(nowG, false)) data |> + registerStatusForNowF nowG (Modified(oldG, false)) + in + StringMap.fold (fun name gc_old (data: carryType) -> + try + let gc_new = StringMap.find name newMap in + match gc_old.def, gc_new.def with + | Some (Var v1), Some (Var v2) -> compare_varinfo v1 v2 data + | None, None -> (match gc_old.decls, gc_new.decls with + | Some v1, Some v2 -> compare_varinfo v1 v2 data + | _ -> data) + | _ -> data + with Not_found -> data + ) oldMap data (*Goes through all old functions and looks for now-functions with the same name. If a pair has been found, onMatch is called with the comparison result. On match then modifies the carryType. Returns (list of the functions that have the same name and match, the updated carry type)*) @@ -266,92 +244,62 @@ let findSameNameMatchingFunctions nowFunctionMap (initialData: 'a) (onMatch: fundec -> fundec -> change_status -> string VarinfoMap.t -> CompareGlobals.glob_var_rename_assumptions -> CompareGlobals.renamesOnSuccess -> 'a -> 'a) : 'a = - StringMap.fold (fun _ (f, _) (data: 'a) -> - let matchingNewFundec = StringMap.find_opt f.svar.vname nowFunctionMap in - match matchingNewFundec with - | Some (newFun, _) -> - (*Compare if they are similar*) - let doMatch, _, function_dependencies, global_var_dependencies, renamesOnSuccess = CompareGlobals.eqF f newFun None VarinfoMap.empty VarinfoMap.empty in - - let actDependencies = getDependencies function_dependencies in - - onMatch f newFun doMatch actDependencies global_var_dependencies renamesOnSuccess data - | None -> data + StringMap.fold (fun name oldFun data -> + try + let newFun = StringMap.find name nowFunctionMap in + match oldFun.def, newFun.def with + | Some (Fun f1), Some (Fun f2) -> + let doMatch, _, function_dependencies, global_var_dependencies, renamesOnSuccess = CompareGlobals.eqF f1 f2 None VarinfoMap.empty VarinfoMap.empty in + let actDependencies = getDependencies function_dependencies in + onMatch f1 f2 doMatch actDependencies global_var_dependencies renamesOnSuccess data + | _ -> data + with Not_found -> data ) oldFunctionMap initialData -let fillStatusForUnassignedElems oldFunctionMap nowFunctionMap oldGVarMap nowGVarMap (data: carryType) = +let fillStatusForUnassignedElems oldMap newMap (data: carryType) = data |> (*Now go through all old functions again. Those who have not been assigned a status are removed*) - StringMap.fold (fun _ (f, _) (data: carryType) -> - assignStatusToUnassignedElem data (Fundec f) registerStatusForOldF data.statusForOldElem data.mapping Deleted - ) oldFunctionMap |> + StringMap.fold (fun name f (data: carryType) -> + assignStatusToUnassignedElem data f registerStatusForOldF data.statusForOldElem data.mapping Deleted + ) oldMap |> (*now go through all new functions. Those have have not been assigned a mapping are added.*) - StringMap.fold (fun _ (nowF, _) (data: carryType) -> - assignStatusToUnassignedElem data (Fundec nowF) registerStatusForNowF data.statusForNowElem data.reverseMapping Created - ) nowFunctionMap |> - StringMap.fold (fun _ (v, _, _) data -> - assignStatusToUnassignedElem data (GlobalVar(v)) registerStatusForOldF data.statusForOldElem data.mapping Deleted - ) oldGVarMap |> - StringMap.fold (fun _ (nowV, _, _) (data: carryType) -> - assignStatusToUnassignedElem data (GlobalVar(nowV)) registerStatusForNowF data.statusForNowElem data.reverseMapping Created - ) nowGVarMap - -let mapAnalysisResultToOutput oldFunctionMap nowFunctionMap oldGVarMap nowGVarMap (data: carryType) : output GlobalElemMap.t = - (*Map back to GFun and exposed function status*) - let extractOutput funMap invertedFunMap gvarMap invertedGvarMap f (s: status) = - let getGlobal gT fundecMap gVarMap = - match gT with - | Fundec(f2) -> - let (f, l) = StringMap.find f2.svar.vname fundecMap in - {decls = None; def = Some(Fun f);} - | GlobalVar(v2) -> - let (v, i, l) = StringMap.find v2.vname gVarMap in - {decls = None; def = Some(Var v);} - in + StringMap.fold (fun name nowF (data: carryType) -> + assignStatusToUnassignedElem data nowF registerStatusForNowF data.statusForNowElem data.reverseMapping Created + ) newMap +let mapAnalysisResultToOutput (oldMap : global_col StringMap.t) (newMap : global_col StringMap.t) (data: carryType) : output GlobalColMap.t = + (*Map back to GFun and exposed function status*) + let extractOutput f (s: status) = let outputS = match s with - | SameName x -> Unchanged (getGlobal x invertedFunMap invertedGvarMap) - | Renamed x -> UnchangedButRenamed(getGlobal x invertedFunMap invertedGvarMap) + | SameName x -> Unchanged x + | Renamed x -> UnchangedButRenamed x | Created -> Added | Deleted -> Removed - | Modified (x, unchangedHeader) -> Changed (getGlobal x invertedFunMap invertedGvarMap, unchangedHeader) + | Modified (x, unchangedHeader) -> Changed (x, unchangedHeader) in - getGlobal f funMap gvarMap, outputS + f, outputS in (*Merge together old and now functions*) - GlobalElemMap.merge (fun _ a b -> + GlobalColMap.merge (fun _ a b -> if Option.is_some a then a else if Option.is_some b then b else None ) - (GlobalElemMap.mapi (extractOutput oldFunctionMap nowFunctionMap oldGVarMap nowGVarMap) data.statusForOldElem) - (GlobalElemMap.mapi (extractOutput nowFunctionMap oldFunctionMap nowGVarMap oldGVarMap) data.statusForNowElem) + (GlobalColMap.mapi extractOutput data.statusForOldElem) + (GlobalColMap.mapi extractOutput data.statusForNowElem) -let detectRenamedFunctions (oldAST: file) (newAST: file) : output GlobalElemMap.t = begin - let oldFunctionMap, oldGVarMap = getFunctionAndGVarMap oldAST in - let nowFunctionMap, nowGVarMap = getFunctionAndGVarMap newAST in - - (*let show x = [%show: (string * string) list] (StringMap.to_seq x |> Seq.map (fun (name, (v, _, _)) -> (name, v.vname)) |> List.of_seq) in - - let _ = Printf.printf "oldGvarMap: %s" (show oldGVarMap) in - let _ = Printf.printf "nowGvarMap: %s" (show nowGVarMap) in*) - - - let initialData: carryType = findSameNameMatchingGVars oldGVarMap nowGVarMap emptyCarryType in +let detectRenamedFunctions (oldMap : global_col StringMap.t) (newMap : global_col StringMap.t) : output GlobalColMap.t = + let initialData: carryType = findSameNameMatchingGVars oldMap newMap emptyCarryType in (*Go through all functions, for all that have not been renamed *) - let finalData = findSameNameMatchingFunctions oldFunctionMap nowFunctionMap initialData (fun oldF nowF change_status functionDependencies global_var_dependencies renamesOnSuccess data -> - let oldG = Fundec(oldF) in - let nowG = Fundec(nowF) in - - (*let _ = Printf.printf "1. Same Name: %s <-> %s: %b, %b\n" oldF.svar.vname nowF.svar.vname doMatch unchangedHeader in*) + let finalData = findSameNameMatchingFunctions oldMap newMap initialData (fun oldF nowF change_status functionDependencies global_var_dependencies renamesOnSuccess data -> + let oldG = GlobalMap.find oldF.svar.vname oldMap in + let nowG = GlobalMap.find nowF.svar.vname newMap in match change_status with | Unchanged -> - let doDependenciesMatch, updatedData = doAllDependenciesMatch functionDependencies global_var_dependencies oldFunctionMap nowFunctionMap oldGVarMap nowGVarMap data in - - (*let _ = Printf.printf "2. Same Name: %s <-> %s: %b\n" oldF.svar.vname nowF.svar.vname doDependenciesMatch in*) + let doDependenciesMatch, updatedData = doAllDependenciesMatch functionDependencies global_var_dependencies oldMap newMap data in if doDependenciesMatch then registerBiStatus oldG nowG (SameName(oldG)) updatedData @@ -366,10 +314,9 @@ let detectRenamedFunctions (oldAST: file) (newAST: file) : output GlobalElemMap. registerStatusForNowF nowG (Modified (oldG, false)) ) |> (*At this point we already know of the functions that have changed and stayed the same. We now assign the correct status to all the functions that - have been mapped. The functions that have not been mapped are added/removed.*) - fillStatusForUnassignedElems oldFunctionMap nowFunctionMap oldGVarMap nowGVarMap + have been mapped. The functions that have not been mapped are added/removed.*) + fillStatusForUnassignedElems oldMap newMap in (*Done with the analyis, the following just adjusts the output types.*) - mapAnalysisResultToOutput oldFunctionMap nowFunctionMap oldGVarMap nowGVarMap finalData -end + mapAnalysisResultToOutput oldMap newMap finalData From 8884a9b332a606c3078c4709331d079876db1234 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Tue, 7 Mar 2023 12:48:45 +0100 Subject: [PATCH 64/90] fix merge mistake --- src/incremental/compareCIL.ml | 5 ++--- src/incremental/compareGlobals.ml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index c58c34d039..23be2d9223 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -12,9 +12,8 @@ let eq_glob (old: global_col) (current: global_col) (cfgs : (cfg * (cfg * cfg)) (*Perform renames no matter what.*) let _ = performRenames renamesOnSuccess in match identical with - | Unchanged when VarinfoMap.is_empty funDep && areGlobalVarRenameAssumptionsEmpty globVarDep -> Unchanged, diffOpt - | _ -> Changed, None) - + | Unchanged when not (VarinfoMap.is_empty funDep && areGlobalVarRenameAssumptionsEmpty globVarDep) -> Changed, diffOpt + | s -> s, diffOpt) | None, None -> (match old.decls, current.decls with | Some x, Some y -> unchanged_to_change_status (eq_varinfo x y ~rename_mapping:empty_rename_mapping |> fst), None | _, _ -> failwith "should never collect any empty entries in GlobalMap") diff --git a/src/incremental/compareGlobals.ml b/src/incremental/compareGlobals.ml index 2686e16a86..99f3fb951c 100644 --- a/src/incremental/compareGlobals.ml +++ b/src/incremental/compareGlobals.ml @@ -133,7 +133,7 @@ let eqF (old: Cil.fundec) (current: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) opti ) in - if sameLocals then + if not sameLocals then (Changed, None, empty_rename_mapping) else match cfgs with From ef2a721fae5c343b53d95ea57717357844abfbfb Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Tue, 7 Mar 2023 13:21:18 +0100 Subject: [PATCH 65/90] cleanup compareCilFiles --- src/incremental/compareCIL.ml | 55 +++++++++++------------------------ 1 file changed, 17 insertions(+), 38 deletions(-) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 23be2d9223..cbe197950d 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -53,43 +53,25 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = global_typ_acc := []; let findChanges map name current_global = try - if not (GobConfig.get_bool "incremental.detect-renames") then - let old_global = GlobalMap.find name map in - let change_status, diff = eq old_global current_global cfgs in - let append_to_changed ~unchangedHeader = - changes.changed <- {current = current_global; old = old_global; unchangedHeader; diff} :: changes.changed - in - match change_status with - | Changed -> - append_to_changed ~unchangedHeader:true - | Unchanged -> changes.unchanged <- {current = current_global; old = old_global} :: changes.unchanged - | ChangedFunHeader f - | ForceReanalyze f -> - changes.exclude_from_rel_destab <- VarinfoSet.add f.svar changes.exclude_from_rel_destab; - append_to_changed ~unchangedHeader:false; + let old_global = GlobalMap.find name map in + let change_status, diff = eq old_global current_global cfgs in + let append_to_changed ~unchangedHeader = + changes.changed <- {current = current_global; old = old_global; unchangedHeader; diff} :: changes.changed + in + match change_status with + | Changed -> + append_to_changed ~unchangedHeader:true + | Unchanged -> changes.unchanged <- {current = current_global; old = old_global} :: changes.unchanged + | ChangedFunHeader f + | ForceReanalyze f -> + changes.exclude_from_rel_destab <- VarinfoSet.add f.svar changes.exclude_from_rel_destab; + append_to_changed ~unchangedHeader:false with Not_found -> changes.added <- current_global::changes.added (* Global could not be found in old map -> added *) in if GobConfig.get_bool "incremental.detect-renames" then ( let renameDetectionResults = detectRenamedFunctions oldMap newMap in - if Messages.tracing then - GlobalColMap.to_seq renameDetectionResults |> - Seq.iter - (fun (gT, (functionGlobal, status)) -> - Messages.trace "compareCIL" "Function status of %s is=" (name_of_global_col gT); - match status with - | Unchanged _ -> Messages.trace "compareCIL" "Same Name\n"; - | Added -> Messages.trace "compareCIL" "Added\n"; - | Removed -> Messages.trace "compareCIL" "Removed\n"; - | Changed _ -> Messages.trace "compareCIL" "Changed\n"; - | UnchangedButRenamed toFrom -> - match toFrom.def with - | Some(Fun f) -> Messages.trace "compareCIL" "Renamed to %s\n" f.svar.vname; - | Some(Var v) -> Messages.trace "compareCIL" "Renamed to %s\n" v.vname; - | _ -> (); - ); - let unchanged, changed, added, removed = GlobalColMap.fold (fun _ (global, status) (u, c, a, r) -> match status with | Unchanged now -> (u @ [{old=global; current=now}], c, a, r) @@ -104,13 +86,10 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = changes.removed <- removed; changes.changed <- changed; changes.unchanged <- unchanged; - ); - - (* 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. *) - GlobalMap.iter (fun name glob_col -> findChanges oldMap name glob_col) newMap; - - if not (GobConfig.get_bool "incremental.detect-renames") then ( + ) else ( + (* 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. *) + GlobalMap.iter (fun name glob_col -> findChanges oldMap name glob_col) newMap; GlobalMap.iter (fun name glob -> if not (GlobalMap.mem name newMap) then changes.removed <- (glob::changes.removed)) oldMap; ); changes From 68ed7e0b07fb933e064640ea877965dc70c08a5c Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Tue, 7 Mar 2023 13:42:04 +0100 Subject: [PATCH 66/90] remove redundant global in output map --- src/incremental/compareCIL.ml | 2 +- src/incremental/detectRenamedFunctions.ml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index cbe197950d..9e3ff5280a 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -72,7 +72,7 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = if GobConfig.get_bool "incremental.detect-renames" then ( let renameDetectionResults = detectRenamedFunctions oldMap newMap in - let unchanged, changed, added, removed = GlobalColMap.fold (fun _ (global, status) (u, c, a, r) -> + let unchanged, changed, added, removed = GlobalColMap.fold (fun global status (u, c, a, r) -> match status with | Unchanged now -> (u @ [{old=global; current=now}], c, a, r) | UnchangedButRenamed now -> (u @ [{old=global; current=now}], c, a, r) diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml index e3ab0328e2..c767b47189 100644 --- a/src/incremental/detectRenamedFunctions.ml +++ b/src/incremental/detectRenamedFunctions.ml @@ -15,7 +15,7 @@ type functionDependencies = string VarinfoMap.t type status = SameName of global_col | Renamed of global_col | Created | Deleted | Modified of global_col * bool type outputFunctionStatus = Unchanged of global_col | UnchangedButRenamed of global_col | Added | Removed | Changed of global_col * bool -type output = global_col * outputFunctionStatus +type output = outputFunctionStatus let pretty (f: status) = match f with @@ -269,7 +269,7 @@ let fillStatusForUnassignedElems oldMap newMap (data: carryType) = let mapAnalysisResultToOutput (oldMap : global_col StringMap.t) (newMap : global_col StringMap.t) (data: carryType) : output GlobalColMap.t = (*Map back to GFun and exposed function status*) - let extractOutput f (s: status) = + let extractOutput _ (s: status) = let outputS = match s with | SameName x -> Unchanged x | Renamed x -> UnchangedButRenamed x @@ -277,7 +277,7 @@ let mapAnalysisResultToOutput (oldMap : global_col StringMap.t) (newMap : global | Deleted -> Removed | Modified (x, unchangedHeader) -> Changed (x, unchangedHeader) in - f, outputS + outputS in (*Merge together old and now functions*) From b959c3c6872798999dd02cbc63448f4de185b230 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Tue, 7 Mar 2023 14:52:50 +0100 Subject: [PATCH 67/90] fix updating of compinfo names and ckeys for comparison without renaming detection --- src/framework/constraints.ml | 2 +- src/incremental/compareAST.ml | 12 ++++------ src/incremental/compareCIL.ml | 27 ++++++++++++++--------- src/incremental/detectRenamedFunctions.ml | 2 +- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index abe4f72804..aeb13d0b5b 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -827,7 +827,7 @@ struct | Some {changes; _} -> changes | None -> empty_change_info () in - List.(Printf.printf "change_info = { unchanged = %d; changed = %d; added = %d; removed = %d }\n" (length c.unchanged) (length c.changed) (length c.added) (length c.removed)); + List.(Printf.printf "change_info = { unchanged = %d; changed = %d (with unchangedHeader = %d); added = %d; removed = %d }\n" (length c.unchanged) (length c.changed) (length (List.filter (fun c -> c.unchangedHeader) c.changed)) (length c.added) (length c.removed)); let changed_funs = List.filter_map (function | {old = {def = Some (Fun f); _}; diff = None; _} -> diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 34368da636..2e836bc257 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -165,15 +165,11 @@ and eq_typ_acc ?(fun_parameter_name_comparison_enabled: bool = true) (a: typ) (b else ( let acc = (a, b) :: acc in let (res, rm) = eq_compinfo compinfo1 compinfo2 acc rename_mapping &&>> forward_list_equal (eq_attribute ~acc) attr1 attr2 in - let updated_rm: rename_mapping = - if res then + let updated_rm = + if res then ( global_typ_acc := (a, b) :: !global_typ_acc; - (* Reset cnames and ckeys to the old value. Only affects anonymous structs/unions where names are not checked for equality. *) - (* TODO - compinfo2.cname <- compinfo1.cname; - compinfo2.ckey <- compinfo1.ckey; - *) - register_rename_on_success rm (Some((compinfo2, compinfo1))) None + register_rename_on_success rm (Some((compinfo2, compinfo1))) None + ) else rm in res, updated_rm ) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 9e3ff5280a..ef12e54b61 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -5,20 +5,25 @@ include CompareAST include CompareCFG open CilMaps -let eq_glob (old: global_col) (current: global_col) (cfgs : (cfg * (cfg * cfg)) option) = match old.def, current.def with - | Some (Var x), Some (Var y) -> unchanged_to_change_status (eq_varinfo x y ~rename_mapping:empty_rename_mapping |> fst), None (* ignore the init_info - a changed init of a global will lead to a different start state *) - | Some (Fun f), Some (Fun g) -> ( +let eq_glob (old: global_col) (current: global_col) (cfgs : (cfg * (cfg * cfg)) option) = + let identical, diff, renamesOnSuccess = match old.def, current.def with + | Some (Var x), Some (Var y) -> + let identical, (_,_,_,renamesOnSuccess) = eq_varinfo x y ~rename_mapping:empty_rename_mapping in + unchanged_to_change_status identical, None, renamesOnSuccess (* ignore the init_info - a changed init of a global will lead to a different start state *) + | Some (Fun f), Some (Fun g) -> ( let identical, diffOpt, funDep, globVarDep, renamesOnSuccess = CompareGlobals.eqF f g cfgs VarinfoMap.empty VarinfoMap.empty in (*Perform renames no matter what.*) - let _ = performRenames renamesOnSuccess in match identical with - | Unchanged when not (VarinfoMap.is_empty funDep && areGlobalVarRenameAssumptionsEmpty globVarDep) -> Changed, diffOpt - | s -> s, diffOpt) - | None, None -> (match old.decls, current.decls with - | Some x, Some y -> unchanged_to_change_status (eq_varinfo x y ~rename_mapping:empty_rename_mapping |> fst), None - | _, _ -> failwith "should never collect any empty entries in GlobalMap") - | _, _ -> Changed, None (* it is considered to be changed (not added or removed) because a global collection only exists in the map - if there is at least one declaration or definition for this global *) + | Unchanged when not (VarinfoMap.is_empty funDep && areGlobalVarRenameAssumptionsEmpty globVarDep) -> Changed, diffOpt, renamesOnSuccess + | s -> s, diffOpt, renamesOnSuccess) + | None, None -> (match old.decls, current.decls with + | Some x, Some y -> + let identical, (_,_,_,renamesOnSuccess) = eq_varinfo x y ~rename_mapping:empty_rename_mapping in + unchanged_to_change_status identical, None, renamesOnSuccess + | _, _ -> failwith "should never collect any empty entries in GlobalMap") + | _, _ -> Changed, None, ([], []) (* it is considered to be changed (not added or removed) because a global collection only exists in the map if there is at least one declaration or definition for this global *) in + performRenames renamesOnSuccess; (* updates enum names and compinfo names and keys that were collected during successful comparisons *) + identical, diff let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = let cfgs = if GobConfig.get_string "incremental.compare" = "cfg" diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml index c767b47189..76279df9d4 100644 --- a/src/incremental/detectRenamedFunctions.ml +++ b/src/incremental/detectRenamedFunctions.ml @@ -42,7 +42,7 @@ let getFunctionAndGVarMap (ast: file) : f StringMap.t * v StringMap.t = let performRenames (renamesOnSuccess: renamesOnSuccess) = begin let (compinfoRenames, enumRenames) = renamesOnSuccess in - List.iter (fun (compinfo2, compinfo1) -> compinfo2.cname <- compinfo1.cname) compinfoRenames; + List.iter (fun (compinfo2, compinfo1) -> compinfo2.cname <- compinfo1.cname; compinfo2.ckey <- compinfo1.ckey) compinfoRenames; List.iter (fun (enum2, enum1) -> enum2.ename <- enum1.ename) enumRenames; end From abdfe66ff3512ac64d73ce32b740fb373eb6de58 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Tue, 7 Mar 2023 15:34:51 +0100 Subject: [PATCH 68/90] remove extra output type and avoid another mapping between types --- src/incremental/compareCIL.ml | 59 +++++++++++------------ src/incremental/detectRenamedFunctions.ml | 34 +------------ 2 files changed, 31 insertions(+), 62 deletions(-) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index ef12e54b61..f8c890396b 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -56,42 +56,41 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = let changes = empty_change_info () in global_typ_acc := []; - let findChanges map name current_global = - try - let old_global = GlobalMap.find name map in - let change_status, diff = eq old_global current_global cfgs in - let append_to_changed ~unchangedHeader = - changes.changed <- {current = current_global; old = old_global; unchangedHeader; diff} :: changes.changed - in - match change_status with - | Changed -> - append_to_changed ~unchangedHeader:true - | Unchanged -> changes.unchanged <- {current = current_global; old = old_global} :: changes.unchanged - | ChangedFunHeader f - | ForceReanalyze f -> - changes.exclude_from_rel_destab <- VarinfoSet.add f.svar changes.exclude_from_rel_destab; - append_to_changed ~unchangedHeader:false - with Not_found -> changes.added <- current_global::changes.added (* Global could not be found in old map -> added *) - in if GobConfig.get_bool "incremental.detect-renames" then ( let renameDetectionResults = detectRenamedFunctions oldMap newMap in - let unchanged, changed, added, removed = GlobalColMap.fold (fun global status (u, c, a, r) -> - match status with - | Unchanged now -> (u @ [{old=global; current=now}], c, a, r) - | UnchangedButRenamed now -> (u @ [{old=global; current=now}], c, a, r) - | Added -> (u, c, a @ [global], r) - | Removed -> (u, c, a, r @ [global]) - | Changed (now,unchangedHeader) -> (u, c @ [{old=global; current=now; unchangedHeader=unchangedHeader; diff=None}], a, r) - ) renameDetectionResults (changes.unchanged, changes.changed, changes.added, changes.removed) - in + let addToChanges firstPass global status = + match status with + | SameName now when firstPass-> changes.unchanged <- {old=global; current=now} :: changes.unchanged + | Renamed now when firstPass -> changes.unchanged <- {old=global; current=now} :: changes.unchanged + | Modified (now, unchangedHeader) when firstPass -> changes.changed <- {old=global; current=now; unchangedHeader=unchangedHeader; diff=None} :: changes.changed + | Created -> changes.added <- global :: changes.added + | Deleted -> changes.removed <- global :: changes.removed + | _ -> () in + + GlobalColMap.iter (addToChanges true) renameDetectionResults.statusForOldElem; + GlobalColMap.iter (addToChanges false) renameDetectionResults.statusForOldElem; - changes.added <- added; - changes.removed <- removed; - changes.changed <- changed; - changes.unchanged <- unchanged; ) else ( + let findChanges map name current_global = + try + let old_global = GlobalMap.find name map in + let change_status, diff = eq old_global current_global cfgs in + let append_to_changed ~unchangedHeader = + changes.changed <- {current = current_global; old = old_global; unchangedHeader; diff} :: changes.changed + in + match change_status with + | Changed -> + append_to_changed ~unchangedHeader:true + | Unchanged -> changes.unchanged <- {current = current_global; old = old_global} :: changes.unchanged + | ChangedFunHeader f + | ForceReanalyze f -> + changes.exclude_from_rel_destab <- VarinfoSet.add f.svar changes.exclude_from_rel_destab; + append_to_changed ~unchangedHeader:false + with Not_found -> changes.added <- current_global::changes.added (* Global could not be found in old map -> added *) + 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. *) GlobalMap.iter (fun name glob_col -> findChanges oldMap name glob_col) newMap; diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml index 76279df9d4..482785791c 100644 --- a/src/incremental/detectRenamedFunctions.ml +++ b/src/incremental/detectRenamedFunctions.ml @@ -10,12 +10,8 @@ type v = varinfo * initinfo * location (*A dependency maps the function it depends on to the name the function has to be changed to*) type functionDependencies = string VarinfoMap.t - (*Renamed: newName * dependencies; Modified=now*unchangedHeader*) type status = SameName of global_col | Renamed of global_col | Created | Deleted | Modified of global_col * bool -type outputFunctionStatus = Unchanged of global_col | UnchangedButRenamed of global_col | Added | Removed | Changed of global_col * bool - -type output = outputFunctionStatus let pretty (f: status) = match f with @@ -267,33 +263,11 @@ let fillStatusForUnassignedElems oldMap newMap (data: carryType) = assignStatusToUnassignedElem data nowF registerStatusForNowF data.statusForNowElem data.reverseMapping Created ) newMap -let mapAnalysisResultToOutput (oldMap : global_col StringMap.t) (newMap : global_col StringMap.t) (data: carryType) : output GlobalColMap.t = - (*Map back to GFun and exposed function status*) - let extractOutput _ (s: status) = - let outputS = match s with - | SameName x -> Unchanged x - | Renamed x -> UnchangedButRenamed x - | Created -> Added - | Deleted -> Removed - | Modified (x, unchangedHeader) -> Changed (x, unchangedHeader) - in - outputS - in - - (*Merge together old and now functions*) - GlobalColMap.merge (fun _ a b -> - if Option.is_some a then a - else if Option.is_some b then b - else None - ) - (GlobalColMap.mapi extractOutput data.statusForOldElem) - (GlobalColMap.mapi extractOutput data.statusForNowElem) - -let detectRenamedFunctions (oldMap : global_col StringMap.t) (newMap : global_col StringMap.t) : output GlobalColMap.t = +let detectRenamedFunctions (oldMap : global_col StringMap.t) (newMap : global_col StringMap.t) : carryType = let initialData: carryType = findSameNameMatchingGVars oldMap newMap emptyCarryType in (*Go through all functions, for all that have not been renamed *) - let finalData = findSameNameMatchingFunctions oldMap newMap initialData (fun oldF nowF change_status functionDependencies global_var_dependencies renamesOnSuccess data -> + findSameNameMatchingFunctions oldMap newMap initialData (fun oldF nowF change_status functionDependencies global_var_dependencies renamesOnSuccess data -> let oldG = GlobalMap.find oldF.svar.vname oldMap in let nowG = GlobalMap.find nowF.svar.vname newMap in @@ -316,7 +290,3 @@ let detectRenamedFunctions (oldMap : global_col StringMap.t) (newMap : global_co (*At this point we already know of the functions that have changed and stayed the same. We now assign the correct status to all the functions that have been mapped. The functions that have not been mapped are added/removed.*) fillStatusForUnassignedElems oldMap newMap - in - - (*Done with the analyis, the following just adjusts the output types.*) - mapAnalysisResultToOutput oldMap newMap finalData From c0aaa8162141fbcb45802468724014547db8126a Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Tue, 7 Mar 2023 16:47:49 +0100 Subject: [PATCH 69/90] cleanup eqF: same handling for cfg and ast comparison --- src/incremental/compareGlobals.ml | 62 +++++++++---------------------- 1 file changed, 17 insertions(+), 45 deletions(-) diff --git a/src/incremental/compareGlobals.ml b/src/incremental/compareGlobals.ml index 99f3fb951c..57883fff7c 100644 --- a/src/incremental/compareGlobals.ml +++ b/src/incremental/compareGlobals.ml @@ -86,64 +86,36 @@ let eqF (old: Cil.fundec) (current: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) opti ForceReanalyze current, None, empty_rename_mapping else - (* 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 unchangedHeader, headerRenameMapping, renamesOnSuccessHeader = match cfgs with - | None -> ( - let headerSizeEqual, headerRenameMapping = rename_mapping_aware_compare old.sformals current.sformals (StringMap.empty) in - let actHeaderRenameMapping = (headerRenameMapping, global_function_rename_mapping, global_var_rename_mapping, ([], [])) in - - let (unchangedHeader, (_, _, _, renamesOnSuccessHeader)) = - eq_varinfo old.svar current.svar ~rename_mapping:actHeaderRenameMapping - &&>> forward_list_equal eq_varinfo old.sformals current.sformals in - unchangedHeader, headerRenameMapping, renamesOnSuccessHeader - ) - | Some _ -> ( - let unchangedHeader, headerRenameMapping = eq_varinfo old.svar current.svar ~rename_mapping:empty_rename_mapping &&>> - forward_list_equal eq_varinfo old.sformals current.sformals in - let (_, _, _, renamesOnSuccessHeader) = headerRenameMapping in - - (unchangedHeader && is_rename_mapping_empty headerRenameMapping), StringMap.empty, renamesOnSuccessHeader - ) - in + let add_locals_to_rename_mapping la lb map = + try + List.fold_left (fun map (a, b) -> StringMap.add a.vname b.vname map) map (List.combine la lb) + with Invalid_argument _ -> map in + + let parameterMapping = add_locals_to_rename_mapping old.sformals current.sformals StringMap.empty in + let renameMapping = (parameterMapping, global_function_rename_mapping, global_var_rename_mapping, ([], [])) in + + (* compare the function header based on the collected rename assumptions for parameters *) + let unchangedHeader, renameMapping = eq_varinfo old.svar current.svar ~rename_mapping:renameMapping + &&>> forward_list_equal eq_varinfo old.sformals current.sformals in if not unchangedHeader then ChangedFunHeader current, None, empty_rename_mapping else - (* Here the local variables are checked to be equal *) - (* sameLocals: when running on cfg, true iff the locals are identical; on ast: if the size of the locals stayed the same*) - let sameLocals, rename_mapping = - match cfgs with - | None -> ( - let sizeEqual, local_rename = rename_mapping_aware_compare old.slocals current.slocals headerRenameMapping in - sizeEqual, (local_rename, global_function_rename_mapping, global_var_rename_mapping, renamesOnSuccessHeader) - ) - | Some _ -> ( - let isEqual, rename_mapping = forward_list_equal eq_varinfo old.slocals current.slocals ~rename_mapping:(StringMap.empty, VarinfoMap.empty, VarinfoMap.empty, renamesOnSuccessHeader) in - isEqual && is_rename_mapping_empty rename_mapping, rename_mapping - ) - in + (* include matching of local variables into rename mapping *) + let renameMapping = match renameMapping with + | (pm, gf, gv, re) -> (add_locals_to_rename_mapping old.slocals current.slocals pm, gf, gv, re) in + let sameLocals, renameMapping = forward_list_equal eq_varinfo old.slocals current.slocals ~rename_mapping:renameMapping in if not sameLocals then (Changed, None, empty_rename_mapping) else match cfgs with | None -> - let (identical, new_rename_mapping) = eq_block (old.sbody, old) (current.sbody, current) ~rename_mapping in + let (identical, new_rename_mapping) = eq_block (old.sbody, old) (current.sbody, current) ~rename_mapping:renameMapping in unchanged_to_change_status identical, None, new_rename_mapping | 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 - let matches, diffNodes1, updated_rename_mapping = compareFun (module CfgOld) (module CfgNew) old current rename_mapping in + let matches, diffNodes1, updated_rename_mapping = compareFun (module CfgOld) (module CfgNew) old current renameMapping in if diffNodes1 = [] then (Unchanged, None, updated_rename_mapping) else (Changed, Some {unchangedNodes = matches; primObsoleteNodes = diffNodes1}, updated_rename_mapping) in From 55da723c58293ab32a347b089e98a1b5cbf0d60c Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Wed, 8 Mar 2023 17:35:07 +0100 Subject: [PATCH 70/90] make detection of renamed globals more concise --- src/incremental/compareAST.ml | 36 +- src/incremental/compareCIL.ml | 21 +- src/incremental/detectRenamedFunctions.ml | 379 ++++++---------------- 3 files changed, 123 insertions(+), 313 deletions(-) diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 2e836bc257..e6ca67f1df 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -3,15 +3,14 @@ open CilMaps module StringMap = Map.Make(String) -type method_rename_assumption = {original_method_name: string; new_method_name: string} -type method_rename_assumptions = method_rename_assumption VarinfoMap.t -type glob_var_rename_assumptions = string VarinfoMap.t +type method_rename_assumptions = varinfo VarinfoMap.t +type glob_var_rename_assumptions = varinfo VarinfoMap.t (*On a successful match, these compinfo and enuminfo names have to be set to the snd element of the tuple. *) type renamesOnSuccess = (compinfo * compinfo) list * (enuminfo * enuminfo) list (*rename_mapping is carried through the stack when comparing the AST. Holds a list of rename assumptions.*) -type rename_mapping = (string StringMap.t) * (method_rename_assumptions) * glob_var_rename_assumptions * renamesOnSuccess +type rename_mapping = (string StringMap.t) * method_rename_assumptions * glob_var_rename_assumptions * renamesOnSuccess (*Compares two names, being aware of the rename_mapping. Returns true iff: 1. there is a rename for name1 -> name2 = rename(name1) @@ -47,13 +46,12 @@ let string_tuple_to_string (tuple: (string * string) list) = "[" ^ (tuple |> let rename_mapping_to_string (rename_mapping: rename_mapping) = let (local, methods, glob_vars, _) = rename_mapping in let local_string = [%show: (string * string) list] (List.of_seq (StringMap.to_seq local)) in - let methods_string: string = List.of_seq (VarinfoMap.to_seq methods |> Seq.map snd) |> - List.map (fun x -> match x with {original_method_name; new_method_name} -> - "(methodName: " ^ original_method_name ^ " -> " ^ new_method_name ^ ")") |> + let methods_string: string = List.of_seq (VarinfoMap.to_seq methods) |> + List.map (fun (oldf, newf) -> "(methodName: " ^ oldf.vname ^ " -> " ^ newf.vname ^ ")") |> String.concat ", " in let global_var_string: string = string_tuple_to_string (List.of_seq (VarinfoMap.to_seq glob_vars) |> - List.map (fun (v, nowName) -> v.vname, nowName)) in + List.map (fun (vold, vnew) -> vold.vname, vnew.vname)) in "(local=" ^ local_string ^ "; methods=[" ^ methods_string ^ "]; glob_vars=" ^ global_var_string ^ ")" @@ -233,10 +231,10 @@ and eq_varinfo (a: varinfo) (b: varinfo) ~(acc: (typ * typ) list) ~(rename_mappi let present_mapping = VarinfoMap.find_opt a glob_vars in match present_mapping with - | Some (knownNowName) -> - b.vname = knownNowName, method_rename_mappings, glob_vars + | Some (knownNowVarinfo) -> + b.vname = knownNowVarinfo.vname, method_rename_mappings, glob_vars | None -> ( - let update_glob_vars = VarinfoMap.add a b.vname glob_vars in + let update_glob_vars = VarinfoMap.add a b glob_vars in true, method_rename_mappings, update_glob_vars ) else rename_mapping_aware_name_comparison a.vname b.vname rename_mapping, method_rename_mappings, glob_vars @@ -247,15 +245,12 @@ and eq_varinfo (a: varinfo) (b: varinfo) ~(acc: (typ * typ) list) ~(rename_mappi | TFun(_, aParamSpec, _, _), TFun(_, bParamSpec, _, _) -> ( let specific_method_rename_mapping = VarinfoMap.find_opt a method_rename_mappings in match specific_method_rename_mapping with - | Some method_rename_mapping -> - let is_naming_ok = method_rename_mapping.original_method_name = a.vname && method_rename_mapping.new_method_name = b.vname in + | Some new_varinfo -> + let is_naming_ok = new_varinfo.vname = b.vname in is_naming_ok, method_rename_mappings, glob_vars | None -> if a.vname <> b.vname then - let assumption = - {original_method_name = a.vname; new_method_name = b.vname} in - - true, VarinfoMap.add a assumption method_rename_mappings, glob_vars + true, VarinfoMap.add a b method_rename_mappings, glob_vars else true, method_rename_mappings, glob_vars ) | TInt (_, _), TInt (_, _) -> compare_local_and_global_var @@ -267,13 +262,6 @@ and eq_varinfo (a: varinfo) (b: varinfo) ~(acc: (typ * typ) list) ~(rename_mappi (*If the following is a method call, we need to check if we have a mapping for that method call. *) let fun_parameter_name_comparison_enabled = match b.vtype with | TFun(_, _, _, _) -> false - (*| GVar (_, _, _) -> ( - let new_local = VarinfoMap.find_opt a glob_vars in - - match new_local with - | Some now_name -> (StringMap.add a.vname now_name StringMap.empty, updated_method_rename_mappings, updatedGlobVarMapping) - | None -> (StringMap.empty, updated_method_rename_mappings, updatedGlobVarMapping) - )*) | _ -> true in diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index f8c890396b..1bda93b6bc 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -14,7 +14,7 @@ let eq_glob (old: global_col) (current: global_col) (cfgs : (cfg * (cfg * cfg)) let identical, diffOpt, funDep, globVarDep, renamesOnSuccess = CompareGlobals.eqF f g cfgs VarinfoMap.empty VarinfoMap.empty in (*Perform renames no matter what.*) match identical with - | Unchanged when not (VarinfoMap.is_empty funDep && areGlobalVarRenameAssumptionsEmpty globVarDep) -> Changed, diffOpt, renamesOnSuccess + | Unchanged when not (VarinfoMap.is_empty funDep && VarinfoMap.for_all (fun ov nv -> ov.vname = nv.vname) globVarDep) -> Changed, diffOpt, renamesOnSuccess | s -> s, diffOpt, renamesOnSuccess) | None, None -> (match old.decls, current.decls with | Some x, Some y -> @@ -58,19 +58,12 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = global_typ_acc := []; if GobConfig.get_bool "incremental.detect-renames" then ( - let renameDetectionResults = detectRenamedFunctions oldMap newMap in - - let addToChanges firstPass global status = - match status with - | SameName now when firstPass-> changes.unchanged <- {old=global; current=now} :: changes.unchanged - | Renamed now when firstPass -> changes.unchanged <- {old=global; current=now} :: changes.unchanged - | Modified (now, unchangedHeader) when firstPass -> changes.changed <- {old=global; current=now; unchangedHeader=unchangedHeader; diff=None} :: changes.changed - | Created -> changes.added <- global :: changes.added - | Deleted -> changes.removed <- global :: changes.removed - | _ -> () in - - GlobalColMap.iter (addToChanges true) renameDetectionResults.statusForOldElem; - GlobalColMap.iter (addToChanges false) renameDetectionResults.statusForOldElem; + let (change_info, final_mapping) = detectRenamedFunctions oldMap newMap in + changes.added <- change_info.added; + changes.removed <- change_info.removed; + changes.changed <- change_info.changed; + changes.unchanged <- change_info.unchanged; + changes.exclude_from_rel_destab <- change_info.exclude_from_rel_destab ) else ( let findChanges map name current_global = diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml index 482785791c..c47f59fc16 100644 --- a/src/incremental/detectRenamedFunctions.ml +++ b/src/incremental/detectRenamedFunctions.ml @@ -2,39 +2,6 @@ open GoblintCil include CompareGlobals open CilMaps -module StringSet = Set.Make(String) - -type f = fundec * location -type v = varinfo * initinfo * location - -(*A dependency maps the function it depends on to the name the function has to be changed to*) -type functionDependencies = string VarinfoMap.t - -(*Renamed: newName * dependencies; Modified=now*unchangedHeader*) -type status = SameName of global_col | Renamed of global_col | Created | Deleted | Modified of global_col * bool - -let pretty (f: status) = - match f with - | SameName _ -> "SameName" - | Renamed x -> ("Renamed to " ^ CompareGlobals.name_of_global_col x) - | Created -> "Added" - | Deleted -> "Removed" - | Modified _ -> "Changed" - -let printFundecMap elemToString map = begin - Seq.iter (fun (f, e) -> - ignore@@Pretty.printf "%s->%s;" f.svar.vname (elemToString e); - ) (FundecMap.to_seq map) -end - -let getFunctionAndGVarMap (ast: file) : f StringMap.t * v StringMap.t = - Cil.foldGlobals ast (fun (functionMap, gvarMap) global -> - match global with - | GFun (fundec, location) -> (StringMap.add fundec.svar.vname (fundec, location) functionMap, gvarMap) - | GVar (varinfo, initinfo, location) -> (functionMap, StringMap.add varinfo.vname (varinfo, initinfo, location) gvarMap) - | _ -> functionMap, gvarMap - ) (StringMap.empty, StringMap.empty) - let performRenames (renamesOnSuccess: renamesOnSuccess) = begin let (compinfoRenames, enumRenames) = renamesOnSuccess in @@ -42,251 +9,113 @@ let performRenames (renamesOnSuccess: renamesOnSuccess) = List.iter (fun (enum2, enum1) -> enum2.ename <- enum1.ename) enumRenames; end -let getDependencies fromEq = VarinfoMap.map (fun assumption -> assumption.new_method_name) fromEq - -(*Data type that holds the important data while checking for renames. - statusForOldElem: Status we have already figured out for a fundec from oldAST; - statusForNowElem: see statusForOldElem; - mapping: Mappings from (fundec of old AST) -> (fundec of now AST) we have already figured out to hold. - reversemapping: see method mapping, but from now -> old -*) -type carryType = { - statusForOldElem : status GlobalColMap.t; - statusForNowElem : status GlobalColMap.t; - mapping: global_col GlobalColMap.t; - reverseMapping: global_col GlobalColMap.t; -} - -let emptyCarryType = { - statusForOldElem = GlobalColMap.empty; - statusForNowElem = GlobalColMap.empty; - mapping = GlobalColMap.empty; - reverseMapping = GlobalColMap.empty; -} - -(*Carry type manipulation functions.*) - -let registerStatusForOldF f status data = - {statusForOldElem = GlobalColMap.add f status data.statusForOldElem; - statusForNowElem=data.statusForNowElem; - mapping=data.mapping; - reverseMapping=data.reverseMapping; - } - -let registerStatusForNowF f status data = - {statusForOldElem = data.statusForOldElem; - statusForNowElem=GlobalColMap.add f status data.statusForNowElem; - mapping=data.mapping; - reverseMapping=data.reverseMapping; - } - -let registerBiStatus (oldF: global_col) (nowF: global_col) (status: status) data = - {statusForOldElem=GlobalColMap.add oldF status data.statusForOldElem; - statusForNowElem=GlobalColMap.add nowF status data.statusForNowElem; - mapping=data.mapping; - reverseMapping=data.reverseMapping; - } - -let registerMapping oldF nowF data = - {statusForOldElem=data.statusForOldElem; - statusForNowElem=data.statusForNowElem; - mapping=GlobalColMap.add oldF nowF data.mapping; - reverseMapping=GlobalColMap.add nowF oldF data.reverseMapping; - } - -let registerGVarMapping oldV nowV data = { - statusForOldElem=data.statusForOldElem; - statusForNowElem=data.statusForNowElem; - mapping=data.mapping; - reverseMapping=data.reverseMapping; -} - -(*True iff the global var rename assumptions contains only entries that are identity mappings*) -let areGlobalVarRenameAssumptionsEmpty (mapping: glob_var_rename_assumptions) : bool = - VarinfoMap.for_all (fun varinfo newName -> varinfo.vname = newName) mapping - -(*returns true iff for all dependencies it is true, that the dependency has a corresponding function with the new name and matches the without having dependencies itself and the new name is not already present on the old AST. *) -let doAllDependenciesMatch (dependencies: functionDependencies) - (global_var_dependencies: glob_var_rename_assumptions) - (oldMap: global_col StringMap.t) - (newMap: global_col StringMap.t) (data: carryType) : bool * carryType = - - let isConsistent = fun old nowName allEqual getName oldMap nowMap getNowOption data -> - (*Early cutoff if a previous dependency returned false. - We never create a mapping between globs where the now name was already part of the old set or the old name is part of the now set. - But only if now and old differ. - *) - if allEqual && (getName old = nowName || (not (StringMap.mem nowName oldMap) && not (StringMap.mem (getName old) nowMap))) then - let knownMapping = GlobalColMap.find_opt old data.mapping in - - (*let _ = Printf.printf "Dep: %s -> %s\n" (globalElemName2 globalElem) nowName in*) - - (*To avoid inconsitencies, if a function has already been mapped to a function, that mapping is reused again.*) - match knownMapping with - | Some(knownElem) -> - (*This function has already been mapped*) - (*let _ = Printf.printf "Already mapped. %s = %s\n" (globalElemName2 knownElem) nowName in*) - name_of_global_col knownElem = nowName, data - | None -> - let nowElemOption = getNowOption nowName in - - match nowElemOption with - | Some(nowElem) -> ( - let compare = fun old now -> - let compareVar oV nV = let (equal, (_, function_dependencies, global_var_dependencies, renamesOnSuccess)) = eq_varinfo oV nV ~rename_mapping:empty_rename_mapping in - (*eq_varinfo always comes back with a self dependency. We need to filter that out.*) - unchanged_to_change_status equal, function_dependencies, (VarinfoMap.filter (fun vi name -> not (vi.vname = oV.vname && name = nowName)) global_var_dependencies), renamesOnSuccess - in - match (old.def, now.def) with - | Some (Fun oF), Some (Fun nF) -> - let doMatch, _, function_dependencies, global_var_dependencies, renamesOnSuccess = CompareGlobals.eqF oF nF None VarinfoMap.empty VarinfoMap.empty in - doMatch, function_dependencies, global_var_dependencies, renamesOnSuccess - | Some (Var oV), Some (Var nV) -> compareVar oV nV - | None, None -> (match old.decls, now.decls with - | Some oV, Some nV -> compareVar oV nV - | _ -> failwith "Unknown or incompatible global types") - | _, _ -> failwith "Unknown or incompatible global types" - in - - let doMatch, function_dependencies, global_var_dependencies, renamesOnSuccess = compare old nowElem in - - (*Having a dependency on yourself is ok.*) - let hasNoExternalDependency = VarinfoMap.is_empty function_dependencies || ( - VarinfoMap.cardinal function_dependencies = 1 && ( - VarinfoMap.fold (fun varinfo dependency _ -> varinfo.vname = name_of_global_col old && dependency.new_method_name = name_of_global_col nowElem) function_dependencies true - ) - ) in - - (*let _ = Printf.printf "%s <-> %s: %b %b %b\n" (globalElemName2 globalElem) (globalElemName2 nowElem) doMatch hasNoExternalDependency (VarinfoMap.is_empty global_var_dependencies) in - - let _ = Printf.printf "%s\n" (rename_mapping_to_string (StringMap.empty, function_dependencies, global_var_dependencies, ([], []))) in*) - - match doMatch with - | Unchanged when hasNoExternalDependency && areGlobalVarRenameAssumptionsEmpty global_var_dependencies -> - let _ = performRenames renamesOnSuccess in - true, registerMapping old nowElem data - | _ -> false, data - ) - | None -> - (*Printf.printf "No elem with name %s found \n" nowName;*) - (*Return true assumes external globs never change. Which is ok for now*) - true, data - else false, data +let detectRenamedFunctions (oldMap : global_col StringMap.t) (newMap : global_col StringMap.t) = + let get_varinfo gc = match gc.decls, gc.def with + | _, Some (Var v) -> v + | _, Some (Fun f) -> f.svar + | Some v, _ -> v + | _ -> failwith "A global should have at least a declaration or a definition" in + let extract_fundecs _ gc map = match gc.def with + | Some (Fun f) -> VarinfoMap.add f.svar f map + | _ -> map in + let var_fun_old = GlobalMap.fold extract_fundecs oldMap VarinfoMap.empty in + let var_fun_new = GlobalMap.fold extract_fundecs newMap VarinfoMap.empty in + let empty_rename_assumptions m = VarinfoMap.for_all (fun vo vn -> vo.vname = vn.vname) m in (* TODO or in final_matches? *) + + let compare_fundec_exact_match f1 f2 change_info final_matches = + let doMatch, diff, function_dependencies, global_var_dependencies, renamesOnSuccess = CompareGlobals.eqF f1 f2 None VarinfoMap.empty VarinfoMap.empty in + match doMatch with + | Unchanged when empty_rename_assumptions function_dependencies && empty_rename_assumptions global_var_dependencies -> + performRenames renamesOnSuccess; + let change_info = {change_info with unchanged = change_info.unchanged} in + let final_matches = VarinfoMap.add f1.svar f2.svar final_matches in + true, change_info, final_matches + | _ -> false, change_info, final_matches in - VarinfoMap.fold (fun old nowName (allEqual, data) -> - let old = StringMap.find old.vname oldMap in - isConsistent - old - nowName - allEqual - (fun x -> name_of_global_col x) - oldMap - newMap - (fun x -> StringMap.find_opt x newMap) - data - ) dependencies (true, data) |> - VarinfoMap.fold (fun oldVarinfo nowName (allEqual, data) -> - isConsistent - (GlobalMap.find oldVarinfo.vname oldMap) - nowName - allEqual - (fun x -> name_of_global_col x) - oldMap - newMap - (fun x -> StringMap.find_opt x newMap) - data + let compare_global_var_exact_match oV nV change_info final_matches = + let (equal, (_, function_dependencies, global_var_dependencies, renamesOnSuccess)) = eq_varinfo oV nV ~rename_mapping:(StringMap.empty, VarinfoMap.empty, VarinfoMap.empty, ([],[])) in + (*eq_varinfo always comes back with a self dependency. We need to filter that out.*) + if equal && empty_rename_assumptions function_dependencies && empty_rename_assumptions global_var_dependencies then ( + performRenames renamesOnSuccess; + true, change_info, VarinfoMap.add oV nV final_matches + ) else ( + false, change_info, final_matches ) - global_var_dependencies - -(*Check if f has already been assigned a status. If yes do nothing. - If not, check if the function took part in the mapping, then register it to have been renamed. Otherwise register it as the supplied status.*) -let assignStatusToUnassignedElem data f registerStatus statusMap mapping status = - if not (GlobalColMap.mem f statusMap) then - if (GlobalColMap.mem f mapping) then - registerStatus f (Renamed (GlobalColMap.find f mapping)) data - else - (*this function has been added/removed*) - registerStatus f status data - else - data - -let findSameNameMatchingGVars (oldMap : global_col StringMap.t) (newMap : global_col StringMap.t) data = - let compare_varinfo v1 v2 data = - let identical, _ = eq_varinfo v1 v2 ~rename_mapping:empty_rename_mapping in - let oldG, nowG = GlobalMap.find v1.vname oldMap, GlobalMap.find v2.vname newMap in - if identical then - registerBiStatus oldG nowG (SameName nowG) data - else - registerStatusForOldF oldG (Modified(nowG, false)) data |> - registerStatusForNowF nowG (Modified(oldG, false)) in - StringMap.fold (fun name gc_old (data: carryType) -> - try - let gc_new = StringMap.find name newMap in - match gc_old.def, gc_new.def with - | Some (Var v1), Some (Var v2) -> compare_varinfo v1 v2 data - | None, None -> (match gc_old.decls, gc_new.decls with - | Some v1, Some v2 -> compare_varinfo v1 v2 data - | _ -> data) - | _ -> data - with Not_found -> data - ) oldMap data - -(*Goes through all old functions and looks for now-functions with the same name. If a pair has been found, onMatch is called with the comparison result. - On match then modifies the carryType. Returns (list of the functions that have the same name and match, the updated carry type)*) -let findSameNameMatchingFunctions - oldFunctionMap - nowFunctionMap - (initialData: 'a) - (onMatch: fundec -> fundec -> change_status -> string VarinfoMap.t -> CompareGlobals.glob_var_rename_assumptions -> CompareGlobals.renamesOnSuccess -> 'a -> 'a) : 'a = - StringMap.fold (fun name oldFun data -> - try - let newFun = StringMap.find name nowFunctionMap in - match oldFun.def, newFun.def with - | Some (Fun f1), Some (Fun f2) -> - let doMatch, _, function_dependencies, global_var_dependencies, renamesOnSuccess = CompareGlobals.eqF f1 f2 None VarinfoMap.empty VarinfoMap.empty in - let actDependencies = getDependencies function_dependencies in - onMatch f1 f2 doMatch actDependencies global_var_dependencies renamesOnSuccess data - | _ -> data - with Not_found -> data - ) oldFunctionMap initialData -let fillStatusForUnassignedElems oldMap newMap (data: carryType) = - data |> - (*Now go through all old functions again. Those who have not been assigned a status are removed*) - StringMap.fold (fun name f (data: carryType) -> - assignStatusToUnassignedElem data f registerStatusForOldF data.statusForOldElem data.mapping Deleted - ) oldMap |> - (*now go through all new functions. Those have have not been assigned a mapping are added.*) - StringMap.fold (fun name nowF (data: carryType) -> - assignStatusToUnassignedElem data nowF registerStatusForNowF data.statusForNowElem data.reverseMapping Created - ) newMap - -let detectRenamedFunctions (oldMap : global_col StringMap.t) (newMap : global_col StringMap.t) : carryType = - let initialData: carryType = findSameNameMatchingGVars oldMap newMap emptyCarryType in - - (*Go through all functions, for all that have not been renamed *) - findSameNameMatchingFunctions oldMap newMap initialData (fun oldF nowF change_status functionDependencies global_var_dependencies renamesOnSuccess data -> - let oldG = GlobalMap.find oldF.svar.vname oldMap in - let nowG = GlobalMap.find nowF.svar.vname newMap in - - match change_status with - | Unchanged -> - let doDependenciesMatch, updatedData = doAllDependenciesMatch functionDependencies global_var_dependencies oldMap newMap data in + let matchGlobal ~matchVars ~matchFuns name gc_old (change_info, final_matches) = + try + let gc_new = StringMap.find name newMap in + let preservesSameNameMatches n_old n_new = n_old = n_new || (not (GlobalMap.mem n_old newMap) && not (GlobalMap.mem n_new oldMap)) in + + let compare_varinfo v1 v2 = + let identical, (_, _, _, renamesOnSuccess) = eq_varinfo v1 v2 ~rename_mapping:empty_rename_mapping in + performRenames renamesOnSuccess; (* updates enum names and compinfo names and keys that were collected during comparison of this matched function *) + + if identical then ( + let extendedUnchanged = {old = gc_old; current = gc_new} :: change_info.unchanged in + {change_info with unchanged = extendedUnchanged}, VarinfoMap.add v1 v2 final_matches + ) else + let extendedChanged = {old = gc_old; current = gc_new; unchangedHeader = true; diff = None} :: change_info.changed in + {change_info with changed = extendedChanged}, VarinfoMap.add v1 v2 final_matches + in + + let compare_same_name_fundec_check_contained_renames f1 f2 : varinfo VarinfoMap.t = + let doMatch, diff, function_dependencies, global_var_dependencies, renamesOnSuccess = CompareGlobals.eqF f1 f2 None VarinfoMap.empty VarinfoMap.empty in + performRenames renamesOnSuccess; (* updates enum names and compinfo names and keys that were collected during comparison of this matched function *) + (* TODO recursively check dependencies, check in rename mapping for globals that were already compared *) + let funDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun f_old_var f_new_var (acc, ci, fm) -> (* TODO add global assumptions check *) + match VarinfoMap.find_opt f_old_var final_matches with + | None -> + let f_old = VarinfoMap.find f_old_var var_fun_old in + let f_new = VarinfoMap.find f_new_var var_fun_new in (* TODO: what happens if there exists no fundec for this varinfo? *) + (* check that names of match are each only contained in new or old file *) + if acc && preservesSameNameMatches f_old_var.vname f_new_var.vname then + compare_fundec_exact_match f_old f_new ci fm + else false, ci, fm + | Some v -> v = f_new_var, ci, fm) function_dependencies (true, change_info, final_matches) in + let globalDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun old_var new_var (acc, ci, fm) -> + match VarinfoMap.find_opt old_var final_matches with + | None -> + if acc && preservesSameNameMatches old_var.vname new_var.vname then + compare_global_var_exact_match old_var new_var ci fm + else false, ci, fm + | Some v -> v = new_var, ci, fm + ) global_var_dependencies (true, change_info, final_matches) in + let dependenciesMatch = funDependenciesMatch && globalDependenciesMatch in + let append_to_changed ~unchangedHeader ~diff = + change_info.changed <- {current = gc_new; old = gc_old; unchangedHeader; diff} :: change_info.changed + in + (* TODO: merge with no-rename-detection case in compareCIL.compareCilFiles *) + (match doMatch with + | Unchanged when dependenciesMatch -> + change_info.unchanged <- {old = gc_old; current = gc_new} :: change_info.unchanged + | Unchanged -> + (* no diff is stored, also when comparing functions based on CFG because currently there is no mechanism to detect which part was affected by the *) + append_to_changed ~unchangedHeader:true ~diff:None + | _ -> (* this can only be ForceReanalyze or ChangedFunHeader *) + change_info.exclude_from_rel_destab <- VarinfoSet.add f1.svar change_info.exclude_from_rel_destab; + append_to_changed ~unchangedHeader:false ~diff:None); + VarinfoMap.add f1.svar f2.svar final_matches in + + match gc_old.def, gc_new.def with + | Some (Var v1), Some (Var v2) when matchVars -> compare_varinfo v1 v2 + | Some (Fun f1), Some (Fun f2) when matchFuns -> change_info, compare_same_name_fundec_check_contained_renames f1 f2 + | None, None -> (match gc_old.decls, gc_new.decls with + | Some v1, Some v2 when matchVars-> compare_varinfo v1 v2 + | _ -> change_info, final_matches) + | _ -> change_info, final_matches + with Not_found -> let extendedRemoved = gc_old :: change_info.removed in {change_info with removed = extendedRemoved}, final_matches in + + let addNewGlobals name gc_new (change_info, final_matches) = + if not (VarinfoMap.mem (get_varinfo gc_new) final_matches) then + let ext_added = gc_new :: change_info.added in + ({change_info with added = ext_added}, final_matches) + else (change_info, final_matches) + in - if doDependenciesMatch then - registerBiStatus oldG nowG (SameName(oldG)) updatedData - else - registerStatusForOldF oldG (Modified (nowG, true)) data |> - registerStatusForNowF nowG (Modified (oldG, true)) - | Changed -> - registerStatusForOldF oldG (Modified (nowG, true)) data |> - registerStatusForNowF nowG (Modified (oldG, true)) - | _ -> - registerStatusForOldF oldG (Modified (nowG, false)) data |> - registerStatusForNowF nowG (Modified (oldG, false)) - ) |> - (*At this point we already know of the functions that have changed and stayed the same. We now assign the correct status to all the functions that - have been mapped. The functions that have not been mapped are added/removed.*) - fillStatusForUnassignedElems oldMap newMap + (empty_change_info (), VarinfoMap.empty) (* change_info and final_matches is propagated *) + |> GlobalMap.fold (matchGlobal ~matchVars:true ~matchFuns:false) oldMap + |> GlobalMap.fold (matchGlobal ~matchVars:false ~matchFuns:true) oldMap + |> GlobalMap.fold addNewGlobals newMap From 009f457baf4dd49961fbeba6b00cdeeffd6f3edd Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Wed, 8 Mar 2023 18:29:43 +0100 Subject: [PATCH 71/90] refactor --- src/incremental/detectRenamedFunctions.ml | 78 +++++++++++------------ 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml index c47f59fc16..8c648355b4 100644 --- a/src/incremental/detectRenamedFunctions.ml +++ b/src/incremental/detectRenamedFunctions.ml @@ -9,12 +9,43 @@ let performRenames (renamesOnSuccess: renamesOnSuccess) = List.iter (fun (enum2, enum1) -> enum2.ename <- enum1.ename) enumRenames; end -let detectRenamedFunctions (oldMap : global_col StringMap.t) (newMap : global_col StringMap.t) = +let preservesSameNameMatches n_old oldMap n_new newMap = n_old = n_new || (not (GlobalMap.mem n_old newMap) && not (GlobalMap.mem n_new oldMap)) + +(* TODO: possibly merge with eq_varinfo, provide only varinfo and mapping from varinfo to global_col *) +(* Compares two varinfos. finalizeOnlyExactMatch=true allows to check a rename assumption and discard the comparison result in case they do not match *) +let compare_varinfo ?(finalizeOnlyExactMatch=false) oV gc_old oldMap nV gc_new newMap change_info final_matches = + if preservesSameNameMatches oV.vname oldMap nV.vname newMap then + (* do not allow for matches between differently named variables if one of the variables names exists in both, the new and old file *) + false, change_info, final_matches + else ( + (* TODO does the emptyness of the dependencies need to be checked? *) + let identical, (_, function_dependencies, global_var_dependencies, renamesOnSuccess) = eq_varinfo oV nV ~rename_mapping:empty_rename_mapping in + + if not finalizeOnlyExactMatch || identical then + performRenames renamesOnSuccess; (* updates enum names and compinfo names and keys that were collected during comparison of this matched function *) + if identical then ( + change_info.unchanged <- {old = gc_old; current = gc_new} :: change_info.unchanged; + true, change_info, VarinfoMap.add oV nV final_matches + ) else if not finalizeOnlyExactMatch then ( + change_info.changed <- {old = gc_old; current = gc_new; unchangedHeader = true; diff = None} :: change_info.changed; + false, change_info, VarinfoMap.add oV nV final_matches + ) else + false, change_info, final_matches + ) +let compare_varinfo_exact = compare_varinfo ~finalizeOnlyExactMatch:true + +let addNewGlobals name gc_new (change_info, final_matches) = let get_varinfo gc = match gc.decls, gc.def with | _, Some (Var v) -> v | _, Some (Fun f) -> f.svar | Some v, _ -> v | _ -> failwith "A global should have at least a declaration or a definition" in + if not (VarinfoMap.mem (get_varinfo gc_new) final_matches) then + let ext_added = gc_new :: change_info.added in + ({change_info with added = ext_added}, final_matches) + else (change_info, final_matches) + +let detectRenamedFunctions (oldMap : global_col StringMap.t) (newMap : global_col StringMap.t) = let extract_fundecs _ gc map = match gc.def with | Some (Fun f) -> VarinfoMap.add f.svar f map | _ -> map in @@ -33,53 +64,29 @@ let detectRenamedFunctions (oldMap : global_col StringMap.t) (newMap : global_co | _ -> false, change_info, final_matches in - let compare_global_var_exact_match oV nV change_info final_matches = - let (equal, (_, function_dependencies, global_var_dependencies, renamesOnSuccess)) = eq_varinfo oV nV ~rename_mapping:(StringMap.empty, VarinfoMap.empty, VarinfoMap.empty, ([],[])) in - (*eq_varinfo always comes back with a self dependency. We need to filter that out.*) - if equal && empty_rename_assumptions function_dependencies && empty_rename_assumptions global_var_dependencies then ( - performRenames renamesOnSuccess; - true, change_info, VarinfoMap.add oV nV final_matches - ) else ( - false, change_info, final_matches - ) - in - let matchGlobal ~matchVars ~matchFuns name gc_old (change_info, final_matches) = try let gc_new = StringMap.find name newMap in - let preservesSameNameMatches n_old n_new = n_old = n_new || (not (GlobalMap.mem n_old newMap) && not (GlobalMap.mem n_new oldMap)) in - let compare_varinfo v1 v2 = - let identical, (_, _, _, renamesOnSuccess) = eq_varinfo v1 v2 ~rename_mapping:empty_rename_mapping in - performRenames renamesOnSuccess; (* updates enum names and compinfo names and keys that were collected during comparison of this matched function *) - - if identical then ( - let extendedUnchanged = {old = gc_old; current = gc_new} :: change_info.unchanged in - {change_info with unchanged = extendedUnchanged}, VarinfoMap.add v1 v2 final_matches - ) else - let extendedChanged = {old = gc_old; current = gc_new; unchangedHeader = true; diff = None} :: change_info.changed in - {change_info with changed = extendedChanged}, VarinfoMap.add v1 v2 final_matches - in - - let compare_same_name_fundec_check_contained_renames f1 f2 : varinfo VarinfoMap.t = + let compare_same_name_fundec_check_contained_renames f1 f2 = let doMatch, diff, function_dependencies, global_var_dependencies, renamesOnSuccess = CompareGlobals.eqF f1 f2 None VarinfoMap.empty VarinfoMap.empty in performRenames renamesOnSuccess; (* updates enum names and compinfo names and keys that were collected during comparison of this matched function *) (* TODO recursively check dependencies, check in rename mapping for globals that were already compared *) - let funDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun f_old_var f_new_var (acc, ci, fm) -> (* TODO add global assumptions check *) + let funDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun f_old_var f_new_var (acc, ci, fm) -> match VarinfoMap.find_opt f_old_var final_matches with | None -> let f_old = VarinfoMap.find f_old_var var_fun_old in let f_new = VarinfoMap.find f_new_var var_fun_new in (* TODO: what happens if there exists no fundec for this varinfo? *) (* check that names of match are each only contained in new or old file *) - if acc && preservesSameNameMatches f_old_var.vname f_new_var.vname then + if acc then compare_fundec_exact_match f_old f_new ci fm else false, ci, fm | Some v -> v = f_new_var, ci, fm) function_dependencies (true, change_info, final_matches) in let globalDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun old_var new_var (acc, ci, fm) -> match VarinfoMap.find_opt old_var final_matches with | None -> - if acc && preservesSameNameMatches old_var.vname new_var.vname then - compare_global_var_exact_match old_var new_var ci fm + if acc && preservesSameNameMatches old_var.vname oldMap new_var.vname newMap then + compare_varinfo_exact old_var gc_old oldMap new_var gc_new newMap ci fm else false, ci, fm | Some v -> v = new_var, ci, fm ) global_var_dependencies (true, change_info, final_matches) in @@ -100,21 +107,14 @@ let detectRenamedFunctions (oldMap : global_col StringMap.t) (newMap : global_co VarinfoMap.add f1.svar f2.svar final_matches in match gc_old.def, gc_new.def with - | Some (Var v1), Some (Var v2) when matchVars -> compare_varinfo v1 v2 + | Some (Var v1), Some (Var v2) when matchVars -> let _, ci, fm = compare_varinfo v1 gc_old oldMap v2 gc_new newMap change_info final_matches in ci, fm | Some (Fun f1), Some (Fun f2) when matchFuns -> change_info, compare_same_name_fundec_check_contained_renames f1 f2 | None, None -> (match gc_old.decls, gc_new.decls with - | Some v1, Some v2 when matchVars-> compare_varinfo v1 v2 + | Some v1, Some v2 when matchVars -> let _, ci, fm = compare_varinfo v1 gc_old oldMap v2 gc_new newMap change_info final_matches in ci, fm | _ -> change_info, final_matches) | _ -> change_info, final_matches with Not_found -> let extendedRemoved = gc_old :: change_info.removed in {change_info with removed = extendedRemoved}, final_matches in - let addNewGlobals name gc_new (change_info, final_matches) = - if not (VarinfoMap.mem (get_varinfo gc_new) final_matches) then - let ext_added = gc_new :: change_info.added in - ({change_info with added = ext_added}, final_matches) - else (change_info, final_matches) - in - (empty_change_info (), VarinfoMap.empty) (* change_info and final_matches is propagated *) |> GlobalMap.fold (matchGlobal ~matchVars:true ~matchFuns:false) oldMap |> GlobalMap.fold (matchGlobal ~matchVars:false ~matchFuns:true) oldMap From 5d90ad02541380fac7251ff00b3a1f5f7f68731f Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Mon, 13 Mar 2023 09:57:56 +0100 Subject: [PATCH 72/90] fixes in rename detection --- src/incremental/detectRenamedFunctions.ml | 77 +++++++++++++++-------- 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml index 8c648355b4..39e0c13850 100644 --- a/src/incremental/detectRenamedFunctions.ml +++ b/src/incremental/detectRenamedFunctions.ml @@ -11,10 +11,13 @@ let performRenames (renamesOnSuccess: renamesOnSuccess) = let preservesSameNameMatches n_old oldMap n_new newMap = n_old = n_new || (not (GlobalMap.mem n_old newMap) && not (GlobalMap.mem n_new oldMap)) +let addToFinalMatchesMapping oV nV final_matches = + VarinfoMap.add oV nV (fst final_matches), VarinfoMap.add nV oV (snd final_matches) + (* TODO: possibly merge with eq_varinfo, provide only varinfo and mapping from varinfo to global_col *) (* Compares two varinfos. finalizeOnlyExactMatch=true allows to check a rename assumption and discard the comparison result in case they do not match *) let compare_varinfo ?(finalizeOnlyExactMatch=false) oV gc_old oldMap nV gc_new newMap change_info final_matches = - if preservesSameNameMatches oV.vname oldMap nV.vname newMap then + if not (preservesSameNameMatches oV.vname oldMap nV.vname newMap) then (* do not allow for matches between differently named variables if one of the variables names exists in both, the new and old file *) false, change_info, final_matches else ( @@ -25,25 +28,32 @@ let compare_varinfo ?(finalizeOnlyExactMatch=false) oV gc_old oldMap nV gc_new n performRenames renamesOnSuccess; (* updates enum names and compinfo names and keys that were collected during comparison of this matched function *) if identical then ( change_info.unchanged <- {old = gc_old; current = gc_new} :: change_info.unchanged; - true, change_info, VarinfoMap.add oV nV final_matches + true, change_info, addToFinalMatchesMapping oV nV final_matches ) else if not finalizeOnlyExactMatch then ( change_info.changed <- {old = gc_old; current = gc_new; unchangedHeader = true; diff = None} :: change_info.changed; - false, change_info, VarinfoMap.add oV nV final_matches + false, change_info, addToFinalMatchesMapping oV nV final_matches ) else false, change_info, final_matches ) let compare_varinfo_exact = compare_varinfo ~finalizeOnlyExactMatch:true -let addNewGlobals name gc_new (change_info, final_matches) = - let get_varinfo gc = match gc.decls, gc.def with +let get_varinfo gc = match gc.decls, gc.def with | _, Some (Var v) -> v | _, Some (Fun f) -> f.svar | Some v, _ -> v - | _ -> failwith "A global should have at least a declaration or a definition" in - if not (VarinfoMap.mem (get_varinfo gc_new) final_matches) then - let ext_added = gc_new :: change_info.added in - ({change_info with added = ext_added}, final_matches) - else (change_info, final_matches) + | _ -> failwith "A global should have at least a declaration or a definition" +let addNewGlobals name gc_new (change_info, final_matches) = + if not (VarinfoMap.mem (get_varinfo gc_new) (snd final_matches)) then + change_info.added <- gc_new :: change_info.added; + (change_info, final_matches) + +let addOldGlobals name gc_old (change_info, final_matches) = + if not (VarinfoMap.mem (get_varinfo gc_old) (fst final_matches)) then + change_info.removed <- gc_old :: change_info.removed; + (change_info, final_matches) + +let iname cg = List.mem (name_of_global_col cg) ["main"; "foo"; "bar"] +let inamev v = List.mem v.vname ["main"; "foo"; "bar"] let detectRenamedFunctions (oldMap : global_col StringMap.t) (newMap : global_col StringMap.t) = let extract_fundecs _ gc map = match gc.def with @@ -51,17 +61,30 @@ let detectRenamedFunctions (oldMap : global_col StringMap.t) (newMap : global_co | _ -> map in let var_fun_old = GlobalMap.fold extract_fundecs oldMap VarinfoMap.empty in let var_fun_new = GlobalMap.fold extract_fundecs newMap VarinfoMap.empty in - let empty_rename_assumptions m = VarinfoMap.for_all (fun vo vn -> vo.vname = vn.vname) m in (* TODO or in final_matches? *) + let extract_globs _ gc map = + let v = get_varinfo gc in + VarinfoMap.add v gc map in + let var_glob_old = GlobalMap.fold extract_globs oldMap VarinfoMap.empty in + let var_glob_new = GlobalMap.fold extract_globs newMap VarinfoMap.empty in + let empty_rename_assms m = VarinfoMap.for_all (fun vo vn -> vo.vname = vn.vname) m in (* TODO or in final_matches? *) let compare_fundec_exact_match f1 f2 change_info final_matches = - let doMatch, diff, function_dependencies, global_var_dependencies, renamesOnSuccess = CompareGlobals.eqF f1 f2 None VarinfoMap.empty VarinfoMap.empty in - match doMatch with - | Unchanged when empty_rename_assumptions function_dependencies && empty_rename_assumptions global_var_dependencies -> - performRenames renamesOnSuccess; - let change_info = {change_info with unchanged = change_info.unchanged} in - let final_matches = VarinfoMap.add f1.svar f2.svar final_matches in - true, change_info, final_matches - | _ -> false, change_info, final_matches + (* check that names of match are each only contained in new or old file *) + if not (preservesSameNameMatches f1.svar.vname oldMap f2.svar.vname newMap) then ( + false, change_info, final_matches + ) else + let doMatch, diff, fun_deps, global_deps, renamesOnSuccess = CompareGlobals.eqF f1 f2 None VarinfoMap.empty VarinfoMap.empty in + match doMatch with + | Unchanged when empty_rename_assms (VarinfoMap.filter (fun vo vn -> not (vo.vname = f1.svar.vname && vn.vname = f2.svar.vname)) fun_deps) && empty_rename_assms global_deps -> + performRenames renamesOnSuccess; + change_info.unchanged <- {old = VarinfoMap.find f1.svar var_glob_old; current = VarinfoMap.find f2.svar var_glob_new} :: change_info.unchanged; + let final_matches = addToFinalMatchesMapping f1.svar f2.svar final_matches in + true, change_info, final_matches + | Unchanged -> false, change_info, final_matches + | Changed -> false, change_info, final_matches + | ChangedFunHeader _ -> false, change_info, final_matches + | ForceReanalyze _ -> false, change_info, final_matches + in let matchGlobal ~matchVars ~matchFuns name gc_old (change_info, final_matches) = @@ -71,21 +94,19 @@ let detectRenamedFunctions (oldMap : global_col StringMap.t) (newMap : global_co let compare_same_name_fundec_check_contained_renames f1 f2 = let doMatch, diff, function_dependencies, global_var_dependencies, renamesOnSuccess = CompareGlobals.eqF f1 f2 None VarinfoMap.empty VarinfoMap.empty in performRenames renamesOnSuccess; (* updates enum names and compinfo names and keys that were collected during comparison of this matched function *) - (* TODO recursively check dependencies, check in rename mapping for globals that were already compared *) let funDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun f_old_var f_new_var (acc, ci, fm) -> - match VarinfoMap.find_opt f_old_var final_matches with + match VarinfoMap.find_opt f_old_var (fst final_matches) with | None -> let f_old = VarinfoMap.find f_old_var var_fun_old in let f_new = VarinfoMap.find f_new_var var_fun_new in (* TODO: what happens if there exists no fundec for this varinfo? *) - (* check that names of match are each only contained in new or old file *) if acc then compare_fundec_exact_match f_old f_new ci fm else false, ci, fm | Some v -> v = f_new_var, ci, fm) function_dependencies (true, change_info, final_matches) in let globalDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun old_var new_var (acc, ci, fm) -> - match VarinfoMap.find_opt old_var final_matches with + match VarinfoMap.find_opt old_var (fst final_matches) with | None -> - if acc && preservesSameNameMatches old_var.vname oldMap new_var.vname newMap then + if acc then compare_varinfo_exact old_var gc_old oldMap new_var gc_new newMap ci fm else false, ci, fm | Some v -> v = new_var, ci, fm @@ -101,10 +122,11 @@ let detectRenamedFunctions (oldMap : global_col StringMap.t) (newMap : global_co | Unchanged -> (* no diff is stored, also when comparing functions based on CFG because currently there is no mechanism to detect which part was affected by the *) append_to_changed ~unchangedHeader:true ~diff:None + | Changed -> append_to_changed ~unchangedHeader:true ~diff:diff | _ -> (* this can only be ForceReanalyze or ChangedFunHeader *) change_info.exclude_from_rel_destab <- VarinfoSet.add f1.svar change_info.exclude_from_rel_destab; append_to_changed ~unchangedHeader:false ~diff:None); - VarinfoMap.add f1.svar f2.svar final_matches in + addToFinalMatchesMapping f1.svar f2.svar final_matches in match gc_old.def, gc_new.def with | Some (Var v1), Some (Var v2) when matchVars -> let _, ci, fm = compare_varinfo v1 gc_old oldMap v2 gc_new newMap change_info final_matches in ci, fm @@ -113,9 +135,10 @@ let detectRenamedFunctions (oldMap : global_col StringMap.t) (newMap : global_co | Some v1, Some v2 when matchVars -> let _, ci, fm = compare_varinfo v1 gc_old oldMap v2 gc_new newMap change_info final_matches in ci, fm | _ -> change_info, final_matches) | _ -> change_info, final_matches - with Not_found -> let extendedRemoved = gc_old :: change_info.removed in {change_info with removed = extendedRemoved}, final_matches in + with Not_found -> change_info, final_matches in - (empty_change_info (), VarinfoMap.empty) (* change_info and final_matches is propagated *) + (empty_change_info (), (VarinfoMap.empty, VarinfoMap.empty)) (* change_info and final_matches (bi-directional) is propagated *) |> GlobalMap.fold (matchGlobal ~matchVars:true ~matchFuns:false) oldMap |> GlobalMap.fold (matchGlobal ~matchVars:false ~matchFuns:true) oldMap |> GlobalMap.fold addNewGlobals newMap + |> GlobalMap.fold addOldGlobals oldMap From f7363667b3f165939ba7fbfda8ab690302814faa Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Mon, 13 Mar 2023 09:58:30 +0100 Subject: [PATCH 73/90] add cram tests for rename detection --- .../04-var-rename/02-rename_and_shuffle.t | 19 +++++++++++++++++++ .../04-var-rename/03-rename_with_usage.t | 19 +++++++++++++++++++ .../04-var-rename/05-renamed_param.t | 19 +++++++++++++++++++ .../06-renamed_param_usage_changed.t | 19 +++++++++++++++++++ .../06-renamed_param_usage_changed.txt | 2 -- tests/incremental/04-var-rename/dune | 2 ++ .../05-method-rename/00-simple_rename.t | 19 +++++++++++++++++++ .../05-method-rename/01-dependent_rename.t | 19 +++++++++++++++++++ .../03-cyclic_rename_dependency.t | 19 +++++++++++++++++++ .../05-method-rename/04-cyclic_with_swap.t | 19 +++++++++++++++++++ .../05-method-rename/05-deep_change.t | 19 +++++++++++++++++++ .../05-method-rename/06-common_rename.t | 19 +++++++++++++++++++ .../05-method-rename/08-recursive_rename.t | 19 +++++++++++++++++++ tests/incremental/05-method-rename/dune | 2 ++ .../06-glob-var-rename/00-simple_rename.t | 19 +++++++++++++++++++ .../01-duplicate_local_global.t | 19 +++++++++++++++++++ .../06-glob-var-rename/02-add_new_gvar.t | 19 +++++++++++++++++++ tests/incremental/06-glob-var-rename/dune | 2 ++ tests/incremental/dune | 3 +++ 19 files changed, 275 insertions(+), 2 deletions(-) create mode 100644 tests/incremental/04-var-rename/02-rename_and_shuffle.t create mode 100644 tests/incremental/04-var-rename/03-rename_with_usage.t create mode 100644 tests/incremental/04-var-rename/05-renamed_param.t create mode 100644 tests/incremental/04-var-rename/06-renamed_param_usage_changed.t delete mode 100644 tests/incremental/04-var-rename/06-renamed_param_usage_changed.txt create mode 100644 tests/incremental/04-var-rename/dune create mode 100644 tests/incremental/05-method-rename/00-simple_rename.t create mode 100644 tests/incremental/05-method-rename/01-dependent_rename.t create mode 100644 tests/incremental/05-method-rename/03-cyclic_rename_dependency.t create mode 100644 tests/incremental/05-method-rename/04-cyclic_with_swap.t create mode 100644 tests/incremental/05-method-rename/05-deep_change.t create mode 100644 tests/incremental/05-method-rename/06-common_rename.t create mode 100644 tests/incremental/05-method-rename/08-recursive_rename.t create mode 100644 tests/incremental/05-method-rename/dune create mode 100644 tests/incremental/06-glob-var-rename/00-simple_rename.t create mode 100644 tests/incremental/06-glob-var-rename/01-duplicate_local_global.t create mode 100644 tests/incremental/06-glob-var-rename/02-add_new_gvar.t create mode 100644 tests/incremental/06-glob-var-rename/dune create mode 100644 tests/incremental/dune diff --git a/tests/incremental/04-var-rename/02-rename_and_shuffle.t b/tests/incremental/04-var-rename/02-rename_and_shuffle.t new file mode 100644 index 0000000000..10ff00e5a6 --- /dev/null +++ b/tests/incremental/04-var-rename/02-rename_and_shuffle.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 02-rename_and_shuffle.json --enable incremental.save 02-rename_and_shuffle.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 02-rename_and_shuffle.c + $ patch -b <02-rename_and_shuffle.patch + patching file 02-rename_and_shuffle.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 02-rename_and_shuffle.json --enable incremental.load 02-rename_and_shuffle.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 1 (with unchangedHeader = 1); added = 0; removed = 0 + +Revert patch + + $ patch -b -R <02-rename_and_shuffle.patch + patching file 02-rename_and_shuffle.c diff --git a/tests/incremental/04-var-rename/03-rename_with_usage.t b/tests/incremental/04-var-rename/03-rename_with_usage.t new file mode 100644 index 0000000000..32d9a95c6d --- /dev/null +++ b/tests/incremental/04-var-rename/03-rename_with_usage.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 03-rename_with_usage.json --enable incremental.save 03-rename_with_usage.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 03-rename_with_usage.c + $ patch -b <03-rename_with_usage.patch + patching file 03-rename_with_usage.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 03-rename_with_usage.json --enable incremental.load 03-rename_with_usage.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 + +Revert patch + + $ patch -b -R <03-rename_with_usage.patch + patching file 03-rename_with_usage.c diff --git a/tests/incremental/04-var-rename/05-renamed_param.t b/tests/incremental/04-var-rename/05-renamed_param.t new file mode 100644 index 0000000000..2401c1bd25 --- /dev/null +++ b/tests/incremental/04-var-rename/05-renamed_param.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 05-renamed_param.json --enable incremental.save 05-renamed_param.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 05-renamed_param.c + $ patch -b <05-renamed_param.patch + patching file 05-renamed_param.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 05-renamed_param.json --enable incremental.load 05-renamed_param.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 + +Revert patch + + $ patch -b -R <05-renamed_param.patch + patching file 05-renamed_param.c diff --git a/tests/incremental/04-var-rename/06-renamed_param_usage_changed.t b/tests/incremental/04-var-rename/06-renamed_param_usage_changed.t new file mode 100644 index 0000000000..ddc1b904aa --- /dev/null +++ b/tests/incremental/04-var-rename/06-renamed_param_usage_changed.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 06-renamed_param_usage_changed.json --enable incremental.save 06-renamed_param_usage_changed.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 06-renamed_param_usage_changed.c + $ patch -b <06-renamed_param_usage_changed.patch + patching file 06-renamed_param_usage_changed.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 06-renamed_param_usage_changed.json --enable incremental.load 06-renamed_param_usage_changed.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 1 (with unchangedHeader = 1); added = 0; removed = 0 + +Revert patch + + $ patch -b -R <06-renamed_param_usage_changed.patch + patching file 06-renamed_param_usage_changed.c diff --git a/tests/incremental/04-var-rename/06-renamed_param_usage_changed.txt b/tests/incremental/04-var-rename/06-renamed_param_usage_changed.txt deleted file mode 100644 index 0dc90594c7..0000000000 --- a/tests/incremental/04-var-rename/06-renamed_param_usage_changed.txt +++ /dev/null @@ -1,2 +0,0 @@ -function parameters a and b and swapped in the function header. But the function body stays the same. -Semantic changes. diff --git a/tests/incremental/04-var-rename/dune b/tests/incremental/04-var-rename/dune new file mode 100644 index 0000000000..1b37756f98 --- /dev/null +++ b/tests/incremental/04-var-rename/dune @@ -0,0 +1,2 @@ +(cram + (deps (glob_files *.{c,json,patch}) (sandbox preserve_file_kind))) diff --git a/tests/incremental/05-method-rename/00-simple_rename.t b/tests/incremental/05-method-rename/00-simple_rename.t new file mode 100644 index 0000000000..59a1cfa469 --- /dev/null +++ b/tests/incremental/05-method-rename/00-simple_rename.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 00-simple_rename.json --enable incremental.save 00-simple_rename.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 00-simple_rename.c + $ patch -b <00-simple_rename.patch + patching file 00-simple_rename.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 00-simple_rename.json --enable incremental.load 00-simple_rename.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 + +Revert patch + + $ patch -b -R <00-simple_rename.patch + patching file 00-simple_rename.c diff --git a/tests/incremental/05-method-rename/01-dependent_rename.t b/tests/incremental/05-method-rename/01-dependent_rename.t new file mode 100644 index 0000000000..75c5797c2a --- /dev/null +++ b/tests/incremental/05-method-rename/01-dependent_rename.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 01-dependent_rename.json --enable incremental.save 01-dependent_rename.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 01-dependent_rename.c + $ patch -b <01-dependent_rename.patch + patching file 01-dependent_rename.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 01-dependent_rename.json --enable incremental.load 01-dependent_rename.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 1 (with unchangedHeader = 1); added = 2; removed = 2 + +Revert patch + + $ patch -b -R <01-dependent_rename.patch + patching file 01-dependent_rename.c diff --git a/tests/incremental/05-method-rename/03-cyclic_rename_dependency.t b/tests/incremental/05-method-rename/03-cyclic_rename_dependency.t new file mode 100644 index 0000000000..5a90ebdbe3 --- /dev/null +++ b/tests/incremental/05-method-rename/03-cyclic_rename_dependency.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 03-cyclic_rename_dependency.json --enable incremental.save 03-cyclic_rename_dependency.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 03-cyclic_rename_dependency.c + $ patch -b <03-cyclic_rename_dependency.patch + patching file 03-cyclic_rename_dependency.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 03-cyclic_rename_dependency.json --enable incremental.load 03-cyclic_rename_dependency.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 1 (with unchangedHeader = 1); added = 2; removed = 2 + +Revert patch + + $ patch -b -R <03-cyclic_rename_dependency.patch + patching file 03-cyclic_rename_dependency.c diff --git a/tests/incremental/05-method-rename/04-cyclic_with_swap.t b/tests/incremental/05-method-rename/04-cyclic_with_swap.t new file mode 100644 index 0000000000..b0a269f646 --- /dev/null +++ b/tests/incremental/05-method-rename/04-cyclic_with_swap.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 04-cyclic_with_swap.json --enable incremental.save 04-cyclic_with_swap.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 04-cyclic_with_swap.c + $ patch -b <04-cyclic_with_swap.patch + patching file 04-cyclic_with_swap.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 04-cyclic_with_swap.json --enable incremental.load 04-cyclic_with_swap.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 1 (with unchangedHeader = 1); added = 3; removed = 2 + +Revert patch + + $ patch -b -R <04-cyclic_with_swap.patch + patching file 04-cyclic_with_swap.c diff --git a/tests/incremental/05-method-rename/05-deep_change.t b/tests/incremental/05-method-rename/05-deep_change.t new file mode 100644 index 0000000000..60aeb46e2a --- /dev/null +++ b/tests/incremental/05-method-rename/05-deep_change.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 05-deep_change.json --enable incremental.save 05-deep_change.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 05-deep_change.c + $ patch -b <05-deep_change.patch + patching file 05-deep_change.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 05-deep_change.json --enable incremental.load 05-deep_change.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 1 (with unchangedHeader = 1); added = 0; removed = 0 + +Revert patch + + $ patch -b -R <05-deep_change.patch + patching file 05-deep_change.c diff --git a/tests/incremental/05-method-rename/06-common_rename.t b/tests/incremental/05-method-rename/06-common_rename.t new file mode 100644 index 0000000000..4ba4bc2750 --- /dev/null +++ b/tests/incremental/05-method-rename/06-common_rename.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 06-common_rename.json --enable incremental.save 06-common_rename.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 06-common_rename.c + $ patch -b <06-common_rename.patch + patching file 06-common_rename.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 06-common_rename.json --enable incremental.load 06-common_rename.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 + +Revert patch + + $ patch -b -R <06-common_rename.patch + patching file 06-common_rename.c diff --git a/tests/incremental/05-method-rename/08-recursive_rename.t b/tests/incremental/05-method-rename/08-recursive_rename.t new file mode 100644 index 0000000000..5036b1da4b --- /dev/null +++ b/tests/incremental/05-method-rename/08-recursive_rename.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 08-recursive_rename.json --enable incremental.save 08-recursive_rename.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 08-recursive_rename.c + $ patch -b <08-recursive_rename.patch + patching file 08-recursive_rename.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 08-recursive_rename.json --enable incremental.load 08-recursive_rename.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 + +Revert patch + + $ patch -b -R <08-recursive_rename.patch + patching file 08-recursive_rename.c diff --git a/tests/incremental/05-method-rename/dune b/tests/incremental/05-method-rename/dune new file mode 100644 index 0000000000..1b37756f98 --- /dev/null +++ b/tests/incremental/05-method-rename/dune @@ -0,0 +1,2 @@ +(cram + (deps (glob_files *.{c,json,patch}) (sandbox preserve_file_kind))) diff --git a/tests/incremental/06-glob-var-rename/00-simple_rename.t b/tests/incremental/06-glob-var-rename/00-simple_rename.t new file mode 100644 index 0000000000..59a1cfa469 --- /dev/null +++ b/tests/incremental/06-glob-var-rename/00-simple_rename.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 00-simple_rename.json --enable incremental.save 00-simple_rename.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 00-simple_rename.c + $ patch -b <00-simple_rename.patch + patching file 00-simple_rename.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 00-simple_rename.json --enable incremental.load 00-simple_rename.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 + +Revert patch + + $ patch -b -R <00-simple_rename.patch + patching file 00-simple_rename.c diff --git a/tests/incremental/06-glob-var-rename/01-duplicate_local_global.t b/tests/incremental/06-glob-var-rename/01-duplicate_local_global.t new file mode 100644 index 0000000000..b1b73f4f26 --- /dev/null +++ b/tests/incremental/06-glob-var-rename/01-duplicate_local_global.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 01-duplicate_local_global.json --enable incremental.save 01-duplicate_local_global.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 01-duplicate_local_global.c + $ patch -b <01-duplicate_local_global.patch + patching file 01-duplicate_local_global.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 01-duplicate_local_global.json --enable incremental.load 01-duplicate_local_global.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 + +Revert patch + + $ patch -b -R <01-duplicate_local_global.patch + patching file 01-duplicate_local_global.c diff --git a/tests/incremental/06-glob-var-rename/02-add_new_gvar.t b/tests/incremental/06-glob-var-rename/02-add_new_gvar.t new file mode 100644 index 0000000000..8450df2d47 --- /dev/null +++ b/tests/incremental/06-glob-var-rename/02-add_new_gvar.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 02-add_new_gvar.json --enable incremental.save 02-add_new_gvar.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 02-add_new_gvar.c + $ patch -b <02-add_new_gvar.patch + patching file 02-add_new_gvar.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 02-add_new_gvar.json --enable incremental.load 02-add_new_gvar.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 1 (with unchangedHeader = 1); added = 1; removed = 0 + +Revert patch + + $ patch -b -R <02-add_new_gvar.patch + patching file 02-add_new_gvar.c diff --git a/tests/incremental/06-glob-var-rename/dune b/tests/incremental/06-glob-var-rename/dune new file mode 100644 index 0000000000..1b37756f98 --- /dev/null +++ b/tests/incremental/06-glob-var-rename/dune @@ -0,0 +1,2 @@ +(cram + (deps (glob_files *.{c,json,patch}) (sandbox preserve_file_kind))) diff --git a/tests/incremental/dune b/tests/incremental/dune new file mode 100644 index 0000000000..fdb1d941c2 --- /dev/null +++ b/tests/incremental/dune @@ -0,0 +1,3 @@ +(cram + (applies_to :whole_subtree) + (deps %{bin:goblint} (package goblint))) ; need entire package for includes/ From 5e1963f75f59010e61b077bec7e1756ff4b5d0e0 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Mon, 13 Mar 2023 10:06:29 +0100 Subject: [PATCH 74/90] remove superflous tests and those without clear expected result --- .../04-var-rename/01-unused_rename.c | 4 --- .../04-var-rename/01-unused_rename.json | 3 -- .../04-var-rename/01-unused_rename.patch | 8 ----- .../04-var-rename/01-unused_rename.txt | 3 -- .../04-var-rename/02-rename_and_shuffle.c | 2 +- .../04-var-rename/02-rename_and_shuffle.txt | 2 -- .../04-var-rename/03-rename_with_usage.txt | 2 -- .../04-var-rename/04-renamed_assert.c | 3 +- .../04-var-rename/04-renamed_assert.txt | 2 -- .../04-var-rename/05-renamed_param.c | 3 +- .../04-var-rename/05-renamed_param.txt | 2 -- .../05-method-rename/02-rename_and_swap.c | 19 ------------ .../05-method-rename/02-rename_and_swap.json | 3 -- .../05-method-rename/02-rename_and_swap.patch | 25 --------------- .../03-cyclic_rename_dependency.c | 2 -- .../05-method-rename/04-cyclic_with_swap.c | 2 -- .../07-common_rename_refactored.c | 20 ------------ .../07-common_rename_refactored.json | 3 -- .../07-common_rename_refactored.patch | 31 ------------------- 19 files changed, 5 insertions(+), 134 deletions(-) delete mode 100644 tests/incremental/04-var-rename/01-unused_rename.c delete mode 100644 tests/incremental/04-var-rename/01-unused_rename.json delete mode 100644 tests/incremental/04-var-rename/01-unused_rename.patch delete mode 100644 tests/incremental/04-var-rename/01-unused_rename.txt delete mode 100644 tests/incremental/04-var-rename/02-rename_and_shuffle.txt delete mode 100644 tests/incremental/04-var-rename/03-rename_with_usage.txt delete mode 100644 tests/incremental/04-var-rename/04-renamed_assert.txt delete mode 100644 tests/incremental/04-var-rename/05-renamed_param.txt delete mode 100644 tests/incremental/05-method-rename/02-rename_and_swap.c delete mode 100644 tests/incremental/05-method-rename/02-rename_and_swap.json delete mode 100644 tests/incremental/05-method-rename/02-rename_and_swap.patch delete mode 100644 tests/incremental/05-method-rename/07-common_rename_refactored.c delete mode 100644 tests/incremental/05-method-rename/07-common_rename_refactored.json delete mode 100644 tests/incremental/05-method-rename/07-common_rename_refactored.patch diff --git a/tests/incremental/04-var-rename/01-unused_rename.c b/tests/incremental/04-var-rename/01-unused_rename.c deleted file mode 100644 index 31eacd5bf9..0000000000 --- a/tests/incremental/04-var-rename/01-unused_rename.c +++ /dev/null @@ -1,4 +0,0 @@ -int main() { - int a = 0; - return 0; -} diff --git a/tests/incremental/04-var-rename/01-unused_rename.json b/tests/incremental/04-var-rename/01-unused_rename.json deleted file mode 100644 index 544b7b4ddd..0000000000 --- a/tests/incremental/04-var-rename/01-unused_rename.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} \ No newline at end of file diff --git a/tests/incremental/04-var-rename/01-unused_rename.patch b/tests/incremental/04-var-rename/01-unused_rename.patch deleted file mode 100644 index 977470ad53..0000000000 --- a/tests/incremental/04-var-rename/01-unused_rename.patch +++ /dev/null @@ -1,8 +0,0 @@ ---- 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; - } diff --git a/tests/incremental/04-var-rename/01-unused_rename.txt b/tests/incremental/04-var-rename/01-unused_rename.txt deleted file mode 100644 index a317916ad1..0000000000 --- a/tests/incremental/04-var-rename/01-unused_rename.txt +++ /dev/null @@ -1,3 +0,0 @@ -local variable a is renamed to b. -a/b is not used. -No semantic changes. diff --git a/tests/incremental/04-var-rename/02-rename_and_shuffle.c b/tests/incremental/04-var-rename/02-rename_and_shuffle.c index 9917738055..d851dcea95 100644 --- a/tests/incremental/04-var-rename/02-rename_and_shuffle.c +++ b/tests/incremental/04-var-rename/02-rename_and_shuffle.c @@ -1,6 +1,6 @@ #include -//a is renamed to c, but the usage of a is replaced by b +//a is renamed to c, but the usage of a is replaced by b (semantic changes) int main() { int a = 0; int b = 1; diff --git a/tests/incremental/04-var-rename/02-rename_and_shuffle.txt b/tests/incremental/04-var-rename/02-rename_and_shuffle.txt deleted file mode 100644 index 8c0ab5ac05..0000000000 --- a/tests/incremental/04-var-rename/02-rename_and_shuffle.txt +++ /dev/null @@ -1,2 +0,0 @@ -a is renamed to c, but the usage of a is replaced by b. -Semantic changes. diff --git a/tests/incremental/04-var-rename/03-rename_with_usage.txt b/tests/incremental/04-var-rename/03-rename_with_usage.txt deleted file mode 100644 index 18ff7e94d4..0000000000 --- a/tests/incremental/04-var-rename/03-rename_with_usage.txt +++ /dev/null @@ -1,2 +0,0 @@ -a is renamed to c, but the usage stays the same. -No semantic changes. diff --git a/tests/incremental/04-var-rename/04-renamed_assert.c b/tests/incremental/04-var-rename/04-renamed_assert.c index 665e44251c..b9f484ba01 100644 --- a/tests/incremental/04-var-rename/04-renamed_assert.c +++ b/tests/incremental/04-var-rename/04-renamed_assert.c @@ -1,9 +1,10 @@ #include +// local var used in assert is renamed (no semantic changes) int main() { int myVar = 0; __goblint_check(myVar < 11); return 0; -} \ No newline at end of file +} diff --git a/tests/incremental/04-var-rename/04-renamed_assert.txt b/tests/incremental/04-var-rename/04-renamed_assert.txt deleted file mode 100644 index 1afc289347..0000000000 --- a/tests/incremental/04-var-rename/04-renamed_assert.txt +++ /dev/null @@ -1,2 +0,0 @@ -local var used in assert is renamed. -No semantic changes. diff --git a/tests/incremental/04-var-rename/05-renamed_param.c b/tests/incremental/04-var-rename/05-renamed_param.c index 72fdfaf0e9..770af2683c 100644 --- a/tests/incremental/04-var-rename/05-renamed_param.c +++ b/tests/incremental/04-var-rename/05-renamed_param.c @@ -1,3 +1,4 @@ +// function param is renamed (no semantic changes) void method(int a) { int c = a; } @@ -5,4 +6,4 @@ void method(int a) { int main() { method(0); return 0; -} \ No newline at end of file +} diff --git a/tests/incremental/04-var-rename/05-renamed_param.txt b/tests/incremental/04-var-rename/05-renamed_param.txt deleted file mode 100644 index 09bca47979..0000000000 --- a/tests/incremental/04-var-rename/05-renamed_param.txt +++ /dev/null @@ -1,2 +0,0 @@ -Function param is renamed. -No semantic changes. diff --git a/tests/incremental/05-method-rename/02-rename_and_swap.c b/tests/incremental/05-method-rename/02-rename_and_swap.c deleted file mode 100644 index f62edd44a4..0000000000 --- a/tests/incremental/05-method-rename/02-rename_and_swap.c +++ /dev/null @@ -1,19 +0,0 @@ -#include - -void foo1() { - printf("foo1"); -} - -void foo2() { - foo1(); -} - -void foo3() { - foo1(); -} - -int main() { - foo2(); - foo3(); - return 0; -} diff --git a/tests/incremental/05-method-rename/02-rename_and_swap.json b/tests/incremental/05-method-rename/02-rename_and_swap.json deleted file mode 100644 index 0db3279e44..0000000000 --- a/tests/incremental/05-method-rename/02-rename_and_swap.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} diff --git a/tests/incremental/05-method-rename/02-rename_and_swap.patch b/tests/incremental/05-method-rename/02-rename_and_swap.patch deleted file mode 100644 index ab39c2dc4b..0000000000 --- a/tests/incremental/05-method-rename/02-rename_and_swap.patch +++ /dev/null @@ -1,25 +0,0 @@ ---- tests/incremental/05-method-rename/02-rename_and_swap.c -+++ tests/incremental/05-method-rename/02-rename_and_swap.c -@@ -1,15 +1,19 @@ - #include - --void foo1() { -+void newFun() { -+ printf("newFun"); -+} -+ -+void bar1() { - printf("foo1"); - } - - void foo2() { -- foo1(); -+ bar1(); - } - - void foo3() { -- foo1(); -+ newFun(); - } - - int main() { diff --git a/tests/incremental/05-method-rename/03-cyclic_rename_dependency.c b/tests/incremental/05-method-rename/03-cyclic_rename_dependency.c index 2509cfbcd5..331a5e25cb 100644 --- a/tests/incremental/05-method-rename/03-cyclic_rename_dependency.c +++ b/tests/incremental/05-method-rename/03-cyclic_rename_dependency.c @@ -1,7 +1,5 @@ #include -//Unchanged. - void foo1(int c) { if (c < 10) foo2(c + 1); } diff --git a/tests/incremental/05-method-rename/04-cyclic_with_swap.c b/tests/incremental/05-method-rename/04-cyclic_with_swap.c index 74123d5a14..34026afa92 100644 --- a/tests/incremental/05-method-rename/04-cyclic_with_swap.c +++ b/tests/incremental/05-method-rename/04-cyclic_with_swap.c @@ -1,7 +1,5 @@ #include -//Changed. - void foo1(int c) { if (c < 10) foo2(c + 1); } diff --git a/tests/incremental/05-method-rename/07-common_rename_refactored.c b/tests/incremental/05-method-rename/07-common_rename_refactored.c deleted file mode 100644 index ce72a6dda1..0000000000 --- a/tests/incremental/05-method-rename/07-common_rename_refactored.c +++ /dev/null @@ -1,20 +0,0 @@ -#include - -void foo() { - printf("foo"); -} - -void fun1() { - foo(); -} - -void fun2() { - foo(); -} - -int main() { - fun1(); - fun2(); - foo(); - return 0; -} diff --git a/tests/incremental/05-method-rename/07-common_rename_refactored.json b/tests/incremental/05-method-rename/07-common_rename_refactored.json deleted file mode 100644 index 0db3279e44..0000000000 --- a/tests/incremental/05-method-rename/07-common_rename_refactored.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} diff --git a/tests/incremental/05-method-rename/07-common_rename_refactored.patch b/tests/incremental/05-method-rename/07-common_rename_refactored.patch deleted file mode 100644 index 4c3d9fa1d6..0000000000 --- a/tests/incremental/05-method-rename/07-common_rename_refactored.patch +++ /dev/null @@ -1,31 +0,0 @@ ---- tests/incremental/05-method-rename/07-common_rename_refactored.c -+++ tests/incremental/05-method-rename/07-common_rename_refactored.c -@@ -1,20 +1,24 @@ - #include - --void foo() { -+void bar() { - printf("foo"); - } - -+void baz() { -+ printf("baz"); -+} -+ - void fun1() { -- foo(); -+ bar(); - } - - void fun2() { -- foo(); -+ bar(); - } - - int main() { - fun1(); - fun2(); -- foo(); -+ baz(); - return 0; - } From b90d0bcedd7a4bf319a124a3591785555e19db9a Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Mon, 13 Mar 2023 10:19:04 +0100 Subject: [PATCH 75/90] fix patches --- tests/incremental/04-var-rename/02-rename_and_shuffle.c | 2 +- .../incremental/04-var-rename/02-rename_and_shuffle.patch | 8 ++++---- .../incremental/04-var-rename/03-rename_with_usage.patch | 6 +++--- tests/incremental/04-var-rename/05-renamed_param.patch | 4 ++-- .../05-method-rename/03-cyclic_rename_dependency.patch | 4 +--- .../05-method-rename/04-cyclic_with_swap.patch | 4 +--- 6 files changed, 12 insertions(+), 16 deletions(-) diff --git a/tests/incremental/04-var-rename/02-rename_and_shuffle.c b/tests/incremental/04-var-rename/02-rename_and_shuffle.c index d851dcea95..7d6ea81e6f 100644 --- a/tests/incremental/04-var-rename/02-rename_and_shuffle.c +++ b/tests/incremental/04-var-rename/02-rename_and_shuffle.c @@ -1,6 +1,6 @@ #include -//a is renamed to c, but the usage of a is replaced by b (semantic changes) +// a is renamed to c, but the usage of a is replaced by b (semantic changes) int main() { int a = 0; int b = 1; diff --git a/tests/incremental/04-var-rename/02-rename_and_shuffle.patch b/tests/incremental/04-var-rename/02-rename_and_shuffle.patch index 5c1dc4785e..365696734c 100644 --- a/tests/incremental/04-var-rename/02-rename_and_shuffle.patch +++ b/tests/incremental/04-var-rename/02-rename_and_shuffle.patch @@ -1,15 +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 + + // a is renamed to c, but the usage of a is replaced by b (semantic changes) int main() { - int a = 0; + int c = 0; int b = 1; - + - printf("Print %d", a); + printf("Print %d", b); - + return 0; } diff --git a/tests/incremental/04-var-rename/03-rename_with_usage.patch b/tests/incremental/04-var-rename/03-rename_with_usage.patch index 26fb98b340..8421a5a920 100644 --- a/tests/incremental/04-var-rename/03-rename_with_usage.patch +++ b/tests/incremental/04-var-rename/03-rename_with_usage.patch @@ -1,15 +1,15 @@ --- tests/incremental/04-var-rename/03-rename_with_usage.c +++ tests/incremental/04-var-rename/03-rename_with_usage.c @@ -2,10 +2,10 @@ - + //a is renamed to c, but its usages stay the same int main() { - int a = 0; + int c = 0; int b = 1; - + - printf("Print %d", a); + printf("Print %d", c); - + return 0; } diff --git a/tests/incremental/04-var-rename/05-renamed_param.patch b/tests/incremental/04-var-rename/05-renamed_param.patch index 944566b05c..8e9963d689 100644 --- a/tests/incremental/04-var-rename/05-renamed_param.patch +++ b/tests/incremental/04-var-rename/05-renamed_param.patch @@ -1,10 +1,10 @@ --- tests/incremental/04-var-rename/05-renamed_param.c +++ tests/incremental/04-var-rename/05-renamed_param.c -@@ -1,5 +1,5 @@ +@@ -2,5 +2,5 @@ -void method(int a) { - int c = a; +void method(int b) { + int c = b; } - + int main() { diff --git a/tests/incremental/05-method-rename/03-cyclic_rename_dependency.patch b/tests/incremental/05-method-rename/03-cyclic_rename_dependency.patch index ae32544efd..936843dfad 100644 --- a/tests/incremental/05-method-rename/03-cyclic_rename_dependency.patch +++ b/tests/incremental/05-method-rename/03-cyclic_rename_dependency.patch @@ -1,8 +1,6 @@ --- tests/incremental/05-method-rename/03-cyclic_rename_dependency.c +++ tests/incremental/05-method-rename/03-cyclic_rename_dependency.c -@@ -2,16 +2,16 @@ - - //Unchanged. +@@ -2,14 +2,14 @@ -void foo1(int c) { - if (c < 10) foo2(c + 1); diff --git a/tests/incremental/05-method-rename/04-cyclic_with_swap.patch b/tests/incremental/05-method-rename/04-cyclic_with_swap.patch index 7e96afd8e0..534faecae4 100644 --- a/tests/incremental/05-method-rename/04-cyclic_with_swap.patch +++ b/tests/incremental/05-method-rename/04-cyclic_with_swap.patch @@ -1,8 +1,6 @@ --- tests/incremental/05-method-rename/04-cyclic_with_swap.c +++ tests/incremental/05-method-rename/04-cyclic_with_swap.c -@@ -2,15 +2,19 @@ - - //Changed. +@@ -2,13 +2,17 @@ -void foo1(int c) { - if (c < 10) foo2(c + 1); From 7c365ba988c19447f4652380f2f30d68dcb73afa Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Mon, 13 Mar 2023 10:30:18 +0100 Subject: [PATCH 76/90] re-establish consecutive test ids --- ..._and_shuffle.c => 01-rename_and_shuffle.c} | 0 ...huffle.json => 01-rename_and_shuffle.json} | 0 ...ffle.patch => 01-rename_and_shuffle.patch} | 4 ++-- .../04-var-rename/01-rename_and_shuffle.t | 19 +++++++++++++++++++ .../04-var-rename/02-rename_and_shuffle.t | 19 ------------------- ...me_with_usage.c => 02-rename_with_usage.c} | 0 ...h_usage.json => 02-rename_with_usage.json} | 0 ...usage.patch => 02-rename_with_usage.patch} | 4 ++-- .../04-var-rename/02-rename_with_usage.t | 19 +++++++++++++++++++ .../04-var-rename/03-rename_with_usage.t | 19 ------------------- ...4-renamed_assert.c => 03-renamed_assert.c} | 0 ...med_assert.json => 03-renamed_assert.json} | 0 ...d_assert.patch => 03-renamed_assert.patch} | 4 ++-- ...{05-renamed_param.c => 04-renamed_param.c} | 0 ...named_param.json => 04-renamed_param.json} | 0 ...med_param.patch => 04-renamed_param.patch} | 4 ++-- .../04-var-rename/04-renamed_param.t | 19 +++++++++++++++++++ .../04-var-rename/05-renamed_param.t | 19 ------------------- ...ged.c => 05-renamed_param_usage_changed.c} | 0 ...on => 05-renamed_param_usage_changed.json} | 0 ...h => 05-renamed_param_usage_changed.patch} | 6 +++--- .../05-renamed_param_usage_changed.t | 19 +++++++++++++++++++ .../06-renamed_param_usage_changed.t | 19 ------------------- ...ndency.c => 02-cyclic_rename_dependency.c} | 0 ....json => 02-cyclic_rename_dependency.json} | 0 ...atch => 02-cyclic_rename_dependency.patch} | 4 ++-- .../02-cyclic_rename_dependency.t | 19 +++++++++++++++++++ .../03-cyclic_rename_dependency.t | 19 ------------------- ...clic_with_swap.c => 03-cyclic_with_swap.c} | 0 ...ith_swap.json => 03-cyclic_with_swap.json} | 0 ...h_swap.patch => 03-cyclic_with_swap.patch} | 4 ++-- .../05-method-rename/03-cyclic_with_swap.t | 19 +++++++++++++++++++ .../05-method-rename/04-cyclic_with_swap.t | 19 ------------------- .../{05-deep_change.c => 04-deep_change.c} | 0 ...5-deep_change.json => 04-deep_change.json} | 0 .../05-method-rename/04-deep_change.patch | 11 +++++++++++ .../05-method-rename/04-deep_change.t | 19 +++++++++++++++++++ ...{06-common_rename.c => 05-common_rename.c} | 0 ...mmon_rename.json => 05-common_rename.json} | 0 ...on_rename.patch => 05-common_rename.patch} | 4 ++-- .../05-method-rename/05-common_rename.t | 19 +++++++++++++++++++ .../05-method-rename/05-deep_change.patch | 11 ----------- .../05-method-rename/05-deep_change.t | 19 ------------------- .../05-method-rename/06-common_rename.t | 19 ------------------- ...cursive_rename.c => 06-recursive_rename.c} | 0 ...e_rename.json => 06-recursive_rename.json} | 0 ...rename.patch => 06-recursive_rename.patch} | 4 ++-- .../05-method-rename/06-recursive_rename.t | 19 +++++++++++++++++++ .../05-method-rename/08-recursive_rename.t | 19 ------------------- 49 files changed, 201 insertions(+), 201 deletions(-) rename tests/incremental/04-var-rename/{02-rename_and_shuffle.c => 01-rename_and_shuffle.c} (100%) rename tests/incremental/04-var-rename/{02-rename_and_shuffle.json => 01-rename_and_shuffle.json} (100%) rename tests/incremental/04-var-rename/{02-rename_and_shuffle.patch => 01-rename_and_shuffle.patch} (66%) create mode 100644 tests/incremental/04-var-rename/01-rename_and_shuffle.t delete mode 100644 tests/incremental/04-var-rename/02-rename_and_shuffle.t rename tests/incremental/04-var-rename/{03-rename_with_usage.c => 02-rename_with_usage.c} (100%) rename tests/incremental/04-var-rename/{03-rename_with_usage.json => 02-rename_with_usage.json} (100%) rename tests/incremental/04-var-rename/{03-rename_with_usage.patch => 02-rename_with_usage.patch} (63%) create mode 100644 tests/incremental/04-var-rename/02-rename_with_usage.t delete mode 100644 tests/incremental/04-var-rename/03-rename_with_usage.t rename tests/incremental/04-var-rename/{04-renamed_assert.c => 03-renamed_assert.c} (100%) rename tests/incremental/04-var-rename/{04-renamed_assert.json => 03-renamed_assert.json} (100%) rename tests/incremental/04-var-rename/{04-renamed_assert.patch => 03-renamed_assert.patch} (64%) rename tests/incremental/04-var-rename/{05-renamed_param.c => 04-renamed_param.c} (100%) rename tests/incremental/04-var-rename/{05-renamed_param.json => 04-renamed_param.json} (100%) rename tests/incremental/04-var-rename/{05-renamed_param.patch => 04-renamed_param.patch} (50%) create mode 100644 tests/incremental/04-var-rename/04-renamed_param.t delete mode 100644 tests/incremental/04-var-rename/05-renamed_param.t rename tests/incremental/04-var-rename/{06-renamed_param_usage_changed.c => 05-renamed_param_usage_changed.c} (100%) rename tests/incremental/04-var-rename/{06-renamed_param_usage_changed.json => 05-renamed_param_usage_changed.json} (100%) rename tests/incremental/04-var-rename/{06-renamed_param_usage_changed.patch => 05-renamed_param_usage_changed.patch} (55%) create mode 100644 tests/incremental/04-var-rename/05-renamed_param_usage_changed.t delete mode 100644 tests/incremental/04-var-rename/06-renamed_param_usage_changed.t rename tests/incremental/05-method-rename/{03-cyclic_rename_dependency.c => 02-cyclic_rename_dependency.c} (100%) rename tests/incremental/05-method-rename/{03-cyclic_rename_dependency.json => 02-cyclic_rename_dependency.json} (100%) rename tests/incremental/05-method-rename/{03-cyclic_rename_dependency.patch => 02-cyclic_rename_dependency.patch} (71%) create mode 100644 tests/incremental/05-method-rename/02-cyclic_rename_dependency.t delete mode 100644 tests/incremental/05-method-rename/03-cyclic_rename_dependency.t rename tests/incremental/05-method-rename/{04-cyclic_with_swap.c => 03-cyclic_with_swap.c} (100%) rename tests/incremental/05-method-rename/{04-cyclic_with_swap.json => 03-cyclic_with_swap.json} (100%) rename tests/incremental/05-method-rename/{04-cyclic_with_swap.patch => 03-cyclic_with_swap.patch} (73%) create mode 100644 tests/incremental/05-method-rename/03-cyclic_with_swap.t delete mode 100644 tests/incremental/05-method-rename/04-cyclic_with_swap.t rename tests/incremental/05-method-rename/{05-deep_change.c => 04-deep_change.c} (100%) rename tests/incremental/05-method-rename/{05-deep_change.json => 04-deep_change.json} (100%) create mode 100644 tests/incremental/05-method-rename/04-deep_change.patch create mode 100644 tests/incremental/05-method-rename/04-deep_change.t rename tests/incremental/05-method-rename/{06-common_rename.c => 05-common_rename.c} (100%) rename tests/incremental/05-method-rename/{06-common_rename.json => 05-common_rename.json} (100%) rename tests/incremental/05-method-rename/{06-common_rename.patch => 05-common_rename.patch} (68%) create mode 100644 tests/incremental/05-method-rename/05-common_rename.t delete mode 100644 tests/incremental/05-method-rename/05-deep_change.patch delete mode 100644 tests/incremental/05-method-rename/05-deep_change.t delete mode 100644 tests/incremental/05-method-rename/06-common_rename.t rename tests/incremental/05-method-rename/{08-recursive_rename.c => 06-recursive_rename.c} (100%) rename tests/incremental/05-method-rename/{08-recursive_rename.json => 06-recursive_rename.json} (100%) rename tests/incremental/05-method-rename/{08-recursive_rename.patch => 06-recursive_rename.patch} (56%) create mode 100644 tests/incremental/05-method-rename/06-recursive_rename.t delete mode 100644 tests/incremental/05-method-rename/08-recursive_rename.t diff --git a/tests/incremental/04-var-rename/02-rename_and_shuffle.c b/tests/incremental/04-var-rename/01-rename_and_shuffle.c similarity index 100% rename from tests/incremental/04-var-rename/02-rename_and_shuffle.c rename to tests/incremental/04-var-rename/01-rename_and_shuffle.c diff --git a/tests/incremental/04-var-rename/02-rename_and_shuffle.json b/tests/incremental/04-var-rename/01-rename_and_shuffle.json similarity index 100% rename from tests/incremental/04-var-rename/02-rename_and_shuffle.json rename to tests/incremental/04-var-rename/01-rename_and_shuffle.json diff --git a/tests/incremental/04-var-rename/02-rename_and_shuffle.patch b/tests/incremental/04-var-rename/01-rename_and_shuffle.patch similarity index 66% rename from tests/incremental/04-var-rename/02-rename_and_shuffle.patch rename to tests/incremental/04-var-rename/01-rename_and_shuffle.patch index 365696734c..94e27d9a80 100644 --- a/tests/incremental/04-var-rename/02-rename_and_shuffle.patch +++ b/tests/incremental/04-var-rename/01-rename_and_shuffle.patch @@ -1,5 +1,5 @@ ---- tests/incremental/04-var-rename/02-rename_and_shuffle.c -+++ tests/incremental/04-var-rename/02-rename_and_shuffle.c +--- tests/incremental/04-var-rename/01-rename_and_shuffle.c ++++ tests/incremental/04-var-rename/01-rename_and_shuffle.c @@ -2,10 +2,10 @@ // a is renamed to c, but the usage of a is replaced by b (semantic changes) diff --git a/tests/incremental/04-var-rename/01-rename_and_shuffle.t b/tests/incremental/04-var-rename/01-rename_and_shuffle.t new file mode 100644 index 0000000000..5cfb03eb54 --- /dev/null +++ b/tests/incremental/04-var-rename/01-rename_and_shuffle.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 01-rename_and_shuffle.json --enable incremental.save 01-rename_and_shuffle.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 01-rename_and_shuffle.c + $ patch -b <01-rename_and_shuffle.patch + patching file 01-rename_and_shuffle.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 01-rename_and_shuffle.json --enable incremental.load 01-rename_and_shuffle.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 1 (with unchangedHeader = 1); added = 0; removed = 0 + +Revert patch + + $ patch -b -R <01-rename_and_shuffle.patch + patching file 01-rename_and_shuffle.c diff --git a/tests/incremental/04-var-rename/02-rename_and_shuffle.t b/tests/incremental/04-var-rename/02-rename_and_shuffle.t deleted file mode 100644 index 10ff00e5a6..0000000000 --- a/tests/incremental/04-var-rename/02-rename_and_shuffle.t +++ /dev/null @@ -1,19 +0,0 @@ -Run Goblint on initial program version - - $ goblint --conf 02-rename_and_shuffle.json --enable incremental.save 02-rename_and_shuffle.c > /dev/null 2>&1 - -Apply patch - - $ chmod +w 02-rename_and_shuffle.c - $ patch -b <02-rename_and_shuffle.patch - patching file 02-rename_and_shuffle.c - -Run Goblint incrementally on new program version and check the change detection result - - $ goblint --conf 02-rename_and_shuffle.json --enable incremental.load 02-rename_and_shuffle.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' - changed = 1 (with unchangedHeader = 1); added = 0; removed = 0 - -Revert patch - - $ patch -b -R <02-rename_and_shuffle.patch - patching file 02-rename_and_shuffle.c diff --git a/tests/incremental/04-var-rename/03-rename_with_usage.c b/tests/incremental/04-var-rename/02-rename_with_usage.c similarity index 100% rename from tests/incremental/04-var-rename/03-rename_with_usage.c rename to tests/incremental/04-var-rename/02-rename_with_usage.c diff --git a/tests/incremental/04-var-rename/03-rename_with_usage.json b/tests/incremental/04-var-rename/02-rename_with_usage.json similarity index 100% rename from tests/incremental/04-var-rename/03-rename_with_usage.json rename to tests/incremental/04-var-rename/02-rename_with_usage.json diff --git a/tests/incremental/04-var-rename/03-rename_with_usage.patch b/tests/incremental/04-var-rename/02-rename_with_usage.patch similarity index 63% rename from tests/incremental/04-var-rename/03-rename_with_usage.patch rename to tests/incremental/04-var-rename/02-rename_with_usage.patch index 8421a5a920..6cfe41bbb1 100644 --- a/tests/incremental/04-var-rename/03-rename_with_usage.patch +++ b/tests/incremental/04-var-rename/02-rename_with_usage.patch @@ -1,5 +1,5 @@ ---- tests/incremental/04-var-rename/03-rename_with_usage.c -+++ tests/incremental/04-var-rename/03-rename_with_usage.c +--- tests/incremental/04-var-rename/02-rename_with_usage.c ++++ tests/incremental/04-var-rename/02-rename_with_usage.c @@ -2,10 +2,10 @@ //a is renamed to c, but its usages stay the same diff --git a/tests/incremental/04-var-rename/02-rename_with_usage.t b/tests/incremental/04-var-rename/02-rename_with_usage.t new file mode 100644 index 0000000000..2abea2988f --- /dev/null +++ b/tests/incremental/04-var-rename/02-rename_with_usage.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 02-rename_with_usage.json --enable incremental.save 02-rename_with_usage.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 02-rename_with_usage.c + $ patch -b <02-rename_with_usage.patch + patching file 02-rename_with_usage.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 02-rename_with_usage.json --enable incremental.load 02-rename_with_usage.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 + +Revert patch + + $ patch -b -R <02-rename_with_usage.patch + patching file 02-rename_with_usage.c diff --git a/tests/incremental/04-var-rename/03-rename_with_usage.t b/tests/incremental/04-var-rename/03-rename_with_usage.t deleted file mode 100644 index 32d9a95c6d..0000000000 --- a/tests/incremental/04-var-rename/03-rename_with_usage.t +++ /dev/null @@ -1,19 +0,0 @@ -Run Goblint on initial program version - - $ goblint --conf 03-rename_with_usage.json --enable incremental.save 03-rename_with_usage.c > /dev/null 2>&1 - -Apply patch - - $ chmod +w 03-rename_with_usage.c - $ patch -b <03-rename_with_usage.patch - patching file 03-rename_with_usage.c - -Run Goblint incrementally on new program version and check the change detection result - - $ goblint --conf 03-rename_with_usage.json --enable incremental.load 03-rename_with_usage.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' - changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 - -Revert patch - - $ patch -b -R <03-rename_with_usage.patch - patching file 03-rename_with_usage.c diff --git a/tests/incremental/04-var-rename/04-renamed_assert.c b/tests/incremental/04-var-rename/03-renamed_assert.c similarity index 100% rename from tests/incremental/04-var-rename/04-renamed_assert.c rename to tests/incremental/04-var-rename/03-renamed_assert.c diff --git a/tests/incremental/04-var-rename/04-renamed_assert.json b/tests/incremental/04-var-rename/03-renamed_assert.json similarity index 100% rename from tests/incremental/04-var-rename/04-renamed_assert.json rename to tests/incremental/04-var-rename/03-renamed_assert.json diff --git a/tests/incremental/04-var-rename/04-renamed_assert.patch b/tests/incremental/04-var-rename/03-renamed_assert.patch similarity index 64% rename from tests/incremental/04-var-rename/04-renamed_assert.patch rename to tests/incremental/04-var-rename/03-renamed_assert.patch index d7dfe6ae8e..c672e68044 100644 --- a/tests/incremental/04-var-rename/04-renamed_assert.patch +++ b/tests/incremental/04-var-rename/03-renamed_assert.patch @@ -1,5 +1,5 @@ ---- tests/incremental/04-var-rename/04-renamed_assert.c -+++ tests/incremental/04-var-rename/04-renamed_assert.c +--- tests/incremental/04-var-rename/03-renamed_assert.c ++++ tests/incremental/04-var-rename/03-renamed_assert.c @@ -1,7 +1,7 @@ int main() { - int myVar = 0; diff --git a/tests/incremental/04-var-rename/05-renamed_param.c b/tests/incremental/04-var-rename/04-renamed_param.c similarity index 100% rename from tests/incremental/04-var-rename/05-renamed_param.c rename to tests/incremental/04-var-rename/04-renamed_param.c diff --git a/tests/incremental/04-var-rename/05-renamed_param.json b/tests/incremental/04-var-rename/04-renamed_param.json similarity index 100% rename from tests/incremental/04-var-rename/05-renamed_param.json rename to tests/incremental/04-var-rename/04-renamed_param.json diff --git a/tests/incremental/04-var-rename/05-renamed_param.patch b/tests/incremental/04-var-rename/04-renamed_param.patch similarity index 50% rename from tests/incremental/04-var-rename/05-renamed_param.patch rename to tests/incremental/04-var-rename/04-renamed_param.patch index 8e9963d689..50a9b69f6a 100644 --- a/tests/incremental/04-var-rename/05-renamed_param.patch +++ b/tests/incremental/04-var-rename/04-renamed_param.patch @@ -1,5 +1,5 @@ ---- tests/incremental/04-var-rename/05-renamed_param.c -+++ tests/incremental/04-var-rename/05-renamed_param.c +--- tests/incremental/04-var-rename/04-renamed_param.c ++++ tests/incremental/04-var-rename/04-renamed_param.c @@ -2,5 +2,5 @@ -void method(int a) { - int c = a; diff --git a/tests/incremental/04-var-rename/04-renamed_param.t b/tests/incremental/04-var-rename/04-renamed_param.t new file mode 100644 index 0000000000..ed13d38fd7 --- /dev/null +++ b/tests/incremental/04-var-rename/04-renamed_param.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 04-renamed_param.json --enable incremental.save 04-renamed_param.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 04-renamed_param.c + $ patch -b <04-renamed_param.patch + patching file 04-renamed_param.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 04-renamed_param.json --enable incremental.load 04-renamed_param.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 + +Revert patch + + $ patch -b -R <04-renamed_param.patch + patching file 04-renamed_param.c diff --git a/tests/incremental/04-var-rename/05-renamed_param.t b/tests/incremental/04-var-rename/05-renamed_param.t deleted file mode 100644 index 2401c1bd25..0000000000 --- a/tests/incremental/04-var-rename/05-renamed_param.t +++ /dev/null @@ -1,19 +0,0 @@ -Run Goblint on initial program version - - $ goblint --conf 05-renamed_param.json --enable incremental.save 05-renamed_param.c > /dev/null 2>&1 - -Apply patch - - $ chmod +w 05-renamed_param.c - $ patch -b <05-renamed_param.patch - patching file 05-renamed_param.c - -Run Goblint incrementally on new program version and check the change detection result - - $ goblint --conf 05-renamed_param.json --enable incremental.load 05-renamed_param.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' - changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 - -Revert patch - - $ patch -b -R <05-renamed_param.patch - patching file 05-renamed_param.c diff --git a/tests/incremental/04-var-rename/06-renamed_param_usage_changed.c b/tests/incremental/04-var-rename/05-renamed_param_usage_changed.c similarity index 100% rename from tests/incremental/04-var-rename/06-renamed_param_usage_changed.c rename to tests/incremental/04-var-rename/05-renamed_param_usage_changed.c diff --git a/tests/incremental/04-var-rename/06-renamed_param_usage_changed.json b/tests/incremental/04-var-rename/05-renamed_param_usage_changed.json similarity index 100% rename from tests/incremental/04-var-rename/06-renamed_param_usage_changed.json rename to tests/incremental/04-var-rename/05-renamed_param_usage_changed.json diff --git a/tests/incremental/04-var-rename/06-renamed_param_usage_changed.patch b/tests/incremental/04-var-rename/05-renamed_param_usage_changed.patch similarity index 55% rename from tests/incremental/04-var-rename/06-renamed_param_usage_changed.patch rename to tests/incremental/04-var-rename/05-renamed_param_usage_changed.patch index a93e45c4c5..9ffc2c1cea 100644 --- a/tests/incremental/04-var-rename/06-renamed_param_usage_changed.patch +++ b/tests/incremental/04-var-rename/05-renamed_param_usage_changed.patch @@ -1,8 +1,8 @@ ---- tests/incremental/04-var-rename/06-renamed_param_usage_changed.c -+++ tests/incremental/04-var-rename/06-renamed_param_usage_changed.c +--- tests/incremental/04-var-rename/05-renamed_param_usage_changed.c ++++ tests/incremental/04-var-rename/05-renamed_param_usage_changed.c @@ -1,6 +1,6 @@ //This test should mark foo and main as changed - + -void foo(int a, int b) { +void foo(int b, int a) { int x = a; diff --git a/tests/incremental/04-var-rename/05-renamed_param_usage_changed.t b/tests/incremental/04-var-rename/05-renamed_param_usage_changed.t new file mode 100644 index 0000000000..7f23cd649f --- /dev/null +++ b/tests/incremental/04-var-rename/05-renamed_param_usage_changed.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 05-renamed_param_usage_changed.json --enable incremental.save 05-renamed_param_usage_changed.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 05-renamed_param_usage_changed.c + $ patch -b <05-renamed_param_usage_changed.patch + patching file 05-renamed_param_usage_changed.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 05-renamed_param_usage_changed.json --enable incremental.load 05-renamed_param_usage_changed.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 1 (with unchangedHeader = 1); added = 0; removed = 0 + +Revert patch + + $ patch -b -R <05-renamed_param_usage_changed.patch + patching file 05-renamed_param_usage_changed.c diff --git a/tests/incremental/04-var-rename/06-renamed_param_usage_changed.t b/tests/incremental/04-var-rename/06-renamed_param_usage_changed.t deleted file mode 100644 index ddc1b904aa..0000000000 --- a/tests/incremental/04-var-rename/06-renamed_param_usage_changed.t +++ /dev/null @@ -1,19 +0,0 @@ -Run Goblint on initial program version - - $ goblint --conf 06-renamed_param_usage_changed.json --enable incremental.save 06-renamed_param_usage_changed.c > /dev/null 2>&1 - -Apply patch - - $ chmod +w 06-renamed_param_usage_changed.c - $ patch -b <06-renamed_param_usage_changed.patch - patching file 06-renamed_param_usage_changed.c - -Run Goblint incrementally on new program version and check the change detection result - - $ goblint --conf 06-renamed_param_usage_changed.json --enable incremental.load 06-renamed_param_usage_changed.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' - changed = 1 (with unchangedHeader = 1); added = 0; removed = 0 - -Revert patch - - $ patch -b -R <06-renamed_param_usage_changed.patch - patching file 06-renamed_param_usage_changed.c diff --git a/tests/incremental/05-method-rename/03-cyclic_rename_dependency.c b/tests/incremental/05-method-rename/02-cyclic_rename_dependency.c similarity index 100% rename from tests/incremental/05-method-rename/03-cyclic_rename_dependency.c rename to tests/incremental/05-method-rename/02-cyclic_rename_dependency.c diff --git a/tests/incremental/05-method-rename/03-cyclic_rename_dependency.json b/tests/incremental/05-method-rename/02-cyclic_rename_dependency.json similarity index 100% rename from tests/incremental/05-method-rename/03-cyclic_rename_dependency.json rename to tests/incremental/05-method-rename/02-cyclic_rename_dependency.json diff --git a/tests/incremental/05-method-rename/03-cyclic_rename_dependency.patch b/tests/incremental/05-method-rename/02-cyclic_rename_dependency.patch similarity index 71% rename from tests/incremental/05-method-rename/03-cyclic_rename_dependency.patch rename to tests/incremental/05-method-rename/02-cyclic_rename_dependency.patch index 936843dfad..7f15d88c3a 100644 --- a/tests/incremental/05-method-rename/03-cyclic_rename_dependency.patch +++ b/tests/incremental/05-method-rename/02-cyclic_rename_dependency.patch @@ -1,5 +1,5 @@ ---- tests/incremental/05-method-rename/03-cyclic_rename_dependency.c -+++ tests/incremental/05-method-rename/03-cyclic_rename_dependency.c +--- tests/incremental/05-method-rename/02-cyclic_rename_dependency.c ++++ tests/incremental/05-method-rename/02-cyclic_rename_dependency.c @@ -2,14 +2,14 @@ -void foo1(int c) { diff --git a/tests/incremental/05-method-rename/02-cyclic_rename_dependency.t b/tests/incremental/05-method-rename/02-cyclic_rename_dependency.t new file mode 100644 index 0000000000..0d706cf320 --- /dev/null +++ b/tests/incremental/05-method-rename/02-cyclic_rename_dependency.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 02-cyclic_rename_dependency.json --enable incremental.save 02-cyclic_rename_dependency.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 02-cyclic_rename_dependency.c + $ patch -b <02-cyclic_rename_dependency.patch + patching file 02-cyclic_rename_dependency.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 02-cyclic_rename_dependency.json --enable incremental.load 02-cyclic_rename_dependency.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 1 (with unchangedHeader = 1); added = 2; removed = 2 + +Revert patch + + $ patch -b -R <02-cyclic_rename_dependency.patch + patching file 02-cyclic_rename_dependency.c diff --git a/tests/incremental/05-method-rename/03-cyclic_rename_dependency.t b/tests/incremental/05-method-rename/03-cyclic_rename_dependency.t deleted file mode 100644 index 5a90ebdbe3..0000000000 --- a/tests/incremental/05-method-rename/03-cyclic_rename_dependency.t +++ /dev/null @@ -1,19 +0,0 @@ -Run Goblint on initial program version - - $ goblint --conf 03-cyclic_rename_dependency.json --enable incremental.save 03-cyclic_rename_dependency.c > /dev/null 2>&1 - -Apply patch - - $ chmod +w 03-cyclic_rename_dependency.c - $ patch -b <03-cyclic_rename_dependency.patch - patching file 03-cyclic_rename_dependency.c - -Run Goblint incrementally on new program version and check the change detection result - - $ goblint --conf 03-cyclic_rename_dependency.json --enable incremental.load 03-cyclic_rename_dependency.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' - changed = 1 (with unchangedHeader = 1); added = 2; removed = 2 - -Revert patch - - $ patch -b -R <03-cyclic_rename_dependency.patch - patching file 03-cyclic_rename_dependency.c diff --git a/tests/incremental/05-method-rename/04-cyclic_with_swap.c b/tests/incremental/05-method-rename/03-cyclic_with_swap.c similarity index 100% rename from tests/incremental/05-method-rename/04-cyclic_with_swap.c rename to tests/incremental/05-method-rename/03-cyclic_with_swap.c diff --git a/tests/incremental/05-method-rename/04-cyclic_with_swap.json b/tests/incremental/05-method-rename/03-cyclic_with_swap.json similarity index 100% rename from tests/incremental/05-method-rename/04-cyclic_with_swap.json rename to tests/incremental/05-method-rename/03-cyclic_with_swap.json diff --git a/tests/incremental/05-method-rename/04-cyclic_with_swap.patch b/tests/incremental/05-method-rename/03-cyclic_with_swap.patch similarity index 73% rename from tests/incremental/05-method-rename/04-cyclic_with_swap.patch rename to tests/incremental/05-method-rename/03-cyclic_with_swap.patch index 534faecae4..0886106162 100644 --- a/tests/incremental/05-method-rename/04-cyclic_with_swap.patch +++ b/tests/incremental/05-method-rename/03-cyclic_with_swap.patch @@ -1,5 +1,5 @@ ---- tests/incremental/05-method-rename/04-cyclic_with_swap.c -+++ tests/incremental/05-method-rename/04-cyclic_with_swap.c +--- tests/incremental/05-method-rename/03-cyclic_with_swap.c ++++ tests/incremental/05-method-rename/03-cyclic_with_swap.c @@ -2,13 +2,17 @@ -void foo1(int c) { diff --git a/tests/incremental/05-method-rename/03-cyclic_with_swap.t b/tests/incremental/05-method-rename/03-cyclic_with_swap.t new file mode 100644 index 0000000000..8bed0df5e9 --- /dev/null +++ b/tests/incremental/05-method-rename/03-cyclic_with_swap.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 03-cyclic_with_swap.json --enable incremental.save 03-cyclic_with_swap.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 03-cyclic_with_swap.c + $ patch -b <03-cyclic_with_swap.patch + patching file 03-cyclic_with_swap.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 03-cyclic_with_swap.json --enable incremental.load 03-cyclic_with_swap.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 1 (with unchangedHeader = 1); added = 3; removed = 2 + +Revert patch + + $ patch -b -R <03-cyclic_with_swap.patch + patching file 03-cyclic_with_swap.c diff --git a/tests/incremental/05-method-rename/04-cyclic_with_swap.t b/tests/incremental/05-method-rename/04-cyclic_with_swap.t deleted file mode 100644 index b0a269f646..0000000000 --- a/tests/incremental/05-method-rename/04-cyclic_with_swap.t +++ /dev/null @@ -1,19 +0,0 @@ -Run Goblint on initial program version - - $ goblint --conf 04-cyclic_with_swap.json --enable incremental.save 04-cyclic_with_swap.c > /dev/null 2>&1 - -Apply patch - - $ chmod +w 04-cyclic_with_swap.c - $ patch -b <04-cyclic_with_swap.patch - patching file 04-cyclic_with_swap.c - -Run Goblint incrementally on new program version and check the change detection result - - $ goblint --conf 04-cyclic_with_swap.json --enable incremental.load 04-cyclic_with_swap.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' - changed = 1 (with unchangedHeader = 1); added = 3; removed = 2 - -Revert patch - - $ patch -b -R <04-cyclic_with_swap.patch - patching file 04-cyclic_with_swap.c diff --git a/tests/incremental/05-method-rename/05-deep_change.c b/tests/incremental/05-method-rename/04-deep_change.c similarity index 100% rename from tests/incremental/05-method-rename/05-deep_change.c rename to tests/incremental/05-method-rename/04-deep_change.c diff --git a/tests/incremental/05-method-rename/05-deep_change.json b/tests/incremental/05-method-rename/04-deep_change.json similarity index 100% rename from tests/incremental/05-method-rename/05-deep_change.json rename to tests/incremental/05-method-rename/04-deep_change.json diff --git a/tests/incremental/05-method-rename/04-deep_change.patch b/tests/incremental/05-method-rename/04-deep_change.patch new file mode 100644 index 0000000000..687b8f74bc --- /dev/null +++ b/tests/incremental/05-method-rename/04-deep_change.patch @@ -0,0 +1,11 @@ +--- tests/incremental/05-method-rename/04-deep_change.c ++++ tests/incremental/05-method-rename/04-deep_change.c +@@ -1,7 +1,7 @@ + #include + + void zap() { +- printf("zap"); ++ printf("drap"); + } + + void bar() { diff --git a/tests/incremental/05-method-rename/04-deep_change.t b/tests/incremental/05-method-rename/04-deep_change.t new file mode 100644 index 0000000000..3ac9ac649c --- /dev/null +++ b/tests/incremental/05-method-rename/04-deep_change.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 04-deep_change.json --enable incremental.save 04-deep_change.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 04-deep_change.c + $ patch -b <04-deep_change.patch + patching file 04-deep_change.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 04-deep_change.json --enable incremental.load 04-deep_change.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 1 (with unchangedHeader = 1); added = 0; removed = 0 + +Revert patch + + $ patch -b -R <04-deep_change.patch + patching file 04-deep_change.c diff --git a/tests/incremental/05-method-rename/06-common_rename.c b/tests/incremental/05-method-rename/05-common_rename.c similarity index 100% rename from tests/incremental/05-method-rename/06-common_rename.c rename to tests/incremental/05-method-rename/05-common_rename.c diff --git a/tests/incremental/05-method-rename/06-common_rename.json b/tests/incremental/05-method-rename/05-common_rename.json similarity index 100% rename from tests/incremental/05-method-rename/06-common_rename.json rename to tests/incremental/05-method-rename/05-common_rename.json diff --git a/tests/incremental/05-method-rename/06-common_rename.patch b/tests/incremental/05-method-rename/05-common_rename.patch similarity index 68% rename from tests/incremental/05-method-rename/06-common_rename.patch rename to tests/incremental/05-method-rename/05-common_rename.patch index 15afbce9ce..93904d5780 100644 --- a/tests/incremental/05-method-rename/06-common_rename.patch +++ b/tests/incremental/05-method-rename/05-common_rename.patch @@ -1,5 +1,5 @@ ---- tests/incremental/05-method-rename/06-common_rename.c -+++ tests/incremental/05-method-rename/06-common_rename.c +--- tests/incremental/05-method-rename/05-common_rename.c ++++ tests/incremental/05-method-rename/05-common_rename.c @@ -1,20 +1,20 @@ #include diff --git a/tests/incremental/05-method-rename/05-common_rename.t b/tests/incremental/05-method-rename/05-common_rename.t new file mode 100644 index 0000000000..faa7ae9f7f --- /dev/null +++ b/tests/incremental/05-method-rename/05-common_rename.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 05-common_rename.json --enable incremental.save 05-common_rename.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 05-common_rename.c + $ patch -b <05-common_rename.patch + patching file 05-common_rename.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 05-common_rename.json --enable incremental.load 05-common_rename.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 + +Revert patch + + $ patch -b -R <05-common_rename.patch + patching file 05-common_rename.c diff --git a/tests/incremental/05-method-rename/05-deep_change.patch b/tests/incremental/05-method-rename/05-deep_change.patch deleted file mode 100644 index 0374da2fb6..0000000000 --- a/tests/incremental/05-method-rename/05-deep_change.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- tests/incremental/05-method-rename/05-deep_change.c -+++ tests/incremental/05-method-rename/05-deep_change.c -@@ -1,7 +1,7 @@ - #include - - void zap() { -- printf("zap"); -+ printf("drap"); - } - - void bar() { diff --git a/tests/incremental/05-method-rename/05-deep_change.t b/tests/incremental/05-method-rename/05-deep_change.t deleted file mode 100644 index 60aeb46e2a..0000000000 --- a/tests/incremental/05-method-rename/05-deep_change.t +++ /dev/null @@ -1,19 +0,0 @@ -Run Goblint on initial program version - - $ goblint --conf 05-deep_change.json --enable incremental.save 05-deep_change.c > /dev/null 2>&1 - -Apply patch - - $ chmod +w 05-deep_change.c - $ patch -b <05-deep_change.patch - patching file 05-deep_change.c - -Run Goblint incrementally on new program version and check the change detection result - - $ goblint --conf 05-deep_change.json --enable incremental.load 05-deep_change.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' - changed = 1 (with unchangedHeader = 1); added = 0; removed = 0 - -Revert patch - - $ patch -b -R <05-deep_change.patch - patching file 05-deep_change.c diff --git a/tests/incremental/05-method-rename/06-common_rename.t b/tests/incremental/05-method-rename/06-common_rename.t deleted file mode 100644 index 4ba4bc2750..0000000000 --- a/tests/incremental/05-method-rename/06-common_rename.t +++ /dev/null @@ -1,19 +0,0 @@ -Run Goblint on initial program version - - $ goblint --conf 06-common_rename.json --enable incremental.save 06-common_rename.c > /dev/null 2>&1 - -Apply patch - - $ chmod +w 06-common_rename.c - $ patch -b <06-common_rename.patch - patching file 06-common_rename.c - -Run Goblint incrementally on new program version and check the change detection result - - $ goblint --conf 06-common_rename.json --enable incremental.load 06-common_rename.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' - changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 - -Revert patch - - $ patch -b -R <06-common_rename.patch - patching file 06-common_rename.c diff --git a/tests/incremental/05-method-rename/08-recursive_rename.c b/tests/incremental/05-method-rename/06-recursive_rename.c similarity index 100% rename from tests/incremental/05-method-rename/08-recursive_rename.c rename to tests/incremental/05-method-rename/06-recursive_rename.c diff --git a/tests/incremental/05-method-rename/08-recursive_rename.json b/tests/incremental/05-method-rename/06-recursive_rename.json similarity index 100% rename from tests/incremental/05-method-rename/08-recursive_rename.json rename to tests/incremental/05-method-rename/06-recursive_rename.json diff --git a/tests/incremental/05-method-rename/08-recursive_rename.patch b/tests/incremental/05-method-rename/06-recursive_rename.patch similarity index 56% rename from tests/incremental/05-method-rename/08-recursive_rename.patch rename to tests/incremental/05-method-rename/06-recursive_rename.patch index 42469f434c..356f959256 100644 --- a/tests/incremental/05-method-rename/08-recursive_rename.patch +++ b/tests/incremental/05-method-rename/06-recursive_rename.patch @@ -1,5 +1,5 @@ ---- tests/incremental/05-method-rename/08-recursive_rename.c -+++ tests/incremental/05-method-rename/08-recursive_rename.c +--- tests/incremental/05-method-rename/06-recursive_rename.c ++++ tests/incremental/05-method-rename/06-recursive_rename.c @@ -1,7 +1,7 @@ -void foo(int x) { - if(x > 1) foo(x - 1); diff --git a/tests/incremental/05-method-rename/06-recursive_rename.t b/tests/incremental/05-method-rename/06-recursive_rename.t new file mode 100644 index 0000000000..b7d0fabe3e --- /dev/null +++ b/tests/incremental/05-method-rename/06-recursive_rename.t @@ -0,0 +1,19 @@ +Run Goblint on initial program version + + $ goblint --conf 06-recursive_rename.json --enable incremental.save 06-recursive_rename.c > /dev/null 2>&1 + +Apply patch + + $ chmod +w 06-recursive_rename.c + $ patch -b <06-recursive_rename.patch + patching file 06-recursive_rename.c + +Run Goblint incrementally on new program version and check the change detection result + + $ goblint --conf 06-recursive_rename.json --enable incremental.load 06-recursive_rename.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' + changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 + +Revert patch + + $ patch -b -R <06-recursive_rename.patch + patching file 06-recursive_rename.c diff --git a/tests/incremental/05-method-rename/08-recursive_rename.t b/tests/incremental/05-method-rename/08-recursive_rename.t deleted file mode 100644 index 5036b1da4b..0000000000 --- a/tests/incremental/05-method-rename/08-recursive_rename.t +++ /dev/null @@ -1,19 +0,0 @@ -Run Goblint on initial program version - - $ goblint --conf 08-recursive_rename.json --enable incremental.save 08-recursive_rename.c > /dev/null 2>&1 - -Apply patch - - $ chmod +w 08-recursive_rename.c - $ patch -b <08-recursive_rename.patch - patching file 08-recursive_rename.c - -Run Goblint incrementally on new program version and check the change detection result - - $ goblint --conf 08-recursive_rename.json --enable incremental.load 08-recursive_rename.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' - changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 - -Revert patch - - $ patch -b -R <08-recursive_rename.patch - patching file 08-recursive_rename.c From 4bb775ab3c80ffabb8d550bb2f6927229c2efbc0 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Mon, 13 Mar 2023 10:58:03 +0100 Subject: [PATCH 77/90] move rename detection and CompareGlobals back to CompareCIL --- src/framework/analyses.ml | 2 +- src/incremental/compareCIL.ml | 267 +++++++++++++++++++++- src/incremental/compareGlobals.ml | 122 ---------- src/incremental/detectRenamedFunctions.ml | 144 ------------ src/incremental/updateCil.ml | 2 +- src/util/server.ml | 2 +- 6 files changed, 264 insertions(+), 275 deletions(-) delete mode 100644 src/incremental/compareGlobals.ml delete mode 100644 src/incremental/detectRenamedFunctions.ml diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 3bbddcb71a..a86689ac1e 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -480,7 +480,7 @@ type increment_data = { server: bool; solver_data: Obj.t; - changes: CompareGlobals.change_info; + changes: CompareCIL.change_info; (* Globals for which the constraint system unknowns should be restarted *) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 1bda93b6bc..136c8434a3 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -1,21 +1,276 @@ open GoblintCil open MyCFG -include DetectRenamedFunctions include CompareAST include CompareCFG open CilMaps +module GlobalMap = Map.Make(String) + +type global_def = Var of varinfo | Fun of fundec +type global_col = {decls: varinfo option; def: global_def option} + +let name_of_global_col gc = match gc.def with + | Some (Fun f) -> f.svar.vname + | Some (Var v) -> v.vname + | None -> match gc.decls with + | Some v -> v.vname + | None -> raise (Failure "empty global record") + +let compare_global_col gc1 gc2 = compare (name_of_global_col gc1) (name_of_global_col gc2) + +module GlobalColMap = Map.Make( + struct + type t = global_col + let compare = compare_global_col + end) + +let name_of_global g = match g with + | GVar (v,_,_) -> v.vname + | GFun (f,_) -> f.svar.vname + | GVarDecl (v,_) -> v.vname + | _ -> failwith "global constructor not supported" + +type nodes_diff = { + unchangedNodes: (node * node) list; + primObsoleteNodes: node list; (** primary obsolete nodes -> all obsolete nodes are reachable from these *) +} + +type unchanged_global = { + old: global_col; + current: global_col +} +(** For semantically unchanged globals, still keep old and current version of global for resetting current to old. *) + +type changed_global = { + old: global_col; + current: global_col; + unchangedHeader: bool; + diff: nodes_diff option +} + +module VarinfoSet = Set.Make(CilType.Varinfo) + +type change_info = { + mutable changed: changed_global list; + mutable unchanged: unchanged_global list; + mutable removed: global_col list; + mutable added: global_col list; + mutable exclude_from_rel_destab: VarinfoSet.t; + (** Set of functions that are to be force-reanalyzed. + These functions are additionally included in the [changed] field, among the other changed globals. *) +} + +let empty_change_info () : change_info = + {added = []; removed = []; changed = []; unchanged = []; exclude_from_rel_destab = VarinfoSet.empty} + +(* 'ChangedFunHeader' is used for functions whose varinfo or formal parameters changed. 'Changed' is used only for + * changed functions whose header is unchanged and changed non-function globals *) +type change_status = Unchanged | Changed | ChangedFunHeader of Cil.fundec | ForceReanalyze of Cil.fundec + +(** Given a boolean that indicates whether the code object is identical to the previous version, returns the corresponding [change_status]*) +let unchanged_to_change_status = function + | true -> Unchanged + | false -> Changed + +let empty_rename_mapping: rename_mapping = (StringMap.empty, VarinfoMap.empty, VarinfoMap.empty, ([], [])) + +let should_reanalyze (fdec: Cil.fundec) = + List.mem fdec.svar.vname (GobConfig.get_string_list "incremental.force-reanalyze.funs") + +(* 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 (old: Cil.fundec) (current: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (global_function_rename_mapping: method_rename_assumptions) (global_var_rename_mapping: glob_var_rename_assumptions) = + let identical, diffOpt, (_, renamed_method_dependencies, renamed_global_vars_dependencies, renamesOnSuccess) = + if should_reanalyze current then + ForceReanalyze current, None, empty_rename_mapping + else + + let add_locals_to_rename_mapping la lb map = + try + List.fold_left (fun map (a, b) -> StringMap.add a.vname b.vname map) map (List.combine la lb) + with Invalid_argument _ -> map in + + let parameterMapping = add_locals_to_rename_mapping old.sformals current.sformals StringMap.empty in + let renameMapping = (parameterMapping, global_function_rename_mapping, global_var_rename_mapping, ([], [])) in + + (* compare the function header based on the collected rename assumptions for parameters *) + let unchangedHeader, renameMapping = eq_varinfo old.svar current.svar ~rename_mapping:renameMapping + &&>> forward_list_equal eq_varinfo old.sformals current.sformals in + + if not unchangedHeader then ChangedFunHeader current, None, empty_rename_mapping + else + (* include matching of local variables into rename mapping *) + let renameMapping = match renameMapping with + | (pm, gf, gv, re) -> (add_locals_to_rename_mapping old.slocals current.slocals pm, gf, gv, re) in + let sameLocals, renameMapping = forward_list_equal eq_varinfo old.slocals current.slocals ~rename_mapping:renameMapping in + + if not sameLocals then + (Changed, None, empty_rename_mapping) + else + match cfgs with + | None -> + let (identical, new_rename_mapping) = eq_block (old.sbody, old) (current.sbody, current) ~rename_mapping:renameMapping in + unchanged_to_change_status identical, None, new_rename_mapping + | 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 + let matches, diffNodes1, updated_rename_mapping = compareFun (module CfgOld) (module CfgNew) old current renameMapping in + if diffNodes1 = [] then (Unchanged, None, updated_rename_mapping) + else (Changed, Some {unchangedNodes = matches; primObsoleteNodes = diffNodes1}, updated_rename_mapping) + in + identical, diffOpt, renamed_method_dependencies, renamed_global_vars_dependencies, renamesOnSuccess + +let performRenames (renamesOnSuccess: renamesOnSuccess) = + begin + let (compinfoRenames, enumRenames) = renamesOnSuccess in + List.iter (fun (compinfo2, compinfo1) -> compinfo2.cname <- compinfo1.cname; compinfo2.ckey <- compinfo1.ckey) compinfoRenames; + List.iter (fun (enum2, enum1) -> enum2.ename <- enum1.ename) enumRenames; + end + +let preservesSameNameMatches n_old oldMap n_new newMap = n_old = n_new || (not (GlobalMap.mem n_old newMap) && not (GlobalMap.mem n_new oldMap)) + +let addToFinalMatchesMapping oV nV final_matches = + VarinfoMap.add oV nV (fst final_matches), VarinfoMap.add nV oV (snd final_matches) + +(* TODO: possibly merge with eq_varinfo, provide only varinfo and mapping from varinfo to global_col *) +(* Compares two varinfos. finalizeOnlyExactMatch=true allows to check a rename assumption and discard the comparison result in case they do not match *) +let compare_varinfo ?(finalizeOnlyExactMatch=false) oV gc_old oldMap nV gc_new newMap change_info final_matches = + if not (preservesSameNameMatches oV.vname oldMap nV.vname newMap) then + (* do not allow for matches between differently named variables if one of the variables names exists in both, the new and old file *) + false, change_info, final_matches + else ( + (* TODO does the emptyness of the dependencies need to be checked? *) + let identical, (_, function_dependencies, global_var_dependencies, renamesOnSuccess) = eq_varinfo oV nV ~rename_mapping:empty_rename_mapping in + + if not finalizeOnlyExactMatch || identical then + performRenames renamesOnSuccess; (* updates enum names and compinfo names and keys that were collected during comparison of this matched function *) + if identical then ( + change_info.unchanged <- {old = gc_old; current = gc_new} :: change_info.unchanged; + true, change_info, addToFinalMatchesMapping oV nV final_matches + ) else if not finalizeOnlyExactMatch then ( + change_info.changed <- {old = gc_old; current = gc_new; unchangedHeader = true; diff = None} :: change_info.changed; + false, change_info, addToFinalMatchesMapping oV nV final_matches + ) else + false, change_info, final_matches + ) +let compare_varinfo_exact = compare_varinfo ~finalizeOnlyExactMatch:true + +let get_varinfo gc = match gc.decls, gc.def with + | _, Some (Var v) -> v + | _, Some (Fun f) -> f.svar + | Some v, _ -> v + | _ -> failwith "A global should have at least a declaration or a definition" + +let addNewGlobals name gc_new (change_info, final_matches) = + if not (VarinfoMap.mem (get_varinfo gc_new) (snd final_matches)) then + change_info.added <- gc_new :: change_info.added; + (change_info, final_matches) + +let addOldGlobals name gc_old (change_info, final_matches) = + if not (VarinfoMap.mem (get_varinfo gc_old) (fst final_matches)) then + change_info.removed <- gc_old :: change_info.removed; + (change_info, final_matches) + +let detectRenamedFunctions (oldMap : global_col StringMap.t) (newMap : global_col StringMap.t) = + let extract_fundecs _ gc map = match gc.def with + | Some (Fun f) -> VarinfoMap.add f.svar f map + | _ -> map in + let var_fun_old = GlobalMap.fold extract_fundecs oldMap VarinfoMap.empty in + let var_fun_new = GlobalMap.fold extract_fundecs newMap VarinfoMap.empty in + let extract_globs _ gc map = + let v = get_varinfo gc in + VarinfoMap.add v gc map in + let var_glob_old = GlobalMap.fold extract_globs oldMap VarinfoMap.empty in + let var_glob_new = GlobalMap.fold extract_globs newMap VarinfoMap.empty in + let empty_rename_assms m = VarinfoMap.for_all (fun vo vn -> vo.vname = vn.vname) m in (* TODO or in final_matches? *) + + let compare_fundec_exact_match f1 f2 change_info final_matches = + (* check that names of match are each only contained in new or old file *) + if not (preservesSameNameMatches f1.svar.vname oldMap f2.svar.vname newMap) then ( + false, change_info, final_matches + ) else + let doMatch, diff, fun_deps, global_deps, renamesOnSuccess = eqF f1 f2 None VarinfoMap.empty VarinfoMap.empty in + match doMatch with + | Unchanged when empty_rename_assms (VarinfoMap.filter (fun vo vn -> not (vo.vname = f1.svar.vname && vn.vname = f2.svar.vname)) fun_deps) && empty_rename_assms global_deps -> + performRenames renamesOnSuccess; + change_info.unchanged <- {old = VarinfoMap.find f1.svar var_glob_old; current = VarinfoMap.find f2.svar var_glob_new} :: change_info.unchanged; + let final_matches = addToFinalMatchesMapping f1.svar f2.svar final_matches in + true, change_info, final_matches + | Unchanged -> false, change_info, final_matches + | Changed -> false, change_info, final_matches + | ChangedFunHeader _ -> false, change_info, final_matches + | ForceReanalyze _ -> false, change_info, final_matches + + in + + let matchGlobal ~matchVars ~matchFuns name gc_old (change_info, final_matches) = + try + let gc_new = StringMap.find name newMap in + + let compare_same_name_fundec_check_contained_renames f1 f2 = + let doMatch, diff, function_dependencies, global_var_dependencies, renamesOnSuccess = eqF f1 f2 None VarinfoMap.empty VarinfoMap.empty in + performRenames renamesOnSuccess; (* updates enum names and compinfo names and keys that were collected during comparison of this matched function *) + let funDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun f_old_var f_new_var (acc, ci, fm) -> + match VarinfoMap.find_opt f_old_var (fst final_matches) with + | None -> + let f_old = VarinfoMap.find f_old_var var_fun_old in + let f_new = VarinfoMap.find f_new_var var_fun_new in (* TODO: what happens if there exists no fundec for this varinfo? *) + if acc then + compare_fundec_exact_match f_old f_new ci fm + else false, ci, fm + | Some v -> v = f_new_var, ci, fm) function_dependencies (true, change_info, final_matches) in + let globalDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun old_var new_var (acc, ci, fm) -> + match VarinfoMap.find_opt old_var (fst final_matches) with + | None -> + if acc then + compare_varinfo_exact old_var gc_old oldMap new_var gc_new newMap ci fm + else false, ci, fm + | Some v -> v = new_var, ci, fm + ) global_var_dependencies (true, change_info, final_matches) in + let dependenciesMatch = funDependenciesMatch && globalDependenciesMatch in + let append_to_changed ~unchangedHeader ~diff = + change_info.changed <- {current = gc_new; old = gc_old; unchangedHeader; diff} :: change_info.changed + in + (* TODO: merge with no-rename-detection case in compareCIL.compareCilFiles *) + (match doMatch with + | Unchanged when dependenciesMatch -> + change_info.unchanged <- {old = gc_old; current = gc_new} :: change_info.unchanged + | Unchanged -> + (* no diff is stored, also when comparing functions based on CFG because currently there is no mechanism to detect which part was affected by the *) + append_to_changed ~unchangedHeader:true ~diff:None + | Changed -> append_to_changed ~unchangedHeader:true ~diff:diff + | _ -> (* this can only be ForceReanalyze or ChangedFunHeader *) + change_info.exclude_from_rel_destab <- VarinfoSet.add f1.svar change_info.exclude_from_rel_destab; + append_to_changed ~unchangedHeader:false ~diff:None); + addToFinalMatchesMapping f1.svar f2.svar final_matches in + + match gc_old.def, gc_new.def with + | Some (Var v1), Some (Var v2) when matchVars -> let _, ci, fm = compare_varinfo v1 gc_old oldMap v2 gc_new newMap change_info final_matches in ci, fm + | Some (Fun f1), Some (Fun f2) when matchFuns -> change_info, compare_same_name_fundec_check_contained_renames f1 f2 + | None, None -> (match gc_old.decls, gc_new.decls with + | Some v1, Some v2 when matchVars -> let _, ci, fm = compare_varinfo v1 gc_old oldMap v2 gc_new newMap change_info final_matches in ci, fm + | _ -> change_info, final_matches) + | _ -> change_info, final_matches + with Not_found -> change_info, final_matches in + + (empty_change_info (), (VarinfoMap.empty, VarinfoMap.empty)) (* change_info and final_matches (bi-directional) is propagated *) + |> GlobalMap.fold (matchGlobal ~matchVars:true ~matchFuns:false) oldMap + |> GlobalMap.fold (matchGlobal ~matchVars:false ~matchFuns:true) oldMap + |> GlobalMap.fold addNewGlobals newMap + |> GlobalMap.fold addOldGlobals oldMap + let eq_glob (old: global_col) (current: global_col) (cfgs : (cfg * (cfg * cfg)) option) = let identical, diff, renamesOnSuccess = match old.def, current.def with | Some (Var x), Some (Var y) -> let identical, (_,_,_,renamesOnSuccess) = eq_varinfo x y ~rename_mapping:empty_rename_mapping in unchanged_to_change_status identical, None, renamesOnSuccess (* ignore the init_info - a changed init of a global will lead to a different start state *) - | Some (Fun f), Some (Fun g) -> ( - let identical, diffOpt, funDep, globVarDep, renamesOnSuccess = CompareGlobals.eqF f g cfgs VarinfoMap.empty VarinfoMap.empty in + | Some (Fun f), Some (Fun g) -> + let identical, diffOpt, funDep, globVarDep, renamesOnSuccess = eqF f g cfgs VarinfoMap.empty VarinfoMap.empty in (*Perform renames no matter what.*) - match identical with - | Unchanged when not (VarinfoMap.is_empty funDep && VarinfoMap.for_all (fun ov nv -> ov.vname = nv.vname) globVarDep) -> Changed, diffOpt, renamesOnSuccess - | s -> s, diffOpt, renamesOnSuccess) + (match identical with + | Unchanged when not (VarinfoMap.is_empty funDep && VarinfoMap.for_all (fun ov nv -> ov.vname = nv.vname) globVarDep) -> Changed, diffOpt, renamesOnSuccess + | s -> s, diffOpt, renamesOnSuccess) | None, None -> (match old.decls, current.decls with | Some x, Some y -> let identical, (_,_,_,renamesOnSuccess) = eq_varinfo x y ~rename_mapping:empty_rename_mapping in diff --git a/src/incremental/compareGlobals.ml b/src/incremental/compareGlobals.ml deleted file mode 100644 index 57883fff7c..0000000000 --- a/src/incremental/compareGlobals.ml +++ /dev/null @@ -1,122 +0,0 @@ -open GoblintCil -open MyCFG -open CilMaps -include CompareAST -include CompareCFG - -module GlobalMap = Map.Make(String) - -type global_def = Var of varinfo | Fun of fundec -type global_col = {decls: varinfo option; def: global_def option} - -let name_of_global_col gc = match gc.def with - | Some (Fun f) -> f.svar.vname - | Some (Var v) -> v.vname - | None -> match gc.decls with - | Some v -> v.vname - | None -> raise (Failure "empty global record") - -let compare_global_col gc1 gc2 = compare (name_of_global_col gc1) (name_of_global_col gc2) - -module GlobalColMap = Map.Make( - struct - type t = global_col - let compare = compare_global_col - end) - -let name_of_global g = match g with - | GVar (v,_,_) -> v.vname - | GFun (f,_) -> f.svar.vname - | GVarDecl (v,_) -> v.vname - | _ -> failwith "global constructor not supported" - -type nodes_diff = { - unchangedNodes: (node * node) list; - primObsoleteNodes: node list; (** primary obsolete nodes -> all obsolete nodes are reachable from these *) -} - -type unchanged_global = { - old: global_col; - current: global_col -} -(** For semantically unchanged globals, still keep old and current version of global for resetting current to old. *) - -type changed_global = { - old: global_col; - current: global_col; - unchangedHeader: bool; - diff: nodes_diff option -} - -module VarinfoSet = Set.Make(CilType.Varinfo) - -type change_info = { - mutable changed: changed_global list; - mutable unchanged: unchanged_global list; - mutable removed: global_col list; - mutable added: global_col list; - mutable exclude_from_rel_destab: VarinfoSet.t; - (** Set of functions that are to be force-reanalyzed. - These functions are additionally included in the [changed] field, among the other changed globals. *) -} - -let empty_change_info () : change_info = - {added = []; removed = []; changed = []; unchanged = []; exclude_from_rel_destab = VarinfoSet.empty} - -(* 'ChangedFunHeader' is used for functions whose varinfo or formal parameters changed. 'Changed' is used only for - * changed functions whose header is unchanged and changed non-function globals *) -type change_status = Unchanged | Changed | ChangedFunHeader of Cil.fundec | ForceReanalyze of Cil.fundec - -(** Given a boolean that indicates whether the code object is identical to the previous version, returns the corresponding [change_status]*) -let unchanged_to_change_status = function - | true -> Unchanged - | false -> Changed - -let empty_rename_mapping: rename_mapping = (StringMap.empty, VarinfoMap.empty, VarinfoMap.empty, ([], [])) - -let should_reanalyze (fdec: Cil.fundec) = - List.mem fdec.svar.vname (GobConfig.get_string_list "incremental.force-reanalyze.funs") - -(* 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 (old: Cil.fundec) (current: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) option) (global_function_rename_mapping: method_rename_assumptions) (global_var_rename_mapping: glob_var_rename_assumptions) = - let identical, diffOpt, (_, renamed_method_dependencies, renamed_global_vars_dependencies, renamesOnSuccess) = - if should_reanalyze current then - ForceReanalyze current, None, empty_rename_mapping - else - - let add_locals_to_rename_mapping la lb map = - try - List.fold_left (fun map (a, b) -> StringMap.add a.vname b.vname map) map (List.combine la lb) - with Invalid_argument _ -> map in - - let parameterMapping = add_locals_to_rename_mapping old.sformals current.sformals StringMap.empty in - let renameMapping = (parameterMapping, global_function_rename_mapping, global_var_rename_mapping, ([], [])) in - - (* compare the function header based on the collected rename assumptions for parameters *) - let unchangedHeader, renameMapping = eq_varinfo old.svar current.svar ~rename_mapping:renameMapping - &&>> forward_list_equal eq_varinfo old.sformals current.sformals in - - if not unchangedHeader then ChangedFunHeader current, None, empty_rename_mapping - else - (* include matching of local variables into rename mapping *) - let renameMapping = match renameMapping with - | (pm, gf, gv, re) -> (add_locals_to_rename_mapping old.slocals current.slocals pm, gf, gv, re) in - let sameLocals, renameMapping = forward_list_equal eq_varinfo old.slocals current.slocals ~rename_mapping:renameMapping in - - if not sameLocals then - (Changed, None, empty_rename_mapping) - else - match cfgs with - | None -> - let (identical, new_rename_mapping) = eq_block (old.sbody, old) (current.sbody, current) ~rename_mapping:renameMapping in - unchanged_to_change_status identical, None, new_rename_mapping - | 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 - let matches, diffNodes1, updated_rename_mapping = compareFun (module CfgOld) (module CfgNew) old current renameMapping in - if diffNodes1 = [] then (Unchanged, None, updated_rename_mapping) - else (Changed, Some {unchangedNodes = matches; primObsoleteNodes = diffNodes1}, updated_rename_mapping) - in - identical, diffOpt, renamed_method_dependencies, renamed_global_vars_dependencies, renamesOnSuccess diff --git a/src/incremental/detectRenamedFunctions.ml b/src/incremental/detectRenamedFunctions.ml deleted file mode 100644 index 39e0c13850..0000000000 --- a/src/incremental/detectRenamedFunctions.ml +++ /dev/null @@ -1,144 +0,0 @@ -open GoblintCil -include CompareGlobals -open CilMaps - -let performRenames (renamesOnSuccess: renamesOnSuccess) = - begin - let (compinfoRenames, enumRenames) = renamesOnSuccess in - List.iter (fun (compinfo2, compinfo1) -> compinfo2.cname <- compinfo1.cname; compinfo2.ckey <- compinfo1.ckey) compinfoRenames; - List.iter (fun (enum2, enum1) -> enum2.ename <- enum1.ename) enumRenames; - end - -let preservesSameNameMatches n_old oldMap n_new newMap = n_old = n_new || (not (GlobalMap.mem n_old newMap) && not (GlobalMap.mem n_new oldMap)) - -let addToFinalMatchesMapping oV nV final_matches = - VarinfoMap.add oV nV (fst final_matches), VarinfoMap.add nV oV (snd final_matches) - -(* TODO: possibly merge with eq_varinfo, provide only varinfo and mapping from varinfo to global_col *) -(* Compares two varinfos. finalizeOnlyExactMatch=true allows to check a rename assumption and discard the comparison result in case they do not match *) -let compare_varinfo ?(finalizeOnlyExactMatch=false) oV gc_old oldMap nV gc_new newMap change_info final_matches = - if not (preservesSameNameMatches oV.vname oldMap nV.vname newMap) then - (* do not allow for matches between differently named variables if one of the variables names exists in both, the new and old file *) - false, change_info, final_matches - else ( - (* TODO does the emptyness of the dependencies need to be checked? *) - let identical, (_, function_dependencies, global_var_dependencies, renamesOnSuccess) = eq_varinfo oV nV ~rename_mapping:empty_rename_mapping in - - if not finalizeOnlyExactMatch || identical then - performRenames renamesOnSuccess; (* updates enum names and compinfo names and keys that were collected during comparison of this matched function *) - if identical then ( - change_info.unchanged <- {old = gc_old; current = gc_new} :: change_info.unchanged; - true, change_info, addToFinalMatchesMapping oV nV final_matches - ) else if not finalizeOnlyExactMatch then ( - change_info.changed <- {old = gc_old; current = gc_new; unchangedHeader = true; diff = None} :: change_info.changed; - false, change_info, addToFinalMatchesMapping oV nV final_matches - ) else - false, change_info, final_matches - ) -let compare_varinfo_exact = compare_varinfo ~finalizeOnlyExactMatch:true - -let get_varinfo gc = match gc.decls, gc.def with - | _, Some (Var v) -> v - | _, Some (Fun f) -> f.svar - | Some v, _ -> v - | _ -> failwith "A global should have at least a declaration or a definition" -let addNewGlobals name gc_new (change_info, final_matches) = - if not (VarinfoMap.mem (get_varinfo gc_new) (snd final_matches)) then - change_info.added <- gc_new :: change_info.added; - (change_info, final_matches) - -let addOldGlobals name gc_old (change_info, final_matches) = - if not (VarinfoMap.mem (get_varinfo gc_old) (fst final_matches)) then - change_info.removed <- gc_old :: change_info.removed; - (change_info, final_matches) - -let iname cg = List.mem (name_of_global_col cg) ["main"; "foo"; "bar"] -let inamev v = List.mem v.vname ["main"; "foo"; "bar"] - -let detectRenamedFunctions (oldMap : global_col StringMap.t) (newMap : global_col StringMap.t) = - let extract_fundecs _ gc map = match gc.def with - | Some (Fun f) -> VarinfoMap.add f.svar f map - | _ -> map in - let var_fun_old = GlobalMap.fold extract_fundecs oldMap VarinfoMap.empty in - let var_fun_new = GlobalMap.fold extract_fundecs newMap VarinfoMap.empty in - let extract_globs _ gc map = - let v = get_varinfo gc in - VarinfoMap.add v gc map in - let var_glob_old = GlobalMap.fold extract_globs oldMap VarinfoMap.empty in - let var_glob_new = GlobalMap.fold extract_globs newMap VarinfoMap.empty in - let empty_rename_assms m = VarinfoMap.for_all (fun vo vn -> vo.vname = vn.vname) m in (* TODO or in final_matches? *) - - let compare_fundec_exact_match f1 f2 change_info final_matches = - (* check that names of match are each only contained in new or old file *) - if not (preservesSameNameMatches f1.svar.vname oldMap f2.svar.vname newMap) then ( - false, change_info, final_matches - ) else - let doMatch, diff, fun_deps, global_deps, renamesOnSuccess = CompareGlobals.eqF f1 f2 None VarinfoMap.empty VarinfoMap.empty in - match doMatch with - | Unchanged when empty_rename_assms (VarinfoMap.filter (fun vo vn -> not (vo.vname = f1.svar.vname && vn.vname = f2.svar.vname)) fun_deps) && empty_rename_assms global_deps -> - performRenames renamesOnSuccess; - change_info.unchanged <- {old = VarinfoMap.find f1.svar var_glob_old; current = VarinfoMap.find f2.svar var_glob_new} :: change_info.unchanged; - let final_matches = addToFinalMatchesMapping f1.svar f2.svar final_matches in - true, change_info, final_matches - | Unchanged -> false, change_info, final_matches - | Changed -> false, change_info, final_matches - | ChangedFunHeader _ -> false, change_info, final_matches - | ForceReanalyze _ -> false, change_info, final_matches - - in - - let matchGlobal ~matchVars ~matchFuns name gc_old (change_info, final_matches) = - try - let gc_new = StringMap.find name newMap in - - let compare_same_name_fundec_check_contained_renames f1 f2 = - let doMatch, diff, function_dependencies, global_var_dependencies, renamesOnSuccess = CompareGlobals.eqF f1 f2 None VarinfoMap.empty VarinfoMap.empty in - performRenames renamesOnSuccess; (* updates enum names and compinfo names and keys that were collected during comparison of this matched function *) - let funDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun f_old_var f_new_var (acc, ci, fm) -> - match VarinfoMap.find_opt f_old_var (fst final_matches) with - | None -> - let f_old = VarinfoMap.find f_old_var var_fun_old in - let f_new = VarinfoMap.find f_new_var var_fun_new in (* TODO: what happens if there exists no fundec for this varinfo? *) - if acc then - compare_fundec_exact_match f_old f_new ci fm - else false, ci, fm - | Some v -> v = f_new_var, ci, fm) function_dependencies (true, change_info, final_matches) in - let globalDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun old_var new_var (acc, ci, fm) -> - match VarinfoMap.find_opt old_var (fst final_matches) with - | None -> - if acc then - compare_varinfo_exact old_var gc_old oldMap new_var gc_new newMap ci fm - else false, ci, fm - | Some v -> v = new_var, ci, fm - ) global_var_dependencies (true, change_info, final_matches) in - let dependenciesMatch = funDependenciesMatch && globalDependenciesMatch in - let append_to_changed ~unchangedHeader ~diff = - change_info.changed <- {current = gc_new; old = gc_old; unchangedHeader; diff} :: change_info.changed - in - (* TODO: merge with no-rename-detection case in compareCIL.compareCilFiles *) - (match doMatch with - | Unchanged when dependenciesMatch -> - change_info.unchanged <- {old = gc_old; current = gc_new} :: change_info.unchanged - | Unchanged -> - (* no diff is stored, also when comparing functions based on CFG because currently there is no mechanism to detect which part was affected by the *) - append_to_changed ~unchangedHeader:true ~diff:None - | Changed -> append_to_changed ~unchangedHeader:true ~diff:diff - | _ -> (* this can only be ForceReanalyze or ChangedFunHeader *) - change_info.exclude_from_rel_destab <- VarinfoSet.add f1.svar change_info.exclude_from_rel_destab; - append_to_changed ~unchangedHeader:false ~diff:None); - addToFinalMatchesMapping f1.svar f2.svar final_matches in - - match gc_old.def, gc_new.def with - | Some (Var v1), Some (Var v2) when matchVars -> let _, ci, fm = compare_varinfo v1 gc_old oldMap v2 gc_new newMap change_info final_matches in ci, fm - | Some (Fun f1), Some (Fun f2) when matchFuns -> change_info, compare_same_name_fundec_check_contained_renames f1 f2 - | None, None -> (match gc_old.decls, gc_new.decls with - | Some v1, Some v2 when matchVars -> let _, ci, fm = compare_varinfo v1 gc_old oldMap v2 gc_new newMap change_info final_matches in ci, fm - | _ -> change_info, final_matches) - | _ -> change_info, final_matches - with Not_found -> change_info, final_matches in - - (empty_change_info (), (VarinfoMap.empty, VarinfoMap.empty)) (* change_info and final_matches (bi-directional) is propagated *) - |> GlobalMap.fold (matchGlobal ~matchVars:true ~matchFuns:false) oldMap - |> GlobalMap.fold (matchGlobal ~matchVars:false ~matchFuns:true) oldMap - |> GlobalMap.fold addNewGlobals newMap - |> GlobalMap.fold addOldGlobals oldMap diff --git a/src/incremental/updateCil.ml b/src/incremental/updateCil.ml index c4516578ae..474254872d 100644 --- a/src/incremental/updateCil.ml +++ b/src/incremental/updateCil.ml @@ -1,5 +1,5 @@ open GoblintCil -open CompareGlobals +open CompareCIL open MaxIdUtil open MyCFG diff --git a/src/util/server.ml b/src/util/server.ml index e260c21965..3dd2adf52d 100644 --- a/src/util/server.ml +++ b/src/util/server.ml @@ -158,7 +158,7 @@ let reparse (s: t) = (* Only called when the file has not been reparsed, so we can skip the expensive CFG comparison. *) let virtual_changes file = let eq (glob: CompareCIL.global_col) _ _ = match glob.def with - | Some (Fun fdec) when CompareGlobals.should_reanalyze fdec -> CompareCIL.ForceReanalyze fdec, None + | Some (Fun fdec) when CompareCIL.should_reanalyze fdec -> CompareCIL.ForceReanalyze fdec, None | _ -> Unchanged, None in CompareCIL.compareCilFiles ~eq file file From 2620cfc7fd2f0b9b2e9631a89a1ccbef42251364 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Mon, 13 Mar 2023 12:36:31 +0100 Subject: [PATCH 78/90] remove debugging functions --- src/incremental/compareAST.ml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index e6ca67f1df..193d98c753 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -39,22 +39,6 @@ let create_locals_rename_mapping (originalLocalNames: string list) (updatedLocal ) else StringMap.empty -let string_tuple_to_string (tuple: (string * string) list) = "[" ^ (tuple |> - List.map (fun x -> match x with (first, second) -> "(" ^ first ^ " -> " ^ second ^ ")") |> - String.concat ", ") ^ "]" - -let rename_mapping_to_string (rename_mapping: rename_mapping) = - let (local, methods, glob_vars, _) = rename_mapping in - let local_string = [%show: (string * string) list] (List.of_seq (StringMap.to_seq local)) in - let methods_string: string = List.of_seq (VarinfoMap.to_seq methods) |> - List.map (fun (oldf, newf) -> "(methodName: " ^ oldf.vname ^ " -> " ^ newf.vname ^ ")") |> - String.concat ", " in - - let global_var_string: string = string_tuple_to_string (List.of_seq (VarinfoMap.to_seq glob_vars) |> - List.map (fun (vold, vnew) -> vold.vname, vnew.vname)) in - - "(local=" ^ local_string ^ "; methods=[" ^ methods_string ^ "]; glob_vars=" ^ global_var_string ^ ")" - let is_rename_mapping_empty (rename_mapping: rename_mapping) = let local, methods, glob_vars, _= rename_mapping in StringMap.is_empty local && VarinfoMap.is_empty methods && VarinfoMap.is_empty glob_vars From 442b6d95d6a51acb50cd55653209de88b69905b5 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Mon, 13 Mar 2023 20:06:57 +0100 Subject: [PATCH 79/90] merge comparison with and without rename detection --- src/incremental/compareCIL.ml | 330 ++++++++++++++++------------------ src/util/server.ml | 13 +- 2 files changed, 163 insertions(+), 180 deletions(-) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 136c8434a3..1be02b44d6 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -18,6 +18,16 @@ let name_of_global_col gc = match gc.def with let compare_global_col gc1 gc2 = compare (name_of_global_col gc1) (name_of_global_col gc2) +let get_varinfo gc = match gc.decls, gc.def with + | _, Some (Var v) -> v + | _, Some (Fun f) -> f.svar + | Some v, _ -> v + | _ -> failwith "A global should have at least a declaration or a definition" + +let get_fundec gc = match gc.decls, gc.def with + | _, Some (Fun f) -> f + | _ -> failwith "Global does not have a function definition" + module GlobalColMap = Map.Make( struct type t = global_col @@ -77,6 +87,41 @@ let empty_rename_mapping: rename_mapping = (StringMap.empty, VarinfoMap.empty, V let should_reanalyze (fdec: Cil.fundec) = List.mem fdec.svar.vname (GobConfig.get_string_list "incremental.force-reanalyze.funs") +let performRenames (renamesOnSuccess: renamesOnSuccess) = + begin + let (compinfoRenames, enumRenames) = renamesOnSuccess in + List.iter (fun (compinfo2, compinfo1) -> compinfo2.cname <- compinfo1.cname; compinfo2.ckey <- compinfo1.ckey) compinfoRenames; + List.iter (fun (enum2, enum1) -> enum2.ename <- enum1.ename) enumRenames; + end + +let preservesSameNameMatches n_old oldMap n_new newMap = n_old = n_new || (not (GlobalMap.mem n_old newMap) && not (GlobalMap.mem n_new oldMap)) + +let addToFinalMatchesMapping oV nV final_matches = + VarinfoMap.add oV nV (fst final_matches), VarinfoMap.add nV oV (snd final_matches) + +let empty_rename_assms m = VarinfoMap.for_all (fun vo vn -> vo.vname = vn.vname) m + +(* Compares two varinfos of globals. finalizeOnlyExactMatch=true allows to check a rename assumption and discard the comparison result in case they do not match *) +let eq_glob_var ?(finalizeOnlyExactMatch=false) oV gc_old oldMap nV gc_new newMap change_info final_matches = + if not (preservesSameNameMatches oV.vname oldMap nV.vname newMap) then + (* do not allow for matches between differently named variables if one of the variables names exists in both, the new and old file *) + false, change_info, final_matches + else ( + let identical, (_, function_dependencies, global_var_dependencies, renamesOnSuccess) = eq_varinfo oV nV ~rename_mapping:empty_rename_mapping in + + if not finalizeOnlyExactMatch || identical then + performRenames renamesOnSuccess; (* updates enum names and compinfo names and keys that were collected during comparison of this matched function *) + if identical then ( + change_info.unchanged <- {old = gc_old; current = gc_new} :: change_info.unchanged; + true, change_info, addToFinalMatchesMapping oV nV final_matches + ) else if not finalizeOnlyExactMatch then ( + change_info.changed <- {old = gc_old; current = gc_new; unchangedHeader = true; diff = None} :: change_info.changed; + false, change_info, addToFinalMatchesMapping oV nV final_matches + ) else + false, change_info, final_matches + ) +let compare_varinfo_exact = eq_glob_var ~finalizeOnlyExactMatch:true + (* 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. *) @@ -121,46 +166,96 @@ let eqF (old: Cil.fundec) (current: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) opti in identical, diffOpt, renamed_method_dependencies, renamed_global_vars_dependencies, renamesOnSuccess -let performRenames (renamesOnSuccess: renamesOnSuccess) = - begin - let (compinfoRenames, enumRenames) = renamesOnSuccess in - List.iter (fun (compinfo2, compinfo1) -> compinfo2.cname <- compinfo1.cname; compinfo2.ckey <- compinfo1.ckey) compinfoRenames; - List.iter (fun (enum2, enum1) -> enum2.ename <- enum1.ename) enumRenames; - end - -let preservesSameNameMatches n_old oldMap n_new newMap = n_old = n_new || (not (GlobalMap.mem n_old newMap) && not (GlobalMap.mem n_new oldMap)) - -let addToFinalMatchesMapping oV nV final_matches = - VarinfoMap.add oV nV (fst final_matches), VarinfoMap.add nV oV (snd final_matches) - -(* TODO: possibly merge with eq_varinfo, provide only varinfo and mapping from varinfo to global_col *) -(* Compares two varinfos. finalizeOnlyExactMatch=true allows to check a rename assumption and discard the comparison result in case they do not match *) -let compare_varinfo ?(finalizeOnlyExactMatch=false) oV gc_old oldMap nV gc_new newMap change_info final_matches = - if not (preservesSameNameMatches oV.vname oldMap nV.vname newMap) then - (* do not allow for matches between differently named variables if one of the variables names exists in both, the new and old file *) +let eqF_only_consider_exact_match f1 f2 change_info final_matches oldMap newMap var_glob_old var_glob_new = + (* check that names of match are each only contained in new or old file *) + if not (preservesSameNameMatches f1.svar.vname oldMap f2.svar.vname newMap) then ( false, change_info, final_matches - else ( - (* TODO does the emptyness of the dependencies need to be checked? *) - let identical, (_, function_dependencies, global_var_dependencies, renamesOnSuccess) = eq_varinfo oV nV ~rename_mapping:empty_rename_mapping in - - if not finalizeOnlyExactMatch || identical then - performRenames renamesOnSuccess; (* updates enum names and compinfo names and keys that were collected during comparison of this matched function *) - if identical then ( - change_info.unchanged <- {old = gc_old; current = gc_new} :: change_info.unchanged; - true, change_info, addToFinalMatchesMapping oV nV final_matches - ) else if not finalizeOnlyExactMatch then ( - change_info.changed <- {old = gc_old; current = gc_new; unchangedHeader = true; diff = None} :: change_info.changed; - false, change_info, addToFinalMatchesMapping oV nV final_matches - ) else - false, change_info, final_matches - ) -let compare_varinfo_exact = compare_varinfo ~finalizeOnlyExactMatch:true + ) else + (* the exact comparison is always uses the AST comparison because only when unchanged this match is manifested *) + let doMatch, diff, fun_deps, global_deps, renamesOnSuccess = eqF f1 f2 None VarinfoMap.empty VarinfoMap.empty in + match doMatch with + | Unchanged when empty_rename_assms (VarinfoMap.filter (fun vo vn -> not (vo.vname = f1.svar.vname && vn.vname = f2.svar.vname)) fun_deps) && empty_rename_assms global_deps -> + performRenames renamesOnSuccess; + change_info.unchanged <- {old = VarinfoMap.find f1.svar var_glob_old; current = VarinfoMap.find f2.svar var_glob_new} :: change_info.unchanged; + let final_matches = addToFinalMatchesMapping f1.svar f2.svar final_matches in + true, change_info, final_matches + | Unchanged -> false, change_info, final_matches + | Changed -> false, change_info, final_matches + | ChangedFunHeader _ -> false, change_info, final_matches + | ForceReanalyze _ -> false, change_info, final_matches + +let eqF_check_contained_renames ~renameDetection f1 f2 oldMap newMap cfgs gc_old gc_new (change_info, final_matches) = + let doMatch, diff, function_dependencies, global_var_dependencies, renamesOnSuccess = eqF f1 f2 cfgs VarinfoMap.empty VarinfoMap.empty in + performRenames renamesOnSuccess; (* updates enum names and compinfo names and keys that were collected during comparison of this matched function *) + + (* for rename detection, check whether the rename assumptions collected during the function comparison actually match exactly, + otherwise check that the function comparison was successful without collecting any rename assumptions *) + let dependenciesMatch = + if renameDetection then + let funDependenciesMatch, change_info, final_matches = + let extract_globs _ gc map = + let v = get_varinfo gc in + VarinfoMap.add v gc map in + let var_glob_old = GlobalMap.fold extract_globs oldMap VarinfoMap.empty in + let var_glob_new = GlobalMap.fold extract_globs newMap VarinfoMap.empty in + VarinfoMap.fold (fun f_old_var f_new_var (acc, ci, fm) -> + match VarinfoMap.find_opt f_old_var (fst final_matches) with + | None -> + let f_old = get_fundec (VarinfoMap.find f_old_var var_glob_old) in + let f_new = get_fundec (VarinfoMap.find f_new_var var_glob_new) in (* TODO: what happens if there exists no fundec for this varinfo? *) + if acc then + eqF_only_consider_exact_match f_old f_new ci fm oldMap newMap var_glob_old var_glob_new + else false, ci, fm + | Some v -> v = f_new_var, ci, fm) function_dependencies (true, change_info, final_matches) in + let globalDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun old_var new_var (acc, ci, fm) -> + match VarinfoMap.find_opt old_var (fst final_matches) with + | None -> + if acc then + compare_varinfo_exact old_var gc_old oldMap new_var gc_new newMap ci fm + else false, ci, fm + | Some v -> v = new_var, ci, fm + ) global_var_dependencies (true, change_info, final_matches) in + funDependenciesMatch && globalDependenciesMatch + else + empty_rename_assms function_dependencies && empty_rename_assms global_var_dependencies in -let get_varinfo gc = match gc.decls, gc.def with - | _, Some (Var v) -> v - | _, Some (Fun f) -> f.svar - | Some v, _ -> v - | _ -> failwith "A global should have at least a declaration or a definition" + let append_to_changed ~unchangedHeader ~diff = + change_info.changed <- {current = gc_new; old = gc_old; unchangedHeader; diff} :: change_info.changed + in + (match doMatch with + | Unchanged when dependenciesMatch -> + change_info.unchanged <- {old = gc_old; current = gc_new} :: change_info.unchanged + | Unchanged -> + (* no diff is stored, also when comparing functions based on CFG because currently there is no mechanism to detect which part was affected by the *) + append_to_changed ~unchangedHeader:true ~diff:None + | Changed -> append_to_changed ~unchangedHeader:true ~diff:diff + | _ -> (* this can only be ForceReanalyze or ChangedFunHeader *) + change_info.exclude_from_rel_destab <- VarinfoSet.add f1.svar change_info.exclude_from_rel_destab; + append_to_changed ~unchangedHeader:false ~diff:None); + change_info, addToFinalMatchesMapping f1.svar f2.svar final_matches + +let eq_glob ?(matchVars=true) ?(matchFuns=true) ?(renameDetection=false) oldMap newMap cfgs gc_old gc_new (change_info, final_matches) = + match gc_old.def, gc_new.def with + | Some (Var v1), Some (Var v2) when matchVars -> let _, ci, fm = eq_glob_var v1 gc_old oldMap v2 gc_new newMap change_info final_matches in ci, fm + | Some (Fun f1), Some (Fun f2) when matchFuns -> + eqF_check_contained_renames ~renameDetection f1 f2 oldMap newMap cfgs gc_old gc_new (change_info, final_matches) + | None, None -> (match gc_old.decls, gc_new.decls with + | Some v1, Some v2 when matchVars -> let _, ci, fm = eq_glob_var v1 gc_old oldMap v2 gc_new newMap change_info final_matches in ci, fm + | _ -> change_info, final_matches (* a global collection should never be empty *)) + (* Without rename detection a global definition or declaration that does not have respective counterpart in the other version is considered to be changed (not added or removed) + because a global collection only exists in the map if there is at least one declaration or definition for this global. + For the rename detection they can only be added to changed when the according flag is set, because there would be duplicates when iterating over the globals several times. *) + | Some (Var _), None + | None, Some (Var _) -> if matchVars then ( + change_info.changed <- {old = gc_old; current = gc_new; diff = None; unchangedHeader = true} :: change_info.changed; + change_info, addToFinalMatchesMapping (get_varinfo gc_old) (get_varinfo gc_new) final_matches) + else + change_info, final_matches + | _, _ -> if matchVars && matchFuns then ( + change_info.changed <- {old = gc_old; current = gc_new; diff = None; unchangedHeader = true} :: change_info.changed; + change_info, addToFinalMatchesMapping (get_varinfo gc_old) (get_varinfo gc_new) final_matches) + else + change_info, final_matches let addNewGlobals name gc_new (change_info, final_matches) = if not (VarinfoMap.mem (get_varinfo gc_new) (snd final_matches)) then @@ -172,114 +267,6 @@ let addOldGlobals name gc_old (change_info, final_matches) = change_info.removed <- gc_old :: change_info.removed; (change_info, final_matches) -let detectRenamedFunctions (oldMap : global_col StringMap.t) (newMap : global_col StringMap.t) = - let extract_fundecs _ gc map = match gc.def with - | Some (Fun f) -> VarinfoMap.add f.svar f map - | _ -> map in - let var_fun_old = GlobalMap.fold extract_fundecs oldMap VarinfoMap.empty in - let var_fun_new = GlobalMap.fold extract_fundecs newMap VarinfoMap.empty in - let extract_globs _ gc map = - let v = get_varinfo gc in - VarinfoMap.add v gc map in - let var_glob_old = GlobalMap.fold extract_globs oldMap VarinfoMap.empty in - let var_glob_new = GlobalMap.fold extract_globs newMap VarinfoMap.empty in - let empty_rename_assms m = VarinfoMap.for_all (fun vo vn -> vo.vname = vn.vname) m in (* TODO or in final_matches? *) - - let compare_fundec_exact_match f1 f2 change_info final_matches = - (* check that names of match are each only contained in new or old file *) - if not (preservesSameNameMatches f1.svar.vname oldMap f2.svar.vname newMap) then ( - false, change_info, final_matches - ) else - let doMatch, diff, fun_deps, global_deps, renamesOnSuccess = eqF f1 f2 None VarinfoMap.empty VarinfoMap.empty in - match doMatch with - | Unchanged when empty_rename_assms (VarinfoMap.filter (fun vo vn -> not (vo.vname = f1.svar.vname && vn.vname = f2.svar.vname)) fun_deps) && empty_rename_assms global_deps -> - performRenames renamesOnSuccess; - change_info.unchanged <- {old = VarinfoMap.find f1.svar var_glob_old; current = VarinfoMap.find f2.svar var_glob_new} :: change_info.unchanged; - let final_matches = addToFinalMatchesMapping f1.svar f2.svar final_matches in - true, change_info, final_matches - | Unchanged -> false, change_info, final_matches - | Changed -> false, change_info, final_matches - | ChangedFunHeader _ -> false, change_info, final_matches - | ForceReanalyze _ -> false, change_info, final_matches - - in - - let matchGlobal ~matchVars ~matchFuns name gc_old (change_info, final_matches) = - try - let gc_new = StringMap.find name newMap in - - let compare_same_name_fundec_check_contained_renames f1 f2 = - let doMatch, diff, function_dependencies, global_var_dependencies, renamesOnSuccess = eqF f1 f2 None VarinfoMap.empty VarinfoMap.empty in - performRenames renamesOnSuccess; (* updates enum names and compinfo names and keys that were collected during comparison of this matched function *) - let funDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun f_old_var f_new_var (acc, ci, fm) -> - match VarinfoMap.find_opt f_old_var (fst final_matches) with - | None -> - let f_old = VarinfoMap.find f_old_var var_fun_old in - let f_new = VarinfoMap.find f_new_var var_fun_new in (* TODO: what happens if there exists no fundec for this varinfo? *) - if acc then - compare_fundec_exact_match f_old f_new ci fm - else false, ci, fm - | Some v -> v = f_new_var, ci, fm) function_dependencies (true, change_info, final_matches) in - let globalDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun old_var new_var (acc, ci, fm) -> - match VarinfoMap.find_opt old_var (fst final_matches) with - | None -> - if acc then - compare_varinfo_exact old_var gc_old oldMap new_var gc_new newMap ci fm - else false, ci, fm - | Some v -> v = new_var, ci, fm - ) global_var_dependencies (true, change_info, final_matches) in - let dependenciesMatch = funDependenciesMatch && globalDependenciesMatch in - let append_to_changed ~unchangedHeader ~diff = - change_info.changed <- {current = gc_new; old = gc_old; unchangedHeader; diff} :: change_info.changed - in - (* TODO: merge with no-rename-detection case in compareCIL.compareCilFiles *) - (match doMatch with - | Unchanged when dependenciesMatch -> - change_info.unchanged <- {old = gc_old; current = gc_new} :: change_info.unchanged - | Unchanged -> - (* no diff is stored, also when comparing functions based on CFG because currently there is no mechanism to detect which part was affected by the *) - append_to_changed ~unchangedHeader:true ~diff:None - | Changed -> append_to_changed ~unchangedHeader:true ~diff:diff - | _ -> (* this can only be ForceReanalyze or ChangedFunHeader *) - change_info.exclude_from_rel_destab <- VarinfoSet.add f1.svar change_info.exclude_from_rel_destab; - append_to_changed ~unchangedHeader:false ~diff:None); - addToFinalMatchesMapping f1.svar f2.svar final_matches in - - match gc_old.def, gc_new.def with - | Some (Var v1), Some (Var v2) when matchVars -> let _, ci, fm = compare_varinfo v1 gc_old oldMap v2 gc_new newMap change_info final_matches in ci, fm - | Some (Fun f1), Some (Fun f2) when matchFuns -> change_info, compare_same_name_fundec_check_contained_renames f1 f2 - | None, None -> (match gc_old.decls, gc_new.decls with - | Some v1, Some v2 when matchVars -> let _, ci, fm = compare_varinfo v1 gc_old oldMap v2 gc_new newMap change_info final_matches in ci, fm - | _ -> change_info, final_matches) - | _ -> change_info, final_matches - with Not_found -> change_info, final_matches in - - (empty_change_info (), (VarinfoMap.empty, VarinfoMap.empty)) (* change_info and final_matches (bi-directional) is propagated *) - |> GlobalMap.fold (matchGlobal ~matchVars:true ~matchFuns:false) oldMap - |> GlobalMap.fold (matchGlobal ~matchVars:false ~matchFuns:true) oldMap - |> GlobalMap.fold addNewGlobals newMap - |> GlobalMap.fold addOldGlobals oldMap - -let eq_glob (old: global_col) (current: global_col) (cfgs : (cfg * (cfg * cfg)) option) = - let identical, diff, renamesOnSuccess = match old.def, current.def with - | Some (Var x), Some (Var y) -> - let identical, (_,_,_,renamesOnSuccess) = eq_varinfo x y ~rename_mapping:empty_rename_mapping in - unchanged_to_change_status identical, None, renamesOnSuccess (* ignore the init_info - a changed init of a global will lead to a different start state *) - | Some (Fun f), Some (Fun g) -> - let identical, diffOpt, funDep, globVarDep, renamesOnSuccess = eqF f g cfgs VarinfoMap.empty VarinfoMap.empty in - (*Perform renames no matter what.*) - (match identical with - | Unchanged when not (VarinfoMap.is_empty funDep && VarinfoMap.for_all (fun ov nv -> ov.vname = nv.vname) globVarDep) -> Changed, diffOpt, renamesOnSuccess - | s -> s, diffOpt, renamesOnSuccess) - | None, None -> (match old.decls, current.decls with - | Some x, Some y -> - let identical, (_,_,_,renamesOnSuccess) = eq_varinfo x y ~rename_mapping:empty_rename_mapping in - unchanged_to_change_status identical, None, renamesOnSuccess - | _, _ -> failwith "should never collect any empty entries in GlobalMap") - | _, _ -> Changed, None, ([], []) (* it is considered to be changed (not added or removed) because a global collection only exists in the map if there is at least one declaration or definition for this global *) in - performRenames renamesOnSuccess; (* updates enum names and compinfo names and keys that were collected during successful comparisons *) - identical, diff - 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) @@ -312,37 +299,30 @@ let compareCilFiles ?(eq=eq_glob) (oldAST: file) (newAST: file) = let changes = empty_change_info () in global_typ_acc := []; - if GobConfig.get_bool "incremental.detect-renames" then ( - let (change_info, final_mapping) = detectRenamedFunctions oldMap newMap in - changes.added <- change_info.added; - changes.removed <- change_info.removed; - changes.changed <- change_info.changed; - changes.unchanged <- change_info.unchanged; - changes.exclude_from_rel_destab <- change_info.exclude_from_rel_destab + let findChanges ?(matchVars=true) ?(matchFuns=true) ?(renameDetection=false) oldMap newMap cfgs name gc_new (change_info, final_matches) = + try + let gc_old = GlobalMap.find name oldMap in + eq ~matchVars ~matchFuns ~renameDetection oldMap newMap cfgs gc_old gc_new (change_info, final_matches) + with Not_found -> + if not renameDetection then + change_info.added <- gc_new::change_info.added; (* Global could not be found in old map -> added *) + change_info, final_matches in + if GobConfig.get_bool "incremental.detect-renames" then ( + let _ = + (changes, (VarinfoMap.empty, VarinfoMap.empty)) (* change_info and final_matches (bi-directional) is propagated *) + |> GlobalMap.fold (findChanges ~matchVars:true ~matchFuns:false ~renameDetection:true oldMap newMap cfgs) newMap + |> GlobalMap.fold (findChanges ~matchVars:false ~matchFuns:true ~renameDetection:true oldMap newMap cfgs) newMap + |> GlobalMap.fold addNewGlobals newMap + |> GlobalMap.fold addOldGlobals oldMap in + + () ) else ( - let findChanges map name current_global = - try - let old_global = GlobalMap.find name map in - let change_status, diff = eq old_global current_global cfgs in - let append_to_changed ~unchangedHeader = - changes.changed <- {current = current_global; old = old_global; unchangedHeader; diff} :: changes.changed - in - match change_status with - | Changed -> - append_to_changed ~unchangedHeader:true - | Unchanged -> changes.unchanged <- {current = current_global; old = old_global} :: changes.unchanged - | ChangedFunHeader f - | ForceReanalyze f -> - changes.exclude_from_rel_destab <- VarinfoSet.add f.svar changes.exclude_from_rel_destab; - append_to_changed ~unchangedHeader:false - with Not_found -> changes.added <- current_global::changes.added (* Global could not be found in old map -> added *) - 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. *) - GlobalMap.iter (fun name glob_col -> findChanges oldMap name glob_col) newMap; - GlobalMap.iter (fun name glob -> if not (GlobalMap.mem name newMap) then changes.removed <- (glob::changes.removed)) oldMap; + let _ = + (changes, (VarinfoMap.empty, VarinfoMap.empty)) (* change_info and final_matches (bi-directional) is propagated *) + |> GlobalMap.fold (findChanges oldMap newMap cfgs) newMap + |> GlobalMap.fold addOldGlobals oldMap in + () ); changes diff --git a/src/util/server.ml b/src/util/server.ml index 3dd2adf52d..7905acb1f0 100644 --- a/src/util/server.ml +++ b/src/util/server.ml @@ -1,6 +1,7 @@ open Batteries open Jsonrpc open GoblintCil +include CompareCIL type t = { mutable file: Cil.file option; @@ -157,16 +158,18 @@ let reparse (s: t) = (* Only called when the file has not been reparsed, so we can skip the expensive CFG comparison. *) let virtual_changes file = - let eq (glob: CompareCIL.global_col) _ _ = match glob.def with - | Some (Fun fdec) when CompareCIL.should_reanalyze fdec -> CompareCIL.ForceReanalyze fdec, None - | _ -> Unchanged, None + let eq ?(matchVars=true) ?(matchFuns=true) ?(renameDetection=false) _ _ _ gc_old (gc_new: global_col) (change_info, final_matches) = (match gc_new.def with + | Some (Fun fdec) when should_reanalyze fdec -> + change_info.exclude_from_rel_destab <- VarinfoSet.add fdec.svar change_info.exclude_from_rel_destab + | _ -> change_info.unchanged <- {old = gc_old; current= gc_new} :: change_info.unchanged); + change_info, final_matches in - CompareCIL.compareCilFiles ~eq file file + compareCilFiles ~eq file file let increment_data (s: t) file reparsed = match Serialize.Cache.get_opt_data SolverData with | Some solver_data when reparsed -> let s_file = Option.get s.file in - let changes = CompareCIL.compareCilFiles s_file file in + let changes = compareCilFiles s_file file in s.max_ids <- UpdateCil.update_ids s_file s.max_ids file changes; (* TODO: get globals for restarting from config *) Some { server = true; Analyses.changes; solver_data; restarting = [] }, false From 6ba71fc9a44d593a76b259ac2b6b0a6a209cc899 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Mon, 13 Mar 2023 20:09:53 +0100 Subject: [PATCH 80/90] make semgrep happy --- src/incremental/compareCIL.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 1be02b44d6..f210647aa0 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -133,7 +133,7 @@ let eqF (old: Cil.fundec) (current: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) opti let add_locals_to_rename_mapping la lb map = try - List.fold_left (fun map (a, b) -> StringMap.add a.vname b.vname map) map (List.combine la lb) + List.fold_left2 (fun map a b -> StringMap.add a.vname b.vname map) map la lb with Invalid_argument _ -> map in let parameterMapping = add_locals_to_rename_mapping old.sformals current.sformals StringMap.empty in From aeaf301216a692ec2d3180efde3dae888cb19161 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Thu, 23 Mar 2023 10:54:25 +0100 Subject: [PATCH 81/90] add incremental cram tests to CI --- .github/workflows/locked.yml | 3 +++ .github/workflows/unlocked.yml | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/.github/workflows/locked.yml b/.github/workflows/locked.yml index 685fdc0afd..fe2cbe4955 100644 --- a/.github/workflows/locked.yml +++ b/.github/workflows/locked.yml @@ -64,6 +64,9 @@ jobs: - name: Test regression cram run: opam exec -- dune runtest tests/regression + - name: Test incremental cram + run: opam exec -- dune runtest tests/incremental + - name: Test unit run: opam exec -- dune runtest unittest diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 5455bb0cb7..2bec6b72fb 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -94,6 +94,9 @@ jobs: - name: Test regression cram run: opam exec -- dune runtest tests/regression + - name: Test incremental cram + run: opam exec -- dune runtest tests/incremental + - name: Test unit run: opam exec -- dune runtest unittest @@ -179,6 +182,9 @@ jobs: - name: Test regression cram run: opam exec -- dune runtest tests/regression + - name: Test incremental cram + run: opam exec -- dune runtest tests/incremental + - name: Test unit run: opam exec -- dune runtest unittest From 033555adc3c58c7a46a6a1869a4730f26447c845 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Thu, 23 Mar 2023 14:01:20 +0100 Subject: [PATCH 82/90] refine implementation based on review comments --- src/framework/constraints.ml | 2 +- src/incremental/compareAST.ml | 44 ++++++++++--------- src/incremental/compareCFG.ml | 6 +-- src/util/server.ml | 11 +++-- .../04-var-rename/01-rename_and_shuffle.t | 5 --- .../04-var-rename/02-rename_with_usage.t | 5 --- .../04-var-rename/04-renamed_param.t | 5 --- .../05-renamed_param_usage_changed.t | 5 --- .../05-method-rename/00-simple_rename.t | 5 --- .../05-method-rename/01-dependent_rename.t | 5 --- .../02-cyclic_rename_dependency.t | 5 --- .../05-method-rename/03-cyclic_with_swap.t | 5 --- .../05-method-rename/04-deep_change.t | 5 --- .../05-method-rename/05-common_rename.t | 5 --- .../05-method-rename/06-recursive_rename.t | 5 --- .../06-glob-var-rename/00-simple_rename.t | 5 --- .../01-duplicate_local_global.t | 5 --- .../06-glob-var-rename/02-add_new_gvar.t | 5 --- 18 files changed, 33 insertions(+), 100 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index aeb13d0b5b..3c734e9694 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -827,7 +827,7 @@ struct | Some {changes; _} -> changes | None -> empty_change_info () in - List.(Printf.printf "change_info = { unchanged = %d; changed = %d (with unchangedHeader = %d); added = %d; removed = %d }\n" (length c.unchanged) (length c.changed) (length (List.filter (fun c -> c.unchangedHeader) c.changed)) (length c.added) (length c.removed)); + List.(Printf.printf "change_info = { unchanged = %d; changed = %d (with unchangedHeader = %d); added = %d; removed = %d }\n" (length c.unchanged) (length c.changed) (BatList.count_matching (fun c -> c.unchangedHeader) c.changed) (length c.added) (length c.removed)); let changed_funs = List.filter_map (function | {old = {def = Some (Fun f); _}; diff = None; _} -> diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 193d98c753..006c5eadb1 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -3,18 +3,25 @@ open CilMaps module StringMap = Map.Make(String) +(* Mapping with rename assumptions about functions collected during the comparison. An assumption means that the + comparison result so far is only correct, if the varinfos of a key-value pair in the mapping represent the same but + renamed function. It is a mapping from a varinfo in the old version to one in the new version. *) type method_rename_assumptions = varinfo VarinfoMap.t + +(* Similiarly to method_rename_assumptions, just that rename assumptions about global variables are collected. *) type glob_var_rename_assumptions = varinfo VarinfoMap.t -(*On a successful match, these compinfo and enuminfo names have to be set to the snd element of the tuple. *) +(* On a successful match, these compinfo and enuminfo names have to be set to the snd element of the tuple. *) type renamesOnSuccess = (compinfo * compinfo) list * (enuminfo * enuminfo) list -(*rename_mapping is carried through the stack when comparing the AST. Holds a list of rename assumptions.*) +(* The rename_mapping is carried through the stack when comparing the AST. Holds a list of rename assumptions. The first + component is a map of rename assumptions about locals, i.e., parameters and local variables and is only used when + comparing functions. *) type rename_mapping = (string StringMap.t) * method_rename_assumptions * glob_var_rename_assumptions * renamesOnSuccess -(*Compares two names, being aware of the rename_mapping. Returns true iff: - 1. there is a rename for name1 -> name2 = rename(name1) - 2. there is no rename for name1 -> name1 = name2*) +(* Compares two names, being aware of the rename_mapping. Returns true iff: + 1. there is a rename for name1 -> name2 = rename(name1) + 2. there is no rename for name1 -> name1 = name2 *) let rename_mapping_aware_name_comparison (name1: string) (name2: string) (rename_mapping: rename_mapping) = if GobConfig.get_bool "incremental.detect-renames" then ( let (local_c, method_c, _, _) = rename_mapping in @@ -24,13 +31,13 @@ let rename_mapping_aware_name_comparison (name1: string) (name2: string) (rename | Some now -> now = name2 | None -> - name1 = name2 (*Var names differ, but there is no assumption, so this can't be good*) + name1 = name2 (* Var names differ, but there is no assumption, so this can't be good *) ) else name1 = name2 -(*Creates the mapping of local renames. If the locals do not match in size, an empty mapping is returned.*) +(* Creates the mapping of local renames. If the locals do not match in size, an empty mapping is returned. *) let create_locals_rename_mapping (originalLocalNames: string list) (updatedLocalNames: string list): string StringMap.t = - if (List.length originalLocalNames) = (List.length updatedLocalNames) then + if List.compare_lengths originalLocalNames updatedLocalNames = 0 then List.combine originalLocalNames updatedLocalNames |> List.filter (fun (original, now) -> not (original = now)) |> List.map (fun (original, now) -> (original, now)) |> @@ -43,18 +50,18 @@ let is_rename_mapping_empty (rename_mapping: rename_mapping) = let local, methods, glob_vars, _= rename_mapping in StringMap.is_empty local && VarinfoMap.is_empty methods && VarinfoMap.is_empty glob_vars -(*rename mapping forward propagation, takes the result from a call and propagates the rename mapping to the next call. - the second call is only executed if the previous call returned true*) +(* rename mapping forward propagation, takes the result from a call and propagates the rename mapping to the next call. + the second call is only executed if the previous call returned true *) let (&&>>) (prev_result: bool * rename_mapping) f : bool * rename_mapping = let (prev_equal, updated_rename_mapping) = prev_result in if prev_equal then f ~rename_mapping:updated_rename_mapping else false, updated_rename_mapping -(*Same as && but propagates the rename mapping*) +(* Same as && but propagates the rename mapping *) let (&&>) (prev_result: bool * rename_mapping) (b: bool) : bool * rename_mapping = let (prev_equal, rename_mapping) = prev_result in (prev_equal && b, rename_mapping) -(*Same as Goblist.eq but propagates the rename_mapping*) +(* Same as Goblist.eq but propagates the rename_mapping *) let forward_list_equal ?(propF = (&&>>)) f l1 l2 ~(rename_mapping: rename_mapping) : bool * rename_mapping = if ((List.compare_lengths l1 l2) = 0) then List.fold_left2 (fun (b, r) x y -> propF (b, r) (f x y)) (true, rename_mapping) l1 l2 @@ -107,8 +114,8 @@ and pretty_length () l = Pretty.num (List.length l) and eq_typ_acc ?(fun_parameter_name_comparison_enabled: bool = true) (a: typ) (b: typ) ~(rename_mapping: rename_mapping) ~(acc: (typ * typ) list) : bool * rename_mapping = (* Registers a compinfo rename or a enum rename*) let register_rename_on_success = fun rename_mapping compinfo_option enum_option -> - let maybeAddTuple = fun list option -> - Option.value ~default:list (Option.bind option (fun elem -> Some(elem :: list))) + let maybeAddTuple list option = + BatOption.map_default (fun v -> v :: list) list option in let (a, b, c, renames_on_success) = rename_mapping in @@ -127,7 +134,7 @@ and eq_typ_acc ?(fun_parameter_name_comparison_enabled: bool = true) (a: typ) (b | TArray (typ1, None, attr1), TArray (typ2, None, attr2) -> eq_typ_acc typ1 typ2 ~rename_mapping ~acc &&>> forward_list_equal (eq_attribute ~acc) attr1 attr2 | TFun (typ1, (Some list1), varArg1, attr1), TFun (typ2, (Some list2), varArg2, attr2) -> eq_typ_acc typ1 typ2 ~rename_mapping ~acc &&>> - forward_list_equal (eq_args ~fun_parameter_name_comparison_enabled:fun_parameter_name_comparison_enabled ~acc) list1 list2 &&> + forward_list_equal (eq_args ~fun_parameter_name_comparison_enabled ~acc) list1 list2 &&> (varArg1 = varArg2) &&>> forward_list_equal (eq_attribute ~acc) attr1 attr2 | TFun (typ1, None, varArg1, attr1), TFun (typ2, None, varArg2, attr2) -> @@ -237,10 +244,7 @@ and eq_varinfo (a: varinfo) (b: varinfo) ~(acc: (typ * typ) list) ~(rename_mappi true, VarinfoMap.add a b method_rename_mappings, glob_vars else true, method_rename_mappings, glob_vars ) - | TInt (_, _), TInt (_, _) -> compare_local_and_global_var - | TFloat (_, _), TFloat (_, _) -> compare_local_and_global_var - | TPtr (_, _), TPtr(_, _) -> compare_local_and_global_var - | _, _ -> rename_mapping_aware_name_comparison a.vname b.vname rename_mapping, method_rename_mappings, glob_vars + | _, _ -> compare_local_and_global_var in (*If the following is a method call, we need to check if we have a mapping for that method call. *) @@ -250,7 +254,7 @@ and eq_varinfo (a: varinfo) (b: varinfo) ~(acc: (typ * typ) list) ~(rename_mappi in (*Ignore rename mapping for type check, as it doesn't change anyway. We only need the renames_on_success*) - let (typeCheck, (_, _, _, updated_renames_on_success)) = eq_typ_acc ~fun_parameter_name_comparison_enabled:fun_parameter_name_comparison_enabled a.vtype b.vtype ~rename_mapping:(StringMap.empty, VarinfoMap.empty, VarinfoMap.empty, renames_on_success) ~acc in + let (typeCheck, (_, _, _, updated_renames_on_success)) = eq_typ_acc ~fun_parameter_name_comparison_enabled a.vtype b.vtype ~rename_mapping:(StringMap.empty, VarinfoMap.empty, VarinfoMap.empty, renames_on_success) ~acc in (isNamingOk && typeCheck, (locals_renames, updated_method_rename_mappings, updatedGlobVarMapping, updated_renames_on_success)) &&>> forward_list_equal (eq_attribute ~acc ) a.vattr b.vattr &&> diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index 0c23edea9e..dff0867b40 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -63,7 +63,7 @@ let compareCfgs (module CfgOld : CfgForward) (module CfgNew : CfgForward) fun1 f let same = {node1to2=NH.create 113; node2to1=NH.create 113} in let waitingList : (node * node) t = Queue.create () in - let rec compareNext () rename_mapping : rename_mapping = + let rec compareNext rename_mapping : rename_mapping = if Queue.is_empty waitingList then rename_mapping else let fromNode1, fromNode2 = Queue.take waitingList in @@ -104,14 +104,14 @@ let compareCfgs (module CfgOld : CfgForward) (module CfgNew : CfgForward) fun1 f if posAmbigEdge edgeList1 then (NH.replace diff toNode1 (); rename_mapping) else findMatch (edgeList1, toNode1) rename_mapping in let updatedRenameMapping = List.fold_left (fun rm e -> iterOuts e rm) rename_mapping outList1 in - compareNext () updatedRenameMapping + compareNext updatedRenameMapping in let entryNode1, entryNode2 = (FunctionEntry fun1, FunctionEntry fun2) in NH.replace same.node1to2 entryNode1 entryNode2; NH.replace same.node2to1 entryNode2 entryNode1; Queue.push (entryNode1,entryNode2) waitingList; - let updatedRenameMapping = compareNext () rename_mapping in + let updatedRenameMapping = compareNext rename_mapping in same, diff, updatedRenameMapping (* This is the second phase of the CFG comparison of functions. It removes the nodes from the matching node set 'same' diff --git a/src/util/server.ml b/src/util/server.ml index 7905acb1f0..6c6901cf0f 100644 --- a/src/util/server.ml +++ b/src/util/server.ml @@ -1,7 +1,6 @@ open Batteries open Jsonrpc open GoblintCil -include CompareCIL type t = { mutable file: Cil.file option; @@ -158,18 +157,18 @@ let reparse (s: t) = (* Only called when the file has not been reparsed, so we can skip the expensive CFG comparison. *) let virtual_changes file = - let eq ?(matchVars=true) ?(matchFuns=true) ?(renameDetection=false) _ _ _ gc_old (gc_new: global_col) (change_info, final_matches) = (match gc_new.def with - | Some (Fun fdec) when should_reanalyze fdec -> - change_info.exclude_from_rel_destab <- VarinfoSet.add fdec.svar change_info.exclude_from_rel_destab + let eq ?(matchVars=true) ?(matchFuns=true) ?(renameDetection=false) _ _ _ gc_old (gc_new: CompareCIL.global_col) ((change_info : CompareCIL.change_info), final_matches) = (match gc_new.def with + | Some (Fun fdec) when CompareCIL.should_reanalyze fdec -> + change_info.exclude_from_rel_destab <- CompareCIL.VarinfoSet.add fdec.svar change_info.exclude_from_rel_destab | _ -> change_info.unchanged <- {old = gc_old; current= gc_new} :: change_info.unchanged); change_info, final_matches in - compareCilFiles ~eq file file + CompareCIL.compareCilFiles ~eq file file let increment_data (s: t) file reparsed = match Serialize.Cache.get_opt_data SolverData with | Some solver_data when reparsed -> let s_file = Option.get s.file in - let changes = compareCilFiles s_file file in + let changes = CompareCIL.compareCilFiles s_file file in s.max_ids <- UpdateCil.update_ids s_file s.max_ids file changes; (* TODO: get globals for restarting from config *) Some { server = true; Analyses.changes; solver_data; restarting = [] }, false diff --git a/tests/incremental/04-var-rename/01-rename_and_shuffle.t b/tests/incremental/04-var-rename/01-rename_and_shuffle.t index 5cfb03eb54..8f3b57f797 100644 --- a/tests/incremental/04-var-rename/01-rename_and_shuffle.t +++ b/tests/incremental/04-var-rename/01-rename_and_shuffle.t @@ -12,8 +12,3 @@ Run Goblint incrementally on new program version and check the change detection $ goblint --conf 01-rename_and_shuffle.json --enable incremental.load 01-rename_and_shuffle.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' changed = 1 (with unchangedHeader = 1); added = 0; removed = 0 - -Revert patch - - $ patch -b -R <01-rename_and_shuffle.patch - patching file 01-rename_and_shuffle.c diff --git a/tests/incremental/04-var-rename/02-rename_with_usage.t b/tests/incremental/04-var-rename/02-rename_with_usage.t index 2abea2988f..1e2818ed4d 100644 --- a/tests/incremental/04-var-rename/02-rename_with_usage.t +++ b/tests/incremental/04-var-rename/02-rename_with_usage.t @@ -12,8 +12,3 @@ Run Goblint incrementally on new program version and check the change detection $ goblint --conf 02-rename_with_usage.json --enable incremental.load 02-rename_with_usage.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 - -Revert patch - - $ patch -b -R <02-rename_with_usage.patch - patching file 02-rename_with_usage.c diff --git a/tests/incremental/04-var-rename/04-renamed_param.t b/tests/incremental/04-var-rename/04-renamed_param.t index ed13d38fd7..9da6d5e888 100644 --- a/tests/incremental/04-var-rename/04-renamed_param.t +++ b/tests/incremental/04-var-rename/04-renamed_param.t @@ -12,8 +12,3 @@ Run Goblint incrementally on new program version and check the change detection $ goblint --conf 04-renamed_param.json --enable incremental.load 04-renamed_param.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 - -Revert patch - - $ patch -b -R <04-renamed_param.patch - patching file 04-renamed_param.c diff --git a/tests/incremental/04-var-rename/05-renamed_param_usage_changed.t b/tests/incremental/04-var-rename/05-renamed_param_usage_changed.t index 7f23cd649f..a465b2b6f2 100644 --- a/tests/incremental/04-var-rename/05-renamed_param_usage_changed.t +++ b/tests/incremental/04-var-rename/05-renamed_param_usage_changed.t @@ -12,8 +12,3 @@ Run Goblint incrementally on new program version and check the change detection $ goblint --conf 05-renamed_param_usage_changed.json --enable incremental.load 05-renamed_param_usage_changed.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' changed = 1 (with unchangedHeader = 1); added = 0; removed = 0 - -Revert patch - - $ patch -b -R <05-renamed_param_usage_changed.patch - patching file 05-renamed_param_usage_changed.c diff --git a/tests/incremental/05-method-rename/00-simple_rename.t b/tests/incremental/05-method-rename/00-simple_rename.t index 59a1cfa469..1855b903eb 100644 --- a/tests/incremental/05-method-rename/00-simple_rename.t +++ b/tests/incremental/05-method-rename/00-simple_rename.t @@ -12,8 +12,3 @@ Run Goblint incrementally on new program version and check the change detection $ goblint --conf 00-simple_rename.json --enable incremental.load 00-simple_rename.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 - -Revert patch - - $ patch -b -R <00-simple_rename.patch - patching file 00-simple_rename.c diff --git a/tests/incremental/05-method-rename/01-dependent_rename.t b/tests/incremental/05-method-rename/01-dependent_rename.t index 75c5797c2a..bb0628447b 100644 --- a/tests/incremental/05-method-rename/01-dependent_rename.t +++ b/tests/incremental/05-method-rename/01-dependent_rename.t @@ -12,8 +12,3 @@ Run Goblint incrementally on new program version and check the change detection $ goblint --conf 01-dependent_rename.json --enable incremental.load 01-dependent_rename.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' changed = 1 (with unchangedHeader = 1); added = 2; removed = 2 - -Revert patch - - $ patch -b -R <01-dependent_rename.patch - patching file 01-dependent_rename.c diff --git a/tests/incremental/05-method-rename/02-cyclic_rename_dependency.t b/tests/incremental/05-method-rename/02-cyclic_rename_dependency.t index 0d706cf320..de9aa48e6c 100644 --- a/tests/incremental/05-method-rename/02-cyclic_rename_dependency.t +++ b/tests/incremental/05-method-rename/02-cyclic_rename_dependency.t @@ -12,8 +12,3 @@ Run Goblint incrementally on new program version and check the change detection $ goblint --conf 02-cyclic_rename_dependency.json --enable incremental.load 02-cyclic_rename_dependency.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' changed = 1 (with unchangedHeader = 1); added = 2; removed = 2 - -Revert patch - - $ patch -b -R <02-cyclic_rename_dependency.patch - patching file 02-cyclic_rename_dependency.c diff --git a/tests/incremental/05-method-rename/03-cyclic_with_swap.t b/tests/incremental/05-method-rename/03-cyclic_with_swap.t index 8bed0df5e9..d2e8dd6d97 100644 --- a/tests/incremental/05-method-rename/03-cyclic_with_swap.t +++ b/tests/incremental/05-method-rename/03-cyclic_with_swap.t @@ -12,8 +12,3 @@ Run Goblint incrementally on new program version and check the change detection $ goblint --conf 03-cyclic_with_swap.json --enable incremental.load 03-cyclic_with_swap.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' changed = 1 (with unchangedHeader = 1); added = 3; removed = 2 - -Revert patch - - $ patch -b -R <03-cyclic_with_swap.patch - patching file 03-cyclic_with_swap.c diff --git a/tests/incremental/05-method-rename/04-deep_change.t b/tests/incremental/05-method-rename/04-deep_change.t index 3ac9ac649c..1adcb56276 100644 --- a/tests/incremental/05-method-rename/04-deep_change.t +++ b/tests/incremental/05-method-rename/04-deep_change.t @@ -12,8 +12,3 @@ Run Goblint incrementally on new program version and check the change detection $ goblint --conf 04-deep_change.json --enable incremental.load 04-deep_change.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' changed = 1 (with unchangedHeader = 1); added = 0; removed = 0 - -Revert patch - - $ patch -b -R <04-deep_change.patch - patching file 04-deep_change.c diff --git a/tests/incremental/05-method-rename/05-common_rename.t b/tests/incremental/05-method-rename/05-common_rename.t index faa7ae9f7f..62e99c6c80 100644 --- a/tests/incremental/05-method-rename/05-common_rename.t +++ b/tests/incremental/05-method-rename/05-common_rename.t @@ -12,8 +12,3 @@ Run Goblint incrementally on new program version and check the change detection $ goblint --conf 05-common_rename.json --enable incremental.load 05-common_rename.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 - -Revert patch - - $ patch -b -R <05-common_rename.patch - patching file 05-common_rename.c diff --git a/tests/incremental/05-method-rename/06-recursive_rename.t b/tests/incremental/05-method-rename/06-recursive_rename.t index b7d0fabe3e..dce0894ff1 100644 --- a/tests/incremental/05-method-rename/06-recursive_rename.t +++ b/tests/incremental/05-method-rename/06-recursive_rename.t @@ -12,8 +12,3 @@ Run Goblint incrementally on new program version and check the change detection $ goblint --conf 06-recursive_rename.json --enable incremental.load 06-recursive_rename.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 - -Revert patch - - $ patch -b -R <06-recursive_rename.patch - patching file 06-recursive_rename.c diff --git a/tests/incremental/06-glob-var-rename/00-simple_rename.t b/tests/incremental/06-glob-var-rename/00-simple_rename.t index 59a1cfa469..1855b903eb 100644 --- a/tests/incremental/06-glob-var-rename/00-simple_rename.t +++ b/tests/incremental/06-glob-var-rename/00-simple_rename.t @@ -12,8 +12,3 @@ Run Goblint incrementally on new program version and check the change detection $ goblint --conf 00-simple_rename.json --enable incremental.load 00-simple_rename.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 - -Revert patch - - $ patch -b -R <00-simple_rename.patch - patching file 00-simple_rename.c diff --git a/tests/incremental/06-glob-var-rename/01-duplicate_local_global.t b/tests/incremental/06-glob-var-rename/01-duplicate_local_global.t index b1b73f4f26..cd2c5c0fea 100644 --- a/tests/incremental/06-glob-var-rename/01-duplicate_local_global.t +++ b/tests/incremental/06-glob-var-rename/01-duplicate_local_global.t @@ -12,8 +12,3 @@ Run Goblint incrementally on new program version and check the change detection $ goblint --conf 01-duplicate_local_global.json --enable incremental.load 01-duplicate_local_global.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' changed = 0 (with unchangedHeader = 0); added = 0; removed = 0 - -Revert patch - - $ patch -b -R <01-duplicate_local_global.patch - patching file 01-duplicate_local_global.c diff --git a/tests/incremental/06-glob-var-rename/02-add_new_gvar.t b/tests/incremental/06-glob-var-rename/02-add_new_gvar.t index 8450df2d47..c71cd6808f 100644 --- a/tests/incremental/06-glob-var-rename/02-add_new_gvar.t +++ b/tests/incremental/06-glob-var-rename/02-add_new_gvar.t @@ -12,8 +12,3 @@ Run Goblint incrementally on new program version and check the change detection $ goblint --conf 02-add_new_gvar.json --enable incremental.load 02-add_new_gvar.c | grep 'change_info' | sed -r 's/^change_info = \{ unchanged = [[:digit:]]+; (.*) \}$/\1/' changed = 1 (with unchangedHeader = 1); added = 1; removed = 0 - -Revert patch - - $ patch -b -R <02-add_new_gvar.patch - patching file 02-add_new_gvar.c From e293a7f09e8e043b8209b6653412508039efd426 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Thu, 23 Mar 2023 15:28:03 +0100 Subject: [PATCH 83/90] missing propagation of change_info and final_matches --- src/incremental/compareCIL.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index f210647aa0..10cf6361a3 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -190,7 +190,7 @@ let eqF_check_contained_renames ~renameDetection f1 f2 oldMap newMap cfgs gc_old (* for rename detection, check whether the rename assumptions collected during the function comparison actually match exactly, otherwise check that the function comparison was successful without collecting any rename assumptions *) - let dependenciesMatch = + let dependenciesMatch, change_info, final_matches = if renameDetection then let funDependenciesMatch, change_info, final_matches = let extract_globs _ gc map = @@ -199,7 +199,7 @@ let eqF_check_contained_renames ~renameDetection f1 f2 oldMap newMap cfgs gc_old let var_glob_old = GlobalMap.fold extract_globs oldMap VarinfoMap.empty in let var_glob_new = GlobalMap.fold extract_globs newMap VarinfoMap.empty in VarinfoMap.fold (fun f_old_var f_new_var (acc, ci, fm) -> - match VarinfoMap.find_opt f_old_var (fst final_matches) with + match VarinfoMap.find_opt f_old_var (fst fm) with | None -> let f_old = get_fundec (VarinfoMap.find f_old_var var_glob_old) in let f_new = get_fundec (VarinfoMap.find f_new_var var_glob_new) in (* TODO: what happens if there exists no fundec for this varinfo? *) @@ -208,16 +208,16 @@ let eqF_check_contained_renames ~renameDetection f1 f2 oldMap newMap cfgs gc_old else false, ci, fm | Some v -> v = f_new_var, ci, fm) function_dependencies (true, change_info, final_matches) in let globalDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun old_var new_var (acc, ci, fm) -> - match VarinfoMap.find_opt old_var (fst final_matches) with + match VarinfoMap.find_opt old_var (fst fm) with | None -> if acc then compare_varinfo_exact old_var gc_old oldMap new_var gc_new newMap ci fm else false, ci, fm | Some v -> v = new_var, ci, fm ) global_var_dependencies (true, change_info, final_matches) in - funDependenciesMatch && globalDependenciesMatch + funDependenciesMatch && globalDependenciesMatch, change_info, final_matches else - empty_rename_assms function_dependencies && empty_rename_assms global_var_dependencies in + empty_rename_assms function_dependencies && empty_rename_assms global_var_dependencies, change_info, final_matches in let append_to_changed ~unchangedHeader ~diff = change_info.changed <- {current = gc_new; old = gc_old; unchangedHeader; diff} :: change_info.changed From e8ce672697664b72023b2cf0bb20730fea54c9a6 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Thu, 23 Mar 2023 15:31:12 +0100 Subject: [PATCH 84/90] remove test script because of restricted parser and few working cases --- scripts/test-refactorings-rename.py | 450 ---------------------------- 1 file changed, 450 deletions(-) delete mode 100644 scripts/test-refactorings-rename.py diff --git a/scripts/test-refactorings-rename.py b/scripts/test-refactorings-rename.py deleted file mode 100644 index 4339113eac..0000000000 --- a/scripts/test-refactorings-rename.py +++ /dev/null @@ -1,450 +0,0 @@ -#!/usr/bin/python -import dataclasses -import os -import pathlib -import re -import shutil -import subprocess -import sys -import tempfile -from os.path import isdir -from pathlib import Path -from pycparser import c_ast, c_parser, parse_file -from pycparser.c_ast import TypeDecl, ArrayDecl, PtrDecl, IdentifierType -from pycparser.c_generator import CGenerator - -parser_errors = 0 -struct_occurrences = 0 -skips = 0 -includes = 0 -includes_only_assert = 0 -invalid_solver = 0 -introduced_changes = 0 -renamed_a_function = 0 - -# to support library headers, first clone https://github.com/eliben/pycparser to the directory next of the analyzer folder. -# Then comment the lines out and in that are described that way. - -def main(): - regression_folder = Path("./tests/regression") - - task = TaskRenameLocals(False) - - test = regression_folder / "25-vla/02-loop.c" - execute_validation_test(test.parent, test, task) - return - - excluded = [ - "44-trier_analyzer/33-recA.c", - # Even though the same file is read in, the type of rec#i differes from int * to int?! - "04-mutex/53-kernel-spinlock.c", # Kernel is broken. - "56-witness/01-base-lor-enums.c", # 0evals? - "56-witness/02-base-lor-addr.c", # 0evals? - "56-witness/03-int-log-short.c", # 0evals? - "56-witness/04-base-priv-sync-prune.c", # 0evals? - "44-trier_analyzer/09-G1.c", # Also renamed glob var - "44-trier_analyzer/21-Pproc.c" # renamed function. - ] - - # folder = regression_folder / "07-uninit" - # for testFile in folder.iterdir(): - # filename, extension = os.path.splitext(testFile.name) - # identifier = f"{folder.name}/{testFile.name}" - # - # if extension == ".c" and not (identifier in excluded): - # execute_validation_test(folder, testFile) - - total_tests = 0 - executed_tests = 0 - - for folder in regression_folder.iterdir(): - if isdir(folder): - for testFile in folder.iterdir(): - filename, extension = os.path.splitext(testFile.name) - if extension == ".c" and not (f"{folder.name}/{testFile.name}" in excluded): - total_tests += 1 - if execute_validation_test(folder, testFile, task): - executed_tests += 1 - - global introduced_changes - global renamed_a_function - - print(f"Executed {executed_tests}/{total_tests}") - if isinstance(task, TaskRenameLocals) and task.introduce_changes: - print(f"Introduced changes in {introduced_changes}/{executed_tests}") - - if isinstance(task, TaskRenameFunction): - print(f"Renamed a function in {renamed_a_function}/{executed_tests}") - - global parser_errors - global struct_occurrences - global skips - global includes - global invalid_solver - global includes_only_assert - - print("Skipped due tue:") - print("Parser errors: " + str(parser_errors)) - print("Struct occurrences: " + str(struct_occurrences)) - print("Skips (//Skip): " + str(skips)) - print(f"Includes: {includes}, of those only assert: {includes_only_assert}") - print("Invalid solver: " + str(invalid_solver)) - - -def execute_validation_test(folder: Path, test_file: Path, task): - print(f"Executing test: {folder.name}/{test_file.name}") - - global parser_errors - global struct_occurrences - global skips - global includes - global invalid_solver - global includes_only_assert - global introduced_changes - global renamed_a_function - - extra_params = "" - - with open(test_file, "r") as filehandle: - lines = filehandle.readlines() - if lines[0].startswith("// PARAM:"): - extra_params = lines[0][len("// PARAM:"):-1] - if lines[0].startswith("// SKIP"): - print("Skipped test.") - skips += 1 - return False - # comment this if out if you want to support library headers - if any(x.startswith("#include") for x in lines): - print("Skipped test because of include") - includes += 1 - - include_lines = [x for x in lines if x.startswith("#include")] - - if all("assert.h" in x for x in include_lines): - includes_only_assert += 1 - - return False - if any("struct" in x for x in lines): - print("Skipped because struct") - struct_occurrences += 1 - return False - - if "slr3" in extra_params or "slr4" in extra_params: - print("Aborted test due to invalid solver.") - invalid_solver += 1 - return False - - modified_file_result = create_modified_file(test_file, task) - - if modified_file_result is None: - print("Aborted test due to parsing error.") - parser_errors += 1 - return False - - base = "./" - - args = f"--enable dbg.debug --enable printstats -v {extra_params}" - - # uncomment to support library headers. - # with tempfile.NamedTemporaryFile() as t: - # subprocess.run(f"cpp -E -I../pycparser/utils/fake_libc_include {test_file} > {t.name}", shell=True) - # - # - # x = subprocess.run(f"./goblint {args} --enable incremental.save {t.name}", shell=True, text=True, capture_output=True) - # if x.returncode != 0: - # includes += 1 - # return False - - subprocess.run(f"./goblint {args} --enable incremental.save {test_file}", shell=True, capture_output=True) - - command = subprocess.run( - f"./goblint {args} --enable incremental.load --set save_run {base}/{test_file}-incrementalrun {modified_file_result.tmp.name}", - shell=True, text=True, capture_output=True) - - found_line = False - - for line in command.stdout.splitlines(): - if line.startswith("change_info = "): - match = re.search("; changed = (\d+)", line) - change_count = int(match.group(1)) - - if modified_file_result.introduced_changes: - invalid_change_count = change_count == 0 - expected = "> 0" - else: - invalid_change_count = change_count != 0 - expected = "= 0" - - if invalid_change_count != 0: - print("-----------------------------------------------------------------") - print(command.stdout) - print("-----------------------------------------------------------------") - print(f"Invalid change count={change_count}. Expected {expected}.") - cleanup(folder, test_file, modified_file_result.tmp) - sys.exit(-1) - found_line = True - break - - if not found_line: - print("Could not find line with change count.") - print(command.stdout) - cleanup(folder, test_file, modified_file_result.tmp) - sys.exit(-1) - - if modified_file_result.introduced_changes: - introduced_changes += 1 - - if modified_file_result.renamed_anything and isinstance(task, TaskRenameFunction): - renamed_a_function += 1 - - cleanup(folder, test_file, modified_file_result.tmp) - - return True - - -def cleanup(folder: Path, test: Path, updated_file): - updated_file.close() - shutil.rmtree(folder / f"{test.name}-incrementalrun") - - -def find_local_vars(node, on_node_found): - if node.body.block_items is not None: - for child in node.body.block_items: - if isinstance(child, c_ast.Decl): - if isinstance(child.type, c_ast.TypeDecl) or isinstance(child.type, c_ast.ArrayDecl): - on_node_found(child) - - -def rename_decl(node, new_name): - if isinstance(node.type, TypeDecl) or isinstance(node.type, ArrayDecl) or isinstance(node.type, PtrDecl): - node.name = new_name - if isinstance(node.type, TypeDecl): - node.type.declname = new_name - if isinstance(node.type, ArrayDecl): - node.type.type.declname = new_name - if isinstance(node.type, PtrDecl): - node.type.type.declname = new_name - -def visit_rest_of_func_def(self, node): - self.visit(node.decl) - if node.param_decls is not None: - self.visit(node.param_decls) - - self.visit(node.body) - -class VarDeclVisitor(c_ast.NodeVisitor): - - def __init__(self): - self.local_variables = {} - self.function_params = {} - - def visit_FuncDef(self, node): - lv = [] - fp = [] - - find_local_vars(node, lambda f: lv.append(f.name)) - if isinstance(node.decl, c_ast.Decl) and isinstance(node.decl.type, c_ast.FuncDecl): - func_decl = node.decl.type - if isinstance(func_decl.args, c_ast.ParamList): - for arg in func_decl.args.params: - if isinstance(arg, c_ast.Decl): - fp.append(arg.name) - - self.local_variables[node.decl.name] = lv - self.function_params[node.decl.name] = fp - - -class RenameVariableVisitor(c_ast.NodeVisitor): - - def __init__(self, rename_mapping): - self.map = rename_mapping - - def visit_ID(self, node): - if node.name in self.map: - node.name = self.map[node.name] - - def visit_Decl(self, node): - if node.name in self.map: - rename_decl(node, self.map[node.name]) - - if node.init is not None: - self.visit(node.init) - - self.visit(node.type) - - -class IntroduceSemanticChangeVisitor(c_ast.NodeVisitor): - - # legal_local_variables: Only these variables may be used to introduce a change - def __init__(self, legal_local_variables): - self.in_fun = False - self.fun_name = None - - self.introduced_change = False - self.found_vars = [] - self.introduced_changes = [] - self.legal_local_variables = legal_local_variables - - def visit_ID(self, node): - if self.in_fun: - if any(found_var for found_var in self.found_vars if found_var.name == node.name): - known_var = [found_var for found_var in self.found_vars if found_var.name == node.name][0] - - # check if we can find another already declared var with the same type - other_decls = [var for var in self.found_vars if - var.type == known_var.type and - var.name != known_var.name and - var.name in self.legal_local_variables[self.fun_name] - ] - - # only introduce change if not already done so for this variable - if len(other_decls) > 0 and known_var.name not in self.introduced_changes: - node.name = other_decls[0].name - self.introduced_change = True - self.introduced_changes.append(known_var.name) - else: - node.name = known_var.name - - - def visit_FuncDef(self, node): - self.in_fun = True - self.fun_name = node.decl.name - self.found_vars = [] - self.introduced_changes = [] - visit_rest_of_func_def(self, node) - self.in_fun = False - self.fun_name = None - - def visit_Decl(self, node): - if self.in_fun and isinstance(node.type, c_ast.TypeDecl) or isinstance(node.type, c_ast.ArrayDecl): - if isinstance(node.type, TypeDecl) and isinstance(node.type.type, IdentifierType): - if len(node.type.type.names) == 1: - self.found_vars.append(LocalVar(node.name, node.type.type.names[0], node.name + "_updated")) - if node.init is not None: - self.visit(node.init) - - self.visit(node.type) - - -# find a single function to rename, but never main -class FindFunctionToRenameVisitor(c_ast.NodeVisitor): - - def __init__(self): - self.fun_name = None - self.updated_fun_name = None - - - def visit_FuncDef(self, node): - fun_name = node.decl.name - if fun_name != "main" and self.fun_name is None: - self.fun_name = fun_name - self.updated_fun_name = fun_name + "_updated" - - -class RenameFunctionVisitor(c_ast.NodeVisitor): - - def __init__(self, function_to_rename_name, updated_name): - self.function_to_rename_name = function_to_rename_name - self.updated_name = updated_name - - def visit_FuncDef(self, node): - fun_name = node.decl.name - if fun_name == self.function_to_rename_name: - node.decl.name = self.updated_name - node.decl.type.type.declname = self.updated_name - - visit_rest_of_func_def(self, node) - - - def visit_ID(self, node): - if node.name == self.function_to_rename_name: - node.name = self.updated_name - - -def create_modified_file(source_file: Path, task): - try: - # uncommet to support library headers. - # gcc = subprocess.run(f"cpp -E -I../pycparser/utils/fake_libc_include {source_file}", shell=True, capture_output=True, text=True) - - # ast = c_parser.CParser().parse(gcc.stdout) - ast = parse_file(source_file, use_cpp=True) - - introduced_change = False - renamed_anything = False - - if isinstance(task, TaskRenameLocals): - v = VarDeclVisitor() - v.visit(ast) - - rename_mapping = {} - local_vars = [x for xs in (list(v.local_variables.values()) + list(v.function_params.values())) for x in xs] - for local_var in local_vars: - rename_mapping[local_var] = local_var + "_updated" - - if task.introduce_changes: - x = IntroduceSemanticChangeVisitor(v.local_variables) - x.visit(ast) - - # print(CGenerator().visit(ast)) - # print("Introduced change:" + str(x.introduced_change)) - - introduced_change = x.introduced_change - else: - introduced_change = False - - RenameVariableVisitor(rename_mapping).visit(ast) - renamed_anything = len(local_vars) > 0 - - if isinstance(task, TaskRenameFunction): - v = FindFunctionToRenameVisitor() - v.visit(ast) - - renamed_anything = v.fun_name is not None - - if v.fun_name is not None: - v = RenameFunctionVisitor(v.fun_name, v.updated_fun_name) - v.visit(ast) - - introduced_change = False - - # print(CGenerator().visit(ast)) - - tmp = tempfile.NamedTemporaryFile() - with open(tmp.name, "w") as f: - f.write(CGenerator().visit(ast)) - - return ModifiedFileResult(tmp, introduced_change, renamed_anything) - except: - return None - - -@dataclasses.dataclass -class ModifiedFileResult: - tmp: tempfile.NamedTemporaryFile - introduced_changes: bool - renamed_anything: bool - - -@dataclasses.dataclass -class LocalVar: - name: str - type: str - new_name: str - - -@dataclasses.dataclass -class TaskRenameLocals: - introduce_changes: bool - - -@dataclasses.dataclass -class TaskRenameFunction: - def __init__(self): - self - - -if __name__ == '__main__': - # result = create_modified_file(Path("scripts/test.c"), TaskRenameFunction()) - # print(result.introduced_changes) - # result.tmp.close() - main() From af79fb2855528663dca6fd95b1d634648fd9f990 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Thu, 23 Mar 2023 16:56:14 +0100 Subject: [PATCH 85/90] no structural comparison when looking through collected final_matches --- src/incremental/compareCIL.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 10cf6361a3..a6cd7cb68b 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -206,14 +206,14 @@ let eqF_check_contained_renames ~renameDetection f1 f2 oldMap newMap cfgs gc_old if acc then eqF_only_consider_exact_match f_old f_new ci fm oldMap newMap var_glob_old var_glob_new else false, ci, fm - | Some v -> v = f_new_var, ci, fm) function_dependencies (true, change_info, final_matches) in + | Some v -> v.vid = f_new_var.vid, ci, fm) function_dependencies (true, change_info, final_matches) in let globalDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun old_var new_var (acc, ci, fm) -> match VarinfoMap.find_opt old_var (fst fm) with | None -> if acc then compare_varinfo_exact old_var gc_old oldMap new_var gc_new newMap ci fm else false, ci, fm - | Some v -> v = new_var, ci, fm + | Some v -> v.vid = new_var.vid, ci, fm ) global_var_dependencies (true, change_info, final_matches) in funDependenciesMatch && globalDependenciesMatch, change_info, final_matches else From 7d68cf22035f141ca101afb4349a55445d7b7ace Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Thu, 23 Mar 2023 18:01:31 +0100 Subject: [PATCH 86/90] fix check whether already matched as unchanged --- src/incremental/compareAST.ml | 2 +- src/incremental/compareCIL.ml | 36 +++++++++++++++++++---------------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 006c5eadb1..269e90a4d7 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -257,7 +257,7 @@ and eq_varinfo (a: varinfo) (b: varinfo) ~(acc: (typ * typ) list) ~(rename_mappi let (typeCheck, (_, _, _, updated_renames_on_success)) = eq_typ_acc ~fun_parameter_name_comparison_enabled a.vtype b.vtype ~rename_mapping:(StringMap.empty, VarinfoMap.empty, VarinfoMap.empty, renames_on_success) ~acc in (isNamingOk && typeCheck, (locals_renames, updated_method_rename_mappings, updatedGlobVarMapping, updated_renames_on_success)) &&>> - forward_list_equal (eq_attribute ~acc ) a.vattr b.vattr &&> + forward_list_equal (eq_attribute ~acc) a.vattr b.vattr &&> (a.vstorage = b.vstorage) &&> (a.vglob = b.vglob) &&> (a.vaddrof = b.vaddrof) (* Ignore the location, vid, vreferenced, vdescr, vdescrpure, vinline *) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index a6cd7cb68b..6425e12d21 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -192,28 +192,32 @@ let eqF_check_contained_renames ~renameDetection f1 f2 oldMap newMap cfgs gc_old otherwise check that the function comparison was successful without collecting any rename assumptions *) let dependenciesMatch, change_info, final_matches = if renameDetection then - let funDependenciesMatch, change_info, final_matches = - let extract_globs _ gc map = - let v = get_varinfo gc in - VarinfoMap.add v gc map in - let var_glob_old = GlobalMap.fold extract_globs oldMap VarinfoMap.empty in - let var_glob_new = GlobalMap.fold extract_globs newMap VarinfoMap.empty in - VarinfoMap.fold (fun f_old_var f_new_var (acc, ci, fm) -> - match VarinfoMap.find_opt f_old_var (fst fm) with - | None -> - let f_old = get_fundec (VarinfoMap.find f_old_var var_glob_old) in - let f_new = get_fundec (VarinfoMap.find f_new_var var_glob_new) in (* TODO: what happens if there exists no fundec for this varinfo? *) - if acc then - eqF_only_consider_exact_match f_old f_new ci fm oldMap newMap var_glob_old var_glob_new - else false, ci, fm - | Some v -> v.vid = f_new_var.vid, ci, fm) function_dependencies (true, change_info, final_matches) in + let extract_globs _ gc map = + let v = get_varinfo gc in + VarinfoMap.add v gc map in + let var_glob_old = GlobalMap.fold extract_globs oldMap VarinfoMap.empty in + let var_glob_new = GlobalMap.fold extract_globs newMap VarinfoMap.empty in + let funDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun f_old_var f_new_var (acc, ci, fm) -> + let glob_old = VarinfoMap.find f_old_var var_glob_old in + let glob_new = VarinfoMap.find f_new_var var_glob_new in + match VarinfoMap.find_opt f_old_var (fst fm) with + | None -> + let f_old = get_fundec glob_old in + let f_new = get_fundec glob_new in (* TODO: what happens if there exists no fundec for this varinfo? *) + if acc then + eqF_only_consider_exact_match f_old f_new ci fm oldMap newMap var_glob_old var_glob_new + else false, ci, fm + | Some v -> acc && v.vid = f_new_var.vid && List.mem {old=glob_old; current=glob_new} ci.unchanged, ci, fm + ) function_dependencies (true, change_info, final_matches) in let globalDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun old_var new_var (acc, ci, fm) -> + let glob_old = VarinfoMap.find old_var var_glob_old in + let glob_new = VarinfoMap.find new_var var_glob_new in match VarinfoMap.find_opt old_var (fst fm) with | None -> if acc then compare_varinfo_exact old_var gc_old oldMap new_var gc_new newMap ci fm else false, ci, fm - | Some v -> v.vid = new_var.vid, ci, fm + | Some v -> acc && v.vid = new_var.vid && List.mem {old=glob_old; current=glob_new} ci.unchanged, ci, fm ) global_var_dependencies (true, change_info, final_matches) in funDependenciesMatch && globalDependenciesMatch, change_info, final_matches else From 79873fe86bc7cc7cb1bf21fcc8574fc4ee1ef191 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Thu, 23 Mar 2023 18:17:11 +0100 Subject: [PATCH 87/90] fix: already matched check needs to consider change_info --- src/incremental/compareCIL.ml | 54 +++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index 6425e12d21..bb9f25a9b5 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -101,9 +101,22 @@ let addToFinalMatchesMapping oV nV final_matches = let empty_rename_assms m = VarinfoMap.for_all (fun vo vn -> vo.vname = vn.vname) m +let already_matched oV nV final_matches = + match VarinfoMap.find_opt oV (fst final_matches) with + | None -> false + | Some v -> v.vid = oV.vid + +(* looks up the result of the already executed comparison and returns true if it is unchanged, false if it is changed. + Throws an exception if not found. *) +let change_info_lookup old_glob new_glob change_info = + List.mem {old = old_glob; current = new_glob} change_info.unchanged + (* Compares two varinfos of globals. finalizeOnlyExactMatch=true allows to check a rename assumption and discard the comparison result in case they do not match *) let eq_glob_var ?(finalizeOnlyExactMatch=false) oV gc_old oldMap nV gc_new newMap change_info final_matches = - if not (preservesSameNameMatches oV.vname oldMap nV.vname newMap) then + if already_matched oV nV final_matches then + (* check if this function was already matched and lookup the result *) + change_info_lookup gc_old gc_new change_info, change_info, final_matches + else if not (preservesSameNameMatches oV.vname oldMap nV.vname newMap) then (* do not allow for matches between differently named variables if one of the variables names exists in both, the new and old file *) false, change_info, final_matches else ( @@ -166,17 +179,20 @@ let eqF (old: Cil.fundec) (current: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) opti in identical, diffOpt, renamed_method_dependencies, renamed_global_vars_dependencies, renamesOnSuccess -let eqF_only_consider_exact_match f1 f2 change_info final_matches oldMap newMap var_glob_old var_glob_new = - (* check that names of match are each only contained in new or old file *) - if not (preservesSameNameMatches f1.svar.vname oldMap f2.svar.vname newMap) then ( +let eqF_only_consider_exact_match f1 f2 change_info final_matches oldMap newMap gc_old gc_new = + if already_matched f1.svar f2.svar final_matches then + (* check if this function was already matched and lookup the result *) + change_info_lookup gc_old gc_new change_info, change_info, final_matches + else if not (preservesSameNameMatches f1.svar.vname oldMap f2.svar.vname newMap) then + (* check that names of match are each only contained in new or old file *) false, change_info, final_matches - ) else + else (* the exact comparison is always uses the AST comparison because only when unchanged this match is manifested *) let doMatch, diff, fun_deps, global_deps, renamesOnSuccess = eqF f1 f2 None VarinfoMap.empty VarinfoMap.empty in match doMatch with | Unchanged when empty_rename_assms (VarinfoMap.filter (fun vo vn -> not (vo.vname = f1.svar.vname && vn.vname = f2.svar.vname)) fun_deps) && empty_rename_assms global_deps -> performRenames renamesOnSuccess; - change_info.unchanged <- {old = VarinfoMap.find f1.svar var_glob_old; current = VarinfoMap.find f2.svar var_glob_new} :: change_info.unchanged; + change_info.unchanged <- {old = gc_old; current = gc_new} :: change_info.unchanged; let final_matches = addToFinalMatchesMapping f1.svar f2.svar final_matches in true, change_info, final_matches | Unchanged -> false, change_info, final_matches @@ -198,26 +214,20 @@ let eqF_check_contained_renames ~renameDetection f1 f2 oldMap newMap cfgs gc_old let var_glob_old = GlobalMap.fold extract_globs oldMap VarinfoMap.empty in let var_glob_new = GlobalMap.fold extract_globs newMap VarinfoMap.empty in let funDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun f_old_var f_new_var (acc, ci, fm) -> - let glob_old = VarinfoMap.find f_old_var var_glob_old in - let glob_new = VarinfoMap.find f_new_var var_glob_new in - match VarinfoMap.find_opt f_old_var (fst fm) with - | None -> - let f_old = get_fundec glob_old in - let f_new = get_fundec glob_new in (* TODO: what happens if there exists no fundec for this varinfo? *) - if acc then - eqF_only_consider_exact_match f_old f_new ci fm oldMap newMap var_glob_old var_glob_new - else false, ci, fm - | Some v -> acc && v.vid = f_new_var.vid && List.mem {old=glob_old; current=glob_new} ci.unchanged, ci, fm + let gc_old = VarinfoMap.find f_old_var var_glob_old in + let gc_new = VarinfoMap.find f_old_var var_glob_new in + let f_old = get_fundec gc_old in + let f_new = get_fundec gc_new in (* TODO: what happens if there exists no fundec for this varinfo? *) + if acc then + eqF_only_consider_exact_match f_old f_new ci fm oldMap newMap gc_old gc_new + else false, ci, fm ) function_dependencies (true, change_info, final_matches) in let globalDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun old_var new_var (acc, ci, fm) -> let glob_old = VarinfoMap.find old_var var_glob_old in let glob_new = VarinfoMap.find new_var var_glob_new in - match VarinfoMap.find_opt old_var (fst fm) with - | None -> - if acc then - compare_varinfo_exact old_var gc_old oldMap new_var gc_new newMap ci fm - else false, ci, fm - | Some v -> acc && v.vid = new_var.vid && List.mem {old=glob_old; current=glob_new} ci.unchanged, ci, fm + if acc then + compare_varinfo_exact old_var glob_old oldMap new_var glob_new newMap ci fm + else false, ci, fm ) global_var_dependencies (true, change_info, final_matches) in funDependenciesMatch && globalDependenciesMatch, change_info, final_matches else From 06502b5fb9dd125239208ae04ceefe39a47f7eb3 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Mon, 17 Apr 2023 17:28:23 +0200 Subject: [PATCH 88/90] remove polymorphic comparison of global_col --- src/incremental/compareCIL.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index bb9f25a9b5..c61d74c627 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -17,6 +17,7 @@ let name_of_global_col gc = match gc.def with | None -> raise (Failure "empty global record") let compare_global_col gc1 gc2 = compare (name_of_global_col gc1) (name_of_global_col gc2) +let equal_name_global_col gc1 gc2 = compare_global_col gc1 gc2 == 0 let get_varinfo gc = match gc.decls, gc.def with | _, Some (Var v) -> v @@ -90,6 +91,7 @@ let should_reanalyze (fdec: Cil.fundec) = let performRenames (renamesOnSuccess: renamesOnSuccess) = begin let (compinfoRenames, enumRenames) = renamesOnSuccess in + (* Reset cnames and ckeys to the old value. Only affects anonymous structs/unions where names are not checked for equality. *) List.iter (fun (compinfo2, compinfo1) -> compinfo2.cname <- compinfo1.cname; compinfo2.ckey <- compinfo1.ckey) compinfoRenames; List.iter (fun (enum2, enum1) -> enum2.ename <- enum1.ename) enumRenames; end @@ -109,7 +111,7 @@ let already_matched oV nV final_matches = (* looks up the result of the already executed comparison and returns true if it is unchanged, false if it is changed. Throws an exception if not found. *) let change_info_lookup old_glob new_glob change_info = - List.mem {old = old_glob; current = new_glob} change_info.unchanged + List.exists (fun (u : unchanged_global) -> equal_name_global_col u.old old_glob && equal_name_global_col u.current new_glob) change_info.unchanged (* Compares two varinfos of globals. finalizeOnlyExactMatch=true allows to check a rename assumption and discard the comparison result in case they do not match *) let eq_glob_var ?(finalizeOnlyExactMatch=false) oV gc_old oldMap nV gc_new newMap change_info final_matches = From 61d15d14a78ab20e153c898552553ef03f0d6eba Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Tue, 2 May 2023 19:28:57 +0200 Subject: [PATCH 89/90] fix new and old global mix-up --- src/incremental/compareCIL.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index c61d74c627..bdb490c096 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -2,7 +2,7 @@ open GoblintCil open MyCFG include CompareAST include CompareCFG -open CilMaps +include CilMaps module GlobalMap = Map.Make(String) @@ -217,7 +217,7 @@ let eqF_check_contained_renames ~renameDetection f1 f2 oldMap newMap cfgs gc_old let var_glob_new = GlobalMap.fold extract_globs newMap VarinfoMap.empty in let funDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun f_old_var f_new_var (acc, ci, fm) -> let gc_old = VarinfoMap.find f_old_var var_glob_old in - let gc_new = VarinfoMap.find f_old_var var_glob_new in + let gc_new = VarinfoMap.find f_new_var var_glob_new in let f_old = get_fundec gc_old in let f_new = get_fundec gc_new in (* TODO: what happens if there exists no fundec for this varinfo? *) if acc then From 72065a554a267029b6e4e4824c1de1c8e580b47d Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Wed, 3 May 2023 10:11:56 +0200 Subject: [PATCH 90/90] handle functions without definitions in exact comparison --- src/incremental/compareCIL.ml | 56 ++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index bdb490c096..709fc9b8bb 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -25,10 +25,6 @@ let get_varinfo gc = match gc.decls, gc.def with | Some v, _ -> v | _ -> failwith "A global should have at least a declaration or a definition" -let get_fundec gc = match gc.decls, gc.def with - | _, Some (Fun f) -> f - | _ -> failwith "Global does not have a function definition" - module GlobalColMap = Map.Make( struct type t = global_col @@ -181,26 +177,34 @@ let eqF (old: Cil.fundec) (current: Cil.fundec) (cfgs : (cfg * (cfg * cfg)) opti in identical, diffOpt, renamed_method_dependencies, renamed_global_vars_dependencies, renamesOnSuccess -let eqF_only_consider_exact_match f1 f2 change_info final_matches oldMap newMap gc_old gc_new = - if already_matched f1.svar f2.svar final_matches then - (* check if this function was already matched and lookup the result *) - change_info_lookup gc_old gc_new change_info, change_info, final_matches - else if not (preservesSameNameMatches f1.svar.vname oldMap f2.svar.vname newMap) then - (* check that names of match are each only contained in new or old file *) - false, change_info, final_matches - else - (* the exact comparison is always uses the AST comparison because only when unchanged this match is manifested *) - let doMatch, diff, fun_deps, global_deps, renamesOnSuccess = eqF f1 f2 None VarinfoMap.empty VarinfoMap.empty in - match doMatch with - | Unchanged when empty_rename_assms (VarinfoMap.filter (fun vo vn -> not (vo.vname = f1.svar.vname && vn.vname = f2.svar.vname)) fun_deps) && empty_rename_assms global_deps -> - performRenames renamesOnSuccess; - change_info.unchanged <- {old = gc_old; current = gc_new} :: change_info.unchanged; - let final_matches = addToFinalMatchesMapping f1.svar f2.svar final_matches in - true, change_info, final_matches - | Unchanged -> false, change_info, final_matches - | Changed -> false, change_info, final_matches - | ChangedFunHeader _ -> false, change_info, final_matches - | ForceReanalyze _ -> false, change_info, final_matches +let eqF_only_consider_exact_match gc_old gc_new change_info final_matches oldMap newMap = + match gc_old.def, gc_new.def with + | None, None -> ( + match gc_old.decls, gc_new.decls with + | Some old_var, Some new_var -> + compare_varinfo_exact old_var gc_old oldMap new_var gc_new newMap change_info final_matches + | _ -> failwith "A global collection should never be empty") + | Some (Fun f1), Some (Fun f2) -> ( + if already_matched f1.svar f2.svar final_matches then + (* check if this function was already matched and lookup the result *) + change_info_lookup gc_old gc_new change_info, change_info, final_matches + else if not (preservesSameNameMatches f1.svar.vname oldMap f2.svar.vname newMap) then + (* check that names of match are each only contained in new or old file *) + false, change_info, final_matches + else + (* the exact comparison is always uses the AST comparison because only when unchanged this match is manifested *) + let doMatch, diff, fun_deps, global_deps, renamesOnSuccess = eqF f1 f2 None VarinfoMap.empty VarinfoMap.empty in + match doMatch with + | Unchanged when empty_rename_assms (VarinfoMap.filter (fun vo vn -> not (vo.vname = f1.svar.vname && vn.vname = f2.svar.vname)) fun_deps) && empty_rename_assms global_deps -> + performRenames renamesOnSuccess; + change_info.unchanged <- {old = gc_old; current = gc_new} :: change_info.unchanged; + let final_matches = addToFinalMatchesMapping f1.svar f2.svar final_matches in + true, change_info, final_matches + | Unchanged -> false, change_info, final_matches + | Changed -> false, change_info, final_matches + | ChangedFunHeader _ -> false, change_info, final_matches + | ForceReanalyze _ -> false, change_info, final_matches) + | _, _ -> false, change_info, final_matches let eqF_check_contained_renames ~renameDetection f1 f2 oldMap newMap cfgs gc_old gc_new (change_info, final_matches) = let doMatch, diff, function_dependencies, global_var_dependencies, renamesOnSuccess = eqF f1 f2 cfgs VarinfoMap.empty VarinfoMap.empty in @@ -218,10 +222,8 @@ let eqF_check_contained_renames ~renameDetection f1 f2 oldMap newMap cfgs gc_old let funDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun f_old_var f_new_var (acc, ci, fm) -> let gc_old = VarinfoMap.find f_old_var var_glob_old in let gc_new = VarinfoMap.find f_new_var var_glob_new in - let f_old = get_fundec gc_old in - let f_new = get_fundec gc_new in (* TODO: what happens if there exists no fundec for this varinfo? *) if acc then - eqF_only_consider_exact_match f_old f_new ci fm oldMap newMap gc_old gc_new + eqF_only_consider_exact_match gc_old gc_new ci fm oldMap newMap else false, ci, fm ) function_dependencies (true, change_info, final_matches) in let globalDependenciesMatch, change_info, final_matches = VarinfoMap.fold (fun old_var new_var (acc, ci, fm) ->