File: stackify.ml

package info (click to toggle)
why3 1.8.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 45,020 kB
  • sloc: xml: 185,443; ml: 111,224; ansic: 3,998; sh: 2,578; makefile: 2,568; java: 865; python: 720; javascript: 290; lisp: 205; pascal: 173
file content (152 lines) | stat: -rw-r--r-- 5,482 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
(********************************************************************)
(*                                                                  *)
(*  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