(** This module provides an interpreter for the LTL language. *) let error_prefix = "LTL interpret" let error s = Error.global_error error_prefix s module Mem = Driver.LTLMemory module Val = Mem.Value let chunk = Driver.LTLMemory.int_size module Eval = I8051.Eval (Val) (* Memory *) type memory = LTL.function_def Mem.memory (* Hardware registers environments. They associate a value to the each hardware register. *) type hdw_reg_env = Val.t I8051.RegisterMap.t (* Execution states. *) type state = { pc : Val.address ; isp : Val.address ; exit : Val.address ; carry : Val.t ; renv : hdw_reg_env ; mem : memory ; trace : CostLabel.t list } (* Helpers *) let change_pc st pc = { st with pc = pc } let change_isp st isp = { st with isp = isp } let change_exit st exit = { st with exit = exit } let change_carry st carry = { st with carry = carry } let change_renv st renv = { st with renv = renv } let change_mem st mem = { st with mem = mem } let change_trace st trace = { st with trace = trace } let add_trace st cost_lbl = change_trace st (cost_lbl :: st.trace) let empty_state = { pc = Val.null ; isp = Val.null ; exit = Val.null ; carry = Val.undef ; renv = I8051.RegisterMap.empty ; mem = Mem.empty ; trace = [] } (* Each label of each function is associated a pointer. The base of this pointer is the base of the function in memory. Inside a function, offsets are bijectively associated to labels. *) module Labels_Offsets = Bijection.Make (Label) (Val.Offset) let labels_offsets_internal int_fun = let f lbl _ (lbls_offs, i) = (Labels_Offsets.add1 lbl i lbls_offs, Val.Offset.succ i) in Label.Map.fold f int_fun.LTL.f_graph (* [labels_offsets p] builds a bijection between the labels found in the functions of [p] and some offsets. *) let labels_offsets p = let f (lbls_offs, i) (_, def) = match def with | LTL.F_int int_fun -> labels_offsets_internal int_fun (lbls_offs, i) | _ -> (lbls_offs, i) in fst (List.fold_left f (Labels_Offsets.empty, Val.Offset.zero) p.LTL.functs) let fun_def_of_ptr mem ptr = match Mem.find_fun_def mem ptr with | LTL.F_int def -> def | _ -> error "Trying to fetch the definition of an external function." let fetch_stmt lbls_offs st = let msg = Printf.sprintf "%s does not point to a statement." (Val.string_of_address st.pc) in if Val.is_mem_address st.pc then let off = Val.offset_of_address st.pc in let def = fun_def_of_ptr st.mem st.pc in let lbl = Labels_Offsets.find2 off lbls_offs in Label.Map.find lbl def.LTL.f_graph else error msg let entry_pc lbls_offs ptr def = Val.change_address_offset ptr (Labels_Offsets.find1 def.LTL.f_entry lbls_offs) let init_fun_call lbls_offs st ptr def = let pc = entry_pc lbls_offs ptr def in change_pc st pc let next_pc lbls_offs st lbl = let off = Labels_Offsets.find1 lbl lbls_offs in change_pc st (Val.change_address_offset st.pc off) let framesize st = if Val.is_mem_address st.pc then let def = fun_def_of_ptr st.mem st.pc in def.LTL.f_stacksize else error "Trying to load the stack size of an external function." let add_reg r v st = let renv = I8051.RegisterMap.add r v st.renv in change_renv st renv let get_reg r st = if I8051.RegisterMap.mem r st.renv then I8051.RegisterMap.find r st.renv else error ("Unknown hardware register " ^ (I8051.print_register r) ^ ".") let push st v = let mem = Mem.store st.mem chunk st.isp v in let isp = Val.add_address st.isp (Val.Offset.of_int chunk) in change_mem (change_isp st isp) mem let pop st = let isp = Val.add_address st.isp (Val.Offset.of_int (-chunk)) in let st = change_isp st isp in let v = Mem.load st.mem chunk st.isp in (st, v) let save_ra lbls_offs st lbl = let ra = Val.change_address_offset st.pc (Labels_Offsets.find1 lbl lbls_offs) in let st = push st (List.nth ra 0) in let st = push st (List.nth ra 1) in st let label_of_pointer lbls_offs ptr = (* Printf.printf "Retrieving label of %s\n%!" (Val.to_string ptr) ; *) let off = Val.offset_of_address ptr in Labels_Offsets.find2 off lbls_offs let pointer_of_label lbls_offs ptr lbl = Val.change_address_offset ptr (Labels_Offsets.find1 lbl lbls_offs) let return_pc st = let (st, pch) = pop st in let (st, pcl) = pop st in (st, [pcl ; pch]) let dptr st = List.map (fun r -> get_reg r st) [I8051.dpl ; I8051.dph] (* State pretty-printing *) let current_label lbls_offs st = Labels_Offsets.find2 (Val.offset_of_address st.pc) lbls_offs let print_renv renv = let f r v = if not (Val.eq v Val.undef) then Printf.printf "\n%s = %s%!" (I8051.print_register r) (Val.to_string v) in I8051.RegisterMap.iter f renv let print_state lbls_offs st = Printf.printf "PC: %s (%s)\n%!" (Val.string_of_address st.pc) (current_label lbls_offs st) ; Printf.printf "SP: %s\n%!" (Val.string_of_address [get_reg I8051.spl st ; get_reg I8051.sph st]) ; Printf.printf "ISP: %s%!" (Val.string_of_address st.isp) ; print_renv st.renv ; Printf.printf "\nC = %s%!" (Val.to_string st.carry) ; Mem.print st.mem ; Printf.printf "\n%!" module InterpretExternal = Primitive.Interpret (Mem) let interpret_external mem f args = match InterpretExternal.t mem f args with | (mem', InterpretExternal.V vs) -> (mem', vs) | (mem', InterpretExternal.A addr) -> (mem', addr) let fetch_external_args f st = let size = Mem.size_of_quantity (Primitive.args_byte_size f) in let params = MiscPottier.prefix size I8051.parameters in List.map (fun r -> get_reg r st) params let set_result st vs = let f st (r, v) = add_reg r v st in List.fold_left f st (MiscPottier.combine I8051.rets vs) let interpret_external_call st f next_pc = let args = fetch_external_args f st in let (mem, vs) = interpret_external st.mem f args in let st = change_mem st mem in let st = set_result st vs in change_pc st next_pc let interpret_call lbls_offs st ptr ra = match Mem.find_fun_def st.mem ptr with | LTL.F_int def -> let st = save_ra lbls_offs st ra in init_fun_call lbls_offs st ptr def | LTL.F_ext def -> let next_pc = Val.change_address_offset st.pc (Labels_Offsets.find1 ra lbls_offs) in interpret_external_call st def.AST.ef_tag next_pc let interpret_return lbls_offs st = let (st, pc) = return_pc st in change_pc st pc (* Interpret statements. *) let interpret_stmt lbls_offs st stmt = let next_pc = next_pc lbls_offs in match stmt with | LTL.St_skip lbl -> next_pc st lbl | LTL.St_comment (s, lbl) -> (* Printf.printf "*** %s ***\n\n%!" s ; *) next_pc st lbl | LTL.St_cost (cost_lbl, lbl) -> let st = add_trace st cost_lbl in next_pc st lbl | LTL.St_int (r, i, lbl) -> let st = add_reg r (Val.of_int i) st in next_pc st lbl | LTL.St_pop lbl -> let (st, v) = pop st in let st = add_reg I8051.a v st in next_pc st lbl | LTL.St_push lbl -> let v = get_reg I8051.a st in let st = push st v in next_pc st lbl | LTL.St_addr (x, lbl) -> let vs = Mem.find_global st.mem x in let st = add_reg I8051.dpl (List.nth vs 0) st in let st = add_reg I8051.dph (List.nth vs 1) st in next_pc st lbl | LTL.St_from_acc (destr, lbl) -> let st = add_reg destr (get_reg (I8051.a) st) st in next_pc st lbl | LTL.St_to_acc (srcr, lbl) -> let st = add_reg I8051.a (get_reg srcr st) st in next_pc st lbl | LTL.St_opaccs (opaccs, lbl) -> let (a, b) = Eval.opaccs opaccs (get_reg I8051.a st) (get_reg I8051.b st) in let st = add_reg I8051.a a st in let st = add_reg I8051.b b st in next_pc st lbl | LTL.St_op1 (op1, lbl) -> let v = Eval.op1 op1 (get_reg I8051.a st) in let st = add_reg I8051.a v st in next_pc st lbl | LTL.St_op2 (op2, srcr, lbl) -> let (v, carry) = Eval.op2 st.carry op2 (get_reg I8051.a st) (get_reg srcr st) in let st = change_carry st carry in let st = add_reg I8051.a v st in next_pc st lbl | LTL.St_clear_carry lbl -> let st = change_carry st Val.zero in next_pc st lbl | LTL.St_set_carry lbl -> let st = change_carry st (Val.of_int 1) in next_pc st lbl | LTL.St_load lbl -> let addr = dptr st in let v = Mem.load st.mem chunk addr in let st = add_reg I8051.a v st in next_pc st lbl | LTL.St_store lbl -> let addr = dptr st in let mem = Mem.store st.mem chunk addr (get_reg I8051.a st) in let st = change_mem st mem in next_pc st lbl | LTL.St_call_id (f, lbl) -> interpret_call lbls_offs st (Mem.find_global st.mem f) lbl | LTL.St_call_ptr lbl -> interpret_call lbls_offs st (dptr st) lbl | LTL.St_condacc (lbl_true, lbl_false) -> let v = get_reg I8051.a st in let lbl = if Val.is_true v then lbl_true else if Val.is_false v then lbl_false else error "Undecidable branchment." in next_pc st lbl | LTL.St_return -> interpret_return lbls_offs st let compute_result st = let vs = List.map (fun r -> get_reg r st) I8051.rets in let f res v = res && (Val.is_int v) in let is_int vs = (List.length vs > 0) && (List.fold_left f true vs) in if is_int vs then let chunks = List.map (fun v -> IntValue.Int32.cast (Val.to_int_repr v)) vs in IntValue.Int32.merge chunks else IntValue.Int32.zero let rec iter_small_step debug lbls_offs st = let print_and_return_result (res, cost_labels) = if debug then Printf.printf "Result = %s\n%!" (IntValue.Int32.to_string res) ; (res, cost_labels) in if debug then print_state lbls_offs st ; match fetch_stmt lbls_offs st with | LTL.St_return when Val.eq_address (snd (return_pc st)) st.exit -> print_and_return_result (compute_result st, List.rev st.trace) | stmt -> let st' = interpret_stmt lbls_offs st stmt in iter_small_step debug lbls_offs st' let add_global_vars = List.fold_left (fun mem (id, size) -> Mem.add_var mem id (AST.SQ (AST.QInt size)) None) let add_fun_defs = List.fold_left (fun mem (f_id, f_def) -> Mem.add_fun_def mem f_id f_def) let init_prog (st : state) (p : LTL.program) : state = let mem = add_global_vars (add_fun_defs st.mem p.LTL.functs) p.LTL.vars in change_mem st mem let init_sp st = let (mem, sp) = Mem.alloc st.mem I8051.ext_ram_size in let sp = Val.change_address_offset sp (Val.Offset.of_int I8051.ext_ram_size) in let st = change_mem st mem in (st, sp) let init_isp st = let (mem, isp) = Mem.alloc st.mem I8051.int_ram_size in let st = change_mem (change_isp st isp) mem in let (mem, exit) = Mem.alloc st.mem 1 in let st = change_exit st exit in let st = push st (List.nth exit 0) in let st = push st (List.nth exit 1) in st let init_renv st sp = let f r st = add_reg r Val.undef st in let st = I8051.RegisterSet.fold f I8051.registers st in let spl = List.nth sp 0 in let sph = List.nth sp 1 in let st = add_reg I8051.spl spl st in let st = add_reg I8051.sph sph st in st let init_main_call lbls_offs st main = let ptr = Mem.find_global st.mem main in match Mem.find_fun_def st.mem ptr with | LTL.F_int def -> init_fun_call lbls_offs st ptr def | _ -> error ("Cannot execute the main (\"" ^ main ^ "\"): it is external.") (* Before interpreting, the environment is initialized: - Build a bijection between the labels in the program and some values (taken amongst the offsets). - Add function definitions to the memory and reserve space for the globals. - Allocate memory to emulate the external stack and initialize the external stack pointer. - Allocate memory to emulate the internal stack and initialize the internal stack pointer. - Initialiaze the physical register environment. All are set to 0, except for the stack pointer registers that take the high and low bits of the external stack pointer. - Initialize a call to the main (set the current program counter to the beginning of the function). - Initialize the carry flag to 0. *) let interpret debug p = Printf.printf "*** LTL interpret ***\n%!" ; match p.LTL.main with | None -> (IntValue.Int8.zero, []) | Some main -> let lbls_offs = labels_offsets p in let st = empty_state in let st = init_prog st p in let (st, sp) = init_sp st in let st = init_isp st in let st = init_renv st sp in let st = init_main_call lbls_offs st main in let st = change_carry st Val.zero in iter_small_step debug lbls_offs st