summaryrefslogtreecommitdiff
path: root/src/jib
diff options
context:
space:
mode:
Diffstat (limited to 'src/jib')
-rw-r--r--src/jib/jib_smt.ml127
-rw-r--r--src/jib/jib_smt.mli113
2 files changed, 176 insertions, 64 deletions
diff --git a/src/jib/jib_smt.ml b/src/jib/jib_smt.ml
index 4fbfa589..0f78f5bf 100644
--- a/src/jib/jib_smt.ml
+++ b/src/jib/jib_smt.ml
@@ -69,32 +69,26 @@ let opt_auto = ref false
let opt_debug_graphs = ref false
+let opt_propagate_vars = ref false
+
module EventMap = Map.Make(Event)
(* Note that we have to use x : ty ref rather than mutable x : ty, to
make sure { ctx with x = ... } doesn't break the mutable state. *)
+(* See mli file for a description of each field *)
type ctx = {
- (* Arbitrary-precision bitvectors are represented as a (BitVec lbits_index, BitVec (2 ^ lbits_index)) pair. *)
lbits_index : int;
- (* The size we use for integers where we don't know how large they are statically. *)
lint_size : int;
- (* A generic vector, vector('a) becomes Array (BitVec vector_index) 'a.
- We need to take care that vector_index is large enough for all generic vectors. *)
vector_index : int;
register_map : id list CTMap.t;
- (* Keep track of all the tuple sizes we need to genenerate types for *)
tuple_sizes : IntSet.t ref;
tc_env : Type_check.Env.t;
pragma_l : Ast.l;
arg_stack : (int * string) Stack.t;
ast : Type_check.tannot defs;
events : smt_exp Stack.t EventMap.t ref;
- (* When generating SMT for an instruction pathcond will contain
- the global path conditional of the containing block in the
- control flow graph *)
pathcond : smt_exp Lazy.t;
- (* Set if we need to use strings *)
use_string : bool ref;
use_real : bool ref
}
@@ -1028,16 +1022,16 @@ let writes = ref (-1)
let builtin_write_mem ctx wk addr_size addr data_size data =
incr writes;
let name = "W" ^ string_of_int !writes in
- [Write_mem (name, smt_cval ctx wk, smt_cval ctx addr, smt_cval ctx data)],
- Var name
+ [Write_mem (name, smt_cval ctx wk, smt_cval ctx addr, smt_ctyp ctx (cval_ctyp addr), smt_cval ctx data, smt_ctyp ctx (cval_ctyp data))],
+ Var (name ^ "_ret")
let reads = ref (-1)
let builtin_read_mem ctx rk addr_size addr data_size ret_ctyp =
incr reads;
let name = "R" ^ string_of_int !reads in
- [Read_mem (name, smt_ctyp ctx ret_ctyp, smt_cval ctx rk, smt_cval ctx addr)],
- Var name
+ [Read_mem (name, smt_ctyp ctx ret_ctyp, smt_cval ctx rk, smt_cval ctx addr, smt_ctyp ctx (cval_ctyp addr))],
+ Var (name ^ "_ret")
let rec smt_conversion ctx from_ctyp to_ctyp x =
match from_ctyp, to_ctyp with
@@ -1596,10 +1590,10 @@ let optimize_smt stack =
end
| (Declare_datatypes _ | Declare_tuple _) as def ->
Stack.push def stack'
- | Write_mem (_, wk, addr, data) as def ->
+ | Write_mem (_, wk, addr, _, data, _) as def ->
uses_in_exp wk; uses_in_exp addr; uses_in_exp data;
Stack.push def stack'
- | Read_mem (_, _, rk, addr) as def ->
+ | Read_mem (_, _, rk, addr, _) as def ->
uses_in_exp rk; uses_in_exp addr;
Stack.push def stack'
| Assert exp as def ->
@@ -1616,17 +1610,21 @@ let optimize_smt stack =
| Declare_const _ as def ->
Queue.add def queue
| Define_const (var, typ, exp) ->
- begin match Hashtbl.find_opt uses var with
- | Some 1 ->
+ begin match Hashtbl.find_opt uses var, simp_smt_exp vars exp with
+ | _, (Bin _ | Bool_lit _) ->
Hashtbl.add vars var exp
- | Some _ ->
- Queue.add (Define_const (var, typ, simp_smt_exp vars exp)) queue
- | None -> assert false
+ | _, Var _ when !opt_propagate_vars ->
+ Hashtbl.add vars var exp
+ | Some 1, _ ->
+ Hashtbl.add vars var exp
+ | Some _, exp ->
+ Queue.add (Define_const (var, typ, exp)) queue
+ | None, _ -> assert false
end
- | Write_mem (name, wk, addr, data) ->
- Queue.add (Write_mem (name, simp_smt_exp vars wk, simp_smt_exp vars addr, simp_smt_exp vars data)) queue
- | Read_mem (name, typ, rk, addr) ->
- Queue.add (Read_mem (name, typ, simp_smt_exp vars rk, simp_smt_exp vars addr)) queue
+ | Write_mem (name, wk, addr, addr_ty, data, data_ty) ->
+ Queue.add (Write_mem (name, simp_smt_exp vars wk, simp_smt_exp vars addr, addr_ty, simp_smt_exp vars data, data_ty)) queue
+ | Read_mem (name, typ, rk, addr, addr_typ) ->
+ Queue.add (Read_mem (name, typ, simp_smt_exp vars rk, simp_smt_exp vars addr, addr_typ)) queue
| Assert exp ->
Queue.add (Assert (simp_smt_exp vars exp)) queue
| (Declare_datatypes _ | Declare_tuple _) as def ->
@@ -1653,15 +1651,15 @@ let smt_header ctx cdefs =
checks if r is any one of the registers with type t, and reads that
register if it is. We also do a similar thing for *r = x
*)
-let expand_reg_deref ctx = function
+let expand_reg_deref env register_map = function
| I_aux (I_funcall (clexp, false, function_id, [reg_ref]), (_, l)) as instr ->
let open Type_check in
- begin match (if Env.is_extern function_id ctx.tc_env "smt" then Some (Env.get_extern function_id ctx.tc_env "smt") else None) with
+ begin match (if Env.is_extern function_id env "smt" then Some (Env.get_extern function_id env "smt") else None) with
| Some "reg_deref" ->
begin match cval_ctyp reg_ref with
| CT_ref reg_ctyp ->
(* Not find all the registers with this ctyp *)
- begin match CTMap.find_opt reg_ctyp ctx.register_map with
+ begin match CTMap.find_opt reg_ctyp register_map with
| Some regs ->
let end_label = label "end_reg_deref_" in
let try_reg r =
@@ -1683,7 +1681,7 @@ let expand_reg_deref ctx = function
| I_aux (I_copy (CL_addr (CL_id (id, ctyp)), cval), (_, l)) ->
begin match ctyp with
| CT_ref reg_ctyp ->
- begin match CTMap.find_opt reg_ctyp ctx.register_map with
+ begin match CTMap.find_opt reg_ctyp register_map with
| Some regs ->
let end_label = label "end_reg_write_" in
let try_reg r =
@@ -1716,12 +1714,43 @@ let rec smt_query ctx = function
| Q_or qs ->
Fn ("or", List.map (smt_query ctx) qs)
-let dump_graph function_id cfg =
- let gv_file = string_of_id function_id ^ ".gv" in
+let dump_graph name cfg =
+ let gv_file = name ^ ".gv" in
let out_chan = open_out gv_file in
Jib_ssa.make_dot out_chan cfg;
close_out out_chan
+let smt_instr_list name ctx all_cdefs instrs =
+ let stack = Stack.create () in
+
+ let open Jib_ssa in
+ let start, cfg = ssa instrs in
+ let visit_order =
+ try topsort cfg with
+ | Not_a_DAG n ->
+ dump_graph name cfg;
+ raise (Reporting.err_general ctx.pragma_l
+ (Printf.sprintf "%s: control flow graph is not acyclic (node %d is in cycle)\nWrote graph to %s.gv" name n name))
+ in
+ if !opt_debug_graphs then
+ dump_graph name cfg;
+
+ List.iter (fun n ->
+ begin match get_vertex cfg n with
+ | None -> ()
+ | Some ((ssanodes, cfnode), preds, succs) ->
+ let muxers =
+ ssanodes |> List.map (smt_ssanode ctx cfg preds) |> List.concat
+ in
+ let ctx = { ctx with pathcond = lazy (get_pathcond n cfg ctx) } in
+ let basic_block = smt_cfnode all_cdefs ctx ssanodes cfnode in
+ push_smt_defs stack muxers;
+ push_smt_defs stack basic_block;
+ end
+ ) visit_order;
+
+ stack
+
let smt_cdef props lets name_file ctx all_cdefs = function
| CDEF_spec (function_id, arg_ctyps, ret_ctyp) when Bindings.mem function_id props ->
begin match find_function [] function_id all_cdefs with
@@ -1730,8 +1759,6 @@ let smt_cdef props lets name_file ctx all_cdefs = function
let pragma = parse_pragma pragma_l prop_args in
- let stack = Stack.create () in
-
let ctx = { ctx with events = ref EventMap.empty; pragma_l = pragma_l; arg_stack = Stack.create () } in
(* When we create each argument declaration, give it a unique
@@ -1743,41 +1770,13 @@ let smt_cdef props lets name_file ctx all_cdefs = function
let open Jib_optimize in
(lets @ intervening_lets @ arg_decls @ instrs)
|> inline all_cdefs (fun _ -> true)
- |> List.map (map_instr (expand_reg_deref ctx))
+ |> List.map (map_instr (expand_reg_deref ctx.tc_env ctx.register_map))
|> flatten_instrs
|> remove_unused_labels
|> remove_pointless_goto
in
- (* let str = Pretty_print_sail.to_string PPrint.(separate_map hardline Jib_util.pp_instr instrs) in
- prerr_endline str; *)
-
- let open Jib_ssa in
- let start, cfg = ssa instrs in
- let visit_order =
- try topsort cfg with
- | Not_a_DAG n ->
- dump_graph function_id cfg;
- raise (Reporting.err_general pragma_l
- (Printf.sprintf "$%s %s: control flow graph is not acyclic (node %d is in cycle)\nWrote graph to %s.gv"
- prop_type (string_of_id function_id) n (string_of_id function_id)))
- in
- if !opt_debug_graphs then
- dump_graph function_id cfg;
-
- List.iter (fun n ->
- begin match get_vertex cfg n with
- | None -> ()
- | Some ((ssanodes, cfnode), preds, succs) ->
- let muxers =
- ssanodes |> List.map (smt_ssanode ctx cfg preds) |> List.concat
- in
- let ctx = { ctx with pathcond = lazy (get_pathcond n cfg ctx) } in
- let basic_block = smt_cfnode all_cdefs ctx ssanodes cfnode in
- push_smt_defs stack muxers;
- push_smt_defs stack basic_block;
- end
- ) visit_order;
+ let stack = smt_instr_list (string_of_id function_id) ctx all_cdefs instrs in
let query = smt_query ctx pragma.query in
push_smt_defs stack [Assert (Fn ("not", [query]))];
@@ -1794,10 +1793,10 @@ let smt_cdef props lets name_file ctx all_cdefs = function
else
output_string out_chan "(set-logic QF_AUFBVDT)\n";
- List.iter (fun def -> output_string out_chan (string_of_smt_def "" def); output_string out_chan "\n") header;
+ List.iter (fun def -> output_string out_chan (string_of_smt_def def); output_string out_chan "\n") header;
let queue = optimize_smt stack in
- Queue.iter (fun def -> output_string out_chan (string_of_smt_def "" def); output_string out_chan "\n") queue;
+ Queue.iter (fun def -> output_string out_chan (string_of_smt_def def); output_string out_chan "\n") queue;
output_string out_chan "(check-sat)\n";
if prop_type = "counterexample" then
diff --git a/src/jib/jib_smt.mli b/src/jib/jib_smt.mli
new file mode 100644
index 00000000..f9f42ff5
--- /dev/null
+++ b/src/jib/jib_smt.mli
@@ -0,0 +1,113 @@
+(**************************************************************************)
+(* Sail *)
+(* *)
+(* Copyright (c) 2013-2017 *)
+(* Kathyrn Gray *)
+(* Shaked Flur *)
+(* Stephen Kell *)
+(* Gabriel Kerneis *)
+(* Robert Norton-Wright *)
+(* Christopher Pulte *)
+(* Peter Sewell *)
+(* Alasdair Armstrong *)
+(* Brian Campbell *)
+(* Thomas Bauereiss *)
+(* Anthony Fox *)
+(* Jon French *)
+(* Dominic Mulligan *)
+(* Stephen Kell *)
+(* Mark Wassell *)
+(* *)
+(* All rights reserved. *)
+(* *)
+(* This software was developed by the University of Cambridge Computer *)
+(* Laboratory as part of the Rigorous Engineering of Mainstream Systems *)
+(* (REMS) project, funded by EPSRC grant EP/K008528/1. *)
+(* *)
+(* Redistribution and use in source and binary forms, with or without *)
+(* modification, are permitted provided that the following conditions *)
+(* are met: *)
+(* 1. Redistributions of source code must retain the above copyright *)
+(* notice, this list of conditions and the following disclaimer. *)
+(* 2. Redistributions in binary form must reproduce the above copyright *)
+(* notice, this list of conditions and the following disclaimer in *)
+(* the documentation and/or other materials provided with the *)
+(* distribution. *)
+(* *)
+(* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' *)
+(* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *)
+(* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *)
+(* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR *)
+(* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *)
+(* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *)
+(* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *)
+(* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *)
+(* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *)
+(* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *)
+(* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *)
+(* SUCH DAMAGE. *)
+(**************************************************************************)
+
+open Ast
+open Ast_util
+open Jib
+open Jib_util
+open Smtlib
+
+val opt_ignore_overflow : bool ref
+val opt_auto : bool ref
+val opt_debug_graphs : bool ref
+val opt_propagate_vars : bool ref
+
+module IntSet : Set.S with type elt = int
+module EventMap : Map.S with type key = Property.event
+
+val opt_default_lint_size : int ref
+val opt_default_lbits_index : int ref
+val opt_default_vector_index : int ref
+
+type ctx = {
+ (** Arbitrary-precision bitvectors are represented as a (BitVec lbits_index, BitVec (2 ^ lbits_index)) pair. *)
+ lbits_index : int;
+ (** The size we use for integers where we don't know how large they are statically. *)
+ lint_size : int;
+ (** A generic vector, vector('a) becomes Array (BitVec vector_index) 'a.
+ We need to take care that vector_index is large enough for all generic vectors. *)
+ vector_index : int;
+ (** A map from each ctyp to a list of registers of that ctyp *)
+ register_map : id list CTMap.t;
+ (** A set to keep track of all the tuple sizes we need to generate types for *)
+ tuple_sizes : IntSet.t ref;
+ (** tc_env is the global type-checking environment *)
+ tc_env : Type_check.Env.t;
+ (** A location, usually the $counterexample or $property we are
+ generating the SMT for. Used for error messages. *)
+ pragma_l : Ast.l;
+ (** Used internally to keep track of function argument names *)
+ arg_stack : (int * string) Stack.t;
+ (** The fully type-checked ast *)
+ ast : Type_check.tannot defs;
+ (** For every event type we have a stack of boolean SMT
+ expressions for each occurance of that event. See
+ src/property.ml for the event types *)
+ events : smt_exp Stack.t EventMap.t ref;
+ (** When generating SMT for an instruction pathcond will contain
+ the global path conditional of the containing block in the
+ control flow graph *)
+ pathcond : smt_exp Lazy.t;
+ (** Set if we need to use strings or real numbers in the generated
+ SMT, which then requires set-logic ALL or similar depending on
+ the solver *)
+ use_string : bool ref;
+ use_real : bool ref
+ }
+
+val smt_instr_list : string -> ctx -> cdef list -> instr list -> smt_def Stack.t
+
+(** Generate SMT for all the $property and $counterexample pragmas in an AST *)
+val generate_smt :
+ (string * string * l * 'a val_spec) Bindings.t
+ -> (string -> string)
+ -> Type_check.Env.t
+ -> Type_check.tannot defs
+ -> unit