THread through a context to track registers.

It seems to work to allocate and free registers with a kind of reference counting
by membership in an auxilliary list.
This commit is contained in:
Simon Forman 2020-01-28 12:24:04 -08:00
parent 768c7f6599
commit 712dd24e88
1 changed files with 66 additions and 24 deletions

View File

@ -640,59 +640,88 @@ stack_1 must point to stack_2, and so on...)
What if we keep a stack of register/RAM locations in the same order as What if we keep a stack of register/RAM locations in the same order as
the Joy stack? the Joy stack?
Reference counting for registers? Can it be avoided? Reference counting for registers? Can it be avoided? When you "free" a
register you can just check the stack to see if it's still in there and,
if not, release it back to the free pool. You can amortize that w/o
keeping a counter by keeping a linear list of registars alongside the
stack and pushing and popping registers from it as they are used/free'd
and then checking if a register is ready for reclaimation is just
member/3. Or you can just keep a reference count for each register...
Would it be useful to put CLP(FD) constraints on the ref counts?
reggy(FreePool, References)
*/ */
get_reg(R, _) --> {gensym(r, R)}, []. get_reggy([], _, _):- writeln('Out of Registers'), fail.
get_reggy([Reg|FreePool], Reg, FreePool).
get_reg(Reg, reggy(FreePool0, References), reggy(FreePool, [Reg|References])) --> [],
{get_reggy(FreePool0, Reg, FreePool)}.
free_reg(Reg, reggy(FreePool0, References0), reggy(FreePool, References)) --> [],
{ select(Reg, References0, References),
( member(Reg, References) % If reg is still in use
-> FreePool= FreePool0 % we can't free it yet
; FreePool=[Reg|FreePool0] % otherwise we put it back in the pool.
)}.
assoc_reg(_, _, _) --> []. assoc_reg(_, _, _) --> [].
thun_compile([], S, S) --> []. thun_compile(E, Si, So) -->
thun_compile([Term|Rest], Si, So) --> thun_compile(Term, Rest, Si, So). {FP=reggy([r0, r1, r2, r3, r4, r5, r6, r7,
r8, r9, rA, rB, rC, rD, rE, rF], [])},
thun_compile(E, Si, So, FP, _).
thun_compile(int(I), E, Si, So) --> thun_compile([], S, S, FP, FP) --> [].
thun_compile([Term|Rest], Si, So, FP0, FP1) --> thun_compile(Term, Rest, Si, So, FP0, FP1).
thun_compile(int(I), E, Si, So, FP0, FP) -->
[mov_imm(R, int(I))], [mov_imm(R, int(I))],
get_reg(R, _), assoc_reg(R, int(I), _), get_reg(R, FP0, FP1), assoc_reg(R, int(I), _),
thun_compile(E, [R|Si], So). thun_compile(E, [R|Si], So, FP1, FP).
thun_compile(bool(B), E, Si, So) --> thun_compile(bool(B), E, Si, So, FP0, FP) -->
get_reg(R, _), assoc_reg(R, bool(B), _), get_reg(R, FP0, FP1), assoc_reg(R, bool(B), _),
thun_compile(E, [R|Si], So). thun_compile(E, [R|Si], So, FP1, FP).
thun_compile(list(L), E, Si, So) --> thun_compile(list(L), E, Si, So, FP0, FP) -->
% encode_list(_), ??? % encode_list(_), ???
get_reg(R, _), assoc_reg(R, list(L), _), get_reg(R, FP0, FP1), assoc_reg(R, list(L), _),
thun_compile(E, [R|Si], So). thun_compile(E, [R|Si], So, FP1, FP).
thun_compile(symbol(Name), E, Si, So) --> {def(Name, _)}, !, def_compile(Name, E, Si, So). thun_compile(symbol(Name), E, Si, So, FP0, FP) --> {def(Name, _)}, !, def_compile(Name, E, Si, So, FP0, FP).
thun_compile(symbol(Name), E, Si, So) --> {func(Name, _, _)}, !, func_compile(Name, E, Si, So). thun_compile(symbol(Name), E, Si, So, FP0, FP) --> {func(Name, _, _)}, !, func_compile(Name, E, Si, So, FP0, FP).
thun_compile(symbol(Name), E, Si, So) --> {combo(Name, _, _, _, _)}, combo_compile(Name, E, Si, So). thun_compile(symbol(Name), E, Si, So, FP0, FP) --> {combo(Name, _, _, _, _)}, combo_compile(Name, E, Si, So, FP0, FP).
% I'm going to assume that any defs that can be compiled to funcs already % I'm going to assume that any defs that can be compiled to funcs already
% have been. Defs that can't be pre-compiled shove their body expression % have been. Defs that can't be pre-compiled shove their body expression
% onto the pending expression (continuation) to be compiled "inline". % onto the pending expression (continuation) to be compiled "inline".
def_compile(Def, E, Si, So) --> def_compile(Def, E, Si, So, FP0, FP) -->
{def(Def, Body), {def(Def, Body),
append(Body, E, Eo)}, append(Body, E, Eo)},
thun_compile(Eo, Si, So). thun_compile(Eo, Si, So, FP0, FP).
% Functions delegate to a per-function compilation relation. % Functions delegate to a per-function compilation relation.
func_compile(+, E, [A, B|S], So) --> !, func_compile(+, E, [A, B|S], So, FP0, FP) --> !,
[add(B, A, B)], [add(B, A, B)],
thun_compile(E, [B|S], So). free_reg(A, FP0, FP1),
thun_compile(E, [B|S], So, FP1, FP).
func_compile(Func, E, Si, So) --> func_compile(_Func, E, Si, So, FP0, FP) -->
% look up function, compile it... % look up function, compile it...
{Si = S}, {Si = S},
thun_compile(E, S, So). thun_compile(E, S, So, FP0, FP).
combo_compile(Combo, E, Si, So) --> combo_compile(_Combo, E, Si, So, FP0, FP) -->
% look up combinator, compile it... % look up combinator, compile it...
{Si = S, E = Eo}, {Si = S, E = Eo},
thun_compile(Eo, S, So). thun_compile(Eo, S, So, FP0, FP).
compiler(InputString, MachineCode, StackIn, StackOut) :- compiler(InputString, MachineCode, StackIn, StackOut) :-
reset_gensym(r), reset_gensym(r),
@ -739,8 +768,21 @@ StackOut = [r3].
MachineCode = [add(r2, r1, r2), add(r3, r2, r3)], MachineCode = [add(r2, r1, r2), add(r3, r2, r3)],
StackOut = [r3, r4, r5, r6, r7]. StackOut = [r3, r4, r5, r6, r7].
- - - - -
?- compiler(`1 2 3 + +`, MachineCode, StackIn, StackOut).
MachineCode = [mov_imm(r0, int(1)), mov_imm(r1, int(2)), mov_imm(r2, int(3)), add(r1, r2, r1), add(r0, r1, r0)],
StackOut = [r0|StackIn].
register free seems to work...
?- compiler(`1 2 + 3 +`, MachineCode, StackIn, StackOut).
MachineCode = [mov_imm(r0, int(1)), mov_imm(r1, int(2)), add(r0, r1, r0), mov_imm(r1, int(3)), add(r0, r1, r0)],
StackOut = [r0|StackIn] ;
false.