diff --git a/ivette/src/dome/renderer/graph/diagram.tsx b/ivette/src/dome/renderer/graph/diagram.tsx index d594f647ca21f1aa19bdea0b29be528c7f48262b..5764a97e7313d3a15cc929d6405e1d16c253f900 100644 --- a/ivette/src/dome/renderer/graph/diagram.tsx +++ b/ivette/src/dome/renderer/graph/diagram.tsx @@ -349,7 +349,7 @@ class Builder { // --- Edge edge(e: Edge): void { const { line = 'solid', head = 'arrow', tail = 'none' } = e; - const tooltip = e.title ?? e.label ?? `${e.source} -> ${e.target}`; + const tooltip = e.title ?? e.label; if (e.aligned === true) this .print('{ rank=same; ') diff --git a/ivette/src/frama-c/plugins/region/memory.tsx b/ivette/src/frama-c/plugins/region/memory.tsx index 2b38fe1acbe532113a27f458bb0a43fe2a30c023..e04a8e56bf3380f062b9c63dd04b982b406fd7e4 100644 --- a/ivette/src/frama-c/plugins/region/memory.tsx +++ b/ivette/src/frama-c/plugins/region/memory.tsx @@ -54,7 +54,7 @@ function makeRecord( cells.push({ label, port }); }); if (offset !== sizeof) - cells.push(`#${sizeof-offset}b`); + cells.push(`#${sizeof - offset}b`); return cells; } @@ -70,30 +70,29 @@ function makeDiagram(regions: readonly Region.region[]): Diagram { const id = `n${r.node}`; // --- Color const color = - r.bytes ? 'red' : - r.pointed !== undefined - ? (r.writes ? 'orange' : 'yellow') - : (r.writes && r.reads) ? 'green' : - r.writes ? 'pink' : r.reads ? 'grey' : 'white'; + (!r.writes && !r.reads) ? undefined : + !r.typed ? 'red' : + r.pointed !== undefined + ? (r.writes ? 'orange' : 'yellow') + : (r.writes && r.reads) ? 'green' : + r.writes ? 'pink' : 'grey'; // --- Shape const font = r.ranges.length > 0 ? 'mono' : 'sans'; const cells = makeRecord(edges, id, r.sizeof, r.ranges); const shape = cells.length > 0 ? cells : undefined; nodes.push({ id, font, color, label: r.label, title: r.title, shape }); // --- Labels - const L: Dot.Node = - { id: '', shape: 'note', font: 'mono' }; + const L: Dot.Node = { id: '', shape: 'note', font: 'mono' }; r.labels.forEach(a => { const lid = `L${a}`; nodes.push({ ...L, id: lid, label: `${a}:` }); edges.push({ source: lid, target: id, aligned: true, - headAnchor: 'n', head: 'none', color: 'grey' + headAnchor: 's', head: 'none', color: 'grey' }); }); // --- Roots - const R: Dot.Node = - { id: '', shape: 'cds', font: 'mono' }; + const R: Dot.Node = { id: '', shape: 'cds', font: 'mono' }; r.roots.forEach(x => { const xid = `X${x}`; nodes.push({ ...R, id: xid, label: x }); diff --git a/src/plugins/region/Region.ml b/src/plugins/region/Region.ml index c440b8e4965381bed61e9c1ad95cd270d5780593..7a88530125422d7b9bd618f1e9c4b2b762d4442d 100644 --- a/src/plugins/region/Region.ml +++ b/src/plugins/region/Region.ml @@ -24,10 +24,31 @@ (* --- Region Analysis API --- *) (* -------------------------------------------------------------------------- *) -let get_map kf = (Code.domain kf).map -let get_id m n = Memory.id @@ Memory.node m n -let get_node m id = Memory.node m @@ Memory.forge id - -include Memory - -let iter = Memory.iter_node +type map = Memory.map +type node = Memory.node +let map kf = (Analysis.get kf).map +let id n = Memory.id n +let uid m n = Memory.id @@ Memory.node m n +let iter = Memory.iter +let find m id = Memory.node m @@ Memory.forge id +let node = Memory.node +let nodes = Memory.nodes +let equal = Memory.equal +let included = Memory.included +let separated = Memory.separated +let singleton = Memory.singleton +let size = Memory.size +let roots = Memory.roots +let labels = Memory.labels +let reads = Memory.reads +let writes = Memory.writes +let shifts = Memory.shifts +let typed = Memory.typed +let parents m n = Memory.nodes m @@ Memory.parents m n +let points_to m n = Option.map (Memory.node m) @@ Memory.points_to m n +let pointed_by m n = Memory.nodes m @@ Memory.pointed_by m n +let lval m l = Memory.node m @@ Memory.lval m l +let exp m e = Option.map (Memory.node m) @@ Memory.exp m e +let cvar = Memory.cvar +let field = Memory.field +let index = Memory.index diff --git a/src/plugins/region/Region.mli b/src/plugins/region/Region.mli index 68c04c93ee0a56e85db49f7521231c011df0da71..46e3997e0d8e2c8d3e72a5c19dcfbfafa1c6fb85 100644 --- a/src/plugins/region/Region.mli +++ b/src/plugins/region/Region.mli @@ -20,7 +20,7 @@ (* *) (**************************************************************************) -(** Interface for the Region plug-in. +(** {1 Interface for the Region plug-in} Each function is assigned a region map. Areas in the map represents l-values or, more generally, class of nodes. Regions are classes equivalences of @@ -29,7 +29,7 @@ Regions can be subdivided into sub-regions. Hence, given two regions, either one is included into the other, or they are separated. Hence, given two l-values, if their associated regions are separated, then they can {i not} - be aliased. + be aliased. Nodes are elementary elements of a region map. Variables maps to nodes, and one can move to one node to another by struct or union field or array @@ -38,44 +38,101 @@ a region. *) - open Cil_types +(** {2 Memory Maps and Nodes} *) + type map type node -val get_map : kernel_function -> map -val get_id : map -> node -> int -val get_node : map -> int -> node +val map : kernel_function -> map + +(** Not normalized. + Two nodes in the same equivalence class may have different identifiers. *) +val id : node -> int + +(** Unique id of normalized node. + This can be considered the unique identifier of the region equivalence + class. *) +val uid : map -> node -> int + +(** Returns a normalized node. + @raises Not_found if not a valid node identifier. *) +val find : map -> int -> node -(** Normalize node *) +(** Normalized node. + The returned node is the representative of the region equivalence class. + There is one unique representative per equivalence class. *) val node : map -> node -> node -(** Normalize set of nodes *) +(** Normalized list of nodes (normalized, uniques, sorted by id) *) val nodes : map -> node list -> node list -(** Nodes are in the same region *) +(** {2 Region Properties} + + All functions in this section provide normalized nodes + and shall never raise exception. *) + +val points_to : map -> node -> node option +val pointed_by : map -> node -> node list + +val size : map -> node -> int +val parents : map -> node -> node list +val roots : map -> node -> varinfo list +val labels: map -> node -> string list +val reads : map -> node -> typ list +val writes : map -> node -> typ list +val shifts : map -> node -> typ list + +val typed : map -> node -> typ option +(** Full-sized cells with unique type access *) + +val iter : map -> (node -> unit) -> unit + +(** {2 Alias Analysis} *) + +(** [equal m a b] checks if nodes [a] and [b] are in the same region. *) val equal : map -> node -> node -> bool -(** First belongs to last *) +(** [include m a b] checks if region [a] is a sub-region of [b] in map [m]. *) val included : map -> node -> node -> bool -(** Nodes can not be aliased *) +(** [separated m a b] checks if region [a] and region [b] are disjoint. + Disjoints regions [a] and [b] have the following properties: + - [a] is {i not} a sub-region of [b]; + - [b] is {i not} a sub-region of [a]; + - two l-values respectively localized in [a] and [b] + can {i never} be aliased. +*) val separated : map -> node -> node -> bool -val cvar : map -> varinfo -> node -val field : map -> node -> fieldinfo -> node -val index : map -> node -> typ -> node +(** [singleton m a] returns [true] when node [a] is guaranteed to have only + one single address in its equivalence class. *) +val singleton : map -> node -> bool + +(** [lval m lv] is region where the address of [l] lives in. + The returned region is normalized. + @raises Not_found if the l-value is not localized in the map *) val lval : map -> lval -> node + +(** [exp m e] is the domain of values that can computed by expression [e]. + The domain is [Some r] is [e] has a pointer type and any pointer computed by + [e] lives in region [r]. The domain is [None] if [e] has a non-pointer + scalar or compound type. + @raises Not_found if the l-value is not localized in the map +*) val exp : map -> exp -> node option -val points_to : map -> node -> node option -val pointed_by : map -> node -> node list -val parents : map -> node -> node list -val roots : map -> node -> varinfo list +(** {2 Low-level Navigation through Memory Maps} -val iter : map -> (node -> unit) -> unit (** Iter over all regions *) + For optimized access, all the fonctions in this section return + unnormalized nodes and may raise [Not_found] for not localized routes. *) -val reads : map -> node -> typ list -val writes : map -> node -> typ list -val shifts : map -> node -> typ list +(** Unormalized. @raises Not_found *) +val cvar : map -> varinfo -> node + +(** Unormalized. @raises Not_found *) +val field : map -> node -> fieldinfo -> node + +(** Unormalized. @raises Not_found *) +val index : map -> node -> typ -> node diff --git a/src/plugins/region/analysis.ml b/src/plugins/region/analysis.ml index b64c6571bc83fbfa648159d1701a12bf6e597a33..8fd275216e39e9ae08e3a0db8463fb1056fbf4e6 100644 --- a/src/plugins/region/analysis.ml +++ b/src/plugins/region/analysis.ml @@ -56,15 +56,6 @@ let get kf = Options.feedback ~ontty:`Transient "Function %a…" Kernel_function.pretty kf ; let domain = Code.domain kf in STATE.add kf domain ; - Options.result "@[<v 2>Function %a:%t@]@." - Kernel_function.pretty kf - begin fun fmt -> - Memory.iter domain.map - begin fun r -> - Format.pp_print_newline fmt () ; - Memory.pp_region fmt r ; - end - end ; domain let compute kf = ignore @@ get kf diff --git a/src/plugins/region/code.ml b/src/plugins/region/code.ml index 858b1a02cda82c4aa449c73ace700e7289e2da88..478264d7ca534fdce3033c5e537688096de89dcc 100644 --- a/src/plugins/region/code.ml +++ b/src/plugins/region/code.ml @@ -51,9 +51,8 @@ and add_loffset (m:map) (s:stmt) (r:node) (ty:typ)= function | Field(fd,ofs) -> add_loffset m s (add_field m r fd) fd.ftype ofs | Index(_,ofs) -> - let elt,size = Cil.typeOf_array_elem_size ty in - let n = Z.to_int @@ Option.get size in - add_loffset m s (add_index m r elt n) elt ofs + let elt = Cil.typeOf_array_elem ty in + add_loffset m s (add_index m r elt) elt ofs and add_value m s e = ignore (add_exp m s e) @@ -66,14 +65,14 @@ and add_exp (m: map) (s:stmt) (e:exp) : scalar = | Lval lv -> let rv = add_lval m s lv in - Memory.read m rv (Lval(s,lv)) ; + Memory.add_read m rv (Lval(s,lv)) ; let ptr = Memory.add_value m rv @@ Cil.typeOfLval lv in { from = Some rv ; ptr } | BinOp((PlusPI|MinusPI),p,k,_) -> add_value m s k ; let v = add_exp m s p in - Option.iter (fun r -> Memory.shift m r (Exp(s,e))) v.from ; v + Option.iter (fun r -> Memory.add_shift m r (Exp(s,e))) v.from ; v | UnOp(_,e,_) -> add_value m s e ; integral @@ -102,7 +101,7 @@ let rec add_init (m:map) (s:stmt) (acs:Access.acs) (lv:lval) (iv:init) = | SingleInit e -> let r = add_lval m s lv in - Memory.write m r acs ; + Memory.add_write m r acs ; Option.iter (Memory.add_points_to m r) (add_exp m s e).ptr | CompoundInit(_,fvs) -> @@ -123,14 +122,14 @@ let add_instr (m:map) (s:stmt) (instr:instr) = | Set(lv, { enode = Lval exp }, _) -> let l = add_lval m s lv in let r = add_lval m s exp in - Memory.read m r (Lval(s,exp)) ; - Memory.write m l (Lval(s,lv)) ; + Memory.add_read m r (Lval(s,exp)) ; + Memory.add_write m l (Lval(s,lv)) ; Memory.merge_copy m ~l ~r | Set(lv,e,_) -> let r = add_lval m s lv in let v = add_exp m s e in - Memory.write m r (Lval(s,lv)) ; + Memory.add_write m r (Lval(s,lv)) ; Option.iter (Memory.add_points_to m r) v.ptr | Local_init(x,AssignInit iv,_) -> @@ -143,7 +142,7 @@ let add_instr (m:map) (s:stmt) (instr:instr) = Option.iter (fun lv -> let r = add_lval m s lv in - Memory.write m r (Lval(s,lv)) + Memory.add_write m r (Lval(s,lv)) ) lr ; Options.warning ~source:(fst @@ Stmt.loc s) "Incomplete call analysis" diff --git a/src/plugins/region/logic.ml b/src/plugins/region/logic.ml index df43142c587fe4fae7664f149f2545712005c2b0..8c79655d0306b0987692e4d4426f4564caf97d0d 100644 --- a/src/plugins/region/logic.ml +++ b/src/plugins/region/logic.ml @@ -27,7 +27,7 @@ let rec add_lval (m:map) (p:path): node = match p.step with | Var x -> add_root m x | Field(lv,fd) -> Memory.add_field m (add_lval m lv) fd - | Index(lv,n) -> Memory.add_index m (add_lval m lv) lv.typ n + | Index(lv,_) -> Memory.add_index m (add_lval m lv) lv.typ | Star e | Cast(_,e) -> add_pointer m e | Shift _ | AddrOf _ -> Options.error ~source:(fst p.loc) diff --git a/src/plugins/region/memory.ml b/src/plugins/region/memory.ml index 2e500375c1477b1120217c34329a90e3a89e4960..df25c23811dbaa1f276f571d3571172fefdb3c06 100644 --- a/src/plugins/region/memory.ml +++ b/src/plugins/region/memory.ml @@ -40,6 +40,7 @@ and layout = | Blob | Cell of int * node option | Compound of int * Fields.domain * node Ranges.t + (* must only contain strict sub-ranges *) and chunk = { cparents: node list ; @@ -66,11 +67,11 @@ type map = { (* -------------------------------------------------------------------------- *) let sizeof = function Blob -> 0 | Cell(s,_) | Compound(s,_,_) -> s -let ranges = function Blob | Cell _ -> [] | Compound(_,_,R rs) -> rs -let fields = function Blob | Cell _ -> Fields.empty | Compound(_,fds,_) -> fds -let pointed = function Blob | Compound _ -> None | Cell(_,p) -> p +let cranges = function Blob | Cell _ -> [] | Compound(_,_,R rs) -> rs +let cfields = function Blob | Cell _ -> Fields.empty | Compound(_,fds,_) -> fds +let cpointed = function Blob | Compound _ -> None | Cell(_,p) -> p -let types (m : chunk) : typ list = +let ctypes (m : chunk) : typ list = let pool = ref Typ.Set.empty in let add acs = pool := Typ.Set.add (Cil.unrollType @@ Access.typeof acs) !pool in @@ -79,7 +80,7 @@ let types (m : chunk) : typ list = Typ.Set.elements !pool let failwith_locked m fn = - if m.locked then raise (Invalid_argument fn) + if m.locked then raise (Invalid_argument (fn ^ ": locked")) let lock m = m.locked <- true let unlock m = m.locked <- false @@ -111,7 +112,7 @@ let pp_chunk fmt (n: node) (m: chunk) = let acs r s = if Access.Set.is_empty s then '-' else r in Format.fprintf fmt "@[<hov 2>%a: %c%c%c" pp_node n (acs 'R' m.creads) (acs 'W' m.cwrites) (acs 'A' m.cshifts) ; - List.iter (Format.fprintf fmt "@ (%a)" Typ.pretty) (types m) ; + List.iter (Format.fprintf fmt "@ (%a)" Typ.pretty) (ctypes m) ; Lset.iter (Format.fprintf fmt "@ %s:") m.clabels ; Vset.iter (Format.fprintf fmt "@ %a" Varinfo.pretty) m.croots ; if Options.debug_atleast 1 then @@ -179,29 +180,17 @@ let update (m: map) (n: node) (f: chunk -> chunk) = (* --- Chunk Constructors --- *) (* -------------------------------------------------------------------------- *) -let new_chunk (m: map) ?(size=0) ?ptr ?pointed () = +let new_chunk (m: map) ?parent ?(size=0) ?ptr ?pointed () = failwith_locked m "Region.Memory.new_chunk" ; let clayout = match ptr with | None -> if size = 0 then Blob else Cell(size,None) - | Some _ -> Cell(Ranges.gcd size (Cil.bitsSizeOf Cil_const.voidPtrType), ptr) + | Some _ -> + Cell(Ranges.gcd size (Cil.bitsSizeOf Cil_const.voidPtrType), ptr) in - let cpointed = - match pointed with - | None -> [] - | Some ptr -> [ptr] - in - Ufind.make m.store { empty with clayout ; cpointed } - -let new_range (m: map) ?(fields=Fields.empty) ~size ~offset ~length data : node = - failwith_locked m "Region.Memory.new_range" ; - let last = offset + length in - if not (0 <= offset && offset < last && last <= size) then - raise (Invalid_argument "Region.Memory.add_range") ; - let ranges = Ranges.singleton { offset ; length ; data } in - let clayout = Compound(size, fields, ranges) in - let n = Ufind.make m.store { empty with clayout } in - update m data (fun r -> { r with cparents = nodes m @@ n :: r.cparents }) ; n + let cparents = match parent with None -> [] | Some root -> [root] in + let cpointed = match pointed with None -> [] | Some ptr -> [ptr] in + Ufind.make m.store { empty with clayout ; cpointed ; cparents } let add_root (m: map) v = try Vmap.find v m.roots with Not_found -> @@ -221,173 +210,49 @@ let add_label (m: map) a = (* --- Iterator --- *) (* -------------------------------------------------------------------------- *) -type range = { - label: string ; (* pretty printed fields *) - offset: int ; - length: int ; - cells: int ; - data: node ; -} - -type region = { - node: node ; - parents: node list ; - roots: varinfo list ; - labels: string list ; - types: typ list ; - fields: Fields.domain ; - reads: Access.acs list ; - writes: Access.acs list ; - shifts: Access.acs list ; - sizeof: int ; - ranges: range list ; - pointed: node option ; -} - -let pp_cells fmt = function - | 1 -> () - | 0 -> Format.fprintf fmt "[…]" - | n -> Format.fprintf fmt "[%d]" n - -type slice = - | Padding of int - | Range of range - -let pad p q s = - let n = q - p in - if n > 0 then Padding n :: s else s - -let rec span k s = function - | [] -> pad k s [] - | r::rs -> pad k r.offset @@ Range r :: span (r.offset + r.length) s rs - -let pp_slice fields fmt = function - | Padding n -> - Format.fprintf fmt "@ %a;" Fields.pp_bits n - | Range r -> - Format.fprintf fmt "@ %t: %a%a;" - (Fields.pslice ~fields ~offset:r.offset ~length:r.length) - pp_node r.data - pp_cells r.cells - -let pp_range fmt (r: range) = - Format.fprintf fmt "@ %d..%d: %a%a;" - r.offset (r.offset + r.length) pp_node r.data pp_cells r.cells - -let pp_region fmt (m: region) = - begin - let acs r s = if s = [] then '-' else r in - Format.fprintf fmt "@[<hov 2>%a: %c%c%c" - pp_node m.node - (acs 'R' m.reads) (acs 'W' m.writes) (acs 'A' m.shifts) ; - List.iter (Format.fprintf fmt "@ %s:") m.labels ; - List.iter (Format.fprintf fmt "@ %a" Varinfo.pretty) m.roots ; - List.iter (Format.fprintf fmt "@ (%a)" Typ.pretty) m.types ; - Format.fprintf fmt "@ %db" m.sizeof ; - Option.iter (Format.fprintf fmt "@ (*%a)" pp_node) m.pointed ; - if m.ranges <> [] then - begin - Format.fprintf fmt "@ @[<hv 0>@[<hv 2>{" ; - if Options.debug_atleast 1 then - List.iter (pp_range fmt) m.ranges - else - List.iter (pp_slice m.fields fmt) (span 0 m.sizeof m.ranges) ; - Format.fprintf fmt "@]@ }@]" ; - end ; - if Options.debug_atleast 1 then - begin - List.iter (Format.fprintf fmt "@ R:%a" Access.pretty) m.reads ; - List.iter (Format.fprintf fmt "@ W:%a" Access.pretty) m.writes ; - List.iter (Format.fprintf fmt "@ A:%a" Access.pretty) m.shifts ; - end ; - Format.fprintf fmt " ;@]" ; - end - -let make_range (m: map) fields Ranges.{ length ; offset ; data } : range = - let s = sizeof (get m data).clayout in - let p = Fields.pslice ~fields ~offset ~length in - { - label = Format.asprintf "%t" p ; - offset ; length ; - cells = if s = 0 then 0 else length / s ; - data = node m data ; - } - -let make_region (m: map) (n: node) (r: chunk) : region = - let fields = fields r.clayout in - { - node = n ; - parents = nodes m r.cparents ; - roots = Vset.elements r.croots ; - labels = Lset.elements r.clabels ; - reads = Access.Set.elements r.creads ; - writes = Access.Set.elements r.cwrites ; - shifts = Access.Set.elements r.cshifts ; - types = types r ; - sizeof = sizeof r.clayout ; - fields ; ranges = List.map (make_range m fields) @@ ranges r.clayout ; - pointed = Option.map (node m) (pointed r.clayout) ; - } - -let region map n = make_region map n (get map n) - -let rec walk h m f n = +let rec walk h m (f: node -> unit) n = let n = Ufind.find m.store n in let id = Store.id n in try Hashtbl.find h id with Not_found -> Hashtbl.add h id () ; + f n ; let r = Ufind.get m.store n in - f (make_region m n r) ; match r.clayout with | Blob -> () | Cell(_,p) -> Option.iter (walk h m f) p | Compound(_,_,rg) -> Ranges.iter (walk h m f) rg -let iter (m:map) (f: region -> unit) = +let iter (m:map) (f: node -> unit) = let h = Hashtbl.create 0 in Vmap.iter (fun _x n -> walk h m f n) m.roots -let rec walk_node h m (f: node -> unit) n = - let n = Ufind.find m.store n in - let id = Store.id n in - try Hashtbl.find h id with Not_found -> - Hashtbl.add h id () ; - f n ; - let r = Ufind.get m.store n in - match r.clayout with - | Blob -> () - | Cell(_,p) -> Option.iter (walk_node h m f) p - | Compound(_,_,rg) -> Ranges.iter (walk_node h m f) rg - -let iter_node (m:map) (f: node -> unit) = - let h = Hashtbl.create 0 in - Vmap.iter (fun _x n -> walk_node h m f n) m.roots - -let regions map = - let pool = ref [] in - iter map (fun r -> pool := r :: !pool) ; - List.rev !pool - +let size (m: map) (r: node) = + sizeof (Ufind.get m.store r).clayout let parents (m: map) (r: node) = - let chunk = Ufind.get m.store r in - nodes m chunk.cparents + nodes m (Ufind.get m.store r).cparents let roots (m: map) (r: node) = - let chunk = Ufind.get m.store r in Vset.elements chunk.croots + Vset.elements (Ufind.get m.store r).croots + +let labels (m: map) (r: node) = + Lset.elements (Ufind.get m.store r).clabels (* -------------------------------------------------------------------------- *) (* --- Merge --- *) (* -------------------------------------------------------------------------- *) type queue = (node * node) Queue.t +type cell = { mutable size : int ; mutable ptr : node option } +let new_cell ?(size=0) ?ptr () = { size ; ptr } +let cell_layout { size ; ptr } = + if size = 0 && ptr = None then Blob else Cell(size,ptr) -let singleton ~size = function - | None -> Ranges.empty - | Some r -> Ranges.range ~length:size r +let merge_push (m: map) (q: queue) (a: node) (b: node) : unit = + if not @@ Ufind.eq m.store a b then Queue.push (a,b) q let merge_node (m: map) (q: queue) (a: node) (b: node) : node = - if not @@ Ufind.eq m.store a b then Queue.push (a,b) q ; + merge_push m q a b ; Ufind.find m.store (min a b) let merge_opt (m: map) (q: queue) @@ -396,6 +261,16 @@ let merge_opt (m: map) (q: queue) | None, p | p, None -> p | Some pa, Some pb -> Some (merge_node m q pa pb) +let merge_cell (m:map) (q:queue) cell root r = + let node = Ufind.get m.store r in + let s = sizeof node.clayout in + let p = cpointed node.clayout in + begin + merge_push m q root r ; + cell.size <- Ranges.gcd cell.size s ; + cell.ptr <- merge_opt m q cell.ptr p ; + end + let merge_range (m: map) (q: queue) (ra : rg) (rb : rg) : node = let na = ra.data in let nb = rb.data in @@ -410,41 +285,42 @@ let merge_range (m: map) (q: queue) (ra : rg) (rb : rg) : node = if size = sa && size = sb then data else merge_node m q (new_chunk m ~size ()) data -let merge_ranges (m: map) (q: queue) +let merge_ranges (m: map) (q: queue) (root: node) (sa : int) (fa : Fields.domain) (wa : node Ranges.t) (sb : int) (fb : Fields.domain) (wb : node Ranges.t) : layout = - let fields = Fields.union fa fb in if sa = sb then - Compound(sa, fields, Ranges.merge (merge_range m q) wa wb) + match Ranges.merge (merge_range m q) wa wb with + | R [{ offset = 0 ; length ; data }] when length = sa -> + merge_push m q root data ; Cell(sa,None) + | ranges -> + let fields = Fields.union fa fb in + Compound(sa, fields, ranges) else let size = Ranges.gcd sa sb in - let ra = Ranges.squash (merge_node m q) wa in - let rb = Ranges.squash (merge_node m q) wb in - Compound(size, fields, singleton ~size @@ merge_opt m q ra rb) + let cell = new_cell ~size () in + Ranges.iter (merge_cell m q cell root) wa ; + Ranges.iter (merge_cell m q cell root) wb ; + cell_layout cell -let merge_layout (m: map) (q: queue) (a : layout) (b : layout) : layout = +let merge_layout (m:map) (q:queue) (root:node) (a:layout) (b:layout) : layout = match a, b with | Blob, c | c, Blob -> c | Cell(sa,pa) , Cell(sb,pb) -> Cell(Ranges.gcd sa sb, merge_opt m q pa pb) | Compound(sa,fa,wa), Compound(sb,fb,wb) -> - merge_ranges m q sa fa wa sb fb wb + merge_ranges m q root sa fa wa sb fb wb - | Compound(sr,fr,wr), Cell(sx,None) - | Cell(sx,None), Compound(sr,fr,wr) -> + | Compound(sr,_,wr), Cell(sx,ptr) + | Cell(sx,ptr), Compound(sr,_,wr) -> let size = Ranges.gcd sx sr in - Compound(size, fr, singleton ~size @@ Ranges.squash (merge_node m q) wr) - - | Compound(sr,fr,wr), Cell(sx,Some ptr) - | Cell(sx,Some ptr), Compound(sr,fr,wr) -> - let rp = new_chunk m ~size:sx ~ptr () in - let fx = Fields.empty in - let wx = Ranges.range ~length:sx rp in - merge_ranges m q sx fx wx sr fr wr + let cell = new_cell ~size ?ptr () in + Ranges.iter (merge_cell m q cell root) wr ; + cell_layout cell -let merge_region (m: map) (q: queue) (a : chunk) (b : chunk) : chunk = { +let merge_chunk (m: map) (q:queue) (root:node) + (a : chunk) (b : chunk) : chunk = { cparents = nodes m @@ Store.bag a.cparents b.cparents ; cpointed = nodes m @@ Store.bag a.cpointed b.cpointed ; clabels = Lset.union a.clabels b.clabels ; @@ -452,16 +328,16 @@ let merge_region (m: map) (q: queue) (a : chunk) (b : chunk) : chunk = { creads = Access.Set.union a.creads b.creads ; cwrites = Access.Set.union a.cwrites b.cwrites ; cshifts = Access.Set.union a.cshifts b.cshifts ; - clayout = merge_layout m q a.clayout b.clayout ; + clayout = merge_layout m q root a.clayout b.clayout ; } let do_merge (m: map) (q: queue) (a: node) (b: node): unit = begin - let ra = Ufind.get m.store a in - let rb = Ufind.get m.store b in - let rx = Ufind.union m.store a b in - let rc = merge_region m q ra rb in - Ufind.set m.store rx rc ; + let ca = Ufind.get m.store a in + let cb = Ufind.get m.store b in + let rt = Ufind.union m.store a b in + let ck = merge_chunk m q rt ca cb in + Ufind.set m.store rt ck ; end let merge_all (m:map) = function @@ -488,26 +364,27 @@ let merge_copy (m: map) ~(l: node) ~(r: node) : unit = let add_field (m:map) (r:node) (fd:fieldinfo) : node = let ci = fd.fcomp in - if ci.cstruct then + if not ci.cstruct then r else let size = Cil.bitsSizeOf (TComp(ci,[])) in let offset, length = Cil.fieldBitsOffset fd in - let rf = new_chunk m () in - let fields = Fields.singleton fd in - let rc = new_range m ~fields ~size ~offset ~length rf in - ignore @@ merge m r rc ; rf - else r - -let add_index (m:map) (r:node) (ty:typ) (n:int) : node = - let s = Cil.bitsSizeOf ty * n in - let re = new_chunk m () in - let rc = new_range m ~size:s ~offset:0 ~length:s re in - ignore @@ merge m r rc ; re + if offset = 0 && size = length then r else + let data = new_chunk m ~parent:r () in + let ranges = Ranges.singleton { offset ; length ; data } in + let fields = Fields.singleton fd in + let clayout = Compound(size,fields,ranges) in + let nc = Ufind.make m.store { empty with clayout } in + merge m r nc ; data + +let add_index (m:map) (r:node) (ty:typ) : node = + let size = Cil.bitsSizeOf ty in + let re = new_chunk m ~size () in + merge m r re ; re let add_points_to (m: map) (a: node) (b : node) = begin failwith_locked m "Region.Memory.points_to" ; - ignore @@ merge m a @@ new_chunk m ~ptr:b () ; - ignore @@ merge m b @@ new_chunk m ~pointed:a () ; + merge m a @@ new_chunk m ~ptr:b () ; + merge m b @@ new_chunk m ~pointed:a () ; end let add_value (m:map) (rv:node) (ty:typ) : node option = @@ -515,7 +392,7 @@ let add_value (m:map) (rv:node) (ty:typ) : node option = begin failwith_locked m "Region.Memory.add_value" ; let rp = new_chunk m ~pointed:rv () in - ignore @@ merge m rv @@ new_chunk m ~ptr:rp () ; + merge m rv @@ new_chunk m ~ptr:rp () ; Some rp end else @@ -530,23 +407,23 @@ let sized (m:map) (a:node) (ty: typ) = let size = Ranges.gcd sr (Cil.bitsSizeOf ty) in if sr <> size then ignore (merge m a (new_chunk m ~size ())) -let read (m: map) (a: node) from = +let add_read (m: map) (a: node) acs = failwith_locked m "Region.Memory.read" ; let r = get m a in - Ufind.set m.store a { r with creads = Access.Set.add from r.creads } ; - sized m a (Access.typeof from) + Ufind.set m.store a { r with creads = Access.Set.add acs r.creads } ; + sized m a (Access.typeof acs) -let write (m: map) (a: node) from = +let add_write (m: map) (a: node) acs = failwith_locked m "Region.Memory.write" ; let r = get m a in - Ufind.set m.store a { r with cwrites = Access.Set.add from r.cwrites } ; - sized m a (Access.typeof from) + Ufind.set m.store a { r with cwrites = Access.Set.add acs r.cwrites } ; + sized m a (Access.typeof acs) -let shift (m: map) (a: node) from = +let add_shift (m: map) (a: node) acs = failwith_locked m "Region.Memory.shift" ; let r = get m a in - Ufind.set m.store a { r with cshifts = Access.Set.add from r.cshifts } ; - sized m a (Access.typeof from) + Ufind.set m.store a { r with cshifts = Access.Set.add acs r.cshifts } ; + sized m a (Access.typeof acs) (* -------------------------------------------------------------------------- *) (* --- Lookup ---- *) @@ -574,9 +451,11 @@ let rec move (m: map) (r: node) (p: int) (s: int) = move m rg.data (p - rg.offset) s let field (m: map) (r: node) (fd: fieldinfo) : node = - let s = Cil.bitsSizeOf fd.ftype in - let (p,_) = Cil.fieldBitsOffset fd in - move m r p s + if fd.fcomp.cstruct then + let s = Cil.bitsSizeOf fd.ftype in + let (p,_) = Cil.fieldBitsOffset fd in + move m r p s + else r let index (m : map) (r: node) (ty:typ) : node = move m r 0 (Cil.bitsSizeOf ty) @@ -641,16 +520,190 @@ let included map source target : bool = let separated map r1 r2 = not (included map r1 r2) && not (included map r2 r1) +let single_path m r0 r s = + match (Ufind.get m.store r0).clayout with + | Blob -> true + | Cell(s0,_) -> s = s0 + | Compound(_,_,R rgs) -> + List.for_all + (fun (rg : node Ranges.range) -> + not (Ufind.eq m.store r rg.data) || rg.length = s + ) rgs + +let rec singleton m r = + let node = Ufind.get m.store r in + (* normalized parents *) + match nodes m node.cparents with + | [] -> Vset.cardinal node.croots = 1 + | [r0] -> + Vset.is_empty node.croots && + single_path m r0 r (sizeof node.clayout) && + singleton m r0 + | _ -> false + (* -------------------------------------------------------------------------- *) -let reads (m : map) (r:node) = +let reads (m:map) (r:node) = let node = Ufind.get m.store r in List.map Access.typeof @@ Access.Set.elements node.creads -let writes (m : map) (r:node) = +let writes (m:map) (r:node) = let node = Ufind.get m.store r in List.map Access.typeof @@ Access.Set.elements node.cwrites -let shifts (m : map) (r:node) = +let shifts (m:map) (r:node) = let node = Ufind.get m.store r in List.map Access.typeof @@ Access.Set.elements node.cshifts + +let types (m:map) (r:node) = ctypes @@ Ufind.get m.store r + +let typed (m:map) (r:node) = + let types = ref None in + let node = Ufind.get m.store r in + let size = sizeof node.clayout in + try + let check acs = + let t = Access.typeof acs in + if Cil.bitsSizeOf t > size then raise Exit ; + match !types with + | None -> types := Some t + | Some t0 -> if not @@ Cil_datatype.Typ.equal t0 t then raise Exit + in + Access.Set.iter check node.creads ; + Access.Set.iter check node.cwrites ; + Access.Set.iter check node.cshifts ; + !types + with Exit -> None + +(* -------------------------------------------------------------------------- *) +(* --- High-Level API --- *) +(* -------------------------------------------------------------------------- *) + +type range = { + label: string ; (* pretty printed fields *) + offset: int ; + length: int ; + cells: int ; + data: node ; +} + +type region = { + node: node ; + parents: node list ; + roots: varinfo list ; + labels: string list ; + types: typ list ; + typed : typ option ; + fields: Fields.domain ; + reads: Access.acs list ; + writes: Access.acs list ; + shifts: Access.acs list ; + sizeof: int ; + singleton : bool ; + ranges: range list ; + pointed: node option ; +} + +let make_range (m: map) fields Ranges.{ length ; offset ; data } : range = + let s = sizeof (get m data).clayout in + let p = Fields.pslice ~fields ~offset ~length in + { + label = Format.asprintf "%t" p ; + offset ; length ; + cells = if s = 0 then 0 else length / s ; + data = node m data ; + } + +let ranges (m:map) (r:node) = + let node = Ufind.get m.store r in + let fields = cfields node.clayout in + List.map (make_range m fields) (cranges node.clayout) + +let make_region (m: map) (n: node) (r: chunk) : region = + let types = ctypes r in + let typed = typed m n in + let sizeof = sizeof r.clayout in + let fields = cfields r.clayout in + let singleton = singleton m n in + { + node = n ; + parents = nodes m r.cparents ; + roots = Vset.elements r.croots ; + labels = Lset.elements r.clabels ; + reads = Access.Set.elements r.creads ; + writes = Access.Set.elements r.cwrites ; + shifts = Access.Set.elements r.cshifts ; + ranges = List.map (make_range m fields) (cranges r.clayout) ; + pointed = Option.map (node m) (cpointed r.clayout) ; + types ; typed ; singleton ; sizeof ; fields ; + } + +let region map n = make_region map n (get map n) + +let regions map = + let pool = ref [] in + iter map (fun r -> pool := region map r :: !pool) ; + List.rev !pool + +(* -------------------------------------------------------------------------- *) +(* --- Pretty Printers --- *) +(* -------------------------------------------------------------------------- *) + +let pp_cells fmt = function + | 1 -> () + | 0 -> Format.fprintf fmt "[…]" + | n -> Format.fprintf fmt "[%d]" n + +type slice = + | Padding of int + | Range of range + +let pad p q s = + let n = q - p in + if n > 0 then Padding n :: s else s + +let rec span k s = function + | [] -> pad k s [] + | r::rs -> pad k r.offset @@ Range r :: span (r.offset + r.length) s rs + +let pp_slice fields fmt = function + | Padding n -> + Format.fprintf fmt "@ %a;" Fields.pp_bits n + | Range r -> + Format.fprintf fmt "@ %t: %a%a;" + (Fields.pslice ~fields ~offset:r.offset ~length:r.length) + pp_node r.data + pp_cells r.cells + +let pp_range fmt (r: range) = + Format.fprintf fmt "@ %d..%d: %a%a;" + r.offset (r.offset + r.length) pp_node r.data pp_cells r.cells + +let pp_region fmt (m: region) = + begin + let acs r s = if s = [] then '-' else r in + Format.fprintf fmt "@[<hov 2>%a: %c%c%c" + pp_node m.node + (acs 'R' m.reads) (acs 'W' m.writes) (acs 'A' m.shifts) ; + List.iter (Format.fprintf fmt "@ %s:") m.labels ; + List.iter (Format.fprintf fmt "@ %a" Varinfo.pretty) m.roots ; + List.iter (Format.fprintf fmt "@ (%a)" Typ.pretty) m.types ; + Format.fprintf fmt "@ %db" m.sizeof ; + Option.iter (Format.fprintf fmt "@ (*%a)" pp_node) m.pointed ; + if m.ranges <> [] then + begin + Format.fprintf fmt "@ @[<hv 0>@[<hv 2>{" ; + if Options.debug_atleast 1 then + List.iter (pp_range fmt) m.ranges + else + List.iter (pp_slice m.fields fmt) (span 0 m.sizeof m.ranges) ; + Format.fprintf fmt "@]@ }@]" ; + end ; + if Options.debug_atleast 1 then + begin + List.iter (Format.fprintf fmt "@ R:%a" Access.pretty) m.reads ; + List.iter (Format.fprintf fmt "@ W:%a" Access.pretty) m.writes ; + List.iter (Format.fprintf fmt "@ A:%a" Access.pretty) m.shifts ; + end ; + Format.fprintf fmt " ;@]" ; + end diff --git a/src/plugins/region/memory.mli b/src/plugins/region/memory.mli index af3cd23eb708edc8f8bc11a89ee58c3abaea3ab3..5e2affc0faa29d338dff62c646b30d086c656332 100644 --- a/src/plugins/region/memory.mli +++ b/src/plugins/region/memory.mli @@ -38,11 +38,13 @@ type region = { roots: varinfo list ; labels: string list ; types: typ list ; + typed : typ option ; fields: Fields.domain ; reads: Access.acs list ; writes: Access.acs list ; shifts: Access.acs list ; sizeof: int ; + singleton : bool ; ranges: range list ; pointed: node option ; } @@ -70,24 +72,28 @@ val equal : map -> node -> node -> bool val node : map -> node -> node val nodes : map -> node list -> node list -val iter : map -> (region -> unit) -> unit -val iter_node : map -> (node -> unit) -> unit -val region : map -> node -> region -val regions : map -> region list +val size : map -> node -> int val parents : map -> node -> node list val roots : map -> node -> varinfo list +val labels : map -> node -> string list +val region : map -> node -> region +val regions : map -> region list +val iter : map -> (node -> unit) -> unit + +val new_chunk : map -> + ?parent:node -> ?size:int -> ?ptr:node -> ?pointed:node -> + unit -> node -val new_chunk : map -> ?size:int -> ?ptr:node -> ?pointed:node -> unit -> node val add_root : map -> Cil_types.varinfo -> node val add_label : map -> string -> node val add_field : map -> node -> fieldinfo -> node -val add_index : map -> node -> typ -> int -> node +val add_index : map -> node -> typ -> node val add_points_to : map -> node -> node -> unit val add_value : map -> node -> typ -> node option -val read : map -> node -> Access.acs -> unit -val write : map -> node -> Access.acs -> unit -val shift : map -> node -> Access.acs -> unit +val add_read : map -> node -> Access.acs -> unit +val add_write : map -> node -> Access.acs -> unit +val add_shift : map -> node -> Access.acs -> unit val merge : map -> node -> node -> unit val merge_all : map -> node list -> unit @@ -99,12 +105,16 @@ val index : map -> node -> typ -> node val lval : map -> lval -> node val exp : map -> exp -> node option +val ranges : map -> node -> range list val points_to : map -> node -> node option val pointed_by : map -> node -> node list val included : map -> node -> node -> bool val separated : map -> node -> node -> bool +val singleton : map -> node -> bool val reads : map -> node -> typ list val writes : map -> node -> typ list val shifts : map -> node -> typ list +val types : map -> node -> typ list +val typed : map -> node -> typ option diff --git a/src/plugins/region/ranges.ml b/src/plugins/region/ranges.ml index 54e7cc2c43fc0d8864f4332f1de80d8006c647f3..f8a6e0d3e9ddfd4ec6762bda129cbeeeb962e32c 100644 --- a/src/plugins/region/ranges.ml +++ b/src/plugins/region/ranges.ml @@ -57,7 +57,7 @@ let range ?(offset=0) ?(length=1) data = singleton { offset ; length ; data } let rec find (k: int) = function | [] -> raise Not_found | ({ offset ; length } as r) :: rs -> - if offset <= k && k <= offset + length then r else find k rs + if offset <= k && k < offset + length then r else find k rs let find k (R rs) = find k rs @@ -70,7 +70,7 @@ let rec merge f ra rb = if a' <= b.offset then a :: merge f wa rb else - if b' < a.offset then + if b' <= a.offset then b :: merge f ra wb else let offset = min a.offset b.offset in @@ -83,12 +83,11 @@ let rec merge f ra rb = let merge f (R x) (R y) = R (merge f x y) -let squash f = function - | R [] -> None - | R (x::xs) -> Some (List.fold_left (fun w r -> f w r.data) x.data xs) - let iteri f (R xs) = List.iter f xs +let foldi f w (R xs) = List.fold_left f w xs let iter f (R xs) = List.iter (fun r -> f r.data) xs +let fold f w (R xs) = List.fold_left (fun w r -> f w r.data) w xs +let mapi f (R xs) = R (List.map f xs) let map f (R xs) = R (List.map (fun r -> { r with data = f r.data }) xs) (* -------------------------------------------------------------------------- *) diff --git a/src/plugins/region/ranges.mli b/src/plugins/region/ranges.mli index b04c273feb06930e56c6d996efe8688f8519dd86..7149b1cd7b3b7da4ce5be8cc8ff8de8c55f92cb6 100644 --- a/src/plugins/region/ranges.mli +++ b/src/plugins/region/ranges.mli @@ -36,9 +36,12 @@ val empty : 'a t val singleton : 'a range -> 'a t val range : ?offset:int -> ?length:int -> 'a -> 'a t val merge : ('a range -> 'a range -> 'a) -> 'a t -> 'a t -> 'a t -val squash : ('a -> 'a -> 'a) -> 'a t -> 'a option val find : int -> 'a t -> 'a range + val map : ('a -> 'b) -> 'a t -> 'b t +val mapi : ('a range -> 'b range) -> 'a t -> 'b t val iter : ('a -> unit) -> 'a t -> unit val iteri : ('a range -> unit) -> 'a t -> unit +val fold : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b +val foldi : ('b -> 'a range -> 'b) -> 'b -> 'a t -> 'b diff --git a/src/plugins/region/register.ml b/src/plugins/region/register.ml index 54cd5cdd62b1d0f0a2e294a2b7ebce49a6390fca..0d904f9fd3ef8e01ccde1980924a745a6a43e312 100644 --- a/src/plugins/region/register.ml +++ b/src/plugins/region/register.ml @@ -31,7 +31,20 @@ let main () = begin Ast.compute () ; R.feedback "Analyzing regions" ; - Globals.Functions.iter Analysis.compute ; + Globals.Functions.iter + begin fun kf -> + let domain = Analysis.get kf in + Options.result "@[<v 2>Function %a:%t@]@." + Kernel_function.pretty kf + begin fun fmt -> + List.iter + begin fun r -> + Format.pp_print_newline fmt () ; + Memory.pp_region fmt r ; + end @@ + Memory.regions domain.map + end + end end let () = Boot.Main.extend main diff --git a/src/plugins/region/services.ml b/src/plugins/region/services.ml index 243f1975703242f4c9a0158b96c5467f842d8fcd..e5f6ebffe1192ec4dd5ac1b6df0e924eb198b78b 100644 --- a/src/plugins/region/services.ml +++ b/src/plugins/region/services.ml @@ -114,6 +114,7 @@ struct let label (m: Memory.region) = let buffer = Buffer.create 4 in + if m.singleton then Buffer.add_string buffer "!" ; if m.reads <> [] then Buffer.add_char buffer 'R' ; if m.writes <> [] then Buffer.add_char buffer 'W' ; if m.pointed <> None then Buffer.add_char buffer '*' @@ -133,7 +134,7 @@ struct Typ.pretty fmt ty let title (m: Memory.region) = - Format.asprintf "%t (%db)" + Format.asprintf "%t (%db)%t" begin fun fmt -> match m.types with | [] -> Format.pp_print_string fmt "Compound" @@ -141,7 +142,11 @@ struct | ty::ts -> pp_typ_layout 0 fmt ty ; List.iter (Format.fprintf fmt ", %a" (pp_typ_layout 0)) ts ; - end m.sizeof + end + m.sizeof + begin fun fmt -> + if m.singleton then Format.pp_print_string fmt " (singleton)" + end let jtype = Data.declare ~package ~name:"region" @@ Jrecord [ @@ -154,7 +159,8 @@ struct "pointed", NodeOpt.jtype ; "reads", Jboolean ; "writes", Jboolean ; - "bytes", Jboolean ; + "typed", Jboolean ; + "singleton", Jboolean ; "label", Jstring ; "title", Jstring ; ] @@ -170,7 +176,8 @@ struct "pointed", NodeOpt.to_json @@ m.pointed ; "reads", Json.of_bool (m.reads <> []) ; "writes", Json.of_bool (m.writes <> []) ; - "bytes", Json.of_bool (List.length m.types > 1) ; + "typed", Json.of_bool (m.typed <> None) ; + "singleton", Json.of_bool m.singleton ; "label", Json.of_string @@ label m ; "title", Json.of_string @@ title m ; ] diff --git a/src/plugins/region/tests/region/comp.c b/src/plugins/region/tests/region/comp.i similarity index 100% rename from src/plugins/region/tests/region/comp.c rename to src/plugins/region/tests/region/comp.i diff --git a/src/plugins/region/tests/region/oracle/blob.res.oracle b/src/plugins/region/tests/region/oracle/blob.res.oracle index f18edf72e524a8a5f91a20925399ee9dd6ddbfb7..2a12e2190c3bbbd534539bcdd62d45b5967050f4 100644 --- a/src/plugins/region/tests/region/oracle/blob.res.oracle +++ b/src/plugins/region/tests/region/oracle/blob.res.oracle @@ -1,9 +1,8 @@ [kernel] Parsing blob.i (no preprocessing) [region] Analyzing regions [region] Function array_blob: - R0003: --- arr 96b { #96b: R0004[…]; } ; - R0004: --- 0b ; - R0001: -W- p (int *) 64b (*R0004) ; + R0003: --- arr 32b ; + R0001: -W- p (int *) 64b (*R0003) ; [region] Function struct_blob: R0003: --- y 64b { .f: R0004[…]; #32b; } ; R0004: --- 0b ; diff --git a/src/plugins/region/tests/region/oracle/comp.res.oracle b/src/plugins/region/tests/region/oracle/comp.res.oracle index d444b5dc68c66f3c4e63bbbbd876679a76495ab0..31059b7aa9c15096b4dca29bebf767c897783b6f 100644 --- a/src/plugins/region/tests/region/oracle/comp.res.oracle +++ b/src/plugins/region/tests/region/oracle/comp.res.oracle @@ -1,9 +1,8 @@ -[kernel] Parsing comp.c (with preprocessing) +[kernel] Parsing comp.i (no preprocessing) [region] Analyzing regions [region] Function f: - R0003: --- s 192b { .x: R0004; #32b; .z: R0008; .arr: R000d; } ; + R0003: --- s 192b { .x: R0004; #32b; .z: R0008; .arr: R000d[4]; } ; R0004: -W- (int) 32b ; R0008: -W- (int *) 64b (*R0001) ; R0001: RW- a (int) 32b ; - R000d: --- 64b { #64b: R000f[4]; } ; - R000f: -W- (short) 16b ; + R000d: -W- (short) 16b ; diff --git a/src/plugins/region/tests/region/oracle/fb_SORT.res.oracle b/src/plugins/region/tests/region/oracle/fb_SORT.res.oracle index 4de3c9517dbac676e8443d7aea783f55c9355fd5..586a487686d61159339da6487b2fd94f5dc0b18f 100644 --- a/src/plugins/region/tests/region/oracle/fb_SORT.res.oracle +++ b/src/plugins/region/tests/region/oracle/fb_SORT.res.oracle @@ -4,14 +4,19 @@ R0003: R-- fb (FB *) 64b (*R0005) ; R0005: --- 704b { - .prm.inp1: R0007[2]; + .prm: R002e; + .inp1: R0007; #128b; .out1: R000f; .out2: R006a; - .out3.idx1: R0017[2]; + .out3: R0074; + .idx1: R0017; #128b; .sum: R0057; } ; + R002e: R-- (struct N *) 64b (*R0031) ; + R0031: --- 128b { .v: R0033; #64b; } ; + R0033: R-- (double) 64b ; R0007: R-- (struct N *) 64b (*R0027) ; R0027: --- 128b { .v: R0029; .s: R0049; #32b; } ; R0029: R-- (double) 64b ; @@ -23,13 +28,17 @@ R006a: R-- (struct N *) 64b (*R006d) ; R006d: --- 128b { .v: R006f; #64b; } ; R006f: R-- (double) 64b ; - R0017: R-- (struct N *) (struct L *) 64b (*R0041) ; - R0041: --- 64b { .v.s: R0043[2]; } ; - R0043: RW- (int) (double) 32b ; + R0074: R-- (struct N *) 64b (*R0077) ; + R0077: --- 128b { .v: R0079; #64b; } ; + R0079: R-- (double) 64b ; + R0017: R-- (struct L *) 64b (*R0041) ; + R0041: --- 64b { .v: R0043; .s: R0052; } ; + R0043: -W- (int) 32b ; + R0052: -W- (int) 32b ; R0057: R-- (struct N *) 64b (*R005a) ; - R005a: --- 128b { .v: R005c; .s: R0083; #32b; } ; + R005a: --- 128b { .v: R005c; .s: R0084; #32b; } ; R005c: -W- (double) 64b ; - R0083: -W- (int) 32b ; + R0084: -W- (int) 32b ; R0001: RWA inp (SN *) 64b (*R0007) ; R000b: RWA out (SN *) 64b (*R000f) ; R0013: RWA idx (SL *) 64b (*R0017) ; diff --git a/src/plugins/region/tests/region/oracle/union.res.oracle b/src/plugins/region/tests/region/oracle/union.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..1f2cf922dfa5937551f43671c258ed47f0f24022 --- /dev/null +++ b/src/plugins/region/tests/region/oracle/union.res.oracle @@ -0,0 +1,5 @@ +[kernel] Parsing union.i (no preprocessing) +[region] Analyzing regions +[region] Function f: + R0001: R-- p (union U *) 64b (*R0003) ; + R0003: RW- (int) (short) 16b ; diff --git a/src/plugins/region/tests/region/union.i b/src/plugins/region/tests/region/union.i new file mode 100644 index 0000000000000000000000000000000000000000..85507b96692f3df1944db26f4f146c2f4243547b --- /dev/null +++ b/src/plugins/region/tests/region/union.i @@ -0,0 +1,10 @@ +union U { + short s[2]; + int i; +}; + +void f (union U *p) { + p->i++; + p->s[0]++; + p->s[1]++; +} diff --git a/src/plugins/wp/Factory.ml b/src/plugins/wp/Factory.ml index f173e8b2e0bb0dda307e02dd515d13002bb160bd..22804c75a399e29cf3fe5f7ab7986c5d461d9ef5 100644 --- a/src/plugins/wp/Factory.ml +++ b/src/plugins/wp/Factory.ml @@ -24,7 +24,7 @@ (* --- Model Factory --- *) (* -------------------------------------------------------------------------- *) -type mheap = Hoare | ZeroAlias | Typed of MemTyped.pointer | Eva | Bytes +type mheap = Hoare | ZeroAlias | Eva | Bytes | Region | Typed of MemTyped.pointer type mvar = Raw | Var | Ref | Caveat type setup = { @@ -69,6 +69,7 @@ let descr_mheap d = function | Typed p -> main d "typed" ; descr_mtyped d p | Eva -> main d "eva" | Bytes -> main d "bytes" + | Region -> main d "region" let descr_mvar d = function | Var -> () @@ -192,6 +193,10 @@ struct let iter = refusage_iter end +module Static = MemVar.Static +module MemRegion = MemRegion.Make(RegionAnalysis)(MemBytes) +module MemEva = MemVal.Make(MemVal.Eva) + (* -------------------------------------------------------------------------- *) (* --- Generator & Model --- *) (* -------------------------------------------------------------------------- *) @@ -199,18 +204,17 @@ end (* Each model must be instanciated statically because of registered memory models identifiers and Frama-C states *) -module Register(V : MemVar.VarUsage)(M : Sigs.Model) +module MakeVar(V : MemVar.VarUsage)(M : Sigs.Model) = MemVar.Make(MakeVarUsage(V))(M) -module Model_Hoare_Raw = Register(MemVar.Raw)(MemEmpty) -module Model_Hoare_Ref = Register(Ref)(MemEmpty) -module Model_Typed_Var = Register(Var)(MemTyped) -module Model_Typed_Ref = Register(Ref)(MemTyped) -module Model_Bytes_Var = Register(Var)(MemBytes) -module Model_Bytes_Ref = Register(Ref)(MemBytes) -module Model_Caveat = Register(Caveat)(MemTyped) - -module MemVal = MemVal.Make(MemVal.Eva) +module Model_Hoare_Raw = MakeVar(MemVar.Raw)(MemEmpty) +module Model_Hoare_Ref = MakeVar(Ref)(MemEmpty) +module Model_Typed_Var = MakeVar(Var)(MemTyped) +module Model_Typed_Ref = MakeVar(Ref)(MemTyped) +module Model_Bytes_Var = MakeVar(Var)(MemBytes) +module Model_Bytes_Ref = MakeVar(Ref)(MemBytes) +module Model_Caveat = MakeVar(Caveat)(MemTyped) +module Model_Region = MakeVar(Static)(MemRegion) module MakeCompiler(M:Sigs.Model) = struct @@ -228,15 +232,12 @@ module Comp_Hoare_Ref = MakeCompiler(Model_Hoare_Ref) module Comp_MemTyped = MakeCompiler(MemTyped) module Comp_Typed_Var = MakeCompiler(Model_Typed_Var) module Comp_Typed_Ref = MakeCompiler(Model_Typed_Ref) - module Comp_Caveat = MakeCompiler(Model_Caveat) - module Comp_MemBytes = MakeCompiler(MemBytes) module Comp_Bytes_Var = MakeCompiler(Model_Bytes_Var) module Comp_Bytes_Ref = MakeCompiler(Model_Bytes_Ref) - -module Comp_MemVal = MakeCompiler(MemVal) - +module Comp_Region = MakeCompiler(Model_Region) +module Comp_MemEva = MakeCompiler(MemEva) let compiler mheap mvar : (module Sigs.Compiler) = match mheap , mvar with @@ -247,11 +248,11 @@ let compiler mheap mvar : (module Sigs.Compiler) = | Typed _ , Raw -> (module Comp_MemTyped) | Typed _ , Var -> (module Comp_Typed_Var) | Typed _ , Ref -> (module Comp_Typed_Ref) - | Eva, _ -> (module Comp_MemVal) + | Eva, _ -> (module Comp_MemEva) | Bytes, Raw -> (module Comp_MemBytes) | Bytes, Var -> (module Comp_Bytes_Var) | Bytes, Ref -> (module Comp_Bytes_Ref) - + | Region, _ -> (module Comp_Region) (* -------------------------------------------------------------------------- *) (* --- Tuning --- *) @@ -260,7 +261,7 @@ let compiler mheap mvar : (module Sigs.Compiler) = let configure_mheap = function | Hoare -> MemEmpty.configure () | ZeroAlias -> MemZeroAlias.configure () - | Eva -> MemVal.configure () + | Eva -> MemEva.configure () | Bytes -> MemBytes.configure () | Typed p -> let rollback_memtyped = MemTyped.configure () in @@ -270,6 +271,7 @@ let configure_mheap = function Context.pop MemTyped.pointer orig_memtyped_pointer in rollback + | Region -> MemBytes.configure () let configure_driver setup driver () = let rollback_mheap = configure_mheap setup.mheap in @@ -345,6 +347,7 @@ let update_config ~warning m s = function | "TYPED" -> { s with mheap = Typed MemTyped.Fits } | "CAST" -> { s with mheap = Typed MemTyped.Unsafe } | "NOCAST" -> { s with mheap = Typed MemTyped.NoCast } + | "REGION" -> { s with mheap = Region } | "EVA" -> { s with mheap = Eva } | "BYTES" -> { s with mheap = Bytes } | "CAVEAT" -> { s with mvar = Caveat } diff --git a/src/plugins/wp/Factory.mli b/src/plugins/wp/Factory.mli index 1b9ac3843c252292249ffcc1f491024aefad20b4..741faf0c4268a07813bdf8ebbad13505b133cca2 100644 --- a/src/plugins/wp/Factory.mli +++ b/src/plugins/wp/Factory.mli @@ -24,7 +24,7 @@ (* --- Model Factory --- *) (* -------------------------------------------------------------------------- *) -type mheap = Hoare | ZeroAlias | Typed of MemTyped.pointer | Eva | Bytes +type mheap = Hoare | ZeroAlias | Eva | Bytes | Region | Typed of MemTyped.pointer type mvar = Raw | Var | Ref | Caveat type setup = { diff --git a/src/plugins/wp/MemBytes.ml b/src/plugins/wp/MemBytes.ml index b3d3dad054fceb0e5f459852e480ce16873a2473..795aec9adeab33b0bbfbbc0c589149d4ccecb7b3 100644 --- a/src/plugins/wp/MemBytes.ml +++ b/src/plugins/wp/MemBytes.ml @@ -28,7 +28,8 @@ module Logic = Qed.Logic (* Why3 symbols *) -module Why3 = struct +module Why3 = +struct let library = "membytes" let t_vblock = Qed.Logic.Array (Qed.Logic.Int, Qed.Logic.Int) @@ -197,13 +198,9 @@ type sigma = Sigma.t type domain = Sigma.domain type segment = loc rloc -let comp_cluster () = - Definitions.cluster ~id:"Compound" ~title:"Memory Compound Loader" () - let shift_cluster () = Definitions.cluster ~id:"Shifts" ~title:"Shifts Definitions" () - (* ********************************************************************** *) (* SIZE *) (* ********************************************************************** *) @@ -676,9 +673,6 @@ module Model = struct Why3.havoc fresh current loc n else fresh - let eqmem obj loc _chunk m1 m2 = - Why3.eqmem m1 m2 loc @@ sizeof obj - let eqmem_forall obj loc _chunk m1 m2 = let xp = Lang.freshvar ~basename:"p" MemAddr.t_addr in let p = e_var xp in @@ -1130,3 +1124,17 @@ let scope seq scope xs = in e_set m (BASE.get x) size) (Sigma.value seq.pre Alloc) xs in [ p_equal (Sigma.value seq.post Alloc) alloc ] + +(* ********************************************************************** *) +(* API with Region *) +(* ********************************************************************** *) + +let sizeof = protected_sizeof_object +let last = Model.last +let frames = Model.frames +let eqmem_forall = Model.eqmem_forall +let set_init = Model.set_init +let is_init_range = Model.is_init_range +let value_footprint = Model.value_footprint +let init_footprint = Model.init_footprint +let havoc = Model.havoc diff --git a/src/plugins/wp/MemBytes.mli b/src/plugins/wp/MemBytes.mli new file mode 100644 index 0000000000000000000000000000000000000000..ddb5ce41037e817691479951fd8e66cb96de2a34 --- /dev/null +++ b/src/plugins/wp/MemBytes.mli @@ -0,0 +1,23 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2024 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* 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 licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +include MemRegion.ModelWithLoader diff --git a/src/plugins/wp/MemLoader.ml b/src/plugins/wp/MemLoader.ml index bf2d6b187cd68dbe8e27c7b9a1b1d5bfa3f092fe..a94374e98ba0114944c99af67840d35ed99ab71d 100644 --- a/src/plugins/wp/MemLoader.ml +++ b/src/plugins/wp/MemLoader.ml @@ -82,7 +82,6 @@ sig val set_init_atom : Sigma.t -> c_object -> loc -> term -> Chunk.t * term val set_init : c_object -> loc -> length:term -> Chunk.t -> current:term -> term - (* val monotonic_init : Sigma.t -> Sigma.t -> pred *) end diff --git a/src/plugins/wp/MemLoader.mli b/src/plugins/wp/MemLoader.mli index 46d62359db33e62fbf0df3d723f02ffe00d4f24c..4e73e424b9802157c98f0a73b38324232500bc20 100644 --- a/src/plugins/wp/MemLoader.mli +++ b/src/plugins/wp/MemLoader.mli @@ -78,7 +78,6 @@ sig val set_init_atom : Sigma.t -> c_object -> loc -> term -> Chunk.t * term val set_init : c_object -> loc -> length:term -> Chunk.t -> current:term -> term - (* val monotonic_init : Sigma.t -> Sigma.t -> pred *) end diff --git a/src/plugins/wp/MemRegion.ml b/src/plugins/wp/MemRegion.ml new file mode 100644 index 0000000000000000000000000000000000000000..06b0704d0da1a99ca9d576d4d7515433f9f08770 --- /dev/null +++ b/src/plugins/wp/MemRegion.ml @@ -0,0 +1,718 @@ +(*************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2024 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* 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 licenses/LGPLv2.1). *) +(* *) +(*************************************************************************) + +(* -------------------------------------------------------------------------- *) +(* --- Region Memory Model --- *) +(* -------------------------------------------------------------------------- *) + +open Cil_types +open Ctypes +open Lang.F +open Sigs +open MemMemory + +type primitive = Int of c_int | Float of c_float | Ptr +type kind = Single of primitive | Many of primitive | Garbled + +let pp_prim fmt = function + | Int i -> Ctypes.pp_int fmt i + | Float f -> Ctypes.pp_float fmt f + | Ptr -> Format.pp_print_string fmt "ptr" + +let pp_kind fmt = function + | Single p -> pp_prim fmt p + | Many p -> Format.fprintf fmt "[%a]" pp_prim p + | Garbled -> Format.pp_print_string fmt "[bytes]" + +let tau_of_prim = function + | Int _ -> Qed.Logic.Int + | Float f -> Cfloat.tau_of_float f + | Ptr -> MemAddr.t_addr + +(* -------------------------------------------------------------------------- *) +(* --- Region Analysis Proxy --- *) +(* -------------------------------------------------------------------------- *) + +module type RegionProxy = +sig + type region + module Type : Sigs.Type with type t = region + val id : region -> int + val of_id : int -> region option + val kind : region -> kind + val name : region -> string option + val cvar : varinfo -> region option + val field : region -> fieldinfo -> region option + val shift : region -> c_object -> region option + val points_to : region -> region option + val literal : eid:int -> Cstring.cst -> region option + val separated : region -> region -> bool + val included : region -> region -> bool +end + +(* -------------------------------------------------------------------------- *) +(* --- Underlying Model (Handles Addresses & Garbled) --- *) +(* -------------------------------------------------------------------------- *) + +module type ModelWithLoader = sig + include Sigs.Model + include Sigs.Model + val sizeof : c_object -> term + + val last : sigma -> c_object -> loc -> term + val frames : c_object -> loc -> chunk -> frame list + + val havoc : c_object -> loc -> length:term -> chunk -> fresh:term -> current:term -> term + + val eqmem_forall : c_object -> loc -> chunk -> term -> term -> var list * pred * pred + + val load_int : sigma -> c_int -> loc -> term + val load_float : sigma -> c_float -> loc -> term + val load_pointer : sigma -> typ -> loc -> loc + + val store_int : sigma -> c_int -> loc -> term -> chunk * term + val store_float : sigma -> c_float -> loc -> term -> chunk * term + val store_pointer : sigma -> typ -> loc -> term -> chunk * term + + val set_init_atom : sigma -> c_object -> loc -> term -> chunk * term + val set_init : c_object -> loc -> length:term -> chunk -> current:term -> term + val is_init_atom : sigma -> c_object -> loc -> term + val is_init_range : sigma -> c_object -> loc -> term -> pred + + val value_footprint : c_object -> loc -> domain + val init_footprint : c_object -> loc -> domain +end + +(* -------------------------------------------------------------------------- *) +(* --- Region Memory Model --- *) +(* -------------------------------------------------------------------------- *) + +module Make (R:RegionProxy) (M:ModelWithLoader) (*: Sigs.Model*) = +struct + + type region = R.region + let datatype = "MemRegion.Make" + (* For projectification. Must be unique among models. *) + + let configure = M.configure + let configure_ia = M.configure_ia + let hypotheses = M.hypotheses + + module MChunk = M.Chunk + module RChunk = + struct + let self = "MemRegion.RChunk" + + type mu = Value of primitive | Array of primitive | ValInit | ArrInit + type t = { mu : mu ; region : R.region } + + let pp_mu fmt = function + | Value p -> Format.fprintf fmt "µ%a" pp_prim p + | Array p -> Format.fprintf fmt "µ%a[]" pp_prim p + | ValInit -> Format.pp_print_string fmt "µinit" + | ArrInit -> Format.pp_print_string fmt "µinit[]" + + let hash { mu ; region } = Hashtbl.hash (mu, R.Type.hash region) + let equal a b = Stdlib.(=) a.mu b.mu && R.Type.equal a.region b.region + let compare a b = + let cmp = Stdlib.compare a.mu b.mu in + if cmp <> 0 then cmp else R.Type.compare a.region b.region + + let pretty fmt { mu ; region } = + Format.fprintf fmt "%a@%03d" pp_mu mu (R.id region) + + let tau_of_chunk { mu } = + match mu with + | Value p -> tau_of_prim p + | ValInit -> Qed.Logic.Bool + | Array p -> Qed.Logic.Array(MemAddr.t_addr,tau_of_prim p) + | ArrInit -> Qed.Logic.Array(MemAddr.t_addr,Qed.Logic.Bool) + + let basename_of_chunk { mu ; region } = + match mu with + | ValInit -> "Vinit" + | ArrInit -> "Minit" + | Array p -> Format.asprintf "M%a" pp_prim p + | Value p -> + match R.name region with + | Some a -> a + | None -> Format.asprintf "V%a" pp_prim p + + let is_framed _ = false + + end + + module MHeap = M.Heap + module MSigma = M.Sigma + + module RHeap = Qed.Collection.Make(RChunk) + module RSigma = Sigma.Make(RChunk)(RHeap) + + type chunk = + | M of MChunk.t + | R of RChunk.t + + let cmap f g (m,r) = (f m, g r) + let cmap2 f g (m1,r1) (m2,r2) = (f m1 m2, g r1 r2) + let capply f g (m,r) = function M c -> f m c, r | R c -> m, g r c + let cmerge f g (m,r) = function M c -> f m c | R c -> g r c + let mseq { pre ; post } = { pre = fst pre ; post = fst post } + let rseq { pre ; post } = { pre = snd pre ; post = snd post } + + module Chunk : Sigs.Chunk with type t = chunk = + struct + type t = chunk + let self = "Wp.MemRegion.Self" + let hash = function + | M c -> 3 * MChunk.hash c + | R c -> 5 * RChunk.hash c + + let equal ca cb = match ca, cb with + | M c1, M c2 -> MChunk.equal c1 c2 + | R c1, R c2 -> RChunk.equal c1 c2 + | M _, R _ | R _, M _ -> false + + let compare c1 c2 = + match c1, c2 with + | M m1, M m2 -> MChunk.compare m1 m2 + | R r1, R r2 -> RChunk.compare r1 r2 + | M _, R _ -> (-1) + | R _, M _ -> (+1) + + let pretty fmt = function + | M c -> MChunk.pretty fmt c + | R c -> RChunk.pretty fmt c + + let tau_of_chunk = function + | M c -> MChunk.tau_of_chunk c + | R c -> RChunk.tau_of_chunk c + + let basename_of_chunk = function + | M c -> MChunk.basename_of_chunk c + | R c -> RChunk.basename_of_chunk c + + let is_framed = function + | M c -> MChunk.is_framed c + | R c -> RChunk.is_framed c + + end + + module Heap = Qed.Collection.Make(Chunk) + module Domain = Heap.Set + + type sigma = MSigma.t * RSigma.t + type domain = Domain.t + + module Sigma = + struct + + type t = sigma + type chunk = Chunk.t + module Chunk = Heap + + type domain = Domain.t + + let create () : t = (MSigma.create (), RSigma.create ()) + + let pretty fmt (m,r) = + Format.fprintf fmt "@[<hv 0>{@[<hv 2>@ %a;@ %a;@]@ }@]" + MSigma.pretty m + RSigma.pretty r + + let empty : domain = Domain.empty + let union = Domain.union + let mem = cmerge MSigma.mem RSigma.mem + let get = cmerge MSigma.get RSigma.get + let value = cmerge MSigma.value RSigma.value + let copy = cmap MSigma.copy RSigma.copy + let choose = cmap2 MSigma.choose RSigma.choose + let havoc_chunk = capply MSigma.havoc_chunk RSigma.havoc_chunk + let havoc_any ~call = cmap (MSigma.havoc_any ~call) (RSigma.havoc_any ~call) + + let merge (m1,r1) (m2,r2) = + let m,p1,p2 = MSigma.merge m1 m2 in + let r,q1,q2 = RSigma.merge r1 r2 in + (m,r), Passive.union p1 q1, Passive.union p2 q2 + + let merge_list l = + let m,p = MSigma.merge_list @@ List.map fst l in + let r,q = RSigma.merge_list @@ List.map snd l in + (m,r), List.map2 Passive.union p q + + let join (m1,r1) (m2,r2) = + Passive.union (MSigma.join m1 m2) (RSigma.join r1 r2) + + let iter f (m,r) = + begin + MSigma.iter (fun c -> f (M c)) m ; + RSigma.iter (fun c -> f (R c)) r ; + end + + let iter2 f (m1,r1) (m2,r2) = + begin + MSigma.iter2 (fun c -> f (M c)) m1 m2 ; + RSigma.iter2 (fun c -> f (R c)) r1 r2 ; + end + + let mdomain d = + MHeap.Set.fold (fun c s -> Domain.add (M c) s) d Domain.empty + let rdomain d = + RHeap.Set.fold (fun c s -> Domain.add (R c) s) d Domain.empty + + let dsplit d = + let m = ref MHeap.Set.empty in + let r = ref RHeap.Set.empty in + Domain.iter + (function + | M c -> m := MHeap.Set.add c !m + | R c -> r := RHeap.Set.add c !r + ) d ; + !m, !r + + let assigned ~pre:(m1,r1) ~post:(m2,r2) d = + let m,r = dsplit d in + let hm = MSigma.assigned ~pre:m1 ~post:m2 m in + let hr = RSigma.assigned ~pre:r1 ~post:r2 r in + Bag.concat hm hr + + let havoc (m,r) d = + let dm,dr = dsplit d in + MSigma.havoc m dm, RSigma.havoc r dr + + let remove_chunks (m,r) d = + let dm,dr = dsplit d in + MSigma.remove_chunks m dm, RSigma.remove_chunks r dr + + let domain (m,r) = + union (mdomain @@ MSigma.domain m) (rdomain @@ RSigma.domain r) + + let writes (s : sigma sequence) = + union + (mdomain @@ MSigma.writes @@ mseq s) + (rdomain @@ RSigma.writes @@ rseq s) + + end + + (* -------------------------------------------------------------------------- *) + (* --- Region Loader --- *) + (* -------------------------------------------------------------------------- *) + + module Loader = + struct + let name = "MemRegion.Loader" + + module Chunk = Chunk + module Sigma = Sigma + + type loc = + | Null + | Raw of M.loc + | Loc of M.loc * region + + let make a = function None -> Raw a | Some r -> Loc(a,r) + let loc = function Null -> M.null | Raw a | Loc(a,_) -> a + let reg = function Null | Raw _ -> None | Loc(_,r) -> Some r + let kind = function Null | Raw _ -> Garbled | Loc(_,r) -> R.kind r + let rfold f = function Null | Raw _ -> None | Loc(_,r) -> f r + + (* ---------------------------------------------------------------------- *) + (* --- Utilities on locations --- *) + (* ---------------------------------------------------------------------- *) + + let last sigma ty l = M.last (fst sigma) ty (loc l) + + let to_addr l = M.pointer_val (loc l) + + let sizeof ty = M.sizeof ty + + let field l fd = + make (M.field (loc l) fd) (rfold (fun r -> R.field r fd) l) + + let shift l obj ofs = + make (M.shift (loc l) obj ofs) (rfold (fun r -> R.shift r obj) l) + + let frames ty l c = + match kind l with + | Single Ptr | Many Ptr | Garbled -> + let offset = M.sizeof ty in + let sizeof = Lang.F.e_one in + let tau = Chunk.tau_of_chunk c in + let basename = Chunk.basename_of_chunk c in + MemMemory.frames ~addr:(to_addr l) ~offset ~sizeof ~basename tau + | _ -> [] + + let havoc ty l ~length chunk ~fresh ~current = + match chunk with + | M c -> M.havoc ty (loc l) ~length c ~fresh ~current + | R c -> + match c.mu with + | Value _ | ValInit -> fresh + | Array _ | ArrInit -> e_fun f_havoc [fresh;current;to_addr l;length] + + let eqmem_forall ty l chunk m1 m2 = + match chunk with + | M c -> M.eqmem_forall ty (loc l) c m1 m2 + | R c -> + match c.mu with + | Value _ | ValInit -> [], p_true, p_equal m1 m2 + | Array _ | ArrInit -> + let xp = Lang.freshvar ~basename:"b" MemAddr.t_addr in + let p = e_var xp in + let n = M.sizeof ty in + let separated = + p_call MemAddr.p_separated [p;e_one;to_addr l;n] in + let equal = p_equal (e_get m1 p) (e_get m2 p) in + [xp],separated,equal + + (* ---------------------------------------------------------------------- *) + (* --- Load --- *) + (* ---------------------------------------------------------------------- *) + + let localized action = function + | Null -> + Warning.error ~source:"MemRegion" + "Attempt to %s at NULL" action + | Raw a -> + Warning.error ~source:"MemRegion" + "Attempt to %s without region (%a)" action M.pretty a + | Loc(l,r) -> l,r + + let to_region_pointer l = + let l,r = localized "loader" l in R.id r, M.pointer_val l + + let of_region_pointer r _ t = + make (M.pointer_loc t) (R.of_id r) + + let check_access action (p:primitive) (q:primitive) = + if Stdlib.(<>) p q then + Warning.error ~source:"MemRegion" + "Inconsistent %s (%a <> %a)" + action pp_prim p pp_prim q + + let load_int sigma iota loc : term = + let l,r = localized "load int" loc in + match R.kind r with + | Garbled -> M.load_int (fst sigma) iota l + | Single p -> + check_access "load" p (Int iota) ; + RSigma.value (snd sigma) { mu = Value p ; region = r } + | Many p -> + check_access "load" p (Int iota) ; + e_get + (RSigma.value (snd sigma) { mu = Array p ; region = r}) + (M.pointer_val l) + + let load_float sigma flt loc : term = + let l,r = localized "load float" loc in + match R.kind r with + | Garbled -> M.load_float (fst sigma) flt l + | Single p -> + check_access "load" p (Float flt) ; + RSigma.value (snd sigma) { mu = Value p ; region = r } + | Many p -> + check_access "load" p (Float flt) ; + e_get + (RSigma.value (snd sigma) { mu = Array p ; region = r}) + (M.pointer_val l) + + let load_pointer sigma ty loc : loc = + let l,r = localized "load pointer" loc in + match R.points_to r with + | None -> + Warning.error ~source:"MemRegion" + "Attempt to load pointer without points-to@\n\ + (addr %a, region %a)" + M.pretty l R.Type.pretty r + | Some _ as rp -> + let loc = + match R.kind r with + | Garbled -> M.load_pointer (fst sigma) ty l + | Single p -> + check_access "load" p Ptr ; + M.pointer_loc @@ + RSigma.value (snd sigma) { mu = Value p ; region = r } + | Many p -> + check_access "load" p Ptr ; + M.pointer_loc @@ + e_get + (RSigma.value (snd sigma) { mu = Array p ; region = r}) + (M.pointer_val l) + in make loc rp + + (* ---------------------------------------------------------------------- *) + (* --- Store --- *) + (* ---------------------------------------------------------------------- *) + + let store_int sigma iota loc v : Chunk.t * term = + let l,r = localized "store int" loc in + match R.kind r with + | Garbled -> + let c, m = M.store_int (fst sigma) iota l v in M c, m + | Single p -> + check_access "store" p (Int iota) ; + R { mu = Value p ; region = r }, v + | Many p -> + check_access "store" p (Int iota) ; + let rc = RChunk.{ mu = Array p ; region = r } in + R rc, e_set (RSigma.value (snd sigma) rc) (M.pointer_val l) v + + let store_float sigma flt loc v : Chunk.t * term = + let l,r = localized "store float" loc in + match R.kind r with + | Garbled -> + let c,m = M.store_float (fst sigma) flt l v in M c, m + | Single p -> + check_access "store" p (Float flt) ; + R { mu = Value p ; region = r }, v + | Many p -> + check_access "store" p (Float flt) ; + let rc = RChunk.{ mu = Array p ; region = r } in + R rc, e_set (RSigma.value (snd sigma) rc) (M.pointer_val l) v + + let store_pointer sigma ty loc v : Chunk.t * term = + let l,r = localized "store pointer" loc in + match R.kind r with + | Garbled -> + let c,m = M.store_pointer (fst sigma) ty l v in M c, m + | Single p -> + check_access "store" p Ptr ; + R { mu = Value p ; region = r }, v + | Many p -> + check_access "store" p Ptr ; + let rc = RChunk.{ mu = Array p ; region = r } in + R rc, e_set (RSigma.value (snd sigma) rc) (M.pointer_val l) v + + (* ---------------------------------------------------------------------- *) + (* --- Init --- *) + (* ---------------------------------------------------------------------- *) + + let is_init_atom sigma ty loc : term = + let l,r = localized "init atom" loc in + match R.kind r with + | Garbled -> M.is_init_atom (fst sigma) ty l + | Single _-> RSigma.value (snd sigma) { mu = ValInit ; region = r } + | Many _ -> + e_get + (RSigma.value (snd sigma) { mu = ArrInit ; region = r }) + (M.pointer_val l) + + let set_init_atom sigma ty loc v : Chunk.t * term = + let l,r = localized "init atom" loc in + match R.kind r with + | Garbled -> + let c,m = M.set_init_atom (fst sigma) ty l v in M c, m + | Single _-> R { mu = ValInit ; region = r }, v + | Many _ -> + let rc = RChunk.{ mu = ArrInit ; region = r } in + R rc, e_set (RSigma.value (snd sigma) rc) (M.pointer_val l) v + + let is_init_range sigma ty loc length : pred = + let l,r = localized "init atom" loc in + match R.kind r with + | Garbled -> M.is_init_range (fst sigma) ty l length + | Single _ -> + Lang.F.p_bool @@ RSigma.value (snd sigma) { mu = ValInit ; region = r } + | Many _ -> + let map = RSigma.value (snd sigma) { mu = ArrInit ; region = r } in + let size = e_mul (M.sizeof ty) length in + p_call p_is_init_r [map;M.pointer_val l;size] + + + let set_init ty loc ~length chunk ~current : term = + let l,r = localized "init atom" loc in + match R.kind r, chunk with + | Garbled, M c -> M.set_init ty l ~length c ~current + | Garbled, R _ -> assert false + | Single _, _ -> e_true + | Many _ , _ -> + let size = e_mul (M.sizeof ty) length in + e_fun f_set_init [current;M.pointer_val l;size] + + (* ---------------------------------------------------------------------- *) + (* --- Footprints --- *) + (* ---------------------------------------------------------------------- *) + + let mfootprint ~value obj l = + if value + then M.value_footprint obj l + else M.init_footprint obj l + + let rec footprint ~value obj loc = match loc with + | Null -> Sigma.mdomain @@ mfootprint ~value obj M.null + | Raw l -> Sigma.mdomain @@ mfootprint ~value obj l + | Loc(l,r) -> + match obj with + | C_comp { cfields = None} -> Domain.empty + | C_comp { cfields = Some fds } -> + List.fold_left + (fun dom fd -> + let obj = Ctypes.object_of fd.ftype in + let loc = field loc fd in + Domain.union dom (footprint ~value obj loc) + ) Domain.empty fds + | C_array { arr_element = elt } -> + let obj = object_of elt in + footprint ~value obj (shift loc obj e_zero) + | C_int _ | C_float _ | C_pointer _ -> + match R.kind r with + | Garbled -> + Sigma.mdomain @@ mfootprint ~value obj l + | Single p -> + let mu = if value then RChunk.Value p else ValInit in + Sigma.rdomain @@ RHeap.Set.singleton { mu ; region = r } + | Many p -> + let mu = if value then RChunk.Array p else ArrInit in + Sigma.rdomain @@ RHeap.Set.singleton { mu ; region = r } + + let value_footprint = footprint ~value:true + let init_footprint = footprint ~value:false + + end + + type loc = Loader.loc + type segment = loc rloc + + open Loader + module LOADER = MemLoader.Make(Loader) + + let load = LOADER.load + let load_init = LOADER.load_init + let stored = LOADER.stored + let stored_init = LOADER.stored_init + let copied = LOADER.copied + let copied_init = LOADER.copied_init + let initialized = LOADER.initialized + let domain = LOADER.domain + let assigned = LOADER.assigned + + (* {2 Reversing the Model} *) + + type state = M.state + + let state sigma = M.state (fst sigma) + + let lookup s e = M.lookup s e + + let updates = M.updates + + let apply = M.apply + + let iter = M.iter + + let pretty fmt (l: loc) = + match l with + | Null -> M.pretty fmt M.null + | Raw l -> M.pretty fmt l + | Loc (l,r) -> Format.fprintf fmt "%a@%a" M.pretty l R.Type.pretty r + + (* {2 Memory Model API} *) + + let vars l = M.vars @@ loc l + let occurs x l = M.occurs x @@ loc l + let null = Null + + let literal ~eid:eid str = make (M.literal ~eid str) (R.literal ~eid str) + + let cvar v = make (M.cvar v) (R.cvar v) + let field = field + let shift = shift + + let pointer_loc t = Raw (M.pointer_loc t) + let pointer_val l = M.pointer_val @@ loc l + let base_addr l = Raw (M.base_addr @@ loc l) + let base_offset l = M.base_offset @@ loc l + let block_length sigma obj l = M.block_length (fst sigma) obj @@ loc l + let is_null = function Null -> p_true | Raw l | Loc(l,_) -> M.is_null l + let loc_of_int obj t = Raw (M.loc_of_int obj t) + let int_of_loc iota l = M.int_of_loc iota @@ loc l + + let cast conv l = + let l0 = loc l in + let r0 = reg l in + make (M.cast conv l0) r0 + + let loc_eq a b = M.loc_eq (loc a) (loc b) + let loc_lt a b = M.loc_lt (loc a) (loc b) + let loc_neq a b = M.loc_neq (loc a) (loc b) + let loc_leq a b = M.loc_leq (loc a) (loc b) + let loc_diff obj a b = M.loc_diff obj (loc a) (loc b) + + let rloc = function + | Rloc(obj, l) -> Rloc (obj, loc l) + | Rrange(l, obj, inf, sup) -> Rrange(loc l, obj, inf, sup) + + let rloc_region = function Rloc(_,l) | Rrange(l,_,_,_) -> reg l + + let valid sigma acs r = M.valid (fst sigma) acs @@ rloc r + let invalid sigma r = M.invalid (fst sigma) (rloc r) + + let included (a : segment) (b : segment) = + match rloc_region a, rloc_region b with + | Some ra, Some rb when R.separated ra rb -> p_false + | _ -> M.included (rloc a) (rloc b) + + let separated (a : segment) (b : segment) = + match rloc_region a, rloc_region b with + | Some ra, Some rb when R.separated ra rb -> p_true + | _ -> M.separated (rloc a) (rloc b) + + let alloc sigma vars = + if vars = [] then sigma else + let m,r = sigma in M.alloc m vars, r + + let scope seq scope vars = M.scope (mseq seq) scope vars + + let global sigma p = M.global (fst sigma) p + + let frame sigma = + let pool = ref @@ M.frame (fst sigma) in + let assume p = pool := p :: !pool in + RSigma.iter + (fun c m -> + let open RChunk in + match c.mu with + | ValInit -> () + | ArrInit -> assume @@ MemMemory.cinits (e_var m) + | Value Ptr -> assume @@ global sigma (e_var m) + | Array Ptr -> assume @@ MemMemory.framed (e_var m) + | Value (Int _ | Float _) | Array (Int _ | Float _) -> () + ) (snd sigma) ; + !pool + + let is_well_formed sigma = + let pool = ref @@ [M.is_well_formed (fst sigma)] in + let assume p = pool := p :: !pool in + RSigma.iter + (fun c m -> + let open RChunk in + match c.mu with + | ValInit | ArrInit -> () + | Value (Int iota) -> assume @@ Cint.range iota (e_var m) + | Array (Int iota) -> + let a = Lang.freshvar ~basename:"p" @@ Lang.t_addr () in + let b = e_get (e_var m) (e_var a) in + assume @@ p_forall [a] (Cint.range iota b) + | Value (Float _ | Ptr) | Array (Float _ | Ptr) -> () + ) (snd sigma) ; + p_conj !pool + +end diff --git a/src/plugins/wp/MemRegion.mli b/src/plugins/wp/MemRegion.mli new file mode 100644 index 0000000000000000000000000000000000000000..545e03ac004fff4c80fce87a0cb2b1ad1e9066eb --- /dev/null +++ b/src/plugins/wp/MemRegion.mli @@ -0,0 +1,82 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2024 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* 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 licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types +open Ctypes +open Lang.F +open Sigs + +type primitive = | Int of c_int | Float of c_float | Ptr +type kind = Single of primitive | Many of primitive | Garbled +val pp_kind : Format.formatter -> kind -> unit + +(* -------------------------------------------------------------------------- *) +(* --- Region Memory Model --- *) +(* -------------------------------------------------------------------------- *) + +module type RegionProxy = +sig + type region + module Type : Sigs.Type with type t = region + val id : region -> int + val of_id : int -> region option + val kind : region -> kind + val name : region -> string option + val cvar : varinfo -> region option + val field : region -> fieldinfo -> region option + val shift : region -> c_object -> region option + val points_to : region -> region option + val literal : eid:int -> Cstring.cst -> region option + val separated : region -> region -> bool + val included : region -> region -> bool +end + +module type ModelWithLoader = +sig + include Sigs.Model + val sizeof : c_object -> term + + val last : sigma -> c_object -> loc -> term + val frames : c_object -> loc -> chunk -> frame list + + val havoc : c_object -> loc -> length:term -> chunk -> fresh:term -> current:term -> term + + val eqmem_forall : c_object -> loc -> chunk -> term -> term -> var list * pred * pred + + val load_int : sigma -> c_int -> loc -> term + val load_float : sigma -> c_float -> loc -> term + val load_pointer : sigma -> typ -> loc -> loc + + val store_int : sigma -> c_int -> loc -> term -> chunk * term + val store_float : sigma -> c_float -> loc -> term -> chunk * term + val store_pointer : sigma -> typ -> loc -> term -> chunk * term + + val set_init_atom : sigma -> c_object -> loc -> term -> chunk * term + val set_init : c_object -> loc -> length:term -> chunk -> current:term -> term + val is_init_atom : sigma -> c_object -> loc -> term + val is_init_range : sigma -> c_object -> loc -> term -> pred + + val value_footprint : c_object -> loc -> domain + val init_footprint : c_object -> loc -> domain +end + +module Make : RegionProxy -> ModelWithLoader -> Sigs.Model diff --git a/src/plugins/wp/MemTyped.mli b/src/plugins/wp/MemTyped.mli index ccdbfbd0b9786393e0819d561a77ce899b49aecc..05488e25a7020525b67f8ac8c9f1e14d9364a19a 100644 --- a/src/plugins/wp/MemTyped.mli +++ b/src/plugins/wp/MemTyped.mli @@ -24,7 +24,7 @@ (* --- Typed Memory Model --- *) (* -------------------------------------------------------------------------- *) -include Sigs.Model - type pointer = NoCast | Fits | Unsafe val pointer : pointer Context.value + +include Sigs.Model diff --git a/src/plugins/wp/RegionAnalysis.ml b/src/plugins/wp/RegionAnalysis.ml new file mode 100644 index 0000000000000000000000000000000000000000..008824f22d3745c7d4ae35baaa1d7967ffbd2892 --- /dev/null +++ b/src/plugins/wp/RegionAnalysis.ml @@ -0,0 +1,104 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2024 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* 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 licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +(* -------------------------------------------------------------------------- *) +(* --- Proxy to Region Analysis for Region Model --- *) +(* -------------------------------------------------------------------------- *) + +type region = Region.node + +let get_map () = + match WpContext.get_scope () with + | Kf kf -> Region.map kf + | Global -> Wp_parameters.not_yet_implemented "[region] logic context" + +let id region = Region.uid (get_map ()) region + +let of_id id = + try Some (Region.find (get_map ()) id) + with Not_found -> None + +module Type = +struct + type t = region + let hash = Region.id + let equal r1 r2 = (Region.id r1 = Region.id r2) + let compare r1 r2 = Int.compare (Region.id r1) (Region.id r2) + let pretty fmt r = Format.fprintf fmt "R%03d" @@ Region.id r +end + +(* Keeping track of the decision to apply which memory model to each region *) +module Kind = WpContext.Generator(Type) + (struct + open MemRegion + let name = "Wp.RegionAnalysis.Kind" + type key = region + type data = kind + let kind map r p = if Region.singleton map r then Single p else Many p + let compile r = + let map = get_map () in + match Region.typed map r with + | Some ty -> + begin + match Ctypes.object_of ty with + | C_int i -> kind map r (Int i) + | C_float f -> kind map r (Float f) + | C_pointer _ -> kind map r Ptr + | _ -> Garbled + end + | None -> Garbled + end) + +module Name = WpContext.Generator(Type) + (struct + let name = "Wp.RegionAnalysis.Name" + type key = region + type data = string option + let compile r = + let map = get_map () in + match Region.labels map r with + | label::_ -> Some label + | [] -> + match Region.roots map r with + | v::_ -> Some v.vorig_name + | _ -> None + end) + +let kind = Kind.get +let name = Name.get +let points_to region = Region.points_to (get_map ()) region +let separated r1 r2 = Region.separated (get_map ()) r1 r2 +let included r1 r2 = Region.included (get_map ()) r1 r2 + +let cvar var = + try Some (Region.cvar (get_map ()) var) + with Not_found -> None + +let field r fd = + try Some (Region.field (get_map ()) r fd) + with Not_found -> None + +let shift r obj = + try Some (Region.index (get_map ()) r (Ctypes.object_to obj)) + with Not_found -> None + +let literal ~eid _ = ignore eid ; None diff --git a/src/plugins/wp/RegionAnalysis.mli b/src/plugins/wp/RegionAnalysis.mli new file mode 100644 index 0000000000000000000000000000000000000000..ebce9717a1d73bcdd4abc795a6b4ba355da31d92 --- /dev/null +++ b/src/plugins/wp/RegionAnalysis.mli @@ -0,0 +1,27 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2024 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* 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 licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +(* -------------------------------------------------------------------------- *) +(* --- Region Analysis API for Region Memory Model --- *) +(* -------------------------------------------------------------------------- *) + +include MemRegion.RegionProxy diff --git a/src/plugins/wp/Sigs.ml b/src/plugins/wp/Sigs.ml index 1fa08a3da0a8c43ab4271d1bffa0065883883c18..c5d734ab9ab8d2f3d72efe26e281f241f087e79d 100644 --- a/src/plugins/wp/Sigs.ml +++ b/src/plugins/wp/Sigs.ml @@ -129,6 +129,15 @@ type update = Mstore of s_lval * term (** {1 Memory Models} *) (* -------------------------------------------------------------------------- *) +module type Type = +sig + type t + val hash : t -> int + val equal : t -> t -> bool + val compare : t -> t -> int + val pretty : Format.formatter -> t -> unit +end + (** Memory Chunks. The concrete memory is partionned into a vector of abstract data. @@ -148,10 +157,7 @@ sig val self : string (** Chunk names, for pretty-printing. *) - val hash : t -> int - val equal : t -> t -> bool - val compare : t -> t -> int - val pretty : Format.formatter -> t -> unit + include Type with type t := t val tau_of_chunk : t -> tau (** The type of data hold in a chunk. *) diff --git a/src/plugins/wp/ctypes.ml b/src/plugins/wp/ctypes.ml index eba61ce0010a6e6c23e66022e3ac36c98d1a7fbe..71ece40c6ca4d674f5dff4adf6bcdebbe6895073 100644 --- a/src/plugins/wp/ctypes.ml +++ b/src/plugins/wp/ctypes.ml @@ -384,6 +384,35 @@ let () = cmp := compare ; end +(* -------------------------------------------------------------------------- *) +(* --- Revert c_object to typ --- *) +(* -------------------------------------------------------------------------- *) + +let ikinds = [ + IBool;IChar; + ISChar;IUChar; + IInt;IUInt; + IShort;IUShort; + ILong;IULong; + ILongLong;IULongLong +] +let fkinds = [FFloat;FDouble;FLongDouble] + +let to_ikind iota = + List.find (fun ik -> c_int ik = iota) ikinds + +let to_fkind flt = + List.find (fun fk -> c_float fk = flt) fkinds + +let object_to = function + | C_int i -> TInt(to_ikind i,[]) + | C_float f -> TFloat(to_fkind f,[]) + | C_pointer typ -> TPtr(typ,[]) + | C_comp comp -> TComp(comp,[]) + | C_array { arr_element = elt ; arr_flat = None } -> TArray(elt,None,[]) + | C_array { arr_element = elt ; arr_flat = Some { arr_size = size } } -> + TArray(elt,Some (Cil.integer ~loc:Location.unknown size),[]) + (* -------------------------------------------------------------------------- *) (* --- Accessor Utilities --- *) (* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/ctypes.mli b/src/plugins/wp/ctypes.mli index f38d4d05826751e7eef0f5ff6ad06028eefb6764..060c3e9ffa0199e75a5102e3538b08d306dd6295 100644 --- a/src/plugins/wp/ctypes.mli +++ b/src/plugins/wp/ctypes.mli @@ -97,7 +97,14 @@ val c_int : ikind -> c_int val c_float : fkind -> c_float (** Conforms to {!Machine.theMachine} *) +val to_ikind : c_int -> ikind +(** @raises Not_found *) + +val to_fkind : c_float -> fkind +(** @raises Not_found *) + val object_of : typ -> c_object +val object_to : c_object -> typ val is_pointer : c_object -> bool diff --git a/src/plugins/wp/dune b/src/plugins/wp/dune index b46cbeb373eb6214a4aa1d096431328d18678263..44dfa7418530fdc02756c3d23ecd077f5f3cd0c2 100644 --- a/src/plugins/wp/dune +++ b/src/plugins/wp/dune @@ -40,6 +40,7 @@ frama-c.kernel frama-c-rtegen.core frama-c-server.core + frama-c-region.core qed why3 zarith ocamlgraph (select wp_eva.ml from (frama-c-eva.core -> wp_eva.enabled.ml) diff --git a/src/plugins/wp/gui/GuiPanel.ml b/src/plugins/wp/gui/GuiPanel.ml index 4b25d1880a95c901a759b5d4830e83c42a2b67cb..69de6705145de3f9286844b9e5312675ac3d687d 100644 --- a/src/plugins/wp/gui/GuiPanel.ml +++ b/src/plugins/wp/gui/GuiPanel.ml @@ -99,7 +99,7 @@ let run_and_prove (* --- Model Panel --- *) (* ------------------------------------------------------------------------ *) -type memory = TREE | HOARE | TYPED | EVA | BYTES +type memory = TREE | HOARE | TYPED | EVA | BYTES | REGION class model_selector (main : Design.main_window_extension_points) = let dialog = new Wpane.dialog @@ -152,6 +152,7 @@ class model_selector (main : Design.main_window_extension_points) = | Typed m -> memory#set TYPED ; c_casts#set (m = MemTyped.Unsafe) | Eva -> memory#set EVA | Bytes -> memory#set BYTES + | Region -> memory#set REGION ) ; c_byref#set (s.mvar = Ref) ; c_ctxt#set (s.mvar = Caveat) ; @@ -167,6 +168,7 @@ class model_selector (main : Design.main_window_extension_points) = (if c_casts#get then MemTyped.Unsafe else MemTyped.Fits) | EVA -> Eva | BYTES -> Bytes + | REGION -> Region in { mheap = m ; mvar = if c_ctxt#get then Caveat else if c_byref#get then Ref else Var ; diff --git a/src/plugins/wp/tests/ptests_config b/src/plugins/wp/tests/ptests_config index 9ea351239b8b70f111befdca7dd03aebaf071a90..b70003f3d7cc939ab94a76c82cddc31ce8b727e5 100644 --- a/src/plugins/wp/tests/ptests_config +++ b/src/plugins/wp/tests/ptests_config @@ -1,8 +1,12 @@ -DEFAULT_SUITES= wp wp_acsl wp_plugin wp_ce wp_bts wp_store wp_hoare -DEFAULT_SUITES= wp_typed wp_bytes wp_usage wp_gallery wp_manual wp_tip +## General Purpose +DEFAULT_SUITES= wp wp_manual wp_usage wp_plugin wp_gallery wp_tip wp_bts +qualif_SUITES= wp wp_manual wp_usage wp_plugin wp_gallery wp_tip wp_bts -qualif_SUITES= wp wp_acsl wp_plugin wp_bts wp_store wp_hoare -qualif_SUITES= wp_typed wp_bytes wp_usage wp_gallery wp_manual wp_tip -qualif_SUITES= why3 +## Memory Models +DEFAULT_SUITES= wp_acsl wp_hoare wp_store wp_typed wp_bytes wp_region +qualif_SUITES= wp_acsl wp_hoare wp_store wp_typed wp_bytes wp_region -ce_SUITES= wp_ce +## Specific Objectives +qualif_SUITES= why3 +DEFAULT_SUITES= wp_ce +ce_SUITES= wp_ce diff --git a/src/plugins/wp/tests/wp_region/affectations.c b/src/plugins/wp/tests/wp_region/affectations.c new file mode 100644 index 0000000000000000000000000000000000000000..601aa47036e6badcfe4b339ce47cc1141c8ecc9f --- /dev/null +++ b/src/plugins/wp/tests/wp_region/affectations.c @@ -0,0 +1,22 @@ +/*@ predicate pointed(int *p, int* q) = p==q || \separated(p,q); +*/ + +/*@ + requires \valid(p); + requires \valid(q); + requires pointed(p,q); + assigns *p, *q; + region PQ: *p, *q; + behavior EQ: + assumes p == q; + ensures P: *p == \old(*p); + ensures Q: *q == \old(*q); + behavior SEP: + assumes p != q; + ensures Q: *p == \old(*p)+1; + ensures Q: *q == \old(*q)-1; +*/ +void f(int* p, int* q) { + (*p)++; + (*q)--; +} diff --git a/src/plugins/wp/tests/wp_region/array.c b/src/plugins/wp/tests/wp_region/array.c new file mode 100644 index 0000000000000000000000000000000000000000..825ceb18aebbc8a3421fe4e49f9b25b92c2d2051 --- /dev/null +++ b/src/plugins/wp/tests/wp_region/array.c @@ -0,0 +1,23 @@ +struct S { + int len; + int content[10]; +}; +/*@ predicate pointed(struct S *p, struct S * q) = p==q || \separated(p,q); +*/ + + +/*@ + requires pointed(a, b); + region AB: *a, *b; + ensures a!=b ==> a->content[2] == \old(a->content[2]) + b->content[2]; + ensures a==b ==> a->content[2] == 2*\old(a->content[2]); + ensures a->content[2] == \old(a->content[2] + b->content[2]); + ensures a->content[4] == \old(a->content[4]); +*/ +void add_first4(struct S * a , struct S * b ) +{ + a->content[0] += b->content[0]; + a->content[1] += b->content[1]; + a->content[2] += b->content[2]; + a->content[3] += b->content[3]; +} diff --git a/src/plugins/wp/tests/wp_region/copy_array.c b/src/plugins/wp/tests/wp_region/copy_array.c new file mode 100644 index 0000000000000000000000000000000000000000..4084930a6721494ad12a67b7ed940b1b9cde3112 --- /dev/null +++ b/src/plugins/wp/tests/wp_region/copy_array.c @@ -0,0 +1,21 @@ + +/*@ requires n>=0 ; + requires \separated( a+ (0..n-1) , b + (0..n-1) ); + ensures \forall integer k ; 0 <= k < n ==> a[k] == b[k] ; + assigns a[0..n-1] ; + region AB: a[..], b[..]; + */ +void copy( int * a , int * b , int n ) +{ + /*@ loop invariant Range: 0 <= i <= n ; + loop invariant Copy: \forall integer k ; 0 <= k < i ==> a[k] == b[k] ; + loop assigns i , a[0..n-1] ; + loop variant n - i; + */ + for (int i = 0 ; i < n ; i++) { + L: + a[i] = b[i] ; + /*@ assert A: \forall integer k ; 0 <= k < i ==> a[k] == \at(a[k],L); */ + /*@ assert B: \forall integer k ; 0 <= k < i ==> b[k] == \at(b[k],L); */ + } +} diff --git a/src/plugins/wp/tests/wp_region/heterogenous_cast.c b/src/plugins/wp/tests/wp_region/heterogenous_cast.c new file mode 100644 index 0000000000000000000000000000000000000000..ef9c1c52cfb71e9dc96f179ee7a16596c2b3557d --- /dev/null +++ b/src/plugins/wp/tests/wp_region/heterogenous_cast.c @@ -0,0 +1,10 @@ +/*@ + assigns *p; +@*/ +void g (int *p) { + *p = 42; + short *q = (short*) p; + q[0] = -1; + q[1] = -1; + //@ assert *p == -1; +} diff --git a/src/plugins/wp/tests/wp_region/oracle/affectations.res.oracle b/src/plugins/wp/tests/wp_region/oracle/affectations.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..90b845e92270d85fecec252c6d7b263039d4c3f6 --- /dev/null +++ b/src/plugins/wp/tests/wp_region/oracle/affectations.res.oracle @@ -0,0 +1,86 @@ +# frama-c -wp -wp-model 'Region' [...] +[kernel] Parsing affectations.c (with preprocessing) +[wp] Running WP plugin... +[wp] [Valid] Goal f_exits (Cfg) (Unreachable) +[wp] [Valid] Goal f_terminates (Cfg) (Trivial) +[wp] Warning: Missing RTE guards +------------------------------------------------------------ + Function f +------------------------------------------------------------ + +Goal Assigns (file affectations.c, line 8) in 'f' (1/2): +Effect at line 20 +Prove: true. + +------------------------------------------------------------ + +Goal Assigns (file affectations.c, line 8) in 'f' (2/2): +Effect at line 21 +Prove: true. + +------------------------------------------------------------ +------------------------------------------------------------ + Function f with behavior EQ +------------------------------------------------------------ + +Goal Post-condition for 'EQ' 'P' in 'f': +Prove: true. + +------------------------------------------------------------ + +Goal Post-condition for 'EQ' 'Q' in 'f': +Prove: true. + +------------------------------------------------------------ +------------------------------------------------------------ + Function f with behavior SEP +------------------------------------------------------------ + +Goal Post-condition for 'SEP' 'Q' in 'f': +Let x = Msint32_0[p]. +Let x_1 = 1 + x. +Let m = Msint32_0[p <- x_1]. +Let x_2 = m[q]. +Let x_3 = x_2 - 1. +Let x_4 = m[q <- x_3][p]. +Assume { + Type: is_sint32(x) /\ is_sint32(Msint32_0[q]) /\ is_sint32(x_2) /\ + is_sint32(x_3) /\ is_sint32(x_4). + (* Heap *) + Type: (region(p.base) <= 0) /\ (region(q.base) <= 0) /\ linked(alloc_0). + (* Pre-condition *) + Have: valid_rw(alloc_0, p, 4). + (* Pre-condition *) + Have: valid_rw(alloc_0, q, 4). + (* Pre-condition *) + Have: P_pointed(p, q). + (* Pre-condition for 'SEP' *) + Have: q != p. +} +Prove: x_4 = x_1. + +------------------------------------------------------------ + +Goal Post-condition for 'SEP' 'Q' in 'f': +Let x = Msint32_0[p]. +Let x_1 = Msint32_0[q]. +Let m = Msint32_0[p <- 1 + x]. +Let x_2 = m[q]. +Let x_3 = x_2 - 1. +Assume { + Type: is_sint32(x) /\ is_sint32(x_1) /\ is_sint32(x_2) /\ is_sint32(x_3) /\ + is_sint32(m[q <- x_3][p]). + (* Heap *) + Type: (region(p.base) <= 0) /\ (region(q.base) <= 0) /\ linked(alloc_0). + (* Pre-condition *) + Have: valid_rw(alloc_0, p, 4). + (* Pre-condition *) + Have: valid_rw(alloc_0, q, 4). + (* Pre-condition *) + Have: P_pointed(p, q). + (* Pre-condition for 'SEP' *) + Have: q != p. +} +Prove: x_2 = x_1. + +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_region/oracle/array.res.oracle b/src/plugins/wp/tests/wp_region/oracle/array.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..5872636534506a3deb2ac371e6e23d96731f49a6 --- /dev/null +++ b/src/plugins/wp/tests/wp_region/oracle/array.res.oracle @@ -0,0 +1,91 @@ +# frama-c -wp -wp-model 'Region' [...] +[kernel] Parsing array.c (with preprocessing) +[wp] Running WP plugin... +[wp] [Valid] Goal add_first4_exits (Cfg) (Unreachable) +[wp] [Valid] Goal add_first4_terminates (Cfg) (Trivial) +[wp] Warning: Missing RTE guards +------------------------------------------------------------ + Function add_first4 +------------------------------------------------------------ + +Goal Post-condition (file array.c, line 12) in 'add_first4': +Let a_1 = shiftfield_F1_S_content(a). +Let a_2 = shift_sint32(a_1, 0). +Let x = Msint32_0[a_2]. +Let a_3 = shift_sint32(a_1, 1). +Let x_1 = Msint32_0[a_3]. +Let a_4 = shift_sint32(a_1, 2). +Let x_2 = Msint32_0[a_4]. +Let a_5 = shift_sint32(a_1, 3). +Let x_3 = Msint32_0[a_5]. +Let a_6 = shiftfield_F1_S_content(b). +Let x_4 = Msint32_0[shift_sint32(a_6, 0)]. +Let a_7 = shift_sint32(a_6, 2). +Let m = Msint32_0[a_2 <- x + x_4]. +Let x_5 = m[shift_sint32(a_6, 1)]. +Let m_1 = m[a_3 <- x_1 + x_5]. +Let x_6 = m_1[a_7]. +Let x_7 = x_2 + x_6. +Let m_2 = m_1[a_4 <- x_7]. +Let x_8 = m_2[shift_sint32(a_6, 3)]. +Let x_9 = m_2[a_5 <- x_3 + x_8][a_7]. +Assume { + Type: is_sint32(x) /\ is_sint32(x_1) /\ is_sint32(x_2) /\ is_sint32(x_3) /\ + is_sint32(Msint32_0[shift_sint32(a_1, 4)]) /\ is_sint32(x_4) /\ + is_sint32(Msint32_0[a_7]) /\ is_sint32(x_5) /\ is_sint32(x_6) /\ + is_sint32(x_7) /\ is_sint32(x_8) /\ is_sint32(x_9). + (* Heap *) + Type: (region(a.base) <= 0) /\ (region(b.base) <= 0). + (* Goal *) + When: b != a. + (* Pre-condition *) + Have: P_pointed(a, b). +} +Prove: x_9 = x_6. + +------------------------------------------------------------ + +Goal Post-condition (file array.c, line 13) in 'add_first4': +Prove: true. + +------------------------------------------------------------ + +Goal Post-condition (file array.c, line 14) in 'add_first4': +Let a_1 = shiftfield_F1_S_content(a). +Let a_2 = shift_sint32(a_1, 0). +Let x = Msint32_0[a_2]. +Let a_3 = shift_sint32(a_1, 1). +Let x_1 = Msint32_0[a_3]. +Let a_4 = shift_sint32(a_1, 2). +Let x_2 = Msint32_0[a_4]. +Let a_5 = shift_sint32(a_1, 3). +Let x_3 = Msint32_0[a_5]. +Let a_6 = shiftfield_F1_S_content(b). +Let x_4 = Msint32_0[shift_sint32(a_6, 0)]. +Let a_7 = shift_sint32(a_6, 2). +Let x_5 = Msint32_0[a_7]. +Let m = Msint32_0[a_2 <- x + x_4]. +Let x_6 = m[shift_sint32(a_6, 1)]. +Let m_1 = m[a_3 <- x_1 + x_6]. +Let x_7 = m_1[a_7]. +Let x_8 = x_2 + x_7. +Let m_2 = m_1[a_4 <- x_8]. +Let x_9 = m_2[shift_sint32(a_6, 3)]. +Assume { + Type: is_sint32(x) /\ is_sint32(x_1) /\ is_sint32(x_2) /\ is_sint32(x_3) /\ + is_sint32(Msint32_0[shift_sint32(a_1, 4)]) /\ is_sint32(x_4) /\ + is_sint32(x_5) /\ is_sint32(x_6) /\ is_sint32(x_7) /\ is_sint32(x_8) /\ + is_sint32(x_9) /\ is_sint32(m_2[a_5 <- x_3 + x_9][a_7]). + (* Heap *) + Type: (region(a.base) <= 0) /\ (region(b.base) <= 0). + (* Pre-condition *) + Have: P_pointed(a, b). +} +Prove: x_7 = x_5. + +------------------------------------------------------------ + +Goal Post-condition (file array.c, line 15) in 'add_first4': +Prove: true. + +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_region/oracle/copy_array.res.oracle b/src/plugins/wp/tests/wp_region/oracle/copy_array.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..75bce10e9805bbf6ad133de1cd99b97755d66e1d --- /dev/null +++ b/src/plugins/wp/tests/wp_region/oracle/copy_array.res.oracle @@ -0,0 +1,213 @@ +# frama-c -wp -wp-model 'Region' [...] +[kernel] Parsing copy_array.c (with preprocessing) +[wp] Running WP plugin... +[wp] [Valid] Goal copy_exits (Cfg) (Unreachable) +[wp] [Valid] Goal copy_terminates (Cfg) (Trivial) +[wp] Warning: Missing RTE guards +[region] copy_array.c:15: Warning: Annotations not analyzed +[region] copy_array.c:18: Warning: Annotations not analyzed +[region] copy_array.c:19: Warning: Annotations not analyzed +------------------------------------------------------------ + Function copy +------------------------------------------------------------ + +Goal Post-condition (file copy_array.c, line 4) in 'copy': +Let a_1 = shift_sint32(a, 0). +Let a_2 = havoc(Msint32_undef_0, Msint32_0, a_1, n). +Let x = 4 * n. +Let a_3 = havoc(Msint32_undef_0, Msint32_0, a_1, i). +Assume { + Type: is_sint32(i) /\ is_sint32(n). + (* Heap *) + Type: (region(a.base) <= 0) /\ (region(b.base) <= 0). + (* Goal *) + When: (0 <= i_1) /\ (i_1 < n). + (* Pre-condition *) + Have: separated(a_1, x, shift_sint32(b, 0), x). + (* Invariant 'Range' *) + Have: 0 <= n. + (* Invariant 'Range' *) + Have: (0 <= i) /\ (i <= n). + (* Invariant 'Copy' *) + Have: forall i_2 : Z. ((0 <= i_2) -> ((i_2 < i) -> + (a_2[shift_sint32(b, i_2)] = a_2[shift_sint32(a, i_2)]))). + (* Else *) + Have: n <= i. +} +Prove: a_3[shift_sint32(b, i_1)] = a_3[shift_sint32(a, i_1)]. + +------------------------------------------------------------ + +Goal Preservation of Invariant 'Copy' (file copy_array.c, line 11): +Let a_1 = shift_sint32(a, 0). +Let a_2 = havoc(Msint32_undef_0, Msint32_0, a_1, n). +Let a_3 = a_2[shift_sint32(a, i) <- a_2[shift_sint32(b, i)]]. +Let x = 4 * n. +Assume { + Type: is_sint32(i) /\ is_sint32(n) /\ is_sint32(1 + i). + (* Heap *) + Type: (region(a.base) <= 0) /\ (region(b.base) <= 0). + (* Goal *) + When: (i_1 <= i) /\ (0 <= i_1). + (* Pre-condition *) + Have: separated(a_1, x, shift_sint32(b, 0), x). + (* Invariant 'Range' *) + Have: 0 <= n. + (* Invariant 'Range' *) + Have: (0 <= i) /\ (i <= n). + (* Invariant 'Copy' *) + Have: forall i_2 : Z. ((0 <= i_2) -> ((i_2 < i) -> + (a_2[shift_sint32(b, i_2)] = a_2[shift_sint32(a, i_2)]))). + (* Then *) + Have: i < n. + (* Assertion 'A' *) + Have: forall i_2 : Z. let a_4 = shift_sint32(a, i_2) in ((0 <= i_2) -> + ((i_2 < i) -> (a_3[a_4] = a_2[a_4]))). + (* Assertion 'B' *) + Have: forall i_2 : Z. let a_4 = shift_sint32(b, i_2) in ((0 <= i_2) -> + ((i_2 < i) -> (a_3[a_4] = a_2[a_4]))). + (* Invariant 'Range' *) + Have: (-1) <= i. +} +Prove: a_3[shift_sint32(b, i_1)] = a_3[shift_sint32(a, i_1)]. + +------------------------------------------------------------ + +Goal Establishment of Invariant 'Copy' (file copy_array.c, line 11): +Prove: true. + +------------------------------------------------------------ + +Goal Preservation of Invariant 'Range' (file copy_array.c, line 10): +Prove: true. + +------------------------------------------------------------ + +Goal Establishment of Invariant 'Range' (file copy_array.c, line 10): +Prove: true. + +------------------------------------------------------------ + +Goal Assertion 'A' (file copy_array.c, line 18): +Let a_1 = shift_sint32(a, 0). +Let a_2 = havoc(Msint32_undef_0, Msint32_0, a_1, n). +Let x = 4 * n. +Let a_3 = shift_sint32(a, i_1). +Assume { + Type: is_sint32(i) /\ is_sint32(n). + (* Heap *) + Type: (region(a.base) <= 0) /\ (region(b.base) <= 0). + (* Goal *) + When: (0 <= i_1) /\ (i_1 < i). + (* Pre-condition *) + Have: separated(a_1, x, shift_sint32(b, 0), x). + (* Invariant 'Range' *) + Have: 0 <= n. + (* Invariant 'Range' *) + Have: (0 <= i) /\ (i <= n). + (* Invariant 'Copy' *) + Have: forall i_2 : Z. ((0 <= i_2) -> ((i_2 < i) -> + (a_2[shift_sint32(b, i_2)] = a_2[shift_sint32(a, i_2)]))). + (* Then *) + Have: i < n. +} +Prove: a_2[shift_sint32(a, i) <- a_2[shift_sint32(b, i)]][a_3] = a_2[a_3]. + +------------------------------------------------------------ + +Goal Assertion 'B' (file copy_array.c, line 19): +Let a_1 = shift_sint32(a, 0). +Let a_2 = havoc(Msint32_undef_0, Msint32_0, a_1, n). +Let a_3 = a_2[shift_sint32(a, i) <- a_2[shift_sint32(b, i)]]. +Let x = 4 * n. +Let a_4 = shift_sint32(b, i_1). +Assume { + Type: is_sint32(i) /\ is_sint32(n). + (* Heap *) + Type: (region(a.base) <= 0) /\ (region(b.base) <= 0). + (* Goal *) + When: (0 <= i_1) /\ (i_1 < i). + (* Pre-condition *) + Have: separated(a_1, x, shift_sint32(b, 0), x). + (* Invariant 'Range' *) + Have: 0 <= n. + (* Invariant 'Range' *) + Have: (0 <= i) /\ (i <= n). + (* Invariant 'Copy' *) + Have: forall i_2 : Z. ((0 <= i_2) -> ((i_2 < i) -> + (a_2[shift_sint32(b, i_2)] = a_2[shift_sint32(a, i_2)]))). + (* Then *) + Have: i < n. + (* Assertion 'A' *) + Have: forall i_2 : Z. let a_5 = shift_sint32(a, i_2) in ((0 <= i_2) -> + ((i_2 < i) -> (a_3[a_5] = a_2[a_5]))). +} +Prove: a_3[a_4] = a_2[a_4]. + +------------------------------------------------------------ + +Goal Loop assigns (file copy_array.c, line 12) (1/3): +Prove: true. + +------------------------------------------------------------ + +Goal Loop assigns (file copy_array.c, line 12) (2/3): +Effect at line 15 +Prove: true. + +------------------------------------------------------------ + +Goal Loop assigns (file copy_array.c, line 12) (3/3): +Effect at line 17 +Let a_1 = shift_sint32(a, 0). +Let a_2 = havoc(Msint32_undef_0, Msint32_0, a_1, n). +Let x = i - 1. +Let a_3 = shift_sint32(a, x). +Let a_4 = a_2[a_3 <- a_2[shift_sint32(b, x)]]. +Let x_1 = 4 * n. +Assume { + Type: is_sint32(i) /\ is_sint32(n) /\ is_sint32(x). + (* Heap *) + Type: (region(a.base) <= 0) /\ (region(b.base) <= 0) /\ linked(alloc_0). + (* Goal *) + When: !invalid(alloc_0[P_a_24 <- 8][P_b_25 <- 8], a_3, 4). + (* Pre-condition *) + Have: separated(a_1, x_1, shift_sint32(b, 0), x_1). + (* Invariant 'Range' *) + Have: 0 <= n. + (* Invariant 'Range' *) + Have: (0 < i) /\ (i <= (1 + n)). + (* Invariant 'Copy' *) + Have: forall i_1 : Z. ((0 <= i_1) -> (((2 + i_1) <= i) -> + (a_2[shift_sint32(b, i_1)] = a_2[shift_sint32(a, i_1)]))). + (* Then *) + Have: i <= n. + (* Assertion 'A' *) + Have: forall i_1 : Z. let a_5 = shift_sint32(a, i_1) in ((0 <= i_1) -> + (((2 + i_1) <= i) -> (a_4[a_5] = a_2[a_5]))). + (* Assertion 'B' *) + Have: forall i_1 : Z. let a_5 = shift_sint32(b, i_1) in ((0 <= i_1) -> + (((2 + i_1) <= i) -> (a_4[a_5] = a_2[a_5]))). + (* Invariant 'Copy' *) + Have: forall i_1 : Z. ((0 <= i_1) -> ((i_1 < i) -> + (a_4[shift_sint32(b, i_1)] = a_4[shift_sint32(a, i_1)]))). +} +Prove: included(a_3, 4, a_1, x_1). + +------------------------------------------------------------ + +Goal Assigns (file copy_array.c, line 5) in 'copy': +Effect at line 15 +Prove: true. + +------------------------------------------------------------ + +Goal Decreasing of Loop variant at loop (file copy_array.c, line 15): +Prove: true. + +------------------------------------------------------------ + +Goal Positivity of Loop variant at loop (file copy_array.c, line 15): +Prove: true. + +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_region/oracle/heterogenous_cast.res.oracle b/src/plugins/wp/tests/wp_region/oracle/heterogenous_cast.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..a6a8f41e11a888e1a6f98cda200b495d786522d4 --- /dev/null +++ b/src/plugins/wp/tests/wp_region/oracle/heterogenous_cast.res.oracle @@ -0,0 +1,41 @@ +# frama-c -wp -wp-model 'Region' [...] +[kernel] Parsing heterogenous_cast.c (with preprocessing) +[wp] Running WP plugin... +[wp] [Valid] Goal g_exits (Cfg) (Unreachable) +[wp] [Valid] Goal g_terminates (Cfg) (Trivial) +[wp] Warning: Missing RTE guards +[region] heterogenous_cast.c:9: Warning: Annotations not analyzed +------------------------------------------------------------ + Function g +------------------------------------------------------------ + +Goal Assertion (file heterogenous_cast.c, line 9): +Let x = read_sint32(write_sint16(write_sint16(write_sint32(mem_0, p, 42), + shift_sint16(p, 0), -1), + shift_sint16(p, 1), -1), p). +Assume { + Type: is_sint32(x). + (* Heap *) + Type: (region(p.base) <= 0) /\ framed(mem_0) /\ sconst(mem_0). +} +Prove: x = (-1). + +------------------------------------------------------------ + +Goal Assigns (file heterogenous_cast.c, line 2) in 'g' (1/3): +Effect at line 5 +Prove: true. + +------------------------------------------------------------ + +Goal Assigns (file heterogenous_cast.c, line 2) in 'g' (2/3): +Effect at line 7 +Prove: true. + +------------------------------------------------------------ + +Goal Assigns (file heterogenous_cast.c, line 2) in 'g' (3/3): +Effect at line 8 +Prove: true. + +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_region/oracle/record.res.oracle b/src/plugins/wp/tests/wp_region/oracle/record.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..721829172e9de5998570491c9e65070ce79fac74 --- /dev/null +++ b/src/plugins/wp/tests/wp_region/oracle/record.res.oracle @@ -0,0 +1,76 @@ +# frama-c -wp -wp-model 'Region' [...] +[kernel] Parsing record.c (with preprocessing) +[wp] Running WP plugin... +[wp] [Valid] Goal f_exits (Cfg) (Unreachable) +[wp] [Valid] Goal f_terminates (Cfg) (Trivial) +[wp] Warning: Missing RTE guards +[region] record.c:22: Warning: Annotations not analyzed +------------------------------------------------------------ + Function f +------------------------------------------------------------ + +Goal Post-condition (file record.c, line 12) in 'f': +Let a = shiftfield_F1_S_g(q). +Let a_1 = shiftfield_F1_S_f(p). +Let x = Msint32_0[a_1]. +Let x_1 = 1 + x. +Let m = Msint32_0[a_1 <- x_1]. +Let a_2 = shiftfield_F1_S_f(q). +Let x_2 = m[a_2]. +Let x_3 = m[a_2 <- x_2 - 1][a_1]. +Assume { + Type: is_sint32(x) /\ is_sint32(x_2) /\ is_sint32(x_3). + (* Heap *) + Type: (region(p.base) <= 0) /\ (region(q.base) <= 0) /\ framed(mem_0) /\ + sconst(mem_0). + (* Goal *) + When: q != p. + (* Pre-condition *) + Have: P_pointed(p, q). + (* Assertion *) + Have: read_sint32(write_sint16(write_sint16(mem_0, shift_sint16(a, 0), -1), + shift_sint16(a, 1), -1), a) = (-1). +} +Prove: x_3 = x_1. + +------------------------------------------------------------ + +Goal Post-condition (file record.c, line 13) in 'f': +Prove: true. + +------------------------------------------------------------ + +Goal Post-condition (file record.c, line 14) in 'f': +Let a = shiftfield_F1_S_g(q). +Let m = write_sint16(write_sint16(mem_0, shift_sint16(a, 0), -1), + shift_sint16(a, 1), -1). +Let x = read_sint32(write_sint32(m, a, 0), a). +Assume { + Type: is_sint32(x). + (* Heap *) + Type: (region(p.base) <= 0) /\ (region(q.base) <= 0) /\ framed(mem_0) /\ + sconst(mem_0). + (* Pre-condition *) + Have: P_pointed(p, q). + (* Assertion *) + Have: read_sint32(m, a) = (-1). +} +Prove: x = 0. + +------------------------------------------------------------ + +Goal Assertion (file record.c, line 22): +Let a = shiftfield_F1_S_g(q). +Let x = read_sint32(write_sint16(write_sint16(mem_0, shift_sint16(a, 0), -1), + shift_sint16(a, 1), -1), a). +Assume { + Type: is_sint32(x). + (* Heap *) + Type: (region(p.base) <= 0) /\ (region(q.base) <= 0) /\ framed(mem_0) /\ + sconst(mem_0). + (* Pre-condition *) + Have: P_pointed(p, q). +} +Prove: x = (-1). + +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_region/oracle/swap.res.oracle b/src/plugins/wp/tests/wp_region/oracle/swap.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..9e615df719c8f1a9aeee600b0d66b123215e5393 --- /dev/null +++ b/src/plugins/wp/tests/wp_region/oracle/swap.res.oracle @@ -0,0 +1,70 @@ +# frama-c -wp -wp-model 'Region' [...] +[kernel] Parsing swap.i (no preprocessing) +[wp] Running WP plugin... +[wp] [Valid] Goal swap_aliased_exits (Cfg) (Unreachable) +[wp] [Valid] Goal swap_aliased_terminates (Cfg) (Trivial) +[wp] Warning: Missing RTE guards +[wp] [Valid] Goal swap_separated_exits (Cfg) (Unreachable) +[wp] [Valid] Goal swap_separated_terminates (Cfg) (Trivial) +------------------------------------------------------------ + Function swap_aliased +------------------------------------------------------------ + +Goal Post-condition (file swap.i, line 20) in 'swap_aliased': +Let x = Msint32_0[a]. +Let x_1 = Msint32_0[b]. +Let x_2 = Msint32_0[a <- x_1][b <- x][a]. +Assume { + Type: is_sint32(x) /\ is_sint32(x_1) /\ is_sint32(x_2). + (* Heap *) + Type: (region(a.base) <= 0) /\ (region(b.base) <= 0) /\ linked(alloc_0). + (* Pre-condition *) + Have: valid_rw(alloc_0, a, 4). + (* Pre-condition *) + Have: valid_rw(alloc_0, b, 4). +} +Prove: x_2 = x_1. + +------------------------------------------------------------ + +Goal Post-condition (file swap.i, line 21) in 'swap_aliased': +Prove: true. + +------------------------------------------------------------ + +Goal Assigns (file swap.i, line 22) in 'swap_aliased' (1/2): +Effect at line 27 +Prove: true. + +------------------------------------------------------------ + +Goal Assigns (file swap.i, line 22) in 'swap_aliased' (2/2): +Effect at line 28 +Prove: true. + +------------------------------------------------------------ +------------------------------------------------------------ + Function swap_separated +------------------------------------------------------------ + +Goal Post-condition (file swap.i, line 5) in 'swap_separated': +Prove: true. + +------------------------------------------------------------ + +Goal Post-condition (file swap.i, line 6) in 'swap_separated': +Prove: true. + +------------------------------------------------------------ + +Goal Assigns (file swap.i, line 7) in 'swap_separated' (1/2): +Effect at line 12 +Prove: true. + +------------------------------------------------------------ + +Goal Assigns (file swap.i, line 7) in 'swap_separated' (2/2): +Effect at line 13 +Prove: true. + +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_region/oracle/union.res.oracle b/src/plugins/wp/tests/wp_region/oracle/union.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..7023fcc52f23930b494cefea0d98c9ca702accf0 --- /dev/null +++ b/src/plugins/wp/tests/wp_region/oracle/union.res.oracle @@ -0,0 +1,66 @@ +# frama-c -wp -wp-model 'Region' [...] +[kernel] Parsing union.i (no preprocessing) +[wp] Running WP plugin... +[wp] [Valid] Goal f_exits (Cfg) (Unreachable) +[wp] [Valid] Goal f_terminates (Cfg) (Trivial) +[wp] Warning: Missing RTE guards +[region] union.i:14: Warning: Annotations not analyzed +------------------------------------------------------------ + Function f +------------------------------------------------------------ + +Goal Post-condition (file union.i, line 8) in 'f': +Let a = shiftfield_F1_U_i(p). +Let a_1 = shiftfield_F1_U_s(p). +Let m = write_sint16(write_sint16(write_sint32(mem_0, a, 42), + shift_sint16(a_1, 0), -1), shift_sint16(a_1, 1), -1). +Let x = read_sint32(write_sint32(m, a, 0), a). +Assume { + Type: is_sint32(x). + (* Heap *) + Type: (region(p.base) <= 0) /\ framed(mem_0) /\ sconst(mem_0). + (* Assertion *) + Have: read_sint32(m, a) = (-1). +} +Prove: x = 0. + +------------------------------------------------------------ + +Goal Assertion (file union.i, line 14): +Let a = shiftfield_F1_U_i(p). +Let a_1 = shiftfield_F1_U_s(p). +Let x = read_sint32(write_sint16(write_sint16(write_sint32(mem_0, a, 42), + shift_sint16(a_1, 0), -1), + shift_sint16(a_1, 1), -1), a). +Assume { + Type: is_sint32(x). + (* Heap *) + Type: (region(p.base) <= 0) /\ framed(mem_0) /\ sconst(mem_0). +} +Prove: x = (-1). + +------------------------------------------------------------ + +Goal Assigns (file union.i, line 7) in 'f' (1/4): +Effect at line 11 +Prove: true. + +------------------------------------------------------------ + +Goal Assigns (file union.i, line 7) in 'f' (2/4): +Effect at line 12 +Prove: true. + +------------------------------------------------------------ + +Goal Assigns (file union.i, line 7) in 'f' (3/4): +Effect at line 13 +Prove: true. + +------------------------------------------------------------ + +Goal Assigns (file union.i, line 7) in 'f' (4/4): +Effect at line 15 +Prove: true. + +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_region/oracle_qualif/affectations.res.oracle b/src/plugins/wp/tests/wp_region/oracle_qualif/affectations.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..3679fc28e57abf2c942f179a5ce6933da56953e9 --- /dev/null +++ b/src/plugins/wp/tests/wp_region/oracle_qualif/affectations.res.oracle @@ -0,0 +1,22 @@ +# frama-c -wp -wp-model 'Region' [...] +[kernel] Parsing affectations.c (with preprocessing) +[wp] Running WP plugin... +[wp] [Valid] Goal f_exits (Cfg) (Unreachable) +[wp] [Valid] Goal f_terminates (Cfg) (Trivial) +[wp] Warning: Missing RTE guards +[wp] 6 goals scheduled +[wp] [Valid] region_f_assigns_part1 (Qed) +[wp] [Valid] region_f_assigns_part2 (Qed) +[wp] [Valid] region_f_EQ_ensures_P (Qed) +[wp] [Valid] region_f_EQ_ensures_Q (Qed) +[wp] [Valid] region_f_SEP_ensures_Q (Alt-Ergo) (Cached) +[wp] [Valid] region_f_SEP_ensures_Q_2 (Alt-Ergo) (Cached) +[wp] Proved goals: 8 / 8 + Terminating: 1 + Unreachable: 1 + Qed: 4 + Alt-Ergo: 2 +------------------------------------------------------------ + Functions WP Alt-Ergo Total Success + f 4 2 6 100% +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_region/oracle_qualif/array.res.oracle b/src/plugins/wp/tests/wp_region/oracle_qualif/array.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..8111c5b2622ed02f7378a3d8ccbf7c4e68fdbdf5 --- /dev/null +++ b/src/plugins/wp/tests/wp_region/oracle_qualif/array.res.oracle @@ -0,0 +1,20 @@ +# frama-c -wp -wp-model 'Region' [...] +[kernel] Parsing array.c (with preprocessing) +[wp] Running WP plugin... +[wp] [Valid] Goal add_first4_exits (Cfg) (Unreachable) +[wp] [Valid] Goal add_first4_terminates (Cfg) (Trivial) +[wp] Warning: Missing RTE guards +[wp] 4 goals scheduled +[wp] [Valid] region_add_first4_ensures (Alt-Ergo) (Cached) +[wp] [Valid] region_add_first4_ensures_2 (Qed) +[wp] [Valid] region_add_first4_ensures_3 (Alt-Ergo) (Cached) +[wp] [Valid] region_add_first4_ensures_4 (Qed) +[wp] Proved goals: 6 / 6 + Terminating: 1 + Unreachable: 1 + Qed: 2 + Alt-Ergo: 2 +------------------------------------------------------------ + Functions WP Alt-Ergo Total Success + add_first4 2 2 4 100% +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_region/oracle_qualif/copy_array.res.oracle b/src/plugins/wp/tests/wp_region/oracle_qualif/copy_array.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..87119772a2ea41da0a911917fa0dd40913588d9c --- /dev/null +++ b/src/plugins/wp/tests/wp_region/oracle_qualif/copy_array.res.oracle @@ -0,0 +1,32 @@ +# frama-c -wp -wp-model 'Region' [...] +[kernel] Parsing copy_array.c (with preprocessing) +[wp] Running WP plugin... +[wp] [Valid] Goal copy_exits (Cfg) (Unreachable) +[wp] [Valid] Goal copy_terminates (Cfg) (Trivial) +[wp] Warning: Missing RTE guards +[region] copy_array.c:15: Warning: Annotations not analyzed +[region] copy_array.c:18: Warning: Annotations not analyzed +[region] copy_array.c:19: Warning: Annotations not analyzed +[wp] 13 goals scheduled +[wp] [Valid] region_copy_ensures (Alt-Ergo) (Cached) +[wp] [Valid] region_copy_loop_invariant_Copy_preserved (Alt-Ergo) (Cached) +[wp] [Valid] region_copy_loop_invariant_Copy_established (Qed) +[wp] [Valid] region_copy_loop_invariant_Range_preserved (Qed) +[wp] [Valid] region_copy_loop_invariant_Range_established (Qed) +[wp] [Valid] region_copy_assert_A (Alt-Ergo) (Cached) +[wp] [Valid] region_copy_assert_B (Alt-Ergo) (Cached) +[wp] [Valid] region_copy_loop_assigns_part1 (Qed) +[wp] [Valid] region_copy_loop_assigns_part2 (Qed) +[wp] [Valid] region_copy_loop_assigns_part3 (Alt-Ergo) (Cached) +[wp] [Valid] region_copy_assigns (Qed) +[wp] [Valid] region_copy_loop_variant_decrease (Qed) +[wp] [Valid] region_copy_loop_variant_positive (Qed) +[wp] Proved goals: 15 / 15 + Terminating: 1 + Unreachable: 1 + Qed: 8 + Alt-Ergo: 5 +------------------------------------------------------------ + Functions WP Alt-Ergo Total Success + copy 8 5 13 100% +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_region/oracle_qualif/heterogenous_cast.res.oracle b/src/plugins/wp/tests/wp_region/oracle_qualif/heterogenous_cast.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..cc19d2134ab95a9f647e807f4b7d9d8d8e377ccf --- /dev/null +++ b/src/plugins/wp/tests/wp_region/oracle_qualif/heterogenous_cast.res.oracle @@ -0,0 +1,21 @@ +# frama-c -wp -wp-model 'Region' [...] +[kernel] Parsing heterogenous_cast.c (with preprocessing) +[wp] Running WP plugin... +[wp] [Valid] Goal g_exits (Cfg) (Unreachable) +[wp] [Valid] Goal g_terminates (Cfg) (Trivial) +[wp] Warning: Missing RTE guards +[region] heterogenous_cast.c:9: Warning: Annotations not analyzed +[wp] 4 goals scheduled +[wp] [Valid] region_g_assert (Alt-Ergo) (Cached) +[wp] [Valid] region_g_assigns_part1 (Qed) +[wp] [Valid] region_g_assigns_part2 (Qed) +[wp] [Valid] region_g_assigns_part3 (Qed) +[wp] Proved goals: 6 / 6 + Terminating: 1 + Unreachable: 1 + Qed: 3 + Alt-Ergo: 1 +------------------------------------------------------------ + Functions WP Alt-Ergo Total Success + g 3 1 4 100% +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_region/oracle_qualif/record.res.oracle b/src/plugins/wp/tests/wp_region/oracle_qualif/record.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..166e787cf3e5c5feb8bc5d0eaf440e4162ba7ea7 --- /dev/null +++ b/src/plugins/wp/tests/wp_region/oracle_qualif/record.res.oracle @@ -0,0 +1,21 @@ +# frama-c -wp -wp-model 'Region' [...] +[kernel] Parsing record.c (with preprocessing) +[wp] Running WP plugin... +[wp] [Valid] Goal f_exits (Cfg) (Unreachable) +[wp] [Valid] Goal f_terminates (Cfg) (Trivial) +[wp] Warning: Missing RTE guards +[region] record.c:22: Warning: Annotations not analyzed +[wp] 4 goals scheduled +[wp] [Valid] region_f_ensures (Alt-Ergo) (Cached) +[wp] [Valid] region_f_ensures_2 (Qed) +[wp] [Valid] region_f_ensures_3 (Alt-Ergo) (Cached) +[wp] [Valid] region_f_assert (Alt-Ergo) (Cached) +[wp] Proved goals: 6 / 6 + Terminating: 1 + Unreachable: 1 + Qed: 1 + Alt-Ergo: 3 +------------------------------------------------------------ + Functions WP Alt-Ergo Total Success + f 1 3 4 100% +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_region/oracle_qualif/swap.res.oracle b/src/plugins/wp/tests/wp_region/oracle_qualif/swap.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..3f2fc25aafc08726123f2af753e77aeeb7334270 --- /dev/null +++ b/src/plugins/wp/tests/wp_region/oracle_qualif/swap.res.oracle @@ -0,0 +1,27 @@ +# frama-c -wp -wp-model 'Region' [...] +[kernel] Parsing swap.i (no preprocessing) +[wp] Running WP plugin... +[wp] [Valid] Goal swap_aliased_exits (Cfg) (Unreachable) +[wp] [Valid] Goal swap_aliased_terminates (Cfg) (Trivial) +[wp] Warning: Missing RTE guards +[wp] [Valid] Goal swap_separated_exits (Cfg) (Unreachable) +[wp] [Valid] Goal swap_separated_terminates (Cfg) (Trivial) +[wp] 8 goals scheduled +[wp] [Valid] region_swap_separated_ensures (Qed) +[wp] [Valid] region_swap_separated_ensures_2 (Qed) +[wp] [Valid] region_swap_separated_assigns_part1 (Qed) +[wp] [Valid] region_swap_separated_assigns_part2 (Qed) +[wp] [Valid] region_swap_aliased_ensures (Alt-Ergo) (Cached) +[wp] [Valid] region_swap_aliased_ensures_2 (Qed) +[wp] [Valid] region_swap_aliased_assigns_part1 (Qed) +[wp] [Valid] region_swap_aliased_assigns_part2 (Qed) +[wp] Proved goals: 12 / 12 + Terminating: 2 + Unreachable: 2 + Qed: 7 + Alt-Ergo: 1 +------------------------------------------------------------ + Functions WP Alt-Ergo Total Success + swap_separated 4 - 4 100% + swap_aliased 3 1 4 100% +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_region/oracle_qualif/union.res.oracle b/src/plugins/wp/tests/wp_region/oracle_qualif/union.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..8ac57f31244c3cae6924ce5daae5907e69300be5 --- /dev/null +++ b/src/plugins/wp/tests/wp_region/oracle_qualif/union.res.oracle @@ -0,0 +1,23 @@ +# frama-c -wp -wp-model 'Region' [...] +[kernel] Parsing union.i (no preprocessing) +[wp] Running WP plugin... +[wp] [Valid] Goal f_exits (Cfg) (Unreachable) +[wp] [Valid] Goal f_terminates (Cfg) (Trivial) +[wp] Warning: Missing RTE guards +[region] union.i:14: Warning: Annotations not analyzed +[wp] 6 goals scheduled +[wp] [Valid] region_f_ensures (Alt-Ergo) (Cached) +[wp] [Valid] region_f_assert (Alt-Ergo) (Cached) +[wp] [Valid] region_f_assigns_part1 (Qed) +[wp] [Valid] region_f_assigns_part2 (Qed) +[wp] [Valid] region_f_assigns_part3 (Qed) +[wp] [Valid] region_f_assigns_part4 (Qed) +[wp] Proved goals: 8 / 8 + Terminating: 1 + Unreachable: 1 + Qed: 4 + Alt-Ergo: 2 +------------------------------------------------------------ + Functions WP Alt-Ergo Total Success + f 4 2 6 100% +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_region/record.c b/src/plugins/wp/tests/wp_region/record.c new file mode 100644 index 0000000000000000000000000000000000000000..10da25acbd0baf51c500b5a055a0d77daa214ead --- /dev/null +++ b/src/plugins/wp/tests/wp_region/record.c @@ -0,0 +1,24 @@ +struct S { + int f; + int g; +}; + +/*@ predicate pointed(struct S *p, struct S * q) = p==q || \separated(p,q); +*/ + + +/*@ requires pointed(p,q); + region PQf: p->f, q->f; + ensures p != q ==> p->f == \old(p->f) + 1; + ensures p == q ==> p->f == \old(p->f); + ensures q->g == 0; +@*/ +void f (struct S *p, struct S *q) { + (p->f)++; + (q->f)--; + short *r = &(q->g); + r[0] = -1; + r[1] = -1; + //@ assert q->g == -1; + (q->g)++; +} diff --git a/src/plugins/wp/tests/wp_region/swap.i b/src/plugins/wp/tests/wp_region/swap.i new file mode 100644 index 0000000000000000000000000000000000000000..04816ea5fd84319d9dad1009e3cbdb643cea9272 --- /dev/null +++ b/src/plugins/wp/tests/wp_region/swap.i @@ -0,0 +1,29 @@ +/*@ + requires \valid(a); + requires \valid(b); + region A: *a, B: *b; + ensures *a == \old(*b); + ensures *b == \old(*a); + assigns *a, *b; + */ +void swap_separated(int *a, int *b) +{ + int tmp = *a ; + *a = *b; + *b = tmp ; +} + +/*@ + requires \valid(a); + requires \valid(b); + region R: *a, *b; + ensures *a == \old(*b); + ensures *b == \old(*a); + assigns *a, *b; + */ +void swap_aliased(int *a, int *b) +{ + int tmp = *a ; + *a = *b; + *b = tmp ; +} diff --git a/src/plugins/wp/tests/wp_region/test_config b/src/plugins/wp/tests/wp_region/test_config new file mode 100644 index 0000000000000000000000000000000000000000..87e39087d8a41ddbc3e6ef2a3acb8dd436efb5c0 --- /dev/null +++ b/src/plugins/wp/tests/wp_region/test_config @@ -0,0 +1 @@ +OPT: -wp-model region diff --git a/src/plugins/wp/tests/wp_region/test_config_qualif b/src/plugins/wp/tests/wp_region/test_config_qualif new file mode 100644 index 0000000000000000000000000000000000000000..87e39087d8a41ddbc3e6ef2a3acb8dd436efb5c0 --- /dev/null +++ b/src/plugins/wp/tests/wp_region/test_config_qualif @@ -0,0 +1 @@ +OPT: -wp-model region diff --git a/src/plugins/wp/tests/wp_region/union.i b/src/plugins/wp/tests/wp_region/union.i new file mode 100644 index 0000000000000000000000000000000000000000..edc24141817bd90b9d349ba38f7810d8506ee736 --- /dev/null +++ b/src/plugins/wp/tests/wp_region/union.i @@ -0,0 +1,16 @@ +union U { + short s[2]; + int i; +}; + +/*@ + assigns *p; + ensures p->i == 0; +@*/ +void f (union U *p) { + p->i = 42; + p->s[0] = -1; + p->s[1] = -1; + //@ assert p->i == -1; + (p->i)++; +}