diff options
Diffstat (limited to 'src/ocaml_backend.ml')
| -rw-r--r-- | src/ocaml_backend.ml | 588 |
1 files changed, 588 insertions, 0 deletions
diff --git a/src/ocaml_backend.ml b/src/ocaml_backend.ml new file mode 100644 index 00000000..333505e0 --- /dev/null +++ b/src/ocaml_backend.ml @@ -0,0 +1,588 @@ +open Ast +open Ast_util +open PPrint +open Type_check + +(* Option to turn tracing features on or off *) +let opt_trace_ocaml = ref false + +type ctx = + { register_inits : tannot exp list; + externs : id Bindings.t; + val_specs : typ Bindings.t + } + +let empty_ctx = + { register_inits = []; + externs = Bindings.empty; + val_specs = Bindings.empty + } + +let gensym_counter = ref 0 + +let gensym () = + let gs = "gs" ^ string_of_int !gensym_counter in + incr gensym_counter; + string gs + +let zchar c = + let zc c = "z" ^ String.make 1 c in + if Char.code c <= 41 then zc (Char.chr (Char.code c + 16)) + else if Char.code c <= 47 then zc (Char.chr (Char.code c + 23)) + else if Char.code c <= 57 then String.make 1 c + else if Char.code c <= 64 then zc (Char.chr (Char.code c + 13)) + else if Char.code c <= 90 then String.make 1 c + else if Char.code c <= 94 then zc (Char.chr (Char.code c - 13)) + else if Char.code c <= 95 then "_" + else if Char.code c <= 96 then zc (Char.chr (Char.code c - 13)) + else if Char.code c <= 121 then String.make 1 c + else if Char.code c <= 122 then "zz" + else if Char.code c <= 126 then zc (Char.chr (Char.code c - 39)) + else raise (Invalid_argument "zchar") + +let zencode_string str = "z" ^ List.fold_left (fun s1 s2 -> s1 ^ s2) "" (List.map zchar (Util.string_to_list str)) + +let zencode_upper_string str = "Z" ^ List.fold_left (fun s1 s2 -> s1 ^ s2) "" (List.map zchar (Util.string_to_list str)) + +let zencode ctx id = + try string (string_of_id (Bindings.find id ctx.externs)) with + | Not_found -> string (zencode_string (string_of_id id)) + +let zencode_upper ctx id = + try string (string_of_id (Bindings.find id ctx.externs)) with + | Not_found -> string (zencode_upper_string (string_of_id id)) + +let zencode_kid kid = string ("'" ^ zencode_string (string_of_id (id_of_kid kid))) + +let ocaml_string_of id = string ("string_of_" ^ zencode_string (string_of_id id)) + +let ocaml_string_parens inside = string "\"(\" ^ " ^^ inside ^^ string " ^ \")\"" + +let ocaml_string_comma = string " ^ \", \" ^ " + +let rec ocaml_string_typ (Typ_aux (typ_aux, _)) arg = + match typ_aux with + | Typ_id id -> ocaml_string_of id ^^ space ^^ arg + | Typ_app (id, []) -> ocaml_string_of id ^^ space ^^ arg + | Typ_app (id, [Typ_arg_aux (Typ_arg_typ (Typ_aux (Typ_id eid, _)), _)]) + when string_of_id id = "list" && string_of_id eid = "bit" -> + string "string_of_bits" ^^ space ^^ arg + | Typ_app (id, [Typ_arg_aux (Typ_arg_typ typ, _)]) when string_of_id id = "list" -> + let farg = gensym () in + separate space [string "string_of_list \", \""; parens (separate space [string "fun"; farg; string "->"; ocaml_string_typ typ farg]); arg] + | Typ_app (_, _) -> string "\"APP\"" + | Typ_tup typs -> + let args = List.map (fun _ -> gensym ()) typs in + let body = + ocaml_string_parens (separate_map ocaml_string_comma (fun (typ, arg) -> ocaml_string_typ typ arg) (List.combine typs args)) + in + parens (separate space [string "fun"; parens (separate (comma ^^ space) args); string "->"; body]) + ^^ space ^^ arg + | Typ_fn (typ1, typ2, _) -> string "\"FN\"" + | Typ_var kid -> string "\"VAR\"" + | Typ_exist _ -> assert false + +let ocaml_typ_id ctx = function + | id when Id.compare id (mk_id "string") = 0 -> string "string" + | id when Id.compare id (mk_id "list") = 0 -> string "list" + | id when Id.compare id (mk_id "bit") = 0 -> string "bit" + | id when Id.compare id (mk_id "int") = 0 -> string "big_int" + | id when Id.compare id (mk_id "nat") = 0 -> string "big_int" + | id when Id.compare id (mk_id "bool") = 0 -> string "bool" + | id when Id.compare id (mk_id "unit") = 0 -> string "unit" + | id when Id.compare id (mk_id "real") = 0 -> string "Num.num" + | id when Id.compare id (mk_id "register") = 0 -> string "ref" + | id -> zencode ctx id + +let rec ocaml_typ ctx (Typ_aux (typ_aux, _)) = + match typ_aux with + | Typ_id id -> ocaml_typ_id ctx id + | Typ_app (id, []) -> ocaml_typ_id ctx id + | Typ_app (id, typs) -> parens (separate_map (string " * ") (ocaml_typ_arg ctx) typs) ^^ space ^^ ocaml_typ_id ctx id + | Typ_tup typs -> parens (separate_map (string " * ") (ocaml_typ ctx) typs) + | Typ_fn (typ1, typ2, _) -> separate space [ocaml_typ ctx typ1; string "->"; ocaml_typ ctx typ2] + | Typ_var kid -> zencode_kid kid + | Typ_exist _ -> assert false +and ocaml_typ_arg ctx (Typ_arg_aux (typ_arg_aux, _) as typ_arg) = + match typ_arg_aux with + | Typ_arg_typ typ -> ocaml_typ ctx typ + | _ -> failwith ("OCaml: unexpected type argument " ^ string_of_typ_arg typ_arg) + +let ocaml_typquant typq = + let ocaml_qi = function + | QI_aux (QI_id kopt, _) -> zencode_kid (kopt_kid kopt) + | QI_aux (QI_const _, _) -> failwith "Ocaml type quantifiers should no longer contain constraints" + in + match quant_items typq with + | [] -> empty + | [qi] -> ocaml_qi qi + | qis -> parens (separate_map (string " * ") ocaml_qi qis) + +let string_lit str = dquotes (string (String.escaped str)) + +let ocaml_lit (L_aux (lit_aux, _)) = + match lit_aux with + | L_unit -> string "()" + | L_zero -> string "B0" + | L_one -> string "B1" + | L_true -> string "true" + | L_false -> string "false" + | L_num n -> parens (string "big_int_of_string" ^^ space ^^ string ("\"" ^ Big_int.string_of_big_int n ^ "\"")) + | L_undef -> failwith "undefined should have been re-written prior to ocaml backend" + | L_string str -> string_lit str + | L_real str -> parens (string "real_of_string" ^^ space ^^ dquotes (string (String.escaped str))) + | _ -> string "LIT" + +let rec ocaml_pat ctx (P_aux (pat_aux, _) as pat) = + match pat_aux with + | P_id id -> + begin + match Env.lookup_id id (pat_env_of pat) with + | Local (Immutable, _) | Unbound -> zencode ctx id + | Enum _ -> zencode_upper ctx id + | _ -> failwith "Ocaml: Cannot pattern match on mutable variable or register" + end + | P_lit lit -> ocaml_lit lit + | P_typ (_, pat) -> ocaml_pat ctx pat + | P_tup pats -> parens (separate_map (comma ^^ space) (ocaml_pat ctx) pats) + | P_list pats -> brackets (separate_map (semi ^^ space) (ocaml_pat ctx) pats) + | P_wild -> string "_" + | P_as (pat, id) -> separate space [ocaml_pat ctx pat; string "as"; zencode ctx id] + | _ -> string ("PAT<" ^ string_of_pat pat ^ ">") + +let begin_end doc = group (string "begin" ^^ nest 2 (break 1 ^^ doc) ^/^ string "end") + +(* Returns true if a type is a register being passed by name *) +let is_passed_by_name = function + | (Typ_aux (Typ_app (tid, _), _)) -> string_of_id tid = "register" + | _ -> false + +let rec ocaml_exp ctx (E_aux (exp_aux, _) as exp) = + match exp_aux with + | E_app (f, [x]) when Env.is_union_constructor f (env_of exp) -> zencode_upper ctx f ^^ space ^^ ocaml_atomic_exp ctx x + | E_app (f, [x]) -> zencode ctx f ^^ space ^^ ocaml_atomic_exp ctx x + | E_app (f, xs) when Env.is_union_constructor f (env_of exp) -> + zencode_upper ctx f ^^ space ^^ parens (separate_map (comma ^^ space) (ocaml_atomic_exp ctx) xs) + (* Make sure we get the correct short circuiting semantics for and and or *) + | E_app (f, [x; y]) when string_of_id f = "and_bool" -> + separate space [ocaml_atomic_exp ctx x; string "&&"; ocaml_atomic_exp ctx y] + | E_app (f, [x; y]) when string_of_id f = "or_bool" -> + separate space [ocaml_atomic_exp ctx x; string "||"; ocaml_atomic_exp ctx y] + | E_app (f, xs) -> + zencode ctx f ^^ space ^^ parens (separate_map (comma ^^ space) (ocaml_atomic_exp ctx) xs) + | E_vector_subrange (exp1, exp2, exp3) -> string "subrange" ^^ space ^^ parens (separate_map (comma ^^ space) (ocaml_atomic_exp ctx) [exp1; exp2; exp3]) + | E_return exp -> separate space [string "r.return"; ocaml_atomic_exp ctx exp] + | E_assert (exp, _) -> separate space [string "assert"; ocaml_atomic_exp ctx exp] + | E_cast (_, exp) -> ocaml_exp ctx exp + | E_block [exp] -> ocaml_exp ctx exp + | E_block [] -> string "()" + | E_block exps -> begin_end (ocaml_block ctx exps) + | E_field (exp, id) -> ocaml_atomic_exp ctx exp ^^ dot ^^ zencode ctx id + | E_exit exp -> string "exit 0" + | E_case (exp, pexps) -> + begin_end (separate space [string "match"; ocaml_atomic_exp ctx exp; string "with"] + ^/^ ocaml_pexps ctx pexps) + | E_assign (lexp, exp) -> ocaml_assignment ctx lexp exp + | E_if (c, t, e) -> separate space [string "if"; ocaml_atomic_exp ctx c; + string "then"; ocaml_atomic_exp ctx t; + string "else"; ocaml_atomic_exp ctx e] + | E_record (FES_aux (FES_Fexps (fexps, _), _)) -> + enclose lbrace rbrace (group (separate_map (semi ^^ break 1) (ocaml_fexp ctx) fexps)) + | E_record_update (exp, FES_aux (FES_Fexps (fexps, _), _)) -> + enclose lbrace rbrace (separate space [ocaml_atomic_exp ctx exp; + string "with"; + separate_map (semi ^^ space) (ocaml_fexp ctx) fexps]) + | E_let (lb, exp) -> + separate space [string "let"; ocaml_letbind ctx lb; string "in"] + ^/^ ocaml_exp ctx exp + | E_internal_let (lexp, exp1, exp2) -> + separate space [string "let"; ocaml_atomic_lexp ctx lexp; + equals; string "ref"; parens (ocaml_atomic_exp ctx exp1 ^^ space ^^ colon ^^ space ^^ ocaml_typ ctx (Rewrites.simple_typ (typ_of exp1))); string "in"] + ^/^ ocaml_exp ctx exp2 + | E_loop (Until, cond, body) -> + let loop_body = + (ocaml_atomic_exp ctx body ^^ semi) + ^/^ + separate space [string "if"; ocaml_atomic_exp ctx cond; + string "then loop ()"; + string "else ()"] + in + (string "let rec loop () =" ^//^ loop_body) + ^/^ string "in" + ^/^ string "loop ()" + | E_loop (While, cond, body) -> + let loop_body = + separate space [string "if"; ocaml_atomic_exp ctx cond; + string "then"; parens (ocaml_atomic_exp ctx body ^^ semi ^^ space ^^ string "loop ()"); + string "else ()"] + in + (string "let rec loop () =" ^//^ loop_body) + ^/^ string "in" + ^/^ string "loop ()" + | E_lit _ | E_list _ | E_id _ | E_tuple _ -> ocaml_atomic_exp ctx exp + | E_for (id, exp_from, exp_to, exp_step, ord, exp_body) -> + let loop_var = separate space [string "let"; zencode ctx id; equals; string "ref"; ocaml_atomic_exp ctx exp_from; string "in"] in + let loop_mod = + match ord with + | Ord_aux (Ord_inc, _) -> string "add_big_int" ^^ space ^^ zencode ctx id ^^ space ^^ ocaml_atomic_exp ctx exp_step + | Ord_aux (Ord_dec, _) -> string "sub_big_int" ^^ space ^^ zencode ctx id ^^ space ^^ ocaml_atomic_exp ctx exp_step + in + let loop_compare = + match ord with + | Ord_aux (Ord_inc, _) -> string "le_big_int" + | Ord_aux (Ord_dec, _) -> string "gt_big_int" + in + let loop_body = + separate space [string "if"; loop_compare; zencode ctx id; ocaml_atomic_exp ctx exp_to] + ^/^ separate space [string "then"; + parens (ocaml_atomic_exp ctx exp_body ^^ semi ^^ space ^^ string "loop" ^^ space ^^ parens loop_mod)] + ^/^ string "else ()" + in + (string ("let rec loop " ^ zencode_string (string_of_id id) ^ " =") ^//^ loop_body) + ^/^ string "in" + ^/^ (string "loop" ^^ space ^^ ocaml_atomic_exp ctx exp_from) + | _ -> string ("EXP(" ^ string_of_exp exp ^ ")") +and ocaml_letbind ctx (LB_aux (lb_aux, _)) = + match lb_aux with + | LB_val (pat, exp) -> separate space [ocaml_pat ctx pat; equals; ocaml_atomic_exp ctx exp] +and ocaml_pexps ctx = function + | [pexp] -> ocaml_pexp ctx pexp + | pexp :: pexps -> ocaml_pexp ctx pexp ^/^ ocaml_pexps ctx pexps + | [] -> empty +and ocaml_pexp ctx = function + | Pat_aux (Pat_exp (pat, exp), _) -> + separate space [bar; ocaml_pat ctx pat; string "->"] + ^//^ group (ocaml_exp ctx exp) + | Pat_aux (Pat_when (pat, wh, exp), _) -> + separate space [bar; ocaml_pat ctx pat; string "when"; ocaml_atomic_exp ctx wh; string "->"] + ^//^ group (ocaml_exp ctx exp) +and ocaml_block ctx = function + | [exp] -> ocaml_exp ctx exp + | exp :: exps -> ocaml_exp ctx exp ^^ semi ^/^ ocaml_block ctx exps + | _ -> assert false +and ocaml_fexp ctx (FE_aux (FE_Fexp (id, exp), _)) = + separate space [zencode ctx id; equals; ocaml_exp ctx exp] +and ocaml_atomic_exp ctx (E_aux (exp_aux, _) as exp) = + match exp_aux with + | E_lit lit -> ocaml_lit lit + | E_id id -> + begin + match Env.lookup_id id (env_of exp) with + | Local (Immutable, _) | Unbound -> zencode ctx id + | Enum _ | Union _ -> zencode_upper ctx id + | Register _ when is_passed_by_name (typ_of exp) -> zencode ctx id + | Register typ -> + if !opt_trace_ocaml then + let var = gensym () in + let str_typ = parens (ocaml_string_typ (Rewrites.simple_typ typ) var) in + parens (separate space [string "let"; var; equals; bang ^^ zencode ctx id; string "in"; + string "trace_read" ^^ space ^^ string_lit (string_of_id id) ^^ space ^^ str_typ ^^ semi; var]) + else bang ^^ zencode ctx id + | Local (Mutable, _) -> bang ^^ zencode ctx id + end + | E_list exps -> enclose lbracket rbracket (separate_map (semi ^^ space) (ocaml_exp ctx) exps) + | E_tuple exps -> parens (separate_map (comma ^^ space) (ocaml_exp ctx) exps) + | _ -> parens (ocaml_exp ctx exp) +and ocaml_assignment ctx (LEXP_aux (lexp_aux, _) as lexp) exp = + match lexp_aux with + | LEXP_cast (_, id) | LEXP_id id -> + begin + match Env.lookup_id id (env_of exp) with + | Register typ -> + let var = gensym () in + let traced_exp = + if !opt_trace_ocaml then + let var = gensym () in + let str_typ = parens (ocaml_string_typ (Rewrites.simple_typ typ) var) in + parens (separate space [string "let"; var; equals; ocaml_atomic_exp ctx exp; string "in"; + string "trace_write" ^^ space ^^ string_lit (string_of_id id) ^^ space ^^ str_typ ^^ semi; var]) + else ocaml_atomic_exp ctx exp + in + separate space [zencode ctx id; string ":="; traced_exp] + | _ -> separate space [zencode ctx id; string ":="; ocaml_exp ctx exp] + end + | _ -> string ("LEXP<" ^ string_of_lexp lexp ^ ">") +and ocaml_lexp ctx (LEXP_aux (lexp_aux, _) as lexp) = + match lexp_aux with + | LEXP_cast _ | LEXP_id _ -> ocaml_atomic_lexp ctx lexp + | _ -> string ("LEXP<" ^ string_of_lexp lexp ^ ">") +and ocaml_atomic_lexp ctx (LEXP_aux (lexp_aux, _) as lexp) = + match lexp_aux with + | LEXP_cast (_, id) -> zencode ctx id + | LEXP_id id -> zencode ctx id + | _ -> parens (ocaml_lexp ctx lexp) + +let rec get_initialize_registers = function + | DEF_fundef (FD_aux (FD_function (_, _, _, [FCL_aux (FCL_Funcl (id, _, E_aux (E_block inits, _)), _)]), _)) :: defs + when Id.compare id (mk_id "initialize_registers") = 0 -> + inits + | _ :: defs -> get_initialize_registers defs + | [] -> [] + +let initial_value_for id inits = + let find_reg = function + | E_aux (E_assign (LEXP_aux (LEXP_cast (_, reg_id), _), init), _) when Id.compare id reg_id = 0 -> Some init + | _ -> None + in + match Util.option_first find_reg inits with + | Some init -> init + | None -> failwith ("No assignment to register ^ " ^ string_of_id id ^ " in initialize_registers") + +let ocaml_dec_spec ctx (DEC_aux (reg, _)) = + match reg with + | DEC_reg (typ, id) -> + separate space [string "let"; zencode ctx id; colon; + parens (ocaml_typ ctx typ); string "ref"; equals; + string "ref"; parens (ocaml_exp ctx (initial_value_for id ctx.register_inits))] + | _ -> failwith "Unsupported register declaration" + +let first_function = ref true + +let function_header () = + if !first_function + then (first_function := false; string "let rec") + else string "and" + +let funcls_id = function + | [] -> failwith "Ocaml: empty function" + | FCL_aux (FCL_Funcl (id, pat, exp),_) :: _ -> id + +let ocaml_funcl_match ctx (FCL_aux (FCL_Funcl (id, pat, exp), _)) = + separate space [bar; ocaml_pat ctx pat; string "->"] + ^//^ ocaml_exp ctx exp + +let rec ocaml_funcl_matches ctx = function + | [] -> failwith "Ocaml: empty function" + | [clause] -> ocaml_funcl_match ctx clause + | (clause :: clauses) -> ocaml_funcl_match ctx clause ^/^ ocaml_funcl_matches ctx clauses + +let ocaml_funcls ctx = + (* Create functions string_of_arg and string_of_ret that print the argument and return types of the function respectively *) + let trace_info typ1 typ2 = + let arg_sym = gensym () in + let ret_sym = gensym () in + let string_of_arg = + separate space [function_header (); arg_sym; parens (string "arg" ^^ space ^^ colon ^^ space ^^ ocaml_typ ctx typ1); + colon; string "string"; equals; ocaml_string_typ typ1 (string "arg")] + in + let string_of_ret = + separate space [function_header (); ret_sym; parens (string "arg" ^^ space ^^ colon ^^ space ^^ ocaml_typ ctx typ2); + colon; string "string"; equals; ocaml_string_typ typ2 (string "arg")] + in + (arg_sym, string_of_arg, ret_sym, string_of_ret) + in + let sail_call id arg_sym pat_sym ret_sym = + if !opt_trace_ocaml + then separate space [string "sail_trace_call"; string_lit (string_of_id id); parens (arg_sym ^^ space ^^ pat_sym); ret_sym] + else separate space [string "sail_call"] + in + let ocaml_funcl call string_of_arg string_of_ret = + if !opt_trace_ocaml + then (call ^^ twice hardline ^^ string_of_arg ^^ twice hardline ^^ string_of_ret) + else call + in + function + | [] -> failwith "Ocaml: empty function" + | [FCL_aux (FCL_Funcl (id, pat, exp),_)] -> + let Typ_aux (Typ_fn (typ1, typ2, _), _) = Bindings.find id ctx.val_specs in + let pat_sym = gensym () in + let annot_pat = + let pat = parens (ocaml_pat ctx pat ^^ space ^^ colon ^^ space ^^ ocaml_typ ctx typ1) in + if !opt_trace_ocaml + then parens (separate space [pat; string "as"; pat_sym]) + else pat + in + let call_header = function_header () in + let arg_sym, string_of_arg, ret_sym, string_of_ret = trace_info typ1 typ2 in + let call = + separate space [call_header; zencode ctx id; annot_pat; colon; ocaml_typ ctx typ2; equals; + sail_call id arg_sym pat_sym ret_sym; string "(fun r ->"] + ^//^ ocaml_exp ctx exp + ^^ rparen + in + ocaml_funcl call string_of_arg string_of_ret + | funcls -> + let id = funcls_id funcls in + let Typ_aux (Typ_fn (typ1, typ2, _), _) = Bindings.find id ctx.val_specs in + let pat_sym = gensym () in + let call_header = function_header () in + let arg_sym, string_of_arg, ret_sym, string_of_ret = trace_info typ1 typ2 in + let call = + separate space [call_header; zencode ctx id; parens (pat_sym ^^ space ^^ colon ^^ space ^^ ocaml_typ ctx typ1); equals; + sail_call id arg_sym pat_sym ret_sym; string "(fun r ->"] + ^//^ (separate space [string "match"; pat_sym; string "with"] ^^ hardline ^^ ocaml_funcl_matches ctx funcls) + ^^ rparen + in + ocaml_funcl call string_of_arg string_of_ret + +let ocaml_fundef ctx (FD_aux (FD_function (_, _, _, funcls), _)) = + ocaml_funcls ctx funcls + +let rec ocaml_fields ctx = + let ocaml_field typ id = + separate space [zencode ctx id; colon; ocaml_typ ctx typ] + in + function + | [(typ, id)] -> ocaml_field typ id + | (typ, id) :: fields -> ocaml_field typ id ^^ semi ^/^ ocaml_fields ctx fields + | [] -> empty + +let rec ocaml_cases ctx = + let ocaml_case = function + | Tu_aux (Tu_id id, _) -> separate space [bar; zencode_upper ctx id] + | Tu_aux (Tu_ty_id (typ, id), _) -> separate space [bar; zencode_upper ctx id; string "of"; ocaml_typ ctx typ] + in + function + | [tu] -> ocaml_case tu + | tu :: tus -> ocaml_case tu ^/^ ocaml_cases ctx tus + | [] -> empty + +let rec ocaml_enum ctx = function + | [id] -> zencode_upper ctx id + | id :: ids -> zencode_upper ctx id ^/^ (bar ^^ space ^^ ocaml_enum ctx ids) + | [] -> empty + +(* We generate a string_of_X ocaml function for each type X, to be used for debugging purposes *) + +let ocaml_def_end = string ";;" ^^ twice hardline + +let ocaml_string_of_enum ctx id ids = + let ocaml_case id = + separate space [bar; zencode_upper ctx id; string "->"; string ("\"" ^ string_of_id id ^ "\"")] + in + separate space [string "let"; ocaml_string_of id; equals; string "function"] + ^//^ (separate_map hardline ocaml_case ids) + +let ocaml_string_of_struct ctx id typq fields = + let arg = gensym () in + let ocaml_field (typ, id) = + separate space [string (string_of_id id ^ " = \""); string "^"; ocaml_string_typ typ arg ^^ string "." ^^ zencode ctx id] + in + separate space [string "let"; ocaml_string_of id; parens (arg ^^ space ^^ colon ^^ space ^^ zencode ctx id); equals] + ^//^ (string "\"{" ^^ separate_map (hardline ^^ string "^ \", ") ocaml_field fields ^^ string " ^ \"}\"") + +let ocaml_string_of_abbrev ctx id typq typ = + let arg = gensym () in + separate space [string "let"; ocaml_string_of id; parens (arg ^^ space ^^ colon ^^ space ^^ zencode ctx id); equals] + ^//^ ocaml_string_typ typ arg + +let ocaml_typedef ctx (TD_aux (td_aux, _)) = + match td_aux with + | TD_record (id, _, typq, fields, _) -> + ((separate space [string "type"; ocaml_typquant typq; zencode ctx id; equals; lbrace] + ^//^ ocaml_fields ctx fields) + ^/^ rbrace) + ^^ ocaml_def_end + ^^ ocaml_string_of_struct ctx id typq fields + | TD_variant (id, _, typq, cases, _) -> + separate space [string "type"; ocaml_typquant typq; zencode ctx id; equals] + ^//^ ocaml_cases ctx cases + | TD_enum (id, _, ids, _) -> + (separate space [string "type"; zencode ctx id; equals] + ^//^ (bar ^^ space ^^ ocaml_enum ctx ids)) + ^^ ocaml_def_end + ^^ ocaml_string_of_enum ctx id ids + | TD_abbrev (id, _, TypSchm_aux (TypSchm_ts (typq, typ), _)) -> + separate space [string "type"; ocaml_typquant typq; zencode ctx id; equals; ocaml_typ ctx typ] + ^^ ocaml_def_end + ^^ ocaml_string_of_abbrev ctx id typq typ + | _ -> failwith "Unsupported typedef" + +let get_externs (Defs defs) = + let extern_id (VS_aux (VS_val_spec (typschm, id, ext, _), _)) = + match ext "ocaml" with + | None -> [] + | Some ext -> [(id, mk_id ext)] + in + let rec extern_ids = function + | DEF_spec vs :: defs -> extern_id vs :: extern_ids defs + | def :: defs -> extern_ids defs + | [] -> [] + in + List.fold_left (fun exts (id, name) -> Bindings.add id name exts) Bindings.empty (List.concat (extern_ids defs)) + +let nf_group doc = + first_function := true; + group doc + +let ocaml_def ctx def = match def with + | DEF_reg_dec ds -> nf_group (ocaml_dec_spec ctx ds) ^^ ocaml_def_end + | DEF_fundef fd -> group (ocaml_fundef ctx fd) ^^ twice hardline + | DEF_type td -> nf_group (ocaml_typedef ctx td) ^^ ocaml_def_end + | DEF_val lb -> nf_group (string "let" ^^ space ^^ ocaml_letbind ctx lb) ^^ ocaml_def_end + | _ -> empty + +let val_spec_typs (Defs defs) = + let typs = ref (Bindings.empty) in + let val_spec_typ (VS_aux (vs_aux, _)) = + match vs_aux with + | VS_val_spec (TypSchm_aux (TypSchm_ts (_, typ), _), id, _, _) -> typs := Bindings.add id typ !typs + in + let rec vs_typs = function + | DEF_spec vs :: defs -> val_spec_typ vs; vs_typs defs + | _ :: defs -> vs_typs defs + | [] -> [] + in + vs_typs defs; + !typs + +let ocaml_defs (Defs defs) = + let ctx = { register_inits = get_initialize_registers defs; + externs = get_externs (Defs defs); + val_specs = val_spec_typs (Defs defs) + } + in + let empty_reg_init = + if ctx.register_inits = [] + then + separate space [string "let"; string "initialize_registers"; string "()"; equals; string "()"] + ^^ ocaml_def_end + else empty + in + (string "open Sail_lib;;" ^^ hardline) + ^^ (string "open Big_int" ^^ ocaml_def_end) + ^^ concat (List.map (ocaml_def ctx) defs) + ^^ empty_reg_init + +let ocaml_main spec = + concat [separate space [string "open"; string (String.capitalize spec)] ^^ ocaml_def_end; + separate space [string "open"; string "Elf_loader"] ^^ ocaml_def_end; + separate space [string "let"; string "()"; equals] + ^//^ (string "Random.self_init ();" + ^/^ string "load_elf ();" + ^/^ string (if !opt_trace_ocaml then "Sail_lib.opt_trace := true;" else "Sail_lib.opt_trace := false;") + ^/^ string "initialize_registers ();" + ^/^ string "Printexc.record_backtrace true;" + ^/^ string "zmain ()") + ] + +let ocaml_pp_defs f defs = + ToChannel.pretty 1. 80 f (ocaml_defs defs) + +let ocaml_compile spec defs = + let sail_lib_dir = + try Sys.getenv "SAILLIBDIR" with + | Not_found -> failwith "Environment variable SAILLIBDIR needs to be set" + in + if Sys.file_exists "_sbuild" then () else Unix.mkdir "_sbuild" 0o775; + let cwd = Unix.getcwd () in + Unix.chdir "_sbuild"; + let _ = Unix.system ("cp -r " ^ sail_lib_dir ^ "/ocaml_rts/. .") in + let out_chan = open_out (spec ^ ".ml") in + ocaml_pp_defs out_chan defs; + close_out out_chan; + if IdSet.mem (mk_id "main") (Initial_check.val_spec_ids defs) + then + begin + print_endline "Generating main"; + let out_chan = open_out "main.ml" in + ToChannel.pretty 1. 80 out_chan (ocaml_main spec); + close_out out_chan; + let _ = Unix.system "ocamlbuild -pkg zarith -pkg uint main.native" in + let _ = Unix.system ("cp main.native " ^ cwd ^ "/" ^ spec) in + () + end + else + let _ = Unix.system ("ocamlbuild -pkg zarith -pkg uint " ^ spec ^ ".cmo") in + (); + Unix.chdir cwd |
