From fb8a2cdcab439f1b65eee29d8dd462a8d75a89cc Mon Sep 17 00:00:00 2001 From: tali Date: Wed, 29 Nov 2023 16:50:26 -0500 Subject: [PATCH] add code interpreter --- bin/main.ml | 8 +++++- lib/runtime/code.ml | 30 +++++++++++++++++++++ lib/runtime/interp.ml | 63 +++++++++++++++++++++++++++++++++++++++++++ lib/spice.ml | 4 +++ 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 lib/runtime/interp.ml diff --git a/bin/main.ml b/bin/main.ml index f234568..c81267d 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -13,5 +13,11 @@ let () = in let prog = Spice.Code.make_program ep in - Fmt.pr "%a" Spice.Code.pp_program prog + let ret = Spice.run prog in + Fmt.pr + "{\"program\":%a,\"output\":%a}" + Spice.Code.pp_program + prog + Spice.Value.pp + ret with Spice.Error msg -> Logs.err (fun m -> m "%s" msg) diff --git a/lib/runtime/code.ml b/lib/runtime/code.ml index a688c54..c9ebfd8 100644 --- a/lib/runtime/code.ml +++ b/lib/runtime/code.ml @@ -40,6 +40,36 @@ type program = { entrypoint : basic_block } let make_program entrypoint = { entrypoint } +let frame_size prog = + let visited = ref [] in + let work_list = ref [ prog.entrypoint ] in + let enqueue bb = if not (List.memq bb !visited) then work_list := bb :: !work_list in + let reg acc i = max acc (i + 1) in + let op acc = function + | Reg i -> reg acc i + | _ -> acc + in + let ins acc = function + | MOV (l, r) | ADD (l, r) | SUB (l, r) | MUL (l, r) -> op (reg acc l) r + | BRT (v, b1, b2) -> + enqueue b1; + enqueue b2; + op acc v + | JMP b -> + enqueue b; + acc + | RET -> acc + in + let rec loop acc = + match !work_list with + | [] -> acc + | bb :: rest -> + visited := bb :: !visited; + work_list := rest; + List.fold_left ins acc (instructions bb) |> loop + in + loop 1 + (* pretty printing *) let pp_reg ppf r = Fmt.pf ppf "$%d" r diff --git a/lib/runtime/interp.ml b/lib/runtime/interp.ml new file mode 100644 index 0000000..454126d --- /dev/null +++ b/lib/runtime/interp.ml @@ -0,0 +1,63 @@ +exception Runtime_error of string + +module Op = struct + let add v1 v2 = + match v1, v2 with + | Value.Int x, Value.Int y -> Value.Int (Int64.add x y) + | _, _ -> raise (Runtime_error "cannot add non integer values") + + let sub v1 v2 = + match v1, v2 with + | Value.Int x, Value.Int y -> Value.Int (Int64.sub x y) + | _, _ -> raise (Runtime_error "cannot sub non integer values") + + let mul v1 v2 = + match v1, v2 with + | Value.Int x, Value.Int y -> Value.Int (Int64.mul x y) + | _, _ -> raise (Runtime_error "cannot mul non integer values") + + let is_true = function + | Value.True -> true + | _ -> false +end + +type frame = { + regs : Value.t array; + mutable pc : Code.ins list; +} + +let make_frame prog = + { + regs = Array.make (Code.frame_size prog) Value.Nil; + pc = Code.instructions Code.(prog.entrypoint); + } + +let return_value fr = fr.regs.(0) + +let eval fr = function + | Code.Cst_nil -> Value.Nil + | Code.Cst_true -> Value.Nil + | Code.Cst_false -> Value.Nil + | Code.Cst_int n -> Value.Int n + | Code.Reg i -> fr.regs.(i) + +let exec fr = function + | Code.MOV (l, r) -> fr.regs.(l) <- eval fr r + | Code.ADD (l, r) -> fr.regs.(l) <- Op.add fr.regs.(l) (eval fr r) + | Code.SUB (l, r) -> fr.regs.(l) <- Op.add fr.regs.(l) (eval fr r) + | Code.MUL (l, r) -> fr.regs.(l) <- Op.mul fr.regs.(l) (eval fr r) + | Code.RET -> fr.pc <- [] + | Code.JMP l -> fr.pc <- Code.instructions l + | Code.BRT (v, l1, l2) -> + if Value.equal (eval fr v) True then fr.pc <- Code.instructions l1 + else fr.pc <- Code.instructions l2 + +let rec run fr = + match fr.pc with + | [] -> return_value fr + | is :: rest -> + fr.pc <- rest; + exec fr is; + run fr + +let run_program pr = run (make_frame pr) diff --git a/lib/spice.ml b/lib/spice.ml index 4d53073..e8af4af 100644 --- a/lib/spice.ml +++ b/lib/spice.ml @@ -16,3 +16,7 @@ let parse input = let compile syn = try Spice_lower.Normalize.to_anf syn with Failure msg -> failf "compilation error: %s" msg + +let run prog = + try Spice_runtime.Interp.run_program prog + with Spice_runtime.Interp.Runtime_error msg -> failf "runtime error: %s" msg