diff --git a/README.md b/README.md index 1c0abfb..aaa88d0 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ You would also need to build the core library, as instructed in the following se ### Usage -MoonBit's core library is typically installed in `~/.moon/lib/core/`. In following commands, we use `$core` to denote the path. The language is shipped with pre-built libraries under different targets: `js`, `wasm` and `wasm-gc`; however, this compiler currently supports only `wasm-gc`. Let `$target` stand for this value. +MoonBit's core library is typically installed in `~/.moon/lib/core/`. In following commands, we use `$core` to denote the path. You can choose your target between `riscv` and `wasm-gc`, which we denote by `$target`. Currently, `riscv` will only produce a `.ssa` file for static single assignment IR, and does not proceed to generate assembly. We use `$src` to denote the path to your main package. This package must contain, along with your source files, a `moon.pkg.json`; if you're not sure how this works, you can use [moon](https://github.com/moonbitlang/moon) to initialize a MoonBit repository. @@ -67,15 +67,17 @@ moon bundle --source-dir $core We strongly recommend that you build the core library yourself via the commands above. The pre-built binaries are not always compatible with this compiler, as MoonBit is still under development. -You should verify that there is a folder called `wasm-gc` under `$core/target`. +You should verify that now there is a folder called `wasm-gc` under `$core/target`. Now you can compile `.mbt` files with these commands: ```bash -bundled=$core/target/$target/release/bundle +# Even if you are targeting RISC-V, you can still use this path. +# That's because it's intermediate representation (IR) in the bundle; +# it is ignorant of target. +bundled=$core/target/wasm-gc/release/bundle # Here, main.mbt should be a file containing `fn main`. -# `build-package` produces intermediate representation (IR); it is ignorant of target. moonc build-package $src/main.mbt -is-main -std-path $bundled -o $obj # If you have more than one package, remember to include all of them in -pkg-sources. They should be separated by colon ':'. diff --git a/README.zh.md b/README.zh.md index a235198..3d4c37d 100644 --- a/README.zh.md +++ b/README.zh.md @@ -41,7 +41,7 @@ dune build -p moonbit-lang ### 使用 -MoonBit 的核心库一般安装在 `~/.moon/lib/core` 下。在下面的命令中,我们会用 `$core` 表示核心库的安装路径。在 `$core/target` 下,有 `js`, `wasm` 和 `wasm-gc` 这三个文件夹,它们包含在对应目标下编译好的核心库。我们用 `$target` 表示这三者之一。 +MoonBit 的核心库一般安装在 `~/.moon/lib/core` 下。在下面的命令中,我们会用 `$core` 表示核心库的安装路径。你可以选择 `riscv` 或 `wasm-gc` 作为编译目标,我们用 `$target` 表示这两者之一。值得注意的是,目前 `riscv` 只会产生 SSA 文件,而不会产生汇编代码。 `$src` 表示源代码的路径;在这个文件夹下,除了源代码之外还必须包括一个 `moon.pkg.json`。如果你不清楚如何编写这个文件,可以考虑使用 [moon](https://github.com/moonbitlang/moon) 来初始化。 @@ -70,7 +70,9 @@ moon bundle --source-dir $core 现在你可以使用这些命令来编译 `.mbt` 文件: ```bash -bundled=$core/target/$target/release/bundle +# 即使 $target 是 `riscv`,也依然可以使用这个路径。 +# 这是因为 bundle 文件夹的内容是中间表示 (IR),它和编译目标无关。 +bundled=$core/target/wasm-gc/release/bundle # 这里 main.mbt 是一个含有 `fn main` 的文件。 moonc build-package $src/main.mbt -is-main -std-path $core/target/$bundled -o $obj -target $target diff --git a/src/ast_derive.ml b/src/ast_derive.ml index 65ff851..9520d81 100644 --- a/src/ast_derive.ml +++ b/src/ast_derive.ml @@ -1329,7 +1329,7 @@ let derive_from_json (trait : Syntax.type_name) (decl : Syntax.type_decl) in (S.pmap pattern, expr)) in - Lst.append_one map_cases err_case |> S.match_ json + List.append map_cases [err_case] |> S.match_ json | Ptd_record fields -> let vars = Lst.map fields (fun _ -> fresh_name "field") in let pattern = diff --git a/src/basic_config.ml b/src/basic_config.ml index 01010d0..27f5d06 100644 --- a/src/basic_config.ml +++ b/src/basic_config.ml @@ -15,15 +15,17 @@ module Map_string = Basic_map_string -type target = Wasm_gc +type target = Wasm_gc | Riscv include struct let sexp_of_target target = match target with | Wasm_gc -> S.Atom "Wasm_gc" + | Riscv -> S.Atom "Riscv" let hash_fold_target hsv arg = Ppx_base.hash_fold_int hsv (match arg with - | Wasm_gc -> 1) + | Wasm_gc -> 1 + | Riscv -> 2) let hash_target arg = Ppx_base.get_hash_value @@ -37,6 +39,7 @@ type error_format = Human | Json let parse_target_exn = function | "wasm-gc" -> Wasm_gc + | "riscv" -> Riscv | other -> raise (Arg.Bad ("unsupported target: " ^ other)) let parse_error_format_exn = function diff --git a/src/basic_core_ident.ml b/src/basic_core_ident.ml index 1ff8916..b842c4f 100644 --- a/src/basic_core_ident.ml +++ b/src/basic_core_ident.ml @@ -28,34 +28,22 @@ module Key = struct include struct let _ = fun (_ : t) -> () - let (hash_fold_t : Ppx_base.state -> t -> Ppx_base.state) = - (fun hsv arg -> - match arg with - | Pdot _a0 -> - let hsv = Ppx_base.hash_fold_int hsv 0 in - let hsv = hsv in - Qual_ident.hash_fold_t hsv _a0 - | Plocal_method _a0 -> - let hsv = Ppx_base.hash_fold_int hsv 1 in - let hsv = hsv in - Ident.hash_fold_local_method hsv _a0 - | Pident _ir -> - let hsv = Ppx_base.hash_fold_int hsv 2 in - let hsv = - let hsv = hsv in - Ppx_base.hash_fold_int hsv _ir.stamp - in - hsv - | Pmutable_ident _ir -> - let hsv = Ppx_base.hash_fold_int hsv 3 in - let hsv = - let hsv = hsv in - Ppx_base.hash_fold_int hsv _ir.stamp - in - hsv - : Ppx_base.state -> t -> Ppx_base.state) - - let _ = hash_fold_t + let hash_fold_t hsv arg = + match arg with + | Pdot _a0 -> + let hsv = Ppx_base.hash_fold_int hsv 0 in + Qual_ident.hash_fold_t hsv _a0 + | Plocal_method _a0 -> + let hsv = Ppx_base.hash_fold_int hsv 1 in + Ident.hash_fold_local_method hsv _a0 + | Pident _ir -> + let hsv = Ppx_base.hash_fold_int hsv 2 in + let hsv = Ppx_base.hash_fold_int hsv _ir.stamp in + hsv + | Pmutable_ident _ir -> + let hsv = Ppx_base.hash_fold_int hsv 3 in + let hsv = Ppx_base.hash_fold_int hsv _ir.stamp in + hsv let (hash : t -> Ppx_base.hash_value) = let func arg = @@ -65,52 +53,40 @@ module Key = struct in fun x -> func x - let _ = hash - - let equal = - (fun a__001_ b__002_ -> - if Stdlib.( == ) a__001_ b__002_ then true - else - match (a__001_, b__002_) with - | Pdot _a__003_, Pdot _b__004_ -> Qual_ident.equal _a__003_ _b__004_ - | Pdot _, _ -> false - | _, Pdot _ -> false - | Plocal_method _a__005_, Plocal_method _b__006_ -> - Ident.equal_local_method _a__005_ _b__006_ - | Plocal_method _, _ -> false - | _, Plocal_method _ -> false - | Pident _a__007_, Pident _b__008_ -> - Stdlib.( = ) (_a__007_.stamp : int) _b__008_.stamp - | Pident _, _ -> false - | _, Pident _ -> false - | Pmutable_ident _a__009_, Pmutable_ident _b__010_ -> - Stdlib.( = ) (_a__009_.stamp : int) _b__010_.stamp - : t -> t -> bool) - - let _ = equal - - let compare = - (fun a__011_ b__012_ -> - if Stdlib.( == ) a__011_ b__012_ then 0 - else - match (a__011_, b__012_) with - | Pdot _a__013_, Pdot _b__014_ -> - Qual_ident.compare _a__013_ _b__014_ - | Pdot _, _ -> -1 - | _, Pdot _ -> 1 - | Plocal_method _a__015_, Plocal_method _b__016_ -> - Ident.compare_local_method _a__015_ _b__016_ - | Plocal_method _, _ -> -1 - | _, Plocal_method _ -> 1 - | Pident _a__017_, Pident _b__018_ -> - Stdlib.compare (_a__017_.stamp : int) _b__018_.stamp - | Pident _, _ -> -1 - | _, Pident _ -> 1 - | Pmutable_ident _a__019_, Pmutable_ident _b__020_ -> - Stdlib.compare (_a__019_.stamp : int) _b__020_.stamp - : t -> t -> int) - - let _ = compare + let equal a b = + if a == b then true + else match (a, b) with + | Pdot x, Pdot y -> Qual_ident.equal x y + | Pdot _, _ -> false + | _, Pdot _ -> false + | Plocal_method x, Plocal_method y -> + Ident.equal_local_method x y + | Plocal_method _, _ -> false + | _, Plocal_method _ -> false + | Pident x, Pident y -> + x.stamp = y.stamp + | Pident _, _ -> false + | _, Pident _ -> false + | Pmutable_ident x, Pmutable_ident y -> + x.stamp = y.stamp + + let compare a b = + if a == b then 0 + else match (a, b) with + | Pdot x, Pdot y -> + Qual_ident.compare x y + | Pdot _, _ -> -1 + | _, Pdot _ -> 1 + | Plocal_method x, Plocal_method y -> + Ident.compare_local_method x y + | Plocal_method _, _ -> -1 + | _, Plocal_method _ -> 1 + | Pident x, Pident y -> + Stdlib.compare x.stamp y.stamp + | Pident _, _ -> -1 + | _, Pident _ -> 1 + | Pmutable_ident x, Pmutable_ident y -> + Stdlib.compare x.stamp y.stamp end let to_string (x : t) = diff --git a/src/basic_lst.ml b/src/basic_lst.ml index c31deb5..6e66553 100644 --- a/src/basic_lst.ml +++ b/src/basic_lst.ml @@ -29,9 +29,9 @@ module Unsafe_external = Basic_unsafe_external module Arr = Basic_arr open Unsafe_external -let rec map l f = List.map f l +let map l f = List.map f l -let rec has_string (l : string list) query = List.mem query l +let has_string (l : string list) query = List.mem query l let rec map_combine l1 l2 f = match (l1, l2) with @@ -87,62 +87,15 @@ let rec map_split_opt (xs : 'a list) (f : 'a -> 'b option * 'c option) : ( (match c with Some c -> c :: cs | None -> cs), match d with Some d -> d :: ds | None -> ds )) -let rec map_snd l f = - match l with - | [] -> [] - | (v1, x1) :: [] -> - let y1 = f x1 in - [ (v1, y1) ] - | [ (v1, x1); (v2, x2) ] -> - let y1 = f x1 in - let y2 = f x2 in - [ (v1, y1); (v2, y2) ] - | [ (v1, x1); (v2, x2); (v3, x3) ] -> - let y1 = f x1 in - let y2 = f x2 in - let y3 = f x3 in - [ (v1, y1); (v2, y2); (v3, y3) ] - | [ (v1, x1); (v2, x2); (v3, x3); (v4, x4) ] -> - let y1 = f x1 in - let y2 = f x2 in - let y3 = f x3 in - let y4 = f x4 in - [ (v1, y1); (v2, y2); (v3, y3); (v4, y4) ] - | (v1, x1) :: (v2, x2) :: (v3, x3) :: (v4, x4) :: (v5, x5) :: tail -> - let y1 = f x1 in - let y2 = f x2 in - let y3 = f x3 in - let y4 = f x4 in - let y5 = f x5 in - (v1, y1) :: (v2, y2) :: (v3, y3) :: (v4, y4) :: (v5, y5) :: map_snd tail f +let map_snd l f = List.map (fun (a, b) -> (a, f b)) l let rec map_last l f = match l with | [] -> [] | x1 :: [] -> - let y1 = f true x1 in - [ y1 ] - | [ x1; x2 ] -> - let y1 = f false x1 in - let y2 = f true x2 in - [ y1; y2 ] - | [ x1; x2; x3 ] -> - let y1 = f false x1 in - let y2 = f false x2 in - let y3 = f true x3 in - [ y1; y2; y3 ] - | [ x1; x2; x3; x4 ] -> - let y1 = f false x1 in - let y2 = f false x2 in - let y3 = f false x3 in - let y4 = f true x4 in - [ y1; y2; y3; y4 ] - | x1 :: x2 :: x3 :: x4 :: tail -> - let y1 = f false x1 in - let y2 = f false x2 in - let y3 = f false x3 in - let y4 = f false x4 in - y1 :: y2 :: y3 :: y4 :: map_last tail f + let y1 = f true x1 in [ y1 ] + | x1 :: tail -> + let y1 = f false x1 in y1 :: map_last tail f let rec mapi_aux lst i f tail = match lst with @@ -151,7 +104,7 @@ let rec mapi_aux lst i f tail = let r = f i a in r :: mapi_aux l (i + 1) f tail -let mapi lst f = mapi_aux lst 0 f [] +let mapi lst f = List.mapi f lst let mapi_append lst f tail = mapi_aux lst 0 f tail let rec last xs = @@ -160,20 +113,6 @@ let rec last xs = | _ :: tl -> last tl | [] -> invalid_arg __FUNCTION__ -let rec append_aux l1 l2 = - match l1 with - | [] -> l2 - | a0 :: [] -> a0 :: l2 - | [ a0; a1 ] -> a0 :: a1 :: l2 - | [ a0; a1; a2 ] -> a0 :: a1 :: a2 :: l2 - | [ a0; a1; a2; a3 ] -> a0 :: a1 :: a2 :: a3 :: l2 - | [ a0; a1; a2; a3; a4 ] -> a0 :: a1 :: a2 :: a3 :: a4 :: l2 - | a0 :: a1 :: a2 :: a3 :: a4 :: rest -> - a0 :: a1 :: a2 :: a3 :: a4 :: append_aux rest l2 - -let append l1 l2 = match l2 with [] -> l1 | _ -> append_aux l1 l2 -let append_one l1 x = append_aux l1 [ x ] - let rec map_append l1 l2 f = match l1 with | [] -> l2 @@ -556,83 +495,15 @@ let rec rev_iter l f = match l with | [] -> () | x1 :: [] -> f x1 - | [ x1; x2 ] -> - f x2; - f x1 - | [ x1; x2; x3 ] -> - f x3; - f x2; - f x1 - | [ x1; x2; x3; x4 ] -> - f x4; - f x3; - f x2; - f x1 - | x1 :: x2 :: x3 :: x4 :: x5 :: tail -> + | x1 :: tail -> rev_iter tail f; - f x5; - f x4; - f x3; - f x2; f x1 -let rec iter l f = - match l with - | [] -> () - | x1 :: [] -> f x1 - | [ x1; x2 ] -> - f x1; - f x2 - | [ x1; x2; x3 ] -> - f x1; - f x2; - f x3 - | [ x1; x2; x3; x4 ] -> - f x1; - f x2; - f x3; - f x4 - | x1 :: x2 :: x3 :: x4 :: x5 :: tail -> - f x1; - f x2; - f x3; - f x4; - f x5; - iter tail f - -let rec iteri_aux l f i = - match l with - | [] -> () - | x1 :: [] -> f i x1 - | [ x1; x2 ] -> - f i x1; - f (i + 1) x2 - | [ x1; x2; x3 ] -> - f i x1; - f (i + 1) x2; - f (i + 2) x3 - | [ x1; x2; x3; x4 ] -> - f i x1; - f (i + 1) x2; - f (i + 2) x3; - f (i + 3) x4 - | x1 :: x2 :: x3 :: x4 :: x5 :: tail -> - f i x1; - f (i + 1) x2; - f (i + 2) x3; - f (i + 3) x4; - f (i + 4) x5; - iteri_aux tail f (i + 5) +let iter l f = List.iter f l -let iteri l f = iteri_aux l f 0 +let iteri l f = List.iteri f l -let rec iter2 l1 l2 f = - match (l1, l2) with - | [], [] -> () - | a1 :: l1, a2 :: l2 -> - f a1 a2; - iter2 l1 l2 f - | _, _ -> invalid_arg __FUNCTION__ +let iter2 l1 l2 f = List.iter2 f l1 l2 let rec for_all lst p = match lst with [] -> true | a :: l -> p a && for_all l p @@ -750,13 +621,6 @@ let rec assoc_by_opt lst comp k = let assoc_str lst str = assoc_by_opt lst String.equal str let assoc_str_exn lst str = assoc_by_string lst str None -let rec nth_aux l n = - match l with - | [] -> None - | a :: l -> if n = 0 then Some a else nth_aux l (n - 1) - -let nth_opt l n = if n < 0 then None else nth_aux l n - let rec iter_snd lst f = match lst with | [] -> () @@ -780,10 +644,9 @@ let rec exists_snd l p = match l with [] -> false | (_, a) :: l -> p a || exists_snd l p let rec concat_append (xss : 'a list list) (xs : 'a list) : 'a list = - match xss with [] -> xs | l :: r -> append l (concat_append r xs) + match xss with [] -> xs | l :: r -> List.append l (concat_append r xs) -let rec fold_left l accu f = - match l with [] -> accu | a :: l -> fold_left l (f accu a) f +let fold_left l init f = List.fold_left f init l let reduce_from_left lst fn = match lst with diff --git a/src/basic_qual_ident.ml b/src/basic_qual_ident.ml index e4511c0..3b2a7cd 100644 --- a/src/basic_qual_ident.ml +++ b/src/basic_qual_ident.ml @@ -12,11 +12,14 @@ . *) - module Type_path = Basic_type_path module Config = Basic_config module Strutil = Basic_strutil +(** +Qual ident stands for qualified identifier. +In other words, an identifier like `@immut/hashmap.T`. +*) type t = | Qregular of { pkg : string; name : string } | Qregular_implicit_pkg of { pkg : string; name : string } diff --git a/src/basic_vec.ml b/src/basic_vec.ml index 441be4a..6c7bd6f 100644 --- a/src/basic_vec.ml +++ b/src/basic_vec.ml @@ -170,6 +170,10 @@ let push (d : 'a t) v = d.len <- d_len + 1; d.arr.!(d_len) <- v) +(** Similar to push, but for a whole vector. *) +let append vec other = + iter other (fun x -> push vec x) + let insert (d : 'a t) idx elt = let enlarge size = if size >= Sys.max_array_length then failwith "exceeds max_array_length"; @@ -184,7 +188,7 @@ let insert (d : 'a t) idx elt = d.arr.(idx) <- elt; d.len <- d.len + 1 -let pop_no_compact (d : 'a t) : 'a option = +let pop_opt (d : 'a t) : 'a option = let d_len = d.len in if d_len = 0 then None else @@ -194,3 +198,14 @@ let pop_no_compact (d : 'a t) : 'a option = fill_with_junk_ d_arr last_index 1; d.len <- last_index; Some last + +let pop (d : 'a t) : 'a = + let d_len = d.len in + if d_len = 0 then failwith __FUNCTION__ + else + let d_arr = d.arr in + let last_index = d_len - 1 in + let last = d_arr.!(last_index) in + fill_with_junk_ d_arr last_index 1; + d.len <- last_index; + last \ No newline at end of file diff --git a/src/driver_util.ml b/src/driver_util.ml index 281187b..386781a 100644 --- a/src/driver_util.ml +++ b/src/driver_util.ml @@ -46,6 +46,9 @@ type target = clam_callback : clam_passes -> Clam.prog -> unit; sexp_callback : W.t list -> unit; } + | Riscv of { + sexp_callback : Riscv.t list -> unit; + } let parse ~diagnostics ~(debug_tokens : bool) (input : mbt_input) : Parsing_parse.output = @@ -239,6 +242,12 @@ let wasm_gen ~(elim_unused_let : bool) (core : Mcore.t) ~clam_callback = |> Wasm_of_clam_gc.compile |> fun sexp -> Wat sexp +let riscv_gen (core : Mcore.t) = + core + |> Riscv_ssa.ssa_of_mcore + |> Riscv_opt.opt + |> Riscv.generate + let link_core ~(shrink_wasm : bool) ~(elim_unused_let : bool) ~(core_inputs : core_input Basic_vec.t) ~(exported_functions : string Basic_hash_string.t) ~(target : target) : unit @@ -262,6 +271,7 @@ let link_core ~(shrink_wasm : bool) ~(elim_unused_let : bool) | Wat sexp -> (if shrink_wasm then Pass_shrink_wasm.shrink sexp else sexp) |> sexp_callback) + | Riscv { sexp_callback; _ } -> riscv_gen mono_core |> sexp_callback let gen_test_info ~(diagnostics : Diagnostics.t) ~(json : bool) (mbt_files : mbt_input list) : string = diff --git a/src/moon0_main.ml b/src/moon0_main.ml index 6c91ec0..ae2acba 100644 --- a/src/moon0_main.ml +++ b/src/moon0_main.ml @@ -143,6 +143,9 @@ let wat_gen (sexp : W.t list) = Buffer.add_string buf (W.to_string s)) ss); Buffer.contents buf +let riscv_gen (sexp : Riscv.t list) = + List.map Riscv.to_asm_string sexp |> String.concat "\n" + let bundle_core () = let output_file = ref "" in let inputs = ref [] in @@ -199,11 +202,19 @@ let link_core () = else raise (Arg.Bad ("unrecognized output file type: " ^ filename ^ "; must be one of .wat or .wasm")) in + let riscv_gen_target sexp = + (* No need to check file type as in wasm. *) + (* We will write RISC-V assembly anyway. *) + Io.write !output_file (riscv_gen sexp) + in + let target = match !Config.target with | Wasm_gc -> Driver_util.Wasm_gc { clam_callback = (fun _ _ -> ()); sexp_callback = wasm_gen_target } + | Riscv -> + Driver_util.Riscv { sexp_callback = riscv_gen_target } in Driver_util.link_core ~shrink_wasm:!shrink_wasm ~elim_unused_let:!elim_unused_let ~core_inputs:input_files @@ -429,6 +440,7 @@ let compile () = | Wasm_gc -> let mod_ = wasm_gen ~name mono_core in postprecess mod_ + | Riscv -> failwith "TODO" (* TODO *) with Exit -> () in Arg.parse_argv ~current:(ref 1) Sys.argv spec diff --git a/src/mtype.ml b/src/mtype.ml index 672068c..609acc4 100644 --- a/src/mtype.ml +++ b/src/mtype.ml @@ -20,9 +20,9 @@ module Lst = Basic_lst type id = string include struct - let _ = fun (_ : id) -> () + let sexp_of_id = (Moon_sexp_conv.sexp_of_string : id -> S.t) - let _ = sexp_of_id + end module Id_hash : Basic_hash_intf.S with type key = id = Basic_hash_string @@ -55,92 +55,54 @@ type t = [@@warning "+4"] include struct - let _ = fun (_ : t) -> () - - let rec sexp_of_t = - (function - | T_int -> S.Atom "T_int" - | T_char -> S.Atom "T_char" - | T_bool -> S.Atom "T_bool" - | T_unit -> S.Atom "T_unit" - | T_byte -> S.Atom "T_byte" - | T_int64 -> S.Atom "T_int64" - | T_uint -> S.Atom "T_uint" - | T_uint64 -> S.Atom "T_uint64" - | T_float -> S.Atom "T_float" - | T_double -> S.Atom "T_double" - | T_string -> S.Atom "T_string" - | T_bytes -> S.Atom "T_bytes" - | T_optimized_option { elem = elem__002_ } -> - let bnds__001_ = ([] : _ Stdlib.List.t) in - let bnds__001_ = - let arg__003_ = sexp_of_t elem__002_ in - (S.List [ S.Atom "elem"; arg__003_ ] :: bnds__001_ : _ Stdlib.List.t) - in - S.List (S.Atom "T_optimized_option" :: bnds__001_) - | T_func { params = params__005_; return = return__007_ } -> - let bnds__004_ = ([] : _ Stdlib.List.t) in - let bnds__004_ = - let arg__008_ = sexp_of_t return__007_ in - (S.List [ S.Atom "return"; arg__008_ ] :: bnds__004_ - : _ Stdlib.List.t) - in - let bnds__004_ = - let arg__006_ = Moon_sexp_conv.sexp_of_list sexp_of_t params__005_ in - (S.List [ S.Atom "params"; arg__006_ ] :: bnds__004_ - : _ Stdlib.List.t) - in - S.List (S.Atom "T_func" :: bnds__004_) - | T_tuple { tys = tys__010_ } -> - let bnds__009_ = ([] : _ Stdlib.List.t) in - let bnds__009_ = - let arg__011_ = Moon_sexp_conv.sexp_of_list sexp_of_t tys__010_ in - (S.List [ S.Atom "tys"; arg__011_ ] :: bnds__009_ : _ Stdlib.List.t) - in - S.List (S.Atom "T_tuple" :: bnds__009_) - | T_fixedarray { elem = elem__013_ } -> - let bnds__012_ = ([] : _ Stdlib.List.t) in - let bnds__012_ = - let arg__014_ = sexp_of_t elem__013_ in - (S.List [ S.Atom "elem"; arg__014_ ] :: bnds__012_ : _ Stdlib.List.t) - in - S.List (S.Atom "T_fixedarray" :: bnds__012_) - | T_constr arg0__015_ -> - let res0__016_ = sexp_of_id arg0__015_ in - S.List [ S.Atom "T_constr"; res0__016_ ] - | T_trait arg0__017_ -> - let res0__018_ = sexp_of_id arg0__017_ in - S.List [ S.Atom "T_trait"; res0__018_ ] - | T_any { name = name__020_ } -> - let bnds__019_ = ([] : _ Stdlib.List.t) in - let bnds__019_ = - let arg__021_ = sexp_of_id name__020_ in - (S.List [ S.Atom "name"; arg__021_ ] :: bnds__019_ : _ Stdlib.List.t) - in - S.List (S.Atom "T_any" :: bnds__019_) - | T_maybe_uninit arg0__022_ -> - let res0__023_ = sexp_of_t arg0__022_ in - S.List [ S.Atom "T_maybe_uninit"; res0__023_ ] - | T_error_value_result { ok = ok__025_; err = err__027_; id = id__029_ } -> - let bnds__024_ = ([] : _ Stdlib.List.t) in - let bnds__024_ = - let arg__030_ = sexp_of_id id__029_ in - (S.List [ S.Atom "id"; arg__030_ ] :: bnds__024_ : _ Stdlib.List.t) - in - let bnds__024_ = - let arg__028_ = sexp_of_t err__027_ in - (S.List [ S.Atom "err"; arg__028_ ] :: bnds__024_ : _ Stdlib.List.t) - in - let bnds__024_ = - let arg__026_ = sexp_of_t ok__025_ in - (S.List [ S.Atom "ok"; arg__026_ ] :: bnds__024_ : _ Stdlib.List.t) - in - S.List (S.Atom "T_error_value_result" :: bnds__024_) - : t -> S.t) - - let _ = sexp_of_t + let rec sexp_of_t t = match t with + | T_int -> S.Atom "int" + | T_char -> S.Atom "char" + | T_bool -> S.Atom "bool" + | T_unit -> S.Atom "unit" + | T_byte -> S.Atom "byte" + | T_int64 -> S.Atom "int64" + | T_uint -> S.Atom "uint" + | T_uint64 -> S.Atom "uint64" + | T_float -> S.Atom "float" + | T_double -> S.Atom "double" + | T_string -> S.Atom "string" + | T_bytes -> S.Atom "bytes" + | T_optimized_option { elem } -> + let x = [S.List [ S.Atom "elem"; sexp_of_t elem ]] in + S.List (S.Atom "optimized_option" :: x) + | T_func { params; return = ret } -> + let x = [S.List [ S.Atom "return"; sexp_of_t ret ]] in + let y = + S.List [ S.Atom "params"; Moon_sexp_conv.sexp_of_list sexp_of_t params ] :: x + in + S.List (S.Atom "func" :: y) + | T_tuple { tys } -> + let x = + [S.List [ S.Atom "tys"; Moon_sexp_conv.sexp_of_list sexp_of_t tys ]] + in + S.List (S.Atom "tuple" :: x) + | T_fixedarray { elem } -> + let x = [S.List [ S.Atom "elem"; sexp_of_t elem ]] in + S.List (S.Atom "fixedarray" :: x) + | T_constr x -> + S.List [ S.Atom "constr"; sexp_of_id x ] + | T_trait x -> + S.List [ S.Atom "trait"; sexp_of_id x ] + | T_any { name } -> + let x = [S.List [ S.Atom "name"; sexp_of_id name ]] in + S.List (S.Atom "any" :: x) + | T_maybe_uninit x -> + S.List [ S.Atom "maybe_uninit"; sexp_of_t x ] + | T_error_value_result { ok; err; id } -> + let x = [S.List [ S.Atom "id"; sexp_of_id id ]] in + let y = S.List [ S.Atom "err"; sexp_of_t err ] :: x in + let z = S.List [ S.Atom "ok"; sexp_of_t ok ] :: y in + S.List (S.Atom "error_value_result" :: z) end +let to_string (t: t) = sexp_of_t t |> S.to_string + let is_numeric (t : t) = match t with | T_unit | T_int | T_uint | T_char | T_bool | T_byte | T_int64 | T_uint64 @@ -161,7 +123,6 @@ let is_numeric (t : t) = type field_name = Named of string | Indexed of int include struct - let _ = fun (_ : field_name) -> () let sexp_of_field_name = (function @@ -173,27 +134,11 @@ include struct S.List [ S.Atom "Indexed"; res0__034_ ] : field_name -> S.t) - let _ = sexp_of_field_name end -let field_index0 = Indexed 0 -let field_index1 = Indexed 1 -let field_index2 = Indexed 2 -let field_index3 = Indexed 3 - -let field_indexed i = - match i with - | 0 -> field_index0 - | 1 -> field_index1 - | 2 -> field_index2 - | 3 -> field_index3 - | n -> Indexed n - type field_info = { field_type : t; name : field_name; mut : bool } include struct - let _ = fun (_ : field_info) -> () - let sexp_of_field_info = (fun { field_type = field_type__036_; name = name__038_; mut = mut__040_ } -> let bnds__035_ = ([] : _ Stdlib.List.t) in @@ -212,8 +157,6 @@ include struct in S.List bnds__035_ : field_info -> S.t) - - let _ = sexp_of_field_info end type constr_info = { payload : field_info list; tag : Tag.t } @@ -615,7 +558,7 @@ let from_stype (stype : Stype.t) ~(stype_defs : Typing_info.stype_defs) let payload = Lst.mapi c.cs_args (fun i ty -> let field_type = go ty in - { field_type; name = field_indexed i; mut = false }) + { field_type; name = Indexed i; mut = false }) in { payload; tag } in @@ -640,7 +583,7 @@ let from_stype (stype : Stype.t) ~(stype_defs : Typing_info.stype_defs) | Labelled { label; is_mut = mut; _ } -> { field_type; name = Named label; mut } | Positional index -> - { field_type; name = field_indexed index; mut = false } + { field_type; name = Indexed index; mut = false } | Optional _ | Autofill _ | Question_optional _ -> assert false) in @@ -679,7 +622,7 @@ let from_stype (stype : Stype.t) ~(stype_defs : Typing_info.stype_defs) | Labelled { label; is_mut = mut; _ } -> { field_type; name = Named label; mut } | Positional index -> - { field_type; name = field_indexed index; mut = false } + { field_type; name = Indexed index; mut = false } | Optional _ | Autofill _ | Question_optional _ -> assert false) in diff --git a/src/placeholder_env.ml b/src/placeholder_env.ml index ec62a1b..fb9beba 100644 --- a/src/placeholder_env.ml +++ b/src/placeholder_env.ml @@ -132,7 +132,7 @@ let make ~foreign_types ~type_defs ~trait_defs = in add_trait name; let object_safety_status = - Lst.append + List.append (Hash_string.find_exn object_safety_of_traits name) (Vec.map_into_list not_object_safe_supers (fun super -> Trait_decl.Bad_super_trait super)) diff --git a/src/primitive.ml b/src/primitive.ml index 4cb67a5..634af78 100644 --- a/src/primitive.ml +++ b/src/primitive.ml @@ -72,17 +72,6 @@ and array_get_kind = Safe | Unsafe | Rev_unsafe and array_set_kind = Null | Default | Value | Unsafe include struct - let _ = fun (_ : operand_type) -> () - let _ = fun (_ : convert_kind) -> () - let _ = fun (_ : arith_operator) -> () - let _ = fun (_ : bitwise_operator) -> () - let _ = fun (_ : comparison) -> () - let _ = fun (_ : cast_kind) -> () - let _ = fun (_ : prim) -> () - let _ = fun (_ : make_array_kind) -> () - let _ = fun (_ : array_get_kind) -> () - let _ = fun (_ : array_set_kind) -> () - let rec sexp_of_operand_type = (function | I32 -> S.Atom "I32" @@ -664,4 +653,4 @@ let is_pure (prim : prim) = | Pintrinsic _ | Praise | Ppanic | Punreachable | Pcatch | Pfixedarray_set_item _ | Psetbytesitem | Pset_enum_field _ | Pprintln | Pcall_object_method _ -> - false + false \ No newline at end of file diff --git a/src/riscv.ml b/src/riscv.ml new file mode 100644 index 0000000..76f5f0d --- /dev/null +++ b/src/riscv.ml @@ -0,0 +1,57 @@ +(* RISC-V assembly commands. *) + +let registers = [| + (* Int registers *) + "zero"; "ra"; "sp"; "gp"; "tp"; + "t0"; "t1"; "t2"; "fp"; "s1"; + "a0"; "a1"; "a2"; "a3"; "a4"; + "a5"; "a6"; "a7"; "s2"; "s3"; + "s4"; "s5"; "s6"; "s7"; "s8"; + "s9"; "s10"; "s11"; "t3"; "t4"; + "t5"; "t6"; + + (* FP registers *) + "ft0"; "ft1"; "ft2"; "ft3"; "ft4"; + "ft5"; "ft6"; "ft7"; "fs0"; "fs1"; + "fa0"; "fa1"; "fa2"; "fa3"; "fa4"; + "fa5"; "fa6"; "fa7"; "fs2"; "fs3"; + "fs4"; "fs5"; "fs6"; "fs7"; "fs8"; + "fs9"; "fs10"; "fs11"; "ft8"; "ft9"; + "ft10"; "ft11"; +|] + +type t = +| Add of int * int * int +| Sub of int * int * int +| Mul of int * int * int +| Div of int * int * int +| Call of string +| Label of string + +let to_string asm = + let convert_3reg ty rd rs1 rs2 = + let rd_str = registers.(rd) in + let rs1_str = registers.(rs1) in + let rs2_str = registers.(rs2) in + Printf.sprintf "%s %s, %s, %s" ty rd_str rs1_str rs2_str + in + + match asm with + | Add (rd, rs1, rs2) -> convert_3reg "add" rd rs1 rs2 + | Sub (rd, rs1, rs2) -> convert_3reg "sub" rd rs1 rs2 + | Mul (rd, rs1, rs2) -> convert_3reg "mul" rd rs1 rs2 + | Div (rd, rs1, rs2) -> convert_3reg "div" rd rs1 rs2 + | Call label -> Printf.sprintf "call %s" label + | Label label -> Printf.sprintf "%s:" label + +(** +Used when emitting assembly. + +We expect every non-label command to be indented by 4 spaces. +*) +let to_asm_string asm = + match asm with + | Label _ -> to_string asm + | _ -> " " ^ to_string asm + +let generate ssa = [] \ No newline at end of file diff --git a/src/riscv_opt.ml b/src/riscv_opt.ml new file mode 100644 index 0000000..2ecd64a --- /dev/null +++ b/src/riscv_opt.ml @@ -0,0 +1,220 @@ +(** Does all sorts of optimizations. *) + +(** Instruction in SSA form; feel free to change it to anything you'd like *) +type instruction = Riscv_ssa.t + +(** Note: `body` does not include the label before instructions. *) +type basic_block = { + body: instruction Basic_vec.t; + succ: string Basic_vec.t; + pred: string Basic_vec.t; +} + +let make () = + { + body = Basic_vec.empty (); + succ = Basic_vec.empty (); + pred = Basic_vec.empty (); + } + + +(** We use the name of a basic block to refer to it. *) +let basic_blocks = Hashtbl.create 1024 + +(** The exit block(s) for each function `fn`, i.e. whose final instruction is `return`. *) +let exit_fn = Hashtbl.create 256 + +(** Get the basic block with label `name`. *) +let block_of name = Hashtbl.find basic_blocks name + +(** +Builds control flow graph. + +Does not return anything; +stores all information in `basic_block`. +*) +let build_cfg fn body = + (* Identify all basic blocks *) + + (* The first basic block in each function is unnamed, *) + (* so we take the function name as its name. *) + let name = ref fn in + let vec = ref (Basic_vec.make ~dummy:Riscv_ssa.Nop 16) in + + (* There might be multiple jumps at end of each basic block. *) + (* Clean them up. *) + let tidy (vec: instruction Basic_vec.t) = + let rec iter () = + let len = Basic_vec.length vec in + if len <= 1 then () + + (* Check penultimate instruction, and pop the last according to it *) + else let x = Basic_vec.get vec (len - 2) in + match x with + | Jump _ -> Basic_vec.pop vec |> ignore; iter () + | Branch _ -> Basic_vec.pop vec |> ignore; iter () + | Return _ -> Basic_vec.pop vec |> ignore; iter () + | _ -> () + in + iter (); + vec + in + + let separate_basic_block (inst: instruction) = + (match inst with + | Label label -> + Hashtbl.add basic_blocks !name (make ()); + Basic_vec.append (block_of !name).body (tidy !vec); + + (* Clear the instructions; Basic_vec does not offer clear() or something alike *) + vec := Basic_vec.make ~dummy:Riscv_ssa.Nop 16; + name := label + + | x -> Basic_vec.push !vec x) + in + List.iter separate_basic_block body; + + (* The last basic block is missed by `separate_basic_block` *) + (* Manually add it *) + Hashtbl.add basic_blocks !name (make ()); + Basic_vec.append (block_of !name).body (!vec); + + Hashtbl.add exit_fn fn (Basic_vec.empty ()); + + (* Find successors of each block. *) + + (* From the generation of SSA, *) + (* it is guaranteed that the structure of basic block is preserved; *) + (* i.e. only the last instruction can be jump/branch/return. *) + (* So we just look at them. *) + let rec find_succ name = + let block = block_of name in + if Basic_vec.is_empty block.succ then + let successors = + (match Basic_vec.last block.body with + | Jump target -> [target] + | Branch { ifso; ifnot } -> [ifso; ifnot] + | Return _ -> Basic_vec.push (Hashtbl.find exit_fn fn) name; [] + | _ -> failwith "riscv_opt.ml: malformed SSA") + in + Basic_vec.append block.succ (Basic_vec.of_list successors); + List.iter find_succ successors + in + find_succ fn; + + (* Find predecessors *) + Hashtbl.iter (fun name block -> + Basic_vec.iter block.succ (fun succ -> Basic_vec.push (block_of succ).pred name) + ) basic_blocks + +let visit_fn f ssa = + let visit (toplevel: instruction) = + match toplevel with + | FnDecl { fn; body; _ } -> f fn body + | _ -> () + in + List.iter visit ssa + +let map_fn f ssa = + let map_aux (toplevel: instruction) = + match toplevel with + | FnDecl { fn; body; args; } -> Riscv_ssa.FnDecl { fn; body = f fn; args } + | x -> x + in + List.map map_aux ssa + +(** Sets to store live variables. *) +module Varset = Set.Make(String) + +(** +Liveness analysis. + +Takes the entry block of a function, and returns a hash table: +for each basic block in this function, +this hash table gives all variables alive at the exit of it. +*) +let liveness_analysis fn = + let live_in = Hashtbl.create 1024 in + let live_out = Hashtbl.create 1024 in + + (* Find all basic blocks in the function `fn` *) + let blocks = Basic_vec.make ~dummy:"" 32 in + let visited = ref Varset.empty in + let rec get_blocks x = + if not (Varset.mem x !visited) then + Basic_vec.push blocks x; + visited := Varset.add x !visited; + Basic_vec.iter (block_of x).succ get_blocks + in + get_blocks fn; + let blocks = Basic_vec.to_list blocks in + + (* Initialize live_in and live_out to empty *) + List.iter (fun name -> + Hashtbl.add live_in name Varset.empty; + Hashtbl.add live_out name Varset.empty + ) blocks; + + (* Keep doing until reaches fixed point *) + let rec iterate worklist = + let last_item = Basic_vec.pop_opt worklist in + match last_item with + | None -> () + | Some fn -> + List.iter (fun name -> + let block = block_of name in + let old_live_in = Hashtbl.find live_in name in + + (* Update live_out *) + (* It should be the union of live_in of all successors *) + let new_live_out = + List.fold_left (fun x succ_name -> + Varset.union x (Hashtbl.find live_in succ_name) + ) Varset.empty (Basic_vec.to_list block.succ) + in + + Hashtbl.replace live_out name new_live_out; + + (* Re-calculate live-in *) + let body = Basic_vec.to_list block.body in + let def_var = List.concat_map Riscv_ssa.def body in + let use_var = List.concat_map Riscv_ssa.use body in + let def = List.map (fun (x: Riscv_ssa.var) -> x.name) def_var |> Varset.of_list in + let use = List.map (fun (x: Riscv_ssa.var) -> x.name) use_var |> Varset.of_list in + let new_live_in = Varset.union use (Varset.diff new_live_out def) in + + (* If live-in has changed, then all predecessors are subject to change; *) + (* Push all of them into worklist *) + if not (Varset.equal old_live_in new_live_in) then + Hashtbl.replace live_in name new_live_in; + Basic_vec.append worklist block.pred; + + iterate worklist + ) blocks; + in + iterate (Hashtbl.find exit_fn fn); + + live_out + +let ssa_of_cfg fn = + let inst = Basic_vec.empty () in + let visited = ref Varset.empty in + let rec get_blocks x = + if not (Varset.mem x !visited) then + let block = block_of x in + + (* Body does not contain labels; *) + (* Fill it in here *) + Basic_vec.push inst (Riscv_ssa.Label x); + Basic_vec.append inst block.body; + visited := Varset.add x !visited; + Basic_vec.iter block.succ get_blocks + in + get_blocks fn; + inst |> Basic_vec.to_list + +let opt ssa = + visit_fn build_cfg ssa; + let s = map_fn ssa_of_cfg ssa in + Basic_io.write "core.ssa" (String.concat "\n" (List.map Riscv_ssa.to_string s)); + s \ No newline at end of file diff --git a/src/riscv_ssa.ml b/src/riscv_ssa.ml new file mode 100644 index 0000000..4fa4503 --- /dev/null +++ b/src/riscv_ssa.ml @@ -0,0 +1,918 @@ +(** Convert common IR into form of static single assignment (SSA). *) + +module Ident = Basic_core_ident + +type var = { + name: string; + ty: Mtype.t; +} + +(* +We store all discarded values (e.g. unit) into variable of this name. +*) +let discard = "_" +let unit = { name = discard; ty = Mtype.T_unit } + +let to_string (r: var) = + Printf.sprintf "%s: %s" r.name (Mtype.to_string r.ty) + +(** Similar to R-type instructions in RISC-V. *) +type r_type = { + rd: var; + rs1: var; + rs2: var; +} + +(** R-type, but only one operand. *) +type r2_type = { + rd: var; + rs1: var; +} + +(** +Calls function named `fn` with args `args`, +and store the result in `rd`. +*) +type call_data = { + rd: var; + fn: string; + args: var list; +} + +(** +Lengths of immediates. +Other lengths (like 16-bit short) are not supported currently. +*) +type imm_type = Bit32 | Bit64 | Bit8 + +(** +Assigns (un-)signed integer `imm` to `rd`. +*) +type assign_int = { + rd: var; + imm: int64; + size: imm_type; + signed: bool; +} + +(** +Assigns floating point number `imm` to `rd`. +*) +type assign_fp = { + rd: var; + imm: float; + size: imm_type; +} + +(** +Assigns string `imm` to `rd`. + +We don't care about how string should be represented; +that's the job for riscv.ml. +*) +type assign_str = { + rd: var; + imm: string; +} + +(** +Assigns `rs` to `rd`. + +These are both variables, as opposed to other assign_* types. +*) +type assign = { + rd: var; + rs: var; +} + +(** +Similar to `ld` and `st` in RISC-V. + +`rd` and `rs` have different meanings in loads and stores: +We load things from `rs` into `rd`, +and store things from `rd` into `rs`. +*) +type mem_access = { + rd: var; + rs: var; + offset: int; +} + +type phi = { + rd: var; + rs: (var * string) list; +} + +type malloc = { + rd: var; + size: int; +} + +(** +`entry` is the first basic block we'll encounter in this function. +*) +and fn = { + fn: string; + args: var list; + body: t list; +} + +and branch = { + cond: var; + ifso: string; + ifnot: string; +} + +(** +Instructions available in 3-address code and SSA. +*) +and t = +(* Arithmetic operations *) +| Add of r_type +| Sub of r_type +| Mul of r_type +| Div of r_type +| Mod of r_type +| Less of r_type +| Leq of r_type +| Great of r_type +| Geq of r_type +| Eq of r_type +| Neq of r_type +| Neg of r2_type + +(* Floating point operations *) +| FAdd of r_type +| FSub of r_type +| FMul of r_type +| FDiv of r_type +| FLess of r_type +| FLeq of r_type +| FGreat of r_type +| FGeq of r_type +| FEq of r_type +| FNeq of r_type +| FNeg of r2_type + +(* Others *) +| Call of call_data +| AssignInt of assign_int +| AssignFP of assign_fp +| AssignStr of assign_str +| Assign of assign +| Load of mem_access +| Store of mem_access +| Jump of string +| Branch of branch +| Label of string +| Phi of phi +| FnDecl of fn +| Malloc of malloc +| Return of var +| Nop + +let to_string t = + let rtype op ({ rd; rs1; rs2 }: r_type) = + Printf.sprintf "%s = %s %s %s" (to_string rd) rs1.name op rs2.name + in + + (** Deal with indentation inside functions. *) + let rec str t depth = + String.make (depth * 2) ' ' ^ + match t with + | Add r -> rtype "+" r + | Sub r -> rtype "-" r + | Mul r -> rtype "*" r + | Div r -> rtype "/" r + | Mod r -> rtype "mod" r + | Less r -> rtype "<" r + | Leq r -> rtype "<=" r + | Great r -> rtype ">" r + | Geq r -> rtype ">=" r + | Eq r -> rtype "==" r + | Neq r -> rtype "!=" r + | Neg { rd; rs1 } -> Printf.sprintf "%s = -%s" (to_string rd) rs1.name + + | FAdd r -> rtype "+." r + | FSub r -> rtype "-." r + | FMul r -> rtype "*." r + | FDiv r -> rtype "/." r + | FLess r -> rtype "<." r + | FLeq r -> rtype "<=." r + | FGreat r -> rtype ">." r + | FGeq r -> rtype ">=." r + | FEq r -> rtype "==." r + | FNeq r -> rtype "!=." r + | FNeg { rd; rs1 } -> Printf.sprintf "%s = -%s" (to_string rd) rs1.name + + | Call { rd; fn; args } -> + let args_list = String.concat ", " (List.map (fun x -> x.name) args) in + Printf.sprintf "%s = call %s (%s)" (to_string rd) fn args_list + + | AssignInt { rd; imm; size; signed } -> + (* We follow C convention of representing literals. *) + let suffix = (match (size, signed) with + | (Bit32, true) -> "" + | (Bit64, true) -> "ll" + | (Bit32, false) -> "u" + | (Bit64, false) -> "ull" + | (Bit8, true) -> "b" + | (Bit8, false) -> "ub") + in + Printf.sprintf "%s = %s%s" (to_string rd) (Int64.to_string imm) suffix + + | AssignFP { rd; imm; size; } -> + let suffix = (match size with + | Bit32 -> "f" + | Bit64 -> "" + | _ -> failwith "riscv_ssa.ml: bad floating-point length") + in + Printf.sprintf "%s = %f%s" (to_string rd) imm suffix + + | AssignStr { rd; imm; } -> + Printf.sprintf "%s = \"%s\"" (to_string rd) imm + + | Assign { rd; rs; } -> + Printf.sprintf "%s = %s" (to_string rd) rs.name + + | Load { rd; rs; offset } -> + Printf.sprintf "%s = %s[offset = %d]" (to_string rd) rs.name offset + + | Store { rd; rs; offset } -> + Printf.sprintf "%s[offset = %d] = %s" rs.name offset rd.name + + | Jump target -> + Printf.sprintf "jump %s" target + + | Branch { cond; ifso; ifnot } -> + Printf.sprintf "br %s true:%s false:%s" cond.name ifso ifnot + + | Label label -> + Printf.sprintf "\n%s%s:" (String.make (depth * 2 - 2) ' ') label + + | Phi { rd; rs } -> + let rs_str = List.map (fun (r, label) -> Printf.sprintf "%s[%s]" r.name label) rs in + Printf.sprintf "%s = φ %s" (to_string rd) (String.concat " " rs_str) + + | Malloc { rd; size } -> + Printf.sprintf "%s = malloc %d" rd.name size + + | FnDecl { fn; args; body; } -> + let args_str = String.concat ", " (List.map to_string args) in + let body_str = String.concat "\n" (List.map (fun t -> str t (depth + 1)) body) in + + Printf.sprintf "fn %s (%s) {\n%s\n}\n" fn args_str body_str + + | Return var -> + Printf.sprintf "return %s" var.name + + | Nop -> "nop" + + in str t 0 + +(** Counter of temporaries. *) +let slot = ref 0 + +(** Construct a new temporary variable. *) +let new_temp ty = + let name = "%" ^ Int.to_string !slot in + slot := !slot + 1; + { name; ty } + + +(** Construct a new label. *) +let new_label prefix = + let name = prefix ^ Int.to_string !slot in + slot := !slot + 1; + name + +(** +Currently I don't know what does `prim` ever mean in some places, +so I ignore them in total. + +Try update this when I know more. +*) +let warn prim = match prim with +| None -> () +| Some _ -> prerr_endline "warning: prim is not null" + + +let offset_table = Hashtbl.create 64 +let size_table = Hashtbl.create 64 + +let offsetof name pos = Hashtbl.find offset_table (name, pos) + + +(** This assumes RISCV64. Perhaps support 32 as well in future? *) +let rec sizeof ty = + let pointer_size = 8 in + + match ty with + | Mtype.T_bool -> 1 + | Mtype.T_byte -> 1 + | Mtype.T_bytes -> pointer_size + | Mtype.T_char -> 1 + | Mtype.T_double -> 8 + | Mtype.T_float -> 4 + | Mtype.T_func _ -> pointer_size + | Mtype.T_int -> 4 + | Mtype.T_int64 -> 8 + | Mtype.T_string -> pointer_size + | Mtype.T_uint -> 4 + | Mtype.T_uint64 -> 8 + | Mtype.T_unit -> 0 + | Mtype.T_tuple { tys } -> List.fold_left (fun total x -> total + sizeof x) 0 tys + | Mtype.T_constr id -> Hashtbl.find size_table id + | _ -> failwith "riscv_ssa.ml: cannot calculate size" + + +(** Maps all result registers with `fd` and all operands with `fs`. *) +let rec reg_map fd fs t = match t with +| Add { rd; rs1; rs2; } -> Add { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| Sub { rd; rs1; rs2; } -> Sub { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| Mul { rd; rs1; rs2; } -> Mul { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| Div { rd; rs1; rs2; } -> Div { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| Mod { rd; rs1; rs2; } -> Mod { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| Less { rd; rs1; rs2; } -> Less { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| Leq { rd; rs1; rs2; } -> Leq { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| Great { rd; rs1; rs2; } -> Great { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| Geq { rd; rs1; rs2; } -> Geq { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| Eq { rd; rs1; rs2; } -> Eq { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| Neq { rd; rs1; rs2; } -> Neq { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| Neg { rd; rs1 } -> Neg { rd = fd rd; rs1 = fs rs1 } +| FAdd { rd; rs1; rs2; } -> FAdd { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| FSub { rd; rs1; rs2; } -> FSub { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| FMul { rd; rs1; rs2; } -> FMul { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| FDiv { rd; rs1; rs2; } -> FDiv { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| FLess { rd; rs1; rs2; } -> FLess { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| FLeq { rd; rs1; rs2; } -> FLeq { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| FGreat { rd; rs1; rs2; } -> FGreat { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| FGeq { rd; rs1; rs2; } -> FGeq { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| FEq { rd; rs1; rs2; } -> FEq { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| FNeq { rd; rs1; rs2; } -> FNeq { rd = fd rd; rs1 = fs rs1; rs2 = fs rs2 } +| FNeg { rd; rs1 } -> FNeg { rd = fd rd; rs1 = fs rs1 } +| Call { rd; fn; args } -> Call { rd = fd rd; fn; args = List.map fs args } +| AssignInt { rd; imm; size; signed } -> AssignInt { rd = fd rd; imm; size; signed } +| AssignFP { rd; imm; size } -> AssignFP { rd = fd rd; imm; size; } +| AssignStr { rd; imm } -> AssignStr { rd = fd rd; imm; } +| Assign { rd; rs } -> Assign { rd = fd rd; rs = fs rs } +| Load { rd; rs; offset } -> Load { rd = fd rd; rs = fs rs; offset } +| Store { rd; rs; offset } -> Load { rd = fs rd; rs = fs rs; offset } +| Jump label -> Jump label +| Branch { cond; ifso; ifnot } -> Branch { cond = fs cond; ifso; ifnot } +| Label label -> Label label +| Phi { rd; rs } -> Phi { rd = fd rd; rs = List.map (fun (x, name) -> (fs x, name)) rs } +| FnDecl { fn; args; body } -> FnDecl { fn; args; body = List.map (fun x -> reg_map fd fs x) body } +| Malloc { rd; size } -> Malloc { rd = fd rd; size } +| Return var -> Return (fs var) +| Nop -> Nop + +(** Variables that has been accessed in this instruction. *) +let use t = + let result = ref [] in + let fs = (fun x -> result := x :: !result; unit) in + reg_map (fun _ -> unit) fs t |> ignore; + !result + +(** The variable defined in the instruction. *) +let def t = + let result = ref [] in + let fd = (fun x -> result := x :: !result; unit) in + reg_map fd (fun _ -> unit) t |> ignore; + !result + +(** Push the correct sequence of instruction based on primitives. *) +let deal_with_prim ssa rd (prim: Primitive.prim) args = + let die () = + failwith "riscv_ssa.ml: bad primitive format" + in + + match prim with + | Pcomparison { operand_type; operator } -> + let is_fp = (operand_type = F32 || operand_type = F64) in + let op = (match is_fp, operator, args with + | false, Lt, [rs1; rs2] -> (Less { rd; rs1; rs2 }) + | true, Lt, [rs1; rs2] -> (FLess { rd; rs1; rs2 }) + | false, Gt, [rs1; rs2] -> (Great { rd; rs1; rs2 }) + | true, Gt, [rs1; rs2] -> (FGreat { rd; rs1; rs2 }) + | false, Ne, [rs1; rs2] -> (Neq { rd; rs1; rs2 }) + | true, Ne, [rs1; rs2] -> (FNeq { rd; rs1; rs2 }) + | false, Eq, [rs1; rs2] -> (Eq { rd; rs1; rs2 }) + | true, Eq, [rs1; rs2] -> (FEq { rd; rs1; rs2 }) + | false, Le, [rs1; rs2] -> (Leq { rd; rs1; rs2 }) + | true, Le, [rs1; rs2] -> (FLeq { rd; rs1; rs2 }) + | false, Ge, [rs1; rs2] -> (Geq { rd; rs1; rs2 }) + | true, Ge, [rs1; rs2] -> (FGeq { rd; rs1; rs2 }) + | _ -> die ()) in + Basic_vec.push ssa op + + | Parith { operand_type; operator } -> + let is_fp = (operand_type = F32 || operand_type = F64) in + let op = (match is_fp, operator, args with + | false, Add, [rs1; rs2] -> (Add { rd; rs1; rs2 }) + | true, Add, [rs1; rs2] -> (FAdd { rd; rs1; rs2 }) + | false, Sub, [rs1; rs2] -> (Sub { rd; rs1; rs2 }) + | true, Sub, [rs1; rs2] -> (FSub { rd; rs1; rs2 }) + | false, Mul, [rs1; rs2] -> (Mul { rd; rs1; rs2 }) + | true, Mul, [rs1; rs2] -> (FMul { rd; rs1; rs2 }) + | false, Div, [rs1; rs2] -> (Div { rd; rs1; rs2 }) + | true, Div, [rs1; rs2] -> (FDiv { rd; rs1; rs2 }) + | false, Mod, [rs1; rs2] -> (Mod { rd; rs1; rs2 }) + | false, Neg, [rs1] -> (Neg { rd; rs1 }) + | true, Neg, [rs1] -> (FNeg { rd; rs1 }) + | _ -> die ()) in + Basic_vec.push ssa op + + | Pignore -> () + + | _ -> Basic_vec.push ssa (Call { rd; fn = (Primitive.sexp_of_prim prim |> S.to_string); args }) + + +(** Calculate offset of fields in record types. *) +let update_types ({ defs; _ }: Mtype.defs) = + let types = Mtype.Id_hash.to_list defs in + + let visit (name, info) = + match info with + | Mtype.Placeholder -> () + | Mtype.Externref -> () + | Mtype.Trait _ -> () + + | Mtype.Record { fields } -> + let extract (x: Mtype.field_info) = x.field_type in + let field_types = List.map extract fields in + let field_sizes = List.map sizeof field_types in + let offset = ref 0 in + let offsets = List.map (fun x -> let y = !offset in offset := x + !offset; y) field_sizes in + List.iteri (fun i x -> Hashtbl.add offset_table (name, i) x) offsets; + Hashtbl.add size_table name !offset + + | _ -> failwith "TODO: riscv_ssa.ml: cannot deal with this type" + in + List.iter visit types + +(** +This is reserved for `continue`s. +See the match case for `Cexpr_loop` in `do_convert` for more details. + +It represents a list of continue clauses, each with a list of arguments and a label, +marking where the argument comes from. +*) +let conts: (var * string) list list ref = ref [] + +(** +This function stores the SSA generated in the given argument `ssa`. + +It returns the variable in which the result of the last instruction pushed is stored. +*) +let rec do_convert ssa (expr: Mcore.expr) = + match expr with + | Cexpr_unit _ -> + unit + + | Cexpr_var { id; ty; prim; _ } -> + warn prim; + + let variable = { name = Ident.to_string id; ty } in + + (* We treat mutables as pointers. *) + (match id with + | Pmutable_ident _ -> + let rd = new_temp ty in + Basic_vec.push ssa (Load { rd; rs = variable; offset = 0 }); + rd + + | _ -> variable); + + + (* Not quite sure about this; is it simply a variable access? *) + | Cexpr_object { self; } -> + do_convert ssa self + + (* Primitives are intrinsic functions. *) + (* We tidy some of these up, and compile others into functions. *) + | Cexpr_prim { prim; args; ty; _ } -> + let rd = new_temp ty in + (match prim, args with + | Psequand, [rs1; rs2] -> + (* Short circuiting, compile into if-else *) + (* rd = rs1 && rs2 -> rd = if (rs1) rs2 else false *) + let ifso = new_label "sequand_if_" in + let ifnot = new_label "sequand_else_" in + let ifexit = new_label "sequand_exit_" in + let t1 = new_temp Mtype.T_bool in + let t2 = new_temp Mtype.T_bool in + let cond = do_convert ssa rs1 in + Basic_vec.push ssa (Branch { cond; ifso; ifnot }); + + Basic_vec.push ssa (Label ifso); + let rs = do_convert ssa rs2 in + Basic_vec.push ssa (Assign { rd = t1; rs }); + Basic_vec.push ssa (Jump ifexit); + + Basic_vec.push ssa (Label ifnot); + Basic_vec.push ssa (AssignInt { rd = t2; imm = 0L; size = Bit8; signed = true }); + Basic_vec.push ssa (Jump ifexit); + + Basic_vec.push ssa (Label ifexit); + Basic_vec.push ssa (Phi { rd; rs = [(t1, ifso); (t2, ifnot) ]}) + + | Psequor, [rs1; rs2] -> + (* Short circuiting, compile into if-else *) + (* rd = rs1 || rs2 -> rd = if (rs1) true else rs2 *) + let ifso = new_label "sequor_if_" in + let ifnot = new_label "sequor_else_" in + let ifexit = new_label "sequor_exit_" in + let t1 = new_temp Mtype.T_bool in + let t2 = new_temp Mtype.T_bool in + let cond = do_convert ssa rs1 in + Basic_vec.push ssa (Branch { cond; ifso; ifnot }); + + Basic_vec.push ssa (Label ifso); + Basic_vec.push ssa (AssignInt { rd = t1; imm = 1L; size = Bit8; signed = true }); + Basic_vec.push ssa (Jump ifexit); + + Basic_vec.push ssa (Label ifnot); + let rs = do_convert ssa rs2 in + Basic_vec.push ssa (Assign { rd = t2; rs }); + Basic_vec.push ssa (Jump ifexit); + + Basic_vec.push ssa (Label ifexit); + Basic_vec.push ssa (Phi { rd; rs = [(t1, ifso); (t2, ifnot) ]}) + + | _ -> + let args = List.map (fun expr -> do_convert ssa expr) args in + deal_with_prim ssa rd prim args); + rd + + | Cexpr_let { name; rhs; body; _ } -> + let rs = do_convert ssa rhs in + (match name with + | Pmutable_ident _ -> + (* We use `bytes` to represent arbitrary pointers. *) + let space = new_temp Mtype.T_bytes in + let rd = { name = Ident.to_string name; ty = Mtype.T_bytes } in + Basic_vec.push ssa (Malloc { rd = space; size = sizeof rd.ty }); + Basic_vec.push ssa (Assign { rd; rs = space }); + Basic_vec.push ssa (Store { rd; rs; offset = 0 }); + + | _ -> + let rd = { name = Ident.to_string name; ty = rs.ty } in + Basic_vec.push ssa (Assign { rd; rs })); + do_convert ssa body + + | Cexpr_apply { func; args; ty; prim; _ } -> + warn prim; + let rd = new_temp ty in + let fn = Ident.to_string func in + let args = List.map (fun expr -> do_convert ssa expr) args in + Basic_vec.push ssa (Call { rd; fn; args }); + rd + + | Cexpr_sequence { expr1; expr2; _ } -> + do_convert ssa expr1 |> ignore; + do_convert ssa expr2 + + (* Meaning: access the `pos`-th field of `record` *) + | Cexpr_field { record; accessor; pos; ty; _ } -> + let rd = new_temp ty in + let rs = do_convert ssa record in + + let name = + (match rs.ty with + | T_constr id -> id + | _ -> failwith "TODO: riscv_ssa.ml: currently unsupported record type") + in + + (match accessor with + | Label _ -> () + | _ -> failwith "TODO: riscv_ssa.ml: currently unsupported accessor"); + + let offset = offsetof name pos in + Basic_vec.push ssa (Load { rd; rs; offset; }); + rd + + (* Meaning: set the `pos`-th field of `record` to `field` *) + | Cexpr_mutate { record; pos; field } -> + let rs = do_convert ssa record in + let rd = do_convert ssa field in + + let name = + (match rs.ty with + | T_constr id -> id + | _ -> failwith "riscv_ssa.ml: can only mutate record types") + in + + let offset = offsetof name pos in + Basic_vec.push ssa (Store { rd; rs; offset; }); + unit + + | Cexpr_if { cond; ifso; ifnot; ty; _ } -> + let rd = new_temp ty in + + let cond = do_convert ssa cond in + + let ifso_ssa = Basic_vec.make ~dummy:Nop 20 in + let ifso_result = do_convert ifso_ssa ifso in + + let ifnot_ssa = Basic_vec.make ~dummy:Nop 20 in + let ifnot_result = + (match ifnot with + | None -> unit + | Some x -> do_convert ifnot_ssa x + ) + in + + let ifso_label = new_label "ifso_" in + let ifnot_label = new_label "ifnot_" in + let ifexit_label = new_label "ifexit_" in + + (* + Compiling into: + + br %cond true:%ifso false:%ifnot + + ifso: + ... + jump ifexit + + ifnot: + ... + jump ifexit + + ifexit: + %rd = φ %ifso_result[ifso] %ifnot_result[ifnot] + *) + + Basic_vec.push ssa (Branch { cond; ifso = ifso_label; ifnot = ifnot_label }); + + Basic_vec.push ssa (Label ifso_label); + Basic_vec.append ssa ifso_ssa; + Basic_vec.push ssa (Jump ifexit_label); + + Basic_vec.push ssa (Label ifnot_label); + Basic_vec.append ssa ifnot_ssa; + Basic_vec.push ssa (Jump ifexit_label); + + Basic_vec.push ssa (Label ifexit_label); + Basic_vec.push ssa (Phi + { rd; rs = [(ifso_result, ifso_label); (ifnot_result, ifnot_label)] }); + + rd + + (* + In MoonBit core IR, loops are by default not looping. + They only jump to beginning when they meet `Cexpr_continue`, + in which case their `args` will be substituted by the `args` provided there, + and the loop entry condition will be tested again. + + Therefore the loop is compiled as follows: + + before: + # evaluate args + jump head + + loop: + %arg = φ %arg[before] %arg1[cont1] ... %argn[contn] + ... + jump exit + + exit: + + A good thing is that loops don't return a value. We don't need to insert + φ after the label `exit`. + *) + | Cexpr_loop { params; body; args; label; _ } -> + (* We need to use the global variable `conts`. *) + (* In case there's an outer loop, we might have tampered it; *) + (* So we must store the contents somewhere. *) + let old_conts = !conts in + + (* Get the labels *) + let loop = Printf.sprintf "%s_%d" label.name label.stamp in + let before = Printf.sprintf "before_%s" loop in + let exit = Printf.sprintf "exit_%s" loop in + + (* Generate body. `conts` will be filled by Cexpr_continue. *) + let body_ssa = Basic_vec.make ~dummy:Nop 32 in + let _ = do_convert body_ssa body in + + (* Start generating according to the template described above. *) + + (* Generate `before`. *) + + Basic_vec.push ssa (Jump before); + Basic_vec.push ssa (Label before); + let results = List.map (do_convert ssa) args in + let cont = List.map (fun x -> (x, before)) results in + conts := cont :: !conts; + + (* Calculate the φ-call. *) + + Basic_vec.push ssa (Jump loop); + Basic_vec.push ssa (Label loop); + + let rec transpose lst = + match lst with + | [] -> [] + | [] :: _ -> [] + | _ -> List.map List.hd lst :: transpose (List.map List.tl lst) + in + + let grouped = transpose !conts in + let gen_phi (par: Mcore.param) rs = + Phi { rd = { name = Ident.to_string par.binder; ty = par.ty }; rs } + in + + let phis = List.map2 gen_phi params grouped in + List.iter (fun x -> Basic_vec.push ssa x) phis; + + (* Generate rest parts. *) + + Basic_vec.append ssa body_ssa; + Basic_vec.push ssa (Jump exit); + Basic_vec.push ssa (Label exit); + + (* Store `conts` back; let outer loop go on normally. *) + conts := old_conts; + unit + + (* See the explanation for Cexpr_loop. *) + | Cexpr_continue { args; label } -> + (* Generate a label, and let the previous block jump to this block. *) + let cont = new_label "continue_" in + Basic_vec.push ssa (Jump cont); + Basic_vec.push ssa (Label cont); + + (* Evaluate arguments and update `conts`. *) + let results = List.map (do_convert ssa) args in + let new_cont = List.map (fun x -> (x, cont)) results in + conts := new_cont :: !conts; + + (* Jump back to the beginning of the loop. *) + let loop_name = Printf.sprintf "%s_%d" label.name label.stamp in + Basic_vec.push ssa (Jump loop_name); + unit + + (* Assigns mutable variables. *) + | Cexpr_assign { var; expr; ty } -> + let rd = do_convert ssa expr in + let rs = { name = Ident.to_string var; ty = Mtype.T_bytes} in + Basic_vec.push ssa (Store { rd; rs; offset = 0 }); + unit + + (* Builds a record type. *) + | Cexpr_record { fields; ty; } -> + (* Allocate space for the record *) + let rd = new_temp Mtype.T_bytes in + Basic_vec.push ssa (Malloc { rd; size = sizeof ty }); + + let name = + (match ty with + | Mtype.T_constr id -> id + (* This must be a record *) + | _ -> assert false) + in + + (* Construct all its fields *) + let visit ({ pos; expr; _ }: Mcore.field_def) = + let result = do_convert ssa expr in + let offset = offsetof name pos in + Basic_vec.push ssa (Store { rd = result; rs = rd; offset }) + in + + List.iter visit fields; + rd + + | Cexpr_break { label; _ } -> + (* Jumps to exit of the loop. *) + let loop_name = Printf.sprintf "%s_%d" label.name label.stamp in + Basic_vec.push ssa (Jump ("exit_" ^ loop_name)); + unit + + | Cexpr_return _ -> + prerr_endline "return"; + unit + + | Cexpr_letfn _ -> + prerr_endline "letfn"; + unit + + | Cexpr_function _ -> + prerr_endline "function"; + unit + + | Cexpr_constr _ -> + prerr_endline "constr"; + unit + + | Cexpr_letrec _ -> + prerr_endline "letrec"; + unit + + | Cexpr_tuple _ -> + prerr_endline "tuple"; + unit + + | Cexpr_record_update _ -> + prerr_endline "record_update"; + unit + + | Cexpr_switch_constr _ -> + prerr_endline "switch constr"; + unit + + | Cexpr_switch_constant _ -> + prerr_endline "switch constant"; + unit + + | Cexpr_handle_error _ -> + prerr_endline "handle error"; + unit + + | Cexpr_array _ -> + prerr_endline "array"; + unit + + | Cexpr_const { c; ty; _ } -> + let rd = new_temp ty in + let instruction = (match c with + | C_string imm -> + AssignStr { rd; imm; } + | C_bool imm -> + AssignInt { rd; imm = Int64.of_int (if imm then 1 else 0); size = Bit8; signed = true; } + | C_char imm -> + AssignInt { rd; imm = Int64.of_int (Uchar.to_int imm); size = Bit8; signed = false; } + | C_int { v; _ } -> + AssignInt { rd; imm = Int64.of_int32 v; size = Bit32; signed = true; } + | C_int64 { v; _ } -> + AssignInt { rd; imm = v; size = Bit64; signed = true; } + | C_uint { v; _ } -> + AssignInt { rd; imm = Int64.of_int32 v; size = Bit32; signed = false; } + | C_uint64 { v; _ } -> + AssignInt { rd; imm = v; size = Bit64; signed = false; } + | C_float { v; _ } -> + AssignFP { rd; imm = v; size = Bit32; } + | C_double { v; _ } -> + AssignFP { rd; imm = v; size = Bit64; } + | C_bytes { v; _ } -> + AssignStr { rd; imm = v } + (* BigInt; currently not supported *) + | _ -> failwith "TODO: riscv_ssa.ml: bigint not supported" + ) in + Basic_vec.push ssa instruction; + rd + +(** +Converts given `expr` into a list of SSA instructions, +along with the variable in which the result of this expression is stored. +*) +let convert_expr (expr: Mcore.expr) = + let ssa = Basic_vec.make ~dummy:Nop 20 in + let return = do_convert ssa expr in + Basic_vec.push ssa (Return return); + Basic_vec.map_into_list ssa (fun x -> x) + + +let convert_toplevel (top: Mcore.top_item) = + match top with + | Ctop_fn { binder; func; export_info_; _ } -> + let var_of_param ({ binder; ty; _ } : Mcore.param) = + { name = Ident.to_string binder; ty } + in + let fn = Ident.to_string binder in + let args = List.map var_of_param func.params in + let body = convert_expr func.body in + if export_info_ != None then + prerr_endline "warning: export info is non-empty"; + [ + FnDecl { fn; args; body } + ] + + (* + No need to deal with stubs. + They are just declarations of builtin functions, which we don't care - + since they don't carry any information about implementation. + *) + | Ctop_stub _ -> [] + + | _ -> failwith "TODO: riscv_ssa.ml: don't know this toplevel" + +let ssa_of_mcore (core: Mcore.t) = + Basic_io.write_s "core.ir" (Mcore.sexp_of_t core); + (* Look through types, and calculate their field offsets *) + update_types core.types; + + (* Deal with other functions *) + let body = List.map convert_toplevel core.body |> List.flatten in + + (* Deal with main *) + let with_main = match core.main with + | Some (main_expr, _) -> + let main_body = convert_expr main_expr in + let main_decl = FnDecl { fn = "main"; args = []; body = main_body } in + main_decl :: body + + | None -> body + in + with_main diff --git a/src/typer.ml b/src/typer.ml index c21f48d..b759c6a 100644 --- a/src/typer.ml +++ b/src/typer.ml @@ -757,7 +757,7 @@ let rec infer_expr (env : Local_env.t) (expr : Syntax.expr) Type.filter_product ~blame:Filtered_type ~arity:None ty_tuple loc_ with | Ok tys -> ( - match Lst.nth_opt tys index with + match List.nth_opt tys index with | Some ty -> ty | None -> add_error diagnostics