mirror of
https://github.com/AdaCore/why3.git
synced 2026-02-12 12:34:55 -08:00
153 lines
5.4 KiB
OCaml
153 lines
5.4 KiB
OCaml
(********************************************************************)
|
|
(* *)
|
|
(* The Why3 Verification Platform / The Why3 Development Team *)
|
|
(* Copyright 2010-2025 -- Inria - CNRS - Paris-Saclay University *)
|
|
(* *)
|
|
(* This software is distributed under the terms of the GNU Lesser *)
|
|
(* General Public License version 2.1, with the special exception *)
|
|
(* on linking described in file LICENSE. *)
|
|
(********************************************************************)
|
|
|
|
open Why3
|
|
open Cfg_ast
|
|
open Ptree
|
|
|
|
(* Todo use labeled graph *)
|
|
module G = Graph.Imperative.Digraph.Concrete (struct
|
|
type t = label
|
|
|
|
let equal a b = a.id_str = b.id_str
|
|
let compare a b = String.compare a.id_str b.id_str
|
|
let hash a = Hashtbl.hash a.id_str
|
|
end)
|
|
|
|
module Dom = Graph.Dominator.Make (G)
|
|
module Topo = Graph.WeakTopological.Make (G)
|
|
|
|
let rec targets (t : cfg_term) : label list =
|
|
match t.cfg_term_desc with
|
|
| CFGgoto l -> [ l ]
|
|
| CFGswitch (_, brs) -> List.fold_left (fun acc (_, a) -> targets a @ acc) [] brs
|
|
| CFGreturn _ -> []
|
|
| CFGabsurd -> []
|
|
|
|
type labeled_block = label * block
|
|
type usage = Multi | One
|
|
|
|
type exp_tree =
|
|
| Scope of label * usage * exp_tree * exp_tree
|
|
| Loop of (loop_clause * ident option * Ptree.term * int option ref) list * exp_tree
|
|
| Block of labeled_block
|
|
|
|
let rec _print_exp_structure' exp =
|
|
match exp with
|
|
| Scope (lbl, _, tgt, exp) ->
|
|
Format.printf "Scope( %s = " lbl.id_str;
|
|
_print_exp_structure' tgt;
|
|
Format.printf " in ";
|
|
_print_exp_structure' exp;
|
|
Format.printf ")"
|
|
| Loop (_, exp) ->
|
|
Format.printf "Loop[ ";
|
|
_print_exp_structure' exp;
|
|
Format.printf "]"
|
|
| Block (l, _) -> Format.printf "Block(%s)" l.id_str
|
|
|
|
let graph_from_blocks (bl : (label * block) list) : G.t =
|
|
let g = G.create () in
|
|
List.iter
|
|
(fun (source, (_, t)) ->
|
|
let target_label = targets t in
|
|
G.add_vertex g source;
|
|
List.iter (fun target -> G.add_edge g source target) target_label)
|
|
bl;
|
|
g
|
|
|
|
exception NotReducible of (string * string)
|
|
exception NotConnected of G.V.t list
|
|
|
|
let () =
|
|
Exn_printer.register (fun fmt exn ->
|
|
match exn with
|
|
| NotReducible (n1, n2) ->
|
|
Format.fprintf fmt "CFG is not a reducible graph. The nodes %s %s belong to a cycle with multiple entries" n1
|
|
n2
|
|
| NotConnected s ->
|
|
Format.fprintf fmt
|
|
"CFG is not a connected graph. The following nodes are not reachable from the start block: ";
|
|
List.iter (fun el -> Format.printf "%s" el.id_str) s
|
|
| _ -> raise exn)
|
|
|
|
module Sint = Extset.Make (struct
|
|
type t = label
|
|
|
|
let compare a b = String.compare a.id_str b.id_str
|
|
end)
|
|
|
|
let _graph_is_reducible (g : G.t) (dom : G.V.t -> G.V.t -> bool) (entry : label) =
|
|
let visited = ref Sint.empty in
|
|
let to_visit = Stack.create () in
|
|
Stack.push entry to_visit;
|
|
|
|
while not (Stack.is_empty to_visit) do
|
|
let node = Stack.pop to_visit in
|
|
visited := Sint.add node !visited;
|
|
|
|
G.iter_succ
|
|
(fun succ ->
|
|
if not (Sint.mem succ !visited) then Stack.push succ to_visit
|
|
else if (* back-edge *)
|
|
not (dom succ node) then raise (NotReducible (succ.id_str, node.id_str)))
|
|
g node
|
|
done;
|
|
(* graph is connected *)
|
|
let unreached = G.fold_vertex (fun v u -> if not (Sint.mem v !visited) then Sint.add v u else u) g Sint.empty in
|
|
if not (Sint.is_empty unreached) then raise (NotConnected (Sint.elements unreached));
|
|
()
|
|
|
|
let rec entry e = match e with Block b -> b | Loop (_, h) -> entry h | Scope (_, _, _, h) -> entry h
|
|
(* unused | _ -> assert false *)
|
|
|
|
let mk_scope label usage tgt body = Scope (label, usage, tgt, body)
|
|
|
|
let rec treeify_from_components pred dom (prev : exp_tree option) blocks (wto : label Graph.WeakTopological.t) :
|
|
exp_tree =
|
|
Option.get
|
|
(Graph.WeakTopological.fold_left
|
|
(fun prev c ->
|
|
let e = treeify_component pred dom blocks c in
|
|
let lbl = fst (entry e) in
|
|
let forward_preds = List.filter (fun pred -> not (dom lbl pred)) (pred lbl) in
|
|
let usage = if List.length forward_preds > 1 then Multi else One in
|
|
match prev with Some prev -> Some (mk_scope lbl usage e prev) | None -> Some e)
|
|
prev wto)
|
|
|
|
and treeify_component pred dom blocks (wto : label Graph.WeakTopological.element) : exp_tree =
|
|
let open Graph.WeakTopological in
|
|
match wto with
|
|
| Vertex b -> Block (List.find (fun (l, _) -> l.id_str = b.id_str) blocks)
|
|
| Component (v, b) ->
|
|
let rec split_invariants = function
|
|
| { cfg_instr_desc = CFGinvariant is } :: xs ->
|
|
let invs, stmts = split_invariants xs in
|
|
(is @ invs, stmts)
|
|
| [] -> ([], [])
|
|
| a -> ([], a)
|
|
in
|
|
let l, (s, t) = List.find (fun (l, _) -> l.id_str = v.id_str) blocks in
|
|
let invariants, stmts = split_invariants s in
|
|
|
|
Loop (invariants, treeify_from_components pred dom (Some (Block (l, (stmts, t)))) blocks b)
|
|
|
|
and treeify pred dom cs = treeify_from_components pred dom None cs
|
|
|
|
let stackify (bl : labeled_block list) (entry : label) : exp_tree =
|
|
let g = graph_from_blocks bl in
|
|
let idom = Dom.compute_idom g entry in
|
|
let dom = Dom.idom_to_dom idom in
|
|
|
|
(* graph_is_reducible g dom entry; *)
|
|
let comps = Topo.recursive_scc g entry in
|
|
let t = treeify (G.pred g) dom bl comps in
|
|
t
|