Preparing for final commit
This commit is contained in:
parent
d26445d151
commit
d838756d0e
4 changed files with 95 additions and 12 deletions
|
@ -159,13 +159,13 @@ module NonRelational (V : VALUE_DOMAIN) : DOMAIN = struct
|
||||||
let rec revise_HC4 tree mapenv newval = match tree with
|
let rec revise_HC4 tree mapenv newval = match tree with
|
||||||
| HC4_int_unary (uop, sub, abst) -> revise_HC4 sub mapenv (V.bwd_unary (get_abst sub) uop (V.meet abst newval))
|
| HC4_int_unary (uop, sub, abst) -> revise_HC4 sub mapenv (V.bwd_unary (get_abst sub) uop (V.meet abst newval))
|
||||||
| HC4_int_binary (bop, sub1, sub2, abst) -> let refined1, refined2 = V.bwd_binary (get_abst sub1) (get_abst sub2) bop (V.meet abst newval) in begin
|
| HC4_int_binary (bop, sub1, sub2, abst) -> let refined1, refined2 = V.bwd_binary (get_abst sub1) (get_abst sub2) bop (V.meet abst newval) in begin
|
||||||
Format.printf "bwd_binary \"%a%s%a = %a\" -> %a, %a@ "
|
(*Format.printf "bwd_binary \"%a%s%a = %a\" -> %a, %a@ "
|
||||||
V.print (get_abst sub1)
|
V.print (get_abst sub1)
|
||||||
(Cfg_printer.string_of_int_binary_op bop)
|
(Cfg_printer.string_of_int_binary_op bop)
|
||||||
V.print (get_abst sub2)
|
V.print (get_abst sub2)
|
||||||
V.print (V.meet abst newval)
|
V.print (V.meet abst newval)
|
||||||
V.print (refined1)
|
V.print (refined1)
|
||||||
V.print (refined2);
|
V.print (refined2);*)
|
||||||
meet (revise_HC4 sub1 mapenv refined1) (revise_HC4 sub2 mapenv refined2) end
|
meet (revise_HC4 sub1 mapenv refined1) (revise_HC4 sub2 mapenv refined2) end
|
||||||
| HC4_int_var (v, abst) -> E (VMap.add v (V.meet abst newval) mapenv)
|
| HC4_int_var (v, abst) -> E (VMap.add v (V.meet abst newval) mapenv)
|
||||||
| HC4_int_const (z,abst) -> if( V.is_bottom (V.meet abst newval) ) then Bot else E mapenv
|
| HC4_int_const (z,abst) -> if( V.is_bottom (V.meet abst newval) ) then Bot else E mapenv
|
||||||
|
@ -181,13 +181,13 @@ module NonRelational (V : VALUE_DOMAIN) : DOMAIN = struct
|
||||||
| Bot -> Bot
|
| Bot -> Bot
|
||||||
| E mapenv -> let hc4_1, hc4_2 = build_HC4 iexp1 mapenv, build_HC4 iexp2 mapenv in
|
| E mapenv -> let hc4_1, hc4_2 = build_HC4 iexp1 mapenv, build_HC4 iexp2 mapenv in
|
||||||
let newval1, newval2 = V.compare (get_abst hc4_1) (get_abst hc4_2) cop in (
|
let newval1, newval2 = V.compare (get_abst hc4_1) (get_abst hc4_2) cop in (
|
||||||
Format.printf "Refined comparison %a%s%a as %a%s%a@ "
|
(*Format.printf "Refined comparison %a%s%a as %a%s%a@ "
|
||||||
V.print (get_abst hc4_1)
|
V.print (get_abst hc4_1)
|
||||||
(Cfg_printer.string_of_compare_op cop)
|
(Cfg_printer.string_of_compare_op cop)
|
||||||
V.print (get_abst hc4_2)
|
V.print (get_abst hc4_2)
|
||||||
V.print newval1
|
V.print newval1
|
||||||
(Cfg_printer.string_of_compare_op cop)
|
(Cfg_printer.string_of_compare_op cop)
|
||||||
V.print newval2;
|
V.print newval2;*)
|
||||||
meet (revise_HC4 hc4_1 mapenv newval1) (revise_HC4 hc4_2 mapenv newval2) )
|
meet (revise_HC4 hc4_1 mapenv newval1) (revise_HC4 hc4_2 mapenv newval2) )
|
||||||
|
|
||||||
let guard env boolexp = guard_noneg env (rm_negations boolexp)
|
let guard env boolexp = guard_noneg env (rm_negations boolexp)
|
||||||
|
|
|
@ -9,6 +9,8 @@ module type ITERABLE = sig
|
||||||
|
|
||||||
val do_compute : arc -> t (*source*) -> (arc -> unit) -> (func -> t -> t) -> t (*to accumulate*)
|
val do_compute : arc -> t (*source*) -> (arc -> unit) -> (func -> t -> t) -> t (*to accumulate*)
|
||||||
val accumulate : arc -> t (*source*) -> t (*old dst*) -> t*bool (*dst*)
|
val accumulate : arc -> t (*source*) -> t (*old dst*) -> t*bool (*dst*)
|
||||||
|
|
||||||
|
val print : Format.formatter -> t -> unit
|
||||||
end
|
end
|
||||||
|
|
||||||
module SimpleIterable (D : DOMAIN) : ITERABLE = struct
|
module SimpleIterable (D : DOMAIN) : ITERABLE = struct
|
||||||
|
@ -17,15 +19,17 @@ module SimpleIterable (D : DOMAIN) : ITERABLE = struct
|
||||||
let bottom = D.bottom
|
let bottom = D.bottom
|
||||||
let init x = D.init x
|
let init x = D.init x
|
||||||
|
|
||||||
|
let print fmt abst = D.print fmt abst
|
||||||
|
|
||||||
let do_compute a src cb_fail cb_fun = match a.arc_inst with
|
let do_compute a src cb_fail cb_fun = match a.arc_inst with
|
||||||
| CFG_skip _ -> src
|
| CFG_skip _ -> src
|
||||||
| CFG_assign (v, iexpr) -> D.assign src v iexpr
|
| CFG_assign (v, iexpr) -> D.assign src v iexpr
|
||||||
| CFG_guard bexpr -> D.guard src bexpr
|
| CFG_guard bexpr -> D.guard src bexpr
|
||||||
| CFG_assert (bexpr, _) -> (let s = D.guard src (CFG_bool_unary (AST_NOT, bexpr)) in
|
| CFG_assert (bexpr, _) -> (let s = D.guard src (CFG_bool_unary (AST_NOT, bexpr)) in
|
||||||
if D.is_bottom s then (
|
if D.is_bottom s then (
|
||||||
Format.printf "State %a is disjoint with %a@ " D.print src Cfg_printer.print_bool_expr (rm_negations (CFG_bool_unary (AST_NOT, bexpr)));
|
(*Format.printf "State %a is disjoint with %a@ " D.print src Cfg_printer.print_bool_expr (rm_negations (CFG_bool_unary (AST_NOT, bexpr)));*)
|
||||||
src) else (
|
src) else (
|
||||||
Format.printf "Failure of assert on %a@ " D.print s;
|
(*Format.printf "Failure of assert on %a@ " D.print s;*)
|
||||||
cb_fail a;
|
cb_fail a;
|
||||||
(D.guard src bexpr)))
|
(D.guard src bexpr)))
|
||||||
| CFG_call f -> cb_fun f src
|
| CFG_call f -> cb_fun f src
|
||||||
|
@ -34,13 +38,13 @@ module SimpleIterable (D : DOMAIN) : ITERABLE = struct
|
||||||
let accumulate a dst_old dst_toacc =
|
let accumulate a dst_old dst_toacc =
|
||||||
if D.subset dst_toacc dst_old then (dst_old, false) else (
|
if D.subset dst_toacc dst_old then (dst_old, false) else (
|
||||||
let accfun = if a.arc_dst.widen_target then D.widen else D.join in
|
let accfun = if a.arc_dst.widen_target then D.widen else D.join in
|
||||||
let str = if a.arc_dst.widen_target then "widen" else "join" in
|
(*let str = if a.arc_dst.widen_target then "widen" else "join" in
|
||||||
Format.printf "@[<h 0>[%i -> %i] Got node %i state %a %s %a " a.arc_src.node_id a.arc_dst.node_id a.arc_dst.node_id
|
Format.printf "@[<h 0>[%i -> %i] Got node %i state %a %s %a " a.arc_src.node_id a.arc_dst.node_id a.arc_dst.node_id
|
||||||
D.print dst_old
|
D.print dst_old
|
||||||
str
|
str
|
||||||
D.print dst_toacc;
|
D.print dst_toacc;*)
|
||||||
let r = accfun dst_old dst_toacc in
|
let r = accfun dst_old dst_toacc in
|
||||||
Format.printf "= %a@]@ " D.print r; r,true)
|
(*Format.printf "= %a@]@ " D.print r;*) r,true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,6 +69,13 @@ module DisjunctiveIterable (D : DOMAIN) : ITERABLE = struct
|
||||||
let bottom = SCRMap.empty
|
let bottom = SCRMap.empty
|
||||||
let init x = SCRMap.singleton [] (D.init x)
|
let init x = SCRMap.singleton [] (D.init x)
|
||||||
|
|
||||||
|
let print fmt abst =
|
||||||
|
Format.fprintf fmt "@[<h 0>[";
|
||||||
|
SCRMap.iter (fun k x -> Format.fprintf fmt "(";
|
||||||
|
List.iter (fun (i, b) -> Format.fprintf fmt "%d:%b," i b) k;
|
||||||
|
Format.fprintf fmt ") %a" D.print x) abst;
|
||||||
|
Format.fprintf fmt "]@]"
|
||||||
|
|
||||||
let do_compute a src cb_fail cb_fun = match a.arc_inst with
|
let do_compute a src cb_fail cb_fun = match a.arc_inst with
|
||||||
| CFG_skip _ -> src
|
| CFG_skip _ -> src
|
||||||
| CFG_assign (v, iexpr) -> SCRMap.map (fun d -> D.assign d v iexpr) src
|
| CFG_assign (v, iexpr) -> SCRMap.map (fun d -> D.assign d v iexpr) src
|
||||||
|
@ -74,7 +85,7 @@ module DisjunctiveIterable (D : DOMAIN) : ITERABLE = struct
|
||||||
| false -> Some d) src None in
|
| false -> Some d) src None in
|
||||||
(match b with
|
(match b with
|
||||||
| None -> src
|
| None -> src
|
||||||
| Some d -> Format.printf "Failure of assert : cannot rule out state %a@ " D.print d;
|
| Some _(*d*) -> (*Format.printf "Failure of assert : cannot rule out state %a@ " D.print d;*)
|
||||||
cb_fail a; SCRMap.map (fun d -> D.guard d bexpr) src)
|
cb_fail a; SCRMap.map (fun d -> D.guard d bexpr) src)
|
||||||
| CFG_call f -> cb_fun f src
|
| CFG_call f -> cb_fun f src
|
||||||
|
|
||||||
|
@ -85,7 +96,7 @@ module DisjunctiveIterable (D : DOMAIN) : ITERABLE = struct
|
||||||
| (ci, _)::q -> (ci, a.arc_parity)::q
|
| (ci, _)::q -> (ci, a.arc_parity)::q
|
||||||
|
|
||||||
let accumulate a dst_old dst_toacc =
|
let accumulate a dst_old dst_toacc =
|
||||||
Format.printf "[%i -> %i] accumulating...@ " a.arc_src.node_id a.arc_dst.node_id;
|
(*Format.printf "[%i -> %i] accumulating...@ " a.arc_src.node_id a.arc_dst.node_id;*)
|
||||||
let tounion = if a.arc_src.branch_node then
|
let tounion = if a.arc_src.branch_node then
|
||||||
let ml = SCRMap.to_list dst_toacc in
|
let ml = SCRMap.to_list dst_toacc in
|
||||||
let modlist = (List.map (fun (key,d) -> (tag_key a key, d)) ml) in
|
let modlist = (List.map (fun (key,d) -> (tag_key a key, d)) ml) in
|
||||||
|
@ -96,7 +107,7 @@ module DisjunctiveIterable (D : DOMAIN) : ITERABLE = struct
|
||||||
let ns = SCRMap.merge (fun _ d d' -> match d,d' with
|
let ns = SCRMap.merge (fun _ d d' -> match d,d' with
|
||||||
| None, None -> None
|
| None, None -> None
|
||||||
| Some d, None -> Some d (*just preserving old state*)
|
| Some d, None -> Some d (*just preserving old state*)
|
||||||
| None, Some d -> (flag := true; Format.printf "Unreached branch took !@ "; Some d)
|
| None, Some d -> (flag := true; (*Format.printf "Unreached branch took !@ ";*) Some d)
|
||||||
| Some d, Some d' -> (if D.subset d' d then (Some d) else
|
| Some d, Some d' -> (if D.subset d' d then (Some d) else
|
||||||
(flag := true; Some (acctor d d')))) dst_old tounion in
|
(flag := true; Some (acctor d d')))) dst_old tounion in
|
||||||
ns, !flag
|
ns, !flag
|
||||||
|
|
|
@ -46,6 +46,11 @@ module Iterator (I : ITERABLE) = struct
|
||||||
iterate (NodeSet.choose !func_dirty)
|
iterate (NodeSet.choose !func_dirty)
|
||||||
end in
|
end in
|
||||||
iterate f.func_entry;
|
iterate f.func_entry;
|
||||||
|
if f.func_name = "main" then
|
||||||
|
Format.printf "@[<v 0>main() analysis result@ %a@]@ "
|
||||||
|
(fun fmt l -> NodeMap.iter
|
||||||
|
(fun n x -> Format.fprintf fmt "<%i> %a@ " n.node_id I.print x) l) !func_state;
|
||||||
|
|
||||||
node_abst f.func_exit;
|
node_abst f.func_exit;
|
||||||
end
|
end
|
||||||
in
|
in
|
||||||
|
|
67
rapport.md
Normal file
67
rapport.md
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
# Analyseur statique
|
||||||
|
|
||||||
|
Le projet reprend et complète la base de code fournie. Nous avons implémenté
|
||||||
|
l'analyse disjonctive par chemin ainsi que le domaine de Karr.
|
||||||
|
|
||||||
|
Les tests de l'analyseur sont fournis avec les tests préexistants et selon leurs conventions. Ils sont intégrés
|
||||||
|
à `scripts/test.sh`. Figurent des tests quand aux support des fonctions, des congruences, de l'analyse
|
||||||
|
disjonctive.
|
||||||
|
|
||||||
|
## Considérations sur les domaines
|
||||||
|
|
||||||
|
### Des domaines nus
|
||||||
|
Que ce soit dans les opérations en avant ou en arrière, le comportement de "Bot"
|
||||||
|
(et dans une moindre mesure de "Top") dans un `VALUE_DOMAIN` ne dépend pas du domaine
|
||||||
|
implémenté. Afin d'aiser l'implémentation des divers `VALUE_DOMAIN` (constantes,intervalles,
|
||||||
|
congruences,signes) on a fait le choix d'implémenter un type de domaine `NAKED_VALUE_DOMAIN`,
|
||||||
|
dont la sémantique est celle d'un `VALUE_DOMAIN` à ceci près que l'on interdit "Bot" et "Top"
|
||||||
|
en entrée des fonctions (ce qui amène à changer un peu la signature, cf. domains/naked.ml).
|
||||||
|
Un foncteur est alors défini, AddTopBot, qui complète un `NAKED_VALUE_DOMAIN` en `VALUE_DOMAIN`.
|
||||||
|
|
||||||
|
Un problème qui apparait est qu'un `NAKED_VALUE_DOMAIN` peut avoir besoin d'exprimer le concept de
|
||||||
|
Top ou Bot, notamment dans les opérations à l'envers. Pour gérer cela, on utilise deux exceptions,
|
||||||
|
`NeedTop` et `Absurd`.
|
||||||
|
|
||||||
|
### Une signature plus expressive
|
||||||
|
|
||||||
|
Afin de faciliter l'écriture du domaine de Karr, la signature de `DOMAIN` change le type de init en
|
||||||
|
un `int -> t`; cela permet d'indiquer au domaine le nombre de variables prévu.
|
||||||
|
|
||||||
|
## L'itérateur
|
||||||
|
|
||||||
|
### Terminaison
|
||||||
|
Dans notre projet, lors de l'exécution, certains noeuds du CFG sont annotés comme des `widen_target` :
|
||||||
|
l'accumulation d'état abstrait se fait sur eux par des `widen` et non des `join`.
|
||||||
|
La terminaison est alors assurée dès lors que chaque boucle apparaissant dans le CFG contient au moins une
|
||||||
|
`widen_target`. Pour ce faire, on procède de deux manières :
|
||||||
|
- lors de la conversion de l'AST en CFG, les noeuds "en tête" de boucle (ceux suivant immédiatement la décision
|
||||||
|
de refaire un tour) sont annotés comme des `widen_target`;
|
||||||
|
- cela ne suffisant pas pour les boucles "implicites" formées via des `goto`, une passe de recherche de cycle
|
||||||
|
par BFS est ensuite effectuée sur chaque fonction; dès qu'elle trouve un cycle, elle note la tête du cycle comme
|
||||||
|
`widen_target`.
|
||||||
|
|
||||||
|
Ce deuxième mécanisme n'apportant aucune garantie sur la pertinence de la cible choisie, un avertissement est
|
||||||
|
émi lorsqu'il annote un noeud.
|
||||||
|
|
||||||
|
### Itérer dans des fonctions
|
||||||
|
Nous avons fait le choix, lors de l'analyse d'une fonction, de remettre à zéro les états des noeuds
|
||||||
|
internes à la fonction (excepté l'entrée). Cela permet à des appels différents à la même fonction de ne
|
||||||
|
pas créer d'imprécision à cause de `join` fortuits. Une telle approche pourrait se généraliser aux
|
||||||
|
fonctions récursives, mais il faudrait du support vis-à-vis du passage de paramètre, de valeur de retour
|
||||||
|
et la terminaison semble plus subtile à garantir.
|
||||||
|
|
||||||
|
### Les iterables
|
||||||
|
|
||||||
|
Initialement, l'algorithme de _worklist_ a été implémenté directement à l'aide des domaines.
|
||||||
|
Toutefois, adapter une telle structure à l'analyse disjonctive aurait nécessité de changer considérablement
|
||||||
|
la signature des domaines pour l'annoter des informations de branchement.
|
||||||
|
|
||||||
|
Ces subtilités ont été encapsulées dans un type de domaine `ITERABLE` (iterator/iterable.ml), dont les
|
||||||
|
instances sont en toute généralité des objets permettant d'effectuer l'algorithme de _worklist._
|
||||||
|
Deux fonctions des domaines vers les itérables sont alors fournis,`SimpleIterable` fournissant une analyse
|
||||||
|
naïve (disponible via l'option `-fno-disjunction`) dont les résultats sont plus lisibles et `DisjunctiveIterable`
|
||||||
|
fournissant une analyse disjonctive.
|
||||||
|
|
||||||
|
Dans la sortie correspondant à une analyse disjonctive, chaque noeud est associé à une fonction partielle de listes de paires entier booléen
|
||||||
|
vers des éléments du domaine. Une liste de paire (entier,booléen) correspond aux exécutions ayant rencontré exactement les conditionnelles indiquées
|
||||||
|
par leur `node_id` et ayant pris la branche indiquée par le booléen (au dernier passage).
|
Loading…
Reference in a new issue