diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/jib/jib_smt.ml | 54 | ||||
| -rw-r--r-- | src/sail.ml | 3 | ||||
| -rw-r--r-- | src/smtlib.ml | 121 |
3 files changed, 146 insertions, 32 deletions
diff --git a/src/jib/jib_smt.ml b/src/jib/jib_smt.ml index 60b7202b..04546e3e 100644 --- a/src/jib/jib_smt.ml +++ b/src/jib/jib_smt.ml @@ -61,6 +61,8 @@ let zencode_name id = string_of_name ~deref_current_exception:false ~zencode:tru let opt_ignore_overflow = ref false +let opt_auto = ref false + type ctx = { (* Arbitrary-precision bitvectors are represented as a (BitVec lbits_index, BitVec (2 ^ lbits_index)) pair. *) lbits_index : int; @@ -498,7 +500,7 @@ let builtin_sign_extend ctx vbits vlen ret_ctyp = let top_bit_one = Fn ("=", [Extract (n - 1, n - 1, bv); Bin "1"]) in Ite (top_bit_one, Fn ("concat", [bvones (m - n); bv]), Fn ("concat", [bvzero (m - n); bv])) - | _ -> failwith "Cannot compile zero_extend" + | _ -> builtin_type_error "sign_extend" [vbits; vlen] (Some ret_ctyp) let builtin_shift shiftop ctx vbits vshift ret_ctyp = match cval_ctyp vbits with @@ -512,7 +514,7 @@ let builtin_shift shiftop ctx vbits vshift ret_ctyp = let shift = bvzeint ctx (lbits_size ctx) vshift in Fn ("Bits", [Fn ("len", [bv]); Fn (shiftop, [Fn ("contents", [bv]); shift])]) - | _ -> failwith ("Cannot compile shift: " ^ shiftop) + | _ -> builtin_type_error shiftop [vbits; vshift] (Some ret_ctyp) let builtin_not_bits ctx v ret_ctyp = match cval_ctyp v, ret_ctyp with @@ -529,35 +531,23 @@ let builtin_not_bits ctx v ret_ctyp = | _, _ -> builtin_type_error "not_bits" [v] (Some ret_ctyp) -let builtin_or_bits ctx v1 v2 ret_ctyp = +let builtin_bitwise fn ctx v1 v2 ret_ctyp = match cval_ctyp v1, cval_ctyp v2 with | CT_fbits (n, _), CT_fbits (m, _) -> assert (n = m); let smt1 = smt_cval ctx v1 in let smt2 = smt_cval ctx v2 in - bvor smt1 smt2 + Fn (fn, [smt1; smt2]) | CT_lbits _, CT_lbits _ -> let smt1 = smt_cval ctx v1 in let smt2 = smt_cval ctx v2 in - Fn ("Bits", [Fn ("len", [smt1]); bvor (Fn ("contents", [smt1])) (Fn ("contents", [smt2]))]) - - | _ -> failwith "Cannot compile or_bits" - -let builtin_and_bits ctx v1 v2 ret_ctyp = - match cval_ctyp v1, cval_ctyp v2 with - | CT_fbits (n, _), CT_fbits (m, _) -> - assert (n = m); - let smt1 = smt_cval ctx v1 in - let smt2 = smt_cval ctx v2 in - bvand smt1 smt2 + Fn ("Bits", [Fn ("len", [smt1]); Fn (fn, [Fn ("contents", [smt1]); Fn ("contents", [smt2])])]) - | CT_lbits _, CT_lbits _ -> - let smt1 = smt_cval ctx v1 in - let smt2 = smt_cval ctx v2 in - Fn ("Bits", [Fn ("len", [smt1]); bvand (Fn ("contents", [smt1])) (Fn ("contents", [smt2]))]) + | _ -> builtin_type_error fn [v1; v2] (Some ret_ctyp) - | _ -> failwith "Cannot compile or_bits" +let builtin_and_bits = builtin_bitwise "bvand" +let builtin_or_bits = builtin_bitwise "bvor" let builtin_append ctx v1 v2 ret_ctyp = match cval_ctyp v1, cval_ctyp v2, ret_ctyp with @@ -663,7 +653,7 @@ let builtin_unsigned ctx v ret_ctyp = let smt = smt_cval ctx v in Fn ("concat", [bvzero (ctx.lint_size - n); smt]) - | ctyp, _ -> failwith (Printf.sprintf "Cannot compile unsigned : %s -> %s" (string_of_ctyp ctyp) (string_of_ctyp ret_ctyp)) + | ctyp, _ -> builtin_type_error "unsigned" [v] (Some ret_ctyp) let builtin_signed ctx v ret_ctyp = match cval_ctyp v, ret_ctyp with @@ -1016,10 +1006,7 @@ let rec ctyp_of_typ ctx typ = | Typ_app (id, [A_aux (A_typ typ, _)]) when string_of_id id = "list" -> CT_list (ctyp_of_typ ctx typ) - (* When converting a sail bitvector type into C, we have three options in order of efficiency: - - If the length is obviously static and smaller than 64, use the fixed bits type (aka uint64_t), fbits. - - If the length is less than 64, then use a small bits type, sbits. - - If the length may be larger than 64, use a large bits type lbits. *) + (* Note that we have to use lbits for zero-length bitvectors because they are not allowed by SMTLIB *) | Typ_app (id, [A_aux (A_nexp n, _); A_aux (A_order ord, _); A_aux (A_typ (Typ_aux (Typ_id vtyp_id, _)), _)]) @@ -1477,14 +1464,12 @@ let smt_cdef props lets name_file ctx all_cdefs = function let stack = Stack.create () in smt_header ctx stack all_cdefs; - let smt_args = - List.map2 (fun id ctyp -> declare_const ctx (Name (id, 0)) ctyp) args arg_ctyps + let arg_decls = + List.map2 (fun id ctyp -> idecl ctyp (name id)) args arg_ctyps in - push_smt_defs stack smt_args; - let instrs = let open Jib_optimize in - (lets @ intervening_lets @ instrs) + (lets @ intervening_lets @ arg_decls @ instrs) |> inline all_cdefs (fun _ -> true) |> List.map (map_instr (expand_reg_deref ctx)) |> flatten_instrs @@ -1516,7 +1501,8 @@ let smt_cdef props lets name_file ctx all_cdefs = function end ) visit_order; - let out_chan = open_out (name_file (string_of_id function_id)) in + let fname = name_file (string_of_id function_id) in + let out_chan = open_out fname in if prop_type = "counterexample" then output_string out_chan "(set-option :produce-models true)\n"; output_string out_chan "(set-logic QF_AUFBVDT)\n"; @@ -1529,7 +1515,11 @@ let smt_cdef props lets name_file ctx all_cdefs = function output_string out_chan "(check-sat)\n"; if prop_type = "counterexample" then - output_string out_chan "(get-model)\n" + output_string out_chan "(get-model)\n"; + + close_out out_chan; + if prop_type = "counterexample" then + check_counterexample fname args | _ -> failwith "Bad function body" end diff --git a/src/sail.ml b/src/sail.ml index d9f4c55a..4be5279d 100644 --- a/src/sail.ml +++ b/src/sail.ml @@ -127,6 +127,9 @@ let options = Arg.align ([ ( "-smt", set_target "smt", " print SMT translated version of input"); + ( "-smt_auto", + Arg.Tuple [set_target "smt"; Arg.Set Jib_smt.opt_auto], + " generate SMT and automatically call the solver (implies -smt)"); ( "-smt_ignore_overflow", Arg.Set Jib_smt.opt_ignore_overflow, " ignore integer overflow in generated SMT"); diff --git a/src/smtlib.ml b/src/smtlib.ml index 7d6bb564..2b874a03 100644 --- a/src/smtlib.ml +++ b/src/smtlib.ml @@ -48,6 +48,9 @@ (* SUCH DAMAGE. *) (**************************************************************************) +open Ast +open Ast_util + type smt_typ = | Bitvec of int | Bool @@ -244,3 +247,121 @@ let string_of_smt_def def = Pretty_print_sail.to_string (pp_smt_def def) let output_smt_defs out_chan smt = List.iter (fun def -> output_string out_chan (string_of_smt_def def ^ "\n")) smt + +(**************************************************************************) +(* 2. Parser for SMT solver output *) +(**************************************************************************) + +(* Output format from each SMT solver does not seem to be completely + standardised, unlike the SMTLIB input format, but usually is some + form of s-expression based representation. Therefore we define a + simple parser for s-expressions using monadic parser combinators. *) + +type sexpr = List of (sexpr list) | Atom of string + +let rec string_of_sexpr = function + | List sexprs -> "(" ^ Util.string_of_list " " string_of_sexpr sexprs ^ ")" + | Atom str -> str + +type 'a parse_result = + | Ok of 'a * Str.split_result list + | Fail + +type 'a parser = Str.split_result list -> 'a parse_result + +let (>>=) (m : 'a parser) (f : 'a -> 'b parser) (toks : Str.split_result list) = + match m toks with + | Ok (r, toks) -> f r toks + | Fail -> Fail + +let pmap f m toks = + match m toks with + | Ok (r, toks) -> Ok (f r, toks) + | Fail -> Fail + +let token f = function + | tok :: toks -> + begin match f tok with + | Some x -> Ok (x, toks) + | None -> Fail + end + | [] -> Fail + +let preturn x toks = Ok (x, toks) + +let rec plist m toks = + match m toks with + | Ok (x, toks) -> + begin match plist m toks with + | Ok (xs, toks) -> Ok (x :: xs, toks) + | Fail -> Fail + end + | Fail -> Ok ([], toks) + +let pchoose m n toks = + match m toks with + | Fail -> n toks + | Ok (x, toks) -> Ok (x, toks) + +let lparen = token (function Str.Delim "(" -> Some () | _ -> None) +let rparen = token (function Str.Delim ")" -> Some () | _ -> None) +let atom = token (function Str.Text str -> Some str | _ -> None) + +let rec sexp toks = + let parse = + pchoose + (atom >>= fun str -> preturn (Atom str)) + (lparen >>= fun _ -> + plist sexp >>= fun xs -> + rparen >>= fun _ -> + preturn (List xs)) + in + parse toks + +let parse_sexps input = + let delim = Str.regexp "[ \n\t]+\\|(\\|)" in + let tokens = Str.full_split delim input in + let non_whitespace = function + | Str.Delim "(" | Str.Delim ")" | Str.Text _ -> true + | Str.Delim _ -> false + in + let tokens = List.filter non_whitespace tokens in + match plist sexp tokens with + | Ok (result, _) -> result + | Fail -> failwith "Parse failure" + +let rec find_arg id = function + | List [Atom "define-fun"; Atom str; List []; _; value] :: _ when (Ast_util.string_of_id id ^ "/0") = str -> + Some (id, value) + | _ :: sexps -> find_arg id sexps + | [] -> None + +let build_counterexample args sexps = + Util.map_filter (fun id -> find_arg id sexps) args + |> List.fold_left (fun m (k, v) -> Bindings.add k v m) Bindings.empty + +let check_counterexample fname args = + let open Printf in + prerr_endline ("Checking counterexample: " ^ fname ^ Util.string_of_list ", " Ast_util.string_of_id args); + let in_chan = ksprintf Unix.open_process_in "cvc4 --lang=smt2.6 %s" fname in + let lines = ref [] in + begin + try + while true do + lines := input_line in_chan :: !lines + done + with + | End_of_file -> () + end; + let solver_output = List.rev !lines |> String.concat "\n" |> parse_sexps in + begin match solver_output with + | Atom "sat" :: List (Atom "model" :: model) :: _ -> + prerr_endline (sprintf "Solver found counterexample: %s" Util.("success" |> green |> clear)); + let counterexample = build_counterexample args model in + if not (Bindings.cardinal counterexample = List.length args) then + ksprintf prerr_endline "Could not find all arguments in model %s" Util.("failure" |> red |> clear); + | _ -> + prerr_endline "failure" + end; + let status = Unix.close_process_in in_chan in + () |
