AStat/rapport.md

68 lines
4.1 KiB
Markdown
Raw Normal View History

2024-06-09 18:11:53 +02:00
# 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).