spice/lib/runtime/code.ml

212 lines
5.5 KiB
OCaml

module Ast = Spice_syntax.Ast
type imm = Value.t
type vtable = Value.vtable
type reg = R of int [@@unboxed]
type ins =
(* registers *)
| LDI of reg * imm
| LDR of reg * reg
(* arithmetic *)
| ADD of reg * reg * reg
| SUB of reg * reg * reg
| MUL of reg * reg * reg
(* comparison *)
| LST of reg * reg * reg
| GRT of reg * reg * reg
| EQL of reg * reg * reg
| NOT of reg * reg
(* objects *)
| GET of reg * reg * reg
| SET of reg * reg * reg
| LOC of reg * reg * string
| CON of reg * vtable
| CAL of reg * reg * reg * reg list
(* control flow *)
| RET of reg
| JMP of block
| CBR of reg * block * block
and block =
{ mutable ins_list_rev : ins list }
let registers = function
| JMP _ -> []
| RET r
| LDI (r, _)
| CON (r, _)
| CBR (r, _, _) -> [r]
| LDR (r1, r2)
| NOT (r1, r2)
| LOC (r1, r2, _) -> [r1; r2]
| ADD (r1, r2, r3)
| SUB (r1, r2, r3)
| MUL (r1, r2, r3)
| LST (r1, r2, r3)
| GRT (r1, r2, r3)
| EQL (r1, r2, r3)
| GET (r1, r2, r3)
| SET (r1, r2, r3) -> [r1; r2; r3]
| CAL (r1, r2, r3, rs) -> r1::r2::r3::rs
let make_block () =
{ ins_list_rev = [] }
let extend b ins =
b.ins_list_rev <- ins :: b.ins_list_rev
let instructions b =
List.rev b.ins_list_rev
let iter_blocks_df f b0 =
let queue = ref [ b0 ] in
let visited = ref !queue in
let enqueue b =
if not (List.memq b !visited) then (
queue := !queue @ [b];
visited := b :: !visited)
in
let rec loop () =
match !queue with
| [] -> ()
| b :: rest ->
queue := rest;
f b;
(* NOTE: only [List.hd b.ins_list_rev] should be a branching instruction, so iterating
the whole list is pointless. but just to be safe ... *)
List.iter
(function
| JMP b1 -> enqueue b1
| CBR (_, b1, b2) -> enqueue b1; enqueue b2
| _ -> ())
b.ins_list_rev;
loop ()
in
loop ()
type funct =
{ n_args : int;
frame_size : int;
entry : block }
type Value.mthd +=
| Function of funct
let make_funct n_args entry =
let frame_size =
let fsize = ref (n_args + 1) in
iter_blocks_df
(fun b ->
fsize :=
List.rev_map registers b.ins_list_rev
|> List.flatten
|> List.fold_left (fun fs (R i) -> max fs (i + 1))
!fsize)
entry;
!fsize
in
{ n_args; frame_size; entry }
(* pretty printing *)
let pp_reg ppf (R i) = Fmt.pf ppf "R%d" i
let pp_vtable ~tbname ppf (vtb : vtable) =
Fmt.pf ppf "%s(%d){" (tbname vtb) vtb.n_slots;
let sep = ref "" in
Hashtbl.iter
(fun name -> function
| Value.Method _ -> ()
| Value.Field idx ->
Fmt.pf ppf "%s%s=%d" !sep name idx;
sep := ",")
vtb.elems;
Fmt.pf ppf "}"
let pp_ins ~tbname ~label ppf = function
| LDI (a, b) -> Fmt.pf ppf "mov %a, %s" pp_reg a (Value.to_string b)
| LDR (a, b) -> Fmt.pf ppf "mov %a, %a" pp_reg a pp_reg b
| ADD (a, b, c) -> Fmt.pf ppf "add %a, %a, %a" pp_reg a pp_reg b pp_reg c
| SUB (a, b, c) -> Fmt.pf ppf "sub %a, %a, %a" pp_reg a pp_reg b pp_reg c
| MUL (a, b, c) -> Fmt.pf ppf "mul %a, %a, %a" pp_reg a pp_reg b pp_reg c
| LST (a, b, c) -> Fmt.pf ppf "lst %a, %a, %a" pp_reg a pp_reg b pp_reg c
| GRT (a, b, c) -> Fmt.pf ppf "grt %a, %a, %a" pp_reg a pp_reg b pp_reg c
| EQL (a, b, c) -> Fmt.pf ppf "eql %a, %a, %a" pp_reg a pp_reg b pp_reg c
| NOT (a, b) -> Fmt.pf ppf "not %a, %a" pp_reg a pp_reg b
| GET (a, b, c) -> Fmt.pf ppf "mov %a, %a[%a]" pp_reg a pp_reg b pp_reg c
| SET (a, b, c) -> Fmt.pf ppf "mov %a[%a], %a" pp_reg b pp_reg c pp_reg a
| LOC (a, b, el) -> Fmt.pf ppf "loc %a, %a.%s" pp_reg a pp_reg b el
| CON (a, vt) -> Fmt.pf ppf "con %a, %a" pp_reg a (pp_vtable ~tbname) vt
| CAL (a, b, c, ds) ->
Fmt.pf ppf "cal %a, %a[%a](" pp_reg a pp_reg b pp_reg c;
List.iteri (fun i d -> if i > 0 then Fmt.pf ppf ","; pp_reg ppf d) ds;
Fmt.pf ppf ")"
| RET a -> Fmt.pf ppf "ret %a" pp_reg a
| JMP b -> Fmt.pf ppf "jmp %s" (label b)
| CBR (a, b1, b2) ->
let l1 = label b1 in
let l2 = label b2 in
Fmt.pf ppf "cbr %a, %s, %s" pp_reg a l1 l2
let dump ?(recursive = true) println main_fn =
let tbqueue = ref [] in
let tbnames = ref [] in
let tbname vtb =
try List.assq vtb !tbnames
with Not_found ->
if recursive then tbqueue := !tbqueue @ [vtb];
let n = List.length !tbnames in
let l = Fmt.str "$tbl%d" n in
tbnames := (vtb, l) :: !tbnames; l
in
let dump_fn fn =
let labels = ref [ fn.entry, "ENTRY" ] in
let label b =
try List.assq b !labels
with Not_found ->
let n = List.length !labels - 1 in
let l = Fmt.str "B%d" n in
labels := (b, l) :: !labels; l
in
let pp_ins = pp_ins ~tbname ~label in
iter_blocks_df
(fun b ->
List.fold_left
(fun pfx ins ->
println (Fmt.str "%-8s%a" pfx pp_ins ins);
"")
(label b ^ ":")
(instructions b)
|> ignore)
fn.entry
in
println "# fun main(0)";
dump_fn main_fn;
let rec loop () =
match !tbqueue with
| [] -> ()
| vtb :: rest ->
tbqueue := rest;
Hashtbl.iter
(fun fname -> function
| Value.Field _ -> ()
| Value.Method i ->
match vtb.mthds.(i) with
| Function fn ->
println "";
println (Fmt.str "# fun %s.%s(%d)" (tbname vtb) fname fn.n_args);
dump_fn fn
| _ -> ())
vtb.elems;
loop ()
in
loop ()