Source file while_analysis.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
(** This module presents the implementation of a simple example analyzer for
{{!While_ast}the while language} using codex constructs. It is detailed in
the While tutorial's {!page-chapter4}. *)
module Log = Tracelog.Make(struct let category = "While_analysis" end)
module Terms = Terms.Builder.Make
(Terms.Condition.ConditionCudd)
(Terms.Relations.Equality)
()
module SVA: Single_value_abstraction.Sig.NUMERIC_ENUM = struct
include Single_value_abstraction.Ival
include Single_value_abstraction.Bitfield
end
module NonRelationalDomain = Codex.Domains.Term_based.Nonrelational.Make(Terms)(SVA)
module Domain = Domains.Term_domain.Make(Terms)(NonRelationalDomain)
module Var = While_ast.Var
module Store = Analysis_sva.State
let store_pp ctx = Analysis_sva.map_pp (Domain.integer_pretty ctx)
type state = {
ctx: Domain.Context.t;
store: Domain.integer Store.t
}
let initial_state() = { ctx = Domain.root_context (); store = Store.empty }
let pp fmt {ctx;store} =
Format.fprintf fmt "{@[<v>ctx:@[%a@]@,store:%a}@]"
Domain.context_pretty ctx (store_pp ctx) store
let serialize ~widens state_a state_b =
Store.fold_on_nonequal_union (fun var i1 i2 (Domain.Context.Result(included, in_tuple, continue) as result) ->
if i1 == i2 then result else
let i1 = match i1 with Some i1 -> i1 | None -> Domain.integer_empty state_a.ctx in
let i2 = match i2 with Some i2 -> i2 | None -> Domain.integer_empty state_b.ctx in
let Domain.Context.Result(included, in_tuple, local_continue) =
Domain.serialize_integer ~widens
state_a.ctx i1
state_b.ctx i2
(included, in_tuple) in
Domain.Context.Result(included, in_tuple, fun ctx out_tuple ->
let integer, out_tuple = local_continue ctx out_tuple in
let store, out_tuple = continue ctx out_tuple in
(Store.add var integer store, out_tuple))
)
state_a.store state_b.store
(Domain.Context.Result (true, Domain.Context.empty_tuple (), fun _ctx out -> state_a.store, out))
let join state_a state_b =
let Domain.Context.Result(included, in_tuple, continue) = serialize ~widens:false state_a state_b in
let ctx, out = Domain.typed_nondet2 state_a.ctx state_b.ctx in_tuple in
let store, _ = continue ctx out in
{ ctx; store }
let join_opt a b = match a, b with
| None, x | x, None -> x
| Some a, Some b -> Some (join a b)
let widen widening_id state_a state_b =
let Domain.Context.Result(included, in_tuple, continue) = serialize ~widens:true state_a state_b in
let ctx, included, out = Domain.widened_fixpoint_step
~widening_id ~previous:state_a.ctx ~next:state_b.ctx (included,in_tuple) in
let store, _ = continue ctx out in
{ ctx; store }, included
let rec aexp: state -> While_ast.aexp -> Domain.integer =
fun state exp -> match exp with
| Var v -> Store.find v state.store
| Int c -> Domain.Integer_Forward.iconst (Z.of_int c) state.ctx
| Add (e1, e2) ->
Domain.Integer_Forward.iadd state.ctx (aexp state e1) (aexp state e2)
| Sub (e1, e2) ->
Domain.Integer_Forward.isub state.ctx (aexp state e1) (aexp state e2)
| Mul (e1, e2) ->
Domain.Integer_Forward.imul state.ctx (aexp state e1) (aexp state e2)
let rec bexp: state -> While_ast.bexp -> Domain.boolean =
fun state exp -> match exp with
| True -> Domain.Boolean_Forward.true_ state.ctx
| False -> Domain.Boolean_Forward.false_ state.ctx
| Le (e1, e2) -> Domain.Integer_Forward.ile state.ctx (aexp state e1) (aexp state e2)
| Eq (e1, e2) -> Domain.Integer_Forward.ieq state.ctx (aexp state e1) (aexp state e2)
| And (e1, e2) -> Domain.Boolean_Forward.(&&) state.ctx (bexp state e1) (bexp state e2)
| Not e1 -> Domain.Boolean_Forward.not state.ctx (bexp state e1)
| Gt (e1, e2) -> Domain.Boolean_Forward.not state.ctx @@ Domain.Integer_Forward.ile state.ctx (aexp state e1) (aexp state e2)
let pp_ret fmt = function
| None -> Format.fprintf fmt "<bottom>"
| Some x -> pp fmt x
let copy { store; ctx } = { store; ctx=Domain.Context.copy ctx }
let (let*) = Option.bind
let rec analyze_stmt: state option -> While_ast.stmt -> state option =
fun state_opt stmt ->
let* state = state_opt in
Log.trace (fun p -> p "analyze_stmt %a" While_ast.pp_stmt stmt) ~pp_ret @@ fun () ->
match stmt with
| Skip -> state_opt
| Assign(var,exp) ->
let v = aexp state exp in
Some {state with store = Store.add var v state.store}
| Seq (c1,c2) ->
let state_opt' = analyze_stmt state_opt c1 in
analyze_stmt state_opt' c2
| If (cond, if_true, if_false) -> (
let bexp_cond = bexp state cond in
let state' = copy state in
match
Domain.assume state.ctx bexp_cond,
Domain.assume state'.ctx (Domain.Boolean_Forward.not state'.ctx bexp_cond)
with
| None, None -> None
| Some ctx, None -> analyze_stmt (Some {state with ctx = ctx}) if_true
| None, Some ctx -> analyze_stmt (Some {state' with ctx = ctx}) if_false
| Some ctx_true, Some ctx_false ->
join_opt
(analyze_stmt (Some {state with ctx = ctx_true}) if_true)
(analyze_stmt (Some {state' with ctx = ctx_false}) if_false))
| While (cond, body) ->
let widening_id = Domains.Sig.Widening_Id.fresh () in
let one_iteration state =
let* ctx = Domain.assume state.ctx (bexp state cond) in
analyze_stmt (Some {state with ctx}) body
in
let initial_state = copy state in
let rec loop head =
let* next_head = one_iteration head in
let next_head = join initial_state next_head in
let widened, included = widen widening_id head next_head in
if not included
then loop widened
else
let* ctx =
bexp next_head cond |>
Domain.Boolean_Forward.not next_head.ctx |>
Domain.assume next_head.ctx in
Some { next_head with ctx }
in loop state