This commit is contained in:
Samin Ishtiaq 2016-02-26 15:39:53 +00:00
Коммит ee24563509
511 изменённых файлов: 53416 добавлений и 0 удалений

12
.ocp-indent Normal file
Просмотреть файл

@ -0,0 +1,12 @@
normal
base = 2
type = 2
in = 0
with = 0
match_clause = 4
max_indent = 2
strict_with = auto
strict_else = always
strict_comments = true
align_ops = true
align_params = always

13
LICENSE Normal file
Просмотреть файл

@ -0,0 +1,13 @@
SLAyer
Copyright (c) Microsoft Corporation
All rights reserved.
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

50
README.md Normal file
Просмотреть файл

@ -0,0 +1,50 @@
# About
SLAyer is an automatic formal verification tool that uses separation logic to verify memory safety of C programs.
# Licence
SLAyer is licensed under the MIT licence included in the [LICENSE](./LICENSE) file.
# Setup
Building and testing SLAyer has some dependencies on the environment.
To set this up, start in a VS 201x shell. Then enter a bash shell, cd
to here, and execute:
`$ source config.sh`
To build SLAyer:
`$ cd src; make; cd ..`
The slayer.exe will be left in the bin directory.
See src/README for additional building instructions.
To run the tests:
Start a new VS2010+bash shell, and cd to here. (The reason to start a
new shell is that SLAyer is built using the usual VS compiler, but
when slayer runs on tests, it needs to run the WDK compiler.)
`$ source ./config.sh`
`$ cd test; source config.sh`
`$ cd sll`
`$ slayer -vSE 3 -vAbs 2 traverse.c`
See test/README for additional testing instructions.
# Contributing
To contribute, you will need to complete a Contributor License Agreement (CLA).
Briefly, this agreement testifies that you are granting us permission to use the submitted change according to the terms of the project's license,
and that the work being submitted is under appropriate copyright.
Please submit a Contributor License Agreement (CLA) before submitting a pull request.
You may visit https://cla.microsoft.com to sign digitally.
Alternatively, download the agreement Microsoft Contribution License Agreement.docx or Microsoft Contribution License Agreement.pdf), sign, scan, and email it back to cla@microsoft.com.
Be sure to include your github user name along with the agreement.
Once we have received the signed CLA, we'll review the request.

31
config.cmd Normal file
Просмотреть файл

@ -0,0 +1,31 @@
@echo off
REM SLAyer setup file.
REM Most of these settings are for running slayer in stand-alone mode.
REM slayer running under slam needs very little of this.
set SLWIN=%~dp0..
set PATH=%SLWIN%\SLAyer\bin;%PATH%
set PATH=%SLWIN%\SLAyer\tools\bin;%PATH%
set PATH=%SLWIN%\SLAyer\tools\Z3\build;%PATH%
set PATH=%SLWIN%\SLAyer\tools\flexdll;%PATH%
REM sd merge.
set PATH=%$PROGRAMFILES%\Emacs\emacs\bin;%PATH%
set SDMERGE=%SLWIN%\SLAyer\tools\site-lisp\msel\mymerge.bat
REM dot
set PATH=%PROGRAMFILES%\Graphviz2.27\bin;%PATH%
REM the directory the ocaml compiler executables are installed into
set OCAML=%SLWIN%\SLAyer\tools\ocaml\bin
REM add ocaml compilers to PATH
set PATH=%SLWIN%\SLAyer\tools\ocaml\bin;%PATH%
REM add ocaml library to INCLUDE, mainly for compiling C interface code
set INCLUDE=%SLWIN%\SLAyer\tools\ocaml\lib;%INCLUDE%
REM must be in windows format
set OCAMLLIB=%SLWIN%\SLAyer\tools\ocaml\lib

25
config.sh Normal file
Просмотреть файл

@ -0,0 +1,25 @@
#!/bin/sh
# echo "Setting environment for building SLAyer"
full=`pwd`/$BASH_SOURCE
SL_SRC="${full%/*}/../"
export SL_WIN=`cygpath -w $SL_SRC`
export SL_UNIX=`cygpath -u $SL_WIN`
#echo "Unix: $SL_UNIX Windows: $SL_WIN"
# add dirs slayer builds are installed into to PATH
export PATH="$SL_UNIX/SLAyer/bin:$PATH"
export PATH="$SL_UNIX/SLAyer/tools/Z3/build:$PATH"
# add ocaml compilers to PATH
export PATH="$SL_UNIX/SLAyer/tools/ocaml/bin:$PATH"
export PATH="$SL_UNIX/SLAyer/tools/ocaml/lib/stublibs:$PATH"
export PATH="$SL_UNIX/SLAyer/tools/flexdll:$PATH"
# add ocaml library to INCLUDE, mainly for compiling C interface code
export INCLUDE="$SL_WIN\SLAyer\tools\ocaml\lib;$INCLUDE"
# must be in windows format
export OCAMLLIB="$SL_WIN\SLAyer\tools\ocaml\lib"

56
include/slayer.h Normal file
Просмотреть файл

@ -0,0 +1,56 @@
/* Copyright (c) Microsoft Corporation. All rights reserved. */
/*
Declare convenient interface to SLAyer intrinsics.
When the "SLAyer" symbol is defined, analysis-specific definitions are used.
Otherwise, executable definitions that under-approximate the SLAyer internal
semantics are defined.
This file is included automatically when the SLAyer frontend invokes the C
compiler. This default can be overridden by passing the SLAyer frontend the
-no-builtins flag.
The "SLAyer" symbol is defined when SLAyer invokes the C compiler.
*/
#ifndef _SLAYER_H_
#define _SLAYER_H_
#include <stdlib.h>
#include <assert.h>
/* Provide executable definitions of SLAyer intrinsics unless SLAyer is defined. */
#ifndef SLAyer
void* _SLAyer_malloc(size_t s) { return malloc(s); }
void _SLAyer_free(void* p) { free(p); }
void _SLAyer_error() { assert(("reached point asserted to be erroneous", 0)); }
void _SLAyer_unreachable() { assert(("reached point assumed to be unreachable", 0)); }
int _SLAyer_nondet() { return rand(); }
#endif // #ifndef SLAyer
#define container_of(ptr, type, member) _SLAyer_containerof(ptr, type, member)
#define CONTAINING_RECORD(ptr, type, member) _SLAyer_containerof(ptr, type, member)
#define malloc(exp) _SLAyer_malloc(exp)
#define free(exp) _SLAyer_free(exp)
#ifdef SLAyer
/* Redefine the result of the definition of assert from assert.h to be robust
with respect to code that includes assert.h (after slayer.h). */
#define _wassert(_exp,_file,_line) _SLAyer_error()
#endif
#define assume(exp) _SLAyer_assume(exp)
#define nondet() _SLAyer_nondet()
/* Deprecated */
#define FAIL assert(0)
#define FAIL_IF(_e) assert(!(_e))
#endif // #ifndef _SLAYER_H_

Просмотреть файл

@ -0,0 +1,47 @@
/* Copyright (c) Microsoft Corporation. All rights reserved. */
/*
Declare SLAyer intrinsics.
SLAyer treats calls to the functions declared here specially, using internal
analysis-specific semantics.
SLAyer recognizes and specially treats the code produced by the macro
definitions, so it is not necessary to use the provided macro names.
This file is included automatically when SLAyer invokes the C compiler.
*/
#ifndef _SLAYER_INTRINSICS_H_
#define _SLAYER_INTRINSICS_H_
#define _SLAyer_NULL ((void *)0)
#ifdef _WIN64
#define _SLAyer_offsetof(s,m) (size_t)( (__int64)&(((s *)0)->m) )
#else
#define _SLAyer_offsetof(s,m) (size_t)&(((s *)0)->m)
#endif
#define _SLAyer_containerof(ptr, type, member) ((type *) ((char *)(ptr) - _SLAyer_offsetof(type, member)))
#ifdef _WIN64
void* _SLAyer_malloc(unsigned __int64);
#else
void* _SLAyer_malloc(unsigned int);
#endif
void _SLAyer_free(void*);
/* Calls to these functions other than through _SLAyer_assert or
_SLAyer_assume may be translated suboptimally. */
void _SLAyer_error(void);
void _SLAyer_unreachable(void);
/* Translate to branches to avoid unnecessary bool->int->bool conversions. */
/* Use a definition like assert.h except with _SLAyer_unreachable in place of
_wassert to enable redefining the result of the standard definition. */
#define _SLAyer_assert(exp) (void)( (!!(exp)) || (_SLAyer_error(), 0) )
#define _SLAyer_assume(exp) (void)( (!!(exp)) || (_SLAyer_unreachable(), 0) )
int _SLAyer_nondet();
#endif // #ifndef _SLAYER_INTRINSICS_H_

1
src/.ocamlspot Normal file
Просмотреть файл

@ -0,0 +1 @@
build_dir=_build

Просмотреть файл

@ -0,0 +1,617 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Abstract transition system abstract domains *)
(**/**)
open Library
open Variable
module S = Substitution
open Program
module I = Inst
module K = ControlPoint
module C = Cmnd
open Interproc_sig
module Positions = Set.Make(Position)
module L = (val Log.std Config.vATS : Log.LOG)
module LSE = (val Log.std Config.vSE : Log.LOG)
(**/**)
(*============================================================================
Heights
============================================================================*)
(* PS#207: heights should be a map from entry * pt to int *)
module Heights = HashMap.Make(ControlPoint)
module Domain
(* states / vertex labels of the abstract transition system *)
(RD: RELATION_DOMAIN) =
struct
(*============================================================================
Transition Relation from Relation Construction
============================================================================*)
module RD = struct
type r = RD.r
type pred = RD.pred
type t = { rel: RD.t; blk: I.t list; }
let create = RD.create
let inject x = {rel= RD.inject x; blk= []}
let project x = RD.project x.rel
let adapted_pre_substate_call r cxt pre call pcall =
match RD.adapted_pre_substate_call r cxt pre.rel call.rel pcall with
| Some(post_rel_to_retn_rel) -> Some(fun post -> {post with rel= post_rel_to_retn_rel post.rel})
| None -> None
let call_to_entry r call pcall =
let entry, post_rel_to_retn_rel = RD.call_to_entry r call.rel pcall in
({rel= entry; blk= []}, (fun post -> {post with rel= post_rel_to_retn_rel post.rel}))
let exit_to_retn callee exit = {exit with rel= RD.exit_to_retn callee exit.rel}
let resolve_indirect_call r call fptr ftyp =
RD.resolve_indirect_call r call.rel fptr ftyp
let error = {rel= RD.error; blk= []}
let tt = {rel= RD.tt; blk= []}
let is_error x = RD.is_error x.rel
let is_false x = RD.is_false x.rel
let exec_inst cxt i x =
let rel = RD.exec_inst cxt i x.rel in
let blk = match i with {I.desc= I.Nop} -> x.blk | _ -> i :: x.blk in
{rel; blk}
let below x y = RD.below x.rel y.rel
let join x y = {rel= RD.join x.rel y.rel; blk= []}
let generalize x =
let rel, jnk = RD.generalize x.rel in
({x with rel}, jnk)
let compare x y =
let o = RD.compare x.rel y.rel in if o <> 0 then o else
List.compare I.compare x.blk y.blk
let equal x y = RD.equal x.rel y.rel && List.equal I.equal x.blk y.blk
let equal_entry x y = RD.equal_entry x.rel y.rel
let fmt ff {rel} = RD.fmt ff rel
let fmt_entry ff x = RD.fmt_entry ff x.rel
let fmt_pre ff (x,p) = RD.fmt_pre ff (x.rel,p)
let fmt_reln ff {rel; blk} =
Format.fprintf ff "@[<hv>%a@ @[<hv>%a@]@ %a@]"
RD.fmt_entry rel (List.fmt ";@ " I.fmt) (List.rev blk) RD.fmt rel
end
(*============================================================================
Transition / Edge Labels
============================================================================*)
(* transitions / edge labels of the abstract transition system *)
module Tr = struct
type t =
| Intra of K.t * I.t list * K.t * bool (** start and end control points, block between, leak *)
| Call of Proc.t Call.t
| Return
| Summary
let append x y =
match x, y with
| Intra(k0,b0,k1,false), Intra(k1',b1,k2,false) when K.equal k1 k1' ->
Some(Intra(k0, List.append b0 b1, k2, false))
| _ ->
None
let equal x y =
match x, y with
| Intra(k0,b0,k0',l0), Intra(k1,b1,k1',l1) ->
K.equal k0 k1 && K.equal k0' k1' && List.equal I.equal b0 b1 && (l0 = l1)
| Call(c0), Call(c1) ->
Call.equal Proc.equal c0 c1
| _ ->
Pervasives.( = ) x y
let compare x y =
match x, y with
| Intra(k0,b0,k0',l0), Intra(k1,b1,k1',l1) ->
let o = K.compare k0 k1 in if o <> 0 then o else
let o = K.compare k0' k1' in if o <> 0 then o else
let o = List.compare I.compare b0 b1 in if o <> 0 then o else
Pervasives.compare l0 l1
| Call(c0), Call(c1) ->
Call.compare Proc.compare c0 c1
| _ ->
Pervasives.compare x y
let fmt ff tr =
let fmt_blk ff blk =
Format.fprintf ff "@[<hv>%a;@]" (List.fmt ";@ " I.fmt) blk
in
let fmt_blk_pos ff blk =
let fmt_pos_inst ff {I.desc; pos} =
Format.fprintf ff "@[<hov 2>%a:@ %a@]" Position.fmt pos I.fmt_desc desc in
Format.fprintf ff "@[<v>%a;@]" (List.fmt ";@\n" fmt_pos_inst) blk
in
let fmt_assumes ff blk =
fmt_blk ff (List.filter (fun {I.desc} -> match desc with I.Assume _ -> true | _ -> false) blk)
in
let aux leak b =
match !Config.vTr with
| 0 -> Format.fprintf ff "@[<hv>%( fmt %)@]" leak
| 1 -> Format.fprintf ff "@[<hv>%( fmt %)%a@]" leak fmt_assumes b
| 2 -> Format.fprintf ff "@[<hv>%( fmt %)%a@]" leak fmt_blk b
| _ -> Format.fprintf ff "@[<hv>%( fmt %)%a@]" leak fmt_blk_pos b
in
match tr with
| Intra(_,b,_,false) -> aux "" b
| Intra(_,b,_,true) -> aux "LEAK@ " b
| Call({Call.proc= {Proc.id; freturn}; areturn} as call) ->
let fmt_ret ff =
match freturn, areturn with
| Some(freturn), Some(areturn) -> Format.fprintf ff "[%a/%a] =@ " Var.fmt areturn Var.fmt freturn
| Some(freturn), None -> Format.fprintf ff "[_/%a] =@ " Var.fmt freturn
| None , Some(areturn) -> Format.fprintf ff "[%a/_] =@ " Var.fmt areturn
| _ -> ()
in
let frmls_to_actls,_ = Call.args {call with Call.areturn= None} in
Format.fprintf ff "@[<hov 2>%t@,@[%a(@[%a@])@]@]" fmt_ret Proc.Id.fmt id S.fmt frmls_to_actls
| Return -> Format.fprintf ff "return"
| Summary -> Format.fprintf ff "summary"
end
(*============================================================================
States / Vertices
============================================================================*)
module Idx = struct
include K
let fmt ff k =
Format.fprintf ff "%a: %a: %a: "
Id.fmt (K.id k) Position.fmt (K.pos k) (Option.fmt "" K.fmt_sort) (K.sort k)
end
(** abstract transition system *)
module ATS = struct
include Graph.Make (Idx) (RD) (Tr)
let concat_blocks g v0 =
L.incf 10 "( concat_blocks" ; L.decf 10 ") concat_blocks" $>
let visited = VertexISet.create ()
in
let collapse_pre_ok v w =
match Idx.sort (index_of v) , Idx.sort (index_of w) with
| _ , None
| Some(K.Entry | K.Cut | K.Join), Some(K.Join | K.Fork)
| Some(K.Return) , Some(K.Fork) -> not (RD.is_error (label_of w))
| Some(K.Return) , Some(K.Join)
| _ , Some(K.Exit | K.Return | K.Cut)
| None , Some(K.Join | K.Fork)
| Some(K.Exit) , _
| _ , Some(K.Entry) -> false
| Some(K.Fork) , _ -> failwith "only called when one succ"
in
let collapse_post_ok v w =
match Idx.sort (index_of v) , Idx.sort (index_of w) with
| None , _
| Some(K.Join | K.Fork) , Some(K.Exit | K.Cut | K.Join)
| Some(K.Fork) , Some(K.Return | K.Fork) -> not (RD.is_error (label_of w))
| Some(K.Join) , Some(K.Return | K.Fork)
| Some(K.Entry | K.Return | K.Cut), _
| Some(K.Join | K.Fork) , None
| Some(K.Exit) , _
| _ , Some(K.Entry) -> false
in
let concat_ok v w =
Idx.sort (index_of v) = None
&& not (RD.is_error (label_of w))
in
let rec start v =
L.printf 10 "start: %a" Vertex.fmt v ;
if not (VertexISet.mem visited v) then
match successors v with
| [(w, (Tr.Intra(_,[],_,false) as e))] when collapse_pre_ok v w ->
collapse_edge_pre g v e w ;
start v
| succs ->
VertexISet.add visited v ;
List.iter (fun (w,e) -> continue v e w) succs
and continue v e w =
L.printf 10 "continue: @[%a@]@ @[%a@]@ @[%a@]" Vertex.fmt v Tr.fmt e Vertex.fmt w ;
match successors w with
| [] when collapse_post_ok v w ->
(match e with
| Tr.Intra(_,[],_,false) ->
collapse_edge_post g v e w
| _ -> ()
)
| [(x, (Tr.Intra(_,[],_,false) as f))] when collapse_post_ok w x ->
collapse_edge_post g w f x ;
continue v e x
| [(x, f)] when concat_ok w x ->
(match Tr.append e f with
| Some(ef) ->
L.printf 10 "@[appending@ @[%a@]@ to @[%a@]@]" Tr.fmt e Tr.fmt f ;
add_edge g v ef x ;
remove_edge g v e w ;
remove_vertex g w ;
continue v ef x
| None ->
start w
)
| _ ->
start w
in
start v0
end
module Vtx = struct
include ATS.Vertex
type d_cp = RD.t
let cp v = ATS.index_of v
let d v = ATS.label_of v
let project v = (d v, cp v)
let fmt ff v = Format.fprintf ff "%a@ %a" Idx.fmt (cp v) RD.fmt (d v)
end
(*============================================================================
Interprocedural Domain
============================================================================*)
module I_D_cp = Vtx
type i_d_cp = Vtx.t
type d_bk = Vtx.t * (Vtx.d_cp * K.t)
type r = {
prog: Prog.t;
ats: ATS.graph;
heights: int Heights.t;
mutable hit_limit: bool;
d: RD.r;
}
let ats {ats} = ats
let states_for {ats} k =
ATS.vertices_for ats k
let errors {ats} =
ATS.fold_vertices (fun v errs ->
if RD.is_error (ATS.label_of v) then
v :: errs
else
errs
) ats []
let leaks {prog; ats} =
let {Prog.main; procs} = prog in
let {Proc.entry} = Proc.IdHMap.find procs main in
ATS.fold_edges (fun _ z -> z) (fun (v,e,_) leaks ->
match e with
| Tr.Intra(_,_,_,true) -> v :: leaks
| _ -> leaks
) ats entry []
let hit_limit r = r.hit_limit
let dead {prog; ats} =
let add {Proc.cfg; entry} unreachable =
CFG.fold_edges (fun _ u -> u) (fun (_,c,_) unreached ->
match c with
| C.Inst({I.pos}) ->
Positions.add (pos) unreached
| C.Call _ | C.ICall _ ->
unreached
) cfg (CFG.index_of entry) unreachable
in
let rem_edg (_,tr,_) unreached =
match tr with
| Tr.Intra(k, blk, k', _) ->
unreached |>
Positions.remove (K.pos k) |>
List.fold (fun {I.pos} unreached ->
Positions.remove (pos) unreached
) blk |>
Positions.remove (K.pos k')
| Tr.Call _ | Tr.Return | Tr.Summary ->
unreached
in
let rem_dominated {Proc.cfg; entry} unreachable =
let _,_,_, dominated_by = CFG.dominance_frontier cfg entry in
CFG.fold_vertices (fun h unreachable ->
if not (Positions.mem (K.pos h) unreachable) then unreachable
else
CFG.VertexISet.fold (fun k unreachable ->
if Position.equal (K.pos h) (K.pos k) then unreachable
else
Positions.remove (K.pos k) unreachable
) (dominated_by h) unreachable
) cfg unreachable
in
Positions.empty |>
Prog.fold_procs add prog |>
ATS.fold_edges (fun _ u -> u) rem_edg ats (ATS.index_of (List.hd (ATS.roots ats))) |>
Prog.fold_procs rem_dominated prog |>
Positions.to_list
let write_ats name prog ats entry =
let open Prog in let open Proc in
let fn k =
Option.map (fun p -> (Id.name p.id, fun ff -> Proc.fmt ff p)) (IdHMap.tryfind prog.procs (K.proc k))
in
Library.with_out (name^".ats.dot") (ATS.write_dot ats entry) ;
Library.with_out (name^".part.ats.dot") (ATS.write_dot_partitioned fn ats [entry])
let create prog =
let {Prog.main; procs} = prog in
let {Proc.entry} = Proc.IdHMap.find procs main in
let r = {
prog;
ats= ATS.create ();
heights= Heights.create 31;
hit_limit= false;
d= RD.create prog;
} in
Pervasives.at_exit (fun()->
if Config.write_ats then
write_ats Config.testname prog r.ats entry
);
r
(*============================================================================
Transition System Operations
============================================================================*)
let add_vertex_for_state r cp d =
(fun v ->
L.printf 2 "@\n@[<hov 2>adding state: %a@]" Vtx.fmt v ;
assert( RD.equal_entry (Vtx.d v) d ))
<&
ATS.add_vertex r.ats (cp, {d with RD.blk= []})
let add_transition_to_vertex r prev prev_to_call msg call =
L.printf 2 "@\n@[<hv 2>adding transition:@\n@[%a@]@ @[<hov 2>from@ %a@]@ @[<hov 2>to@ %s@,%a@]@]"
Tr.fmt prev_to_call Vtx.fmt prev msg Vtx.fmt call ;
assert(
match prev_to_call with
| Tr.Call _ | Tr.Return -> true
| _ -> RD.is_error (Vtx.d call) || RD.equal_entry (Vtx.d prev) (Vtx.d call)
);
ATS.add_edge r.ats prev prev_to_call call
(*============================================================================
Interproc Domain Operations
============================================================================*)
let init r (init_d, init_cp) =
let init = ATS.add_vertex r.ats (init_cp, init_d) in
ATS.root_vertex r.ats init ;
L.printf 2 "@[<hov 2>adding initial state %a@]" Vtx.fmt init ;
init
let now_covered r vtx = not (ATS.mem_vertex r.ats vtx)
let remove_covered_by r new_vtx =
let remove_if_covered old_vtx =
let new_d = Vtx.d new_vtx in
let old_d = Vtx.d old_vtx in
if not (RD.equal old_d new_d) then
if RD.below old_d new_d then (
L.printf 2 "@\n@[<hv 2>removing %a@ now covered by@ %a@]" Vtx.fmt old_vtx RD.fmt new_d ;
ATS.replace_vertex r.ats (fun x -> x) old_vtx new_vtx ;
assert( RD.equal_entry (Vtx.d new_vtx) (Vtx.d old_vtx) )
)
in
List.iter remove_if_covered
(ATS.vertices_for r.ats (ATS.index_of new_vtx))
let join msg r (prev, (tr, next_d, next_cp)) =
if Config.join_powerset then
let trg = add_vertex_for_state r next_cp next_d in
if (Config.join_reduce > 0 && (Some K.Cut) = (K.sort next_cp))
|| (Config.join_reduce > 1 && (Some K.Join) = (K.sort next_cp))
|| (Config.join_reduce > 2)
then
remove_covered_by r trg ;
add_transition_to_vertex r prev tr msg trg ;
trg
else
let trylookup_d r d =
List.tryfind (fun v -> RD.equal_entry d (Vtx.d v))
(ATS.vertices_for r.ats next_cp)
in
let trg =
match trylookup_d r next_d with
| None ->
add_vertex_for_state r next_cp next_d
| Some(old_vtx) ->
let old_or_new = RD.join next_d (Vtx.d old_vtx) in
let trg = add_vertex_for_state r next_cp old_or_new in
ATS.replace_vertex r.ats (fun tr -> tr) old_vtx trg ;
assert( RD.equal_entry (Vtx.d trg) (Vtx.d old_vtx) );
trg
in
add_transition_to_vertex r prev tr msg trg ;
trg
let prev_to_join r (prev, (next_d, next_cp)) =
join "join " r (prev, (Tr.Intra(Vtx.cp prev, List.rev next_d.RD.blk, next_cp, false), next_d, next_cp))
type __covered = WasCoveredByOld | NowCoveredByNew
let widen msg r (prev, (tr, next_d, next_cp)) =
(* Note: this should be the below operation *)
let covered_by r d =
LSE.incf 5 "( covered_by: %a" RD.fmt d ;
LSE.decf 5 ") covered_by: %a@\n" (Option.fmt "None" Vtx.fmt)
<&
let rec loop = function
| [] -> None
| v :: vs -> if RD.below d (Vtx.d v) then Some(v) else loop vs
in
loop (List.rev (ATS.vertices_for r.ats next_cp))
in
let generalize r d =
let new_height =
match Heights.tryfind r.heights next_cp with
| Some(h) -> h + 1
| None -> 1
in
Heights.add r.heights next_cp new_height
;
if Config.limit <= 0 || new_height <= Config.limit then
RD.generalize d
else (
r.hit_limit <- true ;
LSE.printf 1 "ascending chain too long for %a@\n" K.fmt next_cp ;
(RD.join d RD.tt, true)
)
in
match covered_by r next_d with
| Some(trg) ->
add_transition_to_vertex r prev tr "covering " trg ;
(WasCoveredByOld, trg)
| None ->
let abs_d, junk = generalize r next_d in
let trg =
if not junk then
join msg r (prev, (tr, abs_d, next_cp))
else
let leak = join msg r (prev, (tr, next_d, next_cp)) in
LSE.printf 1 "LEAK detected@\n@\n" ;
let leak_to_abs = Tr.Intra(Vtx.cp leak, [], next_cp, true) in
join msg r (leak, (leak_to_abs, abs_d, next_cp))
in
(NowCoveredByNew, trg)
let prev_to_cut r (prev, (next_d, next_cp)) =
widen "new " r (prev, (Tr.Intra(Vtx.cp prev, List.rev next_d.RD.blk, next_cp, false), next_d, next_cp))
(* Note: shouldn't this call to join be made by the fixed-point comp? *)
let post_to_retn r call post_d_to_retn_d (post, (post_d, retn_cp)) =
let retn_d = post_d_to_retn_d post_d
in
(* add exit to return transition *)
let retn =
if Config.generalize_call_retn
then snd (widen "return " r (post, (Tr.Return, retn_d, retn_cp)))
else join "return " r (post, (Tr.Return, retn_d, retn_cp))
in
add_transition_to_vertex r call Tr.Summary "summary " retn
;
retn
let call_to_pre r call pcall pre =
add_transition_to_vertex r call (Tr.Call(pcall)) "pre " pre
let add_prev_to_call_to_pre r (prev, (call_d, call_cp)) pcall pre =
(* add transition to call site *)
let call =
if K.equal (Vtx.cp prev) call_cp
then prev
else
if Config.generalize_call_retn
then snd (widen "call " r (prev, (Tr.Intra(Vtx.cp prev, List.rev call_d.RD.blk, call_cp, false), call_d, call_cp)))
else join "call " r (prev, (Tr.Intra(Vtx.cp prev, List.rev call_d.RD.blk, call_cp, false), call_d, call_cp))
in
(* add call to pre transition *)
call_to_pre r call pcall pre
;
call
(* Note: the signature of this is wrong somehow, call_d can be projected out
of call, and hist and call_cp are not needed *)
let adapted_pre_substate_call r cxt pre ((_, (call_d, _)) as prev_to_call) pcall =
match RD.adapted_pre_substate_call r.d cxt (Vtx.d pre) call_d pcall with
| None -> None
| Some(post_d_to_retn_d) ->
(* add transition to call site *)
let call = add_prev_to_call_to_pre r prev_to_call pcall pre
in
Some(post_to_retn r call post_d_to_retn_d)
let call_to_entry r ((_, (call_d, _)) as prev_to_call) ({Call.proc= callee} as pcall) =
let entry, post_d_to_retn_d = RD.call_to_entry r.d call_d pcall
in
let entry = add_vertex_for_state r callee.Proc.entry entry
in
(* PS#208: here we should instead check if entry with the ghosts
existentially quantified is covered *)
remove_covered_by r entry
;
(* add transition to call site *)
let call = add_prev_to_call_to_pre r prev_to_call pcall entry
in
(entry, post_to_retn r call post_d_to_retn_d)
let exit_to_retn callee (prev, (exit_d, retn_cp)) =
(prev, (RD.exit_to_retn callee exit_d, retn_cp))
let resolve_indirect_call r (_, (call_d, _)) fptr ftyp =
RD.resolve_indirect_call r.d call_d fptr ftyp
let procedure_pres r fn =
(* PS#134: this doesn't necessarily yield reverse order of addition *)
ATS.vertices_for r.ats fn.Proc.entry
(* ensure that pre doesn't get GCed if it gets disconnected *)
let register_pre r pre = ATS.root_vertex r.ats pre
end

Просмотреть файл

@ -0,0 +1,42 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open Library
open Program
open Interproc_sig
module Domain (RD: RELATION_DOMAIN) : sig
module Tr : sig
type t =
| Intra of ControlPoint.t * Inst.t list * ControlPoint.t * bool
| Call of Proc.t Call.t
| Return
| Summary
val append : t -> t -> t option
val compare : t -> t -> int
val equal : t -> t -> bool
val fmt : t formatter
end
module ATS : sig
include
(Graph.GRAPH
with type index = ControlPoint.t
and type e_label = Tr.t)
val concat_blocks : graph -> vertex -> unit
end
include
(INTERPROC_DOMAIN
with type RD.t = ATS.v_label
and type RD.pred = RD.pred
and type I_D_cp.t = ATS.vertex)
val ats : r -> ATS.graph
val write_ats : string -> Prog.t -> ATS.graph -> ATS.index -> unit
end

366
src/Abstraction.ml Normal file
Просмотреть файл

@ -0,0 +1,366 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Abstraction of symbolic heaps *)
(* Notes:
- The definition of abs_junk effectively distributes every [_ * true] to the
root of the formula. An alternative is to keep the [_ * true] subformulas
in the *-\/ tree where the garbage they abstract came from. The second is
logically stronger, but perhaps disturbing to the prover. Overall it is
not clear which is preferable, there should be an option to use either
technique.
- It seems that, since abstracted formulas only get weaker, it should be
possible to normalize the formula to be abstracted just once at the
beginning and then simply reuse the resulting congruence relation.
*)
open Library
open Variable
open Expression
module E = Exp
module S = Substitution
open SymbolicHeap
open Program
(* lvl 1 = entry-point entry/exit and summary rewrite
2 = individual abstraction rewrites
3 = each abstraction rule entry/exit and summary rewrite
> = subsidiary computations *)
module L = (val Log.std Config.vAbs : Log.LOG)
(* Timing =================================================================== *)
let abstract_tmr = Timer.create "Abstraction.abstract"
let abs_junk_tmr = Timer.create "Abstraction.abs_junk"
let abs_ls_tmr = Timer.create "Abstraction.abs_ls"
let abs_arith_tmr = Timer.create "Abstraction.abs_arith"
let abs_pure_tmr = Timer.create "Abstraction.abs_pure"
let normalize_tmr = Timer.create "Abstraction.SH.normalize"
module XSH = struct include XSH
let normalize xsh =
Timer.start normalize_tmr ;
let res = normalize xsh in
Timer.stop normalize_tmr ;
res
end
(* Formatting =============================================================== *)
let fmt_abs lvl msg (xs,sh) (xs',sh') =
if lvl <= !Config.vAbs then
let fmt_o, _fmt_i, fmt_n = SH.fmt_did_xs ((xs,sh), (xs',sh')) in
L.printf lvl "@[<hov 2>%sreplace:@ %t@]@ @[<hov 2>with:@ %t@]" msg fmt_o fmt_n
(*============================================================================
Abstract unreachable points-tos and list-segs.
============================================================================*)
let abs_junk (xs,sh) =
Timer.start abs_junk_tmr ;
L.incf 3 "( abs_junk:@ %a" SH.fmt_xs (xs,sh) ;
(fun xsho ->
Timer.stop abs_junk_tmr ;
Option.option () (fun (xs',sh') -> fmt_abs 2 "abs_junk: " (xs,sh) (xs', SH.Jnk.star sh')) xsho ;
L.decf 3 ") abs_junk:@ %a" (Option.fmt "unchanged" SH.fmt_xs) xsho )
<&
let keep = (fun loc -> Vars.disjoint xs (E.fv loc))
in
let is_reachable = Reachability.is_reachable keep sh
in
let sh, pgs =
SH.map_fold (fun sh pgs ->
let sh, pgs =
SH.PtS.fold (fun ({Pt.loc} as pt) (sh, pgs) ->
if is_reachable sh loc then (sh, pgs)
else (SH.PtS.remove pt sh, true)
) sh (sh, pgs) in
let sh, pgs =
SH.LsS.fold (fun ls (sh, pgs) ->
let loc = Ls.fst_alloc ls in
if is_reachable sh loc then (sh, pgs)
else (SH.LsS.remove ls sh, true)
) sh (sh, pgs) in
(sh, pgs)
) sh false
in
if pgs then
Some(xs,sh)
else
None
(*============================================================================
Abstract lists.
============================================================================*)
let abs_ls (xs,sh) =
Timer.start abs_ls_tmr ;
L.incf 3 "( abs_ls:@ %a" SH.fmt_xs (xs,sh) ;
(fun xsho ->
Timer.stop abs_ls_tmr ;
L.decf 3 ") abs_ls:@ %a" (Option.fmt "unchanged" SH.fmt_xs) xsho )
<&
HeapAbstraction.abstract (xs,sh)
(*============================================================================
Abstract arithmetic expressions.
============================================================================*)
(* Keep integer program constants, as control flow often depends on them.
(For instance, PS#146 documents the case of kdbclass.c using the constant
NT_STATUS returned by IoCreateDevice.)
*)
let pgm_consts : unit Int64HMap.t = Int64HMap.create 31
let _ = Initialize.register (fun {Prog.constants} ->
List.iter (fun i -> Int64HMap.add pgm_consts i ()) constants
)
let pgm_const e =
assert(true$> L.incf 8 "( pgm_const:@ %a" E.fmt e ); (fun b -> assert(true$> L.decf 8 ") pgm_const:@ %b" b )) <&
match E.desc e with
| E.Num(n) when (Int64HMap.mem pgm_consts n) -> true
| _ -> false
let abs_arith (xs,sh) =
Timer.start abs_arith_tmr ;
L.incf 3 "( abs_arith:@ %a" SH.fmt_xs (xs,sh) ;
(fun xsho ->
Timer.stop abs_arith_tmr ;
Option.option () (fmt_abs 2 "abs_arith: " (xs,sh)) xsho ;
L.decf 3 ") abs_arith:@ %a" (Option.fmt "unchanged" SH.fmt_xs) xsho )
<&
(* Abstract constant list lengths *)
let sh, (pgs, fresh_vs) =
SH.map_fold (fun sh (pgs,vs) ->
let vs, lss, sh =
SH.LsS.fold (fun ({Ls.len} as ls) (vs,lss,sh) ->
match E.desc len with
| E.Num _ ->
let k = Var.gensym "a" Var.IntegerSort in
( Vars.add k vs
, {ls with Ls.len= E.mkVar k} :: lss
, SH.LsS.remove ls sh )
| _ ->
(vs,lss,sh)
) sh (vs, [], sh) in
if lss = [] then (sh, (pgs, vs))
else (SH.LsS.star lss sh, (true, vs))
) sh (false, Vars.empty)
in
(* Abstract all integer constants except 0, 1, and those appearing in program *)
let to_abstract e (s, fresh_vs) =
if Config.preserve_consts && pgm_const e then (s, fresh_vs)
else if S.in_dom e s then (s, fresh_vs)
else match E.desc e with
| E.Var _ | E.Num(0L) | E.Num(1L) -> (s, fresh_vs)
| E.Op3(E.Ite,_,E.Num(1L),E.Num(0L)) -> (s, fresh_vs)
| _ when E.sort_of e = Var.IntegerSort ->
let f = Var.gensym "a" Var.IntegerSort in
(S.add e (E.mkVar f) s, Vars.add f fresh_vs)
| _ -> (s, fresh_vs)
in
let exp_subst, fresh_vs = SH.fold_exps to_abstract sh (S.empty, fresh_vs)
in
if not pgs && S.is_empty exp_subst then
None
else
(* Note: change this SH.subst to preserve normalization *)
let sh' = SH.subst exp_subst sh in
Some(Vars.union fresh_vs xs, sh')
(*============================================================================
Abstract Pure Formulas.
============================================================================*)
let abs_pure (xs,sh) =
Timer.start abs_pure_tmr ;
L.incf 3 "( abs_pure:@ %a" SH.fmt_xs (xs,sh) ;
(fun xsho ->
Timer.stop abs_pure_tmr ;
Option.option () (fmt_abs 2 "abs_pure: " (xs,sh)) xsho ;
L.decf 3 ") abs_pure:@ %a" (Option.fmt "unchanged" SH.fmt_xs) xsho )
<&
let pgs = ref false in
let kills = Vars.diff xs (SH.fv (SH.spatial_sf sh)) in
L.printf 6 "kills: @[<hov 1>{%a}@]" Vars.fmt kills ;
if Vars.is_empty kills then None else
let abs_pure_q q _ =
(* L.incf 6 "( abs_pure_q:@ %a" SH.fmt q ; (fun (q',_) -> L.decf 6 ") abs_pure_q:@ %a" SH.fmt q' ) <& *)
(* convert the classes to a list of equalities between not killable exps *)
let classes = SH.Pf.classes q in
let eqs =
Expss.fold (fun cls eqs ->
L.printf 6 "cls: %a" Exps.fmt cls ;
if Exps.cardinal cls <= 1 then eqs
else
(* filter the killable exps out of the class *)
let filtered_cls =
Exps.fold (fun e filtered_cls ->
let fv_e = Exp.fv e in
if Vars.disjoint fv_e kills
then filtered_cls
else
let filtered_cls' = Exps.remove e filtered_cls in
pgs := true ;
L.printf 6 "abs_pure: %a" E.fmt e ;
filtered_cls'
) cls cls in
if Exps.cardinal filtered_cls <= 1 then eqs
else
(* convert the filtered class to a list of equalities *)
let x = Exps.choose filtered_cls in
let xs = Exps.remove x filtered_cls in
let rec aux xs =
if Exps.is_empty xs then
eqs
else
let y = Exps.choose xs in
let ys = Exps.remove y xs in
E.mkEq x y :: aux ys in
(* add killed exps to subst *)
aux xs
) classes []
in
(* L.printf 5 "eqs_pgs: %b" !pgs ; *)
(* L.printf 5 "eqs: %a" (List.fmt ";@ " E.fmt) eqs ; *)
(* remove any boolean exp containing a kill *)
let bex = SH.Pf.term q in
(* L.incf 5 "( remove: %a" E.fmt bex ; *)
let bex' =
E.remove (fun d ->
if Vars.disjoint (E.fv (E.name d)) kills then false
else (
pgs := true ;
true
)
) bex in
(* L.decf 5 ") remove: %a" E.fmt bex' ; *)
(* conjoin the abstracted boolean expression and filtered equalities *)
(* Note: change this SH.Pf.empty to preserve normalization *)
let q' = SH.Pf.star (bex' :: eqs) (SH.Pf.empty q) in
(* remove pure disjunctions *)
let q' =
SH.DjS.filter (fun dj ->
if Dj.for_all SH.is_empty dj then (
pgs := true ;
false
) else
true
) q' in
(q', !pgs)
in
let sh', pgs =
SH.map_fold abs_pure_q sh false
in
if pgs then Some(xs,sh') else None
(*============================================================================
Entry Point.
============================================================================*)
let gen_query id xsh =
TestGen.gen ("abs_" ^ (string_of_int id)) (fun ff ->
Format.fprintf ff
"let tmr = Timer.create() in Timer.start tmr ;@\n\
@\n@[<hov 3>(* abstract:@ %a@]@\n*)@\n\
@[<hov 2>Abstraction.abstract@\n%a@] |> ignore;@\n\
Timer.stop Timer.init ;@\n\
Statistics.report (Timer.create()) tmr (Timer.create())"
XSH.fmt xsh
XSH.fmt_caml xsh
)
let new_query =
let count = ref 0
in
fun xsh ->
incr count;
if !count = Config.abs_query_to_gen then gen_query !count xsh;
!count
let seq f g x = g (Option.from_option x (f x))
let fix f x =
match f x with
| None -> None
| Some(x') ->
let rec loop x =
match f x with
| None -> Some(x)
| Some(x') -> loop x' in
loop x'
let abstract xsh =
Timer.start abstract_tmr ;
let xsh = XSH.normalize xsh in
let query = new_query xsh in
L.incf 1 "( abstract %d:@ %a" query XSH.fmt xsh ;
if XSH.inconsistent xsh then
(XSH.ff, false)
else
let junk = ref false in
let abs_junk x =
let xo = abs_junk x in
match xo with
| Some _ -> junk := true; xo
| None -> xo
in
let abs =
(seq abs_ls
(seq abs_junk
(seq (fix abs_arith)
(seq abs_pure
id
)))) in
let xs, sh = XSH.exists_bind Vars.empty xsh in
let xs',sh' = abs (xs, sh) in
let sh' = if !junk then SH.Jnk.star sh' else sh' in
let xsh' = SH.exists_intro xs' sh' in
if not (SH.equal sh sh') then (
fmt_abs 1 "" (xs,sh) (xs',sh') ;
L.decf 1 ") abstract:@ %a@\n" XSH.fmt xsh'
) else
L.decf 1 ") abstract:@ unchanged@\n" ;
Timer.stop abstract_tmr ;
assert( not Config.check_abs || (
L.printf 6 "checking abstraction" ;
Prover.entailsx xsh xsh' <> None
|| (gen_query query xsh ; failwithf "Abstraction not provably sound" )
));
(xsh', !junk)

23
src/Abstraction.mli Normal file
Просмотреть файл

@ -0,0 +1,23 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Abstraction of symbolic heaps *)
open SymbolicHeap
val abstract_tmr : Timer.t
val abs_junk_tmr : Timer.t
val abs_ls_tmr : Timer.t
val abs_arith_tmr : Timer.t
val abs_pure_tmr : Timer.t
val normalize_tmr : Timer.t
(*============================================================================
Abstraction
============================================================================*)
(** [abstract xsh] applies heuristics to abstract un-needed
pure predicate, arithmetic, ls, dis-equality and points-to
expressions. *)
val abstract : XSH.t -> XSH.t * bool

101
src/Analysis.ml Normal file
Просмотреть файл

@ -0,0 +1,101 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Link the abstract domain and analysis algorithm together *)
(**/**)
open Library
open Variable
open Expression
module E = Exp
module S = Substitution
open Program
module I = Inst
module C = Cmnd
module K = ControlPoint
open SymbolicHeap
(**/**)
module L = (val Log.std Config.vATS : Log.LOG)
module SymbolicHeapsDomain = struct
include SymbolicExecution
let fmt_pre ff (sh,proc) =
let ghosts =
Vars.diff
(Option.option Vars.empty XSH.fv sh)
(Vars.union (Vars.of_list proc.Proc.formals) proc.Proc.modifs) in
Format.fprintf ff "@[<hv>%a%a@]"
(Vars.fmt_embrace "@[<hov 2>! " " .@]@ ") ghosts fmt sh
let error = None
let tt = Some(XSH.tt)
let is_error x = x = None
let is_false = function
| Some(q) when XSH.equal q XSH.ff -> true
| _ -> false
let join p q =
match p, q with
| Some(p), Some(q) -> Some(XSH.disj [p] q)
| _ -> None
let generalize = function
| None ->
(None, false)
| Some(q) ->
let p, j = Abstraction.abstract q in
(Some(p), j)
let below p q =
match p, q with
| _, None -> true
| None, _ -> false
| Some(p), Some(q) -> None <> Prover.entailsx p q
end
module Pair = Interproc.Pair(SymbolicHeapsDomain)
module InterprocDomain = AbstractTransitionSystem.Domain(Pair)
module RD = InterprocDomain.RD
include Interproc.Make (InterprocDomain)
module VertexSet = Set.Make(InterprocDomain.I_D_cp)
let init _prog = Some(XSH.emp)
let safe results = results.safe
let errors results = InterprocDomain.errors results.invariants
let leaks results =
let {Prog.main; procs} = results.program in
let {Proc.exit} = Proc.IdHMap.find procs main
in
VertexSet.of_list (InterprocDomain.leaks results.invariants)
|>
List.fold (fun v leaks ->
match RD.project (fst (InterprocDomain.I_D_cp.project v)) with
| Some(xsh) when not (XSH.is_empty (XSH.Jnk.remove xsh)) -> VertexSet.add v leaks
| _ -> leaks
) (InterprocDomain.states_for results.invariants exit)
|>
VertexSet.to_list
let must_diverge results =
results.safe
&&
let {Prog.main; procs} = results.program in
let {Proc.exit} = Proc.IdHMap.find procs main in
(InterprocDomain.states_for results.invariants exit) = []
let dead results = InterprocDomain.dead results.invariants
let hit_limit results = results.hit_limit || InterprocDomain.hit_limit results.invariants

30
src/Analysis.mli Normal file
Просмотреть файл

@ -0,0 +1,30 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Link the abstract domain and analysis algorithm together *)
(**/**)
open Program
open SymbolicHeap
open Interproc_sig
(**/**)
module SymbolicHeapsDomain : (INTRAPROC_DOMAIN with type t = XSH.t option)
module Pair : (module type of Interproc.Pair(SymbolicHeapsDomain))
module InterprocDomain : (module type of AbstractTransitionSystem.Domain(Pair))
(* Analyze *)
include (module type of Interproc.Make(InterprocDomain))
val init : Prog.t -> SymbolicHeapsDomain.t
val exec_prog : Prog.t -> SymbolicHeapsDomain.t -> t
(* Query analysis result *)
val safe : t -> bool
val errors : t -> InterprocDomain.I_D_cp.t list
val leaks : t -> InterprocDomain.I_D_cp.t list
val must_diverge : t -> bool
val dead : t -> Position.t list
val hit_limit : t -> bool

177
src/BiEdge.ml Normal file
Просмотреть файл

@ -0,0 +1,177 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Labeled bi-directional multi-edges *)
open Library
open Variable
open SYMBOLIC_HEAP
module Poly = struct
(* traversals *)
let map fn {prev; frnt; back; next} =
let prev = List.map fn prev in
let frnt = List.map fn frnt in
let back = List.map fn back in
let next = List.map fn next in
{prev; frnt; back; next}
let map2 fn x y =
let prev = List.map2 fn x.prev y.prev in
let frnt = List.map2 fn x.frnt y.frnt in
let back = List.map2 fn x.back y.back in
let next = List.map2 fn x.next y.next in
{prev; frnt; back; next}
let fold fn x z =
List.fold fn x.prev
(List.fold fn x.frnt
(List.fold fn x.back
(List.fold fn x.next
z)))
let fold2 fn x y z =
List.fold2 fn x.prev y.prev
(List.fold2 fn x.frnt y.frnt
(List.fold2 fn x.back y.back
(List.fold2 fn x.next y.next
z)))
let fold_links fn {prev; frnt; back; next} z =
let rec fold_links_ xs ys z =
match xs, ys with
| x::xs, y::ys -> fold_links_ xs ys (fn (x,y) z)
| _ -> z
in
fold_links_ back prev (fold_links_ frnt next z)
let fold_links2 fn x y z =
let rec fold_links2_ vs ws xs ys z =
match vs, ws, xs, ys with
| v::vs, w::ws, x::xs, y::ys ->
fold_links2_ vs ws xs ys (fn (v,w) (x,y) z)
| _ ->
z
in
fold_links2_ x.back x.prev y.back y.prev
(fold_links2_ x.frnt x.next y.frnt y.next
z)
let may_allocs {frnt; back} =
List.rev_append back frnt
(* constructors *)
(** [reverse] [p,f,b,n] = [n,b,f,p] *)
(* let reverse {prev= p; frnt= f; back= b; next= n} *)
(* = {prev= n; frnt= b; back= f; next= p} *)
(** [between] [s, t;t', u, v;v'] [w, x;x', y, z;z']
= [u, v;v', w, x;x'] *)
let between
{prev=_s; frnt=_t; back= u; next= v} {prev= w; frnt= x; back=_y; next=_z}
= {prev= u; frnt= v; back= w; next= x}
(** [append] [p, f;f', i, j;j'] [k, l;l', b, n;n']
= [p, f;f', b, n;n'] *)
(* Note: Check that args are adjacent? *)
let append
{prev= p; frnt= f; back=_i; next=_j} {prev=_k; frnt=_l; back= b; next= n}
= {prev= p; frnt= f; back= b; next= n}
(** [split w x u_v_y_z] returns [(u_v_w_x, w_x_y_z)] such that
[u_v_y_z = append (append u_v_w_x w_x_w_x) w_x_y_z] where
[w_x_w_x] is the empty segment determined by [x] and [w]. *)
let split w x
{prev= u; frnt= v; back= y; next= z} =
({prev= u; frnt= v; back= w; next= x},
{prev= w; frnt= x; back= y; next= z})
(** [remove_prefix wy xz = (wx, yz)] such that if [wx] is empty, then
[append wy yz = xz]. *)
let remove_prefix
{prev=s; frnt=t; back=u; next=v}
{prev=w; frnt=x; back=y; next=z} =
({prev=s; frnt=t; back=w; next=x}, {prev=u; frnt=v; back=y; next=z})
(** [remove_suffix xz wy = (yz, wx)] such that if [yz] is empty, then
[append wx xz = wy]. *)
let remove_suffix xz wy =
let wx, yz = remove_prefix wy xz in
(yz, wx)
end
module Make (A: TERM) = struct
include Poly
type a = A.t
type t = a edg
(** [adjacent x y] holds if [x] and [y] are adjacent. Eg:
[adjacent] [p, f;f', i, j]
[k, l;l', b, n;n']
holds if i = k & j = l *)
(* let rec adjacent x y = *)
(* match x.next, y.frnt with *)
(* | n::next, f::frnt -> A.equal n f && adjacent {x with next} {y with frnt} *)
(* | _ -> *)
(* match y.prev, x.back with *)
(* | p::prev, b::back -> A.equal p b && adjacent {x with back} {y with prev} *)
(* | _ -> *)
(* true *)
let fv x =
fold (fun a z -> Vars.union (A.fv a) z) x Vars.empty
let map_exps fn x =
map (fun a -> A.map_exps fn a) x
let fold_exps fn x z =
fold (fun a z -> A.fold_exps fn a z) x z
let equal x y =
List.equal A.equal x.prev y.prev
&& List.equal A.equal x.frnt y.frnt
&& List.equal A.equal x.back y.back
&& List.equal A.equal x.next y.next
let compare x y =
let o = List.compare A.compare x.frnt y.frnt in if o <> 0 then o else
let o = List.compare A.compare x.back y.back in if o <> 0 then o else
let o = List.compare A.compare x.prev y.prev in if o <> 0 then o else
List.compare A.compare x.next y.next
(** We write [p;p', f;f', b;b', n;n'] for
[\{fore= \[(f,n);(f',n')\]; back= \[(b,p);(b',p')\]\}] *)
let fmtp fxt ff {prev; frnt; back; next} =
let fmtl f = List.fmt ";@ " f in
Format.fprintf ff
"@[<hv>@[<hv>@[%a@],@ @[%a@]@],@ @[<hv>@[%a@],@ @[%a@]@]@]"
(fmtl (A.fmtp fxt)) prev (fmtl (A.fmtp fxt)) frnt
(fmtl (A.fmtp fxt)) back (fmtl (A.fmtp fxt)) next
let fmt ff x = fmtp (Vars.empty,Vars.empty) ff x
let fmt_caml ff {prev; frnt; back; next} =
Format.fprintf ff
"@[{BiEdge.Poly.@,\
prev= [@[%a@]];@ frnt= [@[%a@]];@ back= [@[%a@]];@ next= [@[%a@]]}@]"
(List.fmt ";@ " A.fmt_caml) prev (List.fmt ";@ " A.fmt_caml) frnt
(List.fmt ";@ " A.fmt_caml) back (List.fmt ";@ " A.fmt_caml) next
end

11
src/BiEdge.mli Normal file
Просмотреть файл

@ -0,0 +1,11 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Labeled bi-directional multi-edges *)
open SYMBOLIC_HEAP
module Poly : POLY_BIEDGE
module Make (A: TERM) : (BIEDGE with type a = A.t)

Просмотреть файл

@ -0,0 +1,91 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open Library
module type CONGRUENCE_RELATION = sig
type exp
(** Exps. *)
type exps
(** Sets of exps. *)
type t
(** A congruence relation representation [r: t] denotes a relation [[r]]
over a finite 'carrier' set of exps that is a congruence with respect
to the subexp relation. *)
val normalize : t -> exp -> exp
(** [normalize r (normalize r e)] = [normalize r e] and [normalize r e] =
[normalize r f] iff ([e],[f]) is in [[r]]. *)
val mem : t -> exp -> exp -> bool
(** [mem r e f] test if [r] proves [e]==[f]. *)
val mem_dqs : t -> exp -> exp -> bool
(** [mem_dqs r e f] test if [r] proves [e]!=[f]. *)
val inconsistent : t -> bool
(** [inconsistent r] holds only if [r] is inconsistent. *)
val class_of : t -> exp -> exps
(** [f] in [class_of r e] iff ([e],[f]) in [[r]]. *)
val mem_carrier : exp -> t -> bool
(** [mem_carrier e r] holds iff [e] is in the carrier of [r]. *)
val carrier : t -> exps
(** [e] in [carrier r] iff [e] is in the carrier of [r]. *)
val representatives : t -> exps
(** [e'] in [representatives r] iff [normalize r e'] = [e']. *)
val fold : (exp -> exp -> 'z -> 'z) -> t -> 'z -> 'z
(** [fold fn r] enumerates the equations in [r]. The representative is
passed to [fn] first. *)
val foldn : (exp -> exp -> 'z -> 'z) -> t -> 'z -> 'z
(** [foldn fn r] enumerates the disequations in [r]. *)
val fold_classes : (exp -> exps -> 'z -> 'z) -> t -> 'z -> 'z
(** [fold_classes fn r] enumerates the equivalence classes of [r]. *)
val empty : t
(** [[empty]] is the empty relation. *)
val merge : (exp -> exp -> bool) -> t -> exp -> exp -> t
(* Note: specify *)
val split : t -> exp -> exp -> t
val union : (exp -> exp -> bool) -> t -> t -> t
(** [[union leq p q]] = [[r]] is the strongest congruence relation
containing [[p]] and [[q]]. [union leq p q] maintains the
representatives of [p] and chooses between them using [leq]: for [leq]
a preorder, and for [normalize q e] = [f] then if [leq f e] then
[normalize r e] = [normalize p f] else [normalize r f] = [normalize p
e]. *)
val inter : (exp -> exp -> bool) -> t -> t -> t
(** [[inter q r]] is the intersection of [[q]] and [[r]]. Smaller wrt
[leq] representatives are chosen where possible. *)
val subst : (exp -> exp -> bool) -> t -> Substitution.t -> t
(* Note: specify *)
val restrict : t -> exps -> t
(** For [es] a superset of the representatives of [r], [normalize
(restrict r es) e] = [normalize r e] if [e] in [es]. *)
val remove_trivial : t -> exps -> t
(** [normalize (remove_trivial r es) e] = [normalize r e] if [e] in [es] and [class_of r e] is only [e]
itseslf. [carrier (remove_trivial r es)] is the subset of [carrier r] excluding such expressions. *)
val implied_by : (exp -> exp -> bool) -> t -> Pure.t -> exp array -> exps -> t
val is_empty : t -> bool
val fmt : t formatter
end

836
src/CngRel.ml Normal file
Просмотреть файл

@ -0,0 +1,836 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Congruence-closed relations *)
(* Notes:
- It is possible for use lists to contain redundant equations. This might
slow down propagation, but detecting and removing them might be more
expensive.
- It is pointless to store use lists for offset exps, they are all
distinct constants and so their use lists will never be looked up.
- Would representing classes as lists instead of sets be more efficient?
- Would separating cls_use into two maps be more efficient? The cost would
be two more finds in propagate1, the benefit would be eliminating the
Cls-Use pairs and reduced domain size of the use map.
*)
open Library
(* open FORMULA *)
(* open CONGRUENCE_RELATION *)
module S = Substitution
module L = (val Log.std Config.vCng : Log.LOG)
open Variable
open Expression
(* Either open Expression or use functor to reduce dependencies, also see EOF *)
(* module Make *)
(* (Exp: EXP) *)
(* (Exps: Set.S with type elt = Exp.t) *)
(* (ExpMap: Map.S with type key = Exp.t) *)
(* : *)
(* (CONGRUENCE_RELATION with type exp := Exp.t and type exps := Exps.t) = *)
(* struct *)
(* module Exps = struct *)
(* include Exps *)
(* let fmt ff es = (List.fmt ",@ " Exp.fmt) ff (to_list es) *)
(* end *)
(* Class lists ============================================================== *)
module type CLS = sig
type t
val mem : Exp.t -> t -> bool
val fold : (Exp.t -> 'a -> 'a) -> t -> 'a -> 'a
val iter : (Exp.t -> unit) -> t -> unit
val to_set : t -> Exps.t
val add : Exp.t -> t -> t
val remove : Exp.t -> t -> t
val is_empty : t -> bool
val singleton : Exp.t -> t
val union : t -> t -> t
val inter : Exps.t -> t -> t
val fmt : t formatter
end
module Cls : CLS = struct
include Exps
let to_set x = x
let fmt ff x = Format.fprintf ff "@[<hov 1>{@[%a@]}@]" fmt x
end
(* Disequality lists ======================================================== *)
module type DQS = sig
type t
val mem : Exp.t -> t -> bool
val fold : (Exp.t -> 'a -> 'a) -> t -> 'a -> 'a
val iter : (Exp.t -> unit) -> t -> unit
val add : Exp.t -> t -> t
val empty : t
val is_empty : t -> bool
val union : t -> t -> t
val inter : Exps.t -> t -> t
val fmt : t formatter
end
module Dqs : DQS = struct
include Exps
let fmt ff x = Format.fprintf ff "@[<hov 1>{@[%a@]}@]" fmt x
end
(* Use lists / Super-expression equations =================================== *)
module type USE = sig
type t
val empty : t
val is_empty : t -> bool
val add : Exp.t * Exp.t * Exp.t -> t -> t
val iter : (Exp.t * Exp.t * Exp.t -> unit) -> t -> unit
val fold : (Exp.t * Exp.t * Exp.t -> 'a -> 'a) -> t -> 'a -> 'a
val exists : (Exp.t * Exp.t * Exp.t -> bool) -> t -> bool
val union : t -> t -> t
val fmt : t formatter
end
(* Represents f(a)==c as App(App(f,a),c), and uses sets of such exps. *)
module Use0 : USE = struct
type t = Exps.t
let inj (f,a,c) = Exp.mkApp (Exp.mkApp f a) c
let prj fac =
let fa, c = Option.from_some (Exp.getApp fac) in
let f, a = Option.from_some (Exp.getApp fa) in
(f,a,c)
let fold fn s z = Exps.fold (fun fac z -> fn (prj fac) z) s z
let iter fn s = Exps.iter (fun fac -> fn (prj fac)) s
let exists pd s = Exps.exists (fun fac -> pd (prj fac)) s
let add fac s = Exps.add (inj fac) s
let union s t = Exps.union s t
let empty = Exps.empty
let is_empty = Exps.is_empty
let fmt ff s =
let fmt_eq ff fac =
match Exp.getApp fac with
| None ->
Format.fprintf ff "@[%a@]" Exp.fmt fac
| Some(fa,c) -> match Exp.getApp fa with
| None ->
Format.fprintf ff "@[%a = %a@]" Exp.fmt fa Exp.fmt c
| Some(f,a) ->
Format.fprintf ff "@[%a(%a) = %a@]" Exp.fmt f Exp.fmt a Exp.fmt c
in
Format.fprintf ff "@[<hov 1>{@[%a@]}@]" (List.fmt ",@ " fmt_eq) (Exps.to_list s)
end
(* Represents f(a)==c as (f,a,c), and uses lists of such triples. *)
module Use1 : USE = struct
include List.Set(struct type t = Exp.t * Exp.t * Exp.t end)
let union = List.rev_append
let fmt ff l =
let fmt_eq ff (f,a,c) =
Format.fprintf ff "@[%a(%a) = %a@]" Exp.fmt f Exp.fmt a Exp.fmt c in
Format.fprintf ff "@[<hov 1>[@[%a@]]@]" (List.fmt ",@ " fmt_eq) l
end
(* Represents f(a)==c as (f,a,c), and uses sets of such triples. *)
module Use2 : USE = struct
include Set.Make(struct
type t = Exp.t * Exp.t * Exp.t
let equal (u,v,w) (x,y,z) =
Exp.equal u x && Exp.equal v y && Exp.equal w z
let compare (u,v,w) (x,y,z) =
let c = Exp.compare u x in if c <> 0 then c else
let c = Exp.compare v y in if c <> 0 then c else
Exp.compare w z
end)
let fmt _ = failwith "Use2.fmt unimplemented"
end
(* Note: remove other implementations *)
module Use = Use1
(* Congruence relations ===================================================== *)
type t = {
sat: bool;
rep: Exp.t ExpMap.t;
(** maps each expression in carrier to its representative *)
cls_use: (Cls.t * Use.t * Dqs.t) ExpMap.t;
(** maps each representative to the expressions in its class, equations
involving a super-expression, and disequal expressions *)
pnd: (Exp.t * Exp.t) list;
(** pairs of expressions in carrier to be equated *)
}
(** See [invariant] below for additional representation properties. *)
(* Formatting =============================================================== *)
let fmt ff {sat; rep; cls_use; pnd} =
let fmt_assocl fmt_k fmt_v ff assocl =
let fmt_assoc ff (k,v) =
Format.fprintf ff "@[[%a => %a]@]" fmt_k k fmt_v v in
Format.fprintf ff "@[<hv>%a@]" (List.fmt ";@ " fmt_assoc) assocl
in
let fmt_pnd ff pnd =
let fmt_eq ff (e,f) = Format.fprintf ff "@[%a = %a@]" Exp.fmt e Exp.fmt f in
if pnd <> [] then
Format.fprintf ff "@ pnd= @[%a@];" (List.fmt ";@ " fmt_eq) pnd
in
let cls, use, dqs =
ExpMap.fold (fun e (e_cls, e_use, e_dqs) (cls, use, dqs) ->
let cls = (e, e_cls) :: cls in
let use = if Use.is_empty e_use then use else (e, e_use) :: use in
let dqs = if Dqs.is_empty e_dqs then dqs else (e, e_dqs) :: dqs in
(cls, use, dqs)
) cls_use ([],[],[])
in
Format.fprintf ff
"@[{@[<hv>sat= %b;@ rep= %a;@ cls= %a;@ dqs= %a;@ use= %a;%a@]@;}@]"
sat
(fmt_assocl Exp.fmt Exp.fmt) (ExpMap.to_list rep)
(fmt_assocl Exp.fmt Cls.fmt) cls
(fmt_assocl Exp.fmt Dqs.fmt) dqs
(fmt_assocl Exp.fmt Use.fmt) use
fmt_pnd pnd
(* Well-formedness ========================================================== *)
let is_rep e r =
try ExpMap.find e r.rep == e
with Not_found ->
failwithf "%a should be a rep but is not in car of@ %a" Exp.fmt e fmt r
let in_car e r =
ExpMap.mem e r.rep
let find_chk e m =
try ExpMap.find e m with Not_found -> failwithf "%a not in car" Exp.fmt e
(* A property maintained by propagating individual equations. This is not
strong enough to establish invariant below since it does not consider
pending equations, which must be added and their consequences propagated
before the relation is guaranteed to be closed. *)
let pre_invariant r =
not Config.check_cng || let()=()in
ExpMap.iter (fun e e' ->
(* carrier is closed under sub-expressions *)
(match Exp.getApp e with None -> () | Some(f,a) ->
assert( in_car f r );
assert( in_car a r );
);
(* rep is idempotent, ie, every representative is its own representative *)
assert( is_rep e' r
|| failwithf "%a not rep in %a" Exp.fmt e' fmt r );
(* every representative is in domain of cls_use *)
assert( not (Exp.equal e e') || ExpMap.mem e' r.cls_use
|| failwithf "%a not in cls_use %a" Exp.fmt e' fmt r );
(* every expression is in class of its representative *)
assert( Cls.mem e (fst3 (find_chk e' r.cls_use))
|| failwithf "@[%a not in cls of %a in@ %a@]" Exp.fmt e Exp.fmt e' fmt r );
(* use lists encode the super-expression relation for representatives, ie,
for each application g(b)==e in the carrier *)
if r.pnd = [] then
(match Exp.getApp e with None -> () | Some(g,b) ->
let aux g b =
(* if an immediate sub-expression g is a representative *)
try
let _,use,_ = ExpMap.find g r.cls_use in
let g' = g in
let b' = find_chk b r.rep in
(* then there is a corresponding equation f(a)==c in g's use list *)
assert(
Use.exists (fun (f,a,c) ->
(* where equations correspond if they are equal under rep *)
let f' = find_chk f r.rep in
let a' = find_chk a r.rep in
let c' = find_chk c r.rep in
Exp.equal c' e'
&& ( (Exp.equal f' g' && Exp.equal a' b')
|| (Exp.equal f' b' && Exp.equal a' g') )
) use
|| failwithf "no use equation for %a(%a) = %a %a(%a) = %a"
Exp.fmt b Exp.fmt g Exp.fmt e
Exp.fmt b' Exp.fmt g' Exp.fmt e'
)
with Not_found -> ()
in
aux g b ;
aux b g ;
);
(* relation is closed under a±F==b ==> a==b∓F *)
if r.pnd = [] then
(match Exp.getApp e with None -> () | Some(f,a) ->
match Exp.invert f with None -> () | Some(inv_f) ->
let a' = find_chk a r.rep in
let e_inv_f' = find_chk (Exp.mkApp inv_f e') r.rep in
assert( Exp.equal a' e_inv_f'
|| failwithf "%a = %a but missing %a = %a (= %a(%a))"
Exp.fmt e Exp.fmt e' Exp.fmt a' Exp.fmt e_inv_f' Exp.fmt inv_f Exp.fmt e' );
);
) r.rep
;
ExpMap.iter (fun e' (cls,use,dqs) ->
(* every expression in domain of cls_use is a representative *)
assert( is_rep e' r );
(* rep maps every expression in class of e' to e' *)
Cls.iter (fun e -> assert( find_chk e r.rep == e' ) ) cls ;
(* every super-expression equation for e': *)
Use.iter (fun (f,a,c) ->
(* is between expressions in carrier *)
let f' = find_chk f r.rep in
let a' = find_chk a r.rep in
let c' = find_chk c r.rep in
(* involves an application super-expression of e' *)
assert( Exp.equal f' e' || Exp.equal a' e' );
(* is proved by the relation *)
if r.pnd = [] then
assert( Exp.equal (find_chk (Exp.mkApp f' a') r.rep) c'
|| failwithf "%a(%a) = %a not proved" Exp.fmt f' Exp.fmt a' Exp.fmt c' );
) use ;
(* every expression in dqs is in carrier *)
Dqs.iter (fun e ->
assert( in_car e r )
) dqs
) r.cls_use
;
true
(* A property maintained by exported operations. *)
let invariant r =
assert( r.pnd = [] );
assert( pre_invariant r );
true
(* Helpers ================================================================== *)
let add_to_rep e e' rep =
ExpMap.add e e' rep
let add_to_cls e' e cls_use =
ExpMap.modify (fun (cls,use,dqs) -> (Cls.add e cls, use, dqs)) e' cls_use
let add_to_use e' fac cls_use =
ExpMap.modify (fun (cls,use,dqs) -> (cls, Use.add fac use, dqs)) e' cls_use
let add_to_uses ((f',a',_) as fac) cls_use =
add_to_use f' fac (add_to_use a' fac cls_use)
let add_to_dqs e' e r =
{r with cls_use= ExpMap.modify (fun (cls,use,dqs) -> (cls, use, Dqs.add e dqs)) e' r.cls_use}
let union_to_dqs e' es r =
{r with cls_use= ExpMap.modify (fun (cls,use,dqs) -> (cls, use, Dqs.union es dqs)) e' r.cls_use}
let add_to_pnd d e r =
if Exp.equal d e then r else
{r with pnd= (d, e) :: r.pnd}
let mem_dqs d' e' r =
Dqs.mem e' (thd3 (ExpMap.find d' r.cls_use))
|| Dqs.mem d' (thd3 (ExpMap.find e' r.cls_use))
(* Extending the carrier ==================================================== *)
exception Found_extend
exception Found_find_extend of Exp.t
let rec extend_ if_found r e =
let rep = ExpMap.modify_add if_found e e r.rep in
let cls_use = ExpMap.add e (Cls.singleton e, Use.empty, Dqs.empty) r.cls_use in
let r = {r with rep; cls_use} in
match Exp.getApp e with
| Some(f,a) ->
let f', r = find_extend f r in
let a', r = find_extend a r in
let f'_a', r = find_extend (Exp.mkApp f' a') r in
let cls_use = add_to_uses (f',a',e) r.cls_use in
let pnd = if Exp.equal f'_a' e then r.pnd else (f'_a', e) :: r.pnd in
{r with cls_use; pnd}
| None ->
r
(** [find_extend e r] is [(e',r')] where [e] is in the carrier of [r'] and
represented by [e']. [[r']] is the union of (e,e') and [[r]]. *)
and find_extend e r =
try
(e, extend_ (fun e' -> raise (Found_find_extend e')) r e)
with Found_find_extend e' ->
(e', r)
let extend e r =
try
(fun r' -> assert(true$>
L.printf 20 "extend: %a@,@[ %a@\n= %a@]" Exp.fmt e fmt r fmt r' ))
<&
extend_ (fun _ -> raise Found_extend) r e
with Found_extend ->
r
(* Injectivity and Inverse axioms =========================================== *)
let apply_add_sub_axiom r f'_a' c' c'_cls =
assert( Exp.sort_of f'_a' = Exp.sort_of c' );
match Exp.getApp f'_a' with
| None ->
r
| Some(f',a') ->
assert(true$>
L.incf 10 "( apply_add_sub_axiom: %a(%a) = %a@ %a@ %a"
Exp.fmt f' Exp.fmt a' Exp.fmt c' Cls.fmt c'_cls fmt r );
(fun r' -> assert(true$>
L.decf 10 ") apply_add_sub_axiom:@ %a" fmt r' ))
<&
match Exp.invert f' with
| None ->
r
| Some(inv_f) ->
(* added a'±o==c', so add c∓o==a' for each c==c' *)
Cls.fold (fun c r -> add_to_pnd (Exp.mkApp inv_f c) a' r) c'_cls r
(* Propagating equalities =================================================== *)
(** [propagate1 e' e r] adds [e'==e] to [r] using [e'] as the representative. *)
let propagate1 e' e r =
assert(
L.incf 10 "( propagate1:@ %a@ %a@ %a" Exp.fmt e' Exp.fmt e fmt r ;
pre_invariant {r with pnd= (e',e) :: r.pnd} );
(fun r' -> assert(
L.decf 10 ") propagate1:@ %a" fmt r' ;
pre_invariant r' ))
<&
(* remove class, superexp equations, and disequations of e, it is no longer a rep *)
let (e_cls, e_use, e_dqs), cls_use = ExpMap.extract e r.cls_use in
let r = {r with cls_use}
in
let r =
Cls.fold (fun d r ->
(* make e' the rep of every exp in class of e *)
let r = {r with rep= ExpMap.add d e' r.rep} in
(* and close under the axiom *)
let e'_cls = if Exp.equal e' e then e_cls else fst3 (ExpMap.find e' r.cls_use) in
apply_add_sub_axiom r d e' e'_cls
) e_cls r
in
(* add disequalities of e to e' *)
let r = union_to_dqs e' e_dqs r
in
(* closing under the axiom cannot promote e back to a rep *)
assert( not (ExpMap.mem e r.cls_use) )
;
(* traverse up exp dag from e, whose rep changed, looking for new equations *)
let r, e'_use_delta =
Use.fold (fun (f,a,c) (r, e'_use_delta) ->
let f' = ExpMap.find f r.rep in
let a' = ExpMap.find a r.rep in
let f'_a' = Exp.mkApp f' a' in
try
let b = ExpMap.find f'_a' r.rep in
(* f'(a')==b already, so add b==c *)
(add_to_pnd b c r, e'_use_delta)
with Not_found ->
(* f'(a') not in relation yet, so add f'(a')==c' *)
let c' = ExpMap.find c r.rep in
let rep = add_to_rep f'_a' c' r.rep in
let cls_use = add_to_cls c' f'_a' r.cls_use in
let r = {r with rep; cls_use} in
let c'_cls,_,_ = ExpMap.find c' r.cls_use in
let r = apply_add_sub_axiom r f'_a' c' c'_cls in
(* Don't need to add (f',a',c') to use of f' or a' since e' is one of
f' or a' and (f',a',c') will be added to use of e' below, and
(f,a,c) will already be in use of the other. *)
let e'_use_delta = Use.add (f',a',c') e'_use_delta in
(r, e'_use_delta)
) e_use (r, Use.empty)
in
(* add class and superexps of e to e' *)
let cls_use =
ExpMap.modify (fun (e'_cls, e'_use, e'_dqs) ->
(Cls.union e_cls e'_cls, Use.union e'_use_delta e'_use, e'_dqs)
) e' r.cls_use
in
{r with cls_use}
module SortMMap =
MultiMap.Make
(struct type t = Var.sort let compare = Pervasives.compare let equal = Pervasives.( = ) end)
(Exps)
(** [propagate leq r] adds pending equations until closure reached. *)
let rec propagate leq r =
assert(
L.incf 10 "( propagate:@ %a" fmt r ;
pre_invariant r );
(fun r' -> assert(
L.decf 10 ") propagate:@ %a" fmt r' ;
invariant r' ))
<&
match r.pnd with
| [] ->
r
| (d,e) :: pnd ->
let r = {r with pnd} in
let d', r = find_extend d r in
let e', r = find_extend e r in
if Exp.equal d' e' then
propagate leq r
else if mem_dqs d' e' r then
{r with sat= false; pnd= []}
(* use e as the new rep if leq e d *)
else if leq e' d' then
propagate leq (propagate1 e' d' r)
else
propagate leq (propagate1 d' e' r)
(* Exported operations ====================================================== *)
let normalize r e =
try ExpMap.find e r.rep with Not_found -> e
let mem r e f =
Exp.equal (normalize r e) (normalize r f)
let mem_dqs r e f =
try mem_dqs (normalize r e) (normalize r f) r
with Not_found -> false
let inconsistent r =
not r.sat
let class_of r e =
try Cls.to_set (fst3 (ExpMap.find (normalize r e) r.cls_use))
with Not_found -> Exps.singleton e
let mem_carrier e r =
ExpMap.mem e r.rep
let carrier r =
ExpMap.fold (fun e _ car ->
Exps.add e car
) r.rep Exps.empty
let representatives r =
ExpMap.fold (fun e' _ reps ->
Exps.add e' reps
) r.cls_use Exps.empty
let fold fn r z =
ExpMap.fold (fun e' (cls,_,_) z ->
Cls.fold (fun e z ->
fn e' e z
) (Cls.remove e' cls) z
) r.cls_use z
let foldn fn r z =
ExpMap.fold (fun e' (_,_,dqs) z ->
Dqs.fold (fun e z ->
if Exp.compare e' e > 0 then z else
fn e' e z
) dqs z
) r.cls_use z
let fold_classes fn r z =
ExpMap.fold (fun e' (cls,_,_) z ->
fn e' (Cls.to_set cls) z
) r.cls_use z
let empty =
(fun r -> assert( invariant r ))
<&
{ sat= true; rep= ExpMap.empty; cls_use= ExpMap.empty; pnd= []; }
(** [merge leq r d e] adds an equation between expressions [d] and [e], and
propagates consequences. Extends carrier as needed. *)
let merge leq r d e =
assert(true$>
L.incf 10 "( merge: %a %s %a@ %a"
Exp.fmt d (if leq e d then ">=" else "<") Exp.fmt e fmt r );
(fun r' -> assert(
L.decf 10 ") merge:@ %a" fmt r' ;
invariant r'
&&
(not r'.sat
||
(normalize r' d == normalize r' e
|| failwithf "%a = %a not in %a" Exp.fmt d Exp.fmt e fmt r' ) &&
not (in_car d r && in_car e r)
|| is_rep (normalize r' d) r ) ))
(* Review: This assertion does not hold, check for reliance on it.
|| let d0' = normalize r d and e0' = normalize r e
and d' = normalize r' d and e' = normalize r' e in
if leq e0' d0'
then d' == e0' || failwithf "%a != %a" Exp.fmt d' Exp.fmt e0'
else e' == d0' || failwithf "%a != %a" Exp.fmt e' Exp.fmt d0' ))
*)
<&
if not r.sat then r else
propagate leq (add_to_pnd d e (extend d (extend e r)))
let split r d e =
if not r.sat then r else
let d', r = find_extend d r in
let e', r = find_extend e r in
if Exp.equal d' e' then
{r with sat= false}
else
add_to_dqs d' e' (add_to_dqs e' d' r)
let union leq p q =
assert(true$>
L.incf 10 "( union:@ @[<hv>%a@ %a@]" fmt p fmt q );
(fun r -> assert(
L.decf 10 ") union:@ %a" fmt r ;
ExpMap.for_all (fun e f ->
match leq e f, leq f e with
| true, false ->
Exp.equal (normalize r e) (normalize p f)
|| failwithf "%a should have rep %a" Exp.fmt e Exp.fmt (normalize p f)
| false, true ->
Exp.equal (normalize r f) (normalize p e)
|| failwithf "%a should have rep %a" Exp.fmt f Exp.fmt (normalize p e)
| _ -> true
) q.rep &&
invariant r ))
<&
if not p.sat then p else if not q.sat then q else
p |>
ExpMap.fold (fun e e' r -> merge leq r e e') q.rep |>
ExpMap.fold (fun e' (_,_,dqs) r -> Dqs.fold (fun e r -> split r e' e) dqs r) q.cls_use
let inter leq p q =
assert(true$>
L.incf 10 "( inter:@ @[<hv>%a@ %a@]" fmt p fmt q );
(fun r -> assert(
L.decf 10 ") inter:@ %a" fmt r ;
invariant r ))
<&
let merge_mem p q r =
ExpMap.fold (fun e e' r ->
if try Exp.equal (ExpMap.find e q.rep) (ExpMap.find e' q.rep) with Not_found -> false
then merge leq r e e'
else r
) p.rep r
in
let split_dqs p q r =
ExpMap.fold (fun e' (_,_,dqs) r ->
Dqs.fold (fun e r ->
if mem_dqs p e' e then
split r e' e
else
r
) dqs r
) q.cls_use r
in
if not p.sat then q else if not q.sat then p else
empty |>
merge_mem q p |>
merge_mem p q |>
split_dqs q p |>
split_dqs p q
(* Notes *)
(* - Is there a better implementation, that avoids enumerating the maps? *)
(* - The carrier need not be preserved by subst since the carrier need not be
closed under application of expressions in the carrier. Therefore a less
naive implementation of updating the use lists is not obvious. *)
(* - Specify more precisely. *)
exception NewEquality of Exp.t * Exp.t
let rec subst leq r kill_to_keep =
assert(true$>
L.incf 10 "( subst: %a@ %a" S.fmt kill_to_keep fmt r );
(fun r' -> assert(
L.decf 10 ") subst:@ %a" fmt r' ;
(not (try invariant r with _ -> false)) || invariant r' ))
<&
if S.is_empty kill_to_keep then r
else try
let rep =
ExpMap.fold (fun d d' rep ->
let e = S.subst kill_to_keep d in
let e' = S.subst kill_to_keep d' in
try
let f = ExpMap.find e r.rep in
let f' = ExpMap.find e' r.rep in
if not (Exp.equal f f') then
(* Substituting revealed a new equality. This is possible since
Substitution sees more internal structure of Expressions than
CngRel does, for instance arithmetic expressions are treated as
atoms in CngRel, but are substituted through. *)
raise (NewEquality (f,f'))
else
raise Not_found
with Not_found ->
if Exp.equal e d then
if Exp.equal e' d'
then rep
else ExpMap.add e e' rep
else ExpMap.add e e' (ExpMap.remove d rep)
) r.rep r.rep
in
let cls_use =
ExpMap.fold (fun d' (cls,_,_) cls_use ->
let e' = S.subst kill_to_keep d' in
let cls =
Cls.fold (fun e cls ->
let e' = S.subst kill_to_keep e in
if Exp.equal e e' then cls else Cls.add e' (Cls.remove e cls)
) cls cls in
let use = Use.empty in
let dqs = Dqs.empty in
if Exp.equal e' d'
then ExpMap.add e' (cls,use,dqs) cls_use
else ExpMap.add e' (cls,use,dqs) (ExpMap.remove d' cls_use)
) r.cls_use r.cls_use
in
let r =
ExpMap.fold (fun c _ r ->
match Exp.getApp c with
| None ->
r
| Some(f,a) ->
let f', r = find_extend f r in
let a', r = find_extend a r in
{r with cls_use= add_to_uses (f',a',c) r.cls_use}
) rep {r with rep; cls_use}
in
r
with
| NewEquality (f,f') ->
(* add new equality and restart *)
subst leq (merge leq r f f') kill_to_keep
(* Notes *)
(* - Is there a better implementation, that avoids enumerating the maps? *)
(* - The result of restrict is not a well-formed relation. The carrier is
not even closed under sub-expressions. Restrict should return a different
type. *)
let restrict r es =
assert(
L.incf 10 "( restrict: @[{%a}@]@ %a" Exps.fmt es fmt r;
let reps = representatives r in
Exps.subset reps es
|| failwithf "reps not in es: {@[%a@]}" Exps.fmt (Exps.diff reps es) );
(fun r -> assert(true$>
L.decf 10 ") restrict:@ %a" fmt r ))
<&
(* restrict dom of rep to es *)
let rep = ExpMap.filter (fun e _ -> Exps.mem e es) r.rep
in
let cls_use =
ExpMap.fold (fun e' (cls,_,dqs) cls_use ->
(* restrict dom of cls_use to es *)
if not (Exps.mem e' es) then
cls_use
else
(* restrict classes to es *)
let cls = Cls.inter es cls in
(* clear superexp equations *)
let use = Use.empty in
(* restrict disequalities to es *)
let dqs = Dqs.inter es dqs in
ExpMap.add e' (cls,use,dqs) cls_use
) r.cls_use ExpMap.empty
in
{r with rep; cls_use}
let remove_trivial r es =
assert(true$>
L.incf 10 "( remove_trivial: @[{%a}@]@ %a" Exps.fmt es fmt r );
(fun r -> assert(true$>
L.decf 10 ") remove_trivial:@ %a" fmt r ))
<&
Exps.fold (fun e r ->
try
let cls,_,_ = ExpMap.find e r.cls_use in
if Cls.is_empty (Cls.remove e cls) then
let rep = ExpMap.remove e r.rep in
let cls_use = ExpMap.remove e r.cls_use in
{r with rep; cls_use}
else
r
with Not_found ->
r
) es r
let implied_by leq init x assumptions carrier =
let terms = Exps.to_array carrier in
match Pure.get_implied_equalities x assumptions terms with
| None ->
empty
| Some(ids) ->
(* L.printf 0 "terms: @[%a@]" (Array.fmt "@ " Exp.fmt) terms ; *)
(* L.printf 0 "ids: @[%a@]" (Array.fmt "@ " Format.pp_print_int) ids ; *)
let n = Array.length terms in
let id_to_rep = IntHMap.create n in
snd (Array.fold_left (fun (i, cng) terms_i ->
try
let e' = IntHMap.find id_to_rep ids.(i) in
(* L.printf 0 "merging %a and %a" Exp.fmt terms_i Exp.fmt e' ; *)
(i+1, merge leq cng terms_i e')
with Not_found ->
(* L.printf 0 "choosing %a as rep for id %n" Exp.fmt terms_i ids.(i) ; *)
IntHMap.add id_to_rep ids.(i) terms_i ;
(i+1, merge leq cng terms_i terms_i)
) (0, init) terms)
let is_empty {rep; cls_use; pnd} =
ExpMap.is_empty rep && ExpMap.is_empty cls_use && pnd = []
(* Debug wrappers for entry points *)
(* let merge = debug_wrap4 Config.vCng 10 merge *)
(* let union = debug_wrap3 Config.vCng 10 union *)
(* let inter = debug_wrap2 Config.vCng 10 inter *)
(* let subst = debug_wrap2 Config.vCng 10 subst *)
(* let restrict = debug_wrap2 Config.vCng 10 restrict *)
(* let implied_by = debug_wrap5 Config.vCng 10 implied_by *)
(* end *)
(* include Make (Expression.Exp) (Expression.Exps) (Expression.ExpMap) *)

9
src/CngRel.mli Normal file
Просмотреть файл

@ -0,0 +1,9 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Congruence-closed relations *)
open CONGRUENCE_RELATION
open Expression
include CONGRUENCE_RELATION with type exp := Exp.t and type exps := Exps.t

541
src/Config.ml Normal file
Просмотреть файл

@ -0,0 +1,541 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Configuration parameters *)
open Library
let next_lexicographic_permutation a =
let rec loop1 k =
if k <= 1 then raise Not_found
else if a.(k-1) < a.(k) then k-1 else loop1 (k-1)
in
let rec loop2 k l =
if a.(k) < a.(l) then l else loop2 k (l-1)
in
let n = Array.length a in
try
let k = loop1 (n-1) in
let l = loop2 k (n-1) in
Array.swap a k l ;
Array.reverse a (k+1) n
with Not_found ->
()
let tag_perm = [|10;2;0;3;4;5;1;6;7;8;9|]
let rec set_perm i =
if i > 0 then (
next_lexicographic_permutation tag_perm ;
set_perm (i-1)
)
let _ = Random.self_init ()
let set_perm_randomly () =
let rec factorial n = if n < 2 then 1 else n * factorial (n-1) in
let i = Random.int (factorial (Array.length tag_perm)) in
Printf.printf "using permutation %i" i ;
set_perm i
type precondition_order = Syntactic | Logical | WeakerSubheap
let precondition_order_of_string = function
| "syntactic" -> Syntactic | "logical" -> Logical | "weaker-subheap" -> WeakerSubheap
| _ -> raise (Arg.Bad "unrecognized precondition order")
let string_of_precondition_order = function
| Syntactic -> "syntactic" | Logical -> "logical" | WeakerSubheap -> "weaker-subheap"
let abs_query_to_gen = ref 0
let c_syntax = ref false
let check_abs = ref false
let check_assumptions_time = ref infinity
let check_cng = ref false
let check_gie = ref false
let check_gie_vs_cc = ref false
let check_prv = ref false
let check_sorts = ref true
let check_scc = ref false
let compile_only = ref false
let continue = ref false
let dcc_gie = ref true
let distrib_pure = ref false
let entails_time = ref infinity
let exp_expand_ite = ref false
let exp_hc_initial_size = ref 16381 (* Note: tune, taken from http://oeis.org/A014234/list *)
let exp_simplify = ref true
let filenames = ref []
let font = ref (try Sys.getenv "SLAyer_FONT" with Not_found -> "")
let frontend_args = ref []
let full_results = ref false
let generalize_call_retn = ref true
let gie = ref 2
let gie_incremental = ref true
let gie_weak = ref false
let instrument = ref false
let join_powerset = ref true
let join_reduce = ref 0
let limit = ref 0
let limit_ghosts = ref (-1)
let margin = ref 176
let margin_frac = ref 64.0
let minor_heap_size = ref ((Gc.get()).Gc.minor_heap_size * Sys.word_size / (8*1024*1024))
let no_builtins = ref false
let norm_in_frontend = ref true
let norm_query_to_gen = ref 0
let optimize_frame = ref false
let optimize_icall_targets = ref false
let optimize_inline = ref 4
let optimize_unused = ref true
let optimize_liveness = ref true
let optimize_boxing = ref true
let precondition_order = ref WeakerSubheap
let preserve_consts = ref true
let propagate = ref false
let prv_gen_test = ref 0
let prv_simplify = ref true
let prv_strong_valid_check = ref false
let prv_valid_check = ref false
let prv_wbn = ref true
let ptr_size = ref 4
let pur_eager_qe = ref true
let quant_weight = ref 0
let raise_exceptions = ref false
let report_dead_code = ref false
let reset_freq = ref 0
let sh_simplify = ref true
let sh_hoist_common_subformulas = ref true
let show_unreachable = ref false
let stats = ref false
let subtract_time = ref infinity
let trust_casts = ref false
let tt_single_step = ref false
let typ_hc_initial_size = ref 1021 (* Note: tune, taken from http://oeis.org/A014234/list *)
let vATS = ref 0
let vAbs = ref 0
let vAbsH = ref 0
let vCEx = ref 0
let vCng = ref 0
let vDCC = ref 0
let vDiscovery = ref 0
let vExp = ref 0
let vFE = ref 0
let vFrame = ref 0
let vGraph = ref 0
let vHG = ref 0
let vInline = ref 0
let vInstr = ref 0
let vJoinPoint = ref 0
let vLiveness = ref 0
let vPrv = ref 0
let vPure = ref 0
let vRch = ref 0
let vSE = ref 0
let vSH = ref 0
let vPgm = ref 0
let vSubst = ref 0
let vTr = ref 2
let vTyp = ref 0
let vVar = ref 2
let vZ3 = ref 0
let version_only = ref false
let weak_pure_consequences = ref true
let write_ats = ref false
let write_cfg = ref false
let write_cl_cfg = ref false
let write_tt = ref false
let z3_distinct = ref false
let z3_ematching = ref true
let z3_log = ref false
let z3_memout = ref 0
let z3_model = ref false
let z3_print_mode = ref "full"
let z3_relevancy = ref 0
let z3_timeout = ref 0
let argspec = ref [
(* options that select which results to compute *)
("-version", Arg.Set version_only,
" Report version and exit");
("-c", Arg.Set compile_only,
" Translate input to internal representation but do not analyze");
("-cfg", Arg.Set write_cfg,
" Write internal representation of program as a control-flow graph to .cfg.dot file");
("-ats", Arg.Set write_ats,
" Write abstract transition system to .ats.dot file");
("-tt", Arg.Set write_tt,
" Write counter-examples to defect.tt to view with sdvdefect");
("-t2", Arg.Set instrument,
" Generate instrumented arithmetic program for T2");
("-continue", Arg.Set continue,
" Continue searching for errors after the first potential memory safety violation");
("-propagate", Arg.Set propagate,
" Propagate error conditions beyond potential memory safety violations");
(* options that affect external behavior *)
("-frame", Arg.Bool (fun x -> optimize_frame := x),
"<bool> Optimize: Frame \
(default="^ (string_of_bool !optimize_frame) ^")");
("-generalize-call-retn", Arg.Bool (fun x -> generalize_call_retn := x),
"<bool> Generalize assertions at procedure call and return sites \
(default="^ (string_of_bool !generalize_call_retn) ^")");
("-join-powerset", Arg.Bool (fun x -> join_powerset := x),
"<bool> Use powerset join instead of constructing disjunctive formulae \
(default="^ (string_of_bool !join_powerset) ^")");
("-limit", Arg.Set_int limit,
"<int> Limit length of chains at a program point (0 for unlimited) \
(default="^ (string_of_int !limit) ^")");
("-limit-ghosts", Arg.Set_int limit_ghosts,
" Limit on the number of stack frames worth of ghost variables kept \
(default="^ (string_of_int !limit_ghosts) ^")");
("-precondition-order", Arg.String (fun s -> precondition_order := precondition_order_of_string s),
" The order relation to use for procedure preconditions, one of syntactic, logical, weaker-subheap \
(default="^ (string_of_precondition_order !precondition_order) ^")");
("-preserve-consts", Arg.Bool (fun x -> preserve_consts := x),
"<bool> Preserve facts about program constants \
(default="^ (string_of_bool !preserve_consts) ^")");
("-trust-casts", Arg.Bool (fun x -> trust_casts := x),
"<bool> Trust casts that increase object size \
(default="^ (string_of_bool !trust_casts) ^")");
(* options that control progress reporting *)
("-results", Arg.Set full_results,
" Report full results");
("-st", Arg.Set stats,
" Report time and memory consumption statistics");
("-dead", Arg.Set report_dead_code,
" Report dead code");
("-ATS-show-unreachable", Arg.Bool (fun x -> show_unreachable := x),
"<bool> Show unreachable states in the abstract transition system \
(default="^(string_of_bool !show_unreachable)^")");
("-ATS-reduce", Arg.Set_int join_reduce,
"<int> Reduce the constructed abstract transition system by removing redundant vertices \
(default="^ (string_of_int !join_reduce) ^")");
("-tt-single-step", Arg.Set tt_single_step,
" Expand counter-example traces in defect.tt to take a step for each internal instruction, \
rather than the default one step per source line");
("-c-syntax", Arg.Set c_syntax,
" Generate output in as close to C syntax as possible");
("-margin", Arg.Set_int margin,
"<int> The right margin used by the pretty printer \
(default="^ (string_of_int !margin) ^")");
("-margin-frac", Arg.Set_float margin_frac,
"<float> The target columns/lines fraction of pretty printed graph vertices \
(default="^ (string_of_float !margin_frac) ^")");
("-font", Arg.Set_string font,
"<string> The font used in dot graphs \
(default="^ !font ^")");
("-vAbs", Arg.Set_int vAbs, "<int> Verbosity of Abstraction module");
("-vAbsH", Arg.Set_int vAbsH, "<int> Verbosity of HeapAbstraction module");
("-vATS", Arg.Set_int vATS, "<int> Verbosity of AbstractTransistionSystem module");
("-vCEx", Arg.Set_int vCEx, "<int> Verbosity of CounterExample module");
("-vCng", Arg.Set_int vCng, "<int> Verbosity of CngRel module");
("-vDCC", Arg.Set_int vDCC, "<int> Verbosity of DisjCngClos module");
("-vDiscovery", Arg.Set_int vDiscovery, "<int> Verbosity of Discovery module");
("-vExp", Arg.Set_int vExp, "<int> Verbosity of Expression module");
("-vFrame", Arg.Set_int vFrame, "<int> Verbosity of Frame module");
("-vGraph", Arg.Set_int vGraph, "<int> Verbosity of Graph module");
("-vHG", Arg.Set_int vHG, "<int> Verbosity of HeapGraph module");
("-vInstr", Arg.Set_int vInstr, "<int> Verbosity of Instrumentation module");
("-vPrv", Arg.Set_int vPrv, "<int> Verbosity of Prover module");
("-vPure", Arg.Set_int vPure, "<int> Verbosity of Pure module") ;
("-vRch", Arg.Set_int vRch, "<int> Verbosity of Reachability module");
("-vSE", Arg.Set_int vSE, "<int> Verbosity of SymbolicExecution module");
("-vSH", Arg.Set_int vSH, "<int> Verbosity of SymbolicHeap module");
("-vPgm", Arg.Set_int vPgm, "<int> Verbosity of Program module");
("-vSubst", Arg.Set_int vSubst, "<int> Verbosity of Substitution module");
("-vTr", Arg.Set_int vTr, "<int> Verbosity of reporting ATS transitions (default="^ (string_of_int !vTr) ^")");
("-vTyp", Arg.Set_int vTyp, "<int> Verbosity of Type module");
("-vVar", Arg.Set_int vVar, "<int> Verbosity of Variable module");
("-vZ3", Arg.Set_int vZ3, "<int> Verbosity of Z3 library") ;
(* options that control internal behavior but should not meaningfully affect results *)
("-DCC-gie", Arg.Bool (fun x -> dcc_gie := x),
"<bool> Call get_implied_equalities from DCC"
^" (default="^(string_of_bool !dcc_gie)^")");
("-Exp-simplify", Arg.Bool (fun x -> exp_simplify := x),
"<bool> Perform syntactic simplification of Expressions"
^" (default="^(string_of_bool !exp_simplify)^")");
("-Exp-expand-ite", Arg.Bool (fun x -> exp_expand_ite := x),
"<bool> Expand if-then-else Expressions into disjunctions"
^" (default="^(string_of_bool !exp_simplify)^")");
("-Exp-hc-size", Arg.Set_int exp_hc_initial_size,
"<int> Initial size of table for hash-consing Expressions"
^" (default="^(string_of_int !exp_hc_initial_size)^")");
("-Exp-compare", Arg.Int set_perm,
"<int> Use the ith lexicographic permutation for comparing expressions");
("-Exp-compare-random", Arg.Unit set_perm_randomly,
" Use a random permutation for comparing expressions");
("-Pur-eager-qe", Arg.Bool (fun x -> pur_eager_qe := x),
"<bool> Perform quantifier elimination when asserting formulas instead of when solving them \
(default="^(string_of_bool !pur_eager_qe)^")");
("-Prv-simplify", Arg.Bool (fun x -> prv_simplify := x),
"<bool> Simplify formulas during proof search \
(default="^(string_of_bool !prv_simplify)^")");
("-Prv-svc", Arg.Set prv_strong_valid_check,
" Use a strong (and expensive) check to fail proof search early \
(default="^(string_of_bool !prv_strong_valid_check)^")");
("-Prv-vc", Arg.Set prv_valid_check,
" Use a pure validity check to fail proof search early \
(default="^(string_of_bool !prv_valid_check)^")");
("-Prv-wbn", Arg.Bool (fun x -> prv_wbn := x),
"<bool> Compute existential witnesses using normalization \
(default="^(string_of_bool !prv_wbn)^")");
("-SH-distrib-pure", Arg.Bool (fun x -> distrib_pure := x),
"<bool> Distribute pure conjunction under disjunction \
(default="^(string_of_bool !distrib_pure)^")");
("-SH-simplify", Arg.Bool (fun x -> sh_simplify := x),
"<bool> Perform syntactic simplification of SymbolicHeaps"
^" (default="^(string_of_bool !sh_simplify)^")");
("-hcs", Arg.Unit (fun () -> sh_hoist_common_subformulas := not !sh_hoist_common_subformulas),
" Hoist common subformulas out of disjunctions"
^" (default="^(string_of_bool !sh_hoist_common_subformulas)^")");
("-SH-weak-pure-consequences", Arg.Bool (fun x -> weak_pure_consequences := x),
"<bool> Use a weak version of pure_consequences during normalization"
^" (default="^(string_of_bool !weak_pure_consequences)^")");
("-Typ-hc-size", Arg.Set_int typ_hc_initial_size,
"<int> Initial size of table for hash-consing Types"
^" (default="^(string_of_int !typ_hc_initial_size)^")");
("-Z3-distinct", Arg.Bool (fun x -> z3_distinct := x),
"<bool> Use 'distinct' formulas in Z3 encoding \
(default="^ (string_of_bool !z3_distinct) ^")") ;
("-Z3-quant-weight", Arg.Set_int quant_weight,
"<int> Set the weight of quantifiers, used by Z3 \
(default="^ (string_of_int !quant_weight) ^")") ;
("-Z3-quant-inst", Arg.Bool (fun x -> z3_ematching := x),
"<bool> Use heuristic quantifier instantiation \
(default="^ (string_of_bool !z3_ematching) ^")") ;
("-Z3-relevancy", Arg.Set_int z3_relevancy,
"<int> relevancy propagation heuristic \
(default="^ (string_of_int !z3_relevancy) ^")") ;
("-Z3-timeout", Arg.Set_int z3_timeout,
"<int> Set a time limit (in milliseconds) for calls to Z3") ;
("-Z3-memout", Arg.Set_int z3_memout,
"<int> Set a memory limit (in megabytes) for calls to Z3") ;
("-Z3-rf", Arg.Set_int reset_freq,
"<int> Z3 context reset frequency (0=never) \
(default="^ (string_of_int !reset_freq) ^")") ;
("-gie", Arg.Set_int gie,
"<int> Select 'get_implied_equalities' algorithm \
(default="^ (string_of_int !gie) ^")") ;
("-gie-incremental", Arg.Bool (fun x -> gie_incremental := x),
"<bool> Manage assertions incrementally for 'get_implied_equalities' \
(default="^ (string_of_bool !gie_incremental) ^")") ;
("-gie-weak", Arg.Bool (fun x -> gie_weak := x),
"<bool> Use weak encoding for 'get_implied_equalities' \
(default="^ (string_of_bool !gie_weak) ^")") ;
("-minor-heap-size", Arg.Set_int minor_heap_size,
"<int> Initial minor heap size in MB \
(default="^ (string_of_int !minor_heap_size) ^")") ;
(* options for debugging *)
("-checkCng", Arg.Bool (fun x -> check_cng := x),
"<bool> Perform expensive checking of CngRel operations \
(default="^ (string_of_bool !check_cng) ^")");
("-checkGIE", Arg.Bool (fun x -> check_gie := x),
"<bool> Check soundness and completeness of get_implied_equalities \
(default="^ (string_of_bool !check_gie) ^")") ;
("-checkGIEvsCC", Arg.Bool (fun x -> check_gie_vs_cc := x),
"<bool> Check that get_implied_equalities and congruence closure have equal strength \
(default="^ (string_of_bool !check_gie_vs_cc) ^")") ;
("-checkAbs", Arg.Bool (fun x -> check_abs := x),
"<bool> Use prover to check soundness of abstraction \
(default="^ (string_of_bool !check_abs) ^")");
("-checkPrv", Arg.Bool (fun x -> check_prv := x),
"<bool> Use prover to check its own soundness \
(default="^ (string_of_bool !check_prv) ^")");
("-checkSorts", Arg.Bool (fun x -> check_sorts := x),
"<bool> Check well-sortedness \
(default="^ (string_of_bool !check_sorts) ^")") ;
("-checkSCC", Arg.Bool (fun x -> check_scc := x),
"<bool> Perform expensive checking of SCC operations \
(default="^ (string_of_bool !check_scc) ^")");
("-exn", Arg.Set raise_exceptions,
" Raise unhandled exceptions for internal errors");
("-gta", Arg.Set_int abs_query_to_gen,
"<int> Generate standalone repro for Abstraction call number <int>");
("-gtn", Arg.Set_int norm_query_to_gen,
"<int> Generate repro for SymbolicHeap normalization");
("-gtp", Arg.Set_int prv_gen_test,
"<int> Generate standalone test for Prover query number <int>");
("-show-models", Arg.Set z3_model,
" Display Z3 models") ;
("-Z3-print-mode", Arg.Set_string z3_print_mode,
"<string> Set Z3 printing mode: full, low, smt, smt2") ;
("-tca", Arg.Set_float check_assumptions_time,
"<float> Report check_assumptions calls exceeding time. Negative for running max");
("-te", Arg.Set_float entails_time,
"<float> Report entails calls exceeding time. Negative for running max");
("-ts", Arg.Set_float subtract_time,
"<float> Report subtract calls exceeding time. Negative for running max");
("-Z3-log", Arg.Set z3_log,
" Log Z3 interactions") ;
(* frontend args *)
("--", Arg.Rest (fun rest -> frontend_args := List.append !frontend_args [rest]),
" Pass remaining arguments to frontend");
("-fe_norm", Arg.Bool (fun x -> norm_in_frontend := x),
"<bool> Normalize internal representation of program before instead of after marshalling (default="^
(string_of_bool !norm_in_frontend) ^")");
("-fe_cfg", Arg.Set write_cl_cfg,
" Write cl-dropped representation of program to .fe.cfg.dot file");
("-no-builtins", Arg.Set no_builtins,
" Do not include slayer.h when invoking C compiler");
("-ptr-size", Arg.Set_int ptr_size,
"<int> Size (in bytes) of pointers, e.g. 4 for 32-bit code or 8 for 64-bit code \
(default="^ (string_of_int !ptr_size) ^")");
("-Oicalls", Arg.Bool (fun x -> optimize_icall_targets := x),
"<int> Optimize: Constrain static approximation of indirect call targets using types \
(default="^ (string_of_bool !optimize_icall_targets) ^")");
("-Oinline", Arg.Set_int optimize_inline,
"<int> Optimize: Inline function calls (0=none, 1=loop- and call-free, 2=loop-free \
non-recursive, 3=as 2 with single call site, 4=non-recursive) (default="^
(string_of_int !optimize_inline) ^")");
("-Oboxing", Arg.Bool (fun x -> optimize_boxing := x),
"<int> Optimize: Only box address taken globals and global structs (default="^
(string_of_bool !optimize_boxing) ^")");
("-Oliveness", Arg.Bool (fun x -> optimize_liveness := x),
"<bool> Optimize: Apply liveness transformations \
(default="^ (string_of_bool !optimize_liveness) ^")");
("-Ounused", Arg.Bool (fun x -> optimize_unused := x),
"<bool> Optimize: Remove unused globals variables \
(default="^ (string_of_bool !optimize_unused) ^")");
("-vFE", Arg.Set_int vFE,
"<int> Verbosity of Frontend module");
("-vInline", Arg.Set_int vInline,
"<int> Verbosity of Inline module");
("-vJoinPoint", Arg.Set_int vJoinPoint,
"<int> Verbosity of JoinPoint module");
("-vLiveness", Arg.Set_int vLiveness,
"<int> Verbosity of Liveness module");
]
let _ =
let argspec = Arg.align !argspec
and anon_arg_func fname = filenames := fname :: !filenames
and usage = "Usage: "^Sys.argv.(0)^" <options> {-version | <file>.{c | sil}}"
in
Arg.parse argspec anon_arg_func usage
;
Format.pp_set_margin Format.str_formatter !margin
;
let good_usage =
!version_only ||
(match !filenames with
| [] -> false
| [fname] -> Filename.check_suffix fname ".c" || Filename.check_suffix fname ".sil"
| fnames -> List.for_all (fun fname -> Filename.check_suffix fname ".c") fnames
)
in
if not good_usage then (
Arg.usage argspec usage ;
exit 1
)
let abs_query_to_gen = !abs_query_to_gen
let check_abs = !check_abs
let check_assumptions_time = !check_assumptions_time
let check_cng = !check_cng
let check_gie = !check_gie
let check_gie_vs_cc = !check_gie_vs_cc
let check_prv = !check_prv
let check_sorts = !check_sorts
let check_scc = !check_scc
let compile_only = !compile_only
let continue = !continue
let dcc_gie = !dcc_gie
let distrib_pure = !distrib_pure
let entails_time = !entails_time
let exp_expand_ite = !exp_expand_ite
let exp_hc_initial_size = !exp_hc_initial_size
let exp_simplify = !exp_simplify
let filenames = List.rev !filenames
let font = !font
let frontend_args = !frontend_args
let full_results = !full_results
let generalize_call_retn = !generalize_call_retn
let gie = !gie
let gie_incremental = !gie_incremental
let gie_weak = !gie_weak
let instrument = !instrument
let join_powerset = !join_powerset
let join_reduce = !join_reduce
let limit = !limit
let limit_ghosts = !limit_ghosts
let margin = !margin
let margin_frac = !margin_frac
let minor_heap_size = !minor_heap_size
let no_builtins = !no_builtins
let norm_in_frontend = !norm_in_frontend
let norm_query_to_gen = !norm_query_to_gen
let optimize_frame = !optimize_frame
let optimize_icall_targets = !optimize_icall_targets
let optimize_inline = !optimize_inline
let optimize_liveness = !optimize_liveness
let optimize_unused = !optimize_unused
let optimize_boxing = !optimize_boxing
let precondition_order = !precondition_order
let preserve_consts = !preserve_consts
let propagate = !propagate
let prv_gen_test = !prv_gen_test
let prv_simplify = !prv_simplify
let prv_strong_valid_check = !prv_strong_valid_check
let prv_valid_check = !prv_valid_check
let prv_wbn = !prv_wbn
let ptr_size = !ptr_size
let pur_eager_qe = !pur_eager_qe
let quant_weight = !quant_weight
let raise_exceptions = !raise_exceptions
let report_dead_code = !report_dead_code
let reset_freq = !reset_freq
let sh_simplify = !sh_simplify
let sh_hoist_common_subformulas = !sh_hoist_common_subformulas
let show_unreachable = !show_unreachable
let stats = !stats
let subtract_time = !subtract_time
let trust_casts = !trust_casts
let tt_single_step = !tt_single_step
let typ_hc_initial_size = !typ_hc_initial_size
let version_only = !version_only
let weak_pure_consequences = !weak_pure_consequences
let write_ats = !write_ats
let write_cfg = !write_cfg
let write_cl_cfg = !write_cl_cfg
let write_tt = !write_tt
let z3_distinct = !z3_distinct
let z3_ematching = !z3_ematching
let z3_log = !z3_log
let z3_memout = !z3_memout
let z3_model = !z3_model
let z3_print_mode = !z3_print_mode
let z3_relevancy = !z3_relevancy
let z3_timeout = !z3_timeout
let testname =
match filenames with
| [fname] -> Filename.chop_extension fname
| _ -> Filename.basename (Sys.getcwd ())

354
src/CounterExample.ml Normal file
Просмотреть файл

@ -0,0 +1,354 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Generation of counter-example trace for sdvdefect viewer *)
open Library
open Variable
open Program
module I = Inst
module C = Cmnd
module K = ControlPoint
module SHD = Analysis.SymbolicHeapsDomain
module ID = Analysis.InterprocDomain
module ATS = ID.ATS
module Tr = ID.Tr
module L = (val Log.std Config.vCEx : Log.LOG)
exception Unsupported
(*================================================================================================================
Abstract Counter-Example Traces
================================================================================================================*)
let path_between shortest ats start error =
let cmp0 x y =
if shortest then x <= y else x >= y
in
let dist =
let vtx_to_idx,_, dist,_ = ATS.fw ats in
let to_idx vtx = ATS.VertexIMap.tryfind vtx_to_idx vtx |> Option.get in
let error_idx = to_idx error in
fun vtx ->
dist.(to_idx vtx).(error_idx)
in
let rec walk cmp m vtx =
if ATS.Vertex.equal error vtx then
[]
else match
ATS.fold_succs (fun succ edge dist_edge_succ ->
let succ_dist = dist succ in
match dist_edge_succ with
| Some(dist,_,_) when cmp dist succ_dist ->
dist_edge_succ
| _ ->
Some(succ_dist, edge, succ)
) vtx None
with
| Some(_, edge, succ) ->
if ATS.VertexSet.mem succ m then
(vtx, edge) :: walk ( <= ) m succ
else
(vtx, edge) :: walk cmp0 (ATS.VertexSet.add succ m) succ
| None ->
[]
in
walk cmp0 (ATS.VertexSet.singleton start) start
let fmt_path ff (path, error) =
Format.fprintf ff "%a@\n%a"
(List.fmt "@\n" (fun ff (v,e) -> Format.fprintf ff "@[<v 4>%a@\n%a@\n@]" ATS.Vertex.fmt v Tr.fmt e)) path
ATS.Vertex.fmt error
(*================================================================================================================
Generation of Counter-Example Traces for sdvdefect
================================================================================================================*)
module SdvDefect = struct
(* Some characters are interpreted specially by statestr2state in TraceTreeParser.cs:
'^' is translated to '\n' and '_' is translated to ' '. *)
let refmt fmt x =
let src_buf = Buffer.create 1024 in
let ff = Format.formatter_of_buffer src_buf in
Format.pp_set_margin ff Config.margin ;
fmt ff x ;
Format.pp_print_flush ff () ;
let dst_buf = Buffer.create 1024 in
for i = 0 to Buffer.length src_buf - 1 do
match Buffer.nth src_buf i with
(* apply inverse of sdvdefect's translation when generating defect.tt files *)
| '\n' -> Buffer.add_string dst_buf "^"
| ' ' -> Buffer.add_string dst_buf "_"
(* translate specially interpreted characters to avoid sdvdefect's translation *)
| '^' -> Buffer.add_string dst_buf "/\\"
| '_' -> Buffer.add_string dst_buf "."
| char -> Buffer.add_char dst_buf char
done ;
Buffer.contents dst_buf
(* sdvdefect generally does not accept empty/whitespace strings *)
let fmt_str ff x =
Format.pp_print_string ff (if x = "" then "?" else x)
(* Regular expressions below are those from TraceTreeParser.cs used to parse defect.tt "InstructionType"s. *)
(* "^(?<step>[0-9]+)[ \\t\"]+(?<file>[^\"]+)[ \\t\"]+(?<line>[0-9]+)[ \\t]+(?<isslice>[^ ]+)[ \\t]+(?<state>[^ ]+)[ \\t]+Call[ \t]+\"(?<caller>[^\"]+)\"+[ \\t]+\"(?<callee>[^\"]+)\"[ \\t]*" *)
let write_call fmt_state ff (step, file, line, isslice, state, caller, callee) =
Format.fprintf ff "%d %a %d %a %a Call \"%a\" \"%a\"@\n"
step fmt_str file line fmt_str isslice fmt_str (refmt fmt_state state) fmt_str caller fmt_str callee
(* "^(?<step>[0-9]+)[ \\t\"]+(?<file>[^\"]+)[ \\t\"]+(?<line>[0-9]+)[ \\t]+(?<isslice>[^ ]+)[ \\t]+(?<state>[^ ]+)[ \\t]+Return[ \\t]*" *)
let write_return fmt_state ff (step, file, line, isslice, state) =
Format.fprintf ff "%d %a %d %a %a Return@\n"
step fmt_str file line fmt_str isslice fmt_str (refmt fmt_state state)
(* "^(?<step>[0-9]+)[ \\t\"]+(?<file>[^\"]+)[ \\t\"]+(?<line>[0-9]+)[ \\t]+(?<isslice>[^ ]+)[ \\t]+(?<state>[^ ]+)[ \\t]+Atomic[ \\t]+(?<desc>[^ ]+)[ \\t]*" *)
let write_atomic fmt_state ff (step, file, line, isslice, state, desc) =
Format.fprintf ff "%d %a %d %a %a Atomic %a@\n"
step fmt_str file line fmt_str isslice fmt_str (refmt fmt_state state) fmt_str desc
(* "^Driver([ \\t\"]+)(?<driver>[^\"]+)[ \\t\"]*" *)
let write_driver ff driver =
Format.fprintf ff "Driver %a@\n"
fmt_str driver
(* "^Rule[ \\t]+(?<rule>[^ ]+)[ \\t]*" *)
let write_rule ff rule =
Format.fprintf ff "Rule %a@\n"
fmt_str rule
(* "^Error[ \\t]+(?<error>.* )" *)
(* sdvdefect complains if there is more than one Error per defect file *)
let _write_pattern ff error =
Format.fprintf ff "Error %a@\n"
fmt_str error
end
let write_defect_tt_path fmt ff path =
let step =
let step = ref 0 in
fun () ->
incr step ;
!step
in
let write_blk vtx blk =
let {I.pos= {Position.dir; file; line}} = List.hd blk in
let fmt_blk ff blk = Format.fprintf ff "@[<hv>%a;@]" (List.fmt ";@ " I.fmt) blk in
let fmt_state ff (vtx, blk) = Format.fprintf ff "@\n%a@\n@\n%a" fmt vtx fmt_blk blk in
SdvDefect.write_atomic fmt_state ff (step(), dir^"\\"^file, line, "false", (vtx, blk), "")
in
let write_call vtx ({Call.proc= {Proc.id}} as call) =
let _,k = ID.I_D_cp.project vtx in
let {Position.dir; file; line} = K.pos k in
let fmt_call ff call = Call.fmt (fun ff {Proc.id} -> Proc.Id.fmt ff id) ff call in
let fmt_state ff (vtx, call) = Format.fprintf ff "@\n%a@\n%a" (Option.fmt "" fmt) vtx fmt_call call in
let caller = Proc.Id.name (K.proc k) in
let callee = Proc.Id.name id in
SdvDefect.write_call fmt_state ff (step(), dir^"\\"^file, line, "false", (Some vtx, call), caller, callee)
in
let write_retn vtx =
let _,k = ID.I_D_cp.project vtx in
let {Position.dir; file; line} = K.pos k in
let fmt_state ff vtx = Format.fprintf ff "@\n%a@\nreturn" (Option.fmt "" fmt) vtx in
SdvDefect.write_return fmt_state ff (step(), dir^"\\"^file, line, "false", Some vtx)
in
let rec write_path = function
| (vtx, edg) :: path ->
(match edg with
| Tr.Intra(_, [], _,_) -> ()
| Tr.Intra(_, blk, _,_) -> write_blk vtx blk
| Tr.Call(call) -> write_call vtx call
| Tr.Return -> write_retn vtx
| Tr.Summary -> ()
);
write_path path
| [] ->
()
in
write_path path
let write_defect_tt paths buf =
let c_syntax = !Config.c_syntax in
Config.c_syntax := not c_syntax ;
let ff = Format.formatter_of_buffer buf in
SdvDefect.write_driver ff Config.testname ;
SdvDefect.write_rule ff "Memory Safety" ;
(* To include multiple traces in one file,
add a bogus toplevel Call/Return and a bogus Call/Return around each trace. *)
SdvDefect.write_call (fun _ () -> ()) ff (0, "", 0, "true", (), "", "Counter-examples") ;
List.iter (fun (name, path, fmt) ->
SdvDefect.write_call (fun _ () -> ()) ff (0, "", 0, "true", (), "", name) ;
write_defect_tt_path fmt ff path ;
SdvDefect.write_return (fun _ () -> ()) ff (0, "", 0, "true", ())
) paths ;
SdvDefect.write_return (fun _ () -> ()) ff (0, "", 0, "true", ()) ;
Config.c_syntax := c_syntax
(*================================================================================================================
Abstract Counter-Example Transition Systems
================================================================================================================*)
let error_slice ats start_vtx error_vtx =
let ats_to_slice, error_slice = ATS.slice start_vtx error_vtx ats in
let start_vtx = Option.get (ATS.VertexMap.tryfind start_vtx ats_to_slice) in
let error_vtx = Option.get (ATS.VertexMap.tryfind error_vtx ats_to_slice) in
(error_slice, start_vtx, error_vtx)
let add_intermediate_states {Prog.globals; procs} ats start_vtx =
let proc = Proc.IdHMap.tryfind procs (K.proc (snd (ID.I_D_cp.project start_vtx))) |> Option.get in
let {Proc.id; formals; freturn; locals} = proc in
let cfg = CFG.create () in
let cxt = Vars.union globals (Vars.adds formals (Option.fold Vars.add freturn locals)) in
ATS.iter_edges (fun _ -> ()) (fun (u,tr,w) ->
match tr with
| Tr.Intra(_, (_::_::_ as c), _, leak) ->
let blks = List.divide (fun i j -> (not Config.tt_single_step) && I.(Position.equal i.pos j.pos)) c in
let rec loop u p blks =
match blks with
| [] ->
()
| [blk] ->
let tr = Tr.Intra(ATS.index_of u, blk, ATS.index_of w, leak) in
ATS.add_edge ats u tr w
| blk :: blks ->
let h = ATS.index_of u in
let {I.pos} = List.hd blk in
let k = CFG.add_vertex cfg (K.mk_label pos id) in
let tr = Tr.Intra(h, blk, k, false) in
let q = List.fold_left (fun q i -> ID.RD.exec_inst cxt i q) p blk in
let v = ATS.add_vertex ats (k, q) in
ATS.add_edge ats u tr v ;
loop v q blks
in
ATS.remove_edge ats u tr w ;
let p,_ = ID.I_D_cp.project u in
loop u p blks
| _ ->
()
) ats (ATS.index_of start_vtx)
let add_error_loops program ats start_vtx error_vtx =
L.incf 10 "( add_error_loops" ; (fun _ -> L.decf 10 ") add_error_loops") <&
let {Prog.procs} = program in
let vtx_map, ats = ATS.copy ats start_vtx in
let start_vtx = Option.get (ATS.VertexMap.tryfind start_vtx vtx_map) in
let error_vtx = Option.get (ATS.VertexMap.tryfind error_vtx vtx_map) in
ATS.iter_preds (fun pred_vtx _ ->
L.printf 10 "error predecessor: %a" ATS.Vertex.fmt pred_vtx ;
let pred_k = ATS.index_of pred_vtx in
let pred_kid = K.id pred_k in
let proc = K.proc pred_k in
let {Proc.cfg} = Option.get (Proc.IdHMap.tryfind procs proc) in
let cfg_scc_of = CFG.scc cfg in
let pred_cfg_vtx = List.hd (CFG.vertices_for cfg pred_kid) in
let pred_cfg_scc = CFG.VertexSet.of_list (Option.get (CFG.VertexMap.tryfind pred_k cfg_scc_of)) in
let pred_cfg_scc_wo_pred = CFG.VertexSet.remove pred_cfg_vtx pred_cfg_scc in
let cfg_to_ats = CFG.VertexMap.singleton pred_cfg_vtx pred_vtx in
CFG.fold_edges
(fun cfg_vtx cfg_to_ats ->
if not (CFG.VertexSet.mem cfg_vtx pred_cfg_scc_wo_pred) then cfg_to_ats else
let ats_vtx = ATS.add_vertex ats (cfg_vtx, ID.RD.inject SHD.tt) in
CFG.VertexMap.add cfg_vtx ats_vtx cfg_to_ats
)
(fun (u,c,v) cfg_to_ats ->
match CFG.VertexMap.tryfind u cfg_to_ats, c, CFG.VertexMap.tryfind v cfg_to_ats with
| Some(u'), C.Inst(i), Some(v') ->
let c' = Tr.Intra(u, [i], v, false) in
ATS.add_edge ats u' c' v' ;
cfg_to_ats
| Some _, c, Some _ ->
L.printf 1 "CounterExample: unexpected command: %a" C.fmt c ;
raise Unsupported
| _ ->
cfg_to_ats
) cfg pred_kid cfg_to_ats
|> ignore
) error_vtx ;
ATS.concat_blocks ats start_vtx ;
(ats, start_vtx, error_vtx)
(*================================================================================================================
Counter-Example Generation Driver
================================================================================================================*)
let fmt_ats_state ff v =
ID.RD.fmt ff (fst (ID.I_D_cp.project v))
let disprove results =
L.incf 10 "( disprove" ; (fun _ -> L.decf 10 ") disprove") <&
try
let {Analysis.program; invariants} = results in
let ats = ID.ats invariants in
let start_vtx = List.hd (ATS.roots ats) in
let traces =
[]
|>
List.fold_right (fun error_vtx traces ->
L.incf 10 "( error: %a" ATS.Vertex.fmt error_vtx ; (fun _ -> L.decf 10 ")") <&
let error_id = string_of_int (ATS.VertexIMap.find (ATS.identify_vertices ats) error_vtx) in
let error_slice, slice_start_vtx, slice_error_vtx = error_slice ats start_vtx error_vtx in
if Config.write_ats then
ID.write_ats (Config.testname^".o.err"^error_id) program error_slice (ATS.index_of slice_start_vtx) ;
add_intermediate_states program error_slice slice_start_vtx ;
if Config.write_ats then
ID.write_ats (Config.testname^".d.err"^error_id) program error_slice (ATS.index_of slice_start_vtx) ;
let error_loops, loops_start_vtx, _ = add_error_loops program ats start_vtx error_vtx in
if Config.write_ats then
ID.write_ats (Config.testname^".l.err"^error_id) program error_loops (ATS.index_of loops_start_vtx) ;
let shortest_path = path_between true error_slice slice_start_vtx slice_error_vtx in
L.printf 1 "@\nshortest error path:@\n%a" fmt_path (shortest_path, error_vtx) ;
let longest_path = path_between false error_slice slice_start_vtx slice_error_vtx in
L.printf 1 "@\nlongest error path:@\n%a" fmt_path (longest_path, error_vtx) ;
("Shortest abstract path to Access Violation "^error_id, shortest_path, fmt_ats_state) ::
("Longest abstract path to Access Violation "^error_id, longest_path, fmt_ats_state) ::
traces
) (Analysis.errors results)
|>
List.fold_right (fun leak_vtx traces ->
L.incf 10 "( leak: %a" ATS.Vertex.fmt leak_vtx ; (fun _ -> L.decf 10 ")") <&
let leak_id = string_of_int (ATS.VertexIMap.find (ATS.identify_vertices ats) leak_vtx) in
let leak_slice, slice_start_vtx, slice_leak_vtx = error_slice ats start_vtx leak_vtx in
if Config.write_ats then
ID.write_ats (Config.testname^".o.leak"^leak_id) program leak_slice (ATS.index_of slice_start_vtx) ;
add_intermediate_states program leak_slice slice_start_vtx ;
if Config.write_ats then
ID.write_ats (Config.testname^".d.leak"^leak_id) program leak_slice (ATS.index_of slice_start_vtx) ;
let shortest_path = path_between true leak_slice slice_start_vtx slice_leak_vtx in
L.printf 1 "@\nshortest leak path:@\n%a" fmt_path (shortest_path, leak_vtx) ;
let longest_path = path_between false leak_slice slice_start_vtx slice_leak_vtx in
L.printf 1 "@\nlongest leak path:@\n%a" fmt_path (longest_path, leak_vtx) ;
("Shortest abstract path to Leak "^leak_id, shortest_path, fmt_ats_state) ::
("Longest abstract path to Leak "^leak_id, longest_path, fmt_ats_state) ::
traces
) (Analysis.leaks results)
in
if Config.write_tt then Library.with_out "defect.tt" (write_defect_tt traces) ;
false
with Unsupported ->
L.printf 0 "CEx: Unsupported transition" ;
false

6
src/CounterExample.mli Normal file
Просмотреть файл

@ -0,0 +1,6 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Generation of counter-example trace for sdvdefect viewer *)
val disprove : Analysis.t -> bool

302
src/Discovery.ml Normal file
Просмотреть файл

@ -0,0 +1,302 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Discovery of patterns for parametric inductive predicates *)
open Library
open Type
open Variable
open Expression
module E = Exp
module S = Substitution
open SYMBOLIC_HEAP
open SymbolicHeap
open Program
module L = (val Log.std Config.vDiscovery : Log.LOG)
module LSH = (val Log.std Config.vSH : Log.LOG)
(*============================================================================
Patterns
============================================================================*)
(** Patn constructor support *)
let deref_typ ty =
match Typ.desc ty with
| Typ.Pointer(t) -> t
| _ -> invalid_arg "Discovery.deref_typ"
let name_of_path path =
String.concat "." (List.rev_map Fld.name path)
let param_of_path path =
let name = name_of_path path in
let p = Var.gensym ("p"^name) Var.PointerSort in
let f = Var.gensym ("f"^name) Var.PointerSort in
let b = Var.gensym ("b"^name) Var.PointerSort in
let n = Var.gensym ("n"^name) Var.PointerSort in
(p, f, b, n)
let pt_of_path base typ cnts path =
let loc = E.mkAdds base path in
let off = Off.mkPath typ path in
let cnt = try Some(List.assoc path cnts) with Not_found -> None in
{Pt.loc; off; cnt}
let obj_of_typ typ cnts pts =
let paths = Typ.all_offsets typ in
let name =
match paths with
| [] -> "void"
| (_,[],_)::_ -> "empty"
| (_,[field],_)::_ -> name_of_path [field]
| (_,_::prefix,_)::_ -> name_of_path prefix in
let paths = if paths <> [] then paths else [((0,None), [], Typ.mkTop)] in
let base = E.mkVar (Var.gensym name Var.PointerSort) in
let pts =
List.fold (fun (_,path,_) pts ->
pt_of_path base typ cnts path :: pts
) paths pts in
(base, pts)
let mk_sll id mk_body =
L.printf 2 "Trying to add %s..." id ;
(fun p -> L.shift_verb (-1) (fun () -> L.printf 2 "@[<hov 2>Adding %s =@ %a@]" id Patn.fmt p))
<&
let typ = Typ.find_by_name id |> Option.get in
let (_,base,_), paths = List.take (fun _ -> true) (Typ.all_offsets typ) in
let _pv, fv, _bv, nv = param_of_path (List.tl base) in
let f, n = E.mkVar fv, E.mkVar nv in
let us, eqs, cnts, pts = mk_body typ f n in
let params, eqs, pts =
List.fold_right (fun (_,path,_) ({prev; frnt; back; next}, eqs, pts) ->
let _p', f', _b', n' = param_of_path path in
( {prev; frnt= f'::frnt; back; next= n'::next}
, E.mkEq (E.mkVar f') (E.mkAdds f path) ::
E.mkEq (E.mkVar n') (E.mkAdds n path) :: eqs
, pt_of_path f typ cnts path :: pts
)
) paths ({prev= []; frnt= []; back=[]; next= []}, eqs, pts) in
let {prev; frnt; back; next} = params in
let params = {prev; frnt= fv::frnt; back; next= nv::next} in
let pts = pt_of_path f typ cnts base :: pts in
let body = SH.Pf.star eqs (SH.PtS.star pts SH.emp) in
Patn.mk ~name:id params
(SH.exists_intro (Vars.diff (SH.fv body) (Vars.union us (Params.fv params)))
body)
let mk_dll id mk_body =
L.printf 2 "Trying to add %s..." id ;
(fun p -> L.shift_verb (-1) (fun () -> L.printf 2 "@[<hov 2>Adding %s =@ %a@]" id Patn.fmt p))
<&
let typ = Typ.find_by_name id |> Option.get in
let (_,base,_), paths = List.take (fun _ -> true) (Typ.all_offsets typ) in
let pv, fv, bv, nv = param_of_path (List.tl base) in
let p, f, b, n = E.mkVar pv, E.mkVar fv, E.mkVar bv, E.mkVar nv in
let us, eqs, cnts, pts = mk_body typ p f b n in
let params, eqs, pts =
List.fold_right (fun (_,path,_) ({prev; frnt; back; next}, eqs, pts) ->
let p', f', b', n' = param_of_path path in
( {prev= p'::prev; frnt= f'::frnt; back=b'::back; next= n'::next}
, E.mkEq (E.mkVar p') (E.mkAdds p path) ::
E.mkEq (E.mkVar f') (E.mkAdds f path) ::
E.mkEq (E.mkVar b') (E.mkAdds b path) ::
E.mkEq (E.mkVar n') (E.mkAdds n path) :: eqs
, pt_of_path f typ cnts path :: pts
)
) paths ({prev= []; frnt= []; back=[]; next= []}, eqs, pts) in
let {prev; frnt; back; next} = params in
let params = {prev= pv::prev; frnt= fv::frnt; back=bv::back; next= nv::next} in
let pts = pt_of_path f typ cnts base :: pts in
let body = SH.Pf.star eqs (SH.PtS.star pts SH.emp) in
Patn.mk ~name:id params
(SH.exists_intro (Vars.diff (SH.fv body) (Vars.union us (Params.fv params)))
body)
(** Patn constructors *)
let mk_patn_sll id =
mk_sll id (fun sll _f n ->
let fFlink, _ = Fld.find_by_name sll "Flink" |> Option.get in
let us = Vars.empty in
let eqs = [] in
let cnts = [([fFlink], n)] in
let pts = [] in
(us, eqs, cnts, pts)
)
let mk_patn_dll id =
mk_dll id (fun dll p f b n ->
let fFlink, _ = Fld.find_by_name dll "Flink" |> Option.get in
let fBlink, _ = Fld.find_by_name dll "Blink" |> Option.get in
let us = Vars.empty in
let eqs = [E.mkEq f b] in
let cnts = [([fFlink], n); ([fBlink], p)] in
let pts = [] in
(us, eqs, cnts, pts)
)
let mk_patn_crom_data id =
mk_dll id (fun cd p f b n ->
let fCromList, le = Fld.find_by_name cd "CromList" |> Option.get in
let fFlink, _ = Fld.find_by_name le "Flink" |> Option.get in
let fBlink, _ = Fld.find_by_name le "Blink" |> Option.get in
let fBuffer, pvoid = Fld.find_by_name cd "Buffer" |> Option.get in
let fpMdl, pmdl = Fld.find_by_name cd "pMdl" |> Option.get in
let us = Vars.empty in
let pts = [] in
let mdl, pts = obj_of_typ (deref_typ pmdl) [] pts in
let buffer, pts = obj_of_typ (deref_typ pvoid) [] pts in
let eqs = [E.mkEq f b] in
let cnts =
[ ([fFlink;fCromList], n)
; ([fBlink;fCromList], p)
; ([fBuffer], buffer)
; ([fpMdl], mdl)
] in
(us, eqs, cnts, pts)
)
let mk_patn_async_address_data id _devExt =
mk_dll id (fun aad p f b n ->
let fAsyncAddressList, le = Fld.find_by_name aad "AsyncAddressList" |> Option.get in
let fFlink, _ = Fld.find_by_name le "Flink" |> Option.get in
let fBlink, _ = Fld.find_by_name le "Blink" |> Option.get in
let fBuffer, pvoid = Fld.find_by_name aad "Buffer" |> Option.get in
let fAddressRange, par = Fld.find_by_name aad "AddressRange" |> Option.get in
let fpMdl, pmdl = Fld.find_by_name aad "pMdl" |> Option.get in
let us = Vars.empty in
let pts = [] in
let mdl, pts = obj_of_typ (deref_typ pmdl) [] pts in
let addressRange, pts = obj_of_typ (deref_typ par) [] pts in
let buffer, pts = obj_of_typ (deref_typ pvoid) [] pts in
let eqs = [E.mkEq f b] in
let cnts =
[ ([fFlink;fAsyncAddressList], n)
; ([fBlink;fAsyncAddressList], p)
; ([fBuffer], buffer)
; ([fAddressRange], addressRange)
; ([fpMdl], mdl)
] in
(us, eqs, cnts, pts)
)
let mk_patn_isoch_detach_data id devExt =
mk_dll id (fun idd p f b n ->
let fIsochDetachList, le = Fld.find_by_name idd "IsochDetachList" |> Option.get in
let fFlink, _ = Fld.find_by_name le "Flink" |> Option.get in
let fBlink, _ = Fld.find_by_name le "Blink" |> Option.get in
let fDeviceExtension, _ = Fld.find_by_name idd "DeviceExtension" |> Option.get in
let us = Vars.singleton devExt in
let pts = [] in
let eqs = [E.mkEq f b] in
let cnts =
[ ([fFlink;fIsochDetachList], n)
; ([fBlink;fIsochDetachList], p)
; ([fDeviceExtension], E.mkVar devExt)
] in
(us, eqs, cnts, pts)
)
let mk_patn_isoch_resource_data id =
mk_dll id (fun idd p f b n ->
let fIsochResourceList, le = Fld.find_by_name idd "IsochResourceList" |> Option.get in
let fFlink, _ = Fld.find_by_name le "Flink" |> Option.get in
let fBlink, _ = Fld.find_by_name le "Blink" |> Option.get in
let us = Vars.empty in
let pts = [] in
let eqs = [E.mkEq f b] in
let cnts =
[ ([fFlink;fIsochResourceList], n)
; ([fBlink;fIsochResourceList], p)
] in
(us, eqs, cnts, pts)
)
(*============================================================================
Initialization
============================================================================*)
(** Patn ids *)
(* SI: the AAD and IDD patterns are hard-coded to pick up the
harness.h-generated SL_Context_DEVICE_EXTENSION device extension. *)
let id_sll = "_SLL_ENTRY"
let id_dll = "_LIST_ENTRY"
let id_crom_data = "_CROM_DATA"
let id_async_address_data = "_ASYNC_ADDRESS_DATA"
let id_isoch_detach_data = "_ISOCH_DETACH_DATA"
let id_isoch_resource_data = "_ISOCH_RESOURCE_DATA"
(** patn.name->patn dictionary *)
(* Only use after [initialize] has run. *)
let patn_dict = ref []
let patn_dict_add (id,p) =
patn_dict := (id,p) :: !patn_dict
let _ = Initialize.register (fun {Prog.globals} ->
L.incf 1 "( Discovery.init: adding patns to discovery list" ;
L.decf 1 ") Discovery.init"
$>
let sl_context_var =
let the_context_var =
Vars.fold (fun v result ->
if ("SL_Context_DEVICE_EXTENSION" = (Var.name v)) then (Some v) else result
) globals None in
match the_context_var with
| Some(v) -> v
| None ->
L.printf 1 "no SL_Context declared." ;
Var.gensym "DummySLContext" Var.PointerSort
in
try patn_dict_add (id_crom_data, mk_patn_crom_data id_crom_data)
with Not_found -> () ;
try patn_dict_add (id_async_address_data, mk_patn_async_address_data id_async_address_data sl_context_var)
with Not_found -> () ;
try patn_dict_add (id_isoch_detach_data, mk_patn_isoch_detach_data id_isoch_detach_data sl_context_var)
with Not_found -> () ;
try patn_dict_add (id_isoch_resource_data, mk_patn_isoch_resource_data id_isoch_resource_data)
with Not_found -> () ;
try patn_dict_add (id_sll, mk_patn_sll id_sll)
with Not_found -> () ;
try patn_dict_add (id_dll, mk_patn_dll id_dll)
with Not_found -> () ;
)
(*============================================================================
Queries
============================================================================*)
type result = Done | More of Patn.t * (unit -> result)
let discover _ =
let rec aux = function
| [] -> Done
| (_,x) :: xl -> More(x, fun()-> aux xl)
in
aux !patn_dict
let fold fn q a =
let rec fold_fn acc res =
match res () with
| Done -> acc
| More(pat,res) -> fold_fn (fn pat acc) res
in
fold_fn a (fun()-> discover q)

17
src/Discovery.mli Normal file
Просмотреть файл

@ -0,0 +1,17 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Discovery of patterns for parametric inductive predicates *)
open SymbolicHeap
(*============================================================================
Discovery
============================================================================*)
type result = Done | More of Patn.t * (unit -> result)
val discover : XSH.t -> result
val fold : (Patn.t -> 'a -> 'a) -> XSH.t -> 'a -> 'a

234
src/DisjCngClos.ml Normal file
Просмотреть файл

@ -0,0 +1,234 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Disjunctive Congruence Closure *)
open Library
open FORMULA
open CONGRUENCE_RELATION
module LblMap = IntMap
module L = (val Log.std Config.vDCC : Log.LOG)
(* Timing =================================================================== *)
let cc_tmr = Timer.create "DCC leaves by congruence closure"
let gie_tmr = Timer.create "DCC leaves by get_implied_equalities"
module Make
(Frm: FORMULA)
(CngRel: CONGRUENCE_RELATION
with type exp := Frm.Exp.t
and type exps := Frm.Exps.t) :
sig
val dcc : (Frm.Exp.t->Frm.Exp.t->bool) -> ?dnf:bool -> ?init:CngRel.t -> Frm.t -> (CngRel.t * CngRel.t) LblMap.t
end = struct
open Expression
(* module Exp = Frm.Exp *)
(* module Exps = Frm.Exps *)
(* module ExpMap = Frm.ExpMap *)
module Weights = struct
include ExpMap
let find e w = try find e w with Not_found -> 0
end
let cc_leaves leq dnf init f =
Timer.start cc_tmr ; (fun _ -> Timer.stop cc_tmr) <&
Frm.fold_dnf ~dnf
(fun f ((cube,r),m) ->
(* L.incf 0 "( map: %a" Frm.fmt f ; (fun _ -> L.decf 0 ") map") <& *)
(* L.incf 0 "( map: %a" Frm.fmt f ; (fun ((_cube,r),(_m,_w)) -> L.decf 0 ") map: %a" CngRel.fmt r) <& *)
let r =
Frm.fold_rels
(fun es r ->
match es with
| e::es -> List.fold (fun e' r -> CngRel.merge leq r e e') es r
| _ -> r
)
(fun e f r -> CngRel.merge leq r e f)
f r in
let r =
Frm.fold_nrels (fun e f r ->
CngRel.split r e f
) f r in
((f::cube,r),m)
)
(fun ((cube,r),m) ->
List.fold (fun f m ->
if not (Frm.is_leaf f) then m
else
let l = Frm.lbl f in
let s' = try CngRel.inter (fun _ _ -> true) (LblMap.find l m) r with Not_found -> r in
(* L.printf 0 "red: %i" l ; *)
(* L.printf 0 "red: %i@ %a" l CngRel.fmt s' ; *)
LblMap.add l s' m
) cube m
)
f ([], init) LblMap.empty
let ctx = Pure.mk ()
let gie_leaves leq init f =
Timer.start gie_tmr ; (fun _ -> Timer.stop gie_tmr) <&
let init_eqs = CngRel.fold (fun e' e b -> Exp.mkEq e' e :: b) init []
in
let pure_f, lbl_to_prop = Frm.labeled_pure_consequences f
in
Pure.conjoin_weak ctx (Exp.mkAnd (Array.of_list (pure_f :: init_eqs)))
;
let m =
Frm.fold_sp
(fun f (lbls, carrier) ->
(* L.printf 0 "dn: %a" Frm.fmt f ; *)
let carrier =
Frm.fold_rels
(fun es z -> List.fold Exps.add es z)
(fun e' e z -> Exps.add e' (Exps.add e z))
f carrier in
let lbls = Frm.lbl f :: lbls in
(lbls, carrier)
)
(fun f (lbls, carrier) m ->
(* L.printf 0 "up: @[[@[%a@]]@ %a@]" (List.fmt ";@ " Format.pp_print_int) lbls Frm.fmt f ; *)
if not (Frm.is_leaf f) then m
else
let lbl = Frm.lbl f in
let assumptions = Array.of_list (List.map (fun lbl -> IntMap.find lbl lbl_to_prop) lbls) in
let cng = CngRel.implied_by leq init ctx assumptions carrier in
LblMap.add lbl cng m
)
f ([], Exps.empty) LblMap.empty
in
Pure.clear ctx
;
m
let dcc leq ?(dnf=true) ?(init=CngRel.empty) f =
(* L.incf 0 "( dcc: %a" Frm.fmt f ; (fun _ -> L.decf 0 ") dcc") <& *)
(* Refine preorder [cmp] to prefer w-heavier exps *)
let init_reps = CngRel.representatives init
in
let leqr e f =
match Exps.mem e init_reps, Exps.mem f init_reps with
| true, false -> true
| false, true -> false
| _ -> leq e f
in
(* Compute strongest congruence relation forced by each leaf branch *)
let m = cc_leaves leqr dnf init f
in
(* Re-express congruence relations for leaves using final weights *)
let w =
LblMap.fold (fun _lbl cng w ->
Exps.fold (fun e' w ->
let cls = CngRel.class_of cng e' in
let cls_size = Exps.cardinal cls - 1 in
Exps.fold (fun e w ->
Weights.modify_add (fun old -> old + cls_size) e cls_size w
) cls w
) (CngRel.representatives cng) w
) m Weights.empty
in
let leqw w e f =
(* (fun o -> L.printf 0 "%a %s %a" Exp.fmt e (if o then "<=" else ">") Exp.fmt f) <& *)
match Exps.mem e init_reps, Exps.mem f init_reps with
| true, false -> true
| false, true -> false
| _ ->
(Weights.find e w) > (Weights.find f w) || leq e f
in
let m = LblMap.map (fun r -> CngRel.union (leqw w) r r) m
in
assert(true$>(
if not Config.dcc_gie || not Config.check_gie_vs_cc then () else
let some_inconsis = ref false
in
let m0 =
if Config.dcc_gie then
LblMap.map (fun r -> CngRel.union (leqw w) r r) (gie_leaves leqr init f)
else
LblMap.empty
in
LblMap.iter (fun lbl cng1 ->
let cng0 = LblMap.find lbl m0 in
if CngRel.is_empty cng0 then
some_inconsis := true
else
let missed_eqs cng0 cng1 =
CngRel.fold (fun e' e eqs ->
try
if not (CngRel.mem_carrier e' cng1) || not (CngRel.mem_carrier e' cng1)
|| (Exp.sort_of e' <> Exp.sort_of e)
|| (CngRel.mem cng1 e' e)
then eqs
else Exp.mkEq e' e :: eqs
with exn ->
L.printf 0 "comparing get_implied_equalities vs congruence closure:@ @[%a@ %a@]"
CngRel.fmt cng0 CngRel.fmt cng1 ;
L.printf 0 "%a = %a" Exp.fmt e' Exp.fmt e ;
raise exn
) cng0 [] in
let cc_missed = missed_eqs cng0 cng1 in
let ie_missed = missed_eqs cng1 cng0 in
if cc_missed <> [] || ie_missed <> [] then
L.printf 0 "comparing get_implied_equalities vs congruence closure:@ @[%a@ %a@]"
CngRel.fmt cng0 CngRel.fmt cng1 ;
if cc_missed <> [] then L.printf 0 "cc missed: @[%a@]" (List.fmt ";@ " Exp.fmt) cc_missed ;
if ie_missed <> [] then L.printf 0 "ie missed: @[%a@]" (List.fmt ";@ " Exp.fmt) ie_missed ;
assert( cc_missed = [] && ie_missed = [] );
) m ;
));
(* Compute congruence relations for internal labels from those for leaves *)
let m =
Frm.fold_sp
(fun f lbls ->
(* L.printf 0 "dn: %a" Frm.fmt f ; *)
Frm.lbl f :: lbls)
(fun f lbls m ->
(* L.printf 0 "up: @[[@[%a@]]@ %a@]" (List.fmt ";@ " Format.pp_print_int) lbls Frm.fmt f ; *)
match lbls with
| _ :: l_parent :: _ ->
let l = Frm.lbl f in
let m_l = LblMap.find l m in
let r' =
try
let m_p = LblMap.find l_parent m in
(* Choose reps from deeper labels if possible *)
let reps = Exps.union (CngRel.representatives m_l) (CngRel.representatives m_p) in
let leq e f = (Exps.mem e reps) || not (Exps.mem f reps) in
CngRel.inter leq m_l m_p
with Not_found ->
m_l
in
(* L.printf 0 "add: %i" l_parent ; *)
(* L.printf 0 "add: %i@ %a" l_parent CngRel.fmt r' ; *)
LblMap.add l_parent r' m
| _ ->
m
)
f [] m
in
(* Compute set of exps that are representatives in some relation *)
let reps =
LblMap.fold (fun _ r reps ->
Exps.union (CngRel.representatives r) reps
) m Exps.empty
in
(* Compute trimmed relation for each label *)
LblMap.fold (fun l r m ->
LblMap.add l (r, CngRel.restrict r reps) m
) m LblMap.empty
end

88
src/DisjTransClos.ml Normal file
Просмотреть файл

@ -0,0 +1,88 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Disjunctive Transitive Closure *)
open Library
open FORMULA
open CONGRUENCE_RELATION
open TRANSITIVE_RELATION
module LblMap = IntMap
module Make
(Frm: FORMULA)
(CngRel: CONGRUENCE_RELATION
with type exp = Frm.Exp.t
and type exps = Frm.Exps.t )
(DisjCngClos: sig
val dcc : Frm.t -> (CngRel.t * CngRel.t) LblMap.t end)
(TransRel: TRANSITIVE_RELATION
with type exp = Frm.Exp.t
and type exps = Frm.Exps.t ) :
sig
val dtc : Frm.t -> TransRel.t LblMap.t
end = struct
module Exp = Frm.Exp
module Exps = Frm.Exps
module ExpMap = Frm.ExpMap
let dtc f =
(* Compute disjunctive congruence closure *)
let cm = DisjCngClos.dcc f
in
(* Compute strongest transitive and trimmed congruence closed relation forced by each leaf branch *)
let m =
Frm.fold_dnf
(fun f ((cube,t),m) ->
let l = Frm.lbl f in
let _, r_trim = LblMap.find l cm in
let t =
Frm.fold_rels
(fun es ->
TransRel.add_scc
(List.fold (fun e scc ->
Exps.union (CngRel.class_of r_trim e) scc
) es Exps.empty)
)
(fun e f t ->
t
|> TransRel.add_scc (CngRel.class_of r_trim e)
|> TransRel.add_scc (CngRel.class_of r_trim f)
|> TransRel.add (e,f)
)
f t in
((f::cube, t), m)
)
(fun ((cube,t),m) ->
(* For all the clauses that mention a particular sub formula,
calculate the intersection of the relations *)
List.fold (fun f m ->
if not (Frm.is_leaf f) then
m
else
LblMap.modify_add (fun t0 -> TransRel.inter t0 t) (Frm.lbl f) t m
) cube m
)
f ([], TransRel.empty) LblMap.empty
in
(* Compute relations for internal labels from those for leaves *)
Frm.fold_sp
(fun f lbls -> Frm.lbl f :: lbls)
(fun f lbls m ->
match lbls with
| _ :: l_parent :: _ ->
let l = Frm.lbl f in
let t = LblMap.find l m in
LblMap.modify_add (fun t0 -> TransRel.inter t t0) l_parent t m
| _ ->
m
)
f [] m
end

1096
src/Expression.ml Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

290
src/Expression.mli Normal file
Просмотреть файл

@ -0,0 +1,290 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Expressions *)
open Library
open Type
open Variable
val stats : unit -> int * int * int * int * int * int
(*============================================================================
Exp
============================================================================*)
module Exp : sig
type op1 = Allocd | Not | ZMin
type op2 = ZLt | ZLe | ZGt | ZGe | ZDiv | ZRem | ZMod
type op3 = Ite
type opN = Distinct | And | Or | ZAdd | ZMul | UFun of string
type t = t_desc HashCons.hc
and t_desc = private
| Var of Var.t
(* Pointer expressions *)
| App of t * t (** Application of pointer expressions *)
| Nil (** Constant symbol for NULL pointer *)
| Add of Fld.t (** Function symbol for adding field to pointer *)
| Sub of Fld.t (** Function symbol for subtracting field from pointer *)
| Idx (** Function symbol for indexing into an array *)
(* Offset expressions *)
| Bas of Typ.t (** Constant symbol for empty offset (from pointer of type) *)
(* Integer, Boolean and String expressions *)
| Eq of t * t
| Num of int64
| Str of string
| Op1 of op1 * t_desc
| Op2 of op2 * t_desc * t_desc
| Op3 of op3 * t_desc * t_desc * t_desc
| OpN of opN * t_desc array
val desc : t -> t_desc
(** [desc e] is the descriptor named by expression [e] *)
val name : t_desc -> t
(** [name d] is the unique expression name of descriptor [d] *)
(* Sort operations ======================================================== *)
val sort_of : t -> Var.sort
val is_pointer : t -> bool
val is_integer : t -> bool
val is_boolean : t -> bool
val is_offset : t -> bool
(* Constructors =========================================================== *)
exception IllSorted of t_desc
val mkVar : Var.t -> t
(* Pointer expressions *)
val mkApp : t -> t -> t
(** [mkApp f a] constructs an expression equivalent to [App(f,a)] *)
val getApp : t -> (t * t) option
(** [getApp (App(f,a))] is [Some(f,a)], and [None] in other cases *)
val nil : t
val mkAdd : t -> Fld.t -> t
val mkSub : t -> Fld.t -> t
val mkAdds : t -> Fld.t list -> t
(** [mkAdds e [fN; ...; f2; f1]] is [e+f1+f2+...+fN] *)
val mkSubs : t -> Fld.t list -> t
(** [mkSubs e [fN; ...; f2; f1]] is [e-fN-...-f2-f1] *)
val invert : t -> t option
(** [invert] transforms [Add(o)] to [Sub(o)] and vice versa, and returns
[None] in other cases *)
val mkIdx : t -> t -> t
(** [mkIdx a i] creates an expression denoting the address of element [i] of array [a] *)
(* Offset expressions *)
val mkBas : Typ.t -> t
(* Integer expressions *)
val zero : t
val one : t
val mkNum : int64 -> t
val mkStr : string -> t
val mkZMin : t -> t
val mkZDiv : t -> t -> t
val mkZRem : t -> t -> t
val mkZMod : t -> t -> t
val mkZAdd : t array -> t
val mkZSub : t array -> t
val mkZMul : t array -> t
val mkUFun : string -> t array -> t
(* Boolean expressions *)
val tt : t
val ff : t
val mkNot : t -> t
val mkAnd : t array -> t
val mkOr : t array -> t
val mkImp : t -> t -> t
val mkIff : t -> t -> t
val mkXor : t -> t -> t
val mkEq : t -> t -> t
val mkDq : t -> t -> t
val mkDistinct : t array -> t
val mkAllocd : t -> t
val mkZLt : t -> t -> t
val mkZLe : t -> t -> t
val mkZGt : t -> t -> t
val mkZGe : t -> t -> t
(* Generic expressions *)
val mkIte : t -> t -> t -> t
(* Conversions *)
val convert : Var.sort -> t -> t option
val mkOp1 : op1 -> t_desc -> t
val mkOp2 : op2 -> t_desc -> t_desc -> t
val mkOp3 : op3 -> t_desc -> t_desc -> t_desc -> t
val mkOpN : opN -> t_desc array -> t
(* Queries ================================================================ *)
(* val size : t -> int *)
(* val is_atomic : t -> bool *)
val fv : t -> Vars.t
(* val diff_inter_diff : t -> t -> t * t * t *)
(* val partition : (t -> bool) -> t -> t * t *)
val remove : (t_desc -> bool) -> t -> t
(* Maps and Folds ========================================================= *)
(** [kmap_fold before after e sa0 la0] simultaneously maps and folds overa
the structure of [e], in continuation-passing style.
Each subexpression [x] of [e] is passed to [before] prior to traversing
its children.
Calling the continuation [k] traverses [x]'s children in right-to-left
order. When calling [k] the structural accumulator [sa] and linear
accumulator [la] can be updated. After traversing [x]'s children, the
expression [x'] returned by [before] is passed to [after].
The structural accumulator [sa] is accumulated over the structure of the
expression, that is, it has been updated by [before] for each
subexpression of [e] that is a proper superexpression of [x]. The
linear accumulator [la] is accumulated over all subexpressions, that is,
it has been updated by [before] and passed to [after] for each
subexpression earlier than [x] in the right-to-left traversal.
*)
(* val kmap_fold : *)
(* (('sa->'la-> 'a) -> t -> 'sa->'la-> t * 'la) -> *)
(* (t -> 'sa -> 'la-> 'a) -> *)
(* t -> 'sa -> 'la-> t * 'la *)
(** [kfold] is like [kmap_fold] but does not transform, only accumulates. *)
(* val kfold : *)
(* (('sa->'la-> 'a) -> t -> 'sa->'la-> 'la) -> *)
(* (t -> 'sa->'la-> 'a) -> *)
(* t -> 'sa->'la-> 'la *)
(** [kmap] is like [kmap_fold] but does not accumulate, only transforms. *)
(* val kmap : ((unit -> t) -> t -> t) -> t -> t *)
(** [pmap_fold fn e z] performs a pre-order traversal of [e] which descends
into each subexpression [d] of [e] only if [fn d z] returns [None]. *)
(* val pmap_fold : (t -> 'a -> (t * 'a) option) -> t -> 'a -> t * 'a *)
(** [pfold] is like [pmap_fold] but does not transform, only accumulates. *)
(* val pfold : (t -> 'a -> 'a option) -> t -> 'a -> 'a *)
(** [pmap] is like [pmap_fold] but does not accumulate, only transforms. *)
val pmap : (t -> t option) -> t -> t
(** [map_fold fn e z] performs a standard post-order traversal of [e]. *)
(* val map_fold : (t -> 'a -> t * 'a) -> t -> 'a -> t * 'a *)
(** [fold] is like [map_fold] but does not transform, only accumulates. *)
val fold : (t -> 'a -> 'a) -> t -> 'a -> 'a
val fold_sp : (t -> 's -> 's) -> (t -> 's -> 'p -> 'p) -> t -> 's -> 'p -> 'p
(** [map] is like [map_fold] but does not accumulate, only transforms. *)
val map : (t -> t) -> t -> t
(** the [*_unord] operations are similar, but use an extended definition of
subexpression, where the subexpressions of an offset include every
unordered subset of the component fields. *)
(* val pmap_unord : (t -> t option) -> t -> t *)
(* val fold_unord : (t -> 'a -> 'a) -> t -> 'a -> 'a *)
val equal : t -> t -> bool
val compare : t -> t -> int
val hash : t -> int
val fmt : t formatter
val fmtp : Var.fxt -> t formatter
val fmt_caml : t formatter
end
(*============================================================================
Off
============================================================================*)
module Off : sig
type t = private Exp.t
type desc = Var of Var.t | Path of Typ.t * Fld.t list
val mk : Exp.t -> t
(** [mk] constructs an offset from an expression representing an offset.
Raises [Assert_failure] if argument does not satisfy [Exp.is_offset]. *)
val mkVar : Var.t -> t
(** [mkVar] constructs a variable offset. *)
val mkPath : Typ.t -> Fld.t list -> t
(** [mkPath] constructs a literal offset from an access path
[\[fN; ...; f2; f1\]] through type [ty] where each [fI] is a
member of the type containing [fI-1]. *)
val desc : t -> desc
(** [desc] returns the represented variable or access path *)
val fv : t -> Vars.t
val is_base : t -> Typ.t option
(** [is_base o] returns the type [o] is an identity offset for, or [None] if [o] is not an identity
offset. *)
val equal : t -> t -> bool
val compare : t -> t -> int
val fmt : t formatter
val fmtp : Var.fxt -> t formatter
val fmt_caml : t formatter
end
(*============================================================================
Collections
============================================================================*)
module Exps : sig
include Set.S with type elt = Exp.t
val fv : t -> Vars.t
(* val pfold : (Exp.t -> 'a -> 'a option) -> t -> 'a -> 'a *)
val fmt_sep : (unit,Format.formatter,unit)format -> t formatter
val fmt : t formatter
end
module Expss : (Set.S with type elt = Exps.t)
module ExpMap : (Map.S with type key = Exp.t)
module ExpMMap : (MultiMap.S with type k = Exp.t
and type v = Exp.t
and type vs = Exps.t)
module ExpHMap : (HashMap.S with type key = Exp.t)

36
src/FLD.ml Normal file
Просмотреть файл

@ -0,0 +1,36 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open Library
module type FLD = sig
type typ
type t
val off : t -> int * int option
val id : t -> int
val name : t -> string
val typ : t -> typ
val is_first : t -> bool
val compare : t -> t -> int
val equal : t -> t -> bool
val hash : t -> int
val mk : int * int option -> string -> t
(** [mk offset name] creates a fresh field for [offset] and name based on [name]. *)
val fmt : t formatter
val fmt_caml : t formatter
val marshal : out_channel -> unit
val unmarshal : in_channel -> unit
val unsafe_create : int -> int * int option -> string -> typ -> t
val find_by_name : typ -> string -> (t * typ) option
(** [find_by_name ty name] returns the member of [ty] named [name]. *)
end

65
src/FORMULA.ml Normal file
Просмотреть файл

@ -0,0 +1,65 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Signatures for formulas used by congruence and transitive closure *)
open Library
module type EXP = sig
include OrderedType
val mkApp : t -> t -> t
(** [mkApp f a] constructs [App(f,a)] *)
val getApp : t -> (t * t) option
(** [getApp (App(f,a))] is [Some(f,a)], and [None] in other cases *)
val invert : t -> t option
(** [invert] transforms [Add(o)] to [Sub(o)] and vice versa, and returns
[None] in other cases *)
val fmt : t formatter
end
module type FORMULA = sig
module Exp: (EXP with type t = Expression.Exp.t)
module Exps: Set.S with type elt = Exp.t and type t = Expression.Exps.t
module ExpMap: Map.S with type key = Exp.t and type 'a t = 'a Expression.ExpMap.t
(* module Exp: EXP *)
(* module Exps: Set.S with type elt = Exp.t *)
(* module ExpMap: Map.S with type key = Exp.t *)
type t
val lbl : t -> int
(** [lbl f] is the label of the root of formula [f] *)
val is_leaf : t -> bool
val fold_rels : (Exp.t list -> 'z -> 'z) -> (Exp.t -> Exp.t -> 'z -> 'z) -> t -> 'z -> 'z
(** [fold_rels] folds over related pairs of expressions, first
function takes a strongly connected component, the second
individual edges. *)
val fold_nrels : (Exp.t -> Exp.t -> 'z -> 'z) -> t -> 'z -> 'z
(** [fold_nrels] folds over provably unrelated pairs of expressions. *)
val fold_sp : (t -> 's -> 's) -> (t -> 's -> 'p -> 'p) -> t -> 's -> 'p -> 'p
val fold_dnf : ?dnf:bool -> (t -> 'c*'d -> 'c*'d) -> ('c*'d -> 'd) -> t -> 'c->'d -> 'd
(** The disjunctive traversal of a formula, [fold_dnf m r f c d], is a generalization of incremental
conversion to disjunctive-normal form that accumulates an abstract conjunction [m] over each cube, and
accumulates an abstract disjunction [r] over the clauses. For example:
[fold_dnf m r (f1 * ((f2 * f3) \/ f4)) c d =
let c,d = m f1 (c,d) in r (m f4 (c, r (m (f2 * f3) (c,d))))]
*)
val labeled_pure_consequences : t -> Exp.t * Exp.t IntMap.t
val fmt : t formatter
end

70
src/Frame.ml Normal file
Просмотреть файл

@ -0,0 +1,70 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Heuristic for localization of analysis of procedure calls *)
(*
Calculate footprint and frame for [{ call } proc(actuals)].
1. Let RG(p) = set of globals "relevant" to proc. A global is relevant to
proc if it appears in proc's body or in the body of any procedure that is in
the static call-graph beginning at proc.
2. Then the principal *-conjunction of call is split into footprint * frame
such that frame includes every spatial subformula that contains a may-alloc
which is not reachable from RG(p) \cup actuals.
*)
(**/**)
open Library
open Variable
open Expression
module E = Exp
open Program
open SymbolicHeap
(**/**)
module L = (val Log.std Config.vFrame : Log.LOG)
let frame_tmr = Timer.create "Frame.footprint"
(*============================================================================
Entry points.
============================================================================*)
let footprint call callee actuals =
Timer.start frame_tmr; assert(true$>(
L.incf 1 "( footprint:@ %a@ %a(%a)" XSH.fmt call Proc.Id.fmt callee.Proc.id (List.fmt ",@ " Var.fmt) actuals ));
(fun (foot,frame) ->
Timer.stop frame_tmr ; assert(true$>(
L.decf 1 ") footprint:@[%a@],@ frame:@[%a@]" XSH.fmt foot XSH.fmt frame )))
<&
let exps_of_vars vv =
Vars.fold (fun v ee -> Exps.add (E.mkVar v) ee) vv Exps.empty
in
let actuals = List.fold (fun a actls -> Exps.add (Exp.mkVar a) actls) actuals Exps.empty
in
let xs, call = XSH.exists_bind (Vars.union (XSH.fv call) (Exps.fv actuals)) call
in
let callee_globals = exps_of_vars callee.Proc.accessed
in
let roots = Exps.union callee_globals actuals
in
let is_reachable = Reachability.is_reachable (fun e -> Exps.mem e roots) call call
in
(* frame away any spatial subformula that includes an unreachable may-alloc *)
let footprint, frame =
SH.partition (function
| SH.Pt({Pt.loc}) ->
is_reachable loc
| SH.Ls(ls) ->
(* Format.printf "List %a reachable?@\n" Ls.fmt ls; *)
let res = List.exists is_reachable (Ls.may_allocs ls) in
(* Format.printf "List %a reachable %b@\n" Ls.fmt ls res; *)
res
| SH.Dj(dj) ->
Dj.for_all (fun dt -> Exps.exists is_reachable (SH.may_allocs dt)) dj
) call
in
(SH.exists_intro xs footprint, SH.exists_intro xs frame)

18
src/Frame.mli Normal file
Просмотреть файл

@ -0,0 +1,18 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Heuristic for localization of analysis of procedure calls *)
open Variable
open Program
open SymbolicHeap
(** For a procedure call [{ call } proc(actuals)],
[footprint call proc actuals] is a pair ([footprint],[frame]) such that
1. [footprint] is a sub-heap of [call] that over-approximates the
footprint of the call; and
2. ([footprint] * [frame]) = [call].
*)
val footprint : XSH.t -> Proc.t -> Var.t list -> XSH.t * XSH.t
val frame_tmr : Timer.t

45
src/Frontend.ml Normal file
Просмотреть файл

@ -0,0 +1,45 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open Library
open Program
module L = (val Log.std Config.vFE : Log.LOG)
let _ = try (
(* turn on backtrace printing in debug mode *)
assert(true$> Printexc.record_backtrace true );
(* Set a handler for Ctrl-C. *)
Sys.catch_break true ;
L.printf 1 "%s@." (String.concat " " (Array.to_list Sys.argv)) ;
Pervasives.flush_all () ;
if Config.version_only then (
Printf.printf "SLAyer frontend %s" Version.version ;
exit 0
) else (
let program = Frontend_esp.program_of_file Config.filenames in
if Config.write_cfg then Prog.write_dot Config.testname ".fe.cfg.dot" program ;
let program = if Config.norm_in_frontend then TransformProgram.normalize program else program in
Library.with_out_bin (Config.testname^".sil") Prog.marshal program
)
) with exc ->
if Config.raise_exceptions then (
prerr_endline (Printexc.to_string exc) ;
raise exc
) else (
print_endline ("\nRESULT: Internal Error: "^(Printexc.to_string exc)) ;
flush_all () ;
exit 1
)

1018
src/Graph.ml Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

35
src/Graph.mli Normal file
Просмотреть файл

@ -0,0 +1,35 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Mutable edge- and vertex-labelled multi-graphs *)
open Library
module type GRAPH = Graph_sig.GRAPH
module Make
(Index: sig
type t
val compare: t -> t -> int
val equal: t -> t -> bool
val hash: t -> int
val fmt : t formatter
end)
(VertexLabel: sig
type t
val compare: t -> t -> int
val equal: t -> t -> bool
val fmt : t formatter
end)
(EdgeLabel: sig
type t
val compare: t -> t -> int
val equal : t -> t -> bool
val fmt : t formatter
end)
:
(GRAPH
with type index = Index.t
and type v_label = VertexLabel.t
and type e_label = EdgeLabel.t
)

191
src/Graph_sig.ml Normal file
Просмотреть файл

@ -0,0 +1,191 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open Library
(** Signature for Graph *)
module type GRAPH = sig
type index
type v_label
type e_label
type graph
module Vertex : sig
type t
val compare : t -> t -> int
val equal : t -> t -> bool
val hash : t -> int
val fmt : t formatter
end
type vertex = Vertex.t
(** sets of vertices *)
module VertexISet : (ImperativeSet.S with type v = vertex)
module VertexSet : (Set.S with type elt = vertex)
(** multi-maps from vertices (to edge labels) *)
module VertexIMap : (ImperativeMap.S with type key = vertex)
module VertexMap : (Map.S with type key = vertex)
val create : unit -> graph
val clear : graph -> unit
val index_of : vertex -> index
val label_of : vertex -> v_label
val in_degree : vertex -> int
val out_degree : vertex -> int
val iter_preds : (vertex -> e_label -> unit) -> vertex -> unit
val iter_succs : (vertex -> e_label -> unit) -> vertex -> unit
val fold_preds : (vertex -> e_label -> 'a -> 'a) -> vertex -> 'a -> 'a
val fold_succs : (vertex -> e_label -> 'a -> 'a) -> vertex -> 'a -> 'a
val predecessors : vertex -> (vertex * e_label) list
val successors : vertex -> (vertex * e_label) list
val vertices_for : graph -> index -> vertex list
val roots : graph -> vertex list
(** [mem_vertex g v] tests if [g] contains vertex [v] *)
val mem_vertex : graph -> vertex -> bool
(** [add_vertex g (k,l)] adds and returns a vertex for [(k,l)] to [g], unless
there already is one, in which case the existing vertex is returned *)
val add_vertex : graph -> index * v_label -> vertex
(** [remove_vertex g v] removes [v] (which must exist) if it is
disconnected, and any other non-rooted vertices that thereby get
disconnected *)
val remove_vertex : graph -> vertex -> unit
(** [replace_vertex g new_lab old_trg new_trg] swing all incoming edges to
[old_trg] to [new_trg] *)
val replace_vertex : graph -> (e_label -> e_label) -> vertex -> vertex -> unit
(** [relabel_vertex g v l] changes the label of [v] to [l], removing [v] from [g] and returning the new
vertex, PROVIDED the old and new labels compare equal according to the [VertexLabel.equal] function
passed to [Graph.Make]. *)
val relabel_vertex : graph -> vertex -> v_label -> vertex
(** [root_vertex g v] marks [v] as "rooted" so that it will not be removed
if it becomes disconnected *)
val root_vertex : graph -> vertex -> unit
(** [unroot_vertex g v] unmarks [v] as "rooted" so that it will be removed
if it becomes disconnected *)
val unroot_vertex : graph -> vertex -> unit
(** [remove_unreachable g] removes all vertices not reachable from a root. *)
val remove_unreachable : graph -> unit
(** [iter_vertices_index fn g k] applies [fn] to every vertex of [g] with
index [k] *)
val iter_vertices_index : (vertex -> unit) -> graph -> index -> unit
(** [iter_vertices fn g] applies [fn] to every vertex of [g] *)
val iter_vertices : (vertex -> unit) -> graph -> unit
(** [fold_vertices_index fn g k] folds [fn] over every vertex of [g] with index [k] *)
val fold_vertices_index : (vertex -> 'a -> 'a) -> graph -> index -> 'a -> 'a
(** [fold_vertices fn g acc] folds [fn] over every vertex of [g] *)
val fold_vertices : (vertex -> 'a -> 'a) -> graph -> 'a -> 'a
(** [mem_edge g src label trg] tests if [g] contains an edge from [src] to
[trg] labeled [label] *)
val mem_edge : graph -> vertex -> e_label -> vertex -> bool
(** [add_edge g src label trg] adds an edge from [src] to [trg] with label
[label] *)
val add_edge : graph -> vertex -> e_label -> vertex -> unit
(** [remove_edge g src label trg] removes the edge (which must exist) from
[src] to [trg] labeled [label] *)
val remove_edge : graph -> vertex -> e_label -> vertex -> unit
(** [collapse_edge_pre g src label trg] swings all outgoing edges of [trg] to [src] *)
val collapse_edge_pre : graph -> vertex -> e_label -> vertex -> unit
(** [collapse_edge_post g src label trg] swings all incoming edges to [src] to [trg] *)
val collapse_edge_post : graph -> vertex -> e_label -> vertex -> unit
(** [iter_edges vertex_fn edge_fn g root] applies [vertex_fn] to every
vertex reachable from [root] in [g], and [edge_fn] to every edge of [g]
between those vertices *)
val iter_edges : (vertex->unit) -> (vertex*e_label*vertex->unit) -> graph -> index -> unit
(** [fold_edges vertex_fn edge_fn g root z] accumulates [vertex_fn] over every vertex reachable from [root]
in [g], and [edge_fn] over every edge of [g] between those vertices. *)
val fold_edges : (vertex -> 'z -> 'z) -> (vertex * e_label * vertex -> 'z -> 'z) -> graph -> index -> 'z -> 'z
(** [identify_vertices g] returns a map that associate a unique integer to
each vertex of [g] *)
val identify_vertices : graph -> int VertexIMap.t
(** [cutpoints v] returns a set of cutpoints of the graph reachable from [v]. *)
val cutpoints : vertex -> VertexSet.t
(** [bfs g s] does a breadth-first search of [g] from [s]. Returns the
distance and predecessor results. *)
val bfs : graph -> vertex -> (int VertexIMap.t * ((vertex*e_label) option VertexIMap.t))
(** [dfs_iter next forwards pre post vs] traverses depth-first from
each elemtent of [vs], presenting vertices to [pre] in preorder,
and to [post] in postorder. It calls [next] each time it moves
to the next entry in [vs]. If [forwards]=true then it walks
forwards, otherwise it walks backwards in the graph. By default
[forwards] is true, and [next] does nothing.*)
val dfs_iter : ?next:(unit->unit) -> ?forwards:bool -> (vertex -> unit) -> (vertex -> unit) -> vertex list -> unit
(** [dfs v] returns a list of vertices reachable from [v] in preorder, a list of vertices in postorder, and
functions mapping vertices to their position in the preorder and in the postorder. *)
val dfs : vertex -> vertex list * vertex list * (vertex -> int) * (vertex -> int)
(** [scc g] returns a map, that maps each vertex to the list of
vertices in its strongly connected component. *)
val scc : graph -> (vertex list) VertexMap.t
(** [dominance_frontier g s] gives the dfset and dominator tree of [g]
[dom n m] is the dominator relationship
[parent_of n] is the immediate dominator of n
[children_of n] is the set of vertices that are dominated by n *)
val dominance_frontier :
graph -> vertex -> (VertexISet.t VertexIMap.t) * (* dfset *)
(vertex -> vertex -> bool) * (* dom *)
(vertex -> vertex option) * (* parent_of *)
(vertex -> VertexISet.t) (* children_of *)
(** [natural_loops g dom] returns a list of loops appearing in [g],
each loop is given as a set of vertices in the loop's body *)
val natural_loops : graph -> (vertex -> vertex -> bool) -> (VertexISet.t * vertex * vertex) list
(** Floyd-Warshall returns distance and predecessor matrices.*)
val fw : graph ->
IntHMap.key VertexIMap.t * VertexIMap.key IntHMap.t *
(int option) array array * (int option) array array
val copy : graph -> vertex -> vertex VertexMap.t * graph
val slice : vertex -> vertex -> graph -> vertex VertexMap.t * graph
(** [write_dot g t root] outputs the subgraph of [g] reachable from [root] in
dot format with tree edges given in [t] *)
val cfg_write_dot : graph -> (Vertex.t -> VertexISet.t) -> index -> Buffer.t -> unit
(** [write_dot g root] outputs the subgraph of [g] reachable from [root] in
dot format *)
val write_dot : graph -> index -> Buffer.t -> unit
(** [write_dot_partitioned fn g root] outputs the subgraph of [g] reachable
from [root] in dot format, where vertices are partitioned into
subgraphs according to [fn] *)
val write_dot_partitioned :
(index -> (string * (Format.formatter->'a)) option) ->
graph -> index list -> Buffer.t -> unit
end

40
src/HashCons.ml Normal file
Просмотреть файл

@ -0,0 +1,40 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Hash-consing construction based on weak hash tables *)
open Library
let verbose = ref 0
module L = (val Log.std verbose : Log.LOG)
type 'a hc = {
desc : 'a;
id : int;
hash : int;
}
module Make (H : HashedType) = struct
include Weak.Make (struct
type t = H.t hc
let equal x y = H.equal x.desc y.desc
let hash x = x.hash
end)
let id = ref 0
let intern t desc =
assert( !id < max_int );
let data = {desc; id= !id; hash= H.hash desc} in
(* Note: If desc is already interned, this record construction is
unnecessary. If we copied the implementation of Weak into this module,
we could avoid constructing the record just to search for it. *)
let data' = merge t data in
if data == data' (* data just added *) then incr id ;
assert( data == data' || data.id <> data'.id );
data'
end

33
src/HashCons.mli Normal file
Просмотреть файл

@ -0,0 +1,33 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Hash-consing construction based on weak hash tables *)
open Library
type 'a hc = private {
desc : 'a;
id : int;
hash : int;
}
module Make (H : sig include HashedType val fmt: t formatter end) : sig
type t
val create : int -> t
val intern : t -> H.t -> H.t hc
val find : t -> H.t hc -> H.t hc
val find_all : t -> H.t hc -> H.t hc list
val iter : (H.t hc -> unit) -> t -> unit
val fold : (H.t hc -> 'z -> 'z) -> t -> 'z -> 'z
val stats : t -> int * int * int * int * int * int
end

291
src/HeapAbstraction.ml Normal file
Просмотреть файл

@ -0,0 +1,291 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Abstraction of the heap part of symbolic heaps *)
(* Notes:
- The definition of Pg.of_pattern only works for patterns where only a
single points-to predicate is needed to follow the list spine. For
instance, the pattern for lists of even length using two points-tos in
the body will not work.
*)
open Library
open Variable
open Expression
module E = Exp
module S = Substitution
open SymbolicHeap
module L = (val Log.std Config.vAbsH : Log.LOG)
module LAbs = (val Log.std Config.vAbs : Log.LOG)
(* Support ================================================================== *)
(* let smaller_by len q x = (SH.sizeof q) - (XSH.sizeof x) >= len *)
(*============================================================================
Pattern Graphs
============================================================================*)
module PatternGraph = struct
include HeapGraph
(* the unit of meet on pattern graphs *)
let top pat =
singleton (Edge.map (const None) pat.Patn.params)
exception Meet_None
let meet sh pat x y =
fold_product (fun x_edg y_edg i ->
try
let xy_edg = Edge.meet x_edg y_edg in
try
let none () = raise Meet_None in
let arg = Edge.map (Option.optionk none id) xy_edg in
let _,body = XSH.exists_bind Vars.empty (Patn.instantiate pat arg) in
let bex = E.map (SH.Pf.normalize sh) (SH.pure_sf body) in
(* only add edge if pure subformula of pat simplifies to true *)
if E.equal E.tt bex then
add xy_edg i
else
i
with Meet_None ->
add xy_edg i
with Undef ->
i
) x y empty
(* let remove_path _len pat edg q = *)
(* let ls = Edge.to_ls pat edg in *)
(* let xs = Vars.diff (SH.fv ls) (SH.fv q) in *)
(* (* remainder from subtract should have at least len fewer *-conjuncts *) *)
(* (* Note: investigate dropping the proviso *) *)
(* match Prover.subtract(* _with_proviso (smaller_by len q) *) q xs ls with *)
(* | Prover.Success(q',_) -> Some(xs, q') *)
(* | Prover.Failure -> None *)
(** [of_pattern pat fg q] is the pattern graph corresponding to a template
of the pattern [pat] filled in with the field graph [fg]. It is the
meet of pattern graphs for each points-to record in [pat] *)
let of_pattern pat sh =
(* L.incf 6 "( Pg.of_pattern: %a" Patn.fmt pat ; *)
(* L.decf 6 ") Pg.of_pattern: %a" fmt *)
(* <& *)
let {Patn.params; body} = pat
in
let bv = Params.fv params
in
let leq e f =
(* L.printf 0 "leq %a %a = %b" E.fmt e E.fmt f <& *)
Vars.intersect bv (E.fv f) || Vars.disjoint bv (E.fv e)
in
(* Note: review ignoring these existentials *)
let _, body = XSH.exists_bind Vars.empty body
in
let sh' = SH.Pf.union leq sh body
(* &> L.printf 0 "sh': %a" SH.fmt *)
in
let params' = Params.map (fun v -> SH.Pf.normalize body (E.mkVar v)) params
in
let pg = top pat
in
let pg =
(* consider every edge x -o-> y in pat body *)
SH.PtS.fold (fun {Pt.loc= x; off= o; cnt= yo} pg ->
(* L.incf 7 "( considering pattern edge : %a" *)
(* Pt.fmt {Pt.loc= x; off= o; cnt= yo} ; *)
(* L.decf 7 ") pg: %a" fmt *)
(* <& *)
let pg' =
(* consider every edge l -o-> e in sh *)
SH.PtS.fold (fun {Pt.loc= l; off= o'; cnt= eo} pg ->
if not (Off.equal o o') then pg
else
let edg =
(* L.incf 7 "( considering sh edge : %a" *)
(* Pt.fmt {Pt.loc= l; off= o'; cnt= eo} ; *)
(* L.decf 7 ") found pattern graph edge: %a" Edge.fmt *)
(* <& *)
let sh' = SH.Pf.merge leq sh' x l in
let sh' =
match yo, eo with
| Some(y), Some(e) -> SH.Pf.merge leq sh' y e
| _ -> sh' in
(* L.printf 0 "sh': %a" SH.fmt sh' ; *)
Edge.map (fun p ->
try
let p' = SH.Pf.normalize sh' p in
(* L.printf 0 "instantiating %a to %a" E.fmt p E.fmt p' ; *)
if Vars.disjoint bv (E.fv p') then Some(p') else None
with Not_found -> None
) params'
in
add edg pg
) sh empty
in
(* L.printf 8 "pg of pat edge: %a" fmt pg' ; *)
if is_empty pg' then pg
else
meet sh pat pg pg'
) body pg
in
(* L.printf 6 "pre-filtered pattern graph:@ %a" fmt pg; *)
(* let pg = filter (fun edg -> None <> remove_path 1 pat edg sh) pg *)
(* in *)
(* add edges for existing lists of the current pattern *)
SH.LsS.fold (fun {Ls.pat= pat'; arg} pg ->
if not (Patn.equal pat pat') then pg
else
let edg = Args.map Option.some arg in
add edg pg
) sh pg
end
module Pg = PatternGraph
(*============================================================================
Generalization
============================================================================*)
let append_segments should_append pat (xs,sh) edg edg' =
assert(true$>(
if Pg.Edge.append edg edg' <> None then
L.incf 5 "( append_segments: @[%a@ %a@]@ %a"
Pg.Edge.fmt edg Pg.Edge.fmt edg' SH.fmt_xs (xs,sh) ));
(fun a -> assert(true$>(
if Pg.Edge.append edg edg' <> None then
L.decf 5 ") append_segments:@ %a" (Option.fmt "failed" (fun ff (_,_,_,xs,sh) ->
SH.fmt_xs ff (xs,sh))) a )))
<&
(* check that edges are adjacent if append should be done *)
match Pg.Edge.append edg edg' with
| Some(cat) when should_append edg edg' sh ->
(* subtract a matching path from the SH *)
(* Note: Refactor: below is almost an inlining of Pg.remove_path. *)
let ls = Pg.Edge.to_ls pat cat in
let ys = Vars.diff (SH.fv ls) (SH.fv sh) in
(* Note: investigate dropping the proviso *)
(match Prover.subtract(* _with_proviso (smaller_by 2 sh) *) sh ys ls with
| Prover.Unknown -> None
| Prover.Success(sh_m_cat,_) ->
(* add a corresponding ls to the SH *)
let xsh' = XSH.star [SH.exists_intro Vars.empty ls] sh_m_cat in
(* Note: Can the pattern graph be incrementally updated here? *)
let zs, sh' = XSH.exists_bind ys xsh' in
if 2 <= !Config.vAbsH then (
let fmt_o, _fmt_i, fmt_n = SH.fmt_did_xs ((xs,sh), (zs,sh')) in
LAbs.printf 2 "@[<hov 2>abs_ls: creating list %a, replace:@ %t@]@ @[<hov 2>with:@ %t@]"
SH.fmt ls fmt_o fmt_n
);
Some(edg, edg', cat, Vars.unions [xs; ys; zs], sh')
)
| _ ->
None
(** [generalize q] attempts to produce a formula [q'] logically weaker than
[q] by appending list-segments. *)
let generalize should_append pat (xs,sh) pgm pgs =
assert(true$>
L.incf 5 "( generalize:@ %a" SH.fmt_xs (xs,sh) );
(fun (sh,(xs,_,_)) -> assert(true$>
L.decf 5 ") generalize:@ %a" SH.fmt_xs (xs,sh)))
<&
(* append edges of pattern graph until no more can be *)
let rec append_edges pg xs' sh' pgs =
match
Pg.take_first_pair (append_segments should_append pat (xs',sh')) pg
with
| Some(edg, edg', cat, xs', sh') ->
(* update the pattern graph *)
let pg' = Pg.add cat (Pg.remove edg (Pg.remove edg' pg)) in
(* assert( Pg.equal pg' (Pg.of_pattern pat sh') || *)
(* (L.warnf "incremental update of pattern graph incorrect"; true )); *)
append_edges pg' xs' sh' true
| None ->
(pg, xs', sh', pgs)
in
(* build initial pattern graph *)
let pg = Pg.of_pattern pat sh
in
(* append edges of stem *)
let pg, xs, sh, pgs = append_edges pg xs sh pgs
in
let pg, xs, sh, pgs =
if SH.DjS.is_empty sh then (pg, xs, sh, pgs)
else
let pg =
(* for each disjunction... *)
SH.DjS.fold (fun dj pg ->
(* compute set of all edges in any disjunct *)
let dt_edges =
Dj.fold (fun dt edgs ->
let pg_dt =
try IntMap.find (SH.lbl dt) pgm with Not_found -> Pg.empty in
Pg.union pg_dt edgs
) dj Pg.empty in
(* add edges common to all disjuncts *)
let pg_dj =
Dj.fold (fun dt pg_dj ->
let pg_dt =
try IntMap.find (SH.lbl dt) pgm with Not_found -> Pg.empty in
(* add 0-length edges of dt_edges based on dt's equalities *)
let pg_dt =
Pg.fold (fun edg pg_dt ->
if
HeapGraph.Edge.fold_links (fun link so_far -> so_far &&
match link with
| (Some(a), Some(d)) ->
E.equal (SH.Pf.normalize dt a) (SH.Pf.normalize dt d)
| _ -> false
) edg true
then
Pg.add edg pg_dt
else
pg_dt
) dt_edges pg_dt in
Pg.meet dt pat pg_dt pg_dj
) dj (Pg.top pat) in
Pg.union pg_dj pg
) sh pg
in
(* append edges of stem plus added edges from disjunctions *)
append_edges pg xs sh pgs
in
(* save updated and transitively closed pattern graph *)
let pgm = IntMap.add (SH.lbl sh) (Pg.transitive_closure pg) pgm in
(sh, (xs, pgm, pgs))
(*============================================================================
Entry Point
============================================================================*)
(** [abstract q] attemps to apply predicate-based generalization and
disjunction factorization to [q], returning [Some q'] if such a [q'] was
obtained, and [None] otherwise. *)
let abstract (xs,sh) =
let should_append = Reachability.should_append (xs,sh) in
let pgm = IntMap.empty in
let sh',(xs',_,pgs) =
(* try each pattern *)
Discovery.fold (fun pat (sh,(xs,pgm,pgs)) ->
let sh, (xs, pgm, pgs) =
SH.map_fold_distrib (fun dt (xs, pgm, pgs) ->
generalize should_append pat (xs,dt) pgm pgs
) sh (xs,pgm,pgs) in
let xs, sh = SH.normalize (xs,sh) in
(sh, (xs, pgm, pgs))
) (SH.exists_intro xs sh) (sh,(xs,pgm,false))
in
if pgs then Some(xs',sh') else None

13
src/HeapAbstraction.mli Normal file
Просмотреть файл

@ -0,0 +1,13 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Abstraction of spatial part of symbolic heaps *)
open Variable
open SymbolicHeap
(*============================================================================
HeapAbstraction
============================================================================*)
val abstract : Vars.t * SH.t -> (Vars.t * SH.t) option

143
src/HeapGraph.ml Normal file
Просмотреть файл

@ -0,0 +1,143 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Graph representation of pointer structure of symbolic heaps *)
open Library
open Variable
open Expression
module E = Exp
module S = Substitution
open SymbolicHeap
module L = (val Log.std Config.vHG : Log.LOG)
(* Timing =================================================================== *)
let add_with_closure_tmr = Timer.create "HeapGraph.add_with_closure"
(*============================================================================
Heap Graphs
============================================================================*)
(** "Lifted" value expressions. *)
module LE = struct
type t = E.t option
let fv eo = Option.option Vars.empty E.fv eo
let map_exps s eo = Option.map s eo
let fold_exps fn eo z = Option.option z (fun e -> E.fold fn e z) eo
let equal = Option.equal E.equal
let compare = Option.compare E.compare
let fmtp fxt ff eo = Option.fmt "-" (E.fmtp fxt) ff eo
let fmt ff eo = fmtp (Vars.empty,Vars.empty) ff eo
let fmt_caml ff eo =
Option.fmt "None" (fun ff -> Format.fprintf ff "Some(%a)" E.fmt_caml) ff eo
end
module Edge = struct
include BiEdge.Make (LE)
let fmt ff e = Format.fprintf ff "@[(%a)@]" fmt e
let meet x y =
(* greatest lower bound in the lattice where Undef < Some < None *)
let meet_ lx ly =
match lx, ly with
| None , None -> None
| None , Some _ -> ly
| Some _, None -> lx
| Some _, Some _ when LE.equal lx ly -> lx
| Some _, Some _ -> raise Undef
in
try map2 meet_ x y
with Invalid_argument _ -> raise Undef
let drop none x = map (Option.optionk none id) x
let append_unchecked = append
let append edg edg' =
let are_adjacent =
try
(* only the common meeting point must be present *)
let btwn = drop (fun () -> raise Undef) (Args.between edg edg') in
Args.fold_links (fun (a,d) so_far -> so_far && E.equal a d) btwn true
with Undef -> false
in
if are_adjacent
then Some (append edg edg')
else None
let to_ls pat edg =
(* L.incf 0 "( to_ls: %a %a" Patn.fmt pat fmt edg ; *)
(* L.decf 0 ") to_ls: %a" SH.fmt <& *)
let none () = E.mkVar (Var.gensym "drop" Var.PointerSort) in
let ls = {Ls.
pat= pat;
len= E.mkVar (Var.gensym "len" Var.IntegerSort);
arg= drop none edg
} in
SH.LsS.star [ls] SH.emp
let fmt ff edg = Format.fprintf ff "@[(%a)@]" fmt edg
end
module EdgeSet = Set.Make(Edge)
let fmt ff pg =
Format.fprintf ff "@[<hov 1>[%a]@]"
(List.fmt ";@ " Edge.fmt) (EdgeSet.to_list pg)
let add_with_closure edg g =
Timer.start add_with_closure_tmr ;
(fun _ -> Timer.stop add_with_closure_tmr)
<&
let hds =
EdgeSet.fold (fun g_edg h ->
match Edge.append g_edg edg with
| None -> h
| Some(g_edg_edg) -> EdgeSet.add g_edg_edg h
) g EdgeSet.empty
in
let tls =
EdgeSet.fold (fun g_edg h ->
match Edge.append edg g_edg with
| None -> h
| Some(edg_g_edg) -> EdgeSet.add edg_g_edg h
) g EdgeSet.empty
in
let spn =
EdgeSet.fold_product (fun hd_edg tl_edg h ->
(* Note: It shouldn't be necessary to call append_unchecked here,
something is wrong with Args.adjacent and normalization. *)
EdgeSet.add (Edge.append_unchecked hd_edg tl_edg) h
) hds tls EdgeSet.empty
in
EdgeSet.union spn (EdgeSet.union tls (EdgeSet.union hds (EdgeSet.add edg g)))
(** [union_with_closure g g'] is the transitive closure of [g] union [g'],
assuming [g'] is closed. *)
let union_with_closure g g' = EdgeSet.fold add_with_closure g g'
let transitive_closure g = union_with_closure g EdgeSet.empty
include EdgeSet

35
src/HeapGraph.mli Normal file
Просмотреть файл

@ -0,0 +1,35 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Graph representation of heap structure of symbolic heaps *)
open Library
open Expression
open SYMBOLIC_HEAP
open SymbolicHeap
module Edge : sig
include BIEDGE
with type a = Exp.t option
val meet : t -> t -> t
val append : t -> t -> t option
val to_ls : Patn.t -> t -> SH.t
end
include Set.S with type elt = Edge.t
val fmt : t formatter
val add_with_closure : Edge.t -> t -> t
val union_with_closure : t -> t -> t
val transitive_closure : t -> t
val add_with_closure_tmr : Timer.t

24
src/Hooks.ml Normal file
Просмотреть файл

@ -0,0 +1,24 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Hooks for very applicaton-specific behavior. *)
open Library
let var_name name =
if !Config.vVar > 2 then
name
else
match name with
| "arg_tmp" -> "at"
| "cast_tmp" -> "ct"
| "incr_load_tmp" -> "ilt"
| "load_tmp" -> "lt"
| "lval_cast_tmp" -> "lct"
| "lval_kill_tmp" -> "lct"
| "store_cast_tmp" -> "sct"
| _ when !Config.vVar > 1 ->
name
| "_WDF_DEVICE_EXTENSION_TYPE_INFO" -> "DETI"
| _ ->
String.filteri (fun i c -> i = 0 || c <> Char.lowercase c) name

6
src/Hooks.mli Normal file
Просмотреть файл

@ -0,0 +1,6 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Hooks for very applicaton-specific behavior. *)
val var_name : string -> string

14
src/Initialize.ml Normal file
Просмотреть файл

@ -0,0 +1,14 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Initialization *)
open Library
let thunks = ref []
let register thunk =
thunks := thunk :: !thunks
let initialize program =
List.iter (fun thunk -> thunk program) !thunks

8
src/Initialize.mli Normal file
Просмотреть файл

@ -0,0 +1,8 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open Program
val register : (Prog.t -> unit) -> unit
val initialize : Prog.t -> unit

275
src/Inline.ml Normal file
Просмотреть файл

@ -0,0 +1,275 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Inline function calls. *)
open Library
(**/**)
open Variable
open Expression
module E = Exp
module S = Substitution
open Program
module I = Inst
module C = Cmnd
module K = ControlPoint
(**/**)
module L = (val Log.std Config.vInline : Log.LOG)
(** Discover which procs are inline-able. *)
module CallGraph = Graph.Make (Proc.Id) (Proc.Id) (K.Id)
let calculate_call_map program =
Prog.fold_procs (fun caller call_map ->
CFG.fold_edges (fun _ cm -> cm) (fun (u,c,_v) call_map ->
match c with
| C.Call{Call.targets}
| C.ICall{Call.targets} -> List.fold (fun x a -> (caller.Proc.id, x, K.id u) :: a) targets call_map
| _ -> call_map
) caller.Proc.cfg (K.id caller.Proc.entry) call_map
) program []
let graph_of_call_map call_map =
let g = CallGraph.create () in
List.iter (fun (caller,callee,node_id) ->
let u = CallGraph.add_vertex g (caller,caller) in
let v = CallGraph.add_vertex g (callee,callee) in
CallGraph.add_edge g u (node_id) v
) call_map ;
g
let calculate_rec_procs program =
let m = calculate_call_map program in
let g = graph_of_call_map m in
let main = program.Prog.main in
let main_vtx = CallGraph.add_vertex g (main,main) in
CallGraph.root_vertex g main_vtx ;
CallGraph.remove_unreachable g ;
let cs = CallGraph.cutpoints main_vtx in
L.printf 2 "cutpoints %a" (List.fmt "," CallGraph.Vertex.fmt) (CallGraph.VertexSet.to_list cs) ;
let recs =
CallGraph.VertexSet.fold (fun c cs ->
(CallGraph.index_of c) :: cs
) cs [] in
let uses = Proc.IdHMap.create 20 in
let main_idx = CallGraph.index_of main_vtx in
CallGraph.iter_edges
(fun c -> Proc.IdHMap.add uses (CallGraph.index_of c) (List.length (CallGraph.predecessors c)))
(fun _edge -> ())
g main_idx ;
Proc.IdHMap.add uses main_idx 1 ;
(recs, uses)
let recursive recs p =
List.mem p.Proc.id recs
(* Returns true, if procedure is a leaf, (makes no calls) *)
let contains_loop_and_leaf_proc proc =
let leaf_proc = ref true in
let contains_loop = ref false in
CFG.iter_edges
(fun v ->
match K.sort v with
| Some(K.Cut) -> contains_loop := true
| _ -> ())
(fun (_v,c,_v') ->
match c with
| C.Call(_)
| C.ICall _ -> leaf_proc := false
| _ -> ()
)
proc.Proc.cfg (K.id proc.Proc.entry) ;
(!contains_loop, !leaf_proc)
let inlineable (recs,uses) p in_loop =
let contains_loops,leaf_proc = contains_loop_and_leaf_proc p in
let macro = leaf_proc && (not contains_loops) in
let no_rec = not (recursive recs p) in
let thin_wrapper = no_rec && (not contains_loops) in
let single_call_not_in_loop = no_rec && (not in_loop) && (Proc.IdHMap.find uses (p.Proc.id) = 1) in
L.printf 2 "proc %a: macro:%b, thin_wrapper:%b, single_call_not_in_loop: %b no_rec:%b"
Proc.Id.fmt p.Proc.id macro thin_wrapper single_call_not_in_loop no_rec ;
match Config.optimize_inline with
| 0 -> false
| 1 -> macro
| 2 -> macro || thin_wrapper
| 3 -> macro || thin_wrapper || single_call_not_in_loop
| 4 -> no_rec
| _ -> failwith "Unknown inline level"
(** inline_proc and inline_body *)
(* Clone callee graph. *)
let clone_for_inlining caller callee =
(* Map of callee vertex to corresponding vertex in inlined caller code. *)
let module CPHMap = HashMap.Make(K) in
let proc_to_inline = CPHMap.create 32 in
let {Proc.id= caller_id; cfg= caller_cfg} = caller in
let {Proc.cfg= callee_cfg; entry= callee_entry; exit= callee_exit} = callee in
(* Clone callee vertices and edges into caller. *)
CFG.iter_vertices (fun v ->
let v' = CFG.add_vertex caller_cfg (K.mk_label ?sort:(K.sort v) (K.pos v) caller_id) in
CPHMap.add proc_to_inline v v'
) callee_cfg ;
CFG.iter_edges (fun _ -> ()) (fun (u,c,v) ->
let u' = CPHMap.find proc_to_inline u in
let v' = CPHMap.find proc_to_inline v in
CFG.add_edge caller_cfg u' c v'
) callee_cfg (K.id callee_entry) ;
(* Find cloned entry and exit. *)
let inlined_entry = CPHMap.find proc_to_inline callee_entry in
let inlined_exit = CPHMap.find proc_to_inline callee_exit in
(* Return cloned callee_cfg as caller sub-graph. *)
(inlined_entry, inlined_exit)
(*
Translate this edge in [proc]:
u --Call(p,args)--> v
into this edge in [proc] (with p's locals and formals now added to [proc]'s):
u --Inst[frmls=actls]--> clone_for_inlining(p) --Inst[Kill(frmls,locals)] --> v
The clone_for_inlining function clones the body of p.
*)
let work_count = ref 0
let proc procs recs_uses ({Proc.id; locals; cfg; entry} as proc) =
L.incf 1 "( analyzing %a" Proc.Id.fmt id ; (fun _p -> L.decf 1 ")") <&
let in_loop =
if Config.optimize_inline = 3 then (
L.incf 3 "( calculating SCC" ; (fun _ -> L.decf 3 ")") <&
let vtx_to_scc = CFG.scc cfg in
fun v ->
(List.length (CFG.VertexMap.find v vtx_to_scc)) > 1
)
else
fun _ -> false in
(* Replacing the graph while folding over it is bad. So we first collect eligible call-sites. *)
let inline_candidates =
CFG.fold_edges (fun _ acc -> acc)
(fun (u,c,v) acc ->
match c with
| C.Call{Call.proc; actuals; areturn} ->
let callee =
try Proc.IdHMap.find procs proc
with Not_found -> failwithf "Undefined procedure: %a" Proc.Id.fmt proc in
if inlineable recs_uses callee (in_loop u) then (
L.printf 1 "inlining %a" Proc.Id.fmt proc ;
(u, c, v, Call.mk callee actuals areturn) :: acc
)
else (
L.printf 2 "Not inlining %a" Proc.Id.fmt proc ;
acc
)
(* Don't care about non-Call commands *)
| _ ->
acc
) cfg (K.id entry) [] in
(* Inline an inline-candidate. Add it's (formals+locals) and ret-sites to [acc]. *)
let inline (call_site, c, retn_site, call) caller_locals =
let {Call.proc= callee; areturn} = call in
let {Proc.formals; freturn; locals} = callee in
incr work_count ;
(* Move actuals to formals. *)
let prologue =
let frmls_to_actls,_ = Call.args {call with Call.areturn= None} in
S.fold (fun f a blk ->
match E.desc f with
| E.Var(f) -> I.mk (I.Move(f, a)) (K.pos call_site) :: blk
| _ -> assert false
) frmls_to_actls [] in
(* Clone body of callee. *)
let inlined_entry, inlined_exit =
clone_for_inlining proc callee in
(* Move formal return to actual return. *)
let epilogue =
match freturn, areturn with
| Some(frtrn), Some(artrn) -> [I.mk (I.Move(artrn, E.mkVar frtrn)) (K.pos inlined_exit)]
| _ -> [] in
(* Kill callee formals, formal return, and locals *)
let extra_locals =
Vars.union (Vars.of_list formals) (Option.fold Vars.add freturn locals) in
let epilogue =
epilogue @ [I.mk I.(Kill(extra_locals)) (K.pos inlined_exit)] in
(* Connect call_site -prologue-> entry and exit -epilogue-> retn_site *)
CFG.add_block_edge cfg call_site prologue inlined_entry ;
CFG.add_block_edge cfg inlined_exit epilogue retn_site ;
CFG.remove_edge cfg call_site c retn_site ;
(Vars.union extra_locals caller_locals)
in
(* Inline calls. *)
let locals = List.fold inline inline_candidates locals in
{proc with Proc.locals}
(* Entry point *)
let prog program =
L.incf 1 "( inline" ; (fun _p -> L.decf 1 ")") <&
if Config.optimize_inline <= 0 then program
else
let rec loop p =
work_count := 0 ;
let recs, uses = calculate_rec_procs p in
(* Notes: Recalculate addr_taken, based on call graph *)
(* Remove all the procedures not used from call graph *)
let procs = p.Prog.procs in
Proc.IdHMap.filter (fun pid _ ->
let b = (* Don't remove procedures that are used *)
try Proc.IdHMap.find uses pid > 0
with Not_found -> false in
if not b then (
L.printf 2 "remove unused proc %a" Proc.Id.fmt pid ;
incr work_count
);
b
) procs ;
if !work_count > 0 then (
L.printf 1 "removed %d unused procs" !work_count ;
work_count := 0
);
let p' = Prog.map_procs (proc procs (recs, uses)) p in
if !work_count > 0 then (
L.printf 1 "inlined %d procs" !work_count ;
loop p'
)
else
p'
in
let program = loop program
in
let ({Prog.procs; global_setup; inits; addr_taken} as program) = program
in
let global_setup = List.filter (Proc.IdHMap.mem procs) global_setup
and inits = List.filter (Proc.IdHMap.mem procs) inits
and addr_taken = List.filter (Proc.IdHMap.mem procs) addr_taken
in
{program with Prog.global_setup; inits; addr_taken}

9
src/Inline.mli Normal file
Просмотреть файл

@ -0,0 +1,9 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Optimize program by inlining function calls *)
open Program
(** Inline Cals, starting from [p.main]. *)
val prog : Prog.t -> Prog.t

451
src/Instrumentation.ml Normal file
Просмотреть файл

@ -0,0 +1,451 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Generation of instrumented arithmetic programs from analysis results *)
(**/**)
open Library
module HC = HashCons
open Type
open Variable
open Expression
module E = Exp
module S = Substitution
open SymbolicHeap
open Program
module I = Inst
module K = ControlPoint
(**/**)
module L = (val Log.std Config.vInstr : Log.LOG)
let fmt_xs = Vars.fmt_embrace "@[<hov 2>? " " .@]@ "
(*============================================================================
Convert SLAyer Expressions, Formulas, Commands to T2 syntax
============================================================================*)
let emit_pos buf {Position.dir; file; line} =
Printf.bprintf buf "AT(%i,\"%s\")\t" line (Filename.concat dir file)
(* Note: Should not use Var.fmt or else it should check for verbosity *)
(* The problem is we print bad variable names (for T2) if verbose is set *)
let emit_var buf v = Printf.bprintf buf "%s" (Format.asprintf "%a" Var.fmt v)
(* Note: " *)
let emit_fld buf f = Printf.bprintf buf "%s" (Format.asprintf "%a" Fld.fmt f)
let isternary e =
match E.desc e with
| E.Op3(E.Ite,_,E.Num(1L),E.Num(0L)) -> true
| _ -> false
let rec emit_exp buf e =
emit_exp_ buf (E.desc e)
and emit_exp_ buf e =
match e with
(* (0 != (g ? 1 : 0)) is just g *)
| E.Op1(E.Not,E.Eq({HC.desc=E.Num(0L)},e2)) when isternary(e2) ->
(match E.desc e2 with
| E.Op3(E.Ite,g,_,_) ->
L.printf 3 "[0] Saving ternary expression";
emit_exp_ buf g
| _ -> failwith "emit_exp: ternary fail") (* should be impossible to reach *)
(* ((g ? 1 : 0) = v) can be rewritten into g && v == 1 || !g && v == 0 *)
| E.Eq(e1,e2) when isternary(e1) || isternary(e2) ->
(match E.desc e1, E.desc e2 with
| E.Op3(E.Ite,g1,_,_), E.Op3(E.Ite,g2,_,_) ->
L.printf 3 "Saving ternary expression %a" E.fmt e1;
emit_exp buf (E.mkOr [|E.mkAnd[|E.name g1; E.name g2|];
E.mkAnd[|E.mkNot (E.name g1); E.mkNot (E.name g2)|]|])
| E.Op3(E.Ite,g,_,_), E.Var(_) ->
L.printf 3 "Saving ternary expression %a" E.fmt e2;
emit_exp buf (E.mkOr [|E.mkAnd[|E.name g; E.mkEq e2 E.one |];
E.mkAnd[|E.mkNot (E.name g); E.mkEq e2 E.zero|]|])
| E.Var(_), E.Op3(E.Ite,g,_,_) ->
L.printf 3 "Saving ternary expression (%a = %a)" E.fmt e1 E.fmt e2;
emit_exp buf (E.mkOr [|E.mkAnd[|E.name g; E.mkEq e1 E.one |];
E.mkAnd[|E.mkNot (E.name g); E.mkEq e1 E.zero|]|])
| E.Op3(E.Ite,_,_,_), e ->
L.printf 3 "[0] Instrumentation: Dropped %a" E.fmt e1;
Printf.bprintf buf "(nondet() == %a)" emit_exp_ e
| e, E.Op3(E.Ite,_,_,_) ->
L.printf 3 "[0] Instrumentation: Dropped %a" E.fmt e2;
Printf.bprintf buf "(%a == nondet())" emit_exp_ e
| _, _ ->
failwith "emit_exp: ternary fail") (* should be impossible to reach *)
| E.Var(v) -> emit_var buf v
| E.Nil -> Printf.bprintf buf "0"
| E.App(f,a) ->
(match E.desc f with
| E.Add(f) -> Printf.bprintf buf "(%a + %a)" emit_exp a emit_fld f
| E.Sub(f) -> Printf.bprintf buf "(%a - %a)" emit_exp a emit_fld f
| _ -> failwith "emit_exp: unexpected function"
)
| E.Add(_) | E.Sub(_) | E.Idx -> failwith "emit_exp: unexpected offset"
| E.Bas(_) -> Printf.bprintf buf "0"
| E.Eq(e,f) -> Printf.bprintf buf "(%a == %a)" emit_exp e emit_exp f
| E.Num(i) -> Printf.bprintf buf "%Li" i
| E.Str(s) -> Printf.bprintf buf "\"%s\"" s
| E.Op1(E.Not,b) -> Printf.bprintf buf "(! %a)" emit_exp_ b
| E.Op1(E.ZMin,v) -> Printf.bprintf buf "(- %a)" emit_exp_ v
| E.Op2(E.ZLt,e,f) -> Printf.bprintf buf "(%a < %a)" emit_exp_ e emit_exp_ f
| E.Op2(E.ZLe,e,f) -> Printf.bprintf buf "(%a <= %a)" emit_exp_ e emit_exp_ f
| E.Op2(E.ZGt,e,f) -> Printf.bprintf buf "(%a > %a)" emit_exp_ e emit_exp_ f
| E.Op2(E.ZGe,e,f) -> Printf.bprintf buf "(%a >= %a)" emit_exp_ e emit_exp_ f
| E.Op2(E.ZDiv,e,f) -> Printf.bprintf buf "(%a / %a)" emit_exp_ e emit_exp_ f
| E.Op2(E.ZRem,e,f) -> Printf.bprintf buf "(%a %% %a)" emit_exp_ e emit_exp_ f
| E.Op3(E.Ite,g,t,e) when E.is_boolean (E.name t) ->
emit_exp buf (E.mkOr [|E.mkAnd[|E.name g; E.name t|];
E.mkAnd[|E.mkNot (E.name g); E.name e|]|])
| E.OpN(E.Distinct,es) ->
(match Array.to_list es with
| [_] -> emit_exp buf E.tt
| _ ->
emit_exp buf
(E.mkAnd
(Array.of_list
(List.fold_pairs (fun e f dqs ->
E.mkDq (E.name e) (E.name f) :: dqs
) (Array.to_list es) []))))
| E.OpN(E.And,bs) -> emit_expl " && " "(0<1)" buf
(List.filter
(fun b ->
match b with
| E.Var(v) when Var.sort v == Var.BooleanSort ->
let _ = L.printf 3 "Dropping singleton bool var %a" Var.fmt v in
false
| E.Op1(E.Allocd,_) as e ->
let _ = L.printf 3 "Dropping singleton allocd() %a" E.fmt (E.name e) in
false
| _ -> true) (Array.to_list bs))
| E.OpN(E.Or,bs) -> emit_expl " || " "(0<0)" buf (Array.to_list bs)
| E.OpN(E.ZAdd,es) -> emit_expl " + " "0" buf (Array.to_list es)
| E.OpN(E.ZMul,es) -> emit_expl " * " "1" buf (Array.to_list es)
| E.Op1(E.Allocd,_)
| E.Op2(E.ZMod,_,_)
| E.Op3(E.Ite,_,_,_)
| E.OpN(E.UFun _,_) ->
L.printf 3 "Instrumentation: Dropped %a" E.fmt (E.name e) ;
Printf.bprintf buf "nondet()"
and emit_expl sep unit buf = function
| [] ->
Printf.bprintf buf "%s" unit
| [e] ->
emit_exp_ buf e
| e::el ->
Printf.bprintf buf "(%a%a)"
emit_exp_ e
(fun buf el ->
List.iter (fun e ->
Printf.bprintf buf "%s%a" sep emit_exp_ e
) el
) el
let emit_kill buf pos xs =
if Vars.is_empty xs then () else
Printf.bprintf buf "%a%a\n"
emit_pos pos
(fun buf ->
Vars.iter (fun v -> Printf.bprintf buf "%a := nondet(); " emit_var v)
) xs
let emit_move buf pos v e =
match E.desc e with
| E.Var(u) when Var.equal v u -> ()
| _ -> Printf.bprintf buf "%a%a := %a;\n" emit_pos pos emit_var v emit_exp e
let emit_assume buf pos bexp =
if E.equal E.tt bexp then () else
Printf.bprintf buf "%aassume(%a);\n" emit_pos pos emit_exp bexp
(*============================================================================
Core instrumentation approximation and logic
============================================================================*)
module KH = HashMap.Make(K.Id)
let enclosing_proc = KH.create 128
module ExpMSet = MultiSet.Make(Exp)
(** [approximate us xsh] is a quantified boolean expression [(xs,b)] no
stronger than [xsh] that can be usefully translated to T2. *)
let approximate us xsh =
assert(true$>
L.incf 2 "( approximate:@ %a" XSH.fmt xsh);
(fun (xs,b) -> assert(true$>
L.decf 2 ") approximate:@ %a%a" fmt_xs xs E.fmt b))
<&
(*
let rec aux locs q z =
(* take boolean part of q *)
let bex = SH.Pf.term q
in
let q = SH.Pf.empty q
in
(* convert lists to disjunctions based on 0,1,many length, convert to DNF,
for each branch assert the allocated locs on that branch are
distinct *)
let locs = SH.PtS.fold (fun {Pt.loc} z -> ExpMSet.add loc z) q locs in
let q = SH.PtS.empty q
in
match SH.LsS.trychoose q with
| Some({Ls.len; arg={Args.fore; back}} as ls) ->
let q = SH.LsS.remove ls q in
let q_ls_emp = SH.Pf.star (Ls.empty_eqs ls) q in
let q_ls_nemp = SH.Pf.star [E.mkGt len E.zero] q
in
let ls_empty = aux locs q_ls_emp []
in
let link_allocd link z =
if link = [] then z else
let locs =
List.fold (fun (a,_,_) z -> ExpMSet.add a z) link locs in
aux locs q_ls_nemp z
in
bex
:: E.mkGe len E.zero (* logically redundant *)
:: E.mkOr[ E.mkAnd ls_empty ;
E.mkAnd (link_allocd fore (link_allocd back [])) ]
:: z
| None ->
match SH.DjS.trychoose q with
| Some(dj) ->
let q = SH.DjS.remove dj q in
let dj_conseqs =
Dj.fold (fun dt bs -> E.mkAnd (aux locs (SH.star [dt] q) []) :: bs)
dj []
in
bex :: E.mkOr dj_conseqs :: z
| None ->
let mk_distinct_dqs es z =
ExpMSet.fold_pairs (fun e f dqs ->
E.mkDq e f :: dqs
) es z
in
bex :: mk_distinct_dqs (ExpMSet.add E.nil locs) z
in
*)
let xs, sh = XSH.exists_bind us xsh in
let mk_distinct_dqs es z =
ExpMSet.fold_pairs (fun e f dqs ->
E.mkDq e f :: dqs
) es z
in
(* only surface-level points-to *)
let locs = SH.PtS.fold (fun {Pt.loc} z -> ExpMSet.add loc z) sh ExpMSet.empty in
let _ = ExpMSet.iter (fun l -> L.printf 3 "locs: %a" E.fmt l) locs in
let z = mk_distinct_dqs (ExpMSet.add E.nil locs) [] in
let _ = L.printf 3 "z: %a" E.fmt (E.mkAnd (Array.of_list z)) in
(* let b = E.mkAnd (aux ExpMSet.empty sh []) in *)
let _ps, c, d = SH.pure_consequences sh in
let b = E.mkAnd [|c; d; (E.mkAnd (Array.of_list z))|] in
(xs, b)
let emit_moves buf pos xs_to_es =
S.iter (fun x e ->
match E.desc x with
| E.Var(x) -> emit_move buf pos x e
| _ -> L.printf 3 "Instrumentation: Dropped %a := %a" E.fmt x E.fmt e
) xs_to_es
let emit_transition buf pos p _blk_start blk blk_end r =
L.incf 1 "( emit_transition:@ @[<hv>%a@ @[%a@]@ %a@]"
XSH.fmt p (List.fmt ";@ " I.fmt) blk XSH.fmt r
;
let exists_bind_subst vs xsh =
let xs, sh = XSH.exists_bind Vars.empty xsh in
let ws, xs_m_vs = Vars.inter_diff xs vs in
let sh', ws', ws_to_ws', ws'_to_ws = SH.rename_vs ws sh in
(Vars.union ws' xs_m_vs, ws', sh', ws_to_ws', ws'_to_ws)
in
let ms =
List.fold (fun i ms ->
match i.I.desc with
| I.Load(v,_) | I.Alloc(v,_) | I.Move(v,_) | I.Cast(v,_,_) -> Vars.add v ms
| I.Kill(vs) -> Vars.union vs ms
| I.Store _ | I.Free _ | I.Assume _ | I.Assert _ -> ms
| _ -> failwithf "Instrumentation: unimplemented command: %a" I.fmt i
) blk Vars.empty
in
let ms', ms'_to_ms =
Vars.fold (fun v (fs,i) ->
let sort = Var.sort v in
let fresh = Var.gensym (Var.name v) sort in
let i' = S.add (E.mkVar fresh) (E.mkVar v) i in
(Vars.add fresh fs, i')
) ms (Vars.empty, S.empty)
in
let tr_for r =
let xs, vs', r', vs_to_vs', _ = exists_bind_subst (XSH.fv p) r in
let ys, ws', p', _, ws'_to_ws = exists_bind_subst (SH.fv r') p
in
let p' = SH.Pf.star (S.fold (fun m' m acc -> E.mkEq m' m :: acc) ms'_to_ms []) p'
in
let cxt = Vars.unions [xs; ys; ms']
in
let q =
match
List.fold_left (fun q i ->
SymbolicExecution.exec_inst cxt i q
) (Some (SH.exists_intro Vars.empty p')) blk
with
| Some(q) ->
if (K.sort blk_end) = Some K.Exit then
(* exiting scope of locals, so quantify them *)
let proc = KH.find enclosing_proc (K.id blk_end) in
XSH.exists_intro proc.Proc.locals q
else
q
| None -> failwithf "Instrumentation: symbolic execution failed"
in
let us, q = XSH.exists_bind cxt q
in
match Prover.entails q xs r' with
| Some(tr) -> Some(xs, vs', vs_to_vs', ws', ws'_to_ws, cxt, XSH.exists_intro us tr)
| None -> None
in
let xs, vs', vs_to_vs', ws', ws'_to_ws, cxt, tr =
match tr_for r with
| Some(xs, vs', vs_to_vs', ws', ws'_to_ws, cxt, tr) -> (xs, vs', vs_to_vs', ws', ws'_to_ws, cxt, tr)
| None ->
(* r need not have well-guarded quantifiers, so abstract to collect garbage and retry *)
let r',_ = Abstraction.abstract r in
match tr_for r' with
| Some(xs, vs', vs_to_vs', ws', ws'_to_ws, cxt, tr) -> (xs, vs', vs_to_vs', ws', ws'_to_ws, cxt, tr)
| None ->
L.printf 1 "Instrumentation: failed to construct transition relation" ;
(Vars.empty, Vars.empty, S.empty, Vars.empty, S.empty, Vars.empty, XSH.emp)
in
let zs, tr_bx = approximate cxt (XSH.normalize tr)
in
let rs = E.fv tr_bx in
let ws'_to_ws = S.restrict rs ws'_to_ws
and ms'_to_ms = S.restrict rs ms'_to_ms
and vs_to_vs' = S.restrict_rng rs vs_to_vs'
and ms = Vars.inter ms rs
and xs = Vars.inter xs rs
and ms' = Vars.inter ms' rs
and vs' = Vars.inter vs' rs
and ws' = Vars.inter ws' rs
in
(* rename from vocabulary of precondition to vocabulary of transition relation *)
emit_moves buf pos ws'_to_ws ;
(* copy modified variables *)
emit_moves buf pos ms'_to_ms ;
(* kill modified variables, and existentials of transition relation formula *)
emit_kill buf pos (Vars.unions [ms; xs; zs]) ;
(* constrain values of modified and existential variables *)
emit_assume buf pos tr_bx ;
(* rename from vocabulary of transition relation to vocabulary of postcondition *)
emit_moves buf pos vs_to_vs' ;
(* kill temporary variables *)
emit_kill buf pos (Vars.unions [vs'; ws'; ms'])
;
L.decf 1 ") emit_transition:@\n@[<v>\
ws'_to_ws: %a@ ms'_to_ms: %a@ xs: @[%a@]@ zs: @[%a@]@ tr: %a@ vs_to_vs': %a@]"
S.fmt ws'_to_ws S.fmt ms'_to_ms Vars.fmt xs Vars.fmt zs E.fmt tr_bx S.fmt vs_to_vs'
(*============================================================================
Convert SLAyer Abstract Transition Systems to T2 program syntax
============================================================================*)
module ID = Analysis.InterprocDomain
module Tr = ID.Tr
module ATS = ID.ATS
let emit_edge buf m error_state id0 p0 (v1, tr) =
Printf.bprintf buf "FROM: %d;\n" id0 ;
let r1, cp1 = ID.I_D_cp.project v1 in
let xshT1 = ID.RD.project r1 in
let id1 = ATS.VertexIMap.find m v1 in
match xshT1 with
| None ->
Printf.bprintf buf "TO: %s;\n\n" error_state
| Some(xsh1) ->
(match tr with
| Tr.Intra(blk_start, blk, blk_end, _) ->
emit_transition buf (K.pos cp1) p0 blk_start blk blk_end xsh1
| Tr.Call _
| Tr.Return
| Tr.Summary ->
failwith "Unsupported: Instrumentation of non-inlined procedures"
);
Printf.bprintf buf "TO: %d;\n\n" id1
(** Print the arithmetic commands originating from the vertex [v0]. *)
let emit_vertex buf m error_state v0 id0 =
let xshT0 = ID.RD.project (fst (ID.I_D_cp.project v0)) in
L.printf 4 "[Instrumentation] emit_vertex %a" ATS.Vertex.fmt v0;
match xshT0 with
| None ->
Printf.bprintf buf "FROM: %s;\nTO: %s;\n\n" error_state error_state
| Some p0 ->
List.iter
(fun v_tr -> emit_edge buf m error_state id0 p0 v_tr)
(ATS.successors v0)
(** Generate arithmetic program in T2's input format. *)
let write_arith_program {Prog.main; procs} ({Analysis.invariants} as results) buf =
let ats = ID.ats invariants in
let m = ATS.identify_vertices ats in
(* Find start vertex *)
let {Proc.entry} = Proc.IdHMap.find procs main in
let start =
match ATS.vertices_for ats entry with
| [v] -> ATS.VertexIMap.find m v
| _ -> failwith "abstract transition system must have a unique start"
in
(* Is this ATS a proof or a counterexample? *)
let error_state =
if Analysis.safe results then "ERROR"
else string_of_int (ATS.VertexIMap.length m)
in
(* Output the program *)
if not (Analysis.safe results) then (
Printf.bprintf buf "// Counterexample error state: %s\n" error_state
);
Printf.bprintf buf "START: %d;\n\n" start;
ATS.VertexIMap.iter (fun v id -> emit_vertex buf m error_state v id) m
let instrument program results =
let {Prog.main; procs} = program in
let main = Proc.IdHMap.find procs main in
if not Config.instrument then ()
else
let instr_fname = if Analysis.safe results then (Config.testname^".t2") else (Config.testname^".cex.t2") in
Prog.fold_proc (Some procs)
(fun _ () -> ())
(fun _ () -> ())
(fun p k () -> KH.add enclosing_proc (K.id k) p)
(fun _ _ () -> ())
main () ;
try
Library.with_out instr_fname (write_arith_program program results)
with exc ->
prerr_endline ("\nInstrumentation Error: "^(Printexc.to_string exc))

12
src/Instrumentation.mli Normal file
Просмотреть файл

@ -0,0 +1,12 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open Variable
open Expression
open SymbolicHeap
open Program
val instrument : Prog.t -> Analysis.t -> unit
val approximate : Vars.t -> XSH.t -> Vars.t * Exp.t

407
src/Interproc.ml Normal file
Просмотреть файл

@ -0,0 +1,407 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Interprocedural abstract fixed-point computation *)
(**/**)
open Library
open Variable
module S = Substitution
open Program
module I = Inst
module C = Cmnd
module K = ControlPoint
include Interproc_sig
module L = (val Log.raw Config.vSE : Log.LOG)
(* PS#193: change to a priority queue where the priorities are computed so as
to attempt to mimic the denotational semantics *)
module DepthFirstWorklist = struct
type t = Nil | Cons of int * (t -> t) * t
let count = ref 0
let rec ids = function
| Nil -> []
| Cons(id,_,items) -> id :: ids items
let empty = Nil
let add item items =
(fun items -> L.printf 5 "W.add: @[%a@]" (List.fmt " " Format.pp_print_int) (ids items)) <& let()=()in
incr count ;
Cons(!count, item, items)
let rem items =
L.printf 5 "W.rem: @[%a@]" (List.fmt " " Format.pp_print_int) (ids items) ;
match items with
| Nil -> None
| Cons(_,item,items) -> Some(item, items)
end
module W = DepthFirstWorklist
module Summaries (I_D_cp: INJECT_D_CONTROL_POINT) : sig
type t
val empty : unit -> t
val await : t -> I_D_cp.t -> (I_D_cp.t -> W.t -> W.t) -> unit
val add : t -> Proc.t -> I_D_cp.t -> I_D_cp.t -> W.t -> W.t
val posts : t -> Proc.t -> I_D_cp.t -> I_D_cp.t list
end = struct
module I_D_cpTbl =
ImperativeMultiMap.Make
(I_D_cp)
(List.Set(struct type t = I_D_cp.t -> W.t -> W.t end))
module I_D_cp_Set = Set.Make(I_D_cp)
module ProcI_D_cpTbl =
ImperativeMultiMap.Make
(struct
type t = Proc.t * I_D_cp.t
let equal = equal_tup2 Proc.equal I_D_cp.equal
let compare = compare_tup2 Proc.compare I_D_cp.compare
end)
(I_D_cp_Set)
type t = {
waiting: I_D_cpTbl.t;
triples: ProcI_D_cpTbl.t
}
let empty () =
{waiting= I_D_cpTbl.create (); triples= ProcI_D_cpTbl.create ()}
let await sums pre thunk = I_D_cpTbl.add sums.waiting pre thunk
let add sums fn pre post wl =
ProcI_D_cpTbl.add sums.triples (fn,pre) post ;
let waiting_on_pre = I_D_cpTbl.find sums.waiting pre
in
List.fold (fun waiting wl -> waiting post wl) waiting_on_pre wl
let posts sums fn entry =
I_D_cp_Set.to_list (ProcI_D_cpTbl.find sums.triples (fn,entry))
end
module Heights (I_D_cp: INJECT_D_CONTROL_POINT) = struct
module M = ImperativeMap.Make(ControlPoint)
type t = int M.t
let create () = M.create ()
let exceeded t k =
let n = try (M.find t k) + 1 with Not_found -> 1 in
M.add t k n ;
(0 < Config.limit && Config.limit < n)
end
module Pair (Inv: INTRAPROC_DOMAIN) = struct
type pred = Inv.t
type r = Inv.r
let create = Inv.create
type t = { entry: Inv.t; curr: Inv.t; }
let inject x = {entry= x; curr= x}
let project x = x.curr
let exec_inst cxt i x = {x with curr= Inv.exec_inst cxt i x.curr}
let error = inject Inv.error
let tt = inject Inv.tt
let is_error x = Inv.is_error x.curr
let is_false x = Inv.is_false x.curr
let join x y = {x with curr= Inv.join x.curr y.curr}
let generalize x =
let x', junk = Inv.generalize x.curr in
({x with curr= x'}, junk)
(* equality on abstract elements at procedure entry points *)
let equal_entry x y = Inv.equal x.entry y.entry
let below x y = equal_entry x y && Inv.below x.curr y.curr
let adapted_pre_substate_call r cxt pre call pcall =
match Inv.adapted_pre_substate_call r cxt pre.curr call.curr pcall with
| None -> None
| Some(post_ra_to_retn_ra) ->
let post_to_retn post_er =
(* pop call stack by reinstating call's entry *)
{call with curr= post_ra_to_retn_ra post_er.curr}
in
Some(post_to_retn)
let call_to_entry r call pcall =
let entry, post_ra_to_retn_ra = Inv.call_to_entry r call.curr pcall
in
let post_to_retn post_er =
(* pop call stack by reinstating call's entry *)
{call with curr= post_ra_to_retn_ra post_er.curr}
in
(inject entry, post_to_retn)
let exit_to_retn callee exit =
{exit with curr= Inv.exit_to_retn callee exit.curr}
let resolve_indirect_call r call fptr ftyp =
Inv.resolve_indirect_call r call.curr fptr ftyp
let compare x y =
compare_tup2 Inv.compare Inv.compare (x.entry,x.curr) (y.entry,y.curr)
let equal x y = Inv.equal x.curr y.curr && Inv.equal x.entry y.entry
let fmt ff x = Inv.fmt ff x.curr
let fmt_entry ff x = Inv.fmt ff x.entry
let fmt_pre ff (x,p) = Inv.fmt_pre ff (x.curr,p)
let fmt_reln ff x =
Format.fprintf ff "(@[%a,@ %a@])" Inv.fmt x.entry Inv.fmt x.curr
end (* Pair *)
module Make (InterprocDomain: INTERPROC_DOMAIN) = struct
module ID = InterprocDomain
module PInv = ID.RD (* a pair of invariants *)
module InjPInv = ID.I_D_cp (* a pair of invariants injected into the InterprocDomain carrier *)
module Summaries = Summaries(InjPInv)
let fmt_summary lvl msg pre ({Proc.formals; freturn} as proc) post =
L.printf lvl "@[<hov 2>%s summary:@ @[<hv>%a@ %a@ %a@]@\n" msg
PInv.fmt_pre (fst (InjPInv.project pre), proc)
(Call.fmt (fun ff {Proc.id} -> Proc.Id.fmt ff id)) (Call.mk proc formals freturn)
PInv.fmt (fst (InjPInv.project post))
module Heights = Heights(InjPInv)
type t = {
program: Prog.t;
invariants: ID.r;
summaries: Summaries.t;
heights: Heights.t;
mutable safe: bool;
mutable hit_limit: bool;
}
let exec_prog ({Prog.globals; procs; main} as program) init =
let r = ID.create program in
let sums = Summaries.empty () in
let heights = Heights.create () in
let res = {program; invariants= r; summaries= sums; heights; safe= true; hit_limit= false}
in
let rec exec_cont
(proc: Proc.t) (* proc currently executing *)
((prev: InjPInv.t), (* invariant for previous "control point" *)
((curr: PInv.t), (* invariant for current cont *)
(pc: K.t)) (* current cont / program counter *)
as prev_to_curr: ID.d_bk) (* path edge from prev to curr points *)
wl (* worklist *)
=
let cxt = Vars.union globals proc.Proc.locals in
let fail ((prev, (_error, _k)) as prev_to_error) wl =
res.safe <- false ;
let next = ID.prev_to_join r prev_to_error in
if Config.propagate then
W.add (exec_cont proc (prev, (InjPInv.project next))) wl
else if Config.continue then
wl
else
W.empty
in
let rec exec_succ ((prev, (curr, pc)) as prev_to_curr) cmnd k wl =
match cmnd with
| C.Inst(inst) ->
L.incf 3 " %a@\n@[<hov 2>exec: %a@\n%a@]" PInv.fmt curr Position.fmt (K.pos pc) I.fmt inst ;
(fun _ -> L.decf 3 "@ ")
<&
let prev_to_k = PInv.exec_inst cxt inst curr in
if PInv.is_error prev_to_k then
let curr = if K.sort pc <> None then prev else ID.prev_to_join r prev_to_curr in
let curr_to_error = PInv.exec_inst cxt inst (fst (InjPInv.project curr)) in
fail (curr, (curr_to_error, k)) wl
else
W.add (exec_cont proc (prev, (prev_to_k, k))) wl
| C.Call({Call.proc= callee_name} as pcall) ->
L.printf 2 " %a@\n@[<hov 2>exec: @[%a@]@\n%a@\n@]" PInv.fmt curr K.fmt pc C.fmt cmnd
;
let callee =
try Proc.IdHMap.find procs callee_name
with Not_found -> failwithf "Undefined procedure: %a" Proc.Id.fmt callee_name
in
let exec_post_to_retn pre post_to_retn post wl =
let retn = post_to_retn (post, (fst (ID.I_D_cp.project post), k))
in
fmt_summary 2 "applying" pre callee post ;
(* execute from the return site *)
W.add (exec_cont proc (retn, ID.I_D_cp.project retn)) wl
in
let apply_existing_summary pre post_to_retn wl =
(* pre covers call *)
let exec_post_to_retn = exec_post_to_retn pre post_to_retn
in
(* execute this call's return site from any posts added to this specification later *)
Summaries.await sums pre exec_post_to_retn
;
(* execute this call's return site from the existing summary's post states *)
List.fold exec_post_to_retn (Summaries.posts sums callee pre) wl
in
let create_new_summary entry post_to_retn wl =
(* execute this call's return site from any posts added to this specification later *)
Summaries.await sums entry (exec_post_to_retn entry post_to_retn)
;
(* execute the callee's body *)
W.add (exec_cont callee (entry, ID.I_D_cp.project entry)) wl
in
let pres = ID.procedure_pres r callee
in
let rec search_for_covering_pre = function
| [] ->
(* no covering pre, compute a new specification *)
L.printf 7 "@[<hov 2>no summary for:@ %a@]" PInv.fmt curr
;
let entry, post_to_retn = ID.call_to_entry r prev_to_curr {pcall with Call.proc= callee}
in
if List.exists (fun pre -> InjPInv.equal entry pre) pres then
apply_existing_summary entry post_to_retn wl
else
create_new_summary entry post_to_retn wl
| pre :: pres ->
(* found a pre *)
L.printf 7 "@[<hov 2>trying summary with pre:@ %a@]" PInv.fmt (fst (InjPInv.project pre))
;
(* check if pre covers call *)
match ID.adapted_pre_substate_call r cxt pre prev_to_curr {pcall with Call.proc= callee} with
| None ->
(* no, keep looking *)
search_for_covering_pre pres
| Some(post_to_retn) ->
(* pre covers call *)
apply_existing_summary pre post_to_retn wl
in
(* find all preconditions for callee *)
search_for_covering_pre pres
| C.ICall({Call.proc; typ; targets} as pcall) ->
L.printf 2 " %a@\n@[<hov 2>exec: @[%a@]@\n%a@\n@]" PInv.fmt curr K.fmt pc C.fmt cmnd
;
let callees = ID.resolve_indirect_call r prev_to_curr proc typ in
if callees = [] then
let curr = ID.prev_to_join r prev_to_curr in
let curr_to_error = PInv.join (fst (InjPInv.project curr)) PInv.error in
fail (curr, (curr_to_error, k)) wl
else
List.fold (fun callee wl ->
if not (List.exists (fun target -> Proc.Id.equal callee target) targets) then
failwith "Unsound static approximation of indirect call targets" ;
exec_succ prev_to_curr (C.Call({pcall with Call.proc= callee})) k wl
) callees wl
in
(* exec_cont *)
if PInv.is_false curr then (
(* don't execute if curr is inconsistent *)
L.printf 3 " %a@\n@[<hov 2>exec: @[%a@]@]@\nunreachable@\n" PInv.fmt curr K.fmt pc ;
if Config.show_unreachable then ignore( ID.prev_to_join r prev_to_curr );
wl
) else if ID.now_covered r prev then (
(* don't execute if pre got covered since it was scheduled *)
L.printf 3 " %a@\n@[<hov 2>exec: @[%a@]@]@\n@[covered:@ %a@]@\n" PInv.fmt curr K.fmt pc InjPInv.fmt prev ;
wl
) else if Heights.exceeded heights pc then (
(* fail if limit on number of iterations is hit *)
L.printf 3 " %a@\n@[<hov 2>exec: @[%a@]@\nchain too long@]@\n" PInv.fmt curr K.fmt pc ;
res.hit_limit <- true ;
fail prev_to_curr wl
) else (
let exec_succs ((_prev, (_curr, pc)) as prev_to_curr) wl =
List.fold (fun (k, edge) wl ->
exec_succ prev_to_curr edge k wl
) (CFG.successors pc) wl
in
match K.sort pc with
| Some K.Exit ->
let prev_to_retn = ID.exit_to_retn proc prev_to_curr
in
let covered, retn = ID.prev_to_cut r prev_to_retn
in
(match covered with
| ID.WasCoveredByOld ->
wl
| ID.NowCoveredByNew ->
let pre =
let equal_entry x y = ID.RD.equal_entry (fst (ID.I_D_cp.project x)) (fst (ID.I_D_cp.project y)) in
try
List.find (equal_entry retn) (ID.procedure_pres r proc)
with Not_found ->
L.printf 2 "@[retn:@ @[%a@]@ pres:@ @[%a@]@]"
PInv.fmt_reln (fst (InjPInv.project retn))
(List.fmt ",@ " (fun ff pre -> PInv.fmt ff (fst (InjPInv.project pre))))
(ID.procedure_pres r proc) ;
failwith "no pre for retn found"
in
fmt_summary 1 "adding" pre proc retn
;
ID.register_pre r pre
;
Summaries.add sums proc pre retn wl
)
| Some K.Cut ->
let covered, cut = ID.prev_to_cut r prev_to_curr in
(match covered with
| ID.WasCoveredByOld ->
wl
| ID.NowCoveredByNew ->
exec_succs (cut, ID.I_D_cp.project cut) wl
)
| Some K.Join ->
let join = ID.prev_to_join r prev_to_curr in
exec_succs (join, ID.I_D_cp.project join) wl
| Some K.Fork ->
let fork = ID.prev_to_join r prev_to_curr in
exec_succs (fork, ID.I_D_cp.project fork) wl
| Some K.Entry | Some K.Return | None ->
exec_succs prev_to_curr wl
)
in
(* exec_prog *)
let rec compute_invariants_fixed_point wl =
match W.rem wl with
| Some(thunk, wl) ->
compute_invariants_fixed_point (thunk wl)
| None ->
res
in
let {Proc.entry} as main = Proc.IdHMap.find procs main
in
let init = ID.init r (PInv.inject init, entry)
in
compute_invariants_fixed_point (W.add (exec_cont main (init, ID.I_D_cp.project init)) W.empty)
end

31
src/Interproc.mli Normal file
Просмотреть файл

@ -0,0 +1,31 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Interprocedural abstract fixed-point computation *)
open Program
open Interproc_sig
module Pair (Inv: INTRAPROC_DOMAIN) : (RELATION_DOMAIN with type pred = Inv.t)
(** Construct an interprocedural analysis given a domain for representing sets
of states and an interprocedural domain, parametric in the intraprocedural
domain and in the lifting from intraprocedural abstract states to
interprocedural abstract states *)
module Make (InterprocDomain: INTERPROC_DOMAIN) : sig
module Summaries : sig type t end
module Heights : sig type t end
type t = {
program: Prog.t;
invariants: InterprocDomain.r;
summaries: Summaries.t;
heights: Heights.t;
mutable safe: bool;
mutable hit_limit: bool;
}
val exec_prog : Prog.t -> InterprocDomain.RD.pred -> t
end

172
src/Interproc_sig.ml Normal file
Просмотреть файл

@ -0,0 +1,172 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Interfaces for analysis modules *)
(**/**)
open Library
open Type
open Variable
open Expression
open Program
(**/**)
module type PROCEDURE_OPERATIONS = sig
type i_d_cp (** (injected) data associated with control points *)
type d_bk (** data associated with control point free paths *)
type r (** domain-specific whole-program data *)
val create : Prog.t -> r
(** For procedure p given by p(f)\{local l in B\}, we use the following
naming conventions for variables denoting abstract predicates based on
the roles they play in judgments of the form
{C \{pre\} p(f) \{post\} |- \{entry\} B \{exit\} }
or {C \{pre\} p(f) \{post\} |- \{call\} p(f\[A/f\]) \{retn\} } *)
(** For a procedure call \{call\} p(f\[A\]), [adapted_pre_substate_call r
cxt pre call p \[A/f\]] determines if a summary from [pre] applies to
the call and if so executes an interprocedural step to [pre] and returns
[post_to_retn] such that for any specification \{pre\} p(f) \{\/_i
post_i\}, [post_to_retn caller post_i] executes an intraprocedural post
to return site step such that \{pre\} p(f\[A/f\]) \{\/_i post_to_retn
post_i\} holds. *)
val adapted_pre_substate_call : r -> Vars.t -> i_d_cp -> d_bk -> Proc.t Call.t -> (d_bk -> i_d_cp) option
(** For a procedure call \{call\} p(A), [call_to_entry r call p A] executes
an interprocedural call site to entry point step, returning a [pre]
such that a specification \{pre\} p(x) \{post\} would apply. *)
val call_to_entry : r -> d_bk -> Proc.t Call.t -> i_d_cp * (d_bk -> i_d_cp)
(** [exit_to_retn p exit] executes an interprocedural exit point to return
site step, returning [retn] such that if \{entry\} B \{exit\} holds
where [p] is given by p(x)\{local l in B\}, then
\{entry\} local l in B \{retn\} holds. *)
val exit_to_retn : Proc.t -> d_bk -> d_bk
(** [resolve_indirect_call r call fptr ftyp] returns an over-approximation of the possible procedures that
[*fptr] of type [ftyp] in states satisfying [call] might call. *)
val resolve_indirect_call : r -> d_bk -> Exp.t -> Typ.t -> Proc.Id.t list
end
(** An abstract domain for intraprocedural analysis *)
module type INTRAPROC_DOMAIN = sig
type t
include
(PROCEDURE_OPERATIONS
with type i_d_cp := t
and type d_bk := t)
(** element representing all error states *)
val error : t
(** element representing all non-error states *)
val tt : t
(** test if element represents error states *)
val is_error : t -> bool
(** test if element represents no states *)
val is_false : t -> bool
(** execution of instructions *)
val exec_inst : Vars.t -> Inst.t -> t -> t
(** abstract order *)
val below : t -> t -> bool
(** abstract element constructors *)
val join : t -> t -> t
val generalize : t -> t * bool
(** utility routines *)
val compare : t -> t -> int
val equal : t -> t -> bool
val fmt : t formatter
val fmt_pre : (t * Proc.t) formatter
end
(** An abstract domain for intraprocedural analysis, but using a form of
abstract states suitable for interprocedural analysis *)
module type RELATION_DOMAIN = sig
include (INTRAPROC_DOMAIN)
val equal_entry : t -> t -> bool
val fmt_entry : t formatter
val fmt_reln : t formatter
type pred
val inject : pred -> t
val project : t -> pred
end
(** Intraprocedural abstract states (a d_cp and a control_point) together with
other domain-specific data *)
module type INJECT_D_CONTROL_POINT = sig
type t
type d_cp
val project : t -> d_cp * ControlPoint.t
val equal : t -> t -> bool
val compare : t -> t -> int
val fmt : t formatter
end
(** An abstract domain for interprocedural analysis *)
module type INTERPROC_DOMAIN = sig
module RD : RELATION_DOMAIN
module I_D_cp : (INJECT_D_CONTROL_POINT with type d_cp = RD.t)
include
(PROCEDURE_OPERATIONS
with type i_d_cp = I_D_cp.t
and type d_bk = I_D_cp.t * (RD.t * ControlPoint.t))
(** [init r p] is the initial element for [p] *)
val init : r -> RD.t * ControlPoint.t -> i_d_cp
(** [now_covered r p] tests if [p] is covered by some other element *)
val now_covered : r -> i_d_cp -> bool
(** [prev_to_join r (prev,(prev_to,next))] executes an intraprocedural
step to a join point *)
val prev_to_join : r -> d_bk -> i_d_cp
(** [prev_to_cut r (prev,(prev_to,next))] executes an intraprocedural
step to a cut point *)
type __covered = WasCoveredByOld | NowCoveredByNew
val prev_to_cut : r -> d_bk -> __covered * i_d_cp
(** [procedure_pres r proc] are the pres of all summaries of [proc] *)
val procedure_pres : r -> Proc.t -> i_d_cp list
(** [register_pre r pre] declares that [pre] is the pre of some summary *)
val register_pre : r -> i_d_cp -> unit
val states_for : r -> ControlPoint.t -> I_D_cp.t list
val errors : r -> I_D_cp.t list
val leaks : r -> I_D_cp.t list
val dead : r -> Position.t list
val hit_limit : r -> bool
end

35
src/Library.ml Normal file
Просмотреть файл

@ -0,0 +1,35 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(* Extensions of the standard library *)
(* Note:
- review the various set modules and make their interfaces
consistent with each other and equally complete
*)
include NSLib
include NSArray
include NSOption
include NSList
include NSSortedList
include NSTuple
include NSSet
include NSMultiSet
(* include NSPolySet *)
include NSIndexedSet
include NSMultiIndexedSet
include NSMultiIndexedMultiSet
include NSMap
include NSMultiMap
include NSImperativeMap
include NSImperativeMultiMap
include NSHashtbl
include NSPolyHashMap
include NSHashMap
include NSHashMultiMap
include NSImperativeSet
include NSHashSet
include NSBinaryRelation
include NSString

33
src/Library.mli Normal file
Просмотреть файл

@ -0,0 +1,33 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Extensions of the standard library. See also standard
{{:file:../../../doc/ocaml%20manual/libref/index.html}Library}. *)
include module type of NSLib
include module type of NSArray
include module type of NSOption
include module type of NSList
include module type of NSSortedList
include module type of NSTuple
include module type of NSSet
include module type of NSMultiSet
(* include module type of NSPolySet *)
include module type of NSIndexedSet
include module type of NSMultiIndexedSet
include module type of NSMultiIndexedMultiSet
include module type of NSMap
include module type of NSMultiMap
include module type of NSImperativeMap
include module type of NSImperativeMultiMap
include module type of NSHashtbl
include module type of NSPolyHashMap
include module type of NSHashMap
include module type of NSHashMultiMap
include module type of NSImperativeSet
include module type of NSHashSet
include module type of NSBinaryRelation
(** {2 Extended Modules } *)
include module type of NSString

43
src/Library/NSArray.ml Normal file
Просмотреть файл

@ -0,0 +1,43 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSList
module Array = struct
include Array
let swap a i j =
let a_i = a.(i) in
a.(i) <- a.(j) ;
a.(j) <- a_i
let reverse a i j =
let rec loop n =
if n < j/2 then (
swap a n (j-n-1) ;
loop (j+1)
)
in
loop i
let equal eq x y =
let l = Array.length x in
l = (Array.length y) &&
let rec equal_ i =
if i >= l then true else
eq x.(i) y.(i) && equal_ (i+1)
in equal_ 0
let compare cmp x y =
let l = Array.length x in
let o = Pervasives.compare l (Array.length y) in if o <> 0 then o else
let rec compare_ i =
if i >= l then 0 else
let o = cmp x.(i) y.(i) in if o <> 0 then o else compare_ (i+1)
in compare_ 0
let fmt sep fn ff a = List.fmt sep fn ff (to_list a)
let for_all pd a = Array.fold_right (fun x b -> b && pd x) a true
end

25
src/Library/NSArray.mli Normal file
Просмотреть файл

@ -0,0 +1,25 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
(** Operations on ['a array]. See also standard
{{:file:../../../doc/ocaml%20manual/libref/Array.html}Array}. *)
module Array : sig
include module type of Array
val swap : 'a array -> int -> int -> unit
val reverse : 'a array -> int -> int -> unit
(* val for_all2 : ('a -> 'b -> bool) -> 'a array -> 'b array -> bool *)
val equal : ('a -> 'a -> bool) -> 'a array -> 'a array -> bool
val compare : ('a -> 'a -> int) -> 'a array -> 'a array -> int
val fmt :
(unit,Format.formatter,unit)format -> 'a formatter -> 'a array formatter
val for_all : ('a -> bool) -> 'a array -> bool
end

Просмотреть файл

@ -0,0 +1,69 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
open NSSet
module BinaryRelation = struct
module Make(Ord: OrderedType) = struct
module OrdOrd = struct
type t = Ord.t * Ord.t
let equal = equal_tup2 Ord.equal Ord.equal
let compare = compare_tup2 Ord.compare Ord.compare
end
include Set.Make (OrdOrd)
let inverse xs =
map (fun (x,y) -> (y,x)) xs
let map_prod fn xs ys =
fold (fun x -> fold (fun y -> add (fn x y)) ys) xs empty
let fold_product fn xs ys =
fold (fun x -> fold (fun y -> fn x y) ys) xs
(* incremental transitive closure *)
let add_with_closure (x,y) us =
let ws = filter (fun (_,y) -> Ord.equal x y) us
and zs = filter (fun (x,_) -> Ord.equal x y) us in
us
|> add (x,y)
|> union (map (fun (w,_) -> (w,y)) ws)
|> union (map (fun (_,z) -> (x,z)) zs)
|> union (map_prod (fun (w,_) (_,z) -> (w,z)) ws zs)
let close xs =
fold add_with_closure xs empty
let choose_maximal_path us =
let rec segment x y seg =
try
(* search backward *)
let w,_ =
choose (filter (fun (w,z) ->
Ord.equal x z && not (List.mem w seg)
) us)
in
(segment w y (w :: seg))
with Not_found ->
try
(* search forward *)
let _,z =
choose (filter (fun (w,z) ->
Ord.equal y w && not (List.mem z seg)
) us)
in
(segment x z (seg @ [z]))
with Not_found ->
[]
in
try
let (x,y) = choose us in
segment x y [x; y]
with Not_found ->
[]
end
end

Просмотреть файл

@ -0,0 +1,16 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
open NSSet
(** Binary Relations of ordered values. *)
module BinaryRelation : sig
module Make (Ord : OrderedType) : sig
include Set.S with type elt = Ord.t * Ord.t
val inverse : t -> t
val fold_product : (elt -> elt -> 'z -> 'z) -> t -> t -> 'z -> 'z
val close : t -> t
val choose_maximal_path : t -> elt list
end
end

55
src/Library/NSHashMap.ml Normal file
Просмотреть файл

@ -0,0 +1,55 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
open NSHashtbl
module HashMap = struct
module type S = sig
type key
type 'a t
val create : int -> 'a t
val clear : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val is_empty : 'a t -> bool
val length : 'a t -> int
val mem : 'a t -> key -> bool
val find : 'a t -> key -> 'a
val tryfind : 'a t -> key -> 'a option
val iter : (key -> 'a -> unit) -> 'a t -> unit
val exists : (key -> 'a -> bool) -> 'a t -> bool
val filter : (key -> 'a -> bool) -> 'a t -> unit
val map : ('v -> 'w) -> 'v t -> 'w t
val mapi : (key -> 'v -> 'w) -> 'v t -> 'w t
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val to_list : 'a t -> (key * 'a) list
end
module Make(H: HashedType) = struct
include Hashtbl.Make(H)
let add = replace
end
end
module StringHMap = HashMap.Make(struct
type t = string
let equal = (Pervasives.( = ) : string -> string -> bool)
let hash = (Hashtbl.hash : string -> int)
end)
module IntHMap = HashMap.Make(struct
type t = int
let equal = (Pervasives.( = ) : int -> int -> bool)
let hash = (Hashtbl.hash : int -> int)
end)
module Int64HMap = HashMap.Make(struct
type t = int64
let equal = (Pervasives.( = ) : int64 -> int64 -> bool)
let hash = (Hashtbl.hash : int64 -> int)
end)

38
src/Library/NSHashMap.mli Normal file
Просмотреть файл

@ -0,0 +1,38 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
(** Imperative maps from hashed keys to single values. *)
module HashMap : sig
module type S = sig
type key
type 'a t
val create : int -> 'a t
val clear : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val is_empty : 'a t -> bool
val length : 'a t -> int
val mem : 'a t -> key -> bool
val find : 'a t -> key -> 'a
val tryfind : 'a t -> key -> 'a option
val iter : (key -> 'a -> unit) -> 'a t -> unit
val exists : (key -> 'a -> bool) -> 'a t -> bool
val filter : (key -> 'a -> bool) -> 'a t -> unit
val map : ('v -> 'w) -> 'v t -> 'w t
val mapi : (key -> 'v -> 'w) -> 'v t -> 'w t
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val to_list : 'a t -> (key * 'a) list
end
module Make (H : HashedType) : (S with type key = H.t)
end
module IntHMap : (HashMap.S with type key = int)
module Int64HMap : (HashMap.S with type key = int64)
module StringHMap : (HashMap.S with type key = string)

Просмотреть файл

@ -0,0 +1,45 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
open NSHashtbl
module HashMultiMap = struct
module type S = sig
type key
type 'a t
val create : int -> 'a t
val clear : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val is_empty : 'a t -> bool
val length : 'a t -> int
val mem : 'a t -> key -> bool
val find : 'a t -> key -> 'a list
val iter : (key -> 'a -> unit) -> 'a t -> unit
val exists : (key -> 'a -> bool) -> 'a t -> bool
val existsi : key -> ('a -> bool) -> 'a t -> bool
(* val filter : (key -> 'a -> bool) -> 'a t -> unit *)
(* val filteri : key -> ('a -> bool) -> 'a t -> unit *)
(* val map : ('a -> 'a) -> 'a t -> unit *)
(* val mapi : (key -> 'a -> 'a) -> 'a t -> unit *)
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val to_list : 'a t -> (key * 'a) list
end
module Make(H: HashedType) = struct
include Hashtbl.Make(H)
let find = find_all
end
end
module IntHMMap = HashMultiMap.Make(struct
type t = int
let equal = (Pervasives.( = ) : int -> int -> bool)
let hash = (Hashtbl.hash : int -> int)
end)

Просмотреть файл

@ -0,0 +1,38 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
(** Imperative maps from hashed keys to multiple values. *)
module HashMultiMap : sig
module type S = sig
type key
type 'a t
val create : int -> 'a t
val clear : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val is_empty : 'a t -> bool
val length : 'a t -> int
val mem : 'a t -> key -> bool
val find : 'a t -> key -> 'a list
val iter : (key -> 'a -> unit) -> 'a t -> unit
val exists : (key -> 'a -> bool) -> 'a t -> bool
val existsi : key -> ('a -> bool) -> 'a t -> bool
(* val filter : (key -> 'a -> bool) -> 'a t -> unit *)
(* val filteri : key -> ('a -> bool) -> 'a t -> unit *)
(* val map : ('a -> 'a) -> 'a t -> unit *)
(* val mapi : (key -> 'a -> 'a) -> 'a t -> unit *)
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val to_list : 'a t -> (key * 'a) list
end
module Make (H : HashedType) : (S with type key = H.t)
end
module IntHMMap : HashMultiMap.S with type key = int

21
src/Library/NSHashSet.ml Normal file
Просмотреть файл

@ -0,0 +1,21 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSPolyHashMap
(* Note: remove polymorphic interface *)
module HashSet = struct
type 'a t = ('a, unit) PolyHMap.t
let create = PolyHMap.create
let add s x = PolyHMap.add s x ()
let remove s x = PolyHMap.remove s x
let cardinal = PolyHMap.length
let mem s x = PolyHMap.mem s x
let iter f = PolyHMap.iter (fun x () -> f x)
let exists p = PolyHMap.exists (fun x () -> p x)
let fold f = PolyHMap.fold (fun x () -> f x)
let to_list s = PolyHMap.fold (fun x () l -> x :: l) s []
end

19
src/Library/NSHashSet.mli Normal file
Просмотреть файл

@ -0,0 +1,19 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Imperative sets of hashed values. *)
module HashSet : sig
type 'a t
val create : int -> 'a t
val add : 'a t -> 'a -> unit
val remove : 'a t -> 'a -> unit
val cardinal : 'a t -> int
val mem : 'a t -> 'a -> bool
val iter : ('a -> unit) -> 'a t -> unit
val exists : ('a -> bool) -> 'a t -> bool
val fold : ('a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val to_list : 'a t -> 'a list
end

84
src/Library/NSHashtbl.ml Normal file
Просмотреть файл

@ -0,0 +1,84 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
module Hashtbl = struct
type ('a,'b) t = ('a,'b) Hashtbl.t
let hash = Hashtbl.hash
let hash_param = Hashtbl.hash_param
let create n = Hashtbl.create n
let clear = Hashtbl.clear
let copy = Hashtbl.copy
let add = Hashtbl.add
let replace = Hashtbl.replace
let remove = Hashtbl.remove
let length = Hashtbl.length
let mem = Hashtbl.mem
let find = Hashtbl.find
let find_all = Hashtbl.find_all
let iter = Hashtbl.iter
let fold = Hashtbl.fold
exception Found_is_empty
let is_empty t =
try iter (fun _ -> raise Found_is_empty) t ; true with Found_is_empty -> false
let tryfind t x = try Some(find t x) with Not_found -> None
exception Found_exists
let exists p t =
try iter (fun k v -> if p k v then raise Found_exists) t ; false
with Found_exists -> true
let existsi k p t = List.exists p (find_all t k)
let to_list t = fold (fun k v es -> (k,v) :: es) t []
let filter p t =
List.iter (fun (k,v) -> if not (p k v) then remove t k) (to_list t)
let map f t =
let t' = create (length t) in
iter (fun k v -> add t' k (f v) ) t ;
t'
let mapi f t =
let t' = create (length t) in
iter (fun k v -> add t' k (f k v) ) t ;
t'
module Make(H: HashedType) = struct
include Hashtbl.Make(H)
let is_empty t =
try iter (fun _ -> raise Found_is_empty) t ; true with Found_is_empty -> false
let tryfind t x = try Some(find t x) with Not_found -> None
let exists p t =
try iter (fun k v -> if p k v then raise Found_exists) t ; false
with Found_exists -> true
let existsi k p t = List.exists p (find_all t k)
let to_list t = fold (fun k v es -> (k,v) :: es) t []
let filter p t =
List.iter (fun (k,v) -> if not (p k v) then remove t k) (to_list t)
let map f t =
let t' = create (length t) in
iter (fun k v -> add t' k (f v) ) t ;
t'
let mapi f t =
let t' = create (length t) in
iter (fun k v -> add t' k (f k v) ) t ;
t'
end
end

54
src/Library/NSHashtbl.mli Normal file
Просмотреть файл

@ -0,0 +1,54 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** @deprecated Make the standard [Hashtbl] read-only since it confuses maps
and multi-maps. Use [HashMap] or [HashMultiMap] instead. *)
module Hashtbl : sig
type ('a, 'b) t = ('a, 'b) Hashtbl.t
val hash : 'a -> int
val hash_param : int -> int -> 'a -> int
val create : int -> ('a, 'b) Hashtbl.t
val clear : ('a, 'b) Hashtbl.t -> unit
val copy : ('a, 'b) Hashtbl.t -> ('a, 'b) Hashtbl.t
val add : ('a, 'b) Hashtbl.t -> 'a -> 'b -> unit
val replace : ('a, 'b) Hashtbl.t -> 'a -> 'b -> unit
val remove : ('a, 'b) Hashtbl.t -> 'a -> unit
val length : ('a, 'b) Hashtbl.t -> int
val mem : ('a, 'b) Hashtbl.t -> 'a -> bool
val find : ('a, 'b) Hashtbl.t -> 'a -> 'b
val find_all : ('a, 'b) Hashtbl.t -> 'a -> 'b list
val iter : ('a -> 'b -> unit) -> ('a, 'b) Hashtbl.t -> unit
val fold : ('a -> 'b -> 'c -> 'c) -> ('a, 'b) Hashtbl.t -> 'c -> 'c
val is_empty : ('a, 'b) Hashtbl.t -> bool
val tryfind : ('a, 'b) Hashtbl.t -> 'a -> 'b option
val exists : ('a -> 'b -> bool) -> ('a, 'b) Hashtbl.t -> bool
val existsi : 'a -> ('b -> bool) -> ('a, 'b) Hashtbl.t -> bool
val to_list : ('a, 'b) Hashtbl.t -> ('a * 'b) list
val filter : ('a -> 'b -> bool) -> ('a, 'b) Hashtbl.t -> unit
val map : ('v -> 'w) -> ('k, 'v) Hashtbl.t -> ('k, 'w) Hashtbl.t
val mapi : ('k -> 'v -> 'w) -> ('k, 'v) Hashtbl.t -> ('k, 'w) Hashtbl.t
module Make(H : NSLib.HashedType) : sig
type key = H.t
type 'a t = 'a Hashtbl.Make(H).t
val create : int -> 'a t
val clear : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val find : 'a t -> key -> 'a
val find_all : 'a t -> key -> 'a list
val replace : 'a t -> key -> 'a -> unit
val mem : 'a t -> key -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val length : 'a t -> int
val is_empty : 'a t -> bool
val tryfind : 'a t -> key -> 'a option
val exists : (key -> 'a -> bool) -> 'a t -> bool
val existsi : key -> ('a -> bool) -> 'a t -> bool
val to_list : 'a t -> (key * 'a) list
val filter : (key -> 'a -> bool) -> 'a t -> unit
val map : ('v -> 'w) -> 'v t -> 'w t
val mapi : (key -> 'v -> 'w) -> 'v t -> 'w t
end
end

Просмотреть файл

@ -0,0 +1,65 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
open NSMap
module ImperativeMap = struct
module type S = sig
type key
type 'a t
val create : unit -> 'a t
val clear : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val is_empty : 'a t -> bool
val length : 'a t -> int
val mem : 'a t -> key -> bool
val find : 'a t -> key -> 'a
val tryfind : 'a t -> key -> 'a option
val iter : (key -> 'a -> unit) -> 'a t -> unit
val exists : (key -> 'a -> bool) -> 'a t -> bool
val filter : (key -> 'a -> bool) -> 'a t -> unit
val map : ('a -> 'a) -> 'a t -> unit
val mapi : (key -> 'a -> 'a) -> 'a t -> unit
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val to_list : 'a t -> (key * 'a) list
end
module Make(Key: OrderedType) = struct
module M = Map.Make(Key)
type key = Key.t
type 'a t = 'a M.t ref
let create () = ref M.empty
let clear t = t := M.empty
let copy t = ref !t
let add t k v = t := M.add k v !t
let remove t k = t := M.remove k !t
let is_empty t = M.is_empty !t
let length t = M.cardinal !t
let mem t k = M.mem k !t
let find t k = M.find k !t
let tryfind t k = M.tryfind k !t
let iter f t = M.iter f !t
let exists f t = M.exists f !t
let filter f t = t := M.filter f !t
let map f t = t := M.map f !t
let mapi f t = t := M.mapi f !t
let fold f t a = M.fold f !t a
let to_list t = M.to_list !t
end
end
module IntIMap = ImperativeMap.Make(struct
type t = int
let compare = (Pervasives.compare : int -> int -> int)
let equal = (Pervasives.( = ) : int -> int -> bool)
end)

Просмотреть файл

@ -0,0 +1,37 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
(** Imperative maps from ordered keys to single values. *)
module ImperativeMap : sig
module type S = sig
type key
type 'a t
val create : unit -> 'a t
val clear : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val is_empty : 'a t -> bool
val length : 'a t -> int
val mem : 'a t -> key -> bool
val find : 'a t -> key -> 'a
val tryfind : 'a t -> key -> 'a option
val iter : (key -> 'a -> unit) -> 'a t -> unit
val exists : (key -> 'a -> bool) -> 'a t -> bool
val filter : (key -> 'a -> bool) -> 'a t -> unit
val map : ('a -> 'a) -> 'a t -> unit
val mapi : (key -> 'a -> 'a) -> 'a t -> unit
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val to_list : 'a t -> (key * 'a) list
end
module Make (Key: OrderedType) : (S with type key = Key.t)
end
module IntIMap : ImperativeMap.S with type key = int

Просмотреть файл

@ -0,0 +1,70 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
open NSMultiMap
module ImperativeMultiMap = struct
module type S = sig
type k
type v
type vs
type t
val create : unit -> t
val clear : t -> unit
val copy : t -> t
val add : t -> k -> v -> unit
val replace : t -> k -> vs -> unit
val remove : t -> k -> unit
val is_empty : t -> bool
val length : t -> int
val mem : t -> k -> bool
val find : t -> k -> vs
val iter : (k -> v -> unit) -> t -> unit
val iter_keys : (k -> vs -> unit) -> t -> unit
val exists : (k -> v -> bool) -> t -> bool
val existsi : k -> (v -> bool) -> t -> bool
val filter : (k -> v -> bool) -> t -> unit
val filteri : k -> (v -> bool) -> t -> unit
val map : (v -> v) -> t -> unit
val mapi : (k -> v -> v) -> t -> unit
val fold : (k -> v -> 'a -> 'a) -> t -> 'a -> 'a
val fold_keys : (k -> vs -> 'a -> 'a) -> t -> 'a -> 'a
val to_list : t -> (k * v) list
end
module Make (Key: OrderedType) (ValSet: Set0) = struct
module M = MultiMap.Make(Key)(ValSet)
type k = M.k
type v = M.v
type vs = M.vs
type t = M.t ref
let create () = ref M.empty
let clear t = t := M.empty
let copy t = ref !t
let add t k v = t := M.add k v !t
let replace t k vs = t := M.replace k vs !t
let remove t k = t := M.remove k !t
let is_empty t = M.is_empty !t
let length t = M.length !t
let mem t k = M.mem k !t
let find t k = M.find k !t
let iter f t = M.iter f !t
let iter_keys f t = M.iter_keys f !t
let exists p t = M.exists p !t
let existsi k p t = M.existsi k p !t
let filter p t = t := M.filter p !t
let filteri k p t = t := M.filteri k p !t
let map f t = t := M.map f !t
let mapi f t = t := M.mapi f !t
let fold f t = M.fold f !t
let fold_keys f t = M.fold_keys f !t
let to_list t = M.to_list !t
end
end

Просмотреть файл

@ -0,0 +1,41 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
(** Imperative maps from ordered keys to multiple values. *)
module ImperativeMultiMap : sig
module type S = sig
type k
type v
type vs
type t
val create : unit -> t
val clear : t -> unit
val copy : t -> t
val add : t -> k -> v -> unit
val replace : t -> k -> vs -> unit
val remove : t -> k -> unit
val is_empty : t -> bool
val length : t -> int
val mem : t -> k -> bool
val find : t -> k -> vs
val iter : (k -> v -> unit) -> t -> unit
val iter_keys : (k -> vs -> unit) -> t -> unit
val exists : (k -> v -> bool) -> t -> bool
val existsi : k -> (v -> bool) -> t -> bool
val filter : (k -> v -> bool) -> t -> unit
val filteri : k -> (v -> bool) -> t -> unit
val map : (v -> v) -> t -> unit
val mapi : (k -> v -> v) -> t -> unit
val fold : (k -> v -> 'a -> 'a) -> t -> 'a -> 'a
val fold_keys : (k -> vs -> 'a -> 'a) -> t -> 'a -> 'a
val to_list : t -> (k * v) list
end
module Make(Key: OrderedType)(ValSet: Set0) :
(S with type k = Key.t and type v = ValSet.elt and type vs = ValSet.t)
end

Просмотреть файл

@ -0,0 +1,48 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
open NSList
open NSImperativeMap
module ImperativeSet = struct
module type S = sig
type v
type t
val create : unit -> t
val clear : t -> unit
val copy : t -> t
val add : t -> v -> unit
val remove : t -> v -> unit
val is_empty : t -> bool
val mem : t -> v -> bool
val iter : (v -> unit) -> t -> unit
val exists : (v -> bool) -> t -> bool
val fold : (v -> 'a -> 'a) -> t -> 'a -> 'a
val to_list : t -> v list
end
module Make(Val: OrderedType) = struct
module M = ImperativeMap.Make(Val)
type v = Val.t
type t = unit M.t
let create = M.create
let clear = M.clear
let copy = M.copy
let add s x = M.add s x ()
let remove = M.remove
let is_empty = M.is_empty
let mem = M.mem
let iter f = M.iter (fun x () -> f x)
let exists p = M.exists (fun x () -> p x)
let fold f = M.fold (fun x () -> f x)
let to_list s = fold List.cons s []
end
end

Просмотреть файл

@ -0,0 +1,28 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
(** Imperative sets of ordered values. *)
module ImperativeSet : sig
module type S = sig
type v
type t
val create : unit -> t
val clear : t -> unit
val copy : t -> t
val add : t -> v -> unit
val remove : t -> v -> unit
val is_empty : t -> bool
val mem : t -> v -> bool
val iter : (v -> unit) -> t -> unit
val exists : (v -> bool) -> t -> bool
val fold : (v -> 'a -> 'a) -> t -> 'a -> 'a
val to_list : t -> v list
end
module Make (Val : OrderedType) : (S with type v = Val.t)
end

229
src/Library/NSIndexedSet.ml Normal file
Просмотреть файл

@ -0,0 +1,229 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
open NSList
open NSSet
open NSMap
module type IndexedType = sig
type t
val equal: t -> t -> bool
val compare: t -> t -> int
type idx
val index : t -> idx
val equal_idx : idx -> idx -> bool
val compare_idx : idx -> idx -> int
end
(** Sets of [IndexedType]s, where an [indexedType] is an [OrderedType] where
each value [v] is associated with an index [index v]. The indices of all
values in an [IndexedSet] are required to be disjoint. *)
module IndexedSet = struct
module type S = sig
include Set.S
type idx
val memi : idx -> t -> bool
val find : idx -> t -> elt
val tryfind : idx -> t -> elt option
val keys : t -> idx list
val fold_keys : (idx -> elt -> 'a -> 'a) -> t -> 'a -> 'a
end
module Make(Val: IndexedType) = struct
module Key = struct
type t = Val.idx
let equal = Val.equal_idx
let compare = Val.compare_idx
end
module M = Map.Make(Key)
type elt = Val.t
type t = Val.t M.t
(* constructors *)
let empty = M.empty
let add v m =
assert(
(try Val.equal v (M.find (Val.index v) m) with Not_found -> true)
|| invalid_arg "IndexedSet: indices must be disjoint"
);
M.add (Val.index v) v m
let adds l m = List.fold add l m
let singleton v = add v empty
let of_list vl = List.fold add vl empty
let remove v s =
let i = Val.index v in
try if Val.equal v (M.find i s) then M.remove i s else s
with Not_found -> s
let union m n =
M.merge (fun _ vo0 vo1 ->
match vo0, vo1 with
| None , None -> None
| Some _ , None -> vo0
| None , Some _ -> vo1
| Some(v0), Some(v1) -> vo0
$> assert(
Val.equal v0 v1
|| invalid_arg "IndexedSet: indices must be disjoint"
)
) m n
let unions = function
| [] -> empty
| m :: [] -> m
| m :: ml -> List.fold union ml m
let inter m n =
M.merge (fun _ vo0 vo1 ->
match vo0, vo1 with
| None , _ -> None
| _ , None -> None
| Some(v0), Some(v1) -> vo0
$> assert(
Val.equal v0 v1
|| invalid_arg "IndexedSet: indices must be disjoint"
)
) m n
let inters = function
| [] -> invalid_arg "IndexedSet.inters: must be non-nil"
| m :: [] -> m
| m :: ml -> List.fold inter ml m
let diff m n =
M.merge (fun _ vo0 vo1 ->
match vo0, vo1 with
| None , _ -> None
| _ , None -> vo0
| Some(v0), Some(v1) -> None
$> assert(
Val.equal v0 v1
|| invalid_arg "IndexedSet: indices must be disjoint"
)
) m n
let fold f m z = M.fold (fun _ v z -> f v z) m z
let fold_keys f m z = M.fold f m z
let mem v s =
try Val.equal v (M.find (Val.index v) s)
with Not_found -> false
let diff_inter_diff s t =
fold (fun a ((s_m_i, i, t_m_i) as acc) ->
if mem a t
then (remove a s_m_i, add a i, remove a t_m_i)
else acc
) s (s, empty, t)
let diff_diff s t =
let s_m_i, _, t_m_i = diff_inter_diff s t in
(s_m_i, t_m_i)
let inter_diff s t =
let _, i, t_m_i = diff_inter_diff s t in
(i, t_m_i)
let map fn s = fold (fun kv t -> add (fn kv) t) s empty
let map_fold _ = failwith "ToDo: IndexedSet.map_fold"
let map_foldi _ = failwith "ToDo: IndexedSet.map_foldi"
let filter p m = M.filter (fun _ v -> p v) m
let partition p m = M.partition (fun _ v -> p v) m
let split v m =
let lt, eq, gt = M.split (Val.index v) m in
(lt, eq <> None, gt)
(* queries / destructors *)
let is_empty = M.is_empty
let compare m n = M.compare Val.compare m n
let equal m n = M.equal Val.equal m n
let subset m n = is_empty (diff m n)
let disjoint _ = failwith "ToDo: IndexedSet.disjoint"
let intersect _ = failwith "ToDo: IndexedSet.intersect"
let iter f m = M.iter (fun _ v -> f v) m
let foldr _ = failwith "ToDo: IndexedSet.foldr"
let foldi _ = failwith "ToDo: IndexedSet.foldi"
let fold_pairs _ = failwith "ToDo: IndexedSet.fold_pairs"
let fold_product fn xs ys =
fold (fun x -> fold (fun y -> fn x y) ys) xs
let fold2 _ = failwith "ToDo: IndexedSet.fold2"
let to_list m = M.fold (fun _ v z -> v :: z) m []
let kfold x fn k = List.kfold fn (to_list x) k
let for_all p m = M.for_all (fun _ v -> p v) m
let exists p m = M.exists (fun _ v -> p v) m
let exists_unique p m = M.exists_unique (fun _ v -> p v) m
let cardinal m = M.cardinal m
let to_array _ = failwith "ToDo: IndexedSet.to_array"
let keys m = M.fold (fun i _ z -> i :: z) m []
let min_elt m = snd (M.min_binding m)
let max_elt m = snd (M.max_binding m)
let next _ = failwith "ToDo: IndexedSet.next"
let choose m = snd (M.choose m)
exception Found of elt
let trychoose s =
try iter (fun kv -> raise (Found(kv))) s ; None
with Found(kv) -> Some(kv)
let extract _ = failwith "ToDo: NSIndexedSet.Lift.extract"
let tryextract _ = failwith "ToDo: NSIndexedSet.Lift.tryextract"
let take _ _ = failwith "ToDo: IndexedSet.take"
let trytake p s =
try Some(take p s) with Not_found -> None
let take_first_pair _ = failwith "ToDo: IndexedSet.take_first_pair"
let the_only _ = failwith "ToDo: IndexedSet.the_only"
type idx = Val.idx
let find i m = M.find i m
let tryfind i m = M.tryfind i m
let memi i m = M.mem i m
let reduce _ = failwith "ToDo: NSIndexedSet.Lift.reduce"
let classify _ = failwith "ToDo: NSIndexedSet.Lift.classify"
end
end

Просмотреть файл

@ -0,0 +1,29 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSSet
module type IndexedType = sig
type t
val equal: t -> t -> bool
val compare: t -> t -> int
type idx
val index : t -> idx
val equal_idx : idx -> idx -> bool
val compare_idx : idx -> idx -> int
end
module IndexedSet : sig
module type S = sig
include Set.S
type idx
val memi : idx -> t -> bool
val find : idx -> t -> elt
val tryfind : idx -> t -> elt option
val keys : t -> idx list
val fold_keys : (idx -> elt -> 'a -> 'a) -> t -> 'a -> 'a
end
module Make(Idx: IndexedType) :
(S with type elt = Idx.t and type idx = Idx.idx)
end

270
src/Library/NSLib.ml Normal file
Просмотреть файл

@ -0,0 +1,270 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(* Non-Standard Library *)
(*============================================================================
Combinators
============================================================================*)
(* Function combinators *)
let id x = x
let const f _ = f
let flip f x y = f y x
let curry f x y = f (x,y)
let uncurry f (x,y) = f x y
let ( &> ) x f = f x ; x
let ( <& ) f x = f x ; x
let ( $> ) x y = y; x
(* Tuple combinators *)
let pair x y = (x,y)
let swap (x,y) = (y,x)
let fst3 (x,_,_) = x
let snd3 (_,y,_) = y
let thd3 (_,_,z) = z
let fst4 (w,_,_,_) = w
let snd4 (_,x,_,_) = x
let thd4 (_,_,y,_) = y
let fth4 (_,_,_,z) = z
let ( *** ) f g (x,y) = (f x, g y)
(* Predicate combinators *)
let ( &&& ) p q x = p x && q x
let ( ||| ) p q x = p x || q x
(* Equality combinators *)
let equal_tup2 equal0 equal1 (x0,x1) (y0,y1) = equal0 x0 y0 && equal1 x1 y1
let equal_tup3 equal0 equal1 equal2 (x0,x1,x2) (y0,y1,y2) = equal0 x0 y0 && equal1 x1 y1 && equal2 x2 y2
let equal_tup4 equal0 equal1 equal2 equal3 (x0,x1,x2,x3) (y0,y1,y2,y3) = equal0 x0 y0 && equal1 x1 y1 && equal2 x2 y2 && equal3 x3 y3
let equal_tup5 equal0 equal1 equal2 equal3 equal4 (x0,x1,x2,x3,x4) (y0,y1,y2,y3,y4) =
equal0 x0 y0 && equal1 x1 y1 && equal2 x2 y2 && equal3 x3 y3 && equal4 x4 y4
let equal_tup6 equal0 equal1 equal2 equal3 equal4 equal5 (x0,x1,x2,x3,x4,x5) (y0,y1,y2,y3,y4,y5) =
equal0 x0 y0 && equal1 x1 y1 && equal2 x2 y2 && equal3 x3 y3 && equal4 x4 y4 && equal5 x5 y5
let equal_tup7 equal0 equal1 equal2 equal3 equal4 equal5 equal6 (x0,x1,x2,x3,x4,x5,x6) (y0,y1,y2,y3,y4,y5,y6) =
equal0 x0 y0 && equal1 x1 y1 && equal2 x2 y2 && equal3 x3 y3 && equal4 x4 y4 && equal5 x5 y5 && equal6 x6 y6
let equal_tup8 equal0 equal1 equal2 equal3 equal4 equal5 equal6 equal7 (x0,x1,x2,x3,x4,x5,x6,x7) (y0,y1,y2,y3,y4,y5,y6,y7) =
equal0 x0 y0 && equal1 x1 y1 && equal2 x2 y2 && equal3 x3 y3 && equal4 x4 y4 && equal5 x5 y5 && equal6 x6 y6 && equal7 x7 y7
let equal_tup9 equal0 equal1 equal2 equal3 equal4 equal5 equal6 equal7 equal8 (x0,x1,x2,x3,x4,x5,x6,x7,x8) (y0,y1,y2,y3,y4,y5,y6,y7,y8) =
equal0 x0 y0 && equal1 x1 y1 && equal2 x2 y2 && equal3 x3 y3 && equal4 x4 y4 && equal5 x5 y5 && equal6 x6 y6 && equal7 x7 y7 && equal8 x8 y8
(* Comparison combinators *)
let compare_tup2 compare0 compare1 (x0,x1) (y0,y1) =
let ord = compare0 x0 y0 in if ord <> 0 then ord else compare1 x1 y1
let compare_tup3 compare0 compare1 compare2 (x0,x1,x2) (y0,y1,y2) =
let ord = compare0 x0 y0 in if ord <> 0 then ord else
let ord = compare1 x1 y1 in if ord <> 0 then ord else
compare2 x2 y2
let compare_tup4 compare0 compare1 compare2 compare3 (x0,x1,x2,x3) (y0,y1,y2,y3) =
let ord = compare0 x0 y0 in if ord <> 0 then ord else
let ord = compare1 x1 y1 in if ord <> 0 then ord else
let ord = compare2 x2 y2 in if ord <> 0 then ord else
compare3 x3 y3
let compare_tup5 compare0 compare1 compare2 compare3 compare4 (x0,x1,x2,x3,x4) (y0,y1,y2,y3,y4) =
let ord = compare0 x0 y0 in if ord <> 0 then ord else
let ord = compare1 x1 y1 in if ord <> 0 then ord else
let ord = compare2 x2 y2 in if ord <> 0 then ord else
let ord = compare3 x3 y3 in if ord <> 0 then ord else
compare4 x4 y4
let compare_tup6 compare0 compare1 compare2 compare3 compare4 compare5 (x0,x1,x2,x3,x4,x5) (y0,y1,y2,y3,y4,y5) =
let ord = compare0 x0 y0 in if ord <> 0 then ord else
let ord = compare1 x1 y1 in if ord <> 0 then ord else
let ord = compare2 x2 y2 in if ord <> 0 then ord else
let ord = compare3 x3 y3 in if ord <> 0 then ord else
let ord = compare4 x4 y4 in if ord <> 0 then ord else
compare5 x5 y5
let compare_tup7 compare0 compare1 compare2 compare3 compare4 compare5 compare6 (x0,x1,x2,x3,x4,x5,x6) (y0,y1,y2,y3,y4,y5,y6) =
let ord = compare0 x0 y0 in if ord <> 0 then ord else
let ord = compare1 x1 y1 in if ord <> 0 then ord else
let ord = compare2 x2 y2 in if ord <> 0 then ord else
let ord = compare3 x3 y3 in if ord <> 0 then ord else
let ord = compare4 x4 y4 in if ord <> 0 then ord else
let ord = compare5 x5 y5 in if ord <> 0 then ord else
compare6 x6 y6
let compare_tup8 compare0 compare1 compare2 compare3 compare4 compare5 compare6 compare7 (x0,x1,x2,x3,x4,x5,x6,x7) (y0,y1,y2,y3,y4,y5,y6,y7) =
let ord = compare0 x0 y0 in if ord <> 0 then ord else
let ord = compare1 x1 y1 in if ord <> 0 then ord else
let ord = compare2 x2 y2 in if ord <> 0 then ord else
let ord = compare3 x3 y3 in if ord <> 0 then ord else
let ord = compare4 x4 y4 in if ord <> 0 then ord else
let ord = compare5 x5 y5 in if ord <> 0 then ord else
let ord = compare6 x6 y6 in if ord <> 0 then ord else
compare7 x7 y7
let compare_tup9 compare0 compare1 compare2 compare3 compare4 compare5 compare6 compare7 compare8 (x0,x1,x2,x3,x4,x5,x6,x7,x8) (y0,y1,y2,y3,y4,y5,y6,y7,y8) =
let ord = compare0 x0 y0 in if ord <> 0 then ord else
let ord = compare1 x1 y1 in if ord <> 0 then ord else
let ord = compare2 x2 y2 in if ord <> 0 then ord else
let ord = compare3 x3 y3 in if ord <> 0 then ord else
let ord = compare4 x4 y4 in if ord <> 0 then ord else
let ord = compare5 x5 y5 in if ord <> 0 then ord else
let ord = compare6 x6 y6 in if ord <> 0 then ord else
let ord = compare7 x7 y7 in if ord <> 0 then ord else
compare8 x8 y8
(* File handling *)
let with_in_bin filename fn =
let chan = open_in_bin filename in
let res = fn chan in
close_in chan ;
res
let with_out_bin filename fn arg =
let chan = open_out_bin filename in
let res = fn chan arg in
close_out chan ;
res
let with_out filename outputter =
let buf = Buffer.create 128 in
let res = outputter buf in
let chan = open_out filename in
Buffer.output_buffer chan buf ;
close_out chan ;
res
(* Formatting *)
type 'a formatter = Format.formatter -> 'a -> unit
type 'a format_str = ('a formatter -> 'a -> unit, Format.formatter, unit) format
let ifbreakf fmt ff =
Format.pp_print_if_newline ff () ;
Format.fprintf ff fmt
let failwithf fmt =
Format.kfprintf (fun _ -> failwith (Format.flush_str_formatter ()))
Format.str_formatter ("@\n@["^^fmt^^"@]@\n")
let invalid_argf fmt =
Format.kfprintf (fun _ -> invalid_arg (Format.flush_str_formatter ()))
Format.str_formatter ("@\n@["^^fmt^^"@]@\n")
exception Nothing_to_fmt
(* Exception handling *)
exception Undef
let try_finally f g =
let res =
try
f ()
with e ->
g () ;
raise e
in
g () ;
res
let finally_try g f =
let res =
try
f ()
with e ->
prerr_endline (Printexc.to_string e) ;
g () ;
raise e
in
g () ;
res
let debug_wrap1 verbose shift fn z =
try fn z with _ -> verbose := !verbose + shift ; fn z
let debug_wrap2 verbose shift fn y z =
try fn y z with _ -> verbose := !verbose + shift ; fn y z
let debug_wrap3 verbose shift fn x y z =
try fn x y z with _ -> verbose := !verbose + shift ; fn x y z
let debug_wrap4 verbose shift fn w x y z =
try fn w x y z with _ -> verbose := !verbose + shift ; fn w x y z
let debug_wrap5 verbose shift fn v w x y z =
try fn v w x y z with _ -> verbose := !verbose + shift ; fn v w x y z
(* Module Types *)
module type EqualityType = sig
type t
val equal: t -> t -> bool
end
module type OrderedType = sig
type t
val equal: t -> t -> bool
val compare: t -> t -> int
end
module type HashedType = sig
type t
val equal: t -> t -> bool
val hash: t -> int
end
module HashedTypeTup2 (H0: HashedType) (H1: HashedType) = struct
type t = H0.t * H1.t
let equal = equal_tup2 H0.equal H1.equal
let hash (x,y) = Hashtbl.hash (H0.hash x, H1.hash y)
end
module type Set0 = sig
type elt
type t
val empty : t
val is_empty : t -> bool
val add : elt -> t -> t
val singleton : elt -> t
val iter : (elt -> unit) -> t -> unit
val map : (elt -> elt) -> t -> t
val fold : (elt -> 'a -> 'a) -> t -> 'a -> 'a
val map_fold : (elt * 'z -> elt * 'z) -> t * 'z -> t * 'z
val kfold : t -> (elt -> ('a->'b) -> 'a->'b) -> ('a->'b) -> 'a->'b
val for_all : (elt -> bool) -> t -> bool
val exists : (elt -> bool) -> t -> bool
val exists_unique : (elt -> bool) -> t -> bool
val filter : (elt -> bool) -> t -> t
val cardinal : t -> int
val of_list : elt list -> t
val to_list : t -> elt list
val choose : t -> elt
val union : t -> t -> t
val diff : t -> t -> t
end
module type Set1 = sig
include Set0
include OrderedType with type t := t
val remove : elt -> t -> t
val diff_inter_diff : t -> t -> t * t * t
end

186
src/Library/NSLib.mli Normal file
Просмотреть файл

@ -0,0 +1,186 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Extensions of the standard library. *)
(*============================================================================
Combinators
============================================================================*)
(** {3 Combinators } *)
(** {4 Function combinators } *)
val id : 'a -> 'a
val const : 'a -> 'b -> 'a
val flip : ('a -> 'b -> 'c) -> 'b -> 'a -> 'c
val curry : ('a * 'b -> 'c) -> 'a -> 'b -> 'c
val uncurry : ('a -> 'b -> 'c) -> 'a * 'b -> 'c
val ( &> ) : 'a -> ('a -> unit) -> 'a
(** [x &> f] applies [f] to [x] and returns [x], left associative. *)
val ( <& ) : ('a -> unit) -> 'a -> 'a
(** [f <& x] applies [f] to [x] and returns [x], left associative. *)
val ( $> ) : 'a -> unit -> 'a
(** Reverse sequential composition, left associative *)
(** {4 Tuple combinators } *)
val pair : 'a -> 'b -> 'a * 'b
val swap : 'a * 'b -> 'b * 'a
val fst3 : ('a * 'b * 'c) -> 'a
val snd3 : ('a * 'b * 'c) -> 'b
val thd3 : ('a * 'b * 'c) -> 'c
val fst4 : ('a * 'b * 'c * 'd) -> 'a
val snd4 : ('a * 'b * 'c * 'd) -> 'b
val thd4 : ('a * 'b * 'c * 'd) -> 'c
val fth4 : ('a * 'b * 'c * 'd) -> 'd
val ( *** ) : ('a -> 'b) -> ('c -> 'd) -> 'a * 'c -> 'b * 'd
(** {4 Predicate combinators } *)
val ( &&& ) : ('a -> bool) -> ('a -> bool) -> 'a -> bool
(** Short-circuit conjunction lifted to predicates, left associative. *)
val ( ||| ) : ('a -> bool) -> ('a -> bool) -> 'a -> bool
(** Short-circuit disjunction lifted to predicates, left associative. *)
(** {4 Equality combinators } *)
val equal_tup2 : ('a->'b->bool)->('c->'d->bool)->'a*'c->'b*'d->bool
val equal_tup3 : ('a->'b->bool)->('c->'d->bool)->('e->'f->bool)->'a*'c*'e->'b*'d*'f->bool
val equal_tup4 : ('a->'b->bool)->('c->'d->bool)->('e->'f->bool)->('g->'h->bool)->'a*'c*'e*'g->'b*'d*'f*'h->bool
val equal_tup5 : ('a->'b->bool)->('c->'d->bool)->('e->'f->bool)->('g->'h->bool)->('i->'j->bool)->'a*'c*'e*'g*'i->'b*'d*'f*'h*'j->bool
val equal_tup6 : ('a->'b->bool)->('c->'d->bool)->('e->'f->bool)->('g->'h->bool)->('i->'j->bool)->('k->'l->bool)->'a*'c*'e*'g*'i*'k->'b*'d*'f*'h*'j*'l->bool
val equal_tup7 : ('a->'b->bool)->('c->'d->bool)->('e->'f->bool)->('g->'h->bool)->('i->'j->bool)->('k->'l->bool)->('m->'n->bool)->'a*'c*'e*'g*'i*'k*'m->'b*'d*'f*'h*'j*'l*'n->bool
val equal_tup8 : ('a->'b->bool)->('c->'d->bool)->('e->'f->bool)->('g->'h->bool)->('i->'j->bool)->('k->'l->bool)->('m->'n->bool)->('o->'p->bool)->'a*'c*'e*'g*'i*'k*'m*'o->'b*'d*'f*'h*'j*'l*'n*'p->bool
val equal_tup9 : ('a->'b->bool)->('c->'d->bool)->('e->'f->bool)->('g->'h->bool)->('i->'j->bool)->('k->'l->bool)->('m->'n->bool)->('o->'p->bool)->('q->'r->bool)->'a*'c*'e*'g*'i*'k*'m*'o*'q->'b*'d*'f*'h*'j*'l*'n*'p*'r->bool
(** {4 Comparison combinators } *)
val compare_tup2 : ('a->'b->int)->('c->'d->int)->'a*'c->'b*'d->int
val compare_tup3 : ('a->'b->int)->('c->'d->int)->('e->'f->int)->'a*'c*'e->'b*'d*'f->int
val compare_tup4 : ('a->'b->int)->('c->'d->int)->('e->'f->int)->('g->'h->int)->'a*'c*'e*'g->'b*'d*'f*'h->int
val compare_tup5 : ('a->'b->int)->('c->'d->int)->('e->'f->int)->('g->'h->int)->('i->'j->int)->'a*'c*'e*'g*'i->'b*'d*'f*'h*'j->int
val compare_tup6 : ('a->'b->int)->('c->'d->int)->('e->'f->int)->('g->'h->int)->('i->'j->int)->('k->'l->int)->'a*'c*'e*'g*'i*'k->'b*'d*'f*'h*'j*'l->int
val compare_tup7 : ('a->'b->int)->('c->'d->int)->('e->'f->int)->('g->'h->int)->('i->'j->int)->('k->'l->int)->('m->'n->int)->'a*'c*'e*'g*'i*'k*'m->'b*'d*'f*'h*'j*'l*'n->int
val compare_tup8 : ('a->'b->int)->('c->'d->int)->('e->'f->int)->('g->'h->int)->('i->'j->int)->('k->'l->int)->('m->'n->int)->('o->'p->int)->'a*'c*'e*'g*'i*'k*'m*'o->'b*'d*'f*'h*'j*'l*'n*'p->int
val compare_tup9 : ('a->'b->int)->('c->'d->int)->('e->'f->int)->('g->'h->int)->('i->'j->int)->('k->'l->int)->('m->'n->int)->('o->'p->int)->('q->'r->int)->'a*'c*'e*'g*'i*'k*'m*'o*'q->'b*'d*'f*'h*'j*'l*'n*'p*'r->int
(** {3 File handling } *)
val with_in_bin : string -> (in_channel -> 'a) -> 'a
val with_out_bin : string -> (out_channel -> 'a -> 'b) -> 'a -> 'b
val with_out : string -> (Buffer.t -> 'a) -> 'a
(** {3 Exception handling } *)
exception Undef
val try_finally : (unit -> 'a) -> (unit -> 'b) -> 'a
val finally_try : (unit -> 'b) -> (unit -> 'a) -> 'a
val debug_wrap1 : int ref -> int -> ('a->'b) -> 'a->'b
val debug_wrap2 : int ref -> int -> ('a->'b->'c) -> 'a->'b->'c
val debug_wrap3 : int ref -> int -> ('a->'b->'c->'d) -> 'a->'b->'c->'d
val debug_wrap4 : int ref -> int -> ('a->'b->'c->'d->'e) -> 'a->'b->'c->'d->'e
val debug_wrap5 : int ref -> int -> ('a->'b->'c->'d->'e->'f) -> 'a->'b->'c->'d->'e->'f
(*============================================================================
Formatting
============================================================================*)
(** {3 Formatting } *)
exception Nothing_to_fmt
(** Type of functions for formatting ['a] values. *)
type 'a formatter = Format.formatter -> 'a -> unit
(** Type of format strings that make a single call to an ['a formatter]. *)
type 'a format_str = ('a formatter -> 'a -> unit, Format.formatter, unit) format
val ifbreakf : ('a, Format.formatter, unit) format -> Format.formatter -> 'a
val failwithf : ('a, Format.formatter, unit, 'b) format4 -> 'a
val invalid_argf : ('a, Format.formatter, unit, 'b) format4 -> 'a
(*============================================================================
Collections
============================================================================*)
(** {2 Collections } *)
(** Types equipped with an equivalence relation. *)
module type EqualityType = sig
type t
val equal: t -> t -> bool
end
(** Types equipped with a total order. *)
module type OrderedType = sig
type t
val equal: t -> t -> bool
val compare: t -> t -> int
end
(** Types equipped with a hash function. *)
module type HashedType = sig
type t
val equal: t -> t -> bool
val hash: t -> int
end
(** Pairs of types equipped with a hash function. *)
module HashedTypeTup2 (H0: HashedType) (H1: HashedType)
: (HashedType with type t = H0.t * H1.t)
(** Sets of unordered values. *)
module type Set0 = sig
type elt
type t
val empty : t
val is_empty : t -> bool
val add : elt -> t -> t
val singleton : elt -> t
val iter : (elt -> unit) -> t -> unit
val map : (elt -> elt) -> t -> t
val fold : (elt -> 'a -> 'a) -> t -> 'a -> 'a
val map_fold : (elt * 'z -> elt * 'z) -> t * 'z -> t * 'z
val kfold : t -> (elt -> ('a->'b) -> 'a->'b) -> ('a->'b) -> 'a->'b
val for_all : (elt -> bool) -> t -> bool
val exists : (elt -> bool) -> t -> bool
val exists_unique : (elt -> bool) -> t -> bool
val filter : (elt -> bool) -> t -> t
val cardinal : t -> int
val of_list : elt list -> t
val to_list : t -> elt list
val choose : t -> elt
val union : t -> t -> t
val diff : t -> t -> t
end
module type Set1 = sig
include Set0
include OrderedType with type t := t
val remove : elt -> t -> t
val diff_inter_diff : t -> t -> t * t * t
end

321
src/Library/NSList.ml Normal file
Просмотреть файл

@ -0,0 +1,321 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
module List = struct
include List
let cons x xs = x :: xs
let tryfind pred lst = try Some(find pred lst) with Not_found -> None
let rec no_duplicates = function
| [] -> true
| x::xs -> if (List.mem x xs) then false else (no_duplicates xs)
let rec iteri_aux fn i = function
| [] -> ()
| x::xs -> fn i x; iteri_aux fn (i+1) xs
let iteri fn xs = iteri_aux fn 0 xs
let fold fn l a = fold_left (fun a x -> fn x a) a l
let foldi fn s z = snd (fold (fun x (i,z) -> (i+1, fn i x z)) s (0, z))
let fold2 fn xs ys a = fold_left2 (fun a x y -> fn x y a) a xs ys
let fold3 fn =
let rec fold3_fn xs ys zs a =
match xs, ys, zs with
| [], [], [] -> a
| x::xs, y::ys, z::zs -> fold3_fn xs ys zs (fn x y z a)
| _ -> invalid_arg "List.fold3: expected equal-length lists"
in
fold3_fn
let mapi fn xs = rev (snd (fold (fun x (i,z) -> (i+1, fn i x :: z)) xs (0,[])))
let map3 fn =
let rec map3 xs ys zs =
match xs, ys, zs with
| [], [], [] -> []
| x::xs, y::ys, z::zs -> fn x y z :: map3 xs ys zs
| _ -> invalid_arg "List.map3: expected equal-length lists"
in
map3
let map_append fn xs ys = fold (fun x ys -> fn x :: ys) xs ys
let map_to_array fn xs =
match xs with
| [] -> [||]
| hd::tl as xs ->
let a = Array.make (List.length xs) (fn hd) in
let rec set i = function
| [] -> a
| hd::tl -> Array.set a i (fn hd); set (i+1) tl
in
set 1 tl
let map_fold fn (xs,z) =
fold_right (fun x (ys,z) ->
let y, z = fn (x,z) in
(y::ys, z)
) xs ([],z)
let reduce fn = function
| [] -> invalid_arg "List.reduce"
| x::xs -> fold fn xs x
let rec kfold fn xs k a =
match xs with
| [] -> k a
| x::xs -> fn x (kfold fn xs k) a
let rec kfold2 fn xs ys k a =
match xs, ys with
| [], [] -> k a
| x::xs, y::ys -> fn x y (kfold2 fn xs ys k) a
| _ -> invalid_arg "List.kfold2: expected equal-length lists"
let rec kfold3 fn xs ys zs k a =
match xs, ys, zs with
| [], [], [] -> k a
| x::xs, y::ys, z::zs -> fn x y z (kfold3 fn xs ys zs k) a
| _ -> invalid_arg "List.kfold3: expected equal-length lists"
let rec fold_pairs fn xs a =
match xs with
| [] -> a
| x::xs -> fold (fn x) xs (fold_pairs fn xs a)
let rec kfold_pairs fn k xs a =
match xs with
| [] -> k a
| x::xs -> kfold (fn x) xs (kfold_pairs fn k xs) a
let rec fold_product fn xs ys a =
match xs with
| [] -> a
| x::xs -> fold (fn x) ys (fold_product fn xs ys a)
let rec kfold_product xs ys fn k a =
match xs with
| [] -> k a
| x::xs -> kfold (fn x) ys (kfold_product xs ys fn k) a
let rec prefixes = function
| [] -> [[]]
| x :: xs -> [] :: map (fun l -> x :: l) (prefixes xs)
let rec infixes = function
| [] -> [[]]
| x :: xs -> fold (fun l z -> (x :: l) :: z) (prefixes xs) (infixes xs)
let rec powerlist = function
| [] -> [[]]
| x :: xs ->
let pow_xs = powerlist xs in
fold (fun l z -> (x :: l) :: z) pow_xs pow_xs
let combs fn xs ys =
let rec loop1 zs xs = function
| [] -> [[]]
| y :: ys ->
let rec loop2 zs a = function
| [] -> a
| x :: xs ->
loop2
(x :: zs)
(fold (fun comb a -> (fn x y comb) :: a) (loop1 zs xs ys) a)
xs
in loop2 [] (loop2 [] [] zs) xs
in loop1 [] xs ys
let rec permutations fn xs =
let rec loop zs xs ps =
match xs with
| [] -> ps
| x :: xs ->
loop (x :: zs) xs
(List.fold_left (fn x) ps (permutations fn (zs @ xs)))
in
if xs = [] then [[]] else loop [] xs []
let fin_funs xs ys =
List.map (List.combine xs) (permutations (fun x ps p -> (x :: p) :: ps) ys)
let map_partial fn xs =
List.fold_right (fun x maybe_ys ->
match maybe_ys with
| None -> None
| Some ys ->
match fn x with
| None -> None
| Some y -> Some (y::ys)
) xs (Some [])
let classify fn xs =
let rec classify_one x = function
| xs :: xss when fn x (List.hd xs) -> (x :: xs) :: xss
| xs :: xss -> xs :: (classify_one x xss)
| [] -> [[x]]
in
fold classify_one xs []
let divide fn ys =
let rec divide_ xss ys =
match xss, ys with
| xs :: xss, y :: ys when fn y (List.hd xs) -> divide_ ((y :: xs) :: xss) ys
| xss, y :: ys -> divide_ ([y] :: xss) ys
| xss, [] -> List.rev_map List.rev xss
in
divide_ [] ys
let rec range i j = if i <= j then i::(range (i+1) j) else []
let rec replicate n x =
if n <= 0 then [] else x :: replicate (n-1) x
let inter xs ys = List.filter (fun x -> List.mem x ys) xs
let union xs ys = fold (fun x us -> if List.mem x us then us else x::us) xs ys
let diff xs rs = find_all (fun x -> not (List.mem x rs)) xs
let rec take_ p ys xs =
match xs with
| x :: xs ->
if p x then
(x, rev_append ys xs)
else
take_ p (x :: ys) xs
| [] ->
raise Not_found
let take p xs =
take_ p [] xs
let exists_unique p xs =
let module M = struct exception Found end in
try
fold (fun x found ->
if found
then not (p x) || raise M.Found
else p x
) xs false
with M.Found -> false
let rec equal fn xs ys =
if xs == ys then true else
match xs,ys with
| [], [] -> true
| [], _::_
| _::_, [] -> false
| x::xs, y::ys -> fn x y && equal fn xs ys
let rec compare fn xs ys =
if xs == ys then 0 else
match xs,ys with
| [], _::_ -> -1
| [], [] -> 0
| _::_, [] -> 1
| x::xs, y::ys -> compare_tup2 fn (compare fn) (x,xs) (y,ys)
let rec compare_lex fn xs ys =
if xs == ys then 0 else
match xs,ys with
| [], _::_ -> -1
| [], [] -> 0
| _::_, [] -> 1
| x::xs, y::ys -> compare_tup2 (compare_lex fn) fn (xs,x) (ys,y)
let compare_sorted cmp xs ys =
if xs == ys then 0 else
let rec loop xs ys = match xs,ys with
| [], _::_ -> -1
| [], [] -> 0
| _::_, [] -> 1
| x::xs, y::ys -> compare_tup2 cmp loop (x,xs) (y,ys)
in
loop (fast_sort cmp xs) (fast_sort cmp ys)
let fmt sep fn ff xs =
let rec aux ff = function
| [] -> ()
| [x] -> (try fn ff x with Nothing_to_fmt -> ())
| x::xs ->
try Format.fprintf ff "%a%( fmt %)%a" fn x sep aux xs
with Nothing_to_fmt -> aux ff xs
in
aux ff xs
let fmtt sep ff xs =
let rec aux ff = function
| [] -> ()
| [x] -> (try x ff with Nothing_to_fmt -> ())
| x::xs ->
try Format.fprintf ff "%t%( fmt %)%a" x sep aux xs
with Nothing_to_fmt -> aux ff xs
in
aux ff xs
module Set (Elt: sig type t end) = struct
type elt = Elt.t
type t = elt list
let empty = []
let is_empty s = s = []
let add e s = e :: s
let singleton e = [e]
let iter = iter
let map = map
let fold = fold
let map_fold = map_fold
let kfold fn s z = kfold s fn z
let for_all = for_all
let exists = exists
let exists_unique = exists_unique
let filter = filter
let cardinal = length
let of_list s = s
let to_list s = s
let choose = function x::_ -> x | [] -> raise Not_found
let union = rev_append
let diff = diff
end
module SetOrd (Elt: OrderedType) = struct
include Set(Elt)
let equal x y = equal Elt.equal x y
let compare x y = compare Elt.compare x y
let rec remove x = function
| [] -> []
| y::ys when Elt.equal x y -> ys
| y::ys -> y :: remove x ys
let diff_inter_diff xs ys =
let xs = fast_sort Elt.compare xs
and ys = fast_sort Elt.compare ys in
let rec did xxs yys =
match xxs, yys with
| [], ys -> ([], [], ys)
| xs, [] -> (xs, [], [])
| x::xs, y::ys ->
let o = Elt.compare x y in
if o = 0 then
let xs_ys, i, ys_xs = did xs ys in
(xs_ys, x::i, ys_xs)
else if o < 0 then
let xs_yys, i, yys_xs = did xs yys in
(x::xs_yys, i, yys_xs)
else
let xxs_ys, i, ys_xxs = did xxs ys in
(xxs_ys, i, y::ys_xxs)
in
did xs ys
end
end

130
src/Library/NSList.mli Normal file
Просмотреть файл

@ -0,0 +1,130 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
(** Operations on ['a list]. See also standard
{{:file:../../../doc/ocaml%20manual/libref/List.html}List}. *)
module List : sig
include module type of List
val cons : 'a -> 'a list -> 'a list
val tryfind : ('a -> bool) -> 'a list -> 'a option
val no_duplicates : 'a list -> bool
val exists_unique : ('a -> bool) -> 'a list -> bool
val iteri : (int -> 'a -> unit) -> 'a list -> unit
val mapi : (int -> 'a -> 'b) -> 'a list -> 'b list
val fold : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b
(** Fold over the elements in the list.
Eg: [fold f \[a0;a1;;aN\] z] = [f aN ((f a1 (f a0 z)))] *)
val foldi : (int -> 'a -> 'b -> 'b) -> 'a list -> 'b -> 'b
val map_append : ('a -> 'b) -> 'a list -> 'b list -> 'b list
val map_to_array : ('a -> 'b) -> 'a list -> 'b array
val reduce : ('a -> 'a -> 'a) -> 'a list -> 'a
val map3 : ('a -> 'b -> 'c -> 'z) -> 'a list -> 'b list -> 'c list -> 'z list
val fold2 : ('a -> 'b -> 'z -> 'z) -> 'a list -> 'b list -> 'z -> 'z
(** Fold over the corresponding elements in the two lists. The lists must
have the same length.
Eg: [fold2 f \[a0;a1;;aN\] \[b0;b1;;bN\] z]
= [f aN bN ((f a1 b1 (f a0 b0 z)))] *)
val fold3 : ('a -> 'b -> 'c -> 'z -> 'z) -> 'a list -> 'b list -> 'c list -> 'z -> 'z
val equal : ('a -> 'a -> bool) -> 'a list -> 'a list -> bool
val compare : ('a -> 'a -> int) -> 'a list -> 'a list -> int
val compare_lex : ('a -> 'a -> int) -> 'a list -> 'a list -> int
val compare_sorted : ('a -> 'a -> int) -> 'a list -> 'a list -> int
val kfold : ('a -> ('b->'c) -> 'b->'c) -> 'a list -> ('b->'c) -> 'b->'c
(** e.g. [kfold (fun x k () -> (-x) :: k ()) [1;2;3;4] (fun()-> []) ()] =
[[-1; -2; -3; -4]] *)
val kfold2 : ('a -> 'b -> ('c->'d) -> 'c->'d) -> 'a list -> 'b list -> ('c->'d) -> 'c->'d
val kfold3 : ('a -> 'b -> 'c -> ('d->'e) -> 'd->'e) -> 'a list -> 'b list -> 'c list -> ('d->'e) -> 'd->'e
val fold_pairs : ('a -> 'a -> 'b -> 'b) -> 'a list -> 'b -> 'b
(** e.g. [fold_pairs (fun x y l -> (x,y)::l) [1;2;3;4] []] =
[[(1, 4); (1, 3); (1, 2); (2, 4); (2, 3); (3, 4)]] *)
val kfold_pairs :
('a -> 'a -> ('b->'c) -> 'b->'c) -> ('b->'c) -> 'a list -> 'b->'c
(** e.g. [kfold_pairs (fun x y k () -> (x,y)::k()) (fun()->[]) [1;2;3;4] ()] =
[[(1, 2); (1, 3); (1, 4); (2, 3); (2, 4); (3, 4)]] *)
val fold_product : ('a -> 'b -> 'c -> 'c) -> 'a list -> 'b list -> 'c -> 'c
(** e.g. [fold_product (fun x y l -> (x,y)::l) [1;2;3] [4;5] []] =
[[(1, 5); (1, 4); (2, 5); (2, 4); (3, 5); (3, 4)]] *)
val kfold_product :
'a list -> 'b list -> ('a -> 'b -> ('c->'d) -> 'c->'d) -> ('c->'d) -> 'c->'d
(** e.g.
[kfold_product (fun x y k () -> (x,y)::k()) (fun()->[]) [1;2;3] [4;5] ()]
= [[(1, 5); (1, 4); (2, 5); (2, 4); (3, 5); (3, 4)]] *)
val prefixes : 'a list -> 'a list list
(** e.g. [prefixes [1;2;3]] = [[]; [1]; [1;2]; [1;2;3]] *)
val infixes : 'a list -> 'a list list
(** e.g. [infixes [1;2;3]] = [[[1;2;3]; [1;2]; [1]; [2;3]; [2]; [3]; []]] *)
val powerlist : 'a list -> 'a list list
(** e.g. [powerlist [1;2;3]] =
[[1;2;3]; [1;2]; [1;3]; [1]; [2;3]; [2]; [3]; []] *)
val combs :
('a -> 'b -> 'c list -> 'c list) -> 'a list -> 'b list -> 'c list list
(** e.g., [combs (fun x y l -> (x,y)::l) [1;2;3] [4;5]] =
[[[(3, 4); (2, 5)]; [(3, 4); (1, 5)]; [(2, 4); (1, 5)]; [(2, 4); (3, 5)];
[(1, 4); (2, 5)]; [(1, 4); (3, 5)]]] *)
val permutations :
('a -> 'b list list -> 'b list -> 'b list list) -> 'a list -> 'b list list
(** [permutations xs] returns all the permutations of [xs], e.g.
[permutations (fun x ps p -> (x :: p) :: ps) [1;2;3]] =
[[3; 2; 1]; [3; 1; 2]; [2; 1; 3]; [2; 3; 1]; [1; 2; 3]; [1; 3; 2]] *)
val fin_funs : 'a list -> 'b list -> ('a * 'b) list list
(** [fin_funs [1;2;3] [4;5;6]] =
[[(1, 4); (2, 5); (3, 6)]; [(1, 4); (2, 6); (3, 5)];
[(1, 5); (2, 4); (3, 6)]; [(1, 5); (2, 6); (3, 4)];
[(1, 6); (2, 4); (3, 5)]; [(1, 6); (2, 5); (3, 4)]] *)
val map_partial : ('a -> 'b option) -> 'a list -> 'b list option
val classify : ('a -> 'a -> bool) -> 'a list -> 'a list list
(** [classify pred xs] partitions [xs] into classes equivalent modulo
[pred]. Guarantees that no empty classes are returned. *)
val divide : ('a -> 'a -> bool) -> 'a list -> 'a list list
(** [divide pred xs] divides [xs] into the contiguous sublists of elements equivalent modulo [pred]. Guarantees
that no empty sublists are returned. *)
val range : int -> int -> int list
val replicate : int -> 'a -> 'a list
val inter : 'a list -> 'a list -> 'a list
val union : 'a list -> 'a list -> 'a list
val diff : 'a list -> 'a list -> 'a list
val take : ('a -> bool) -> 'a list -> 'a * 'a list
(** [take p l] returns the first element of [l] that satisfies [p], and the remainder of [l] *)
val fmt :
(unit,Format.formatter,unit)format -> 'a formatter -> 'a list formatter
val fmtt :
('a, 'b, 'c, 'd, 'd, 'a) format6 ->
Format.formatter -> (Format.formatter -> unit) list -> unit
module Set (Elt: sig type t end) :
(Set0 with type elt = Elt.t and type t = Elt.t list)
module SetOrd (Elt: OrderedType) :
(Set1 with type elt = Elt.t and type t = Elt.t list)
end

188
src/Library/NSMap.ml Normal file
Просмотреть файл

@ -0,0 +1,188 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
open NSList
open NSSet
module Map = struct
module type S = sig
include Map.S
val tryfind : key -> 'a t -> 'a option
val exists_unique : (key -> 'a -> bool) -> 'a t -> bool
val extract : key -> 'a t -> 'a * 'a t
val modify : ('a -> 'a) -> key -> 'a t -> 'a t
val modify_add : ('a -> 'a) -> key -> 'a -> 'a t -> 'a t
val pop : 'a t -> (key * 'a) * 'a t
val to_list : 'a t -> (key * 'a) list
val map_to_array : (key -> 'a -> 'b) -> 'a t -> 'b array
(** Sets of Key-Val pairs where the keys of all elements are distinct. *)
module Set (Val: OrderedType) : (Set.S with type elt = key * Val.t
and type t = Val.t t)
end
module Make (Key: OrderedType) = struct
include Map.Make(Key)
let tryfind k m = try Some(find k m) with Not_found -> None
let exists_unique _ = failwith "Map.exists_unique unimplemented"
(* Note: extract, modify*, pop should be implemented with one search *)
let extract k m = (find k m, remove k m)
let modify fn k m = add k (fn (find k m)) m
let modify_add fn k v m = try modify fn k m with Not_found -> add k v m
let pop m =
let k,v = choose m in
((k,v), remove k m)
let to_list = bindings
let map_to_array fn m =
let len = cardinal m in
if len = 0 then [||]
else
let (k,v),m = pop m in
let a = Array.make len (fn k v) in
let i = ref 0 in
iter (fun k v -> incr i ; Array.set a !i (fn k v)) m ;
a
(*============================================================================
Map.Set
============================================================================*)
module Set (Val: OrderedType) = struct
type elt = Key.t * Val.t
type _t = Val.t t
type t = _t
(* constructors *)
let empty = empty
let add (k,v) s =
match tryfind k s with
| None -> add k v s
| Some(w) when Val.equal v w -> s
| Some _ ->
invalid_arg "Map.Set.add: element with same key already exists"
let adds l s = List.fold add l s
let singleton kv = add kv empty
let of_list l = List.fold_right add l empty
let remove (k,v) s =
match tryfind k s with
| Some(w) when Val.equal v w -> remove k s
| _ -> s
let fold fn s a = fold (fun x y -> fn (x,y)) s a
(* Warning: union is ill-defined *)
let union = fold add
let unions _ = failwith "Map.unions unimplemented"
let inter _ = failwith "Map.inter unimplemented"
let inters _ = failwith "Map.inters unimplemented"
let diff _ = failwith "Map.diff unimplemented"
let mem (k,v) s =
match tryfind k s with
| Some(w) -> Val.equal v w
| None -> false
let diff_inter_diff s t =
fold (fun a ((s_m_i,i,t_m_i) as acc) ->
if mem a t
then (remove a s_m_i, add a i, remove a t_m_i)
else acc
) s (s,empty,t)
let diff_diff _ = failwith "Map.diff_diff unimplemented"
let inter_diff _ = failwith "Map.inter_diff unimplemented"
let map fn s = fold (fun kv t -> add (fn kv) t) s empty
let map_fold _ = failwith "Map.map_fold unimplemented"
let map_foldi _ = failwith "Map.map_foldi unimplemented"
let filter _ = failwith "Map.filter unimplemented"
let partition _ = failwith "Map.partition unimplemented"
let split _ = failwith "Map.split unimplemented"
(* queries / destructors *)
let is_empty = is_empty
let compare = compare Val.compare
let equal = equal Val.equal
let subset s t = fold (fun kv a -> a && mem kv t) s true
let disjoint _ = failwith "Map.disjoint unimplemented"
let intersect _ = failwith "Map.intersect unimplemented"
let iter fn s = iter (fun k v -> fn (k,v)) s
let foldr _ = failwith "Map.foldr unimplemented"
let foldi _ = failwith "Map.foldi unimplemented"
let fold_pairs _ = failwith "Map.fold_pairs unimplemented"
let fold_product fn xs ys =
fold (fun x -> fold (fun y -> fn x y) ys) xs
let fold2 _ = failwith "Map.fold2 unimplemented"
let kfold x fn k = List.kfold fn (to_list x) k
let for_all _ = failwith "Map.for_all unimplemented"
let exists _ = failwith "Map.exists unimplemented"
let exists_unique _ = failwith "Map.exists_unique unimplemented"
let cardinal = cardinal
let to_list = bindings
let to_array _ = failwith "Map.to_array unimplemented"
let min_elt _ = failwith "Map.min_elt unimplemented"
let max_elt _ = failwith "Map.max_elt unimplemented"
let next _ = failwith "Map.next unimplemented"
let choose = choose
exception Found of elt
let trychoose s =
try iter (fun kv -> raise (Found(kv))) s ; None
with Found(kv) -> Some(kv)
let extract _ = failwith "NSMap.Lift.extract unimplemented"
let tryextract _ = failwith "NSMap.Lift.tryextract unimplemented"
let take _ _ = failwith "Map.take unimplemented"
let trytake p s =
try Some(take p s) with Not_found -> None
let take_first_pair _ = failwith "Map.take_first_pair unimplemented"
let the_only _ = failwith "Map.the_only unimplemented"
let reduce _ = failwith "NSMap.Lift.reduce unimplemented"
let classify _ = failwith "NSMap.Lift.classify unimplemented"
end
end
end
module IntMap = Map.Make(struct
type t = int
let compare = (Pervasives.compare : int -> int -> int)
let equal = (Pervasives.( = ) : int -> int -> bool)
end)

34
src/Library/NSMap.mli Normal file
Просмотреть файл

@ -0,0 +1,34 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
open NSSet
(** Maps from ordered keys to single values. See also standard
{{:file:../../../doc/ocaml%20manual/libref/Map.html}Map}. *)
module Map : sig
module type S = sig
include Map.S
val tryfind : key -> 'a t -> 'a option
val exists_unique : (key -> 'a -> bool) -> 'a t -> bool
val extract : key -> 'a t -> 'a * 'a t
val modify : ('a -> 'a) -> key -> 'a t -> 'a t
val modify_add : ('a -> 'a) -> key -> 'a -> 'a t -> 'a t
val pop : 'a t -> (key * 'a) * 'a t
val to_list : 'a t -> (key * 'a) list
val map_to_array : (key -> 'a -> 'b) -> 'a t -> 'b array
(*============================================================================
Map.Set
============================================================================*)
(** Sets of Key-Val pairs where the keys of all elements are distinct. *)
module Set (Val: OrderedType) : (Set.S with type elt = key * Val.t
and type t = Val.t t)
end
module Make (Key: OrderedType) : S with type key = Key.t
end
module IntMap : Map.S with type key = int

Просмотреть файл

@ -0,0 +1,63 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
open NSMultiSet
open NSMultiIndexedSet
open NSMultiMap
open NSList
module MultiIndexedMultiSet = struct
module type S = sig
include MultiSet.S
type elts
type idx
val memi : idx -> t -> bool
val find : idx -> t -> elts
val keys : t -> idx list
end
module Make (Idx: MultiIndexedType) (EltSet: Set0 with type elt = Idx.t) = struct
module Key = struct
type t = Idx.idx
let equal = Idx.equal_idx
let compare = Idx.compare_idx
end
module M = MultiMap.Make (Key) (EltSet)
type idx = Idx.idx
type elt = Idx.t
type elts = EltSet.t
type t = M.t
let empty = M.empty
let is_empty s = M.is_empty s
let add e s = M.add (Idx.index e) e s
let singleton e = add e empty
let iter fn s = M.iter (fun _ e -> fn e) s
let map fn s = M.map fn s
let fold fn s z = M.fold (fun _ e z -> fn e z) s z
let map_fold _ _ = failwith "ToDo: MultiIndexedMultiSet.map_fold"
let kfold _ _ _ _ = failwith "ToDo: MultiIndexedMultiSet.kfold"
let for_all _ _ = failwith "ToDo: MultiIndexedMultiSet.for_all"
let exists _ _ = failwith "ToDo: MultiIndexedMultiSet.exists"
let exists_unique _ _ = failwith "ToDo: MultiIndexedMultiSet.exists_unique"
let filter fn s = M.filter (fun _ e -> fn e) s
let cardinal s = M.length s
let of_list es = List.fold add es empty
let to_list s = fold List.cons s []
let choose _ = failwith "ToDo: MultiIndexedMultiSet.choose"
let union _ _ = failwith "ToDo: MultiIndexedMultiSet.union"
let diff _ _ = failwith "ToDo: MultiIndexedMultiSet.diff"
let equal _ _ = failwith "ToDo: MultiIndexedMultiSet.equal"
let compare _ _ = failwith "ToDo: MultiIndexedMultiSet.compare"
let remove _ _ = failwith "ToDo: MultiIndexedMultiSet.remove"
let foldm fn s z = M.fold_keys (fun _ es z -> fn (EltSet.choose es) (EltSet.cardinal es) z) s z
let fold_pairs _ _ _ = failwith "ToDo: MultiIndexedMultiSet.fold_pairs"
let memi i s = M.mem i s
let find i s = M.find i s
let keys s = M.fold_keys (fun i _ is -> i :: is) s []
end
end

Просмотреть файл

@ -0,0 +1,20 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
open NSMultiSet
open NSMultiIndexedSet
module MultiIndexedMultiSet : sig
module type S = sig
include MultiSet.S
type idx
type elts
val memi : idx -> t -> bool
val find : idx -> t -> elts
val keys : t -> idx list
end
module Make (Idx: MultiIndexedType) (EltSet: Set0 with type elt = Idx.t) :
(S with type idx = Idx.idx and type elt = Idx.t and type elts = EltSet.t)
end

Просмотреть файл

@ -0,0 +1,258 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
open NSList
open NSSet
open NSMap
module type MultiIndexedType = sig
type t
val equal: t -> t -> bool
val compare: t -> t -> int
type idx
val index : t -> idx
val indices : t -> idx list
val equal_idx : idx -> idx -> bool
val compare_idx : idx -> idx -> int
end
(** Sets of [MultiIndexedType]s, where a [MultiIndexedType] is an
[OrderedType] where each value [v] is associated with some indices
[indices v], one of which [index v] is primary. The [indices] of all
values in a [MultiIndexedSet] are required to be disjoint. *)
module MultiIndexedSet = struct
module type S = sig
include Set.S
type idx
val memi : idx -> t -> bool
val find : idx -> t -> elt
val tryfind : idx -> t -> elt option
val keys : t -> idx list
end
module Make(Val: MultiIndexedType) = struct
module Key = struct
type t = Val.idx
let equal = Val.equal_idx
let compare = Val.compare_idx
end
module M = Map.Make(Key)
type elt = Val.t
type t = Val.t M.t
(* constructors *)
let empty = M.empty
let add v m =
List.fold_left (fun m i ->
assert(
(try Val.equal v (M.find i m) with Not_found -> true)
|| invalid_arg "MultiIndexedSet: indices must be disjoint"
);
M.add i v m
) m (Val.indices v)
let adds l s = List.fold add l s
let singleton v = add v empty
let of_list vl = List.fold add vl empty
let remove v s =
List.fold_left (fun s i ->
try if Val.equal v (M.find i s) then M.remove i s else s
with Not_found -> s
) s (Val.indices v)
let union m n =
M.merge (fun _ vo0 vo1 ->
match vo0, vo1 with
| None , None -> None
| Some _ , None -> vo0
| None , Some _ -> vo1
| Some(v0), Some(v1) -> vo0
$> assert(
Val.equal v0 v1
|| invalid_arg "MultiIndexedSet: indices must be disjoint"
)
) m n
let unions = function
| [] -> empty
| m :: [] -> m
| m :: ml -> List.fold union ml m
let inter m n =
M.merge (fun _ vo0 vo1 ->
match vo0, vo1 with
| None , _ -> None
| _ , None -> None
| Some(v0), Some(v1) -> vo0
$> assert(
Val.equal v0 v1
|| invalid_arg "MultiIndexedSet: indices must be disjoint"
)
) m n
let inters = function
| [] -> invalid_arg "MultiIndexedSet.inters: must be non-nil"
| m :: [] -> m
| m :: ml -> List.fold inter ml m
let diff m n =
M.merge (fun _ vo0 vo1 ->
match vo0, vo1 with
| None , _ -> None
| _ , None -> vo0
| Some(v0), Some(v1) -> None
$> assert(
Val.equal v0 v1
|| invalid_arg "MultiIndexedSet: indices must be disjoint"
)
) m n
let fold f m z =
M.fold (fun i v z ->
if Val.equal_idx i (Val.index v) then f v z else z
) m z
let mem v s =
try Val.equal v (M.find (Val.index v) s)
with Not_found -> false
let diff_inter_diff s t =
fold (fun a ((s_m_i, i, t_m_i) as acc) ->
if mem a t
then (remove a s_m_i, add a i, remove a t_m_i)
else acc
) s (s, empty, t)
let diff_diff s t =
let s_m_i, _, t_m_i = diff_inter_diff s t in
(s_m_i, t_m_i)
let inter_diff s t =
let _, i, t_m_i = diff_inter_diff s t in
(i, t_m_i)
let map fn s = fold (fun kv t -> add (fn kv) t) s empty
let map_fold _ = failwith "ToDo: MultiIndexedSet.map_fold"
let map_foldi _ = failwith "ToDo: MultiIndexedSet.map_foldi"
let filter p m = M.filter (fun _ v -> p v) m
let partition p m = M.partition (fun _ v -> p v) m
let split v m =
let lt, eq, gt = M.split (Val.index v) m in
(lt, eq <> None, gt)
(* queries / destructors *)
let is_empty = M.is_empty
let compare m n = M.compare Val.compare m n
let equal m n = M.equal Val.equal m n
let subset m n = is_empty (diff m n)
let disjoint _ = failwith "ToDo: MultiIndexedSet.disjoint"
let intersect _ = failwith "ToDo: MultiIndexedSet.intersect"
let iter f m =
M.iter (fun i v ->
if Val.equal_idx i (Val.index v) then f v
) m
let foldr _ = failwith "ToDo: MultiIndexedSet.foldr"
let foldi _ = failwith "ToDo: MultiIndexedSet.foldi"
let fold_pairs _ = failwith "ToDo: MultiIndexedSet.fold_pairs"
let fold_product fn xs ys =
fold (fun x -> fold (fun y -> fn x y) ys) xs
let fold2 _ = failwith "ToDo: MultiIndexedSet.fold2"
let to_list m =
M.fold (fun i v z ->
if Val.equal_idx i (Val.index v) then v :: z else z
) m []
let kfold x fn k = List.kfold fn (to_list x) k
let for_all p m =
M.for_all (fun i v ->
not (Val.equal_idx i (Val.index v)) || p v
) m
let exists p m =
M.exists (fun i v ->
Val.equal_idx i (Val.index v) && p v
) m
let exists_unique p m =
M.exists_unique (fun i v ->
Val.equal_idx i (Val.index v) && p v
) m
let cardinal m =
M.fold (fun i v c ->
if Val.equal_idx i (Val.index v) then succ c else c
) m 0
let to_array _ = failwith "ToDo: MultiIndexedSet.to_array"
let keys m = M.fold (fun i _ z -> i :: z) m []
let min_elt m = snd (M.min_binding m)
let max_elt m = snd (M.max_binding m)
let next _ = failwith "ToDo: MultiIndexedSet.next"
let choose m = snd (M.choose m)
exception Found of elt
let trychoose s =
try iter (fun kv -> raise (Found(kv))) s ; None
with Found(kv) -> Some(kv)
let extract _ = failwith "ToDo: NSMultiIndexedSet.Lift.extract"
let tryextract _ = failwith "ToDo: NSMultiIndexedSet.Lift.tryextract"
let take _ _ = failwith "ToDo: MultiIndexedSet.take"
let trytake p s =
try Some(take p s) with Not_found -> None
let take_first_pair _ = failwith "ToDo: MultiIndexedSet.take_first_pair"
let the_only _ = failwith "ToDo: MultiIndexedSet.the_only"
type idx = Val.idx
let find i m = M.find i m
let tryfind i m = M.tryfind i m
let memi i m = M.mem i m
let reduce _ = failwith "ToDo: NSMultiIndexedSet.Lift.reduce"
let classify fn xs =
let rec classify_one x = function
| xs :: xss when fn x (List.hd xs) -> (x :: xs) :: xss
| xs :: xss -> xs :: (classify_one x xss)
| [] -> [[x]]
in
fold classify_one xs []
end
end

Просмотреть файл

@ -0,0 +1,29 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSSet
module type MultiIndexedType = sig
type t
val equal: t -> t -> bool
val compare: t -> t -> int
type idx
val index : t -> idx
val indices : t -> idx list
val equal_idx : idx -> idx -> bool
val compare_idx : idx -> idx -> int
end
module MultiIndexedSet : sig
module type S = sig
include Set.S
type idx
val memi : idx -> t -> bool
val find : idx -> t -> elt
val tryfind : idx -> t -> elt option
val keys : t -> idx list
end
module Make(Idx: MultiIndexedType) :
(S with type elt = Idx.t and type idx = Idx.idx)
end

121
src/Library/NSMultiMap.ml Normal file
Просмотреть файл

@ -0,0 +1,121 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
open NSMap
module MultiMap = struct
module type S = sig
type k
type v
type vs
type t
val empty : t
val add : k -> v -> t -> t
val union : k -> vs -> t -> t
val diff : k -> vs -> t -> t
val replace : k -> vs -> t -> t
val remove : k -> t -> t
val is_empty : t -> bool
val length : t -> int
val mem : k -> t -> bool
val find : k -> t -> vs
val iter : (k -> v -> unit) -> t -> unit
val iter_keys : (k -> vs -> unit) -> t -> unit
val exists : (k -> v -> bool) -> t -> bool
val existsi : k -> (v -> bool) -> t -> bool
val filter : (k -> v -> bool) -> t -> t
val filteri : k -> (v -> bool) -> t -> t
val map : (v -> v) -> t -> t
val mapi : (k -> v -> v) -> t -> t
val fold : (k -> v -> 'a -> 'a) -> t -> 'a -> 'a
val fold_keys : (k -> vs -> 'a -> 'a) -> t -> 'a -> 'a
val to_list : t -> (k * v) list
(* val equal : t -> t -> bool *)
(* val compare : t -> t -> int *)
end
module Make (Key: OrderedType) (ValSet: Set0) = struct
module M = Map.Make(Key)
type k = Key.t
type v = ValSet.elt
type vs = ValSet.t
type t = vs M.t
module S = ValSet
let empty = M.empty
let add k v t =
match M.tryfind k t with
| None -> M.add k (S.singleton v) t
| Some(vs) -> M.add k (S.add v vs) t
let union k vs t =
if S.is_empty vs then t else
match M.tryfind k t with
| None -> M.add k vs t
| Some(us) -> M.add k (S.union us vs) t
let diff k vs t =
if S.is_empty vs then t else
match M.tryfind k t with
| None -> t
| Some(us) -> M.add k (S.diff us vs) t
let replace k vs t = M.add k vs t
let remove = M.remove
let is_empty = M.is_empty
let mem = M.mem
let find k t = try M.find k t with Not_found -> S.empty
let iter f t = M.iter (fun k vs -> S.iter (f k) vs) t
let iter_keys f t = M.iter f t
let exists p t = M.exists (fun k vs -> S.exists (p k) vs) t
let existsi k p t = S.exists p (find k t)
let filter p t =
M.fold (fun k vs t ->
let vs' = S.filter (p k) vs in
if S.is_empty vs'
then M.remove k t
else M.add k vs' t
) t t
let filteri k p t =
match M.tryfind k t with
| None -> t
| Some(vs) ->
let vs' = S.filter p vs in
if S.is_empty vs'
then M.remove k t
else M.add k vs' t
let map f t = M.map (fun vs -> S.map f vs) t
let mapi f t = M.mapi (fun k vs -> S.map (f k) vs) t
let fold f = M.fold (fun k -> S.fold (f k))
let fold_keys f = M.fold f
let length m = fold (fun _ _ n -> n+1) m 0
let to_list m = fold (fun k v es -> (k,v) :: es) m []
(* let compare = M.compare S.compare *)
(* let equal = M.equal S.equal *)
end
end

Просмотреть файл

@ -0,0 +1,44 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
(** Maps from ordered keys to multiple values. *)
module MultiMap : sig
module type S = sig
type k
type v
type vs
type t
val empty : t
val add : k -> v -> t -> t
val union : k -> vs -> t -> t
val diff : k -> vs -> t -> t
val replace : k -> vs -> t -> t
val remove : k -> t -> t
val is_empty : t -> bool
val length : t -> int
val mem : k -> t -> bool
val find : k -> t -> vs
val iter : (k -> v -> unit) -> t -> unit
val iter_keys : (k -> vs -> unit) -> t -> unit
val exists : (k -> v -> bool) -> t -> bool
val existsi : k -> (v -> bool) -> t -> bool
val filter : (k -> v -> bool) -> t -> t
val filteri : k -> (v -> bool) -> t -> t
val map : (v -> v) -> t -> t
val mapi : (k -> v -> v) -> t -> t
val fold : (k -> v -> 'a -> 'a) -> t -> 'a -> 'a
val fold_keys : (k -> vs -> 'a -> 'a) -> t -> 'a -> 'a
val to_list : t -> (k * v) list
(* val equal : t -> t -> bool *)
(* val compare : t -> t -> int *)
end
module Make (Key: OrderedType) (ValSet: Set0) :
(S with type k = Key.t and type v = ValSet.elt and type vs = ValSet.t)
end

98
src/Library/NSMultiSet.ml Normal file
Просмотреть файл

@ -0,0 +1,98 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
open NSList
open NSMap
module MultiSet = struct
module type S = sig
include Set0
val equal: t -> t -> bool
val compare: t -> t -> int
val remove : elt -> t -> t
val foldm : (elt -> int -> 'z -> 'z) -> t -> 'z -> 'z
val fold_pairs : (elt -> elt -> 'a -> 'a) -> t -> 'a -> 'a
end
module Make (Ord: OrderedType) = struct
module M = Map.Make(Ord)
type elt = Ord.t
type t = int M.t
let empty = M.empty
let is_empty = M.is_empty
let addn x n s = M.add x (try n + M.find x s with Not_found -> n) s
let removen x n s = M.add x (try let x = M.find x s - n in if x<0 then 0 else x with Not_found -> 0) s
let add x s = addn x 1 s
let singleton x = add x empty
let union s t = M.fold addn s t
let choose s = fst (M.choose s)
let remove x s =
try
let m = M.find x s in
if m = 1
then M.remove x s
else M.add x (m - 1) s
with Not_found -> s
let diff s t = M.fold removen s t
let iter f s = M.iter (fun x m -> for _i = 1 to m do f x done) s
let map f s = M.fold (fun x m t -> addn (f x) m t) s empty
let fold f s z =
M.fold (fun x m z ->
let rec loop n z = if n = 0 then z else loop (n-1) (f x z) in
loop m z
) s z
let foldm = M.fold
let map_fold _ = failwith "ToDo: MultiSet.map_fold"
let kfold _ = failwith "ToDo: MultiSet.kfold"
let rec fold_pairs fn xs a =
try
let x = choose xs in
let xs = remove x xs in
fold (fun a -> fn x a) xs (fold_pairs fn xs a)
with
Not_found -> a
let for_all p s = M.for_all (fun x _ -> p x) s
let exists p s = M.exists (fun x _ -> p x) s
let exists_unique p s = M.exists_unique (fun x _ -> p x) s
let filter p s = M.filter (fun x _ -> p x) s
let cardinal s = M.fold (fun _ m c -> c + m) s 0
let of_list l = List.fold add l empty
let to_list s =
M.fold (fun x n l ->
let rec loop l = function
| 0 -> l
| n -> loop (x::l) (n-1) in
loop l n
) s []
let equal s t = M.equal Pervasives.( = ) s t
let compare s t = M.compare Pervasives.compare s t
end
end

Просмотреть файл

@ -0,0 +1,64 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
(** Multisets of ordered values. *)
module MultiSet : sig
module type S = sig
type elt
(** The type of multiset elements. *)
type t
(** The type of multisets of elt. *)
val empty : t
(** The empty multiset. *)
val is_empty : t -> bool
val add : elt -> t -> t
(** [add e s] increases the multiplicity of [e] by one. *)
val singleton : elt -> t
val iter : (elt -> unit) -> t -> unit
val map : (elt -> elt) -> t -> t
val fold : (elt -> 'z -> 'z) -> t -> 'z -> 'z
val map_fold : (elt * 'z -> elt * 'z) -> t * 'z -> t * 'z
val kfold : t -> (elt -> ('a->'b) -> 'a->'b) -> ('a->'b) -> 'a->'b
val for_all : (elt -> bool) -> t -> bool
val exists : (elt -> bool) -> t -> bool
val exists_unique : (elt -> bool) -> t -> bool
val filter : (elt -> bool) -> t -> t
val cardinal : t -> int
val of_list : elt list -> t
val to_list : t -> elt list
(** [to_list s] is a list of the elements of [s], in increasing order,
where elements are repeated according to their multiplicities. *)
val choose : t -> elt
val union : t -> t -> t
(** [union s t] sums the multiplicities of elements of [s] and [t]. *)
val diff : t -> t -> t
val equal: t -> t -> bool
val compare: t -> t -> int
val remove : elt -> t -> t
val foldm : (elt -> int -> 'z -> 'z) -> t -> 'z -> 'z
(** [foldm f s z] computes [(f eN mN … (f e2 m2 (f e1 m1 z)))], where
[e1eN] are the distinct elements of [s], in increasing order, and
[m1mN] are their multiplicities. *)
val fold_pairs : (elt -> elt -> 'a -> 'a) -> t -> 'a -> 'a
end
module Make (Ord: OrderedType) : (S with type elt = Ord.t)
end

140
src/Library/NSOption.ml Normal file
Просмотреть файл

@ -0,0 +1,140 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(*============================================================================
Option
============================================================================*)
module Option = struct
let option d f = function
| Some(x) -> f x
| None -> d
let optionk dk f = function
| Some(x) -> f x
| None -> dk()
let is_some = function
| Some(_) -> true
| None -> false
let is_none o = not (is_some o)
let get = function
| Some(x) -> x
| None -> raise Not_found
let from_some = get
let or_get y = function
| Some(x) -> x
| None -> y
let from_option = or_get
let get_or o y =
match o with
| Some(x) -> x
| None -> y
let some x = Some(x)
let bind o fn =
match o with
| Some(x) -> fn x
| None -> None
let flatten = function
| Some(x) -> x
| None -> None
let map fn = function
| Some(x) -> Some(fn x)
| None -> None
let map2 fn o p =
match o,p with
| Some(x), Some(y) -> Some(fn x y)
| _ -> None
let map3 fn o p q =
match o,p,q with
| Some(x), Some(y), Some(z) -> Some(fn x y z)
| _ -> None
let mapN fn os =
try Some(fn (Array.map from_some os))
with Not_found -> None
let fold fn o z =
match o with
| Some(x) -> fn x z
| None -> z
let iter fn o =
match o with
| Some(x) -> fn x
| None -> ()
(* list operations *)
let to_list = function
| None -> []
| Some(x) -> [x]
let of_list = function
| [] -> None
| x :: _ -> Some(x)
let meet o p =
match o,p with
| Some(x), Some(y) -> Some(x,y)
| _ -> None
let meetN os =
try Some(List.map from_some os)
with Not_found -> None
let rec concat = function
| [] -> []
| Some(x) :: os -> x :: concat os
| None :: os -> concat os
let rec until_none fn a =
match fn a with
| None -> a
| Some(b) -> until_none fn b
let equal eq_fn o p =
match o,p with
| None, None -> true
| Some(x), Some(y) -> eq_fn x y
| _ -> false
let compare fn o p =
match o,p with
| Some(x), Some(y) -> fn x y
| None, None -> 0
| None, _ -> -1
| _, None -> 1
let fmt none fn ff = function
| None -> Format.fprintf ff none
| Some(x) -> Format.fprintf ff "@[%a@]" fn x
end
let ( >>= ) o fn = Option.bind o fn
let ( =<< ) fn o = Option.bind o fn
let ( >>== ) o fn = Option.map fn o
let ( >=> ) f g x = f x >>= g
let ( <=< ) g f x = f x >>= g
let ( |+| ) = Option.meet
let ( !! ) x = Option.from_some(!x)

116
src/Library/NSOption.mli Normal file
Просмотреть файл

@ -0,0 +1,116 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
(** Operations on ['a option]. *)
module Option : sig
val option : 'b -> ('a -> 'b) -> 'a option -> 'b
(** [option d f o] tranforms [o] with [f] using [d] as a default value. *)
val optionk : (unit -> 'b) -> ('a -> 'b) -> 'a option -> 'b
(** [optionk k f o] tranforms [o] with [f] using [k()] as a default value. *)
val is_some : 'a option -> bool
(** Test for existence of a carried value. *)
val is_none : 'a option -> bool
(** Test for absence of a carried value. *)
val get : 'a option -> 'a
(** Extract carried value, raises [Invalid_argument] if [None]. *)
val from_some : 'a option -> 'a
(** Extract carried value, raises [Invalid_argument] if [None]. *)
val get_or : 'a option -> 'a -> 'a
(** Extract carried value, using given value as a default. *)
val or_get : 'a -> 'a option -> 'a
(** Extract carried value, using given value as a default. *)
val from_option : 'a -> 'a option -> 'a
(** Extract carried value, using given value as a default. *)
val some : 'a -> 'a option
(** Monadic unit for options. *)
val bind : 'a option -> ('a -> 'b option) -> 'b option
(** Monadic bind for options. *)
val flatten : 'a option option -> 'a option
(** Monadic join for options. *)
val map : ('a -> 'b) -> 'a option -> 'b option
(** Monadic lift for options. *)
val map2 : ('a -> 'b -> 'c) -> 'a option -> 'b option -> 'c option
(** Monadic binary lift for options. *)
val map3 : ('a->'b->'c->'d) -> 'a option -> 'b option -> 'c option -> 'd option
(** Monadic ternary lift for options. *)
val mapN : ('a array -> 'b) -> 'a option array -> 'b option
(** Monadic n-ary lift for options. *)
val fold : ('a -> 'z -> 'z) -> 'a option -> 'z -> 'z
val iter : ('a -> unit) -> 'a option -> unit
val to_list : 'a option -> 'a list
(** Convert option to list. *)
val of_list : 'a list -> 'a option
(** Convert (head of) list to option. *)
val meet : 'a option -> 'b option -> ('a*'b) option
(** Smash product. *)
val meetN : 'a option list -> 'a list option
(** Smash product, n-ary. *)
val concat : 'a option list -> 'a list
(** Return list of carried values. *)
val until_none : ('a -> 'a option) -> 'a -> 'a
(** Apply given function to given value repeatedly until [None] results,
then return the value that the function mapped to [None]. *)
val equal : ('a -> 'b -> bool) -> 'a option -> 'b option -> bool
(** Equivalence relation on options. *)
val compare : ('a -> 'b -> int) -> 'a option -> 'b option -> int
(** Total order on options, [None] is smaller than [Some]. *)
val fmt :
(unit,Format.formatter,unit)format -> 'a formatter -> 'a option formatter
(** Formatter for options, accepting a format string to use for [None] and a
formatter for carried values. *)
end
(** {4 Option combinators } *)
val ( >>= ) : 'a option -> ('a -> 'b option) -> 'b option
(** Monadic bind for options, left associative. *)
val ( =<< ) : ('a -> 'b option) -> 'a option -> 'b option
(** Monadic bind for options with arguments flipped, left associative. *)
val ( >>== ) : 'a option -> ('a -> 'b) -> 'b option
(** Monadic lift for options with arguments flipped, left associative. *)
val ( >=> ) : ('a -> 'b option) -> ('b -> 'c option) -> 'a -> 'c option
(** Left-to-right Kleisli composition. *)
val ( <=< ) : ('b -> 'c option) -> ('a -> 'b option) -> 'a -> 'c option
(** Right-to-left Kleisli composition. *)
val ( |+| ) : 'a option -> 'b option -> ('a * 'b) option
(** Smash product. *)
val ( !! ) : 'a option ref -> 'a
(** Dereference possibly-NULL pointer. *)

Просмотреть файл

@ -0,0 +1,9 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSHashtbl
module PolyHMap = struct
include Hashtbl
let add = replace
end

Просмотреть файл

@ -0,0 +1,29 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Imperative maps from keys to single values using polymorphic equality and
hashing. @deprecated Polymorphic equality is usually wrong, use [HashMap]
instead. *)
module PolyHMap : sig
type ('a, 'b) t
val create : int -> ('a, 'b) t
val clear : ('a, 'b) t -> unit
val copy : ('a, 'b) t -> ('a, 'b) t
val add : ('a, 'b) t -> 'a -> 'b -> unit
val remove : ('a, 'b) t -> 'a -> unit
val is_empty : ('a, 'b) t -> bool
val length : ('a, 'b) t -> int
val mem : ('a, 'b) t -> 'a -> bool
val find : ('a, 'b) t -> 'a -> 'b
val tryfind : ('a, 'b) t -> 'a -> 'b option
val iter : ('a -> 'b -> unit) -> ('a, 'b) t -> unit
val exists : ('a -> 'b -> bool) -> ('a, 'b) t -> bool
val filter : ('a -> 'b -> bool) -> ('a,'b) t -> unit
val map : ('v -> 'w) -> ('k, 'v) t -> ('k, 'w) t
val mapi : ('k -> 'v -> 'w) -> ('k, 'v) t -> ('k, 'w) t
val fold : ('a -> 'b -> 'c -> 'c) -> ('a, 'b) t -> 'c -> 'c
val to_list : ('a, 'b) t -> ('a * 'b) list
end

160
src/Library/NSPolySet.ml Normal file
Просмотреть файл

@ -0,0 +1,160 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
open NSList
module PolySet = struct
module type S = sig
type 'a elt
type 'a t
val empty : 'a t
val is_empty : 'a t -> bool
val add : 'a elt -> 'a t -> 'a t
val singleton : 'a elt -> 'a t
val iter : ('a elt -> unit) -> 'a t -> unit
val map : ('a elt -> 'a elt) -> 'a t -> 'a t
val fold : ('a elt -> 'z -> 'z) -> 'a t -> 'z -> 'z
val exists : ('a elt -> bool) -> 'a t -> bool
val filter : ('a elt -> bool) -> 'a t -> 'a t
val mem : 'a elt -> 'a t -> bool
val remove : 'a elt -> 'a t -> 'a t
val union : 'a t -> 'a t -> 'a t
val unions : 'a t list -> 'a t
val inter : 'a t -> 'a t -> 'a t
val inters : 'a t list -> 'a t
val diff : 'a t -> 'a t -> 'a t
val diff_diff : 'a t -> 'a t -> 'a t * 'a t
val inter_diff : 'a t -> 'a t -> 'a t * 'a t
val diff_inter_diff : 'a t -> 'a t -> 'a t * 'a t * 'a t
val subset : 'a t -> 'a t -> bool
val disjoint : 'a t -> 'a t -> bool
val intersect : 'a t -> 'a t -> bool
val kfold : 'a t -> ('a elt -> ('y->'z) -> 'y->'z) -> ('y->'z) -> 'y->'z
val fold2 : ('z -> 'a elt -> 'a elt -> 'z) -> 'z -> 'a t -> 'a t -> 'z
val for_all : ('a elt -> bool) -> 'a t -> bool
val partition : ('a elt -> bool) -> 'a t -> 'a t * 'a t
val cardinal : 'a t -> int
val to_list : 'a t -> 'a elt list
val of_list : 'a elt list -> 'a t
val to_array : 'a t -> 'a elt array
val min_elt : 'a t -> 'a elt
val max_elt : 'a t -> 'a elt
val choose : 'a t -> 'a elt
val trychoose : 'a t -> 'a elt option
val split : 'a elt -> 'a t -> 'a t * bool * 'a t
val next : 'a elt -> 'a t -> 'a elt
val fold_pairs : ('a elt -> 'a elt -> 'z -> 'z) -> 'a t -> 'z -> 'z
val fold_product : ('a elt->'a elt -> 'z->'z) -> 'a t->'a t -> 'z->'z
val the_only : ('a elt -> bool) -> 'a t -> 'a elt option
(* val take : ('a elt -> bool) -> 'a t -> 'a elt option *)
val take_first_pair : ('a elt -> 'a elt -> 'z option) -> 'a t -> 'z option
val equal : 'a t -> 'a t -> bool
val compare : 'a t -> 'a t -> int
end
module Make(Ord: PolySet.OrderedType) = struct
include PolySet.Make(Ord)
let equal x y = if x == y then true else 0 = compare x y
let union x y = if x == y then x else union x y
let unions x = List.fold_left union empty x
let inters = function
| [] -> empty
| xs::xss -> List.fold_left inter xs xss
let diff_inter_diff s t = let i = inter s t in (diff s i, i, diff t i)
(* fold (fun a ((s_m_i,i,t_m_i) as acc) -> *)
(* if mem a t *)
(* then (remove a s_m_i, add a i, remove a t_m_i) *)
(* else acc) *)
(* s (s,empty,t) *)
let inter_diff x y = (inter x y, diff x y)
let diff_diff x y = let (x_i, _, y_i) = diff_inter_diff x y in (x_i, y_i)
let disjoint x y = is_empty (inter x y)
(* Within the implementation of Set, the following implementation (due to
Christophe Raffalli [christophe.raffalli@univ-savoie.fr]) should be
faster:
let rec disjoint t1 t2 =
match (t1, t2) with
(Empty, _) | (_, Empty) ->
true
| (Node (l1, v1, r1, _), Node (l2, v2, r2, _)) ->
let c = Ord.compare v1 v2 in
if c = 0 then
false
else if c < 0 then
disjoint (Node (l1, v1, Empty, 0)) l2 && disjoint r1 t2
else
disjoint l1 (Node (l2, v2, Empty, 0)) && disjoint t1 r2
*)
let intersect x y = not (disjoint x y)
let map fn s = fold (fun kv t -> add (fn kv) t) s empty
let to_list = elements
let kfold x fn k = List.kfold fn (to_list x) k
let of_list l = List.fold add l empty
let to_array s = Array.of_list (to_list s)
let trychoose s = try Some (choose s) with Not_found -> None
let next elt s =
let _below, present, above = split elt s in
assert present ;
min_elt above
let rec fold_pairs fn xs a =
try
let x = choose xs in
let xs = remove x xs in
fold (fn x) xs (fold_pairs fn xs a)
with
Not_found -> a
let fold_product fn xs ys =
fold (fun x -> fold (fun y -> fn x y) ys) xs
let the_only p s =
match filter p s with
| s' when cardinal s' = 1 -> Some (choose s')
| _ -> None
(* exception TakeFound of elt *)
(* let take p s = *)
(* try iter (fun x -> if p x then raise (TakeFound(x))) s ; None *)
(* with TakeFound(x) -> Some(x) *)
let rec take_first_pair fn s =
try
let x = choose s in
let s = remove x s in
match
fold (fun y found ->
match found with
| None ->
(match fn x y with
| None -> fn y x
| z -> z)
| some -> some
) s None
with
| None -> take_first_pair fn s
| a -> a
with
Not_found -> None
let fold2 fn a s t = List.fold_left2 fn a (to_list s) (to_list t)
end
end

58
src/Library/NSPolySet.mli Normal file
Просмотреть файл

@ -0,0 +1,58 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
(** Sets of ordered values. See also standard
{{:file:../../../doc/ocaml%20manual/libref/Set.html}Set}. *)
module PolySet : sig
module type S = sig
type 'a elt
type 'a t
val empty : 'a t
val is_empty : 'a t -> bool
val add : 'a elt -> 'a t -> 'a t
val singleton : 'a elt -> 'a t
val iter : ('a elt -> unit) -> 'a t -> unit
val map : ('a elt -> 'a elt) -> 'a t -> 'a t
val fold : ('a elt -> 'z -> 'z) -> 'a t -> 'z -> 'z
val exists : ('a elt -> bool) -> 'a t -> bool
val filter : ('a elt -> bool) -> 'a t -> 'a t
val mem : 'a elt -> 'a t -> bool
val remove : 'a elt -> 'a t -> 'a t
val union : 'a t -> 'a t -> 'a t
val unions : 'a t list -> 'a t
val inter : 'a t -> 'a t -> 'a t
val inters : 'a t list -> 'a t
val diff : 'a t -> 'a t -> 'a t
val diff_diff : 'a t -> 'a t -> 'a t * 'a t
val inter_diff : 'a t -> 'a t -> 'a t * 'a t
val diff_inter_diff : 'a t -> 'a t -> 'a t * 'a t * 'a t
val subset : 'a t -> 'a t -> bool
val disjoint : 'a t -> 'a t -> bool
val intersect : 'a t -> 'a t -> bool
val kfold : 'a t -> ('a elt -> ('y->'z) -> 'y->'z) -> ('y->'z) -> 'y->'z
val fold2 : ('z -> 'a elt -> 'a elt -> 'z) -> 'z -> 'a t -> 'a t -> 'z
val for_all : ('a elt -> bool) -> 'a t -> bool
val partition : ('a elt -> bool) -> 'a t -> 'a t * 'a t
val cardinal : 'a t -> int
val to_list : 'a t -> 'a elt list
val of_list : 'a elt list -> 'a t
val to_array : 'a t -> 'a elt array
val min_elt : 'a t -> 'a elt
val max_elt : 'a t -> 'a elt
val choose : 'a t -> 'a elt
val trychoose : 'a t -> 'a elt option
val split : 'a elt -> 'a t -> 'a t * bool * 'a t
val next : 'a elt -> 'a t -> 'a elt
val fold_pairs : ('a elt -> 'a elt -> 'z -> 'z) -> 'a t -> 'z -> 'z
val fold_product : ('a elt -> 'a elt -> 'z->'z) -> 'a t -> 'a t -> 'z->'z
val the_only : ('a elt -> bool) -> 'a t -> 'a elt option
(* val take : ('a elt -> bool) -> 'a t -> 'a elt option *)
val take_first_pair : ('a elt -> 'a elt -> 'z option) -> 'a t -> 'z option
val equal : 'a t -> 'a t -> bool
val compare : 'a t -> 'a t -> int
end
module Make (Ord : PolySet.OrderedType) : (S with type 'a elt = 'a Ord.t)
end

313
src/Library/NSSet.ml Normal file
Просмотреть файл

@ -0,0 +1,313 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
open NSList
open NSOption
module Set = struct
module type Q = sig
type elt
type t
val is_empty : t -> bool
val mem : elt -> t -> bool
val compare : t -> t -> int
val equal : t -> t -> bool
val subset : t -> t -> bool
val disjoint : t -> t -> bool
val intersect : t -> t -> bool
val iter : (elt -> unit) -> t -> unit
val fold : (elt -> 'a -> 'a) -> t -> 'a -> 'a
val foldr : (elt -> 'a -> 'a) -> t -> 'a -> 'a
val foldi : (int -> elt -> 'a -> 'a) -> t -> 'a -> 'a
val fold_pairs : (elt -> elt -> 'a -> 'a) -> t -> 'a -> 'a
val fold_product : (elt -> elt -> 'a -> 'a) -> t -> t -> 'a -> 'a
val fold2 : ('a -> elt -> elt -> 'a) -> 'a -> t -> t -> 'a
val kfold : t -> (elt -> ('a->'b) -> 'a->'b) -> ('a->'b) -> 'a->'b
val reduce : (elt -> 'a) -> (elt -> 'a -> 'a) -> t -> 'a option
val for_all : (elt -> bool) -> t -> bool
val exists : (elt -> bool) -> t -> bool
val exists_unique : (elt -> bool) -> t -> bool
val cardinal : t -> int
val to_list : t -> elt list
val to_array : t -> elt array
val min_elt : t -> elt
val max_elt : t -> elt
val next : elt -> t -> elt
val choose : t -> elt
val trychoose : t -> elt option
val extract : t -> elt * t
val tryextract : t -> (elt * t) option
val take : (elt -> bool) -> t -> elt
val trytake : (elt -> bool) -> t -> elt option
val take_first_pair : (elt -> elt -> 'a option) -> t -> 'a option
val the_only : (elt -> bool) -> t -> elt option
val classify : (elt -> elt -> bool) -> t -> elt list list
end
module type R = sig
include Q
val add : elt -> t -> t
val adds : elt list -> t -> t
val singleton : elt -> t
val of_list : elt list -> t
val remove : elt -> t -> t
val union : t -> t -> t
val unions : t list -> t
val inter : t -> t -> t
val inters : t list -> t
val diff : t -> t -> t
val diff_inter_diff : t -> t -> t * t * t
val diff_diff : t -> t -> t * t
val inter_diff : t -> t -> t * t
val map : (elt -> elt) -> t -> t
val map_fold : (elt * 'z -> elt * 'z) -> t * 'z -> t * 'z
val map_foldi : (int -> elt * 'z -> elt * 'z) -> t * 'z -> t * 'z
val filter : (elt -> bool) -> t -> t
val partition : (elt -> bool) -> t -> t * t
val split : elt -> t -> t * bool * t
end
module type S = sig
include R
val empty : t
end
module Make (Ord: OrderedType) = struct
include Set.Make(Ord)
let equal x y = if x == y then true else 0 = compare x y
let union x y = if x == y then x else union x y
let unions = List.fold_left union empty
let inters = function
| [] -> empty
| xs::xss -> List.fold_left inter xs xss
let diff_inter_diff s t = let i = inter s t in (diff s i, i, diff t i)
(* fold (fun a ((s_m_i,i,t_m_i) as acc) -> *)
(* if mem a t *)
(* then (remove a s_m_i, add a i, remove a t_m_i) *)
(* else acc) *)
(* s (s,empty,t) *)
let inter_diff x y = (inter x y, diff x y)
let diff_diff x y = let (x_i, _, y_i) = diff_inter_diff x y in (x_i, y_i)
let disjoint x y = is_empty (inter x y)
(* Within the implementation of Set, the following implementation (due to
Christophe Raffalli [christophe.raffalli@univ-savoie.fr]) should be
faster:
let rec disjoint t1 t2 =
match (t1, t2) with
(Empty, _) | (_, Empty) ->
true
| (Node (l1, v1, r1, _), Node (l2, v2, r2, _)) ->
let c = Ord.compare v1 v2 in
if c = 0 then
false
else if c < 0 then
disjoint (Node (l1, v1, Empty, 0)) l2 && disjoint r1 t2
else
disjoint l1 (Node (l2, v2, Empty, 0)) && disjoint t1 r2
*)
let intersect x y = not (disjoint x y)
let to_list = elements
let map fn s = fold (fun kv t -> add (fn kv) t) s empty
let foldr fn s = List.fold_right fn (to_list s)
let foldi fn s z = snd (fold (fun x (i,z) -> (i+1, fn i x z)) s (0, z))
let kfold x fn k = List.kfold fn (to_list x) k
let map_fold fn (s,z) =
fold (fun x (s',z) ->
let x', z' = fn (x, z) in
(add x' s', z')
) s (empty, z)
let map_foldi fn (s,z) =
foldi (fun i x (s',z) ->
let x', z' = fn i (x, z) in
(add x' s', z')
) s (empty, z)
let of_list l = List.fold add l empty
let adds l s = List.fold add l s
let to_array s = Array.of_list (to_list s)
let trychoose s = try Some (choose s) with Not_found -> None
let next elt s =
let _below, present, above = split elt s in
assert present ;
min_elt above
let rec fold_pairs fn xs a =
try
let x = choose xs in
let xs = remove x xs in
fold (fun a -> fn x a) xs (fold_pairs fn xs a)
with
Not_found -> a
let fold_product fn xs ys z =
fold (fun x z -> fold (fun y z -> fn x y z) ys z) xs z
let the_only p s =
match filter p s with
| s' when cardinal s' = 1 -> Some (choose s')
| _ -> None
(* Note: should be implemented with one search *)
let extract xs =
let x = choose xs in
(x, remove x xs)
let tryextract xs =
try Some(extract xs) with Not_found -> None
exception TakeFound of elt
let take p s =
try iter (fun x -> if p x then raise (TakeFound(x))) s ; raise Not_found
with TakeFound(x) -> x
let trytake p s =
try Some(take p s) with Not_found -> None
let rec take_first_pair fn s =
try
let x = choose s in
let s = remove x s in
match
fold (fun y found ->
match found with
| None ->
(match fn x y with
| None -> fn y x
| z -> z)
| some -> some
) s None
with
| None -> take_first_pair fn s
| a -> a
with
Not_found -> None
let fold2 fn a s t = List.fold_left2 fn a (to_list s) (to_list t)
let exists_unique p xs =
let module M = struct exception Found end in
try
fold (fun x found ->
if found
then not (p x) || raise M.Found
else p x
) xs false
with M.Found -> false
let reduce init fn xs =
tryextract xs
>>== fun (x, xs) -> fold fn xs (init x)
let classify fn xs =
let rec classify_one x = function
| xs :: xss when fn x (List.hd xs) -> (x :: xs) :: xss
| xs :: xss -> xs :: (classify_one x xss)
| [] -> [[x]]
in
fold classify_one xs []
end
module type EmbedProject = sig
type t
type s
val embed : s -> t -> t
val project : t -> s
end
module Lift (S: S) (EP: EmbedProject with type s = S.t) : sig
include S with type elt = S.elt and type t = EP.t
val empty : t -> t
end = struct
type elt = S.elt
type t = EP.t
let is_empty x = S.is_empty (EP.project x)
let mem e x = S.mem e (EP.project x)
let compare _ = failwith "ToDo: NSSet.Lift.compare"
let equal _ = failwith "ToDo: NSSet.Lift.equal"
let subset _ = failwith "ToDo: NSSet.Lift.subset"
let disjoint _ = failwith "ToDo: NSSet.Lift.disjoint"
let intersect _ = failwith "ToDo: NSSet.Lift.intersect"
let iter _ = failwith "ToDo: NSSet.Lift.iter"
let fold f x z = S.fold f (EP.project x) z
let foldr _ = failwith "ToDo: NSSet.Lift.foldr"
let foldi f x z = S.foldi f (EP.project x) z
let fold_pairs _ = failwith "ToDo: NSSet.Lift.fold_pairs"
let fold_product _ = failwith "ToDo: NSSet.Lift.fold_product"
let fold2 _ = failwith "ToDo: NSSet.Lift.fold2"
let kfold x f k z = S.kfold (EP.project x) f k z
let for_all _ = failwith "ToDo: NSSet.Lift.for_all"
let exists _ = failwith "ToDo: NSSet.Lift.exists"
let exists_unique _ = failwith "ToDo: NSSet.Lift.exists_unique"
let cardinal x = S.cardinal (EP.project x)
let to_list _ = failwith "ToDo: NSSet.Lift.to_list"
let to_array _ = failwith "ToDo: NSSet.Lift.to_array"
let min_elt _ = failwith "ToDo: NSSet.Lift.min_elt"
let max_elt _ = failwith "ToDo: NSSet.Lift.max_elt"
let next _ = failwith "ToDo: NSSet.Lift.next"
let choose x = S.choose (EP.project x)
let trychoose x = S.trychoose (EP.project x)
let extract _ = failwith "ToDo: NSSet.Lift.extract"
let tryextract _ = failwith "ToDo: NSSet.Lift.tryextract"
let take _ _ = failwith "ToDo: NSSet.Lift.take"
let trytake p s =
try Some(take p s) with Not_found -> None
let take_first_pair _ = failwith "ToDo: NSSet.Lift.take_first_pair"
let the_only _ = failwith "ToDo: NSSet.Lift.the_only"
let reduce _ = failwith "ToDo: NSSet.Lift.reduce"
let classify _ = failwith "ToDo: NSSet.Lift.classify"
let add e x = EP.embed (S.add e (EP.project x)) x
let adds l x = EP.embed (S.adds l (EP.project x)) x
let singleton _ = failwith "ToDo: NSSet.Lift.singleton"
let of_list _ = failwith "ToDo: NSSet.Lift.of_list"
let remove e x = EP.embed (S.remove e (EP.project x)) x
let union _ = failwith "ToDo: NSSet.Lift.union"
let unions _ = failwith "ToDo: NSSet.Lift.unions"
let inter _ = failwith "ToDo: NSSet.Lift.inter"
let inters _ = failwith "ToDo: NSSet.Lift.inters"
let diff _ = failwith "ToDo: NSSet.Lift.diff"
let diff_inter_diff _ = failwith "ToDo: NSSet.Lift.diff_inter_diff"
let diff_diff _ = failwith "ToDo: NSSet.Lift.diff_diff"
let inter_diff _ = failwith "ToDo: NSSet.Lift.inter_diff"
let map f x = EP.embed (S.map f (EP.project x)) x
let map_fold _ = failwith "ToDo: NSSet.Lift.map_fold"
let map_foldi _ = failwith "ToDo: NSSet.Lift.map_foldi"
let filter p x = EP.embed (S.filter p (EP.project x)) x
let partition _ = failwith "ToDo: NSSet.Lift.partition"
let split _ = failwith "ToDo: NSSet.Lift.split"
let empty x = EP.embed S.empty x
end
end
module IntSet = Set.Make(struct
type t = int
let compare = (Pervasives.compare : int -> int -> int)
let equal = (Pervasives.( = ) : int -> int -> bool)
end)

91
src/Library/NSSet.mli Normal file
Просмотреть файл

@ -0,0 +1,91 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
open NSLib
(** Sets of ordered values. See also standard
{{:file:../../../doc/ocaml%20manual/libref/Set.html}Set}. *)
module Set : sig
module type Q = sig
type elt
type t
val is_empty : t -> bool
val mem : elt -> t -> bool
val compare : t -> t -> int
val equal : t -> t -> bool
val subset : t -> t -> bool
val disjoint : t -> t -> bool
val intersect : t -> t -> bool
val iter : (elt -> unit) -> t -> unit
val fold : (elt -> 'a -> 'a) -> t -> 'a -> 'a
val foldr : (elt -> 'a -> 'a) -> t -> 'a -> 'a
val foldi : (int -> elt -> 'a -> 'a) -> t -> 'a -> 'a
val fold_pairs : (elt -> elt -> 'a -> 'a) -> t -> 'a -> 'a
val fold_product : (elt -> elt -> 'a -> 'a) -> t -> t -> 'a -> 'a
val fold2 : ('a -> elt -> elt -> 'a) -> 'a -> t -> t -> 'a
val kfold : t -> (elt -> ('a->'b) -> 'a->'b) -> ('a->'b) -> 'a->'b
val reduce : (elt -> 'a) -> (elt -> 'a -> 'a) -> t -> 'a option
val for_all : (elt -> bool) -> t -> bool
val exists : (elt -> bool) -> t -> bool
val exists_unique : (elt -> bool) -> t -> bool
val cardinal : t -> int
val to_list : t -> elt list
val to_array : t -> elt array
val min_elt : t -> elt
val max_elt : t -> elt
val next : elt -> t -> elt
val choose : t -> elt
val trychoose : t -> elt option
val extract : t -> elt * t
val tryextract : t -> (elt * t) option
val take : (elt -> bool) -> t -> elt
val trytake : (elt -> bool) -> t -> elt option
val take_first_pair : (elt -> elt -> 'a option) -> t -> 'a option
val the_only : (elt -> bool) -> t -> elt option
val classify : (elt -> elt -> bool) -> t -> elt list list
end
module type R = sig
include Q
val add : elt -> t -> t
val adds : elt list -> t -> t
val singleton : elt -> t
val of_list : elt list -> t
val remove : elt -> t -> t
val union : t -> t -> t
val unions : t list -> t
val inter : t -> t -> t
val inters : t list -> t
val diff : t -> t -> t
val diff_inter_diff : t -> t -> t * t * t
val diff_diff : t -> t -> t * t
val inter_diff : t -> t -> t * t
val map : (elt -> elt) -> t -> t
val map_fold : (elt * 'z -> elt * 'z) -> t * 'z -> t * 'z
val map_foldi : (int -> elt * 'z -> elt * 'z) -> t * 'z -> t * 'z
val filter : (elt -> bool) -> t -> t
val partition : (elt -> bool) -> t -> t * t
val split : elt -> t -> t * bool * t
end
module type S = sig
include R
val empty : t
end
module Make (Ord : OrderedType) : (S with type elt = Ord.t)
module type EmbedProject = sig
type t
type s
val embed : s -> t -> t
val project : t -> s
end
module Lift (S: S) (EP: EmbedProject with type s = S.t) : sig
include S with type elt = S.elt and type t = EP.t
val empty : t -> t
end
end
module IntSet : Set.S with type elt = int

107
src/Library/NSSortedList.ml Normal file
Просмотреть файл

@ -0,0 +1,107 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
module SortedList = struct
let rec is_sorted cmp = function
| [] | [_] -> true
| x :: (y :: _ as yzs) -> cmp x y <= 0 && is_sorted cmp yzs
let check_sorted cmp xs = assert( is_sorted cmp xs ); xs
let sort cmp x = List.fast_sort cmp x
let rec add cmp x = function
| [] -> [x]
| y :: ys as yys ->
let ord = cmp x y in
if ord < 0 then x :: yys
else if ord = 0 then yys
else (* ord > 0 *) y :: add cmp x ys
let rec merge cmp cons bbs0 bbs1 =
match bbs0, bbs1 with
| [] , bbs1 -> bbs1
| bbs0 , [] -> bbs0
| b0 :: bs0, b1 :: bs1 ->
let ord = cmp b0 b1 in
if ord <= 0 then cons b0 (merge cmp cons bs0 bbs1)
else (* ord > 0 *) cons b1 (merge cmp cons bbs0 bs1)
let rec union cmp cons bbs0 bbs1 =
match bbs0, bbs1 with
| [] , bbs1 -> bbs1
| bbs0 , [] -> bbs0
| b0 :: bs0, b1 :: bs1 ->
let ord = cmp b0 b1 in
if ord < 0 then cons b0 (union cmp cons bs0 bbs1)
else if ord = 0 then union cmp cons bs0 bbs1
else (* ord > 0 *) cons b1 (union cmp cons bbs0 bs1)
let rec inter cmp xxs yys =
match xxs, yys with
| [] , _ -> []
| _ , [] -> []
| x::xs, y::ys ->
match cmp x y with
| n when n < 0 -> inter cmp xs yys
| 0 -> x :: (inter cmp xs ys)
| _ -> inter cmp xxs ys
let rec intersect cmp xxs yys =
match xxs, yys with
| [] , _ -> false
| _ , [] -> false
| x::xs, y::ys ->
match cmp x y with
| n when n < 0 -> intersect cmp xs yys
| 0 -> true
| _ -> intersect cmp xxs ys
let rec diff cmp xxs yys =
match xxs, yys with
| [] , _ -> []
| _ , [] -> xxs
| x::xs, y::ys ->
match cmp x y with
| n when n < 0 -> x :: diff cmp xs yys
| 0 -> diff cmp xs ys
| _ -> diff cmp xxs ys
let rec diff_inter_diff cmp xxs yys =
match xxs, yys with
| [] , [] -> ([], [], [])
| [] , _ -> ([], [], yys)
| _ , [] -> (xxs, [], [])
| x::xs, y::ys ->
match cmp x y with
| n when n < 0 ->
let xs_yys, is, yys_xs = diff_inter_diff cmp xs yys in
(x::xs_yys, is, yys_xs)
| 0 ->
let xs_ys, is, ys_xs = diff_inter_diff cmp xs ys in
(xs_ys, x::is, ys_xs)
| _ ->
let xxs_ys, is, ys_xxs = diff_inter_diff cmp xxs ys in
(xxs_ys, is, y::ys_xxs)
let rec mem cmp x = function
| [] -> false
| y :: xs ->
match cmp x y with
| n when n < 0 -> false
| 0 -> true
| _ -> mem cmp x xs
let rec subsupset cmp xxs yys =
match xxs, yys with
| [] , [] -> (true, true)
| _::_ , [] -> (false, true)
| [] , _::_ -> (true, false)
| x::xs, y::ys ->
match cmp x y with
| n when n < 0 -> (false, snd (subsupset cmp xs yys))
| 0 -> subsupset cmp xs ys
| _ -> (fst (subsupset cmp xxs ys), false)
end

Просмотреть файл

@ -0,0 +1,39 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
(** Operations on sorted ['a list]. *)
module SortedList : sig
val is_sorted : ('a->'a->int)-> 'a list -> bool
val check_sorted : ('a->'a->int)-> 'a list -> 'a list
(** Construct a SortedList from an unsorted list. *)
val sort : ('a->'a->int)-> 'a list -> 'a list
(** Adds an element into a sorted list, if it is not already a member. *)
val add : ('a->'a->int)-> 'a -> 'a list -> 'a list
(** Merges two sorted lists using given addition operation, keeping
duplicates. *)
val merge : ('a->'a->int)->
('a -> 'a list -> 'a list) -> 'a list -> 'a list -> 'a list
(** Unions two sorted lists using given addition operation. *)
val union : ('a->'a->int)->
('a -> 'a list -> 'a list) -> 'a list -> 'a list -> 'a list
val inter : ('a->'a->int)-> 'a list -> 'a list -> 'a list
val diff : ('a->'a->int)-> 'a list -> 'a list -> 'a list
val intersect : ('a->'a->int)-> 'a list -> 'a list -> bool
val diff_inter_diff : ('a->'a->int)->
'a list -> 'a list -> 'a list * 'a list * 'a list
val mem : ('a->'a->int)-> 'a -> 'a list -> bool
(** Takes two sorted lists [xs], [ys] and returns a pair of booleans
indicating whether [xs] is a subset of [ys], and whether [xs] is a
superset of [ys]. *)
val subsupset : ('a->'a->int)-> 'a list -> 'a list -> bool * bool
end

22
src/Library/NSString.ml Normal file
Просмотреть файл

@ -0,0 +1,22 @@
(* Copyright (c) Microsoft Corporation. All rights reserved. *)
module String = struct
include String
let filteri fn s =
let len = length s in
let s' = create len in
let start = ref (-1) in
let count = ref 0 in
for i = 0 to len - 1 do
if fn i s.[i] then (
s'.[!count] <- s.[i] ;
incr count ;
if !start < 0 then start := i ;
)
done ;
if !start < 0 then start := 0 ;
sub s' !start !count
end

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше