open Printf
open MIPSOps
open Primitive
open InterpretUPP
open InterpretRTL
open ERTL
open Integer
exception RuntimeError = InterpretPP.RuntimeError
type ('procedure, 'label) address =
| AddrCode of 'procedure * 'label
| AddrInit
type 'value henv =
'value ref MIPS.RegisterMap.t
let hlookup henv hwr =
try
MIPS.RegisterMap.find hwr henv
with Not_found ->
assert false
let hread henv hwr =
if MIPS.equal hwr MIPS.zero then
VInt 0l
else
!(hlookup henv hwr)
let hwrite henv hwr v =
(hlookup henv hwr) := v
let interpret_primitive henv (p : primitive) =
match p with
| Write ->
let v = hread henv (List.hd MIPS.parameters) in
fprintf stdout "%ld%!" (asInt v)
| Writeln ->
let v = hread henv (List.hd MIPS.parameters) in
fprintf stdout "%ld\n%!" (asInt v)
| Readln ->
let line = input_line stdin in
let v = try
VInt (Int32.of_string line)
with Failure "int of string" ->
fprintf stderr "readln: error: \"%s\" is not the representation of an integer.\n%!" line;
raise RuntimeError
in
hwrite henv MIPS.result v
| Alloc ->
let v = hread henv (List.hd MIPS.parameters) in
hwrite henv MIPS.result (interpret_alloc v)
module Int32Map =
Map.Make(Int32)
type frame = {
incoming: ertl_address value ref Int32Map.t;
mutable outgoing: ertl_address value ref Int32Map.t;
local: ertl_address value ref Register.Map.t;
owner: procedure;
}
and ertl_address =
(ERTL.procedure, Label.t) address
type stack =
frame list
let destruct stack =
match stack with
| [] ->
fprintf stderr "Runtime error -- the call stack is empty (mismatched newframe/deleteframe).\n";
raise RuntimeError
| frame :: stack ->
frame, stack
let slookup stack slot =
let frame, _ = destruct stack in
match slot with
| SlotIncoming offset ->
begin try
Int32Map.find offset frame.incoming
with Not_found ->
fprintf stderr "Runtime error -- invalid incoming slot (%ld).\n" offset;
raise RuntimeError
end
| SlotOutgoing offset ->
begin try
Int32Map.find offset frame.outgoing
with Not_found ->
let cell = ref default in
frame.outgoing <- Int32Map.add offset cell frame.outgoing;
cell
end
let read stack r =
let frame, _ = destruct stack in
read frame.local r
let write stack r v =
let frame, _ = destruct stack in
write frame.local r v
let sread stack slot =
!(slookup stack slot)
let swrite stack slot v =
(slookup stack slot) := v
let empty_stack =
[]
let newframe stack proc =
match stack with
| [] ->
[{
incoming = Int32Map.empty;
outgoing = Int32Map.empty;
local = Register.Map.lift allocate proc.locals;
owner = proc;
}]
| frame :: _ ->
{
incoming = frame.outgoing;
outgoing = Int32Map.empty;
local = Register.Map.lift allocate proc.locals;
owner = proc;
} :: stack
let deleteframe stack =
let _, stack = destruct stack in
stack
let interpret p =
let genv = Array.make (p.globals / MIPS.word) (VInt 0l) in
let henv = MIPS.RegisterMap.lift allocate MIPS.allocatable in
let calls = ref 0 in
let interpret_call stack callee next : stack * ertl_address =
match callee with
| CPrimitiveFunction p ->
interpret_primitive henv p;
stack,
next
| CUserFunction f ->
let proc =
try
StringMap.find f p.defs
with Not_found ->
assert false
in
incr calls;
hwrite henv MIPS.ra (VCode next);
stack,
AddrCode (proc, proc.entry)
in
let valid_frame stack proc =
begin match stack with
| frame :: _ when proc == frame.owner ->
()
| _ ->
fprintf stderr "Runtime error -- current pc does not match current stack frame.\n";
fprintf stderr " -- maybe $ra was incorrectly set,\n";
fprintf stderr " -- or newframe/deleteframe were incorrectly used?\n";
raise RuntimeError
end;
if !calls <> List.length stack then begin
fprintf stderr "Runtime error -- current frames on stack do not match current pending calls.\n";
fprintf stderr " -- maybe newframe/deleteframe were incorrectly used?\n";
raise RuntimeError
end;
in
let interpret_instruction stack proc i : stack * ertl_address =
match i with
| INewFrame l ->
newframe stack proc,
AddrCode(proc, l)
| IDeleteFrame l ->
valid_frame stack proc;
deleteframe stack,
AddrCode (proc, l)
| IGetHwReg (destr, sourcehwr, l) ->
valid_frame stack proc;
write stack destr (hread henv sourcehwr);
stack,
AddrCode (proc, l)
| ISetHwReg (desthwr, sourcer, l) ->
valid_frame stack proc;
hwrite henv desthwr (read stack sourcer);
stack,
AddrCode (proc, l)
| IGetStack (destr, slot, l) ->
valid_frame stack proc;
write stack destr (sread stack slot);
stack,
AddrCode (proc, l)
| ISetStack (slot, sourcer, l) ->
valid_frame stack proc;
swrite stack slot (read stack sourcer);
stack,
AddrCode (proc, l)
| IConst (destr, i, l) ->
valid_frame stack proc;
write stack destr (VInt i);
stack,
AddrCode (proc, l)
| IUnOp (op, destr, sourcer, l) ->
valid_frame stack proc;
write stack destr (unop op (read stack sourcer));
stack,
AddrCode (proc, l)
| IBinOp (op, destr, sourcer1, sourcer2, l) ->
valid_frame stack proc;
write stack destr (binop op (read stack sourcer1) (read stack sourcer2));
stack,
AddrCode (proc, l)
| ICall (callee, _, l) ->
interpret_call stack callee (AddrCode (proc, l))
| ILoad (destr, addressr, offset, l) ->
valid_frame stack proc;
write stack destr (load (read stack addressr) offset);
stack,
AddrCode (proc, l)
| IStore (addressr, offset, valuer, l) ->
valid_frame stack proc;
store (read stack addressr) offset (read stack valuer);
stack,
AddrCode (proc, l)
| IGetGlobal (destr, offset, l) ->
valid_frame stack proc;
write stack destr (gread genv offset);
stack,
AddrCode (proc, l)
| ISetGlobal (offset, sourcer, l) ->
valid_frame stack proc;
gwrite genv offset (read stack sourcer);
stack,
AddrCode (proc, l)
| IGoto l ->
stack,
AddrCode (proc, l)
| IUnBranch (cond, sourcer, l1, l2) ->
valid_frame stack proc;
stack,
AddrCode (proc,
if InterpretMIPS.uncon cond (asInt (read stack sourcer)) then l1 else l2
)
| IBinBranch (cond, sourcer1, sourcer2, l1, l2) ->
valid_frame stack proc;
stack,
AddrCode (proc,
if InterpretMIPS.bincon cond (asInt (read stack sourcer1)) (asInt (read stack sourcer2)) then l1 else l2
)
| IReturn _ ->
decr calls;
stack,
asAddress (hread henv MIPS.ra)
| ITailCall (callee, _) ->
decr calls;
interpret_call stack callee (asAddress (hread henv MIPS.ra))
in
let interpret_instruction_at stack proc l i =
try
interpret_instruction stack proc i
with RuntimeError as e ->
fprintf stderr "Runtime error -- at label: %s\n" (Label.print l);
raise e
in
let rec fetch_and_execute stack proc l : unit =
let i =
try
Label.Map.find l proc.graph
with Not_found ->
fprintf stderr
"Runtime error -- no instruction is associated with the current label (%s).\n" (Label.print l);
raise RuntimeError
in
continue (interpret_instruction_at stack proc l i)
and continue (stack, next) : unit =
match next with
| AddrInit ->
()
| AddrCode (proc, l) ->
fetch_and_execute stack proc l
in
continue (interpret_call empty_stack (CUserFunction "_main") AddrInit)