package codex

  1. Overview
  2. Docs
The Codex library for building static analysers based on abstract interpretation

Install

dune-project
 Dependency

Authors

Maintainers

Sources

1.0-rc4.tar.gz
md5=bc7266a140c6886add673ede90e335d3
sha512=8da42c0ff2c1098c5f9cb2b5b43b306faf7ac93b8f5ae00c176918cee761f249ff45b29309f31a05bbcf6312304f86a0d5a000eb3f1094d3d3c2b9b4c7f5c386

doc/src/codex.fixpoint/wto_iteration.ml.html

Source file wto_iteration.ml

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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
(**************************************************************************)
(*  This file is part of the Codex semantics library.                     *)
(*                                                                        *)
(*  Copyright (C) 2013-2025                                               *)
(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
(*         alternatives)                                                  *)
(*                                                                        *)
(*  you can redistribute it and/or modify it under the terms of the GNU   *)
(*  Lesser General Public License as published by the Free Software       *)
(*  Foundation, version 2.1.                                              *)
(*                                                                        *)
(*  It is distributed in the hope that it will be useful,                 *)
(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
(*  GNU Lesser General Public License for more details.                   *)
(*                                                                        *)
(*  See the GNU Lesser General Public License version 2.1                 *)
(*  for more details (enclosed in the file LICENSE).                      *)
(*                                                                        *)
(**************************************************************************)

module type ABSTRACTDOMAIN = sig

  (* An abstract domain is a lattice... *)
  type t
  val join: t -> t -> t
  val is_included: t -> t -> bool
  val bottom: t

  module ControlLocation:sig
    include Datatype_sig.S
    val to_int: t -> int                  (* Unique integer identifier. *)
    val preds: t -> t list                (* Predecessors to the block. *)
  end

  (* And a transfer function. Given a control location (cl) and an
     abstract state, returns the successor control locations and
     abstract states. *)
  val transfer: ControlLocation.t -> t -> (ControlLocation.t * t) list

  (* Pretty printer used for debugging. *)
  val pp: Format.formatter -> t -> unit

end

(* This module defines functions for fixpoint computation over Bourdoncle's weak topological ordering.
     The recursive and iterative strategies are defined by Bourdoncle (we only have a small trick
     in the iterative strategy, to avoid recomputations at the nodes at the beginning of a component
     if things are already propagated.) *)
module WTOFixpoint(L:ABSTRACTDOMAIN) = struct


  module CLMap = PatriciaTree.MakeMap(L.ControlLocation)

  (* We store the values on the edges between cls. *)
  module CLPair = Datatype_sig.Prod2(L.ControlLocation)(L.ControlLocation);;

  (* Note: maps are faster than hashtbls here. Okasakimaps and regular maps are almost equally fast, with a small advantage to Okasaki maps. *)
  (* module CLPairMap = Map.Make(CLPair);; *)
  module CLPairMap = PatriciaTree.MakeMap(struct
      include CLPair
      let to_int (a,b) =
        assert(L.ControlLocation.to_int a < 1 lsl 32);
        assert(L.ControlLocation.to_int b < 1 lsl 32);
        ((L.ControlLocation.to_int a) lsl 32 ) lor (L.ControlLocation.to_int b)
    end);;

  module CLPairHash = Hashtbl.Make(CLPair);;

  let reduce = function
    | [] -> L.bottom
    | (a::b) -> List.fold_left L.join a b;;

  (* Get the prestate. *)
  let pre init cl (map,_) =
    (* Printf.printf "Pre cl %d\n" cl.Cil_types.sid; *)
    let preds = L.ControlLocation.preds cl in
    let tentative =
      reduce  @@ List.map(fun pred ->
          try CLPairMap.find (pred,cl) map(* CLPairHash.find hashtbl (pred,cl) *)
          with Not_found -> L.bottom ) preds
    in
    (* Printf.printf "Preds size is %d\n" @@ List.length preds; *)
    (* Printf.printf "Init is %b\n" @@ CLMap.mem cl init; *)
    try L.join tentative @@ CLMap.find cl init
    with Not_found -> tentative
  ;;

  let update prestate node (edgemap,clmap) =
    let clmap = CLMap.add node prestate clmap in
    (* Self.feedback "Update cl %d %a %a\n" node.Cil_types.sid Cil_datatype.Cl.pretty node L.pp prestate; *)
    (* let l = pre init node acc in *)
    let result = L.transfer node prestate in
    let edgemap = List.fold_left (fun edgemap (cl,state) ->
        (* CLPairHash.replace hashtbl (node,cl) state; acc *)
        CLPairMap.add (node,cl) state edgemap
      ) edgemap result
    in (edgemap,clmap)
  ;;

  (* A counter to count the maximum number of iterations. *)
  let max_iterations = ref 0;;

  module RecursiveStrategy = struct

    let rec fixpoint_component count init acc = function
      | Wto.Node n ->
        let prestate = pre init n acc in
        update prestate n acc
      | Wto.Component(head,part) ->
        let head_prestate = pre init head acc in
        (* Note: if we don't pass acc here and start from the old acc for every loop,
           it works but is much slower. Check why and see the algorithm in the paper. *)
        let rec loop acc count head_prestate =
          (* Printf.printf "loop head %d %d\n" head.Cil_types.sid count; *)
          let acc = update head_prestate head acc in
          let acc = fixpoint_partition init acc part in
          let new_head_prestate = pre init head acc in
          if L.is_included new_head_prestate head_prestate
          then (max_iterations := max !max_iterations count; acc)
          else loop acc (count + 1) new_head_prestate
        in
        loop acc 1 head_prestate
    and fixpoint_partition init acc list =
      List.fold_left (fixpoint_component 0 init) acc list

  end

  module IterativeStrategy = struct

    let fixpoint_component count init acc c =


      (* We know that we will have to redo one iteration, so we just update. *)
      let rec loop_update_component c acc =
        (* Printf.printf "Loop update component\n"; *)
        match c with
        | Wto.Node n ->
          let prestate = pre init n acc in
          update prestate n acc
        | Wto.Component(head,part) ->
          let prestate = pre init head acc in
          let acc = update prestate head acc in
          loop_update_partition part acc
      and loop_update_partition part acc = match part with
        | [] -> acc
        | hd::tl ->
          let acc = loop_update_component hd acc
          in loop_update_partition tl acc
      in

      (* We don't know if we have to redo one iteration: we check and do not update
         as long as we can.  We return true while the fixpoint is reached. *)
      let rec loop_check_component c ((edgemap,clmap) as acc) =
        (* Printf.printf "Loop check component\n"; *)
        match c with
        | Wto.Node _ -> acc,true
        | Wto.Component(head,part) ->
          let old_prestate = CLMap.find head clmap in
          let new_prestate = pre init head acc in
          (* Self.feedback "inclusion test %a %a %a" Cil_datatype.Cl.pretty head *)
          (*   L.pp new_prestate L.pp old_prestate *)
          (* ; *)

          if L.is_included new_prestate old_prestate
          then loop_check_partition part acc
          else begin
            (*   Self.feedback "not included test %a %a %a" Cil_datatype.Cl.pretty head *)
            (*   L.pp new_prestate L.pp old_prestate *)
            (* ; *)
            let clmap = CLMap.add head new_prestate clmap in
            let acc = update new_prestate head (edgemap,clmap) in
            loop_update_partition part acc, false
          end
      and loop_check_partition part acc = match part with
        | [] -> acc,true
        | hd::tl ->
          let acc,cond = loop_check_component hd acc in
          if cond then loop_check_partition tl acc
          else loop_update_partition part acc, false
      in

      (* Top level iteration. *)
      match c with
      | Wto.Node n ->
        let prestate = pre init n acc in
        update prestate n acc
      | Wto.Component(head,part) as c ->
        (* We need at least two iterations, the second one to check. *)
        let acc = loop_update_component c acc in
        let rec loop numiter acc =
          (* Self.feedback "Doing the iteration %d %a\n" numiter Cil_datatype.Cl.pretty head; *)
          let acc, fp_reached = loop_check_component c acc in
          if fp_reached then acc
          else begin
            (* We only count the iterations that updated the analysis,
               not the ones that do only checks. So we update numiter
               here (fp_reached false => we did some updates) *)
            let numiter = numiter + 1 in
            max_iterations := max numiter !max_iterations;
            loop numiter acc
          end
        in loop 1 acc
    ;;



    let fixpoint_partition init acc list =
      List.fold_left (fixpoint_component 0 init) acc list
    ;;
  end

  let iteration_strategy = `iterative

  let fixpoint_partition = match iteration_strategy with
    | `iterative -> IterativeStrategy.fixpoint_partition
    | `recursive -> RecursiveStrategy.fixpoint_partition
  ;;

  let fixpoint_partition init c =
    let (edgemap,clmap) = fixpoint_partition init (CLPairMap.empty,CLMap.empty) c in
    clmap

end