summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlasdair Armstrong2019-05-14 15:46:10 +0100
committerAlasdair Armstrong2019-05-14 15:46:10 +0100
commit9d6734f717639f9babdec4441f8362bfeca10d66 (patch)
tree91080afb376c38328de7262352f7c3217bc22719 /src
parent63d7f669f3d292315e4a353115284358ba7d5627 (diff)
parentf6cc45f2788dc777d1fa35aa9a216de994992288 (diff)
Merge branch 'smt_experiments' into sail2
Diffstat (limited to 'src')
-rw-r--r--src/initial_check.ml3
-rw-r--r--src/jib/jib_smt.ml213
-rw-r--r--src/jib/jib_ssa.ml119
-rw-r--r--src/jib/jib_ssa.mli15
-rw-r--r--src/jib/jib_util.ml4
-rw-r--r--src/pretty_print_coq.ml3
-rw-r--r--src/pretty_print_lem.ml3
-rw-r--r--src/property.mli2
-rw-r--r--src/reporting.ml5
-rw-r--r--src/reporting.mli5
-rw-r--r--src/sail.ml3
-rw-r--r--src/sail_lib.ml4
-rw-r--r--src/smtlib.ml18
-rw-r--r--src/type_check.ml2
-rw-r--r--src/value.ml7
15 files changed, 308 insertions, 98 deletions
diff --git a/src/initial_check.ml b/src/initial_check.ml
index ad52751b..522faab7 100644
--- a/src/initial_check.ml
+++ b/src/initial_check.ml
@@ -425,7 +425,8 @@ and to_ast_exp ctx (P.E_aux(exp,l) : P.exp) =
E_internal_return(to_ast_exp ctx exp)
else
raise (Reporting.err_general l "Internal return construct found without -dmagic_hash")
- | _ -> raise (Reporting.err_unreachable l __POS__ "Unparsable construct in to_ast_exp")
+ | P.E_deref exp ->
+ E_app (Id_aux (Id "__deref", l), [to_ast_exp ctx exp])
), (l,()))
and to_ast_measure ctx (P.Measure_aux(m,l)) : unit internal_loop_measure =
diff --git a/src/jib/jib_smt.ml b/src/jib/jib_smt.ml
index 086d4d11..7f622f85 100644
--- a/src/jib/jib_smt.ml
+++ b/src/jib/jib_smt.ml
@@ -67,6 +67,8 @@ let opt_ignore_overflow = ref false
let opt_auto = ref false
+let opt_debug_graphs = ref false
+
module EventMap = Map.Make(Event)
(* Note that we have to use x : ty ref rather than mutable x : ty, to
@@ -91,7 +93,7 @@ type ctx = {
(* 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;
+ pathcond : smt_exp Lazy.t;
(* Set if we need to use strings *)
use_string : bool ref;
use_real : bool ref
@@ -116,7 +118,7 @@ let initial_ctx () = {
arg_stack = Stack.create ();
ast = Defs [];
events = ref EventMap.empty;
- pathcond = Bool_lit true;
+ pathcond = lazy (Bool_lit true);
use_string = ref false;
use_real = ref false;
}
@@ -278,6 +280,10 @@ let rec smt_cval ctx cval =
Fn ("=", [smt_cval ctx cval; Bin "1"])
| V_call (Bnot, [cval]) ->
Fn ("not", [smt_cval ctx cval])
+ | V_call (Band, cvals) ->
+ smt_conj (List.map (smt_cval ctx) cvals)
+ | V_call (Bor, cvals) ->
+ smt_disj (List.map (smt_cval ctx) cvals)
| V_ctor_kind (union, ctor_id, unifiers, _) ->
Fn ("not", [Tester (zencode_ctor ctor_id unifiers, smt_cval ctx union)])
| V_ctor_unwrap (ctor_id, union, unifiers, _) ->
@@ -320,7 +326,10 @@ let rec smt_cval ctx cval =
let add_event ctx ev smt =
let stack = event_stack ctx ev in
- Stack.push (Fn ("=>", [ctx.pathcond; smt])) stack
+ Stack.push (Fn ("=>", [Lazy.force ctx.pathcond; smt])) stack
+
+let add_pathcond_event ctx ev =
+ Stack.push (Lazy.force ctx.pathcond) (event_stack ctx ev)
let overflow_check ctx smt =
if not !opt_ignore_overflow then (
@@ -903,6 +912,16 @@ let builtin_compare_bits fn ctx v1 v2 ret_ctyp =
| _ -> builtin_type_error ctx fn [v1; v2] (Some ret_ctyp)
+(* ***** String operations: lib/real.sail ***** *)
+
+let builtin_decimal_string_of_bits ctx v =
+ begin match cval_ctyp v with
+ | CT_fbits (n, _) ->
+ Fn ("int.to.str", [Fn ("bv2nat", [smt_cval ctx v])])
+
+ | _ -> builtin_type_error ctx "decimal_string_of_bits" [v] None
+ end
+
(* ***** Real number operations: lib/real.sail ***** *)
let builtin_sqrt_real ctx root v =
@@ -986,6 +1005,7 @@ let smt_builtin ctx name args ret_ctyp =
(* string builtins *)
| "concat_str", [v1; v2], CT_string -> ctx.use_string := true; Fn ("str.++", [smt_cval ctx v1; smt_cval ctx v2])
| "eq_string", [v1; v2], CT_bool -> ctx.use_string := true; Fn ("=", [smt_cval ctx v1; smt_cval ctx v2])
+ | "decimal_string_of_bits", [v], CT_string -> ctx.use_string := true; builtin_decimal_string_of_bits ctx v
(* lib/real.sail *)
(* Note that sqrt_real is special and is handled by smt_instr. *)
@@ -1002,6 +1022,23 @@ let smt_builtin ctx name args ret_ctyp =
| _ -> failwith ("Unknown builtin " ^ name ^ " " ^ Util.string_of_list ", " string_of_ctyp (List.map cval_ctyp args) ^ " -> " ^ string_of_ctyp ret_ctyp)
+(* Memory reads and writes as defined in lib/regfp.sail *)
+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
+
+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
+
let rec smt_conversion ctx from_ctyp to_ctyp x =
match from_ctyp, to_ctyp with
| _, _ when ctyp_equal from_ctyp to_ctyp -> x
@@ -1214,7 +1251,7 @@ let push_smt_defs stack smt_defs =
(* When generating SMT when we encounter joins between two or more
blocks such as in the example below, we have to generate a muxer
that chooses the correct value of v_n or v_m to assign to v_o. We
- use the pi nodes that contain the global path condition for each
+ use the pi nodes that contain the path condition for each
block to generate an if-then-else for each phi function. The order
of the arguments to each phi function is based on the graph node
index for the predecessor nodes.
@@ -1249,11 +1286,7 @@ let smt_ssanode ctx cfg preds =
let pis = List.map get_pi (IntSet.elements preds) in
let mux =
List.fold_right2 (fun pi id chain ->
- let pathcond =
- match pi with
- | [cval] -> smt_cval ctx cval
- | _ -> Fn ("and", List.map (smt_cval ctx) pi)
- in
+ let pathcond = smt_conj (List.map (smt_cval ctx) pi) in
match chain with
| Some smt ->
Some (Ite (pathcond, Var (zencode_name id), smt))
@@ -1266,12 +1299,68 @@ let smt_ssanode ctx cfg preds =
| Some mux ->
[Define_const (zencode_name id, smt_ctyp ctx ctyp, mux)]
-let rec get_pathcond ctx fns =
+(* The pi condition are computed by traversing the dominator tree,
+ with each node having a pi condition defined as the conjunction of
+ all guards between it and the start node in the dominator
+ tree. This is imprecise because we have situations like:
+
+ 1
+ / \
+ 2 3
+ | |
+ | 4
+ | |\
+ 5 6 9
+ \ / |
+ 7 10
+ |
+ 8
+
+ where 8 = match_failure, 1 = start and 10 = return.
+ 2, 3, 6 and 9 are guards as they come directly after a control flow
+ split, which always follows a conditional jump.
+
+ Here the path through the dominator tree for the match_failure is
+ 1->7->8 which contains no guards so the pi condition would be empty.
+ What we do now is walk backwards (CFG must be acyclic at this point)
+ until we hit the join point prior to where we require a path
+ condition. We then take the disjunction of the pi conditions for the
+ join point's predecessors, so 5 and 6 in this case. Which gives us a
+ path condition of 2 | (3 & 6) as the dominator chains are 1->2->5 and
+ 1->3->4->6.
+
+ This should work as any split in the control flow must have been
+ caused by a conditional jump followed by distinct guards, so each of
+ the nodes immediately prior to a join point must be dominated by at
+ least one unique guard. It also explains why the pi conditions are
+ sufficient to choose outcomes of phi functions above.
+
+ If we hit a guard before a join (such as 9 for return's path
+ conditional) we just return the pi condition for that guard, i.e.
+ (3 & 9) for 10. If we reach start then the path condition is simply
+ true.
+*)
+let rec get_pathcond n cfg ctx =
let open Jib_ssa in
- match fns with
- | [] | Pi [] :: _ -> Bool_lit true
- | Pi cvals :: _ -> Fn ("and", List.map (smt_cval ctx) cvals)
- | Phi _ :: fns -> get_pathcond ctx fns
+ let get_pi m =
+ match get_vertex cfg m with
+ | Some ((ssanodes, _), _, _) ->
+ V_call (Band, List.concat (List.map (function Pi guards -> guards | _ -> []) ssanodes))
+ | None -> failwith "Node does not exist"
+ in
+ match get_vertex cfg n with
+ | Some ((_, CF_guard cond), _, _) ->
+ smt_cval ctx (get_pi n)
+ | Some (_, preds, succs) ->
+ if IntSet.cardinal preds = 0 then
+ Bool_lit true
+ else if IntSet.cardinal preds = 1 then
+ get_pathcond (IntSet.min_elt preds) cfg ctx
+ else
+ let pis = List.map get_pi (IntSet.elements preds) in
+ smt_cval ctx (V_call (Bor, pis))
+
+ | None -> assert false (* Should never be called for a non-existent node *)
(* For any complex l-expression we need to turn it into a
read-modify-write in the SMT solver. The SSA transform turns CL_id
@@ -1323,6 +1412,19 @@ let rmw_modify smt = function
end
| _ -> assert false
+let smt_terminator ctx =
+ let open Jib_ssa in
+ function
+ | T_end id ->
+ add_event ctx Return (Var (zencode_name id));
+ []
+
+ | T_match_failure ->
+ add_pathcond_event ctx Match;
+ []
+
+ | T_undefined _ | T_goto _ | T_jump _ | T_label _ | T_none -> []
+
(* For a basic block (contained in a control-flow node / cfnode), we
turn the instructions into a sequence of define-const and
declare-const expressions. Because we are working with a SSA graph,
@@ -1340,6 +1442,24 @@ let smt_instr ctx =
| _ ->
Reporting.unreachable l __POS__ "Bad arguments for sqrt_real"
end
+ (* See lib/regfp.sail *)
+ else if name = "platform_write_mem" then
+ begin match args with
+ | [wk; addr_size; addr; data_size; data] ->
+ let mem_event, var = builtin_write_mem ctx wk addr_size addr data_size data in
+ mem_event @ [define_const ctx id ret_ctyp var]
+ | _ ->
+ Reporting.unreachable l __POS__ "Bad arguments for __write_mem"
+ end
+ else if name = "platform_read_mem" then
+ begin match args with
+ | [rk; addr_size; addr; data_size] ->
+ let mem_event, var = builtin_read_mem ctx rk addr_size addr data_size ret_ctyp in
+ mem_event @ [define_const ctx id ret_ctyp var]
+
+ | _ ->
+ Reporting.unreachable l __POS__ "Bad arguments for __read_mem"
+ end
else
let value = smt_builtin ctx name args ret_ctyp in
[define_const ctx id ret_ctyp value]
@@ -1401,21 +1521,11 @@ let smt_instr ctx =
end;
[declare_const ctx id ctyp]
- | I_aux (I_end id, _) ->
- add_event ctx Return (Var (zencode_name id));
- []
-
| I_aux (I_clear _, _) -> []
- | I_aux (I_match_failure, _) ->
- add_event ctx Match (Bool_lit false);
- []
-
- | I_aux (I_undefined ctyp, _) -> []
-
- (* I_jump and I_goto will only appear as terminators for basic blocks. *)
- | I_aux ((I_jump _ | I_goto _), _) -> []
-
+ (* Should only appear as terminators for basic blocks. *)
+ | I_aux ((I_jump _ | I_goto _ | I_end _ | I_match_failure | I_undefined _), (_, l)) ->
+ Reporting.unreachable l __POS__ "SMT: Instruction should only appear as block terminator"
| instr ->
failwith ("Cannot translate: " ^ Pretty_print_sail.to_string (pp_instr instr))
@@ -1431,9 +1541,10 @@ let smt_cfnode all_cdefs ctx ssanodes =
| _ -> declare_const ctx id ctyp
in
smt_reg_decs @ List.map smt_start (NameMap.bindings inits)
- | CF_block instrs ->
- let ctx = { ctx with pathcond = get_pathcond ctx ssanodes } in
- List.concat (List.map (smt_instr ctx) instrs)
+ | CF_block (instrs, terminator) ->
+ let smt_instrs = List.map (smt_instr ctx) instrs in
+ let smt_term = smt_terminator ctx terminator in
+ List.concat (smt_instrs @ [smt_term])
(* We can ignore any non basic-block/start control-flow nodes *)
| _ -> []
@@ -1486,6 +1597,12 @@ let optimize_smt stack =
end
| (Declare_datatypes _ | Declare_tuple _) as def ->
Stack.push def stack'
+ | 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 ->
+ uses_in_exp rk; uses_in_exp addr;
+ Stack.push def stack'
| Assert exp as def ->
uses_in_exp exp;
Stack.push def stack'
@@ -1507,6 +1624,10 @@ let optimize_smt stack =
Queue.add (Define_const (var, typ, simp_smt_exp vars 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
| Assert exp ->
Queue.add (Assert (simp_smt_exp vars exp)) queue
| (Declare_datatypes _ | Declare_tuple _) as def ->
@@ -1582,16 +1703,6 @@ let expand_reg_deref ctx = function
end
| instr -> instr
-let smt_conj = function
- | [] -> Bool_lit true
- | [x] -> x
- | xs -> Fn ("and", xs)
-
-let smt_disj = function
- | [] -> Bool_lit false
- | [x] -> x
- | xs -> Fn ("or", xs)
-
let rec smt_query ctx = function
| Q_all ev ->
let stack = event_stack ctx ev in
@@ -1606,6 +1717,12 @@ 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 out_chan = open_out gv_file in
+ Jib_ssa.make_dot out_chan cfg;
+ close_out out_chan
+
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
@@ -1641,14 +1758,13 @@ let smt_cdef props lets name_file ctx all_cdefs = function
let visit_order =
try topsort cfg with
| Not_a_DAG n ->
- let gv_file = string_of_id function_id ^ ".gv" in
- let out_chan = open_out gv_file in
- make_dot out_chan cfg;
- close_out out_chan;
+ 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"
- prop_type (string_of_id function_id) n gv_file))
+ (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
@@ -1657,6 +1773,7 @@ let smt_cdef props lets name_file ctx all_cdefs = function
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;
@@ -1671,12 +1788,14 @@ let smt_cdef props lets name_file ctx all_cdefs = function
if prop_type = "counterexample" then
output_string out_chan "(set-option :produce-models true)\n";
+ let header = smt_header ctx all_cdefs in
+
if !(ctx.use_string) || !(ctx.use_real) then
output_string out_chan "(set-logic ALL)\n"
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") (smt_header ctx all_cdefs);
+ 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;
diff --git a/src/jib/jib_ssa.ml b/src/jib/jib_ssa.ml
index 9a9a3361..0f0f582e 100644
--- a/src/jib/jib_ssa.ml
+++ b/src/jib/jib_ssa.ml
@@ -53,6 +53,7 @@ open Jib
open Jib_util
module IntSet = Set.Make(struct type t = int let compare = compare end)
+module IntMap = Map.Make(struct type t = int let compare = compare end)
(**************************************************************************)
(* 1. Mutable graph type *)
@@ -60,14 +61,24 @@ module IntSet = Set.Make(struct type t = int let compare = compare end)
type 'a array_graph = {
mutable next : int;
- mutable nodes : ('a * IntSet.t * IntSet.t) option array
+ mutable nodes : ('a * IntSet.t * IntSet.t) option array;
+ mutable next_cond : int;
+ mutable conds : cval IntMap.t
}
let make ~initial_size () = {
next = 0;
- nodes = Array.make initial_size None
+ nodes = Array.make initial_size None;
+ next_cond = 1;
+ conds = IntMap.empty
}
+let get_cond graph n =
+ if n >= 0 then
+ IntMap.find n graph.conds
+ else
+ V_call (Bnot, [IntMap.find (abs n) graph.conds])
+
let get_vertex graph n = graph.nodes.(n)
let iter_graph f graph =
@@ -77,6 +88,12 @@ let iter_graph f graph =
| None -> ()
done
+let add_cond cval graph =
+ let n = graph.next_cond in
+ graph.conds <- IntMap.add n cval graph.conds;
+ graph.next_cond <- n + 1;
+ n
+
(** Add a vertex to a graph, returning the node index *)
let add_vertex data graph =
let n = graph.next in
@@ -187,12 +204,32 @@ let prune visited graph =
(* 2. Mutable control flow graph *)
(**************************************************************************)
+type terminator =
+ | T_undefined of ctyp
+ | T_match_failure
+ | T_end of name
+ | T_goto of string
+ | T_jump of int * string
+ | T_label of string
+ | T_none
+
type cf_node =
| CF_label of string
- | CF_block of instr list
- | CF_guard of cval
+ | CF_block of instr list * terminator
+ | CF_guard of int
| CF_start of ctyp NameMap.t
+let to_terminator graph = function
+ | I_label label -> T_label label
+ | I_goto label -> T_goto label
+ | I_jump (cval, label) ->
+ let n = add_cond cval graph in
+ T_jump (n, label)
+ | I_end name -> T_end name
+ | I_match_failure -> T_match_failure
+ | I_undefined ctyp -> T_undefined ctyp
+ | _ -> assert false
+
(* For now we only generate CFGs for flat lists of instructions *)
let control_flow_graph instrs =
let module StringMap = Map.Make(String) in
@@ -209,46 +246,42 @@ let control_flow_graph instrs =
let cf_split (I_aux (aux, _)) =
match aux with
- | I_block _ | I_label _ | I_goto _ | I_jump _ | I_if _ | I_end _ | I_match_failure | I_undefined _ -> true
+ | I_label _ | I_goto _ | I_jump _ | I_end _ | I_match_failure | I_undefined _ -> true
| _ -> false
in
let rec cfg preds instrs =
let before, after = instr_split_at cf_split instrs in
- let last = match after with
- | I_aux (I_label _, _) :: _ -> []
- | instr :: _ -> [instr]
- | _ -> []
+ let terminator, after = match after with
+ | I_aux (instr, _) :: after -> to_terminator graph instr, after
+ | [] -> T_none, []
in
- let preds = match before @ last with
- | [] -> preds
- | instrs ->
- let n = add_vertex ([], CF_block instrs) graph in
+ let preds = match before, terminator with
+ | [], (T_none | T_label _) -> preds
+ | instrs, _ ->
+ let n = add_vertex ([], CF_block (instrs, terminator)) graph in
List.iter (fun p -> add_edge p n graph) preds;
[n]
in
- match after with
- | I_aux ((I_end _ | I_match_failure | I_undefined _), _) :: after ->
+ match terminator with
+ | T_end _ | T_match_failure | T_undefined _ ->
cfg [] after
- | I_aux (I_goto label, _) :: after ->
+ | T_goto label ->
List.iter (fun p -> add_edge p (StringMap.find label !labels) graph) preds;
cfg [] after
- | I_aux (I_jump (cval, label), _) :: after ->
- let t = add_vertex ([], CF_guard cval) graph in
- let f = add_vertex ([], CF_guard (V_call (Bnot, [cval]))) graph in
+ | T_jump (cond, label) ->
+ let t = add_vertex ([], CF_guard cond) graph in
+ let f = add_vertex ([], CF_guard (- cond)) graph in
List.iter (fun p -> add_edge p t graph; add_edge p f graph) preds;
add_edge t (StringMap.find label !labels) graph;
cfg [f] after
- | I_aux (I_label label, _) :: after ->
+ | T_label label ->
cfg (StringMap.find label !labels :: preds) after
- | I_aux (_, (_, l)) :: after ->
- Reporting.unreachable l __POS__ "Could not add instruction to control-flow graph"
-
- | [] -> preds
+ | T_none -> preds
in
let start = add_vertex ([], CF_start NameMap.empty) graph in
@@ -422,7 +455,7 @@ let place_phi_functions graph df =
let orig_A n =
match graph.nodes.(n) with
- | Some ((_, CF_block instrs), _, _) ->
+ | Some ((_, CF_block (instrs, _)), _, _) ->
let vars = List.fold_left NameCTSet.union NameCTSet.empty (List.map instr_typed_writes instrs) in
let vars = NameCTSet.diff vars (all_decls instrs) in
all_vars := NameCTSet.union vars !all_vars;
@@ -542,21 +575,32 @@ let rename_variables graph root children =
counts := NameMap.add id i !counts;
push_stack id i;
I_init (ctyp, ssa_name i id, cval)
- | I_jump (cval, label) ->
- I_jump (fold_cval cval, label)
- | I_end id ->
- let i = top_stack id in
- I_end (ssa_name i id)
| instr -> instr
in
I_aux (aux, annot)
in
+ let ssa_terminator = function
+ | T_jump (cond, label) ->
+ begin match IntMap.find_opt cond graph.conds with
+ | Some cval ->
+ graph.conds <- IntMap.add cond (fold_cval cval) graph.conds;
+ T_jump (cond, label)
+ | None -> assert false
+ end
+ | T_end id ->
+ let i = top_stack id in
+ T_end (ssa_name i id)
+ | terminator -> terminator
+ in
+
let ssa_cfnode = function
| CF_start inits -> CF_start inits
- | CF_block instrs -> CF_block (List.map ssa_instr instrs)
+ | CF_block (instrs, terminator) ->
+ let instrs = List.map ssa_instr instrs in
+ CF_block (instrs, ssa_terminator terminator)
| CF_label label -> CF_label label
- | CF_guard cval -> CF_guard (fold_cval cval)
+ | CF_guard cond -> CF_guard cond
in
let ssa_ssanode = function
@@ -612,7 +656,12 @@ let rename_variables graph root children =
let place_pi_functions graph start idom children =
let get_guard = function
- | CF_guard guard -> [guard]
+ | CF_guard cond ->
+ begin match IntMap.find_opt (abs cond) graph.conds with
+ | Some guard when cond > 0 -> [guard]
+ | Some guard -> [V_call (Bnot, [guard])]
+ | None -> assert false
+ end
| _ -> []
in
let get_pi_contents ssanodes =
@@ -681,9 +730,9 @@ let string_of_phis = function
let string_of_node = function
| (phis, CF_label label) -> string_of_phis phis ^ label
- | (phis, CF_block instrs) -> string_of_phis phis ^ Util.string_of_list "\\l" (fun instr -> String.escaped (Pretty_print_sail.to_string (pp_instr ~short:true instr))) instrs
+ | (phis, CF_block (instrs, terminator)) -> string_of_phis phis ^ Util.string_of_list "\\l" (fun instr -> String.escaped (Pretty_print_sail.to_string (pp_instr ~short:true instr))) instrs
| (phis, CF_start inits) -> string_of_phis phis ^ "START"
- | (phis, CF_guard cval) -> string_of_phis phis ^ (String.escaped (Pretty_print_sail.to_string (pp_cval cval)))
+ | (phis, CF_guard cval) -> string_of_phis phis ^ string_of_int cval
let vertex_color = function
| (_, CF_start _) -> "peachpuff"
diff --git a/src/jib/jib_ssa.mli b/src/jib/jib_ssa.mli
index 759e1ccb..dadb20fd 100644
--- a/src/jib/jib_ssa.mli
+++ b/src/jib/jib_ssa.mli
@@ -60,6 +60,8 @@ val make : initial_size:int -> unit -> 'a array_graph
module IntSet : Set.S with type elt = int
+val get_cond : 'a array_graph -> int -> Jib.cval
+
val get_vertex : 'a array_graph -> int -> ('a * IntSet.t * IntSet.t) option
val iter_graph : ('a -> IntSet.t -> IntSet.t -> unit) -> 'a array_graph -> unit
@@ -77,10 +79,19 @@ exception Not_a_DAG of int
val topsort : 'a array_graph -> int list
+type terminator =
+ | T_undefined of Jib.ctyp
+ | T_match_failure
+ | T_end of Jib.name
+ | T_goto of string
+ | T_jump of int * string
+ | T_label of string
+ | T_none
+
type cf_node =
| CF_label of string
- | CF_block of Jib.instr list
- | CF_guard of Jib.cval
+ | CF_block of Jib.instr list * terminator
+ | CF_guard of int
| CF_start of Jib.ctyp NameMap.t
val control_flow_graph : Jib.instr list -> int * int list * ('a list * cf_node) array_graph
diff --git a/src/jib/jib_util.ml b/src/jib/jib_util.ml
index 378f5fae..3326d4ad 100644
--- a/src/jib/jib_util.ml
+++ b/src/jib/jib_util.ml
@@ -278,6 +278,8 @@ let string_of_name ?deref_current_exception:(dce=true) ?zencode:(zencode=true) =
let string_of_op = function
| Bnot -> "@not"
+ | Band -> "@and"
+ | Bor -> "@or"
| List_hd -> "@hd"
| List_tl -> "@tl"
| Bit_to_bool -> "@bit_to_bool"
@@ -896,6 +898,8 @@ let rec infer_call op vs =
match op, vs with
| Bit_to_bool, _ -> CT_bool
| Bnot, _ -> CT_bool
+ | Band, _ -> CT_bool
+ | Bor, _ -> CT_bool
| List_hd, [v] ->
begin match cval_ctyp v with
| CT_list ctyp -> ctyp
diff --git a/src/pretty_print_coq.ml b/src/pretty_print_coq.ml
index 55928e06..37e99662 100644
--- a/src/pretty_print_coq.ml
+++ b/src/pretty_print_coq.ml
@@ -920,8 +920,7 @@ let doc_lit (L_aux(lit,l)) =
let denom = Big_int.pow_int_positive 10 (String.length f) in
(Big_int.add (Big_int.mul (Big_int.of_string i) denom) (Big_int.of_string f), denom)
| _ ->
- raise (Reporting.Fatal_error
- (Reporting.Err_syntax_locn (l, "could not parse real literal"))) in
+ raise (Reporting.err_syntax_loc l "could not parse real literal") in
parens (separate space (List.map string [
"realFromFrac"; Big_int.to_string num; Big_int.to_string denom]))
diff --git a/src/pretty_print_lem.ml b/src/pretty_print_lem.ml
index ae86a0fd..89830d38 100644
--- a/src/pretty_print_lem.ml
+++ b/src/pretty_print_lem.ml
@@ -423,8 +423,7 @@ let doc_lit_lem (L_aux(lit,l)) =
let denom = Big_int.pow_int_positive 10 (String.length f) in
(Big_int.add (Big_int.mul (Big_int.of_string i) denom) (Big_int.of_string f), denom)
| _ ->
- raise (Reporting.Fatal_error
- (Reporting.Err_syntax_locn (l, "could not parse real literal"))) in
+ raise (Reporting.err_syntax_loc l "could not parse real literal") in
parens (separate space (List.map string [
"realFromFrac"; Big_int.to_string num; Big_int.to_string denom]))
diff --git a/src/property.mli b/src/property.mli
index 3789fa63..f84a85e3 100644
--- a/src/property.mli
+++ b/src/property.mli
@@ -90,6 +90,8 @@ val rewrite : tannot defs -> tannot defs
type event = Overflow | Assertion | Assumption | Match | Return
+val string_of_event : event -> string
+
module Event : sig
type t = event
val compare : event -> event -> int
diff --git a/src/reporting.ml b/src/reporting.ml
index c85f20ff..9387ee6b 100644
--- a/src/reporting.ml
+++ b/src/reporting.ml
@@ -133,7 +133,7 @@ type error =
| Err_unreachable of Parse_ast.l * (string * int * int * int) * string
| Err_todo of Parse_ast.l * string
| Err_syntax of Lexing.position * string
- | Err_syntax_locn of Parse_ast.l * string
+ | Err_syntax_loc of Parse_ast.l * string
| Err_lex of Lexing.position * string
| Err_type of Parse_ast.l * string
@@ -145,7 +145,7 @@ let dest_err = function
(Printf.sprintf "Internal error: Unreachable code (at \"%s\" line %d)" file line, Loc l, m ^ issues)
| Err_todo (l, m) -> ("Todo" ^ m, Loc l, "")
| Err_syntax (p, m) -> ("Syntax error", Pos p, m)
- | Err_syntax_locn (l, m) -> ("Syntax error", Loc l, m)
+ | Err_syntax_loc (l, m) -> ("Syntax error", Loc l, m)
| Err_lex (p, s) -> ("Lexical error", Pos p, s)
| Err_type (l, m) -> ("Type error", Loc l, m)
@@ -156,6 +156,7 @@ let err_todo l m = Fatal_error (Err_todo (l, m))
let err_unreachable l ocaml_pos m = Fatal_error (Err_unreachable (l, ocaml_pos, m))
let err_general l m = Fatal_error (Err_general (l, m))
let err_typ l m = Fatal_error (Err_type (l,m))
+let err_syntax_loc l m = Fatal_error (Err_syntax_loc (l, m))
let unreachable l pos msg =
raise (err_unreachable l pos msg)
diff --git a/src/reporting.mli b/src/reporting.mli
index 86399e84..28f2a799 100644
--- a/src/reporting.mli
+++ b/src/reporting.mli
@@ -93,7 +93,7 @@ type error =
| Err_todo of Parse_ast.l * string
| Err_syntax of Lexing.position * string
- | Err_syntax_locn of Parse_ast.l * string
+ | Err_syntax_loc of Parse_ast.l * string
| Err_lex of Lexing.position * string
| Err_type of Parse_ast.l * string
@@ -111,6 +111,9 @@ val err_unreachable : Parse_ast.l -> (string * int * int * int) -> string -> exn
(** [err_typ l m] is an abreviatiation for [Fatal_error (Err_type (l, m))] *)
val err_typ : Parse_ast.l -> string -> exn
+(** [err_syntax_loc] is an abbreviation for [Fatal_error (Err_syntax_loc (l, m))] *)
+val err_syntax_loc : Parse_ast.l -> string -> exn
+
val unreachable : Parse_ast.l -> (string * int * int * int) -> string -> 'a
val print_error : error -> unit
diff --git a/src/sail.ml b/src/sail.ml
index 13fea6f2..eff90fa3 100644
--- a/src/sail.ml
+++ b/src/sail.ml
@@ -321,6 +321,9 @@ let options = Arg.align ([
( "-ddump_flow_graphs",
Arg.Set Jib_compile.opt_debug_flow_graphs,
" (debug) dump flow analysis for Sail functions when compiling to C");
+ ( "-ddump_smt_graphs",
+ Arg.Set Jib_smt.opt_debug_graphs,
+ " (debug) dump flow analysis for properties when generating SMT");
( "-dtc_verbose",
Arg.Int (fun verbosity -> Type_check.opt_tc_debug := verbosity),
"<verbosity> (debug) verbose typechecker output: 0 is silent");
diff --git a/src/sail_lib.ml b/src/sail_lib.ml
index 83d58178..13ed491b 100644
--- a/src/sail_lib.ml
+++ b/src/sail_lib.ml
@@ -193,6 +193,10 @@ let sint = function
let add_int (x, y) = Big_int.add x y
let sub_int (x, y) = Big_int.sub x y
+let sub_nat (x, y) =
+ let z = Big_int.sub x y in
+ if Big_int.less z Big_int.zero then Big_int.zero else z
+
let mult (x, y) = Big_int.mul x y
(* This is euclidian division from lem *)
diff --git a/src/smtlib.ml b/src/smtlib.ml
index 7f485447..ab6ebf55 100644
--- a/src/smtlib.ml
+++ b/src/smtlib.ml
@@ -100,6 +100,16 @@ type smt_exp =
| Extract of int * int * smt_exp
| Tester of string * smt_exp
+let smt_conj = function
+ | [] -> Bool_lit true
+ | [x] -> x
+ | xs -> Fn ("and", xs)
+
+let smt_disj = function
+ | [] -> Bool_lit false
+ | [x] -> x
+ | xs -> Fn ("or", xs)
+
let extract i j x = Extract (i, j, x)
let bvnot x = Fn ("bvnot", [x])
@@ -172,6 +182,8 @@ type smt_def =
| Define_fun of string * (string * smt_typ) list * smt_typ * smt_exp
| Declare_const of string * smt_typ
| Define_const of string * smt_typ * smt_exp
+ | Write_mem of string * smt_exp * smt_exp * smt_exp
+ | Read_mem of string * smt_typ * smt_exp * smt_exp
| Declare_datatypes of string * (string * (string * smt_typ) list) list
| Declare_tuple of int
| Assert of smt_exp
@@ -232,6 +244,12 @@ let pp_smt_def =
| Define_const (name, ty, exp) ->
pp_sfun "define-const" [string name; pp_smt_typ ty; pp_smt_exp exp]
+ | Write_mem (name, wk, addr, data) ->
+ pp_sfun "declare-const" [string name; pp_smt_typ Bool]
+
+ | Read_mem (name, ty, rk, addr) ->
+ pp_sfun "declare-const" [string name; pp_smt_typ ty]
+
| Declare_datatypes (name, ctors) ->
let pp_ctor (ctor_name, fields) =
match fields with
diff --git a/src/type_check.ml b/src/type_check.ml
index 2ce6ea94..2be68ade 100644
--- a/src/type_check.ml
+++ b/src/type_check.ml
@@ -903,7 +903,7 @@ end = struct
try
Bindings.find id env.top_val_specs
with
- | Not_found -> typ_error env (id_loc id) ("No val spec found for " ^ string_of_id id)
+ | Not_found -> typ_error env (id_loc id) ("No type signature found for " ^ string_of_id id)
let get_val_spec id env =
try
diff --git a/src/value.ml b/src/value.ml
index 2760b220..6c2e0839 100644
--- a/src/value.ml
+++ b/src/value.ml
@@ -350,11 +350,8 @@ let value_sub_int = function
| _ -> failwith "value sub"
let value_sub_nat = function
- | [v1; v2] -> V_int (
- let n = Sail_lib.sub_int (coerce_int v1, coerce_int v2) in
- if Big_int.less_equal n Big_int.zero then Big_int.zero else n
- )
- | _ -> failwith "value sub_int"
+ | [v1; v2] -> V_int (Sail_lib.sub_nat (coerce_int v1, coerce_int v2))
+ | _ -> failwith "value sub_nat"
let value_negate = function
| [v1] -> V_int (Sail_lib.negate (coerce_int v1))