summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlasdair Armstrong2018-11-23 17:55:54 +0000
committerAlasdair Armstrong2018-11-23 18:27:31 +0000
commitea177d95766789b0500317f12fe0939d1508e19c (patch)
tree094ce6f42f23c8a526c8d75cf777d2eb400f0a8d /src
parent01a6e9b8ad00728fdbf12a76cda24144a75ec552 (diff)
C backend improvements
- Propagate types more accurately to improve optimization on ANF representation. - Add a generic optimization pass to remove redundant variables that simply alias other variables. - Modify Sail interactive mode, so it can compile a specification with the :compile command, view generated intermediate representation via the :ir <function> command, and step-through the IR with :exec <exp> (although this is very incomplete) - Introduce a third bitvector representation, between fast fixed-precision bitvectors, and variable length large bitvectors. The bitvector types are now from most efficient to least * CT_fbits for fixed precision, 64-bit or less bitvectors * CT_sbits for 64-bit or less, variable length bitvectors * CT_lbits for arbitrary variable length bitvectors - Support for generating C code using CT_sbits is currently incomplete, it just exists in the intermediate representation right now. - Include ctyp in AV_C_fragment, so we don't have to recompute it
Diffstat (limited to 'src')
-rw-r--r--src/anf.ml7
-rw-r--r--src/anf.mli2
-rw-r--r--src/bytecode_interpreter.ml162
-rw-r--r--src/bytecode_util.ml133
-rw-r--r--src/c_backend.ml496
-rw-r--r--src/c_backend.mli10
-rw-r--r--src/constant_fold.ml1
-rw-r--r--src/isail.ml138
-rw-r--r--src/sail.ml3
9 files changed, 678 insertions, 274 deletions
diff --git a/src/anf.ml b/src/anf.ml
index 50f3ccba..1f93e00f 100644
--- a/src/anf.ml
+++ b/src/anf.ml
@@ -127,7 +127,7 @@ and 'a aval =
| AV_list of ('a aval) list * 'a
| AV_vector of ('a aval) list * 'a
| AV_record of ('a aval) Bindings.t * 'a
- | AV_C_fragment of fragment * 'a
+ | AV_C_fragment of fragment * 'a * ctyp
(* Renaming variables in ANF expressions *)
@@ -183,7 +183,7 @@ let rec aval_rename from_id to_id = function
| AV_list (avals, typ) -> AV_list (List.map (aval_rename from_id to_id) avals, typ)
| AV_vector (avals, typ) -> AV_vector (List.map (aval_rename from_id to_id) avals, typ)
| AV_record (avals, typ) -> AV_record (Bindings.map (aval_rename from_id to_id) avals, typ)
- | AV_C_fragment (fragment, typ) -> AV_C_fragment (frag_rename from_id to_id fragment, typ)
+ | AV_C_fragment (fragment, typ, ctyp) -> AV_C_fragment (frag_rename from_id to_id fragment, typ, ctyp)
let rec aexp_rename from_id to_id (AE_aux (aexp, env, l)) =
let recur = aexp_rename from_id to_id in
@@ -423,7 +423,8 @@ and pp_aval = function
| AV_id (id, lvar) -> pp_lvar lvar (pp_id id)
| AV_tuple avals -> parens (separate_map (comma ^^ space) pp_aval avals)
| AV_ref (id, lvar) -> string "ref" ^^ space ^^ pp_lvar lvar (pp_id id)
- | AV_C_fragment (frag, typ) -> pp_annot typ (string (string_of_fragment frag |> Util.cyan |> Util.clear))
+ | AV_C_fragment (frag, typ, ctyp) ->
+ pp_annot typ (string ("(" ^ string_of_ctyp ctyp ^ ")" ^ string_of_fragment frag |> Util.cyan |> Util.clear))
| AV_vector (avals, typ) ->
pp_annot typ (string "[" ^^ separate_map (comma ^^ space) pp_aval avals ^^ string "]")
| AV_list (avals, typ) ->
diff --git a/src/anf.mli b/src/anf.mli
index 9854b04c..5e162b7c 100644
--- a/src/anf.mli
+++ b/src/anf.mli
@@ -96,7 +96,7 @@ and 'a aval =
| AV_list of ('a aval) list * 'a
| AV_vector of ('a aval) list * 'a
| AV_record of ('a aval) Bindings.t * 'a
- | AV_C_fragment of fragment * 'a
+ | AV_C_fragment of fragment * 'a * ctyp
val gensym : unit -> id
diff --git a/src/bytecode_interpreter.ml b/src/bytecode_interpreter.ml
new file mode 100644
index 00000000..398e0c9d
--- /dev/null
+++ b/src/bytecode_interpreter.ml
@@ -0,0 +1,162 @@
+(**************************************************************************)
+(* 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 Bytecode
+open Bytecode_util
+
+module StringMap = Map.Make(String)
+
+type 'a frame = {
+ jump_table : int StringMap.t;
+ locals : 'a Bindings.t;
+ pc : int;
+ instrs : instr array
+ }
+
+type 'a gstate = {
+ globals : 'a Bindings.t;
+ cdefs : cdef list
+ }
+
+type 'a stack = {
+ top : 'a frame;
+ ret : ('a -> 'a frame) list
+ }
+
+let make_jump_table instrs =
+ let rec aux n = function
+ | I_aux (I_label label, _) :: instrs -> StringMap.add label n (aux (n + 1) instrs)
+ | _ :: instrs -> aux (n + 1) instrs
+ | [] -> StringMap.empty
+ in
+ aux 0 instrs
+
+let new_gstate cdefs = {
+ globals = Bindings.empty;
+ cdefs = cdefs
+ }
+
+let new_stack instrs = {
+ top = {
+ jump_table = make_jump_table instrs;
+ locals = Bindings.empty;
+ pc = 0;
+ instrs = Array.of_list instrs
+ };
+ ret = []
+ }
+
+let with_top stack f =
+ { stack with top = f (stack.top) }
+
+let eval_fragment gstate locals = function
+ | F_id id ->
+ begin match Bindings.find_opt id locals with
+ | Some vl -> vl
+ | None ->
+ begin match Bindings.find_opt id gstate.globals with
+ | Some vl -> vl
+ | None -> failwith "Identifier not found"
+ end
+ end
+ | F_lit vl -> vl
+ | _ -> failwith "Cannot eval fragment"
+
+let is_function id = function
+ | CDEF_fundef (id', _, _, _) when Id.compare id id' = 0 -> true
+ | _ -> false
+
+let step (gstate, stack) =
+ let I_aux (instr_aux, (_, l)) = stack.top.instrs.(stack.top.pc) in
+ match instr_aux with
+ | I_decl _ ->
+ gstate, with_top stack (fun frame -> { frame with pc = frame.pc + 1 })
+
+ | I_init (_, id, (fragment, _)) ->
+ let vl = eval_fragment gstate stack.top.locals fragment in
+ gstate,
+ with_top stack (fun frame -> { frame with pc = frame.pc + 1; locals = Bindings.add id vl frame.locals })
+
+ | I_jump ((fragment, _), label) ->
+ let vl = eval_fragment gstate stack.top.locals fragment in
+ gstate,
+ begin match vl with
+ | V_bool true ->
+ with_top stack (fun frame -> { frame with pc = StringMap.find label frame.jump_table })
+ | V_bool false ->
+ with_top stack (fun frame -> { frame with pc = frame.pc + 1 })
+ | _ ->
+ failwith "Type error"
+ end
+
+ | I_funcall (clexp, _, id, cvals) ->
+ let args = List.map (fun (fragment, _) -> eval_fragment gstate stack.top.locals fragment) cvals in
+ let params, instrs =
+ match List.find_opt (is_function id) gstate.cdefs with
+ | Some (CDEF_fundef (_, _, params, instrs)) -> params, instrs
+ | _ -> failwith "Function not found"
+ in
+ gstate,
+ {
+ top = {
+ jump_table = make_jump_table instrs;
+ locals = List.fold_left2 (fun locals param arg -> Bindings.add param arg locals) Bindings.empty params args;
+ pc = 0;
+ instrs = Array.of_list instrs;
+ };
+ ret = (fun vl -> { stack.top with pc = stack.top.pc + 1 }) :: stack.ret
+ }
+
+ | I_goto label ->
+ gstate, with_top stack (fun frame -> { frame with pc = StringMap.find label frame.jump_table })
+
+ | _ -> raise (Reporting.err_unreachable l __POS__ "Unhandled instruction")
diff --git a/src/bytecode_util.ml b/src/bytecode_util.ml
index 6334210e..783f6793 100644
--- a/src/bytecode_util.ml
+++ b/src/bytecode_util.ml
@@ -131,6 +131,73 @@ let rec frag_rename from_id to_id = function
| F_raw raw -> F_raw raw
| F_poly f -> F_poly (frag_rename from_id to_id f)
+let cval_rename from_id to_id (frag, ctyp) = (frag_rename from_id to_id frag, ctyp)
+
+let rec clexp_rename from_id to_id = function
+ | CL_id (id, ctyp) when Id.compare id from_id = 0 -> CL_id (to_id, ctyp)
+ | CL_id (id, ctyp) -> CL_id (id, ctyp)
+ | CL_field (clexp, field) ->
+ CL_field (clexp_rename from_id to_id clexp, field)
+ | CL_addr clexp ->
+ CL_addr (clexp_rename from_id to_id clexp)
+ | CL_tuple (clexp, n) ->
+ CL_tuple (clexp_rename from_id to_id clexp, n)
+ | CL_current_exception ctyp -> CL_current_exception ctyp
+ | CL_have_exception -> CL_have_exception
+
+let rec instr_rename from_id to_id (I_aux (instr, aux)) =
+ let instr = match instr with
+ | I_decl (ctyp, id) when Id.compare id from_id = 0 -> I_decl (ctyp, to_id)
+ | I_decl (ctyp, id) -> I_decl (ctyp, id)
+
+ | I_init (ctyp, id, cval) when Id.compare id from_id = 0 ->
+ I_init (ctyp, to_id, cval_rename from_id to_id cval)
+ | I_init (ctyp, id, cval) ->
+ I_init (ctyp, id, cval_rename from_id to_id cval)
+
+ | I_if ((frag, ctyp1), then_instrs, else_instrs, ctyp2) ->
+ I_if ((frag_rename from_id to_id frag, ctyp1), then_instrs, else_instrs, ctyp2)
+
+ | I_jump (cval, label) -> I_jump (cval_rename from_id to_id cval, label)
+
+ | I_funcall (clexp, extern, id, args) ->
+ I_funcall (clexp_rename from_id to_id clexp, extern, id, List.map (cval_rename from_id to_id) args)
+
+ | I_copy (clexp, cval) -> I_copy (clexp_rename from_id to_id clexp, cval_rename from_id to_id cval)
+
+ | I_clear (ctyp, id) when Id.compare id from_id = 0 -> I_clear (ctyp, to_id)
+ | I_clear (ctyp, id) -> I_clear (ctyp, id)
+
+ | I_return cval -> I_return (cval_rename from_id to_id cval)
+
+ | I_block instrs -> I_block (List.map (instr_rename from_id to_id) instrs)
+
+ | I_try_block instrs -> I_try_block (List.map (instr_rename from_id to_id) instrs)
+
+ | I_throw cval -> I_throw (cval_rename from_id to_id cval)
+
+ | I_comment str -> I_comment str
+
+ | I_raw str -> I_raw str
+
+ | I_label label -> I_label label
+
+ | I_goto label -> I_goto label
+
+ | I_undefined ctyp -> I_undefined ctyp
+
+ | I_match_failure -> I_match_failure
+
+ | I_reset (ctyp, id) when Id.compare id from_id = 0 -> I_reset (ctyp, to_id)
+ | I_reset (ctyp, id) -> I_reset (ctyp, id)
+
+ | I_reinit (ctyp, id, cval) when Id.compare id from_id = 0 ->
+ I_reinit (ctyp, to_id, cval_rename from_id to_id cval)
+ | I_reinit (ctyp, id, cval) ->
+ I_reinit (ctyp, id, cval_rename from_id to_id cval)
+ in
+ I_aux (instr, aux)
+
(**************************************************************************)
(* 1. Instruction pretty printer *)
(**************************************************************************)
@@ -175,12 +242,14 @@ and string_of_fragment' ?zencode:(zencode=true) f =
(* String representation of ctyps here is only for debugging and
intermediate language pretty-printer. *)
and string_of_ctyp = function
- | CT_int -> "mpz_t"
- | CT_bits true -> "bv_t(dec)"
- | CT_bits false -> "bv_t(inc)"
- | CT_bits64 (n, true) -> "uint64_t(" ^ string_of_int n ^ ", dec)"
- | CT_bits64 (n, false) -> "uint64_t(" ^ string_of_int n ^ ", int)"
- | CT_int64 -> "int64_t"
+ | CT_int -> "int"
+ | CT_lbits true -> "lbits(dec)"
+ | CT_lbits false -> "lbits(inc)"
+ | CT_fbits (n, true) -> "fbits(" ^ string_of_int n ^ ", dec)"
+ | CT_fbits (n, false) -> "fbits(" ^ string_of_int n ^ ", int)"
+ | CT_sbits true -> "sbits(dec)"
+ | CT_sbits false -> "sbits(inc)"
+ | CT_int64 -> "int64"
| CT_bit -> "bit"
| CT_unit -> "unit"
| CT_bool -> "bool"
@@ -197,12 +266,14 @@ and string_of_ctyp = function
(** This function is like string_of_ctyp, but recursively prints all
constructors in variants and structs. Used for debug output. *)
and full_string_of_ctyp = function
- | CT_int -> "mpz_t"
- | CT_bits true -> "bv_t(dec)"
- | CT_bits false -> "bv_t(inc)"
- | CT_bits64 (n, true) -> "uint64_t(" ^ string_of_int n ^ ", dec)"
- | CT_bits64 (n, false) -> "uint64_t(" ^ string_of_int n ^ ", int)"
- | CT_int64 -> "int64_t"
+ | CT_int -> "int"
+ | CT_lbits true -> "lbits(dec)"
+ | CT_lbits false -> "lbits(inc)"
+ | CT_fbits (n, true) -> "fbits(" ^ string_of_int n ^ ", dec)"
+ | CT_fbits (n, false) -> "fbits(" ^ string_of_int n ^ ", int)"
+ | CT_sbits true -> "sbits(dec)"
+ | CT_sbits false -> "sbits(inc)"
+ | CT_int64 -> "int64"
| CT_bit -> "bit"
| CT_unit -> "unit"
| CT_bool -> "bool"
@@ -222,7 +293,8 @@ and full_string_of_ctyp = function
| CT_poly -> "*"
let rec map_ctyp f = function
- | (CT_int | CT_int64 | CT_bits _ | CT_bits64 _ | CT_bit | CT_unit | CT_bool | CT_real | CT_string | CT_poly | CT_enum _) as ctyp -> f ctyp
+ | (CT_int | CT_int64 | CT_lbits _ | CT_fbits _ | CT_sbits _
+ | CT_bit | CT_unit | CT_bool | CT_real | CT_string | CT_poly | CT_enum _) as ctyp -> f ctyp
| CT_tup ctyps -> f (CT_tup (List.map (map_ctyp f) ctyps))
| CT_ref ctyp -> f (CT_ref (map_ctyp f ctyp))
| CT_vector (direction, ctyp) -> f (CT_vector (direction, map_ctyp f ctyp))
@@ -233,8 +305,9 @@ let rec map_ctyp f = function
let rec ctyp_equal ctyp1 ctyp2 =
match ctyp1, ctyp2 with
| CT_int, CT_int -> true
- | CT_bits d1, CT_bits d2 -> d1 = d2
- | CT_bits64 (m1, d1), CT_bits64 (m2, d2) -> m1 = m2 && d1 = d2
+ | CT_lbits d1, CT_lbits d2 -> d1 = d2
+ | CT_sbits d1, CT_sbits d2 -> d1 = d2
+ | CT_fbits (m1, d1), CT_fbits (m2, d2) -> m1 = m2 && d1 = d2
| CT_bit, CT_bit -> true
| CT_int64, CT_int64 -> true
| CT_unit, CT_unit -> true
@@ -271,8 +344,9 @@ let rec ctyp_unify ctyp1 ctyp2 =
let rec ctyp_suprema = function
| CT_int -> CT_int
- | CT_bits d -> CT_bits d
- | CT_bits64 (_, d) -> CT_bits d
+ | CT_lbits d -> CT_lbits d
+ | CT_fbits (_, d) -> CT_lbits d
+ | CT_sbits d -> CT_lbits d
| CT_int64 -> CT_int
| CT_unit -> CT_unit
| CT_bool -> CT_bool
@@ -298,7 +372,7 @@ let rec ctyp_ids = function
IdSet.add id (List.fold_left (fun ids (_, ctyp) -> IdSet.union (ctyp_ids ctyp) ids) IdSet.empty ctors)
| CT_tup ctyps -> List.fold_left (fun ids ctyp -> IdSet.union (ctyp_ids ctyp) ids) IdSet.empty ctyps
| CT_vector (_, ctyp) | CT_list ctyp | CT_ref ctyp -> ctyp_ids ctyp
- | CT_int | CT_int64 | CT_bits _ | CT_bits64 _ | CT_unit
+ | CT_int | CT_int64 | CT_lbits _ | CT_fbits _ | CT_sbits _ | CT_unit
| CT_bool | CT_real | CT_bit | CT_string | CT_poly -> IdSet.empty
let rec unpoly = function
@@ -310,7 +384,7 @@ let rec unpoly = function
| f -> f
let rec is_polymorphic = function
- | CT_int | CT_int64 | CT_bits _ | CT_bits64 _ | CT_bit | CT_unit | CT_bool | CT_real | CT_string -> false
+ | CT_int | CT_int64 | CT_lbits _ | CT_fbits _ | CT_sbits _ | CT_bit | CT_unit | CT_bool | CT_real | CT_string -> false
| CT_tup ctyps -> List.exists is_polymorphic ctyps
| CT_enum _ -> false
| CT_struct (_, ctors) | CT_variant (_, ctors) -> List.exists (fun (_, ctyp) -> is_polymorphic ctyp) ctors
@@ -685,3 +759,24 @@ let rec map_instrs f (I_aux (instr, aux)) =
| I_comment _ | I_label _ | I_goto _ | I_raw _ | I_match_failure | I_undefined _ -> instr
in
I_aux (instr, aux)
+
+let rec instr_ids (I_aux (instr, _)) =
+ let reads, writes = instr_deps instr in
+ let get_id = function
+ | G_id id -> Some id
+ | _ -> None
+ in
+ NS.elements reads @ NS.elements writes
+ |> List.map get_id
+ |> Util.option_these
+ |> IdSet.of_list
+
+let rec filter_instrs f instrs =
+ let filter_instrs' = function
+ | I_aux (I_block instrs, aux) -> I_aux (I_block (filter_instrs f instrs), aux)
+ | I_aux (I_try_block instrs, aux) -> I_aux (I_try_block (filter_instrs f instrs), aux)
+ | I_aux (I_if (cval, instrs1, instrs2, ctyp), aux) ->
+ I_aux (I_if (cval, filter_instrs f instrs1, filter_instrs f instrs2, ctyp), aux)
+ | instr -> instr
+ in
+ List.filter f (List.map filter_instrs' instrs)
diff --git a/src/c_backend.ml b/src/c_backend.ml
index d3bb7c2a..2bac9945 100644
--- a/src/c_backend.ml
+++ b/src/c_backend.ml
@@ -71,6 +71,7 @@ let opt_no_main = ref false
let optimize_primops = ref false
let optimize_hoist_allocations = ref false
let optimize_struct_updates = ref false
+let optimize_alias = ref false
let c_debug str =
if !c_verbosity > 0 then prerr_endline (Lazy.force str) else ()
@@ -89,6 +90,13 @@ let zencode_id = function
let max_int64 = Big_int.of_int64 Int64.max_int
let min_int64 = Big_int.of_int64 Int64.min_int
+(** The context type contains two type-checking
+ environments. ctx.local_env contains the closest typechecking
+ environment, usually from the expression we are compiling, whereas
+ ctx.tc_env is the global type checking environment from
+ type-checking the entire AST. We also keep track of local variables
+ in ctx.locals, so we know when their type changes due to flow
+ typing. *)
type ctx =
{ records : (ctyp Bindings.t) Bindings.t;
enums : IdSet.t Bindings.t;
@@ -115,50 +123,55 @@ let initial_ctx env =
optimize_z3 = true;
}
-(** Convert a sail type into a C-type **)
+(** Convert a sail type into a C-type. This function can be quite
+ slow, because it uses ctx.local_env and Z3 to analyse the Sail
+ types and attempts to fit them into the smallest possible C
+ types, provided ctx.optimize_z3 is true (default) **)
let rec ctyp_of_typ ctx typ =
let Typ_aux (typ_aux, l) as typ = Env.expand_synonyms ctx.tc_env typ in
match typ_aux with
- | Typ_id id when string_of_id id = "bit" -> CT_bit
- | Typ_id id when string_of_id id = "bool" -> CT_bool
- | Typ_id id when string_of_id id = "int" -> CT_int
- | Typ_id id when string_of_id id = "nat" -> CT_int
+ | Typ_id id when string_of_id id = "bit" -> CT_bit
+ | Typ_id id when string_of_id id = "bool" -> CT_bool
+ | Typ_id id when string_of_id id = "int" -> CT_int
+ | Typ_id id when string_of_id id = "nat" -> CT_int
+ | Typ_id id when string_of_id id = "unit" -> CT_unit
+ | Typ_id id when string_of_id id = "string" -> CT_string
+ | Typ_id id when string_of_id id = "real" -> CT_real
+
| Typ_app (id, _) when string_of_id id = "range" || string_of_id id = "atom" ->
- begin
- match destruct_range Env.empty typ with
- | None -> assert false (* Checked if range type in guard *)
- | Some (kids, constr, n, m) ->
- match nexp_simp n, nexp_simp m with
- | Nexp_aux (Nexp_constant n, _), Nexp_aux (Nexp_constant m, _)
- when Big_int.less_equal min_int64 n && Big_int.less_equal m max_int64 ->
+ begin match destruct_range Env.empty typ with
+ | None -> assert false (* Checked if range type in guard *)
+ | Some (kids, constr, n, m) ->
+ match nexp_simp n, nexp_simp m with
+ | Nexp_aux (Nexp_constant n, _), Nexp_aux (Nexp_constant m, _)
+ when Big_int.less_equal min_int64 n && Big_int.less_equal m max_int64 ->
+ CT_int64
+ | n, m when ctx.optimize_z3 ->
+ if prove ctx.local_env (nc_lteq (nconstant min_int64) n) && prove ctx.local_env (nc_lteq m (nconstant max_int64)) then
CT_int64
- | n, m when ctx.optimize_z3 ->
- if prove ctx.local_env (nc_lteq (nconstant min_int64) n) && prove ctx.local_env (nc_lteq m (nconstant max_int64)) then
- CT_int64
- else
- CT_int
- | _ -> CT_int
+ else
+ CT_int
+ | _ -> CT_int
end
| Typ_app (id, [Typ_arg_aux (Typ_arg_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. *)
| Typ_app (id, [Typ_arg_aux (Typ_arg_nexp n, _);
Typ_arg_aux (Typ_arg_order ord, _);
Typ_arg_aux (Typ_arg_typ (Typ_aux (Typ_id vtyp_id, _)), _)])
when string_of_id id = "vector" && string_of_id vtyp_id = "bit" ->
- begin
- let direction = match ord with Ord_aux (Ord_dec, _) -> true | Ord_aux (Ord_inc, _) -> false | _ -> assert false in
- match nexp_simp n with
- | Nexp_aux (Nexp_constant n, _) when Big_int.less_equal n (Big_int.of_int 64) -> CT_bits64 (Big_int.to_int n, direction)
- | _ when not ctx.optimize_z3 -> CT_bits direction
- | _ -> CT_bits direction
- (* This is extremely slow :(
- match solve ctx.local_env n with
- | Some n when Big_int.less_equal n (Big_int.of_int 64) -> CT_bits64 (Big_int.to_int n, direction)
- | _ -> CT_bits direction
- *)
+ let direction = match ord with Ord_aux (Ord_dec, _) -> true | Ord_aux (Ord_inc, _) -> false | _ -> assert false in
+ begin match nexp_simp n with
+ | Nexp_aux (Nexp_constant n, _) when Big_int.less_equal n (Big_int.of_int 64) -> CT_fbits (Big_int.to_int n, direction)
+ | n when ctx.optimize_z3 && prove ctx.local_env (nc_lteq n (nint 64)) -> CT_lbits direction (* TODO: CT_sbits direction *)
+ | _ -> CT_lbits direction
end
+
| Typ_app (id, [Typ_arg_aux (Typ_arg_nexp n, _);
Typ_arg_aux (Typ_arg_order ord, _);
Typ_arg_aux (Typ_arg_typ typ, _)])
@@ -166,46 +179,54 @@ let rec ctyp_of_typ ctx typ =
let direction = match ord with Ord_aux (Ord_dec, _) -> true | Ord_aux (Ord_inc, _) -> false | _ -> assert false in
CT_vector (direction, ctyp_of_typ ctx typ)
- | Typ_id id when string_of_id id = "unit" -> CT_unit
- | Typ_id id when string_of_id id = "string" -> CT_string
- | Typ_id id when string_of_id id = "real" -> CT_real
-
| Typ_app (id, [Typ_arg_aux (Typ_arg_typ typ, _)]) when string_of_id id = "register" ->
CT_ref (ctyp_of_typ ctx typ)
- | Typ_id id | Typ_app (id, _) when Bindings.mem id ctx.records -> CT_struct (id, Bindings.find id ctx.records |> Bindings.bindings)
+ | Typ_id id | Typ_app (id, _) when Bindings.mem id ctx.records -> CT_struct (id, Bindings.find id ctx.records |> Bindings.bindings)
| Typ_id id | Typ_app (id, _) when Bindings.mem id ctx.variants -> CT_variant (id, Bindings.find id ctx.variants |> Bindings.bindings)
| Typ_id id when Bindings.mem id ctx.enums -> CT_enum (id, Bindings.find id ctx.enums |> IdSet.elements)
| Typ_tup typs -> CT_tup (List.map (ctyp_of_typ ctx) typs)
| Typ_exist _ when ctx.optimize_z3 ->
- (* Use Type_check.destruct_exist when optimising with z3, to ensure that we
- don't cause any type variable clashes in local_env. *)
+ (* Use Type_check.destruct_exist when optimising with z3, to
+ ensure that we don't cause any type variable clashes in
+ local_env, and that we can optimize the existential based upon
+ it's constraints. *)
begin match destruct_exist ctx.local_env typ with
| Some (kids, nc, typ) ->
let env = add_existential l kids nc ctx.local_env in
ctyp_of_typ { ctx with local_env = env } typ
- | None -> c_error "Existential cannot be destructured. This should be impossible!"
+ | None -> raise (Reporting.err_unreachable l __POS__ "Existential cannot be destructured!")
end
| Typ_exist (_, _, typ) -> ctyp_of_typ ctx typ
- | Typ_var kid -> CT_poly (* c_error ~loc:l ("Polymorphic type encountered " ^ string_of_kid kid) *)
+ | Typ_var kid -> CT_poly
| _ -> c_error ~loc:l ("No C type for type " ^ string_of_typ typ)
let rec is_stack_ctyp ctyp = match ctyp with
- | CT_bits64 _ | CT_int64 | CT_bit | CT_unit | CT_bool | CT_enum _ -> true
- | CT_bits _ | CT_int | CT_real | CT_string | CT_list _ | CT_vector _ -> false
+ | CT_fbits _ | CT_sbits _ | CT_int64 | CT_bit | CT_unit | CT_bool | CT_enum _ -> true
+ | CT_lbits _ | CT_int | CT_real | CT_string | CT_list _ | CT_vector _ -> false
| CT_struct (_, fields) -> List.for_all (fun (_, ctyp) -> is_stack_ctyp ctyp) fields
- | CT_variant (_, ctors) -> false (* List.for_all (fun (_, ctyp) -> is_stack_ctyp ctyp) ctors *) (*FIXME*)
+ | CT_variant (_, ctors) -> false (* List.for_all (fun (_, ctyp) -> is_stack_ctyp ctyp) ctors *) (* FIXME *)
| CT_tup ctyps -> List.for_all is_stack_ctyp ctyps
| CT_ref ctyp -> true
| CT_poly -> true
let is_stack_typ ctx typ = is_stack_ctyp (ctyp_of_typ ctx typ)
+let is_fbits_typ ctx typ =
+ match ctyp_of_typ ctx typ with
+ | CT_fbits _ -> true
+ | _ -> false
+
+let is_sbits_typ ctx typ =
+ match ctyp_of_typ ctx typ with
+ | CT_sbits _ -> true
+ | _ -> false
+
let ctor_bindings = List.fold_left (fun map (id, ctyp) -> Bindings.add id ctyp map) Bindings.empty
(**************************************************************************)
@@ -236,15 +257,15 @@ let hex_char =
let literal_to_fragment (L_aux (l_aux, _) as lit) =
match l_aux with
| L_num n when Big_int.less_equal min_int64 n && Big_int.less_equal n max_int64 ->
- Some (F_lit (V_int n))
+ Some (F_lit (V_int n), CT_int64)
| L_hex str when String.length str <= 16 ->
let padding = 16 - String.length str in
let padding = Util.list_init padding (fun _ -> Sail2_values.B0) in
let content = Util.string_to_list str |> List.map hex_char |> List.concat in
- Some (F_lit (V_bits (padding @ content)))
- | L_unit -> Some (F_lit V_unit)
- | L_true -> Some (F_lit (V_bool true))
- | L_false -> Some (F_lit (V_bool false))
+ Some (F_lit (V_bits (padding @ content)), CT_fbits (String.length str * 4, true))
+ | L_unit -> Some (F_lit V_unit, CT_unit)
+ | L_true -> Some (F_lit (V_bool true), CT_bool)
+ | L_false -> Some (F_lit (V_bool false), CT_bool)
| _ -> None
let c_literals ctx =
@@ -252,7 +273,7 @@ let c_literals ctx =
| AV_lit (lit, typ) as v when is_stack_ctyp (ctyp_of_typ { ctx with local_env = env } typ) ->
begin
match literal_to_fragment lit with
- | Some frag -> AV_C_fragment (frag, typ)
+ | Some (frag, ctyp) -> AV_C_fragment (frag, typ, ctyp)
| None -> v
end
| AV_tuple avals -> AV_tuple (List.map (c_literal env l) avals)
@@ -287,36 +308,45 @@ let rec c_aval ctx = function
| AV_lit (lit, typ) as v ->
begin
match literal_to_fragment lit with
- | Some frag -> AV_C_fragment (frag, typ)
+ | Some (frag, ctyp) -> AV_C_fragment (frag, typ, ctyp)
| None -> v
end
- | AV_C_fragment (str, typ) -> AV_C_fragment (str, typ)
+ | AV_C_fragment (str, typ, ctyp) -> AV_C_fragment (str, typ, ctyp)
(* An id can be converted to a C fragment if it's type can be
stack-allocated. *)
| AV_id (id, lvar) as v ->
begin
match lvar with
- | Local (_, typ) when is_stack_typ ctx typ ->
- begin
- try
- (* We need to check that id's type hasn't changed due to flow typing *)
- let _, ctyp = Bindings.find id ctx.locals in
- if is_stack_ctyp ctyp then
- AV_C_fragment (F_id id, typ)
- else
- v (* id's type went from heap -> stack due to flow typing, so it's really still heap allocated! *)
- with
- (* Hack: Assuming global letbindings don't change from flow typing... *)
- Not_found -> AV_C_fragment (F_id id, typ)
- (* failwith ("could not find " ^ string_of_id id ^ " in local variables") *)
- end
+ | Local (_, typ) ->
+ let ctyp = ctyp_of_typ ctx typ in
+ if is_stack_ctyp ctyp then
+ begin
+ try
+ (* We need to check that id's type hasn't changed due to flow typing *)
+ let _, ctyp' = Bindings.find id ctx.locals in
+ if ctyp_equal ctyp ctyp' then
+ AV_C_fragment (F_id id, typ, ctyp)
+ else
+ (* id's type changed due to flow
+ typing, so it's really still heap allocated! *)
+ v
+ with
+ (* Hack: Assuming global letbindings don't change from flow typing... *)
+ Not_found -> AV_C_fragment (F_id id, typ, ctyp)
+ end
+ else
+ v
| Register (_, _, typ) when is_stack_typ ctx typ ->
- AV_C_fragment (F_id id, typ)
+ let ctyp = ctyp_of_typ ctx typ in
+ if is_stack_ctyp ctyp then
+ AV_C_fragment (F_id id, typ, ctyp)
+ else
+ v
| _ -> v
end
| AV_vector (v, typ) when is_bitvector v && List.length v <= 64 ->
let bitstring = F_lit (V_bits (List.map value_of_aval_bit v)) in
- AV_C_fragment (bitstring, typ)
+ AV_C_fragment (bitstring, typ, CT_fbits (List.length v, true))
| AV_tuple avals -> AV_tuple (List.map (c_aval ctx) avals)
| aval -> aval
@@ -325,7 +355,7 @@ let is_c_fragment = function
| _ -> false
let c_fragment = function
- | AV_C_fragment (frag, _) -> frag
+ | AV_C_fragment (frag, _, _) -> frag
| _ -> assert false
let v_mask_lower i = F_lit (V_bits (Util.list_init i (fun _ -> Sail2_values.B1)))
@@ -365,8 +395,9 @@ let rec analyze_functions ctx f (AE_aux (aexp, env, l)) =
AE_for (id, aexp1, aexp2, aexp3, order, aexp4)
| AE_case (aval, cases, typ) ->
- let analyze_case (pat, aexp1, aexp2) =
+ let analyze_case (AP_aux (_, env, _) as pat, aexp1, aexp2) =
let pat_bindings = Bindings.bindings (apat_types pat) in
+ let ctx = { ctx with local_env = env } in
let ctx =
List.fold_left (fun ctx (id, typ) -> { ctx with locals = Bindings.add id (Immutable, ctyp_of_typ ctx typ) ctx.locals }) ctx pat_bindings
in
@@ -392,85 +423,108 @@ let analyze_primop' ctx id args typ =
c_debug (lazy ("Analyzing primop " ^ extern ^ "(" ^ Util.string_of_list ", " (fun aval -> Pretty_print_sail.to_string (pp_aval aval)) args ^ ")"));
match extern, args with
- | "eq_bits", [AV_C_fragment (v1, typ1); AV_C_fragment (v2, typ2)] ->
- AE_val (AV_C_fragment (F_op (v1, "==", v2), typ))
-
+ | "eq_bits", [AV_C_fragment (v1, typ1, _); AV_C_fragment (v2, typ2, _)] ->
+ AE_val (AV_C_fragment (F_op (v1, "==", v2), typ, CT_bool))
+(*
| "neq_bits", [AV_C_fragment (v1, typ1); AV_C_fragment (v2, typ2)] ->
AE_val (AV_C_fragment (F_op (v1, "!=", v2), typ))
+ *)
- | "eq_int", [AV_C_fragment (v1, typ1); AV_C_fragment (v2, typ2)] ->
- AE_val (AV_C_fragment (F_op (v1, "==", v2), typ))
+ | "eq_int", [AV_C_fragment (v1, typ1, _); AV_C_fragment (v2, typ2, _)] ->
+ AE_val (AV_C_fragment (F_op (v1, "==", v2), typ, CT_bool))
| "zeros", [_] ->
begin match destruct_vector ctx.tc_env typ with
| Some (Nexp_aux (Nexp_constant n, _), _, Typ_aux (Typ_id id, _))
when string_of_id id = "bit" && Big_int.less_equal n (Big_int.of_int 64) ->
- AE_val (AV_C_fragment (F_raw "0x0", typ))
+ AE_val (AV_C_fragment (F_raw "0x0", typ, CT_fbits (Big_int.to_int n, true)))
| _ -> no_change
end
- | "gteq", [AV_C_fragment (v1, _); AV_C_fragment (v2, _)] ->
- AE_val (AV_C_fragment (F_op (v1, ">=", v2), typ))
+ | "gteq", [AV_C_fragment (v1, _, _); AV_C_fragment (v2, _, _)] ->
+ AE_val (AV_C_fragment (F_op (v1, ">=", v2), typ, CT_bool))
- | "xor_bits", [AV_C_fragment (v1, typ1); AV_C_fragment (v2, typ2)] ->
- AE_val (AV_C_fragment (F_op (v1, "^", v2), typ))
+ | "xor_bits", [AV_C_fragment (v1, typ1, ctyp); AV_C_fragment (v2, typ2, _)] ->
+ AE_val (AV_C_fragment (F_op (v1, "^", v2), typ, ctyp))
- | "or_bits", [AV_C_fragment (v1, typ1); AV_C_fragment (v2, typ2)] ->
- AE_val (AV_C_fragment (F_op (v1, "|", v2), typ))
+ | "or_bits", [AV_C_fragment (v1, typ1, ctyp); AV_C_fragment (v2, typ2, _)] ->
+ AE_val (AV_C_fragment (F_op (v1, "|", v2), typ, ctyp))
- | "and_bits", [AV_C_fragment (v1, typ1); AV_C_fragment (v2, typ2)] ->
- AE_val (AV_C_fragment (F_op (v1, "&", v2), typ))
+ | "and_bits", [AV_C_fragment (v1, typ1, ctyp); AV_C_fragment (v2, typ2, _)] ->
+ AE_val (AV_C_fragment (F_op (v1, "&", v2), typ, ctyp))
- | "not_bits", [AV_C_fragment (v, _)] ->
+ | "not_bits", [AV_C_fragment (v, _, ctyp)] ->
begin match destruct_vector ctx.tc_env typ with
| Some (Nexp_aux (Nexp_constant n, _), _, Typ_aux (Typ_id id, _))
when string_of_id id = "bit" && Big_int.less_equal n (Big_int.of_int 64) ->
- AE_val (AV_C_fragment (F_op (F_unary ("~", v), "&", v_mask_lower (Big_int.to_int n)), typ))
+ AE_val (AV_C_fragment (F_op (F_unary ("~", v), "&", v_mask_lower (Big_int.to_int n)), typ, ctyp))
| _ -> no_change
end
- | "vector_subrange", [AV_C_fragment (vec, _); AV_C_fragment (f, _); AV_C_fragment (t, _)] when is_stack_typ ctx typ ->
+ | "vector_subrange", [AV_C_fragment (vec, _, _); AV_C_fragment (f, _, _); AV_C_fragment (t, _, _)]
+ when is_fbits_typ ctx typ ->
let len = F_op (f, "-", F_op (t, "-", v_one)) in
- AE_val (AV_C_fragment (F_op (F_call ("safe_rshift", [F_raw "UINT64_MAX"; F_op (v_int 64, "-", len)]), "&", F_op (vec, ">>", t)), typ))
+ AE_val (AV_C_fragment (F_op (F_call ("safe_rshift", [F_raw "UINT64_MAX"; F_op (v_int 64, "-", len)]), "&", F_op (vec, ">>", t)),
+ typ,
+ ctyp_of_typ ctx typ))
- | "vector_access", [AV_C_fragment (vec, _); AV_C_fragment (n, _)] ->
- AE_val (AV_C_fragment (F_op (v_one, "&", F_op (vec, ">>", n)), typ))
+ | "vector_access", [AV_C_fragment (vec, _, _); AV_C_fragment (n, _, _)] ->
+ AE_val (AV_C_fragment (F_op (v_one, "&", F_op (vec, ">>", n)), typ, CT_bit))
- | "eq_bit", [AV_C_fragment (a, _); AV_C_fragment (b, _)] ->
- AE_val (AV_C_fragment (F_op (a, "==", b), typ))
+ | "eq_bit", [AV_C_fragment (a, _, _); AV_C_fragment (b, _, _)] ->
+ AE_val (AV_C_fragment (F_op (a, "==", b), typ, CT_bool))
- | "slice", [AV_C_fragment (vec, _); AV_C_fragment (start, _); AV_C_fragment (len, _)] when is_stack_typ ctx typ ->
- AE_val (AV_C_fragment (F_op (F_call ("safe_rshift", [F_raw "UINT64_MAX"; F_op (v_int 64, "-", len)]), "&", F_op (vec, ">>", start)), typ))
+ | "slice", [AV_C_fragment (vec, _, _); AV_C_fragment (start, _, _); AV_C_fragment (len, _, _)]
+ when is_fbits_typ ctx typ ->
+ AE_val (AV_C_fragment (F_op (F_call ("safe_rshift", [F_raw "UINT64_MAX"; F_op (v_int 64, "-", len)]), "&", F_op (vec, ">>", start)),
+ typ,
+ ctyp_of_typ ctx typ))
| "undefined_bit", _ ->
- AE_val (AV_C_fragment (F_lit (V_bit Sail2_values.B0), typ))
+ AE_val (AV_C_fragment (F_lit (V_bit Sail2_values.B0), typ, CT_bit))
- | "undefined_vector", [AV_C_fragment (len, _); _] ->
+ | "undefined_vector", [AV_C_fragment (len, _, _); _] ->
begin match destruct_vector ctx.tc_env typ with
| Some (Nexp_aux (Nexp_constant n, _), _, Typ_aux (Typ_id id, _))
when string_of_id id = "bit" && Big_int.less_equal n (Big_int.of_int 64) ->
- AE_val (AV_C_fragment (F_lit (V_bit Sail2_values.B0), typ))
+ AE_val (AV_C_fragment (F_lit (V_bit Sail2_values.B0), typ, ctyp_of_typ ctx typ))
| _ -> no_change
end
- | "sail_uint", [AV_C_fragment (frag, vtyp)] ->
+ | "sail_unsigned", [AV_C_fragment (frag, vtyp, _)] ->
begin match destruct_vector ctx.tc_env vtyp with
| Some (Nexp_aux (Nexp_constant n, _), _, _)
when Big_int.less_equal n (Big_int.of_int 63) && is_stack_typ ctx typ ->
- AE_val (AV_C_fragment (frag, typ))
+ AE_val (AV_C_fragment (F_call ("fast_unsigned", [frag]), typ, ctyp_of_typ ctx typ))
| _ -> no_change
end
- | "replicate_bits", [AV_C_fragment (vec, vtyp); AV_C_fragment (times, _)] ->
+ | "add_int", [AV_C_fragment (op1, _, _); AV_C_fragment (op2, _, _)] ->
+ begin match destruct_range Env.empty typ with
+ | None -> no_change
+ | Some (kids, constr, n, m) ->
+ match nexp_simp n, nexp_simp m with
+ | Nexp_aux (Nexp_constant n, _), Nexp_aux (Nexp_constant m, _)
+ when Big_int.less_equal min_int64 n && Big_int.less_equal m max_int64 ->
+ AE_val (AV_C_fragment (F_op (op1, "+", op2), typ, CT_int64))
+ | n, m when prove ctx.local_env (nc_lteq (nconstant min_int64) n) && prove ctx.local_env (nc_lteq m (nconstant max_int64)) ->
+ AE_val (AV_C_fragment (F_op (op1, "+", op2), typ, CT_int64))
+ | _ -> no_change
+ end
+
+ | "neg_int", [AV_C_fragment (frag, _, _)] ->
+ AE_val (AV_C_fragment (F_op (v_int 0, "-", frag), typ, CT_int64))
+
+ | "replicate_bits", [AV_C_fragment (vec, vtyp, _); AV_C_fragment (times, _, _)] ->
begin match destruct_vector ctx.tc_env typ, destruct_vector ctx.tc_env vtyp with
| Some (Nexp_aux (Nexp_constant n, _), _, _), Some (Nexp_aux (Nexp_constant m, _), _, _)
when Big_int.less_equal n (Big_int.of_int 64) ->
- AE_val (AV_C_fragment (F_call ("fast_replicate_bits", [F_lit (V_int m); vec; times]), typ))
+ AE_val (AV_C_fragment (F_call ("fast_replicate_bits", [F_lit (V_int m); vec; times]), typ, ctyp_of_typ ctx typ))
| _ -> no_change
end
| "undefined_bool", _ ->
- AE_val (AV_C_fragment (F_lit (V_bool false), typ))
+ AE_val (AV_C_fragment (F_lit (V_bool false), typ, CT_bool))
| _, _ ->
c_debug (lazy ("No optimization routine found"));
@@ -622,7 +676,10 @@ let rec chunkify n xs =
| xs, ys -> xs :: chunkify n ys
let rec compile_aval l ctx = function
- | AV_C_fragment (frag, typ) ->
+ | AV_C_fragment (frag, typ, ctyp) ->
+ let ctyp' = ctyp_of_typ ctx typ in
+ if not (ctyp_equal ctyp ctyp') then
+ raise (Reporting.err_unreachable l __POS__ (string_of_ctyp ctyp ^ " != " ^ string_of_ctyp ctyp'));
[], (frag, ctyp_of_typ ctx typ), []
| AV_id (id, typ) ->
@@ -665,6 +722,8 @@ let rec compile_aval l ctx = function
(F_id gs, CT_real),
[iclear CT_real gs]
+ | AV_lit (L_aux (L_unit, _), _) -> [], (F_lit V_unit, CT_unit), []
+
| AV_lit (L_aux (_, l) as lit, _) ->
c_error ~loc:l ("Encountered unexpected literal " ^ string_of_lit lit)
@@ -706,9 +765,9 @@ let rec compile_aval l ctx = function
let len = List.length avals in
match destruct_vector ctx.tc_env typ with
| Some (_, Ord_aux (Ord_inc, _), _) ->
- [], (bitstring, CT_bits64 (len, false)), []
+ [], (bitstring, CT_fbits (len, false)), []
| Some (_, Ord_aux (Ord_dec, _), _) ->
- [], (bitstring, CT_bits64 (len, true)), []
+ [], (bitstring, CT_fbits (len, true)), []
| Some _ ->
c_error "Encountered order polymorphic bitvector literal"
| None ->
@@ -723,12 +782,12 @@ let rec compile_aval l ctx = function
let first_chunk = bitstring (Util.take (len mod 64) avals) in
let chunks = Util.drop (len mod 64) avals |> chunkify 64 |> List.map bitstring in
let gs = gensym () in
- [iinit (CT_bits true) gs (first_chunk, CT_bits64 (len mod 64, true))]
- @ List.map (fun chunk -> ifuncall (CL_id (gs, CT_bits true))
+ [iinit (CT_lbits true) gs (first_chunk, CT_fbits (len mod 64, true))]
+ @ List.map (fun chunk -> ifuncall (CL_id (gs, CT_lbits true))
(mk_id "append_64")
- [(F_id gs, CT_bits true); (chunk, CT_bits64 (64, true))]) chunks,
- (F_id gs, CT_bits true),
- [iclear (CT_bits true) gs]
+ [(F_id gs, CT_lbits true); (chunk, CT_fbits (64, true))]) chunks,
+ (F_id gs, CT_lbits true),
+ [iclear (CT_lbits true) gs]
(* If we have a bitvector value, that isn't a literal then we need to set bits individually. *)
| AV_vector (avals, Typ_aux (Typ_app (id, [_; Typ_arg_aux (Typ_arg_order ord, _); Typ_arg_aux (Typ_arg_typ (Typ_aux (Typ_id bit_id, _)), _)]), _))
@@ -740,7 +799,7 @@ let rec compile_aval l ctx = function
| Ord_aux (Ord_var _, _) -> c_error "Polymorphic vector direction found"
in
let gs = gensym () in
- let ctyp = CT_bits64 (len, direction) in
+ let ctyp = CT_fbits (len, direction) in
let mask i = V_bits (Util.list_init (63 - i) (fun _ -> Sail2_values.B0) @ [Sail2_values.B1] @ Util.list_init i (fun _ -> Sail2_values.B0)) in
let aval_mask i aval =
let setup, cval, cleanup = compile_aval l ctx aval in
@@ -988,7 +1047,7 @@ let rec compile_aexp ctx (AE_aux (aexp_aux, env, l)) =
let compile_case (apat, guard, body) =
let trivial_guard = match guard with
| AE_aux (AE_val (AV_lit (L_aux (L_true, _), _)), _, _)
- | AE_aux (AE_val (AV_C_fragment (F_lit (V_bool true), _)), _, _) -> true
+ | AE_aux (AE_val (AV_C_fragment (F_lit (V_bool true), _, _)), _, _) -> true
| _ -> false
in
let case_label = label "case_" in
@@ -1029,7 +1088,7 @@ let rec compile_aexp ctx (AE_aux (aexp_aux, env, l)) =
let compile_case (apat, guard, body) =
let trivial_guard = match guard with
| AE_aux (AE_val (AV_lit (L_aux (L_true, _), _)), _, _)
- | AE_aux (AE_val (AV_C_fragment (F_lit (V_bool true), _)), _, _) -> true
+ | AE_aux (AE_val (AV_C_fragment (F_lit (V_bool true), _, _)), _, _) -> true
| _ -> false
in
let try_label = label "try_" in
@@ -1736,7 +1795,7 @@ let rec instrs_rename from_id to_id =
| [] -> []
let hoist_ctyp = function
- | CT_int | CT_bits _ | CT_struct _ -> true
+ | CT_int | CT_lbits _ | CT_struct _ -> true
| _ -> false
let hoist_counter = ref 0
@@ -1797,39 +1856,39 @@ let flat_id () =
incr flat_counter;
id
-let flatten_instrs =
- let rec flatten = function
- | I_aux (I_decl (ctyp, decl_id), aux) :: instrs ->
- let fid = flat_id () in
- I_aux (I_decl (ctyp, fid), aux) :: flatten (instrs_rename decl_id fid instrs)
-
- | I_aux ((I_block block | I_try_block block), _) :: instrs ->
- flatten block @ flatten instrs
-
- | I_aux (I_if (cval, then_instrs, else_instrs, _), _) :: instrs ->
- let then_label = label "then_" in
- let endif_label = label "endif_" in
- [ijump cval then_label]
- @ flatten else_instrs
- @ [igoto endif_label]
- @ [ilabel then_label]
- @ flatten then_instrs
- @ [ilabel endif_label]
- @ flatten instrs
-
- | I_aux (I_comment _, _) :: instrs -> flatten instrs
-
- | instr :: instrs -> instr :: flatten instrs
- | [] -> []
- in
+let rec flatten_instrs = function
+ | I_aux (I_decl (ctyp, decl_id), aux) :: instrs ->
+ let fid = flat_id () in
+ I_aux (I_decl (ctyp, fid), aux) :: flatten_instrs (instrs_rename decl_id fid instrs)
+
+ | I_aux ((I_block block | I_try_block block), _) :: instrs ->
+ flatten_instrs block @ flatten_instrs instrs
+
+ | I_aux (I_if (cval, then_instrs, else_instrs, _), _) :: instrs ->
+ let then_label = label "then_" in
+ let endif_label = label "endif_" in
+ [ijump cval then_label]
+ @ flatten_instrs else_instrs
+ @ [igoto endif_label]
+ @ [ilabel then_label]
+ @ flatten_instrs then_instrs
+ @ [ilabel endif_label]
+ @ flatten_instrs instrs
+
+ | I_aux (I_comment _, _) :: instrs -> flatten_instrs instrs
+
+ | instr :: instrs -> instr :: flatten_instrs instrs
+ | [] -> []
+
+let flatten_cdef =
function
| CDEF_fundef (function_id, heap_return, args, body) ->
flat_counter := 0;
- CDEF_fundef (function_id, heap_return, args, flatten body)
+ CDEF_fundef (function_id, heap_return, args, flatten_instrs body)
| CDEF_let (n, bindings, instrs) ->
flat_counter := 0;
- CDEF_let (n, bindings, flatten instrs)
+ CDEF_let (n, bindings, flatten_instrs instrs)
| cdef -> cdef
@@ -1960,75 +2019,98 @@ let sort_ctype_defs cdefs =
ctype_defs @ cdefs
- (*
-(* When this optimization fires we know we have bytecode of the form
-
- recreate x : S; x = y; ...
-
- when this continues with x.A = a, x.B = b etc until y = x. Then
- provided there are no further references to x we can eliminate
- the variable x.
-
- Must be called after hoist_allocations, otherwise does nothing. *)
-let fix_struct_updates ctx =
- (* FIXME need to check no remaining references *)
- let rec fix_updates struct_id id = function
- | I_aux (I_copy (CL_field (struct_id', field, ctyp), cval), aux) :: instrs
- when Id.compare struct_id struct_id' = 0 ->
- Util.option_map (fun instrs -> I_aux (I_copy (CL_field (id, field, ctyp), cval), aux) :: instrs) (fix_updates struct_id id instrs)
- | I_aux (I_copy (CL_id id', (F_id struct_id', ctyp)), aux) :: instrs
- when Id.compare struct_id struct_id' = 0 && Id.compare id id' = 0->
- Some instrs
- | _ -> None
+let removed = icomment "REMOVED"
+
+let is_not_removed = function
+ | I_aux (I_comment "REMOVED", _) -> false
+ | _ -> true
+
+(** This optimization looks for patterns of the form:
+
+ create x : t;
+ x = y;
+ // modifications to x, and no changes to y
+ y = x;
+ // no further changes to x
+ kill x;
+
+ If found, we can remove the variable x, and directly modify y instead. *)
+let remove_alias ctx =
+ let pattern ctyp id =
+ let alias = ref None in
+ let rec scan ctyp id n instrs =
+ match n, !alias, instrs with
+ | 0, None, I_aux (I_copy (CL_id (id', ctyp'), (F_id a, ctyp'')), _) :: instrs
+ when Id.compare id id' = 0 && ctyp_equal ctyp ctyp' && ctyp_equal ctyp' ctyp'' ->
+ alias := Some a;
+ scan ctyp id 1 instrs
+
+ | 1, Some a, I_aux (I_copy (CL_id (a', ctyp'), (F_id id', ctyp'')), _) :: instrs
+ when Id.compare a a' = 0 && Id.compare id id' = 0 && ctyp_equal ctyp ctyp' && ctyp_equal ctyp' ctyp'' ->
+ scan ctyp id 2 instrs
+
+ | 1, Some a, instr :: instrs ->
+ if IdSet.mem a (instr_ids instr) then
+ None
+ else
+ scan ctyp id 1 instrs
+
+ | 2, Some a, I_aux (I_clear (ctyp', id'), _) :: instrs
+ when Id.compare id id' = 0 && ctyp_equal ctyp ctyp' ->
+ scan ctyp id 2 instrs
+
+ | 2, Some a, instr :: instrs ->
+ if IdSet.mem id (instr_ids instr) then
+ None
+ else
+ scan ctyp id 2 instrs
+
+ | 2, Some a, [] -> !alias
+
+ | n, _, _ :: instrs when n = 0 || n > 2 -> scan ctyp id n instrs
+ | _, _, I_aux (_, (_, l)) :: instrs -> raise (Reporting.err_unreachable l __POS__ "optimize_alias")
+ | _, _, [] -> None
+ in
+ scan ctyp id 0
in
- let rec fix_updates_ret struct_id id = function
- | I_aux (I_copy (CL_field (struct_id', field, ctyp), cval), aux) :: instrs
- when Id.compare struct_id struct_id' = 0 ->
- Util.option_map (fun instrs -> I_aux (I_copy (CL_addr_field (id, field, ctyp), cval), aux) :: instrs) (fix_updates_ret struct_id id instrs)
- | I_aux (I_copy (CL_addr id', (F_id struct_id', ctyp)), aux) :: instrs
- when Id.compare struct_id struct_id' = 0 && Id.compare id id' = 0->
- Some instrs
- | _ -> None
+ let remove_alias id alias = function
+ | I_aux (I_copy (CL_id (id', _), (F_id alias', _)), _)
+ when Id.compare id id' = 0 && Id.compare alias alias' = 0 -> removed
+ | I_aux (I_copy (CL_id (alias', _), (F_id id', _)), _)
+ when Id.compare id id' = 0 && Id.compare alias alias' = 0 -> removed
+ | I_aux (I_clear (_, id'), _) -> removed
+ | instr -> instr
in
- let rec opt hr = function
- | (I_aux (I_reset (ctyp, struct_id), _) as instr1)
- :: (I_aux (I_copy (CL_id (struct_id', _), (F_id id, ctyp')), _) as instr2)
- :: instrs
- when is_ct_struct ctyp && ctyp_equal ctyp ctyp' && Id.compare struct_id struct_id' = 0 ->
- begin match fix_updates struct_id id instrs with
- | None -> instr1 :: instr2 :: opt hr instrs
- | Some updated -> opt hr updated
+ let rec opt = function
+ | I_aux (I_decl (ctyp, id), _) as instr :: instrs ->
+ begin match pattern ctyp id instrs with
+ | None -> instr :: opt instrs
+ | Some alias ->
+ let instrs = List.map (map_instr (remove_alias id alias)) instrs in
+ filter_instrs is_not_removed (List.map (instr_rename id alias) instrs)
end
- | (I_aux (I_reset (ctyp, struct_id), _) as instr) :: instrs
- when is_ct_struct ctyp && Util.is_some hr ->
- let id = match hr with Some id -> id | None -> assert false in
- begin match fix_updates_ret struct_id id instrs with
- | None -> instr :: opt hr instrs
- | Some updated -> opt hr updated
- end
-
- | I_aux (I_block block, aux) :: instrs -> I_aux (I_block (opt hr block), aux) :: opt hr instrs
- | I_aux (I_try_block block, aux) :: instrs -> I_aux (I_try_block (opt hr block), aux) :: opt hr instrs
+ | I_aux (I_block block, aux) :: instrs -> I_aux (I_block (opt block), aux) :: opt instrs
+ | I_aux (I_try_block block, aux) :: instrs -> I_aux (I_try_block (opt block), aux) :: opt instrs
| I_aux (I_if (cval, then_instrs, else_instrs, ctyp), aux) :: instrs ->
- I_aux (I_if (cval, opt hr then_instrs, opt hr else_instrs, ctyp), aux) :: opt hr instrs
+ I_aux (I_if (cval, opt then_instrs, opt else_instrs, ctyp), aux) :: opt instrs
- | instr :: instrs -> instr :: opt hr instrs
+ | instr :: instrs ->
+ instr :: opt instrs
| [] -> []
in
function
| CDEF_fundef (function_id, heap_return, args, body) ->
- [CDEF_fundef (function_id, heap_return, args, opt heap_return body)]
+ [CDEF_fundef (function_id, heap_return, args, opt body)]
| cdef -> [cdef]
- *)
let concatMap f xs = List.concat (List.map f xs)
let optimize ctx cdefs =
let nothing cdefs = cdefs in
cdefs
+ |> (if !optimize_alias then concatMap (remove_alias ctx) else nothing)
|> (if !optimize_hoist_allocations then concatMap (hoist_allocations ctx) else nothing)
-(* |> (if !optimize_struct_updates then concatMap (fix_struct_updates ctx) else nothing) *)
(**************************************************************************)
(* 6. Code generation *)
@@ -2041,10 +2123,11 @@ let rec sgen_ctyp = function
| CT_unit -> "unit"
| CT_bit -> "mach_bits"
| CT_bool -> "bool"
- | CT_bits64 _ -> "mach_bits"
+ | CT_fbits _ -> "mach_bits"
+ | CT_sbits _ -> "sbits"
| CT_int64 -> "mach_int"
| CT_int -> "sail_int"
- | CT_bits _ -> "sail_bits"
+ | CT_lbits _ -> "sail_bits"
| CT_tup _ as tup -> "struct " ^ Util.zencode_string ("tuple_" ^ string_of_ctyp tup)
| CT_struct (id, _) -> "struct " ^ sgen_id id
| CT_enum (id, _) -> "enum " ^ sgen_id id
@@ -2060,10 +2143,11 @@ let rec sgen_ctyp_name = function
| CT_unit -> "unit"
| CT_bit -> "mach_bits"
| CT_bool -> "bool"
- | CT_bits64 _ -> "mach_bits"
+ | CT_fbits _ -> "mach_bits"
+ | CT_sbits _ -> "sbits"
| CT_int64 -> "mach_int"
| CT_int -> "sail_int"
- | CT_bits _ -> "sail_bits"
+ | CT_lbits _ -> "sail_bits"
| CT_tup _ as tup -> Util.zencode_string ("tuple_" ^ string_of_ctyp tup)
| CT_struct (id, _) -> sgen_id id
| CT_enum (id, _) -> sgen_id id
@@ -2077,9 +2161,11 @@ let rec sgen_ctyp_name = function
let sgen_cval_param (frag, ctyp) =
match ctyp with
- | CT_bits direction ->
+ | CT_lbits direction ->
+ string_of_fragment frag ^ ", " ^ string_of_bool direction
+ | CT_sbits direction ->
string_of_fragment frag ^ ", " ^ string_of_bool direction
- | CT_bits64 (len, direction) ->
+ | CT_fbits (len, direction) ->
string_of_fragment frag ^ ", UINT64_C(" ^ string_of_int len ^ ") , " ^ string_of_bool direction
| _ ->
string_of_fragment frag
@@ -2208,25 +2294,25 @@ let rec codegen_instr fid ctx (I_aux (instr, (_, l))) =
end
| "vector_update_subrange", _ -> Printf.sprintf "vector_update_subrange_%s" (sgen_ctyp_name ctyp)
| "vector_subrange", _ -> Printf.sprintf "vector_subrange_%s" (sgen_ctyp_name ctyp)
- | "vector_update", CT_bits64 _ -> "update_mach_bits"
- | "vector_update", CT_bits _ -> "update_sail_bits"
+ | "vector_update", CT_fbits _ -> "update_mach_bits"
+ | "vector_update", CT_lbits _ -> "update_sail_bits"
| "vector_update", _ -> Printf.sprintf "vector_update_%s" (sgen_ctyp_name ctyp)
| "string_of_bits", _ ->
begin match cval_ctyp (List.nth args 0) with
- | CT_bits64 _ -> "string_of_mach_bits"
- | CT_bits _ -> "string_of_sail_bits"
+ | CT_fbits _ -> "string_of_mach_bits"
+ | CT_lbits _ -> "string_of_sail_bits"
| _ -> assert false
end
| "decimal_string_of_bits", _ ->
begin match cval_ctyp (List.nth args 0) with
- | CT_bits64 _ -> "decimal_string_of_mach_bits"
- | CT_bits _ -> "decimal_string_of_sail_bits"
+ | CT_fbits _ -> "decimal_string_of_mach_bits"
+ | CT_lbits _ -> "decimal_string_of_sail_bits"
| _ -> assert false
end
| "internal_vector_update", _ -> Printf.sprintf "internal_vector_update_%s" (sgen_ctyp_name ctyp)
| "internal_vector_init", _ -> Printf.sprintf "internal_vector_init_%s" (sgen_ctyp_name ctyp)
- | "undefined_vector", CT_bits64 _ -> "UNDEFINED(mach_bits)"
- | "undefined_vector", CT_bits _ -> "UNDEFINED(sail_bits)"
+ | "undefined_vector", CT_fbits _ -> "UNDEFINED(mach_bits)"
+ | "undefined_vector", CT_lbits _ -> "UNDEFINED(sail_bits)"
| "undefined_bit", _ -> "UNDEFINED(mach_bits)"
| "undefined_vector", _ -> Printf.sprintf "UNDEFINED(vector_%s)" (sgen_ctyp_name ctyp)
| fname, _ -> fname
@@ -2285,7 +2371,7 @@ let rec codegen_instr fid ctx (I_aux (instr, (_, l))) =
| CT_unit -> "UNIT", []
| CT_bit -> "UINT64_C(0)", []
| CT_int64 -> "INT64_C(0xdeadc0de)", []
- | CT_bits64 _ -> "UINT64_C(0xdeadc0de)", []
+ | CT_fbits _ -> "UINT64_C(0xdeadc0de)", []
| CT_bool -> "false", []
| CT_enum (_, ctor :: _) -> sgen_id ctor, []
| CT_tup ctyps when is_stack_ctyp ctyp ->
@@ -2901,7 +2987,7 @@ let rec ctyp_dependencies = function
| CT_ref ctyp -> ctyp_dependencies ctyp
| CT_struct (_, ctors) -> List.concat (List.map (fun (_, ctyp) -> ctyp_dependencies ctyp) ctors)
| CT_variant (_, ctors) -> List.concat (List.map (fun (_, ctyp) -> ctyp_dependencies ctyp) ctors)
- | CT_int | CT_int64 | CT_bits _ | CT_bits64 _ | CT_unit | CT_bool | CT_real | CT_bit | CT_string | CT_enum _ | CT_poly -> []
+ | CT_int | CT_int64 | CT_lbits _ | CT_fbits _ | CT_sbits _ | CT_unit | CT_bool | CT_real | CT_bit | CT_string | CT_enum _ | CT_poly -> []
let codegen_ctg ctx = function
| CTG_vector (direction, ctyp) -> codegen_vector ctx (direction, ctyp)
diff --git a/src/c_backend.mli b/src/c_backend.mli
index 6048f6b6..b39f58e2 100644
--- a/src/c_backend.mli
+++ b/src/c_backend.mli
@@ -64,17 +64,25 @@ val opt_no_main : bool ref
val optimize_primops : bool ref
val optimize_hoist_allocations : bool ref
val optimize_struct_updates : bool ref
+val optimize_alias : bool ref
(** The compilation context. *)
type ctx
+(** Convert a typ to a IR ctyp *)
+val ctyp_of_typ : ctx -> Ast.typ -> ctyp
+
(** Create a context from a typechecking environment. This environment
should be the environment returned by typechecking the full AST. *)
val initial_ctx : Env.t -> ctx
+val compile_aexp : ctx -> Ast.typ Anf.aexp -> instr list * (clexp -> instr) * instr list
+
val compile_ast : ctx -> string list -> tannot Ast.defs -> unit
val bytecode_ast : ctx -> (cdef list -> cdef list) -> tannot Ast.defs -> cdef list
(** Rewriting steps for compiled ASTs *)
-val flatten_instrs : cdef -> cdef
+val flatten_instrs : instr list -> instr list
+
+val flatten_cdef : cdef -> cdef
diff --git a/src/constant_fold.ml b/src/constant_fold.ml
index 407bd69a..f22b48de 100644
--- a/src/constant_fold.ml
+++ b/src/constant_fold.ml
@@ -100,6 +100,7 @@ let safe_primops =
"prerr_string";
"read_ram";
"write_ram";
+ "get_time_ns";
"Elf_loader.elf_entry";
"Elf_loader.elf_tohost"
]
diff --git a/src/isail.ml b/src/isail.ml
index 55f08f17..83bff3f3 100644
--- a/src/isail.ml
+++ b/src/isail.ml
@@ -57,6 +57,7 @@ open Pretty_print_sail
type mode =
| Evaluation of frame
+ | Bytecode of Value2.vl Bytecode_interpreter.gstate * Value2.vl Bytecode_interpreter.stack
| Normal
let current_mode = ref Normal
@@ -65,6 +66,7 @@ let prompt () =
match !current_mode with
| Normal -> "sail> "
| Evaluation _ -> "eval> "
+ | Bytecode _ -> "ir> "
let eval_clear = ref true
@@ -72,6 +74,7 @@ let mode_clear () =
match !current_mode with
| Normal -> ()
| Evaluation _ -> if !eval_clear then LNoise.clear_screen () else ()
+ | Bytecode _ -> () (* if !eval_clear then LNoise.clear_screen () else () *)
let rec user_input callback =
match LNoise.linenoise (prompt ()) with
@@ -105,16 +108,35 @@ let vs_ids = ref (Initial_check.val_spec_ids !interactive_ast)
let interactive_state = ref (initial_state !interactive_ast Value.primops)
+let interactive_bytecode = ref []
+
+let sep = "-----------------------------------------------------" |> Util.blue |> Util.clear
+
let print_program () =
match !current_mode with
| Normal -> ()
| Evaluation (Step (out, _, _, stack)) ->
- let sep = "-----------------------------------------------------" |> Util.blue |> Util.clear in
List.map stack_string stack |> List.rev |> List.iter (fun code -> print_endline (Lazy.force code); print_endline sep);
print_endline (Lazy.force out)
| Evaluation (Done (_, v)) ->
print_endline (Value.string_of_value v |> Util.green |> Util.clear)
| Evaluation _ -> ()
+ | Bytecode (_, stack) ->
+ let open Bytecode_interpreter in
+ let open Bytecode_util in
+ let pc = stack.top.pc in
+ let instrs = stack.top.instrs in
+ for i = 0 to stack.top.pc - 1 do
+ print_endline (" " ^ Pretty_print_sail.to_string (pp_instr instrs.(i)))
+ done;
+ print_endline (">> " ^ Pretty_print_sail.to_string (pp_instr instrs.(stack.top.pc)));
+ for i = stack.top.pc + 1 to Array.length instrs - 1 do
+ print_endline (" " ^ Pretty_print_sail.to_string (pp_instr instrs.(i)))
+ done;
+ print_endline sep;
+ print_endline (Util.string_of_list ", "
+ (fun (id, vl) -> Printf.sprintf "%s = %s" (string_of_id id) (string_of_value vl))
+ (Bindings.bindings stack.top.locals))
let rec run () =
match !current_mode with
@@ -138,6 +160,7 @@ let rec run () =
print_endline "Breakpoint";
current_mode := Evaluation frame
end
+ | Bytecode _ -> ()
let rec run_steps n =
print_endline ("step " ^ string_of_int n);
@@ -163,6 +186,7 @@ let rec run_steps n =
print_endline "Breakpoint";
current_mode := Evaluation frame
end
+ | Bytecode _ -> ()
let help = function
| ":t" | ":type" ->
@@ -232,6 +256,8 @@ let handle_input' input =
| Command (cmd, arg) ->
begin
match cmd with
+ | ":n" | ":normal" ->
+ current_mode := Normal
| ":t" | ":type" ->
let typq, typ = Type_check.Env.get_val_spec (mk_id arg) !interactive_env in
pretty_sail stdout (doc_binding (typq, typ));
@@ -286,18 +312,25 @@ let handle_input' input =
interactive_state := initial_state !interactive_ast Value.primops
| ":pretty" ->
print_endline (Pretty_print_sail.to_string (Latex.defs !interactive_ast))
- | ":bytecode" ->
+ | ":compile" ->
let open PPrint in
let open C_backend in
let ast = Process_file.rewrite_ast_c !interactive_ast in
let ast, env = Specialize.specialize ast !interactive_env in
let ctx = initial_ctx env in
- let byte_ast = bytecode_ast ctx (List.map flatten_instrs) ast in
- let chan = open_out arg in
- Util.opt_colors := false;
- Pretty_print_sail.pretty_sail chan (separate_map hardline Bytecode_util.pp_cdef byte_ast);
- Util.opt_colors := true;
- close_out chan
+ interactive_bytecode := bytecode_ast ctx (List.map flatten_cdef) ast
+ | ":ir" ->
+ print_endline arg;
+ let open Bytecode in
+ let open Bytecode_util in
+ let open PPrint in
+ let is_cdef = function
+ | CDEF_fundef (id, _, _, _) when Id.compare id (mk_id arg) = 0 -> true
+ | CDEF_spec (id, _, _) when Id.compare id (mk_id arg) = 0 -> true
+ | _ -> false
+ in
+ let cdefs = List.filter is_cdef !interactive_bytecode in
+ print_endline (Pretty_print_sail.to_string (separate_map hardline pp_cdef cdefs))
| ":ast" ->
let chan = open_out arg in
Pretty_print_sail.pp_defs chan !interactive_ast;
@@ -335,6 +368,16 @@ let handle_input' input =
vs_ids := Initial_check.val_spec_ids !interactive_ast;
(* See initial_check.mli for an explanation of why we need this. *)
Initial_check.have_undefined_builtins := false
+ | ":exec" ->
+ let open Bytecode_interpreter in
+ let exp = Type_check.infer_exp !interactive_env (Initial_check.exp_of_string Ast_util.dec_ord arg) in
+ let anf = Anf.anf exp in
+ let ctx = C_backend.initial_ctx !interactive_env in
+ let ctyp = C_backend.ctyp_of_typ ctx (Type_check.typ_of exp) in
+ let setup, call, cleanup = C_backend.compile_aexp ctx anf in
+ let instrs = C_backend.flatten_instrs (setup @ [call (CL_id (mk_id "interactive#", ctyp))] @ cleanup) in
+ current_mode := Bytecode (new_gstate !interactive_bytecode, new_stack instrs);
+ print_program ()
| _ -> unrecognised_command cmd
end
| Expression str ->
@@ -346,44 +389,51 @@ let handle_input' input =
| Empty -> ()
end
| Evaluation frame ->
- begin
- match input with
- | Command (cmd, arg) ->
- (* Evaluation mode commands *)
- begin
- match cmd with
- | ":r" | ":run" ->
- run ()
- | ":s" | ":step" ->
- run_steps (int_of_string arg)
- | ":n" | ":normal" ->
- current_mode := Normal
- | _ -> unrecognised_command cmd
- end
- | Expression str ->
- print_endline "Already evaluating expression"
- | Empty ->
- (* Empty input will evaluate one step, or switch back to
+ begin match input with
+ | Command (cmd, arg) ->
+ (* Evaluation mode commands *)
+ begin
+ match cmd with
+ | ":r" | ":run" ->
+ run ()
+ | ":s" | ":step" ->
+ run_steps (int_of_string arg)
+ | _ -> unrecognised_command cmd
+ end
+ | Expression str ->
+ print_endline "Already evaluating expression"
+ | Empty ->
+ (* Empty input will evaluate one step, or switch back to
normal mode when evaluation is completed. *)
- begin
- match frame with
- | Done (state, v) ->
+ begin match frame with
+ | Done (state, v) ->
+ interactive_state := state;
+ print_endline ("Result = " ^ Value.string_of_value v);
+ current_mode := Normal
+ | Step (out, state, _, stack) ->
+ begin
+ try
interactive_state := state;
- print_endline ("Result = " ^ Value.string_of_value v);
- current_mode := Normal
- | Step (out, state, _, stack) ->
- begin
- try
- interactive_state := state;
- current_mode := Evaluation (eval_frame frame);
- print_program ()
- with
- | Failure str -> print_endline str; current_mode := Normal
- end
- | Break frame ->
- print_endline "Breakpoint";
- current_mode := Evaluation frame
- end
+ current_mode := Evaluation (eval_frame frame);
+ print_program ()
+ with
+ | Failure str -> print_endline str; current_mode := Normal
+ end
+ | Break frame ->
+ print_endline "Breakpoint";
+ current_mode := Evaluation frame
+ end
+ end
+ | Bytecode (gstate, stack) ->
+ begin match input with
+ | Command (cmd, arg) ->
+ ()
+ | Expression str ->
+ print_endline "Evaluating IR, cannot evaluate expression"
+ | Empty ->
+ let gstate, stack = Bytecode_interpreter.step (gstate, stack) in
+ current_mode := Bytecode (gstate, stack);
+ print_program ()
end
let handle_input input =
diff --git a/src/sail.ml b/src/sail.ml
index c9cead2a..44766926 100644
--- a/src/sail.ml
+++ b/src/sail.ml
@@ -128,7 +128,8 @@ let options = Arg.align ([
Arg.Set C_backend.optimize_hoist_allocations;
Arg.Set Initial_check.opt_fast_undefined;
Arg.Set Type_check.opt_no_effects;
- Arg.Set C_backend.optimize_struct_updates ],
+ Arg.Set C_backend.optimize_struct_updates;
+ Arg.Set C_backend.optimize_alias],
" turn on optimizations for C compilation");
( "-Oconstant_fold",
Arg.Set Constant_fold.optimize_constant_fold,