package codex
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
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.whilelib/while_analysis.ml.html
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 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). *) (* *) (**************************************************************************) (** 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}. *) (* Tracelog output will be handly. *) module Log = Tracelog.Make(struct let category = "While_analysis" end) (* $MDX part-begin=make_terms *) module Terms = Terms.Builder.Make (Terms.Condition.ConditionCudd) (Terms.Relations.Equality) () (* $MDX part-end *) (* $MDX part-begin=make_sva *) module SVA: Single_value_abstraction.Sig.NUMERIC_ENUM = struct include Single_value_abstraction.Ival include Single_value_abstraction.Bitfield end (* $MDX part-end *) (* $MDX part-begin=make_nonrel *) module NonRelationalDomain = Codex.Domains.Term_based.Nonrelational.Make(Terms)(SVA) (* $MDX part-end *) (* $MDX part-begin=make_domain *) (* This is an instance of the numerical domain, of signature Domain_sig.Base. *) module Domain = Domains.Term_domain.Make(Terms)(NonRelationalDomain) (* $MDX part-end *) module Var = While_ast.Var module Store = Analysis_sva.State let store_pp ctx = Analysis_sva.map_pp (Domain.integer_pretty ctx) (* $MDX part-begin=state *) type state = { ctx: Domain.Context.t; store: Domain.integer Store.t } (* initial value at analysis start *) let initial_state() = { ctx = Domain.root_context (); store = Store.empty } (* $MDX part-end *) let pp fmt {ctx;store} = Format.fprintf fmt "{@[<v>ctx:@[%a@]@,store:%a}@]" Domain.context_pretty ctx (store_pp ctx) store (* $MDX part-begin=serialize *) let serialize ~widens state_a state_b = (* for all While variables that are not bound to the same Domain.integer *) Store.fold_on_nonequal_union (fun var i1 i2 (Domain.Context.Result(included, in_tuple, continue) as result) -> if i1 == i2 then result else (* check they are indeed bound to different integers *) (* If one of these variable is unbound in one branch, bind it to top *) 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 (* Create a new join variable using Domain.serialize_integer *) 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 -> (* Call the local continuation which will give us the new integer value for our variable *) let integer, out_tuple = local_continue ctx out_tuple in (* Call the global continuation which will build the rest of the store *) let store, out_tuple = continue ctx out_tuple in (* Add the new value to the store *) (Store.add var integer store, out_tuple)) ) state_a.store state_b.store (* Initial result: returned when all variables are the same: - inclusion is true - the serialize tuple is empty - the store is initialized to the left branch's store *) (Domain.Context.Result (true, Domain.Context.empty_tuple (), fun _ctx out -> state_a.store, out)) (* $MDX part-end *) (* $MDX part-begin=join *) 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 } (* $MDX part-end *) (* $MDX part-begin=join_opt *) let join_opt a b = match a, b with | None, x | x, None -> x | Some a, Some b -> Some (join a b) (* $MDX part-end *) (* $MDX part-begin=widen *) 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 (* $MDX part-end *) (* $MDX part-begin=aexp *) 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) (* $MDX part-end *) (* $MDX part-begin=bexp *) 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) (* $MDX part-end *) let pp_ret fmt = function | None -> Format.fprintf fmt "<bottom>" | Some x -> pp fmt x (* $MDX part-begin=stmt *) 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 (* bottom *) -> None | Some ctx, None (* true *) -> analyze_stmt (Some {state with ctx = ctx}) if_true | None, Some ctx (* false *) -> analyze_stmt (Some {state' with ctx = ctx}) if_false | Some ctx_true, Some ctx_false (* top *) -> 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 = (* update the state by one loop iteration: assume the condition and apply the body *) 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 (* fixpoint reached: exit loop, assume condition is false *) 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 (* $MDX part-end *)
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>