Prolog ThesisAppendix
Prolog ThesisAppendix
Copyright © 1990
Peter Lodewijk Van Roy
Volume 2
Appendices
Appendix A: User manual for the Aquarius Prolog compiler ........................................................ 175
Appendix B: Formal specification of the Berkeley Abstract Machine syntax ............................... 183
Appendix C: Formal specification of the Berkeley Abstract Machine semantics ......................... 189
Appendix E: Extended DCG notation: A tool for applicative programming in Prolog ............... 219
Appendix A
1. Introduction
The Aquarius Prolog compiler reads clauses and directives from stdin and outputs Prolog-readable
compiled code to stdout as one fact per instruction. The output is assembly code for the Berkeley Abstract
Machine (BAM). Directives hold starting from the next predicate that is input. Clauses do not have to be
contiguous in the input stream, however, the whole stream is read before compilation starts.
This manual is organized into ten sections. Section 2 documents the compiler’s directives. Section 3
gives the compiler’s options. Section 4 gives a short overview of the dataflow analysis done by the com-
piler. Section 5 gives the type declarations accepted by the compiler. Section 6 summarizes the differ-
ences between Aquarius Prolog and the Edinburgh standard. Section 7 gives an example showing how to
use the compiler. Section 8 describes the method used to compile specialized entry points to increase the
efficiency of built-ins. Section 9 describes the assembly language interface. Section 10 describes how to
define BAM assembly macros.
2. Directives
The directives recognized by the Aquarius compiler are given in Table 1.
3. Options
The Aquarius compiler’s options are given in three categories: high-level (these options control
actions of the compiler at the Prolog level), architecture-dependent (these options are constant for a partic-
ular architecture), and low-level (mainly useful for debugging purposes). The default options are set for
the VLSI-BAM processor. The options are given in Tables 2, 3, and 4.
4. Dataflow analysis
Dataflow analysis is enabled with the analyze option. It generates ground, nonvar, recursively
dereferenced and uninitialized variable types which are merged with the programmer’s types. Both unini-
tialized memory and uninitialized register types are generated. Entry declarations (given by entry
directives) are used to drive the analysis. Predicates of arity zero are always used as entry declarations.
The quality of the generated types is such that compilation time, execution time, and code size are all
significantly reduced. Therefore it is recommended always to compile with analysis. The whole program
is kept in memory during the analysis.
All mode, entry, and op directives are executed before the analysis starts. Other directives are
executed after the analysis and before compilation. The directives default and clear interfere with
dataflow analysis, so they should be given only when the analyze option is disabled.
5. Types
The Aquarius compiler accepts type declarations for a predicate. Using types results in a significant
improvement in code quality. Types are represented as (Head:-Formula) where Head contains
only variables and Formula is a logical conjunction. Almost any Prolog test can be used in a type for-
mula. Possible type formulas are given in Table 5. This representation for types is simple, yet powerful
enough to represent much important information in a compact way. The representation generalizes the
declarations of Dec-10 Prolog. For example, the Dec-10 declaration:
:- mode(concat(+,+,-))
is expressed here as:
:- mode((concat(A,B,C):-nonvar(A),nonvar(B),var(C))).
procedure(a/1).
deref(r(0),r(0)).
hash(atomic,r(0),2,l(a/1,1)).
fail.
label(l(a/1,1)).
pragma(hash_length(2)).
pair(a,l(a/1,3)).
pair(b,l(a/1,4)).
label(l(a/1,3)).
label(l(a/1,4)).
return.
VLSI-BAM processor assembly). A simple call may not be nested. It is more efficient than a standard call
because it does not need an environment frame around it in the calling routine.
If the survive flag is n then the predicate is assumed to invalidate all argument register values. In
this case the argument registers are available as scratch registers and the calling routine will create an
environment frame.
Efficient parameter passing is implemented by using uninitialized variables. These are of two kinds:
uninitialized memory and uninitialized register variables. An uninitialized memory variable is a pointer to
an empty memory cell. Binding to it is a store to memory. An uninitialized register variable is an empty
register. Binding to it is a move to the register. No trailing or dereferencing is needed in either case.
Declaring an argument to have a uninitialized register type means that the output of the routine is
stored in the corresponding argument register. Similarly, an uninitialized memory type requires the output
to be stored to the location pointed to by the argument register. Inputs and outputs must be put in separate
registers.
Appendix B
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
instr(pad(N)) :- pos(N).
instr(unify_atomic(V,I,L)) :- var_i(V), an_atomic(I), lbl(L).
instr(fail).
% 3. Arithmetic instructions:
instr(add(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
instr(sub(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
instr(mul(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
instr(div(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
instr(mod(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
instr(and(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
instr( or(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
instr(xor(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
instr(not(A,V)) :- numarg_i(A), a_var(V).
instr(sll(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
instr(sra(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
instr(sll). /* vlsi_plm only */
instr(sra). /* vlsi_plm only */
% 4. Procedural instructions:
instr(procedure(N/A)) :- atom(N), natural(A).
instr(call(N/A)) :- atom(N), natural(A).
instr(return).
instr(simple_call(N/A)) :- atom(N), natural(A).
instr(simple_return).
instr(label(L)) :- lbl(L).
instr(jump(L)) :- lbl(L).
instr(allocate(Perms)) :- natural(Perms).
instr(deallocate(Perms)) :- natural(Perms).
instr(nop).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This section describes the parts of the BAM language that are never output
% by the compiler, but only used by the BAM assembly programmer. This is used
% to write the run-time system in BAM code, so that it is as portable as
185
user_instr(jump_reg(R)) :- reg(R).
user_instr(jump_nt(C,A,B,L)) :- cond(C), numarg_i(A), numarg_i(B), lbl(L).
user_instr(ord(A,B)) :- arg(A), a_var(B).
user_instr(val(T,A,V)) :- a_tag(T), numarg_i(A), a_var(V).
user_instr(add_nt(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
user_instr(sub_nt(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
user_instr(and_nt(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
user_instr( or_nt(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
user_instr(xor_nt(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
user_instr(not_nt(A,V)) :- numarg_i(A), a_var(V).
user_instr(sll_nt(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
user_instr(sra_nt(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
user_instr(trail_bda(X)) :- a_var(X).
% Additional registers:
% See Implementation Manual for list of existing registers.
user_reg(r(A)) :- atom(A).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% A variable is a multiple of N.
% Inserted just before loads in readmode unification.
pragma(align(V,N)) :- a_var(V), pos(N).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
a_tag(tatm). /* atom */
a_tag(tint). /* integer */
a_tag(tneg). /* negative integer */
a_tag(tpos). /* nonnegative integer */
a_tag(tstr). /* structure */
186
atom_tag(tatm).
pointer_tag(tstr).
pointer_tag(tlst).
pointer_tag(tvar).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
heap_ptr(r(h)).
choice_ptr(r(b)).
reg(r(I)) :- int(I).
reg(U) :- user_reg(U).
hreg(R) :- reg(R).
hreg(R) :- heap_ptr(R).
perm(p(I)) :- natural(I).
an_atomic(I) :- int(I).
an_atomic(TˆA) :- atom(A), atom_tag(T).
an_atomic(Tˆ(F/N)) :- atom(F), pos(N), atom_tag(T).
a_var(Reg) :- reg(Reg).
a_var(Perm) :- perm(Perm).
arg(Arg) :- a_var(Arg).
arg(Arg) :- an_atomic(Arg).
var_i(Var) :- a_var(Var).
var_i([Var]) :- a_var(Var).
arg_i(Arg) :- var_i(Arg).
arg_i(Arg) :- an_atomic(Arg).
numreg(Arg) :- reg(Arg).
numreg(Arg) :- int(Arg).
numarg_i(Arg) :- var_i(Arg).
numarg_i(Arg) :- int(Arg).
var_off([Var]) :- a_var(Var).
var_off([Var+I]) :- a_var(Var), pos(I).
ea_m(Arg) :- arg(Arg).
ea_m(VarOff) :- var_off(VarOff).
ea_m(TagˆH) :- pointer_tag(Tag), heap_ptr(H).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
eq_ne(eq). /* Equal */
eq_ne(ne). /* Not equal */
hash_type(atomic).
hash_type(structure).
lbl(fail).
lbl(N/A) :- atom(N), natural(A).
lbl(l(N/A,I)) :- atom(N), natural(A), natural(I).
nv_flag(nonvar).
nv_flag(var).
nv_flag(’?’).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
ground(N, _) :- N=:=0.
ground(N, X) :- N=\=0, arg(N, X, A), ground(A), N1 is N-1, ground(N1, X).
int(N) :- integer(N).
natural(N) :- integer(N), N>=0.
pos(N) :- integer(N), N>0.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
188
189
Appendix C
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% The specification does not include the user instructions of the BAM since
% their behavior depends on the target machine.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Meaning of registers:
% r(b) Index to most recent choice point.
% r(e) Index to current environment.
% r(tr) Top of trail stack.
% r(h) Top of heap stack.
% r(hb) Value of r(h) at last choice point creation.
% r(pc) Code address.
% r(cp) Continuation pointer for code.
% r(tmp_cp) Temporary continuation pointer for code, used only in simple_call.
% r(retry) Retry address for backtracking, only exists inside choice points.
% r(I) Argument and temporary register.
% p(I) Location on current environment.
% Comments:
% A word is either an integer or a structure of the form TagˆValue where Value
% is a natural number except if Tag=tatm, in which case Value is an atom or a
% structure (F/N) where F is an atom and N is a natural number.
190
% A symbolic label is either the atom ’fail’, or the structure F/N, or the
% structure l(F/N,I), where F is an atom and N and I are natural numbers.
% r(cp) is stored in environments, allowing nested calls.
% r(tmp_cp) is not stored in environments, allowing only one level of call.
% However, no environment is needed in a predicate containing a simple_call.
% There are no explicit stacks for environments or choice points; registers
% r(e) and r(b) each contain a set of register values.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Accumulator declarations:
% Accumulators:
% Predicate declarations:
% Top level:
pred_info( execute, 0, [regs,heap,trail,code,lblmap,count]).
pred_info( instr_loop, 0, [regs,heap,trail,code,lblmap,count]).
pred_info( instr_loop_end, 1, [regs,heap,trail,code,lblmap,count]).
pred_info( instr, 1, [regs,heap,trail,code,lblmap]).
pred_info( numeric_pc, 2, [lblmap]).
% Addressing modes:
pred_info( heap, 3, [ heap]).
pred_info( reg, 3, [regs ]).
pred_info( perm, 3, [regs ]).
pred_info( a_var, 3, [regs ]).
pred_info( var_i, 3, [regs,heap]).
pred_info( arg, 2, [regs ]).
pred_info( arg_i, 2, [regs,heap]).
pred_info( numreg, 2, [regs ]).
pred_info( numarg, 2, [regs,heap]).
pred_info( var_off, 2, [regs,heap]).
pred_info( imm_tag, 2, [regs ]).
pred_info( ea_e, 2, [regs,heap]).
pred_info( ea_m, 2, [regs,heap]).
pred_info( ea_p, 2, [regs,heap]).
% Instruction utilities:
pred_info( deref_rtn, 2, [regs,heap,trail]).
pred_info( deref_rtn_cont, 3, [regs,heap,trail]).
pred_info( equal_rtn, 3, [regs,heap,trail]).
pred_info( switch_rtn, 5, [regs,heap,trail]).
pred_info( test_rtn, 4, [regs,heap,trail]).
pred_info( jump_cond_rtn, 4, [regs,heap,trail]).
pred_info( hash_lookup, 3, [regs,heap,trail,lblmap,code]).
191
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
:- dynamic(bamspec_option/1).
main :-
save(bamspec, 1),
prompt(_, ’’),
( copyright,
execute
; error([’Sorry, the executable BAM specification has failed.’])
),
halt.
main :-
halt.
copyright :-
write(’Berkeley Abstract Machine (BAM) Executable Specification’), nl,
write(’Copyright (C) 1990 Peter Van Roy and ’),
write(’Regents of the University of California’), nl, nl.
192
% Read in the instructions and create the code array and label map:
% The code array gives the instruction corresponding to each PC value.
% The label map gives the PC value corresponding to each symbolic label.
read_code(Code, LblMap) :-
read(Instr),
read_code(Instr, 0, Code, LblMap).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
execute :-
write(’Reading BAM code’), nl,
read_code(Code, LblMap),
write(’Starting execution’), nl,
execute(leaf, Regs, leaf, _, leaf, _, Code, _, LblMap, _, 0, N),
write(’Executed ’), write(N), write(’ instructions.’), nl,
print_array(Regs).
execute(File) :-
seeing(OldFile),
see(File),
read_code(Code, LblMap),
seen,
see(OldFile),
execute(leaf, Regs, leaf, _, leaf, _, Code, _, LblMap, _, 0, N),
write(’Executed ’), write(N), write(’ instructions.’), nl,
print_array(Regs).
193
execute -->>
[set(r(e),leaf)]:regs,
[set(r(b),leaf)]:regs,
[set(r(h),tvarˆ0)]:regs,
[set(r(tr),0)]:regs,
[set(r(pc),0)]:regs,
[set(r(cp),global_success/0)]:regs,
instr(choice(1/2,[],global_failure/0)),
instr_loop.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
arg_i(A, Y),
{lbl(L)},
equal_rtn(X, Y, L).
instr(unify(V,W,F,G,L)) -->>
var_i(get, V, X),
var_i(get, W, Y),
{nv_flag(F)},
{nv_flag(G)},
{lbl(L)},
unify_rtn(X, Y, L).
instr(unify_atomic(V,I,L)) -->>
var_i(get, V, X),
{an_atomic(I)},
{lbl(L)},
unify_rtn(X, I, L).
instr(trail(V)) -->>
var_i(get, V, X),
trail_rtn(X).
instr(move(EA,VI)) -->>
ea_m(EA, X),
var_i(set, VI, X), !.
instr(push(EA,R,N)) -->>
ea_p(EA, X),
{hreg(R)},
[get(R,Y)]:regs,
[set(Y,X)]:heap,
{pos(N)},
add_word(Y, N, YN),
[set(R,YN)]:regs.
instr(adda(R,S,T)) -->>
{hreg(R)},
[get(R,X)]:regs,
numreg(S, Off),
add_word(X, Off, NX),
{hreg(T)},
[set(T,NX)]:regs.
instr(pad(N)) -->>
[get(r(h),H)]:regs,
{pos(N)},
add_word(H, N, NewH),
[set(r(h),NewH)]:regs.
hash_indirect(T, X, Y),
[get(L,PC)]:lblmap,
hash_lookup(PC, Y, N).
instr(pair(_,_)) -->>
{error([’Attempt to execute inside a hash table.’])}.
% 3. Arithmetic instructions:
instr(add(A,B,V)) -->> arith(add, A, B, V).
instr(sub(A,B,V)) -->> arith(sub, A, B, V).
instr(mul(A,B,V)) -->> arith(mul, A, B, V).
instr(div(A,B,V)) -->> arith(div, A, B, V).
instr(mod(A,B,V)) -->> arith(mod, A, B, V).
instr(and(A,B,V)) -->> arith(and, A, B, V).
instr( or(A,B,V)) -->> arith( or, A, B, V).
instr(xor(A,B,V)) -->> arith(xor, A, B, V).
instr(not(A,V)) -->> arith(not, A, 0, V).
instr(sll(A,B,V)) -->> arith(sll, A, B, V).
instr(sra(A,B,V)) -->> arith(sra, A, B, V).
% 4. Procedural instructions:
instr(procedure(N/A)) -->> {atom(N), natural(A)}.
instr(call(N/A)) -->> {atom(N), natural(A)},
[get(r(pc),PC)]:regs,
[set(r(cp),PC)]:regs,
[set(r(pc),N/A)]:regs.
instr(return) -->>
[get(r(cp),PC)]:regs,
[set(r(pc),PC)]:regs.
instr(simple_call(N/A)) -->> {atom(N), natural(A)},
[get(r(pc),PC)]:regs,
[set(r(tmp_cp),PC)]:regs,
[set(r(pc),N/A)]:regs.
instr(simple_return) -->>
[get(r(tmp_cp),PC)]:regs,
[set(r(pc),PC)]:regs.
instr(label(L)) -->> {lbl(L)}.
instr(jump(L)) -->> {lbl(L)},
[set(r(pc),L)]:regs.
instr(allocate(N)) -->>
{natural(N)},
[get(r(e),E)]:regs,
{ins(NewE, r(e), E)},
[get(r(cp),CP)]:regs,
{ins(NewE, r(cp), CP)},
{seal(NewE)},
[set(r(e),NewE)]:regs.
instr(deallocate(N)) -->>
{natural(N)},
[get(r(e),E)]:regs,
{get(E,r(e),NewE)},
{get(E,r(cp),NewCP)},
[set(r(e),NewE)]:regs,
[set(r(cp),NewCP)]:regs.
instr(nop) -->> [].
197
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% A variable is a multiple of N.
% Inserted just before loads in readmode unification.
pragma(align(V,N)) :- a_var(V), pos(N).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
a_tag(tatm). /* atom */
a_tag(tint). /* integer */
a_tag(tneg). /* negative integer */
a_tag(tpos). /* nonnegative integer */
a_tag(tstr). /* structure */
a_tag(tlst). /* cons cell */
a_tag(tvar). /* variable */
atom_tag(tatm).
atomic_tag(tatm).
atomic_tag(tint).
atomic_tag(tneg).
atomic_tag(tpos).
pointer_tag(tstr).
pointer_tag(tlst).
pointer_tag(tvar).
198
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
ptr_word(Tˆ_) :- pointer_tag(T).
reg(r(I)) :- int(I), !.
hreg(R) :- reg(R), !.
hreg(r(h)).
perm(p(I)) :- natural(I).
a_var(Reg) :- reg(Reg), !.
a_var(Perm) :- perm(Perm).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
eq_ne(eq). /* Equal */
eq_ne(ne). /* Not equal */
hash_type(atomic).
hash_type(structure).
lbl(fail).
lbl(N/A) :- atom(N), natural(A).
lbl(l(N/A,I)) :- atom(N), natural(A), natural(I).
nv_flag(nonvar).
nv_flag(var).
nv_flag(’?’).
200
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Dereference utilities:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Equal routine:
equal_rtn(X, X, _) -->> !.
equal_rtn(_, _, L) -->> [set(r(pc),L)]:regs.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Arithmetic utilities:
arith(Op, A, B, V) -->>
numarg(A, XA), {extract_value(XA, VA), check_int(XA)},
numarg(B, XB), {extract_value(XB, VB), check_int(XB)},
arith_operation(Op, VA, VB, VC),
a_var(set, V, VC).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Conditional jump:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
hash_lookup(PC, X, N) -->>
{PC1 is PC+1},
[get(PC1,pragma(hash_length(N)))]:code,
{PC2 is PC1+1},
{PCN is PC1+N},
hash_lookup_2(PC2, PCN, X).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
trail_rtn(X) -->>
[get(r(hb),HB)]:regs,
cmp_trail(X, HB).
% Restore to unbound the variables on the trail between OldTR and CurTR.
detrail_rtn(CurTR, OldTR) -->> {CurTR=<OldTR}, !.
detrail_rtn(CurTR, OldTR) -->> {CurTR>OldTR},
{CurTR1 is CurTR-1},
[get(CurTR1,V)]:trail,
[set(V,V)]:heap,
detrail_rtn(CurTR1, OldTR).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
trail_rtn(tvarˆV1),
{make_word(NTag, V2, Word)},
[set(tvarˆV1,Word)]:heap.
unify_rtn_2(NTag, V2, tvar, V1, success) -->> {NTag\==tvar}, !,
trail_rtn(tvarˆV1),
{make_word(NTag, V2, Word)},
[set(tvarˆV1,Word)]:heap.
unify_rtn_2(tvar, V1, tvar, V2, success) -->> !,
unify_varvar(V1, V2).
% Matching atomic tags:
unify_rtn_2(ATag, V1, BTag, V2, Flag) -->>
{atomic_tag(ATag)},
{atomic_tag(BTag)},
{equivalent_tag(ATag, BTag)},
!,
unify_atm(V1, V2, Flag).
% Non-matching nonvariable tags:
unify_rtn_2(ATag, _, BTag, _, fail) -->>
{ATag\==tvar, BTag\==tvar},
{\+equivalent_tag(ATag, BTag)},
!.
% Matching pointer tags (recursive case):
unify_rtn_2(ATag, V1, ATag, V2, Flag) -->>
{pointer_tag(ATag)},
get_size(ATag, V1, Sz),
unify_rtn_args_2(0, Sz, ATag, V1, V2, Flag).
% The term’s Size is the maximum offset needed to traverse the term in memory.
get_size(tlst, _, 1) -->> [].
get_size(tstr, V, N) -->>
[get(tstrˆV,Func)]:heap,
{Func=(tatmˆ(_/N))}.
trail_rtn(tvarˆV1),
[set(tvarˆV1,tvarˆV2)]:heap.
unify_varvar(V1, V2) -->> {V1=<V2}, !,
trail_rtn(tvarˆV2),
[set(tvarˆV2,tvarˆV1)]:heap.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
ground(N, _) :- N=:=0, !.
ground(N, X) :- N=\=0, arg(N, X, A), ground(A), N1 is N-1, ground(N1, X).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This takes into account the relationship between tpos, tneg and tint.
% For integers it extracts tpos or tneg tags and the absolute value
% of the integer. It creates the correct integer, given the tpos, tneg
% or tint tags.
equivalent_tag(T, T) :- !.
equivalent_tag(tint, tpos) :- !.
equivalent_tag(tint, tneg).
extract_tag_value(W, T, V) :-
extract_tag(W, T),
extract_value(W, V).
nonvartag(I) :- int(I), !.
nonvartag(Tˆ_) :- \+T=tvar.
make_word(tint, I, I) :- !.
make_word(tpos, I, I) :- !.
make_word(tneg, N, I) :- !, I is -N.
make_word(T, V, TˆV).
205
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Table utilities:
ins_2(N, V, _, _, I, V) :- I=N, !.
ins_2(N, _, L, R, I, V) :-
compare(Order, I, N),
ins_2(Order, I, V, L, R).
get_2(node(N,W,L,R), I, V) :-
compare(Order, I, N),
get_3(Order, I, V, W, L, R).
set_2(leaf, I, V, node(I,V,leaf,leaf)).
set_2(node(N,W,L,R), I, V, node(N,NW,NL,NR)) :-
compare(Order, I, N),
set_3(Order, I, V, W, L, R, NW, NL, NR).
seal(leaf).
seal(node(_,_,L,R)) :- seal(L), seal(R).
print_list([]).
print_list([(A->B)|L]) :-
write(A), put(9), write(’= ’), write(B), nl,
print_list(L).
flat_array(Term, N, Sort) :-
N>0, N1 is N-1,
flat_array(Term, N1, Flat, []), !,
sort(Flat, Sort).
flat_array(leaf, N, []) :- N=:=0, !.
flat_array(node(_,_,_,_), N, ’...’) :- N=:=0, !.
flat_array(Term, _, Term).
hash_2(0, _, _) :- !.
hash_2(N, T, H) :- N>0,
arg(N, T, X),
arg(N, H, Y),
hash(X, Y),
N1 is N-1,
hash_2(N1, T, H).
bit_invert(0, 0, _) :- !.
bit_invert(N, I, B) :- N>0,
L is N>>1,
R is N/\1,
B1 is B-1,
bit_invert(L, LI, B1),
I is R*(1<<B) + LI.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
207
% Error handling:
error(L) :-
write(’*** Error: ’),
error_loop(L),
write(’ ***’), nl.
error_loop([]).
error_loop([M|L]) :- write(M), error_loop(L).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
write_rtn -->>
[get(r(0),X)]:regs,
write_rtn(X).
write_arg(V, I) -->>
{W is V+I},
[get(tstrˆW,X)]:heap,
deref_rtn(X, DX),
write_rtn(DX).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
208
209
Appendix D
1. Introduction
This appendix gives an English-language description of the semantics of the Berkeley Abstract
Machine (BAM) as comments attached to a Prolog specification of its syntax. The BAM is intended to
operate on the same data structures as the Warren Abstract Machine (WAM), therefore some familiarity
with the WAM is an advantage. The semantics are represented by short descriptions supplemented by
pseudo-code and examples where necessary.
The BAM is designed to be simple and easily translated to most general-purpose processors. Many
of its optimizations apply to any processor, for example the streamlined choice point management and the
use of write-once permanent variables to simplify trailing. Although the first target is the VLSI-BAM pro-
cessor, we have built translators for other processors including the MIPS and the MC68020. Pragmas give
information that is used to obtain the best translation for different processors.
The instruction set is divided in six categories, each in a different section. Each section starts with a
box giving the syntax of the instructions presented in that section. This is followed by a description of the
instructions’ actions. Section 2 gives the unification instructions. Section 3 gives the conditional control
flow instructions. Section 4 gives the arithmetic instructions. Section 5 gives the procedural control flow
instructions. Section 6 gives the pragmas, which contain information that allows better translation. Section
7 gives the user instructions, additions to the BAM that are never output by the compiler but are intended
for the BAM assembly programmer. The last section defines the syntax and semantics of the addressing
modes used in the instructions.
In explaining the semantics, a few assumptions are made about the data representation. An infinite
number of registers is assumed; the translator should map registers of sufficiently large index to memory.
A tagged architecture is assumed; i.e. each word contains a tag and a value field which are treated as
separate entities in some instructions and as a unit in other instructions. A load-store architecture is
assumed; almost any architecture has a subset of instructions that satisfy this assumption. The actual
details of the translation to the target architecture are not given since they depend on the characteristics of
the architecture. These characteristics include the number of registers, the addressing modes, hardware
support for certain features (tagging, dereferencing, trailing, etc.), the precise format of choice points and
environments, and so forth.
210
2. Unification instructions
Unification syntax
instr(deref(V,W)) :- var_i(V), var_i(W).
instr(equal(EA,A,L)) :- ea_e(EA), arg_i(A), lbl(L).
instr(unify(V,W,F,G,L)) :- var_i(V),var_i(W),nv_flag(F),nv_flag(G),lbl(L).
instr(trail(V)) :- var_i(V).
instr(move(EA,VI)) :- ea_m(EA), var_i(VI).
instr(push(EA,R,N)) :- ea_p(EA), hreg(R), pos(N).
instr(adda(R,S,T)) :- numreg(R), numreg(S), hreg(T).
instr(pad(N)) :- pos(N).
instr(unify_atomic(V,I,L)):- var_i(V), an_atomic(I), lbl(L).
instr(fail).
deref(V,W) Dereference the argument V and store the result in W. The argument
V is unchanged. This is the only instruction which dereferences its
argument. All other instructions assume that their arguments are
dereferenced. Giving the dereference instruction two arguments
simplifies the implementation of write-once permanent variables and
makes a fast implementation of trailing possible.
equal(X,Y,L) Compare X to Y and branch to L if they are not equal. The comparison
is a full word operation, equivalent to ‘‘eq’’ in Lisp. It is assumed that
X and Y are dereferenced.
unify(X,Y,T,U,L) Perform a general unification of X and Y, and branch to L if it fails.
Always binds oldest variables to the youngest. In the failure case all
bindings are undone. It is assumed that X and Y are dereferenced. The
two parameters T and U are added as an optimization, and may be
safely ignored. They are flags (with values ’?’, var, or nonvar)
that say whether it is known if X and Y are variables or nonvariables.
With this information a better translation to the target processor can be
done.
trail(X) Push the address of X on the trail stack if the trail condition X<r(hb)
is satisfied. It is assumed that X is a dereferenced unbound variable,
i.e. it has a tvar tag. Only one comparison is necessary for the trail
check. The state register r(hb) points to the heap location which
was the top of the heap when the most recent choice point was created.
move(X,Y) Move X to Y. Depending on the addressing mode, this instruction does
a load or store or creates a tagged value.
push(X,R,N) Push X on the stack with stack pointer R, then increment R by N. This
instruction is used for write mode unification.
adda(X,Y,R) Add X and Y into R. This is a full word operation which never traps,
unlike the arithmetic instructions in section 4. This instruction is used
to allocate space for uninitialized variables. The second argument Y is
an offset which is scaled properly by the translator (i.e. it is unchanged
for the VLSI-BAM since it is word-addressed, and it is multiplied by 4
for the MIPS, since it is byte-addressed).
211
pad(N) Add N words to the heap pointer r(h). This is a full word operation
which never traps, unlike the arithmetic instructions in section 4. It is
used to ensure the correct alignment of compound terms. The space
reserved by pad will never be stored to. If the increment is a multiple
of the alignment then the pad disappears. The increment is scaled
properly by the translator (see previous description of adda).
unify_atomic(X,Y,L) Unify the variable X with the atomic term Y, and branch to L if it fails.
It is assumed that X is dereferenced. The unify_atomic instruc-
tion is a special case of general unification that is added to reduce code
size in the VLSI-BAM processor. There is a compiler option to enable
or disable the generation of this instruction.
fail Untrail all variable bindings and jump to the retry address. Do not
restore argument registers. Argument registers are restored by the
choice point management instructions.
4. Arithmetic instructions
Arithmetic syntax
instr(add(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
instr(sub(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
instr(mul(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
instr(div(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
instr(and(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
instr( or(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
instr(xor(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
instr(not(A,V)) :- numarg_i(A), a_var(V).
instr(sll(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
instr(sra(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
All arithmetic instructions assume that their operands are dereferenced and destructively overwrite
the result register. All perform operations on integers with correct tag and return a result with correct tag,
trapping if either operand or the result is not a integer. Arithmetic semantics are:
add(X,Y,Z) Z ← X+Y
sub(X,Y,Z) Z ← X-Y
mul(X,Y,Z) Z ← X*Y
div(X,Y,Z) Z ← X/Y
and(X,Y,Z) Z ← X and Y (bitwise and)
or(X,Y,Z) Z ← X or Y (bitwise or)
xor(X,Y,Z) Z ← X xor Y (bitwise exclusive or)
sll(X,Y,Z) Z ← X << Y (logical shift of X left Y places)
sra(X,Y,Z) Z ← X >> Y (arithmetic shift of X right Y places)
not(X,Z) Z ← not X (bitwise invert X into Z)
simple_call(N/A) Simple call of the procedure N/A, assuming the same argument pass-
ing as call(N/A). This is a one-level call; it may not be nested. It
does not require a surrounding allocate-deallocate pair. It can be
implemented by saving the return address in a fixed register. This
instruction is useful for interfacing with assembly routines.
simple_return Return from a simple call.
label(L) Denotes a branch destination. The label fail is not an address, but
denotes a branch to the global failure routine.
jump(L) Jump unconditionally to label L. The label may be to the first instruc-
tion of another procedure N/A or it may be internal to the current pro-
cedure. The label fail is not an address, but denotes a branch to the
global failure routine.
allocate(N) Create an environment of size N on the local stack, i.e. a new set of N
permanent variables which are denoted by p(I). Typically, the only
state registers stored in the environment are r(e) and r(cp). The
environment must NOT contain the r(b) register.
deallocate(N) Remove the top-most environment (which is of size N) from the local
stack.
6. Pragmas
Pragma syntax
instr(pragma(Pragma)) :- pragma(Pragma).
pragma(align(V,N)) :- a_var(V), pos(N).
pragma(push(term(Size))) :- pos(Size).
pragma(push(cons)).
pragma(push(structure(A))) :- pos(A).
pragma(push(variable)).
pragma(tag(V,T)) :- a_var(V), a_tag(T).
pragma(hash_length(Len)) :- pos(Len).
tag(V,T) The contents of variable V have tag T. This pragma precedes a load or
a store with address V. It is used to make loads and stores efficient for
processors which do not have explicit tag support.
7. User instructions
This section describes the parts of the BAM language that are never output by the compiler, but only
used by the BAM assembly programmer. This is used to write the run-time system in BAM code, so that it
is as portable as possible. Additional instructions are jump to register address, creating and decomposing
tagged words, non-trapping full-word arithmetic, non-trapping full-word unsigned comparison, and trailing
for backtrackable destructive assignment. Additional registers are used in implementing the run-time sys-
tem, and can be mapped to memory locations.
Additional instructions
instr(I) :- user_instr(I).
user_instr(jump_reg(R)) :- reg(R).
user_instr(jump_nt(C,A,B,L)):- cond(C),numarg_i(A),numarg_i(B),lbl(L).
user_instr(ord(A,B)) :- arg(A), a_var(B).
user_instr(val(T,A,V)) :- a_tag(T), numarg_i(A), a_var(V).
user_instr(add_nt(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
user_instr(sub_nt(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
user_instr(and_nt(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
user_instr( or_nt(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
user_instr(xor_nt(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
user_instr(not_nt(A,V)) :- numarg_i(A), a_var(V).
user_instr(sll_nt(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
user_instr(sra_nt(A,B,V)) :- numarg_i(A), numarg_i(B), a_var(V).
user_instr(trail_bda(X)) :- a_var(X).
user_reg(r(A)) :- atom(A).
8. Instruction arguments
This section defines the syntax of the instructions’ arguments.
Addressing modes for equal, move and push
% Effective address for equal:
ea_e(Var) :- a_var(Var).
ea_e(VarOff) :- var_off(VarOff).
% Effective address for move:
ea_m(Arg) :- arg(Arg).
ea_m(VarOff) :- var_off(VarOff).
ea_m(TagˆH) :- pointer_tag(Tag), heap_ptr(H).
% Effective address for push:
ea_p(Arg) :- arg_i(Arg).
ea_p(TagˆH) :- pointer_tag(Tag), heap_ptr(H).
ea_p(Tagˆ(H+D)) :- pointer_tag(Tag), pos(D), heap_ptr(H).
217
Tag syntax
a_tag(tatm). /* atom */
a_tag(tint). /* integer */
a_tag(tneg). /* negative integer */
a_tag(tpos). /* nonnegative integer */
a_tag(tstr). /* structure */
a_tag(tlst). /* cons cell */
a_tag(tvar). /* variable */
atom_tag(tatm).
pointer_tag(tstr).
pointer_tag(tlst).
pointer_tag(tvar).
218
Conditionals syntax
eq_ne(eq).
eq_ne(ne).
cond(eq). /* Equal */
cond(ne). /* Not equal */
cond(lts). /* Signed less than */
cond(les). /* Signed less than or equal */
cond(gts). /* Signed greater than */
cond(ges). /* Signed greater than or equal */
Miscellaneous syntax
hash_type(atomic).
hash_type(structure).
lbl(fail).
lbl(N/A) :- atom(N), natural(A).
lbl(l(N/A,I)) :- atom(N), natural(A), int(I).
nv_flag(nonvar).
nv_flag(var).
nv_flag(’?’).
% A list of register numbers:
% (May contain the value ’no’ as well)
regs([]).
regs([R|Set]) :- (int(R); R=no), regs(Set).
Utility predicates
ground(X) :- nonvar(X), functor(X, _, N), ground(N, X).
ground(N, _) :- N=:=0.
ground(N, X) :- N=\=0, arg(N,X,A), ground(A), N1 is N-1, ground(N1,X).
int(N) :- integer(N).
natural(N) :- integer(N), N>=0.
pos(N) :- integer(N), N>0.
219
Appendix E
1. Introduction
This appendix describes a preprocessor that simplifies purely applicative programming in Prolog.
The preprocessor generalizes Prolog’s Definite Clause Grammar (DCG) notation to allow programming
with multiple accumulators. It has been an indispensable tool in the development of the Aquarius Prolog
compiler. Its use is transparent in versions of Prolog that conform to the Edinburgh standard. The prepro-
cessor and a user manual are available by anonymous ftp to arpa.berkeley.edu.
It is desirable to program in a purely applicative style, i.e. within the pure logical subset of Prolog.
In that case a predicate’s meaning depends only on its definition, and not on any outside information. This
has two important advantages. First, it greatly simplifies verifying correctness. Simple inspection is often
sufficient. Second, since all information is passed locally, it makes the program more amenable to parallel
execution. However, in practice the number of arguments of predicates written in this style is large, which
makes writing and maintaining them difficult. Two ways of getting around this problem are (1) to encapsu-
late information in compound structures which are passed in single arguments, and (2) to use global instead
of local information. Both of these techniques are commonly used in imperative languages such as C, but
neither is a satisfying way to program in Prolog, for the following reasons:
Because Prolog is a single-assignment language, modifying encapsulated information requires a
time-consuming copy of the entire structure. Sophisticated optimizations could make this efficient,
but compilers implementing them do not yet exist.
Using global information destroys the advantages of programming in an applicative style, such as the
ease of mathematical analysis and the suitability for parallel execution.
A third approach with neither of the above disadvantages is extending Prolog to allow an arbitrary number
of arguments without increasing the size of the source code. The extended Prolog is translated into stan-
dard Prolog by a preprocessor. This report describes an extension to Prolog’s Definite Clause Grammar
notation that implements this idea.
Each predicate is given two additional arguments. Chaining together these arguments implements the
accumulator.
220
4. An example
To illustrate the extended notation, consider the following Prolog predicate which converts infix
expressions containing identifiers, integers, and addition (+) into machine code for a simple stack machine,
and also calculates the size of the code:
expr_code(A+B, S1, S4, C1, C4) :-
expr_code(A, S1, S2, C1, C2),
expr_code(B, S2, S3, C2, C3),
C3=[plus|C4], /* Explicitly accumulate ’plus’ */
S4 is S3+1. /* Explicitly add 1 to the size */
expr_code(I, S1, S2, C1, C2) :-
atomic(I),
C1=[push(I)|C2],
S2 is S1+1.
This predicate has two accumulators: the machine code and its size. A sample call is
expr_code(a+3+b,0,Size,Code,[]), which returns the result:
Size = 5
Code = [push(a),push(3),plus,push(b),plus]
With DCG notation it is possible to hide the code accumulator, although the size is still calculated expli-
citly:
expr_code(A+B, S1, S4) -->
expr_code(A, S1, S2),
expr_code(B, S2, S3),
[plus], /* Accumulate ’plus’ in a hidden accumulator */
{S4 is S3+1}. /* Explicitly add 1 to the size */
expr_code(I, S1, S2) -->
{atomic(I)},
[push(I)],
{S2 is S1+1}.
expr_code(A+B) -->>
expr_code(A),
expr_code(B),
[plus]:code, /* Accumulate ’plus’ in the code accumulator */
[1]:size. /* Accumulate 1 in the size accumulator */
expr_code(I) -->>
{atomic(I)},
[push(I)]:code,
[1]:size.
The translation of this version is identical to the original definition. The preprocessor needs the following
declarations:
acc_info(code, T, Out, In, (Out=[T|In]))./* Accumulator declarations */
acc_info(size, T, In, Out, (Out is In+T)).
For each accumulator this declares the accumulating function, and for each predicate this declares the
name, arity (number of arguments), and accumulators it uses. The order of the In and Out arguments
determines whether accumulation proceeds in the forward direction (see size) or in the reverse direction
(see code). Choosing the proper direction is important if the accumulating function requires some of its
arguments to be instantiated.
5. Concluding remarks
An extension to Prolog’s DCG notation that implements an unlimited number of named accumula-
tors was developed to simplify purely applicative Prolog programming. Comments and suggestions for
improvements are welcome.
6. References
[Abramson 1984]
H. Abramson, ‘‘Definite Clause Translation Grammars,’’ Proc. 1984 International Symposium on
Logic Programming, 1984, pp 233-240.
[Clocksin & Mellish 1981]
W.F. Clocksin and C.S. Mellish, ‘‘Programming in Prolog,’’ Springer-Verlag, 1981.
[O’Keefe 1988]
R. A. O’Keefe, ‘‘Practical Prolog for Real Programmers,’’ Tutorial 8, Fifth International Conference
Symposium on Logic Programming, Aug. 1988.
[Pereira 1981]
F. Pereira, ‘‘Extraposition Grammars,’’ American Journal of Computational Linguistics, 1981, vol.
7, no. 4, pp 243-255.
[Pereira & Shieber 1981]
F. Pereira and S. Shieber, ‘‘Prolog and Natural-Language Analysis,’’ CSLI Lecture Notes 10, 1987.
[Pereira & Warren 1980]
F. Pereira and D.H.D. Warren, ‘‘Definite Clause Grammars for Language Analysis—A Survey of the
Formalism and a Comparison with Augmented Transition Networks,’’ Journal of Artificial Intelli-
gence, 1980, vol. 13, no. 3, pp 231-278.
[Sterling & Shapiro 1986]
L. Sterling and E. Shapiro, ‘‘The Art of Prolog,’’ MIT Press, 1986.
222
User Manual
1. Introduction
This manual describes a preprocessor for Prolog that adds an arbitrary number of arguments to a
predicate without increasing the size of the source code. The hidden arguments are of two kinds:
(1) Accumulators, useful for results that are calculated incrementally in many predicates. An accumula-
tor expands into two additional arguments per predicate.
(2) Passed arguments, used to pass global information to many predicates. A passed argument expands
into a single additional argument per predicate.
The preprocessor has been tested under C-Prolog and Quintus Prolog. It is being used by the author in pro-
gram development, and is believed to be relatively bug-free. However, it is still being refined and
extended. The most recent version is available by anonymous ftp to arpa.berkeley.edu or by contacting the
author. Please let me know if you find any bugs. Comments and suggestions for improvements are wel-
come.
% The program:
p(X) -->> Y is X+1, q(Y), r(Y).
This example declares one accumulator, one passed argument, and three predicates using them. The pro-
gram consists of a single clause. The preprocessor is used as follows: (bold-face denotes user input)
% cprolog
C-Prolog version 1.5
| ?- [’accumulator.pl’].
accumulator.pl consulted 9780 bytes 1.7 sec.
yes
| ?- [’example.pl’].
example.pl consulted 668 bytes 0.25 sec.
yes
| ?-
Now the predicate p(X) has been expanded. We can see what it looks like with the listing com-
mand:
| ?- listing(p).
p(X, S1, S3, P) :- Y is X+1, q(Y, S1, S2, P), r(Y, S2, S3, P).
(Variable names have been changed for clarity.) The arguments S1, S2, and S3, which implement the
224
accumulator castor, are chained together. The argument P implements the passed argument. It is
added as an extra argument to each predicate.
In object-oriented terminology the declarations of hidden parameters correspond to classes with a
single method defined for each. Declarations of predicates specify the inheritance of the predicate from
multiple classes, namely each hidden parameter.
3. Declarations
The predicate Name/Arity has the hidden parameters given in List. The parameters are added in the
order given by List and their names must be atoms.
In most cases the short form gives sufficient information. It declares the accumulator Acc, which must be
an atom, along with the accumulating function, Joiner, and its arguments Term, the term to be accu-
mulated, and Left & Right, the variables used in chaining.
The long form of acc_info is useful in more complex programs. It contains two additional argu-
ments, LStart and RStart, that are used to give default starting values for an accumulator occurring
in a body goal that does not occur in the head. The starting values are given to the unused accumulator to
ensure that it will execute correctly even though its value is not used. Care is needed to give correct values
for LStart and RStart. For DCG-like list accumulation both may remain unbound.
Two conventions are used for the two variables used in chaining depending on which direction the
accumulation is done. For forward accumulation, Left is the input and Right is the output. For
reverse accumulation, Right is the input and Left is the output.
To see how these declarations work, consider the following program:
% Example illustrating the difference between
% forward and reverse accumulation:
This defines two accumulators fwd and rev that both accumulate lists, but in different directions. The
225
joiner of both accumulators is the unification Out=[T|In], which adds T to the head of the list In
and creates the list Out. In accumulator fwd the output Out is the left argument and the input In is
the right argument. This builds the list in ascending order. Switching the arguments, as in the accumulator
rev, builds the list in reverse. A sample execution gives these results:
| ?- flist(10, [], List).
List = [1,2,3,4,5,6,7,8,9,10]
yes
| ?- rlist(10, List, []).
List = [10,9,8,7,6,5,4,3,2,1]
yes
| ?-
If the joining function is not reversible then the accumulator can only be used in one direction. For exam-
ple, the accumulator add with declaration:
acc_info(add, I, In, Out, Out is I+In).
It can only be used as a forward accumulator. Attempting to use it in reverse results in an error because the
argument In of the joiner is uninstantiated. The reason for this is that the predicate is/2 is not pure
logic: it requires the expression in its right-hand side to be ground.
In most cases the short form is sufficient. It declares a passed argument Pass, that must be an atom. The
long form also contains the starting value PStart that is used to give a default value for a passed argu-
ment in a body goal that does not occur in the head. Most of the time this situation does not occur.
gives all predicates the hidden parameters in List. This keeps programming simple regardless of the
number of hidden parameters.
226
227
Appendix F
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#include <stdio.h>
int tak(x,y,z)
int x, y, z;
{
int a1, a2, a3;
if (x <= y) return z;
a1 = tak(x-1,y,z);
a2 = tak(y-1,z,x);
a3 = tak(z-1,x,y);
return tak(a1,a2,a3);
}
main()
{
printf("%d\n", tak(24, 16, 8));
}
-------------------------------------------------------------------------------
tak(X,Y,Z,A) :- X =< Y, Z = A.
tak(X,Y,Z,A) :- X > Y,
X1 is X - 1, tak(X1,Y,Z,A1),
Y1 is Y - 1, tak(Y1,Z,X,A2),
Z1 is Z - 1, tak(Z1,X,Y,A3),
tak(A1,A2,A3,A).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#include <stdio.h>
int fib(x)
int x;
{
if (x <= 1) return 1;
return (fib(x-1)+fib(x-2));
}
228
main()
{
printf("%d\n", fib(30));
}
-------------------------------------------------------------------------------
fib(N,F) :- N =< 1, F = 1.
fib(N,F) :- N > 1,
N1 is N - 1, fib(N1,F1),
N2 is N - 2, fib(N2,F2),
F is F1 + F2.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#include <stdio.h>
han(n,a,b,c)
{
int n1;
if (n<=0) return;
n1 = n-1;
han(n1,a,c,b);
han(n1,c,b,a);
}
main()
{
han(20,1,2,3);
}
-------------------------------------------------------------------------------
main :- han(20,1,2,3).
han(N,_,_,_) :- N=<0.
han(N,A,B,C) :- N>0,
N1 is N - 1,
han(N1,A,C,B),
han(N1,C,B,A).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#include <stdio.h>
int list[50];
qsort(l, r)
int l, r;
{
int v, t, i, j;
if (l<r) {
v=list[l]; i=l; j=r+1;
do {
do i++; while (list[i]<v);
do j--; while (list[j]>v);
t=list[j]; list[j]=list[i]; list[i]=t;
} while (j>i);
list[i]=list[j]; list[j]=list[l]; list[l]=t;
qsort(l,j-1);
qsort(j+1,r);
}
}
main()
{
int i, j;
-------------------------------------------------------------------------------
range(L,L,H).
range(L,I,H) :- L<H, L1 is L+1, range(L1,I,H).
qsort(S) :- qsort([27,74,17,33,94,18,46,83,65, 2,
32,53,28,85,99,47,28,82, 6,11,
55,29,39,81,90,37,10, 0,66,51,
7,21,85,27,31,63,75, 4,95,99,
230
11,28,61,74,18,92,40,53,59, 8],S,[]).
qsort([X|L],R,R0) :-
partition(L,X,L1,L2),
qsort(L2,R1,R0),
qsort(L1,R,[X|R1]).
qsort([],R,R).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
231
Appendix G
accumulator.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California.
% All rights reserved. This program may be freely used and modified for
% non-commercial purposes provided this copyright notice is kept unchanged.
% Written by Peter Van Roy
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Comments, suggestions, flames, manifestos, and bug reports are most welcome.
% Please send to:
% Peter Van Roy
% 508-10 Evans Hall
% University of California
% Berkeley, CA 94720
% E-mail: [email protected]
term_expansion((H-->>B), (TH:-FTB)) :-
functor(H, Na, Ar),
’_has_hidden’(H, HList),
’_new_goal’(H, HList, HArity, TH),
’_create_acc_pass’(HList, HArity, TH, Acc, Pass),
’_flat_conj’(B, FB),
’_expand_body’(FB, TB, Na/Ar, HList, Acc, Pass),
’_flat_conj’(TB, FTB), !.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Use the Acc and Pass data structures to create the arguments of a body goal:
% Add the hidden parameters named in GList to the goal.
’_use_acc_pass’([], _, _, Acc, Acc, _).
% 1a. The accumulator A is used in the head:
’_use_acc_pass’([A|GList], Index, TGoal, Acc, NewAcc, Pass) :-
’_replace_acc’(A, LeftA, RightA, MidA, RightA, Acc, MidAcc), !,
Index1 is Index+1,
arg(Index1, TGoal, LeftA),
Index2 is Index+2,
arg(Index2, TGoal, MidA),
’_use_acc_pass’(GList, Index2, TGoal, MidAcc, NewAcc, Pass).
% 1b. The accumulator A is not used in the head:
’_use_acc_pass’([A|GList], Index, TGoal, Acc, NewAcc, Pass) :-
’_acc_info’(A, LStart, RStart), !,
Index1 is Index+1,
arg(Index1, TGoal, LStart),
Index2 is Index+2,
arg(Index2, TGoal, RStart),
’_use_acc_pass’(GList, Index2, TGoal, Acc, NewAcc, Pass).
% 2a. The passed argument A is used in the head:
’_use_acc_pass’([A|GList], Index, TGoal, Acc, NewAcc, Pass) :-
’_is_pass’(A),
’_member’(pass(A,Arg), Pass), !,
Index1 is Index+1,
arg(Index1, TGoal, Arg),
’_use_acc_pass’(GList, Index1, TGoal, Acc, NewAcc, Pass).
% 2b. The passed argument A is not used in the head:
’_use_acc_pass’([A|GList], Index, TGoal, Acc, NewAcc, Pass) :-
’_pass_info’(A, AStart), !,
Index1 is Index+1,
arg(Index1, TGoal, AStart),
’_use_acc_pass’(GList, Index1, TGoal, Acc, NewAcc, Pass).
% 3. Defaulty case when A does not exist:
’_use_acc_pass’([A|GList], Index, TGoal, Acc, Acc, Pass) :-
write(’*** Error: the hidden parameter ’),write(A),
write(’ does not exist.’),nl.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Specialized utilities:
% Succeeds if A is an accumulator:
’_is_acc’(A) :- atomic(A), !, ’_acc_info’(A, _, _, _, _, _, _).
’_is_acc’(A) :- functor(A, N, 2), !, ’_acc_info’(N, _, _, _, _, _, _).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Generic utilities:
arg(L, Q, A),
L1 is L+1,
’_match’(L1, H, P, Q).
’_flat_conj’(true, X, X).
’_flat_conj’((A,B), X1, X3) :-
’_flat_conj’(A, X1, X2),
’_flat_conj’(B, X2, X3).
’_flat_conj’(G, (G,X), X) :-
\+G=true,
\+G=(_,_).
’_member’(X, [X|_]).
’_member’(X, [_|L]) :- ’_member’(X, L).
’_append’([], L, L).
’_append’([X|L1], L2, [X|L3]) :- ’_append’(L1, L2, L3).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
238
accumulator_cleanup.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
:- abolish([pass_info/1,pass_info/2,acc_info/5,acc_info/7,pred_info/3]).
:- multifile pass_info/1.
:- multifile pass_info/2.
:- multifile acc_info/5.
:- multifile acc_info/7.
:- multifile pred_info/3.
pass_info(’_dummy’).
pass_info(’_dummy’, _).
acc_info(’_dummy’, _, _, _, _).
acc_info(’_dummy’, _, _, _, _, _, _).
pred_info(’_dummy’, 0, _, _, _).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
239
analyze.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
% This module does a flow analysis, deriving ’uninit’, ’ground’, ’nonvar’, and
% ’rderef’ modes by doing an abstract interpretation of the source code.
% It traverses the call tree and keeps track of entry and exit modes.
% It iterates and propagates modes until the least fixed point is reached.
% The module is a transformation pass in the Aquarius compiler.
% any
% / \
% nonvar rderef
% / \ / \
% ground non+drf uninit
% \ / /
% gnd+drf /
% \ /
% unknown
% The exit mode lattice is identical (there is no ’uninit’ element for non-unify
% goals). The rderef lattice element corresponds to the mode rderef(X) which
% means that all subterms of X are dereferenced. It propagates just like
% ground(X). It is useful to reduce the large amount of dereferencing which is
% going on (usually between 10% and 20% of the execution time with analyzer 2!).
% These simple lattices are easily implemented for four reasons: (1) neither
% uninits nor grounds are affected by aliasing, and (2) uninit, grounds, and
% rderefs all propagate indefinitely into explicit unifications with compound
% terms, (3) nonvars and grounds, once true, do not become false again, and
% (4) there is no explicit representation for aliasing--modes are calculated
% only for unaliased variables (e.g. uninit and rderef) and for cases where
% aliasing has no effect (e.g. ground and nonvar).
% Notes:
% 1. A set of entry points must exist to do the analysis.
% 2. In each pass, this algorithm traverses the call tree starting from those
% predicates whose entry modes have changed or which contain a goal whose
% exit modes have changed. The algorithm differs from O’Keefe’s in the
% data structures used.
% 3. Only modes "generated" within the strees given are propagated. Except for
% the entry modes, all modes "already existing" are ignored. This allows
% the compiler to correct programmer’s errors.
% 4. Predicates of arity zero are always used as entry points in the analysis.
% 5. To get better results for exit modes, this algorithm back-propagates
% the results at the end of the clause to the beginning, through the
% unifications in the clause. For example, part1(A,B) :- A=[X|L], B=[X|L1],
% part2(L,L1). Let A be ground at entry. If L1 returns ground from part2
240
% For later:
% 1. Back propagate univ, i.e. in A=..[X|Y], if X & Y are ground then A is too.
% Can use existing unify(_,_,_) data structure. Generalize?
% 2. The mode rlist(X) (recursive list) propagates similarly to ground(X) also.
% It should be added to the exit mode analyzer too.
% 3. This version does not use cuts to get better modes.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Accumulator declarations:
pass_info(pred).
pass_info(dup).
% Predicate declarations:
% *** Initialization:
pred_info( init_strees, 1, [entry,exit,defs,occurs]).
pred_info( init_disj, 1, [entry,exit,defs,occurs,pred]).
pred_info( init_conj, 1, [entry,exit,defs,occurs,pred]).
pred_info( init_goal, 1, [entry,exit,defs,occurs,pred]).
pred_info( entry_init, 1, [entry]).
pred_info( entry_init_one, 1, [entry]).
% *** Top level analysis:
pred_info(analyze_closure, 1, [entry,exit,defs,spec,occurs]).
pred_info( trav_preds, 1, [entry,exit,defs,spec,change]).
% *** Traversal of call tree:
pred_info( trav_pred, 4,
[entry,exit,defs,spec,change,gnd,nonvar,uninit,rderef,sf]).
pred_info( trav_disj, 8,
[entry,exit,defs,spec,change,gnd,nonvar,uninit,rderef,sf]).
pred_info( trav_conj, 5,
[entry,exit,defs,spec,change,gnd,nonvar,uninit,rderef,sf,uni,dup]).
241
pred_info( trav_goal, 6,
[entry,exit,defs,spec,change,gnd,nonvar,uninit,rderef,sf,uni,dup]).
pred_info( trav_call, 6,
[entry,exit,defs,spec,change,gnd,nonvar,uninit,rderef,sf]).
pred_info( trav_def, 7,
[entry,exit,defs,spec,change,gnd,nonvar,uninit,rderef,sf]).
% *** Update of entry & exit mode tables:
pred_info( update_entry, 3, [entry, change,gnd,nonvar,uninit,rderef,sf]).
pred_info( update_entry, 4, [entry, change]).
pred_info( update_exit, 1, [exit, change,gnd,nonvar,rderef,sf]).
pred_info( update_exit, 3, [exit, change]).
% *** Utilities:
pred_info( trav_goal_n, 2, [nonvar]).
pred_info( trav_non, 2, [nonvar]).
pred_info( trav_goal_u, 3, [gnd,uninit,rderef,sf]).
pred_info( trav_unif, 4, [gnd,uninit,rderef,sf]).
pred_info( trav_goal_d, 4, [gnd,uninit,rderef,sf]).
pred_info( trav_drf1, 4, [gnd,uninit,rderef,sf]).
pred_info( trav_drf2, 5, [gnd,uninit,rderef,sf]).
pred_info( new_dref, 2, [ uninit,rderef,sf]).
pred_info( add_dref, 1, [ uninit,rderef,sf]).
pred_info( new_drf_gnd, 4, [gnd,nonvar,uninit,rderef,sf]).
pred_info( new_drf_set, 2, [gnd, uninit,rderef,sf]).
pred_info( calc_exit, 2, [gnd,nonvar,rderef]).
pred_info( calc_entry, 2, [gnd,nonvar,uninit,rderef,sf]).
pred_info( back_propagate, 1, [gnd,nonvar,rderef]).
pred_info( back_prop_g_cl, 1, [gnd]).
pred_info( back_prop_g_cl, 2, [gnd]).
pred_info( back_prop_g, 2, [gnd,count]).
pred_info( back_prop_d_cl, 1, [rderef]).
pred_info( back_prop_d_cl, 2, [rderef]).
pred_info( back_prop_d, 2, [rderef,count]).
pred_info( back_prop_n_cl, 1, [nonvar]).
pred_info( back_prop_n_cl, 2, [nonvar]).
pred_info( back_prop_n, 2, [nonvar,count]).
pred_info( make_uni, 3, [uninit,sf,uni,dup]).
pred_info( dref_prop_flag, 2, [uninit,sf,uni,dup]).
pred_info( new_preds, 1, [occurs,change]).
pred_info( new_sets, 2, [gnd,nonvar,uninit,rderef,sf]).
pred_info( save_sets, 1, [gnd,nonvar,uninit,rderef,sf]).
pred_info( restore_sets, 1, [gnd,nonvar,uninit,rderef,sf]).
pred_info( spec_goal, 5, [gnd,nonvar,uninit,rderef,sf,spec]).
pred_info( spec_goal2, 6, [gnd,nonvar,uninit,rderef,sf,spec]).
pred_info( spec_update, 7, [gnd,nonvar,uninit,rderef,sf,spec]).
% *** Used in conversion of uninit to uninit_reg:
pred_info( convert_closure, 1, [defs,occurs,fast,uregs]).
pred_info( conv_preds, 1, [defs, fast,uregs,change]).
pred_info( calc_convert_set, 3, [defs, fast,uregs,change,set]).
pred_info( last_goal_ureg, 3, [defs, fast,uregs,change,set]).
pred_info( update_fast_goal, 3, [defs, fast,uregs,change]).
pred_info( update_ureg, 3, [uregs,change]).
pred_info(init_convert_strees, 1, [entry,preds,fast,uregs]).
pred_info( enter_fast, 2, [fast]).
242
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% *** Add the derived modes to the strees and to the stored modes:
create_mode_strees([], [], _, _, _, _) :- !.
create_mode_strees([S|Ss], [AS|ASs], Entry, Exit, UReg, F) :-
create_mode_stree(S, AS, Entry, Exit, UReg, F),
create_mode_strees(Ss, ASs, Entry, Exit, UReg, F).
create_mode_stree(stree(NaAr,HD,(H:-OldF),OH,DList,SD),
stree(NaAr,HD,(H:-NewF),OH,ADList,SD), Entry,Exit,UReg,F):- !,
lattice_modes_table(NaAr, Entry, H, Flow),
lattice_modes_table(NaAr, Exit, H, ExFlow),
new_formula(H, Flow, ExFlow, HD, UReg, NewReq, NewBef, NewAft),
add_mode_option(analyze_mode(H,NewReq,NewBef,NewAft)),
flat_conj((NewReq,NewBef), NewF),
write_mode(F, H),
create_mode_strees(DList, ADList, Entry, Exit, UReg, nontop).
create_mode_stree(D, D, _, _, _, _) :- directive(D).
% |/(combine) | |
% | | |
% NewReq NewBef NewAft
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This is done only for uninitialized variables which occur up to once in the
% last non-survive goal or the survive goals beyond it, and do not occur before
% that in the body of each clause.
% The last non-survive goal, if it exists, must have an uninit_reg mode in the
% same argument position as those uninit. vars, otherwise no tail recursion
% optimization can be done. A closure calculation is done to guarantee this &
% not to lose optimization opportunities.
% (An easy approximation is to allow the conversion only for recursive calls,
% but this loses the optimization for predicates with cuts and factoring.)
% *** Calculate for each predicate the set of arguments that may be uninit_reg.
245
% If the last goal in the clause is a non-survive goal, then the head’s
% uninitialized register arguments must be uninitialized register arguments
% of the last goal and in the same argument position. This guarantees no
% extraneous move instructions destroying the possibility for TRO in the
% clause.
% If the last goal in the clause is a survive goal, then there are no extra
% conditions on the uninitialized register arguments since no TRO is possible
% anyway.
% If the last goal is not defined in the program being analyzed, then no
% uninitialized register arguments are possible.
last_goal_ureg(Goal, _, _) -->> {survive(Goal)}, !.
last_goal_ureg(Goal, Head, _/Ar) -->>
S/set, {cons(S)},
{functor(Goal, N, A)},
[fget(N/A,UR)]:uregs,
[fget(N/A,(G:-_))]:defs,
!,
% Rename UR’s variables to those of Goal:
{stats(a,data10(N/A,G,Goal,UR))},
{map_args(G, UR, Goal, URSet)},
{stats(a,11)},
{stats(a,data12(URSet))},
intersectv(URSet):set,
{stats(a,13)},
{min_integer(A, Ar, Min)},
{stats(a,data14(A,Ar,Min))},
match_corresponding_args(1, Min, Head, Goal):set,
{stats(a,15)}.
last_goal_ureg(_, _, _) -->> insert(_,[]):set.
% Return the arguments of Set which are in the same position in Head and Goal.
match_corresponding_args(N, M, _, _, _, []) :- N>M, !.
match_corresponding_args(N, M, Head, Goal, Set, Out) :- N=<M,
arg(N, Head, X), inv(X, Set),
arg(N, Goal, Y), X==Y,
!,
Out = [X|Next],
N1 is N+1,
match_corresponding_args(N1, M, Head, Goal, Set, Next).
match_corresponding_args(N, M, Head, Goal, Set, Out) :- N=<M,
N1 is N+1,
match_corresponding_args(N1, M, Head, Goal, Set, Out).
% Split a conjunction into two parts, where the second part is the largest
% conjunction of which all goals but the first don’t kill argument registers.
split_conj_begin_end(true, true, true).
split_conj_begin_end((Goal,Conj), true, (Goal,Conj)) :- all_survive(Conj), !.
split_conj_begin_end((Goal,Conj), (Goal,Begin), End) :-
split_conj_begin_end(Conj, Begin, End).
all_survive(true).
all_survive((Goal,Conj)) :- survive(Goal), all_survive(Conj).
% Succeeds for the definition of a "fast" routine, i.e. the routine calls only
% builtins and survive goals so that its execution time is almost independent
% of its arguments.
fast_routine((A;B)) :- !, fast_routine(A), fast_routine(B).
fast_routine((A,B)) :- !, fast_routine(A), fast_routine(B).
fast_routine(Goal) :- survive(Goal), !.
fast_routine(Goal) :- builtin(Goal), !.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[get(Na/Ar,B)]:exit,
Pred/pred,
[add(Na/Ar,Pred)]:occurs.
init_goal(Goal) -->> {unify_p(Goal)}, !.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Through the entry points’ modes the analysis is kept consistent with
% external calls.
% Get all definitions of arity zero. They are always entry points.
entry_zero([]) --> [].
entry_zero([stree(Na/0,_,_,_,DL,_)|Ss]) --> !, [Na/0],
entry_zero(Ss), entry_zero(DL).
entry_zero([stree(_/N,_,_,_,DL,_)|Ss]) --> {N>0}, !,
entry_zero(Ss), entry_zero(DL).
entry_zero([_|Ss]) --> entry_zero(Ss).
entry_init_one(entry(Head,V,G,N,U,D)) -->>
update_entry(Head, _, _)
:[gnd(G,_),nonvar(N,_),uninit(U,_),rderef(D,_),sf(V,_),change([],_)].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% During traversal of call tree update the entry & exit mode tables and
% keep track of the predicates whose entry and exit modes are changed.
% Repeat traversal step with the predicates that need propagating (i.e. those
% whose entry mode is changed & those which contain a goal whose exit mode
% is changed) until there are no more changes.
analyze(Strees, CStrees) :-
stats(an,1),
init_tables(Strees, Ent, Ex, Defs, Occurs),
entry_data(Strees, Defs, EntData, EntPreds),
cons(EntPreds), !,
entry_init(EntData, Ent, MidEnt),
stats(an,2),
analyze_closure(EntPreds, MidEnt, OutEnt, Ex, OutEx, Defs, _,
_, Spec, Occurs,_),
stats(an,3),
% Replace goals by their most specialized equivalents:
seal(Spec),
spec_strees(Strees, AStrees, Spec),
stats(an,4),
% Calculate which modes can be converted to uninit_reg:
convert_uninit_strees(AStrees, OutEnt, Defs, Occurs, OutUReg),
stats(an,5),
% Update the modes:
wn(’% Modes generated:’),
create_mode_strees(AStrees, BStrees, OutEnt, OutEx, OutUReg),
stats(an,6),
% Re-unravel the heads with the new modes (gives better selection):
re_unr_strees(BStrees, CStrees),
stats(an,7).
analyze(Strees, Strees) :-
warning([’There are no usable entry points, so no flow analysis was done.’]).
% ChgSet is the set of all predicates whose entry or exit modes have changed.
analyze_closure([]) -->> !.
analyze_closure(EntPreds) -->> cons(EntPreds), !,
trav_preds(EntPreds):change([],ChgSet),
{stats(an,2.5)},
{length(ChgSet, N)},
251
% Calculate from ChgSet the set of predicates that need further traversal:
% (This predicate also used in convert_uninit_strees)
new_preds([]) -->> !.
new_preds([entry(NaAr)|ChgSet]) -->> !,
[add(NaAr)]:change,
new_preds(ChgSet).
new_preds([exit(NaAr)|ChgSet]) -->> [fget(NaAr,Occurs)]:occurs, !,
[add_set(Occurs)]:change,
new_preds(ChgSet).
new_preds([_|ChgSet]) -->>
new_preds(ChgSet).
% Top level call: Traverse predicates & update exit mode for each predicate:
trav_preds([]) -->> !.
trav_preds([NaAr|Preds]) -->>
[fget(NaAr,Entry)]:entry,
[fget(NaAr,Def)]:defs, !,
{copy(Def,(Head:-Disj))},
{stats(trav_pred,NaAr)},
trav_pred(Disj, Head, Entry, NaAr),
trav_preds(Preds).
% The predicate is not defined here (it is probably a builtin):
trav_preds([_|Preds]) -->>
trav_preds(Preds).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Give each choice the same gnd, rderef, sf, and uninit values.
% Return the set of arguments ground at the output.
trav_disj(fail, Head, _, _, _, All, All, All) -->> !,
{varset(Head, All)}.
% Don’t redo an active clause: (unless current entry worse than stored?)
trav_disj((Conj;Disj), Head, NaAr, Num, CallList, OutGnd, OutNon, OutDrf) -->>
{member(caller(NaAr,Num), CallList)}, !,
{Num1 is Num+1},
252
% Back propagate gnd, rderef, and nonvar sets at end of clause to the head:
% Do closure on the clause’s unifications at the end of the clause
% to find any additional arguments that exit as gnd or rderef terms.
% This routine is not needed for correctness, but it improves results.
back_propagate(Unis) -->>
back_prop_d_cl(Unis),
back_prop_g_cl(Unis),
Gnd/gnd, [add_set(Gnd)]:nonvar,
back_prop_n_cl(Unis).
back_prop_d_cl(0, _) -->> !.
back_prop_d_cl(N, Unis) -->> {N>0}, !, back_prop_d_cl(Unis).
back_prop_g_cl(0, _) -->> !.
back_prop_g_cl(N, Unis) -->> {N>0}, !, back_prop_g_cl(Unis).
253
back_prop_n_cl(0, _) -->> !.
back_prop_n_cl(N, Unis) -->> {N>0}, !, back_prop_n_cl(Unis).
trav_conj(true, _, _, _, _) -->> !,
% Any uninits remaining are initialized & thus become dereffed:
Uni/uninit, [add_set(Uni)]:rderef.
trav_conj((Goal,Conj), NaAr, I, J, CallList) -->>
{varset(Goal, Vars)},
{stats(trav_goal,d(NaAr,I,J))},
trav_goal(Goal, NaAr, I, J, CallList, Vars),
{J1 is J+1},
trav_conj(Conj, NaAr, I, J1, CallList).
{term_dupset(A=B, Dups)},
trav_goal_d(A, B, Vars, Dups),
trav_goal_u(A, B, Vars),
Gnd/gnd, [add_set(Gnd)]:nonvar,
trav_goal_n(A, B),
% How do duplicate variables affect the Deref set? Not at all.
% How do circular terms affect the Deref set?
[sub_set(Dups)]:uninit,
Uni/uninit, [add_set(Uni)]:rderef,
[Vars]:sf.
[add(X)]:rderef.
new_dref(X, TVars) -->> [].
[sub_set(Vars)]:uninit.
% Calculate new rderef & gnd sets from the exit sets of a predicate:
% New rderef set = (Drf n Gnd) u (ExitDrf n (Uni u Drf u (Vars-SF)))
% New gnd set = (Gnd u ExitGnd)
% New nonvar set = (Non u ExitNon)
new_drf_gnd(ExitDrf, ExitGnd, ExitNon, Vars) -->>
new_drf_set(ExitDrf, Vars),
[add_set(ExitGnd)]:gnd,
[add_set(ExitNon)]:nonvar.
new_drf_set([], _) -->> !,
Gnd/gnd,
intersectv(Gnd):rderef.
new_drf_set(ExitDrf, Vars) -->> cons(ExitDrf), !,
insert(Drf,NewDrf):rderef,
Gnd/gnd,
Uni/uninit,
SF/sf,
{unionv(Uni, Drf, D1)},
{diffv(Vars, SF, NV)},
{unionv(NV, D1, D2)},
{intersectv(ExitDrf, D2, D3)},
{intersectv(Gnd, Drf, D4)},
{unionv(D3, D4, NewDrf)}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% specialized entry for each call that has a modal_entry. The index of spec
% is index(NaAr,ClauseNum,GoalNum), which uniquely identifies each goal in
% the program.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
save_sets(state(G,N,U,D,S)) -->>
G/gnd,
N/nonvar,
U/uninit,
D/rderef,
S/sf.
restore_sets(state(G,N,U,D,S)) -->>
insert(_,G):gnd,
insert(_,N):nonvar,
insert(_,U):uninit,
insert(_,D):rderef,
insert(_,S):sf.
map_argvars(I, Ar, G1, Set1, G2) --> {I=<Ar, arg(I, G1, A), inv(A, Set1)}, !,
{arg(I, G2, X)},
varbag(X),
{I1 is I+1},
map_argvars(I1, Ar, G1, Set1, G2).
map_argvars(I, Ar, G1, Set1, G2) --> {I=<Ar}, !,
{I1 is I+1},
map_argvars(I1, Ar, G1, Set1, G2).
map_argvars(I, Ar, _, _, _) --> {I>Ar}, !.
% Get the set of head arguments corresponding to the lattice value Type:
get_set(Type, Head, Mode, TypeSet) :-
functor(Head, Na, Ar),
get_bag(1, Ar, Type, Head, Mode, Bag, []),
sort(Bag, TypeSet).
% Update the entry mode & return the most general entry mode.
% (Does NOT modify gnd or uninit sets)
update_entry(Goal, NewEntry, Flag) -->>
calc_entry(Goal, Entry),
{functor(Goal, Na, Ar)},
[fget(Na/Ar,OldEntry)]:entry,
{lub_call(OldEntry, Entry, NewEntry)},
update_entry(Na/Ar, OldEntry, NewEntry, Flag).
262
% Update the exit mode & return the most general exit mode.
update_exit(Goal) -->>
{functor(Goal, Na, Ar)},
[fget(Na/Ar,OldExit)]:exit,
!,
calc_exit(Goal, Exit),
{lub_call(OldExit, Exit, NewExit)},
update_exit(Na/Ar, OldExit, NewExit).
update_exit(_) -->> [].
% Calculate the exit mode corresponding to the current values of the sets:
calc_exit(Goal, Exit) -->>
{functor(Goal, Na, Ar)},
{functor(Exit, Na, Ar)},
Gnd/gnd,
Non/nonvar,
Drf/rderef,
{calc_exit_2(1, Ar, Gnd, Non, Drf, Goal, Exit)}.
% Calculate the entry mode corresponding to the current values of the sets:
calc_entry(Goal, Entry) -->>
{term_dupset(Goal, Dups)},
Gnd/gnd,
263
Non/nonvar,
Uni/uninit,
Drf/rderef,
SF/sf,
{functor(Goal, Na, Ar)},
{functor(Entry, Na, Ar)},
{calc_entry_2(1, Ar, Gnd, Non, Dups, Uni, Drf, SF, Goal, Entry)}.
add_nonvar_info( no, Y, Y) :- !.
add_nonvar_info(yes, rderef, nondrf) :- !.
add_nonvar_info(yes, any, nonvar) :- !.
add_nonvar_info(yes, Y, Y) :- !.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Lattice utilities:
% Calculate the least upper bound of two arguments on the argument lattice:
lub(unknown, X, X) :- !.
264
lub( X, unknown, X) :- !.
lub( any, _, any) :- !.
lub( _, any, any) :- !.
lub( X, X, X) :- !.
lub( nonvar, ground, nonvar) :- !.
lub( ground, nonvar, nonvar) :- !.
lub( nonvar, nondrf, nonvar) :- !.
lub( nondrf, nonvar, nonvar) :- !.
lub( nonvar, gnddrf, nonvar) :- !.
lub( gnddrf, nonvar, nonvar) :- !.
lub( ground, nondrf, nonvar) :- !.
lub( nondrf, ground, nonvar) :- !.
lub( ground, gnddrf, ground) :- !.
lub( gnddrf, ground, ground) :- !.
lub( nondrf, gnddrf, nondrf) :- !.
lub( gnddrf, nondrf, nondrf) :- !.
lub( rderef, nondrf, rderef) :- !.
lub( nondrf, rderef, rderef) :- !.
lub( rderef, gnddrf, rderef) :- !.
lub( gnddrf, rderef, rderef) :- !.
lub( rderef, uninit, rderef) :- !.
lub( uninit, rderef, rderef) :- !.
lub( uninit, gnddrf, rderef) :- !.
lub( gnddrf, uninit, rderef) :- !.
lub( uninit, nondrf, rderef) :- !.
lub( nondrf, uninit, rderef) :- !.
lub( nonvar, rderef, any) :- !.
lub( rderef, nonvar, any) :- !.
lub( nonvar, uninit, any) :- !.
lub( uninit, nonvar, any) :- !.
lub( ground, rderef, any) :- !.
lub( rderef, ground, any) :- !.
lub( ground, uninit, any) :- !.
lub( uninit, ground, any) :- !.
lub(A,B,any) :- error([’Bug in lub with ’,lub(A,B,_)]).
% Calculate the least upper bound of two modes on the call lattice:
lub_call(Call1, Call2, Lub) :-
functor(Call1, Na, Ar),
functor(Call2, Na, Ar),
functor(Lub, Na, Ar),
lub_call(1, Ar, Call1, Call2, Lub).
265
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% General utilities:
266
% Return a flat to show if an argument’s variables are disjoint from the set:
disjoint_flag(Vars, Set, yes) :- disjointv(Vars, Set), !.
disjoint_flag(X, _, no).
% Calculate the set of variables in Goal that occur more than once:
% Also calculate the set or bag of variables in Goal.
% (Also used in clause_code.pl)
term_dupset(Goal, DupSet) :-
term_dupset_varbag(Goal, DupSet, _).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
267
clause_code.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
% Notes:
% 1. Any variable X that is var(X) which is passed to a user-defined predicate
% must have the var(X) formula removed after the predicate, since we don’t
% know if the predicate instantiates any arguments. All other tests remain
% true, because they start with an instantiated variable which can’t change.
% 2. Steps 1,2,3 could be interleaved: pre - call_move - post for each
% argument. If done, this must be signaled to regalloc.
% 3. In combination with procedure selection code generation, this module
% could use more information on which registers contain which arguments.
% Presently it assumes that procedure selection changes no argument
% registers.
% 4. This module does not instantiate any variables in the input clause.
% 5. The calls to ’univ’ can be rewritten in a more verbose style using
% indexing. Transformation should be able to take care of this.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
{entry_comment(H, B, Entries)},
{split_uninit_list(Entries, UMSets, URSets, UForms, IForms)},
[label(N/A)],
clause_code((H:-B), DList, return, NewF),
clause_code_list(Bodies, DList).
% The body has not been called at all,
% therefore it is not necessary to compile it:
clause_code_list([body((H:-B),Entries)|Bodies], DList) -->
{var(Entries)}, !,
clause_code_list(Bodies, DList).
% From the entries, extract: (1) sets of uninit vars, (2) uninit formulas, and
% (3) remaining formulas.
split_uninit_list([], [], [], [], []).
split_uninit_list([entry(_,F)|Fs], [UMVs|UMSets], [URVs|URSets], [UF|UFs],
[IF|IFs]) :-
split_uninit(F, UF, IF),
uninit_set_type(mem, UF, UMVs),
uninit_set_type(reg, UF, URVs),
split_uninit_list(Fs, UMSets, URSets, UFs, IFs).
% Insert code for each entry to initialize the correct uninit variables,
% and then jump to the body.
insert_uninit([], _, _, [], _) --> [].
insert_uninit([Us|USets], Head, UIn, [entry(Lbl,_)|Es], NaAr) -->
{diffv(Us, UIn, Xs)},
[label(Lbl)],
init_code_block(Xs, Head),
[jump(NaAr)],
insert_uninit(USets, Head, UIn, Es, NaAr).
% Calculate the origin sets (the arguments of the unifications where they
% could have been created) for all uninitialized variables:
% This is necessary because each entry to the body may have created the same
% uninitialized variable in a different unification. Whenever a variable is
% passed as an argument then all uninits containing it in their origin sets
% have to be initialized.
uninit_origins(Fs, Origins) :-
origin_fragments(Fs, Fragments, []),
keysort(Fragments, Sort),
origin_merge(Sort, Origins).
{append(T, S, Sp)},
origin_merge(Frags, X, Sp).
origin_merge([Y-T|Frags], X, S) --> {X\==Y}, !,
[X-S],
origin_merge(Frags, Y, T).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
pass_info(dlist).
pass_info(gperms).
pass_info(aun). % Set of vars allowed to be created as uninitialized.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Bypass the extra link in the dereference chain caused by the binding of
% new variables (incl. uninit mem). Every occurrence X of a new variable
% that has been initialized is replaced by [X] (an extra indirection).
% This speeds up later dereferencing of that value & also guarantees
% that all variables considered dereferenced by flow analysis are really so.
% For correctness, ’pref’ allocation is not done for variables that get an
% extra indirection.
R/repi,
perm(Goal, VGoal):[code(Code,[])],
map_instr_list(Code, I, R):code,
{map_varlist(VGoal, NewVGoal, I)}.
perm_c(Goal, VGoal) -->>
perm(Goal, VGoal).
make_indirect([], []).
make_indirect([X|USet], [[X]|Ind]) :- make_indirect(USet, Ind).
map_varlist_goal(VL, ’$varlist’(NewVL,Link), I) :-
map_varlist_list(VL, I, NewVL, Link).
map_varlist_list(VL, NewVL, I) :-
map_varlist_list(VL, I, NewVL, []).
276
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% *** Generate code for goals where each argument has 3x3 possibilities:
% Method:
% 0. get required modes of the goal from tables.
% 1. call one_arg_pre for all Arg-NewArg pairs.
% 2. generate code for the call itself.
% 3. add pred’s "after" modes to current formula.
% 4. call one_arg_post for all Arg-NewArg pairs.
% Wrapped calls (’$body’ calls created by segment.pl)
% need minimal support (i.e. only passing arguments):
goal(G, ’$varlist’(V1), Data, none, true) -->>
{call_p(G)},
{unwrap_goal(G, Goal, yes)}, !,
{varbag(Goal, Varbag)},
init_uninit(Varbag):vl(V1,V2),
{functor(Goal, N, A)},
{gensym("$e_", N/A, Np)},
unwrap_form(G, Np/A):form,
Form/form,
{uninit_set_type(reg, Form, URegs)},
x_call_moves_pre(Goal, URegs, V2, V3):code,
{fence_if_die(Goal, V3, [])},
[call(Np/A)]:code,
{x_call_moves_post_c1(Goal, URegs, Data)},
unionv(URegs):sf,
remove_vars,
remove_all_uninit.
% Standard calls need lots of support:
goal(G, VarList, none, Data, After) -->>
{call_p(G)},
{unwrap_goal(G, Goal, no)}, !,
{functor(Goal, N, A)},
{Goal=..[N|Args]},
{VarList=(’$varlist’(Vpre),VT,’$varlist’(Vm),VGoal)},
parameter_setup(Goal, Flags, IReq):vl(Vpre,Vm1),
{stats(x,15)},
args_pre(Flags, Args, NArgs):vl(Vm1,[]),
MyF/form,
{stats(x,data16(ireq(IReq),form(MyF)))},
{NGoal=..[N|NArgs]},
rtests_m(IReq, VT):[form,code],
{stats(x,17)},
warn_fail(G):form,
277
warn_dup_uninit(_, []) :- !.
warn_dup_uninit(Goal, [X]) :- !,
comment(Goal, [’Argument ’,X,’ of ’,Goal,’ is duplicated.’]).
warn_dup_uninit(Goal, S) :- cons(S), !,
comment(Goal, [’Arguments ’,S,’ of ’,Goal,’ are duplicated.’]).
280
warn_req_uninit(_, _, [], _) :- !.
warn_req_uninit(I, Goal, [flag(ini,mem)|Flags], NaAr) :- !,
arg(I, Goal, X),
warning(Goal, [’Argument ’,X,’ of ’,Goal,
’ is given an init but requires an uninit.’]),
I1 is I+1,
warn_req_uninit(I1, Goal, Flags, NaAr).
warn_req_uninit(I, Goal, [_|Flags], NaAr) :-
I1 is I+1,
warn_req_uninit(I1, Goal, Flags, NaAr).
% First argument:
% reg(T) argument A is given uninit(reg,A) OR (var(A) & A notin InSF) (1st occ).
% mem argument A is given uninit(mem,A).
% ini argument A is given an initialized argument (default).
% Second argument:
% reg predicate requires uninit(reg,A).
% mem predicate requires uninit(mem,A).
% ini predicate requires an initialized argument (default).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[move(r(I),X)],
{I1 is I+1},
head_moves_pre_2(NArgs, I1, URegs, VL).
[move(r(I),X)],
{I1 is I+1},
call_moves_post(I1, Flags, NArgs, VL, L).
call_moves_post(I, [_|Flags], [_|NArgs], VL, L) -->
{I1 is I+1},
call_moves_post(I1, Flags, NArgs, VL, L).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Notes:
% 1. The permanents that should be renamed are the initialized global perms
% that are in:
% 1. If survive(Goal) then (vars to be dereffed) U (bindset)
% 2. If \+survive(Goal) then (vars to be dereffed)
% where:
% (vars to be dereffed) = (required dereffed) - (already dereffed)
% Justification: binding of a survive goal arg changes the value in the
% current environment, so it requires a new permanent. Binding of a
% \+survive goal arg doesn’t change the value in the current environment,
% so it doesn’t need to be renamed in the current environment.
% 2. During execution of body/2, accumulators form and goal are kept with
% their original variables & only mapped for goal/2. Accumulators vl, sf,
% and code always use extra variables.
% 3. The instruction deref(p(I)) should never be generated when the option
% write_once exists. If it is then there is a bug in the clause compiler.
% 4. Initialization of global permanent variables is not done here, which
% should be ok, since this code only initializes the variables which it
% creates, i.e. extra variables.
{\+write_once}, !.
not_deref([], _, []) :- !.
not_deref([X|Xs], F, [X|Ns]) :- \+implies(F, deref(X)), !, not_deref(Xs, F, Ns).
not_deref([_|Xs], F, Ns) :- not_deref(Xs, F, Ns).
[deref(Xold,Xp)]:code,
[deref(X)]:form.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
find_gperms(Cl, Perms) :-
split(Cl, Head, Body),
varset(Head, HVars),
gpermvars(Body, HVars, Vars, [], Half, [], Perms).
gpermvars(true) -->> !.
gpermvars((Goal,Body)) -->>
gpermstep(Goal),
gpermsurv(Goal),
gpermvars(Body).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Used in clause_code:
% This entry point adds uninit modes to Form.
efficient_entry(Goal, EGoal, Form, Form, SF, SF) :-
modal_entry(Goal, Tree),
firstuse_uninit(Goal, SF, Form, NewF),
tree_trav_entry(Tree, NewF, EGoal),
!,
comment([’Replaced ’,Goal,’ by ’,EGoal]),
comment(Goal, [’The mode formula at this point is ’,NewF]).
efficient_entry(Goal, Goal, Form, Form, SF, SF).
modal_entry(Goal, Tree),
tree_trav_entry(Tree, Form, EGoal),
!,
comment([’During analysis, replaced ’,Goal,’ by ’,EGoal]),
comment(Goal, [’The mode formula at this point is ’,Form]).
efficient_entry(Goal, Goal, _).
tree_trav_entry(entry(EGoal), _, EGoal).
tree_trav_entry(mode(F,True,False), Form, EGoal) :-
(implies(Form, F)
-> tree_trav_entry(True, Form, EGoal)
; tree_trav_entry(False, Form, EGoal)
).
make_uninit([], true).
make_uninit([X|Uni], (uninit(either,X),UnF)) :- make_uninit(Uni, UnF).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
288
conditions.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This routine handles multiple argument tests, when the test’s registers
% are the same as its arguments:
rtest_m(Test, Varlist, InF, OutF) -->
rtest_m_nf(Test, Varlist, InF),
{update_formula(Test, InF, OutF)}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[trail(Ra)],
[label(L)].
expand_test(nil(A), A, Ra, _, Lbl) --> !,
[equal(Ra,Tatmˆ[],Lbl)], {tag(atom, Tatm)}.
expand_test(A==B, A, Ra, _, Lbl) --> {atomic(B)}, !,
{atomic_word(B,W)},
[equal(Ra,W,Lbl)].
expand_test(B==A, A, Ra, _, Lbl) --> {atomic(B)}, !,
{atomic_word(B,W)},
[equal(Ra,W,Lbl)].
% These are handled by the default case:
% expand_test(list(A), A, Ra, _, Lbl) --> !,
% [test(eq,Tlst,Ra,L)], {tag(cons, Tlst)},
% [equal(Ra,Tatmˆ[],Lbl)], {tag(atom, Tatm)},
% [label(L)].
% expand_test(compound(A), A, Ra, _, Lbl) --> !,
% [test(eq,Tstr,Ra,L)], {tag(structure, Tstr)},
% [test(ne,Tlst,Ra,Lbl)], {tag(cons, Tlst)},
% [label(L)].
% expand_test(number(A), A, Ra, _, Lbl) --> {split_integer}, !,
% [test(eq,Tpos,Ra,L)], {tag(nonnegative, Tpos)},
% [test(ne,Tneg,Ra,Lbl)], {tag(negative, Tneg)},
% [label(L)].
% expand_test(integer(A), A, Ra, _, Lbl) --> {split_integer}, !,
% [test(eq,Tpos,Ra,L)], {tag(nonnegative, Tpos)},
% [test(ne,Tneg,Ra,Lbl)], {tag(negative, Tneg)},
% [label(L)].
expand_test(negative(A), A, Ra, _, Lbl) --> {\+split_integer}, !,
{arith_test(X>=Y, Ges)},
[jump(Ges,Ra,0,Lbl)].
% [cmpi(Lts,Ra,0),jump(false,Lbl)].
expand_test(nonnegative(A), A, Ra, _, Lbl) --> {\+split_integer}, !,
{arith_test(X<Y, Lts)},
[jump(Lts,Ra,0,Lbl)].
% [cmpi(Lts,Ra,0),jump(true,Lbl)].
expand_test(number(A), A, Ra, _, Lbl) --> {\+split_integer}, !,
[test(ne,Tint,Ra,Lbl)], {tag(integer,Tint)}.
expand_test(Test, A, Ra, _, Lbl) --> {test_tag(Test,A,Tag)}, !,
[test(ne,Tag,Ra,Lbl)].
expand_test(nonvar(A), A, Ra, _, Lbl) --> !,
[test(eq,Tvar,Ra,Lbl)], {tag(var, Tvar)}.
expand_test(functor(A,F,N), A, Ra, InF, Lbl) -->
{atom(F), integer(N), F==’.’, N==2},
ctest(nonvar(A), InF), !,
[test(ne,Tlst,Ra,Lbl)], {tag(cons, Tlst)}.
expand_test(functor(A,F,N), A, Ra, InF, Lbl) -->
{atom(F), integer(N), N>0, (F\==’.’; N\==2)},
ctest(nonvar(A), InF), !,
tag(atom, Tatm),
test_one_tag(structure(A), Ra, InF, Lbl),
{tag(structure, Tstr)},
[pragma(tag(Ra,Tstr))],
{align(K)},
[pragma(align(Ra,K))],
292
[equal([Ra],Tatmˆ(F/N),Lbl)].
expand_test(functor(A,F,N), A, Ra, InF, Lbl) -->
{atom(F), integer(N), N==0},
ctest(nonvar(A), InF), !,
tag(atom, Tatm),
[equal(Ra,TatmˆF,Lbl)].
expand_test(functor(A,F,N), A, Ra, InF, Lbl) -->
{number(F), integer(N), N==0},
ctest(nonvar(A), InF), !,
[equal(Ra,F,Lbl)].
expand_test(’$name_arity’(A,F,N), A, Ra, InF, Lbl) -->
{atom(F), integer(N), F==’.’, N==2}, !,
[test(ne,Tlst,Ra,Lbl)], {tag(cons, Tlst)}.
expand_test(’$name_arity’(A,F,N), A, Ra, InF, Lbl) -->
{atom(F), integer(N), N>0, (F\==’.’; N\==2)}, !,
tag(atom, Tatm),
test_one_tag(structure(A), Ra, InF, Lbl),
{tag(structure, Tstr)},
[pragma(tag(Ra,Tstr))],
{align(K)},
[pragma(align(Ra,K))],
[equal([Ra],Tatmˆ(F/N),Lbl)].
expand_test(’$name_arity’(A,F,N), A, Ra, InF, Lbl) -->
{atom(F), integer(N), N==0}, !,
tag(atom, Tatm),
[equal(Ra,TatmˆF,Lbl)].
expand_test(’$name_arity’(A,F,N), A, Ra, InF, Lbl) -->
{number(F), integer(N), N==0}, !,
[equal(Ra,F,Lbl)].
expand_test(float(A), A, Ra, InF, Lbl) --> !,
[jump(Lbl)]. % There are no floats--it always fails.
expand_test(Test, A, Ra, InF, Lbl) -->
{test_to_disj(Test, A, Disj)},
expand_test_disj(Disj, Ra, InF, Lbl).
test_one_tag(Test, Ra, InF, Lbl) --> test_one_tag(ne, Test, Ra, InF, Lbl).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
pred_info(expand, 3, [form,code]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Goal subsumption:
% Note:
% 0. This routine is slow.
% 1. Later this routine will be extended to handle user-defined goals and goals
% with side effects. These goals may only be removed if they don’t do any
% work that is not done already by P.
% Currently, the only case that is handled is bindings with unify goals.
% 2. By ’work’ I mean bindings and side-effects. This definition must be made
% more precise.
% Succeeds if the work P does subsumes the work the goal G does:
% (This predicate will be extended later.)
subsume_work(P, G, _) :- P==G, !.
subsume_work(P, G, Time) :- implies(P, G, Time).
prolog_subsume_work(P, G, _) :- P==G, !.
prolog_subsume_work(P, G, Time) :- prolog_implies(P, G, Time).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
else(P, G, SG) :-
simplify(not(P), NP),
subsume(NP, G, SG).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Simplification of formulas:
simp_up(’$case’(N,I,C), S,L):- !,
simp_upv(C,B,L),simp_one(’$case’(N,I,B),S,L).
simp_up(’$test’(D,P,T,A,C),S,L):- !,
simp_upv(C,B,L),simp_one(’$test’(D,P,T,A,B),S,L).
simp_up(’$else’(P,A,C), S,L):- !,
simp_upv(C,B,L),simp_one(’$else’(P,A,B),S,L).
simp_up( (A,B), S, L) :- !,
simp_upv(A,SA,L),simp_upv(B,SB,L),simp_one((SA,SB),S,L).
simp_up( (A;B), S, L) :- !,
simp_upv(A,SA,L),simp_upv(B,SB,L),simp_one((SA;SB),S,L).
simp_up((A->B), S, L) :- !,
simp_upv(A,SA,L),simp_upv(B,SB,L),simp_one((SA->SB),S,L).
simp_up((A=>B), S, L) :- !,
simp_upv(A,SA,L),simp_upv(B,SB,L),simp_one((SA=>SB),S,L).
simp_up( \+(A), S, L) :- !,
simp_upv(A,SA,L),simp_one( \+(SA),S,L).
simp_up(not(A), S, L) :- !,
simp_upv(A,SA,L),simp_one(not(SA),S,L).
simp_up( A, S, L) :- simp_one(A, S, L).
det_control(’$case’(_,_,_)).
det_control(’$test’(_,_,_,_,_)).
det_control(’$else’(_,_,_)).
diff_sol :- \+same_number.
succeeds(Rel) :-
encode_relop(Rel, A, Ord, B),
test_relop(A, Ord, B),
\+errs(Rel).
% If adding any tests that bind here then must change simp_one:
opp(true, fail).
opp(A<B, A>=B).
opp(A>B, A=<B).
opp(A=:=B, A=\=B).
opp(A@<B, A@>=B).
opp(A@>B, A@=<B).
opp(A==B, A\==B).
opp(var(A), nonvar(A)).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Generic update_formula:
% (Flag is connected with aliasing; see split_formula)
% For later: Also convert a test to bitmap representation if possible
% update_formula(G, InF, OutF) :-
% exact_bitmap(G), !,
% bitmap_test(G, GB),
% xupdate_formula(GB, InF, yes, _, OutF).
update_formula(G, InF, OutF) :-
xupdate_formula(G, prolog, InF, yes, _, OutF).
xupdate_formula(fail, _, _, _, _, fail) :- !.
xupdate_formula(_, _, fail, _, _, fail) :- !.
xupdate_formula(G, _, true, _, _, G) :- !.
xupdate_formula(true, _, G, _, _, G) :- !.
xupdate_formula(var(X), _, InF, _, _, fail) :- implies(InF, nonvar(X)), !.
xupdate_formula(var(X), _, InF, _, _, (var(X),InF)) :- !.
xupdate_formula(G, _, InF, _, _, InF) :- memberv_conj(G, InF), !.
xupdate_formula(deref(X), _, InF, _, _, InF) :- memberv_conj(rderef(X), InF), !.
xupdate_formula(deref(X), _, InF, _, _, (deref(X),InF)) :- !.
xupdate_formula(rderef(X), _, InF, _, _, (rderef(X),MidF)) :- !,
remove_formula(deref(X), InF, MidF).
xupdate_formula((A,B), T, InF, Fl, _, OutF) :-
nonvar(Fl), !,
xupdate_formula(A, T, InF, Fl, _, MidF),
xupdate_formula(B, T, MidF, Fl, _, OutF).
xupdate_formula((A,B), T, InF, Fl, SF, OutF) :-
var(Fl), !,
xupdate_formula(A, T, InF, _, SF, MidF),
xupdate_formula(B, T, MidF, _, SF, OutF).
xupdate_formula(G, T, InF, Flag, SoFar, OutF) :-
( T=logical ->
( mutex(G, InF, after) ->
301
OutF=fail
; (bitmap_combine(G, InF, OutF) -> true ; OutF=(G,InF))
)
; T=prolog ->
( var(Flag) -> extended_bindset(G, InF, SoFar, Bs)
; nonvar(Flag) -> extended_bindset(G, InF, Bs)
),
( Bs=[] ->
( mutex(G, InF, left, prolog) ->
OutF=fail
; (bitmap_combine(G, InF, OutF) -> true ; OutF=(G,InF))
)
; cons(Bs) ->
split_formula(Flag, SoFar, Bs, InF, SplitF, RestF),
remove_vars(SplitF, MidF),
( mutex(G, MidF, left, prolog) ->
OutF=fail
; combine_formula((G,MidF), RestF, OutF)
)
; Bs=all ->
remove_vars(InF, MidF),
( mutex(G, MidF, left, prolog) ->
OutF=fail
; OutF=(G,MidF)
)
)
).
% Remove all var(X) goals and some deref(X) goals from a formula.
% 1. Rderef(X) is removed only if it’s not known that X is ground.
% 2. Deref(X) is removed only if it’s not known that X is nonvariable.
% 3. Rderef(X) is changed to deref(X) if X is known nonvar & not known ground.
% 4. Uninit modes are NOT affected by aliasing. They are guaranteed not to be
% aliased to anything.
remove_vars(InF, OutF) :-
remove_all_vars(InF, MidF),
notnonvar_ground_formula(InF, NotNV, NotGnd),
remove_all_derefs(MidF, MidF2, NotNV, NotGnd),
squeeze_conj(MidF2, OutF), !.
remove_all_vars((A,B), (RA,RB)) :- !,
remove_all_vars(A, RA),
remove_all_vars(B, RB).
remove_all_vars(var(V), true) :- var(V), !.
remove_all_vars( Goal, Goal).
% *** Split a mode formula into two parts: One which contains all predicates
% relevant to the variables in Term, and the rest.
% Need to follow unification terms, hence the ’vars_in_unify’ predicate
% and its implementation through graph closure. The important variables
% are those which have been initialized (in SF) AND are related textually
% (through unification terms).
% Single-goal split:
% - Var(X) is only relevant if there is possible aliasing.
% - Rderef(X) is relevant if X is not implied to be ground.
% - Deref(X) is relevant if X is not implied to be nonvar.
% If X is implied to be nonvar, then if it’s dereferenced
% it stays that way, so it doesn’t have to be split off.
% It is split off anyway, so that its truth will be known for
% both output formulas. This is useful in the use of split_formula
% in flatten.
% - Else if a goal has no variables in common with Vs,
% then it is not relevant (which is true independent of aliasing).
split_one(_, _, _, _, true, true, true) :- !.
split_one(_, _, _, _, fail, fail, true) :- !.
split_one(yes, _, _, _, var(X), var(X), true) :- !.
split_one(yes, NN, _, _, deref(X), deref(X), true) :- inv(X, NN), !.
split_one(yes, NN, _, _, deref(X), deref(X), deref(X)) :- !.
split_one(yes, _, NG, _, rderef(X), rderef(X), true) :- inv(X, NG), !.
split_one(yes, _, NG, _, rderef(X), rderef(X), rderef(X)) :- !.
split_one(_, _, _, Vs, A, true, A) :-
varset(A, VA), disjointv(VA, Vs), !.
split_one(_, _, _, _, A, A, true).
% Get set of all vars NOT implied nonvar & NOT implied ground:
notnonvar_ground_formula(Form, NotNV, NotGnd) :-
305
varset(Form, VS),
nonvar_set(Form, NV),
ground_set(Form, Gnd),
diffv(VS, NV, NotNV),
diffv(VS, Gnd, NotGnd).
% Aliasing flag:
% Aliasing is possible if VarsInitSoFar and RelevantVars have at least
% one variable in common.
% aliasing_flag(SoFar, Rel, F) :- intersectv(SoFar, Rel, X), cons(X), !, F=yes.
aliasing_flag(AliVars, Rel, F) :- disjointv(AliVars, Rel), !, F=no.
aliasing_flag(AliVars, Rel, yes).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
mem_reg(mem).
mem_reg(reg).
% *** Split a formula into uninit & deref modes & others:
% Uninit & deref are require modes for external mode input.
split_uninit_deref((A,B), (AU,BU), (AI,BI)) :- !,
split_uninit_deref(A, AU, AI),
split_uninit_deref(B, BU, BI).
split_uninit_deref(deref(X), deref(X), true) :- !.
split_uninit_deref( G, G, true) :- an_uninit_mode(G), !.
split_uninit_deref( G, true, G).
306
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% *** A more discriminating bindset that also uses the set of existing
% variables: Returns those variables in the goal that are not known not to be
% bound to a nonvariable. Does not return any variables which are either not
% bound or remain unbound (i.e. being bound to another unbound variable is ok).
bindset( X=Y, _, SF, BS) :- var(X), \+inv(X, SF), !, BS=[X].
bindset( X=Y, _, SF, BS) :- var(Y), \+inv(Y, SF), !, BS=[Y].
bindset(Goal, InF, _, BS) :- bindset(Goal, InF, BS).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
309
compiler.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This program may be freely used and modified for non-commercial purposes
% provided this notice is kept unchanged and proper credit is given.
% Written by Peter Van Roy in the Aquarius Project.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
% Operator declarations:
:- op( 700, xfx, [’\=’]).
:- op(1050, xfx, [’=>’]).
:- op( 500, yfx, [and,or,xor]).
split_integer :- compile_option(split_integer), !.
merge_add :- compile_option(merge_add), !.
write_once :- compile_option(write_once), !.
same_number :- compile_option(same_number_solutions), !.
same_order :- compile_option(same_order_solutions), !.
mips :- compile_option(mips), !.
vlsi_plm :- compile_option(vlsi_plm), !.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Piped compiler:
creator(C) :-
save(C, 1),
( pipe
; error(’Sorry old chap, the compiler has failed.’)
),
halt.
creator(_) :-
halt.
pipe_loop(end_of_file) :-
pop_incl_stack(F), !, seen, see(F),
read_expand(Cl), pipe_loop(Cl).
pipe_loop(end_of_file) :- !.
pipe_loop(Cl) :- pipe_loop_one(Cl, NextCl), asserta(save_clause(NextCl)), fail.
pipe_loop(_) :- retract(save_clause(NextCl)), pipe_loop(NextCl).
pipe_loop_one(Cl, NextCl) :-
collect_clauses(Cl, NextCl, Cls),
internal_comp(Cls), !.
read_expand(NextCl).
one_step((:-D), NextCl, Link, Link) :- !,
handle_dir(D),
read_expand(NextCl).
one_step(Cl, NextCl, [Cl|Cls], Link) :-
getname(Cl, NaAr),
read_clauses(NaAr, NextCl, Cls, Link).
getname(Clause, Name/Arity) :-
split(Clause, Head, _),
functor(Head, Name, Arity), !.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% All directives listed here are executed after all source transformation
% and dataflow analysis. They are executed interleaved with compile_stree.
% All directives not listed here are executed immediately upon reading them.
% Note that ’include’, ’op’, ’entry’, and ’mode’ must be executed immediately,
% as well as ’option(analyze)’, ’notoption(analyze)’.
after_collect((A,B)) :- !, after_collect(A), after_collect(B).
after_collect(D) :-
nonvar(D), D=..[DN|DA],
after_collect(DN, DA).
now_list([nomem,stats(_),analyze,analyze_uninit_reg,compile,comment,uni,
factor,test,test_unify,test_arith,test_typecheck,firstarg]).
% Print version:
print_version :- nl, print_copyright, print_date, nl.
pop_incl_stack(File) :-
include_stack([File|Files]),
my_retractall(include_stack(_)),
asserta(include_stack(Files)).
include_stack([]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Interactive compiler:
% Compile to a file:
cf(F, RawCls) :- cf(F, RawCls, []).
cf(F, RawCls, M) :- tell(F), comp(RawCls, M), !, told.
cf(_, _, _) :- told.
comp(RawCls, Modes) :-
make_list(Modes, ML),
comp(RawCls, ML, Code),
write_source(RawCls, ML),
write_code(Code).
compex([]).
compex([X|Examples]) :- ex(X), fail.
compex([_|Examples]) :- compex(Examples).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Internal compiler:
comp_steps(WFlag) -->
{stats(t,1)},
transform_cut_ptrees,
{stats(t,2)},
factor_ptrees,
{stats(t,3)},
ptrees_to_strees,
{stats(t,4)},
flatten_strees,
{stats(t,5)},
inline_strees,
316
{stats(t,6)},
analyze_strees,
{stats(t,7)},
compile_strees(WFlag),
{done_stats}, !.
print_stree(S) :- compile_option(print_stree), !,
write(’% ’), q_inst_writeq(S), wn(’.’).
print_stree(_).
compile_comment(NaAr) :- compile_option(compile), !,
nl, comment([’Compiling ’,NaAr,’ ...’]).
compile_comment(_).
builtin_warning(Stree),
stats(c,1),
trim_stree(Stree, TrimStree),
stats(c,2),
% Run-time selection without modes:
select_stree(TrimStree, SelStree, BList),
SelStree = stree(NaAr,(Head:-Disj),Mode,_,DList,_),
proc_header(HFlag, NaAr, Code, PCode),
% Procedure compilation:
stats(c,3),
segment_disj(Head, Disj, SegDisj, Mode, BodyList, BList),
stats(c,4),
selection_disj(Head, SegDisj, Mode, SelDisj),
stats(c,5),
proc_code(Head, SelDisj, Mode, DList, RFlag, UnoptCode, C2),
% Clause compilation:
stats(c,6),
clause_code_list(BodyList, DList, C2, C3),
% Peephole optimization:
stats(c,7),
peephole(NaAr, UnoptCode, PCode, PFlag),
stats(c,8),
!.
compile_stree(_, _, _, _, []).
% Handle HFlag:
proc_header(noheader, _) --> !.
proc_header(header, NaAr) --> [procedure(NaAr)].
% Handle RFlag:
% Dummies have a jump to this label instead of a return.
% end_label(return) --> [].
end_label(return) --> [return].
end_label(jump(Lbl)) --> [label(Lbl)].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clr_modal_entry :- my_retractall(modal_entry(_,_)).
check_modal_entry(H, T) :-
320
check_tree_entry(entry(E)) :- nonvar(E).
check_tree_entry(mode(F,True,False)) :-
nonvar(F), nonvar(True), nonvar(False),
check_tree_entry(True),
check_tree_entry(False).
% Add an external entry point and its mode to the global data base:
% An entry is a mode declaration with the additional task of being
% a starting point for flow analysis.
add_entry_option(entry(H,R,B,A,S)) :-
add_mode_option(mode(H,R,B,A,S)),
nox(entry(H,_)),
translate((R,B), TB),
flat_conj(TB, FB),
logical_simplify(FB, SB),
asserta(compile_option(entry(H,SB))).
add_entry_option(entry((H:-B))) :-
add_mode_option(mode((H:-B))),
nox(entry(H,_)),
translate(B, TB),
flat_conj(TB, FB),
logical_simplify(FB, SB),
asserta(compile_option(entry(H,SB))).
add_entry_option(entry(Name/Arity)) :-
atom(Name), nonnegative(Arity),
functor(H, Name, Arity),
add_mode_option(mode((H:-true))),
nox(entry(H,_)),
asserta(compile_option(entry(H,true))).
clr_entry :- my_retractall(compile_option(entry(_,_))).
add_mode_options([]) :- !.
add_mode_options([(H:-B)|ML]) :- !,
322
add_mode_option(mode((H:-B))),
add_mode_options(ML).
add_mode_options([true|ML]) :- !,
add_mode_options(ML).
add_mode_options([M|ML]) :-
add_mode_option(M),
add_mode_options(ML).
clr_mode :- my_retractall(mode_option(_,_,_,_,_)).
% Make sure that no modes are added for builtins (they would override
% the modes in the tables):
check_non_builtin(Head) :-
nonvar(Head),
functor(Head, N, A),
check_nb(Head,
[’Attempt to give modes for builtin ’,N/A,’ is ignored.’]).
% Add an option:
ox(Option) :- ox(Option, _).
ox(Option, true) :-
compile_option(Option), !.
ox(Option, false) :-
\+compile_option(Option),
pragma_write_once(Option),
asserta(compile_option(Option)).
% Remove an option:
nox(Option) :- nox(Option, _).
nox(Option, true) :-
copy(Option, Op),
compile_option(Op), !,
pragma_no_write_once(Option),
my_retractall(compile_option(Option)).
nox(Option, false) :-
\+compile_option(Option).
nox_list([]) :- !.
nox_list([Opt|Opts]) :- !, nox_list(Opt), nox_list(Opts).
323
my_retractall(C) :-
copy(C, D),
retract(D), !,
my_retractall(C).
my_retractall(_).
% Change/add an option:
% Add if it doesn’t exist, change if it does.
cox(Option) :- cox(Option, true).
cox(Option, true) :-
atomic(Option),
compile_option(Option), !.
cox(Option, Exists) :-
functor(Option, N, A),
functor(Dummy, N, A),
nox(Dummy, Exists), !,
ox(Option, _).
cox(Option, _) :-
ox(Option).
cox_list([]) :- !.
cox_list([Opt|Opts]) :- !, cox_list(Opt), cox_list(Opts).
cox_list(Opt) :- \+list(Opt), !, cox(Opt).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
324
add_macro_def(macro((H:-B))) :- check_macro_def(H), !,
my_retractall(macro(H,_,_,_,_,_)),
conjlist(B, BL, []),
remove_double_indirect(H, NewH, NewBL, BL),
varset(NewBL, Vars),
difflist(NewBL, Code, Link),
macro_varlist(NewBL, Varlist, Varlink),
asserta(macro(NewH,Code,Link,Varlist,Varlink,Vars)).
add_macro_def(macro(_)).
check_macro_def(Head) :-
nonvar(Head),
functor(Head, N, A),
check_nb(Head,
[’Attempt to give macro definition for builtin ’,N/A,’ is ignored.’]).
% Correctness of this varlist generation depends on the fact that for all
% instructions the output is written rightmost. This means that a varbag
% that keeps the order is correct.
macro_varlist([move(A,B)|List]) --> {var(A),var(B)}, !,
[pref,A,B],
macro_varlist(List).
325
macro_varlist([label(_)|List]) --> !,
macro_varlist(List).
macro_varlist([I|List]) --> {branch(I,Lbls)}, !,
{varbag(I,Vars)},
{diffbag(Vars, Lbls, Ls)},
difflist(Ls),
macro_varlist(List).
macro_varlist([I|List]) -->
varbag(I),
macro_varlist(List).
macro_varlist([]) --> [].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
326
expression.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
eval_args(I, N, _, _) :- I>N, !.
eval_args(I, N, Expr, EExpr) :- I=<N, !,
arg(I, Expr, E),
arg(I, EExpr, EE),
arith_eval(E, EE),
I1 is I+1,
eval_args(I1, N, Expr, EExpr).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
328
factor.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
% This module may need other heuristics to avoid superfluous choice point
% creation which may slow down execution in some cases. For example, the
% savings of multiple structure creation (how many structures are saved)
% can be weighed against the cost of creating and using the extra choice point.
% What’s inside the MSG’s can also be taken into account. If they’re all
% variables then deterministic selection may not be able to do much, but if
% they’re all different nonvariable terms then much can be done.
% tests beyond the MSG. Then some determinism selection will work
% for them.
% Possible heuristic: don’t factor if it’s not needed to get determinism.
% How to approximate this in practice?
% Possible heuristic: don’t factor if there are ’enough’ tests in the clauses.
% Notes:
% 1. The call graph of this module is highly cyclic, but clean. Termination
% is somewhat subtle to show.
% 2. The ’match_...’ predicates are also used elsewhere. They may be better
% off as utilities.
% 3. The transfer of modes from a predicate to its dummies is done in a naive
% way: the same formula is used. A later version should look inside
% structures and lists and infer modes about them.
% 4. Atomic head arguments are NOT factored out, since this does NOT speed
% up execution.
% 4. This module should be used before standardization is done.
% 5. Can do dereference factoring: know that arguments are dereferenced.
% Convention that all args upon entry are always dereferenced.
% 6. This module increases the average arity of predicates.
factor_dlist(ptree(NaAr,Cls,M,D), ptree(NaAr,Cls,M,ND)) :-
factor_ptrees(D, ND).
% Given the arguments, factor out all Args of a Ptree, and get a new Ptree:
% The DList is unchanged.
factor_args_ptree([], P, P).
factor_args_ptree([N|Args], ptree(NaAr,Cls,M,D), ptree(NaAr,NCls,M,ND)) :-
factor_arg_cls(NaAr, N, Cls, NCls, M, Ptrees, []),
factor_args_ptrees(Args, Ptrees, ND, D).
% Given the arguments, factor a set of Ptrees, and get a set of new Ptrees:
factor_args_ptrees(_, [], Link, Link).
factor_args_ptrees(Args, [P|Ptrees], [NP|NPtrees], Link) :-
factor_args_ptree(Args, P, NP),
factor_args_ptrees(Args, Ptrees, NPtrees, Link).
solution_order(Args, OrderArgs),
collect_info(NaAr, OrderArgs, Info, MsgList),
cons(Info),
check_heuristic(MsgList),
!,
transform(Cls, 1, N, Info, NewCls, DummyProcs),
keysort(DummyProcs, SortProcs),
collect_ptrees(SortProcs, N, Mode, Ptrees, Link).
factor_arg_cls(NaAr, N, Cls, Cls, Mode, Link, Link).
% \+check_arity(Cls, N), !.
% Succeed (& then factor the argument) only if no non-adjacent heads have
% a non-trivial MSG:
check_heuristic(MsgList) :-
keysort(MsgList, Sort),
\+adjacent_nontrivial(Sort).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
331
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% First collect adjacent arguments into a list Msgs with terms Msg-ClauseNums
% while the msg is not a variable or atomic. Expand this list into clause order
% again and attach a dummy name to each clause index. This name will be used
% for the dummy procedures to be generated. Each element in Info is of the
% form:
% ClauseNum-info(Msg,Dummy,Varnum,Rank)
% where:
% Msg = the most-specific-generalization common to the ClauseNums
% Dummy = the name to be used for the dummy predicate
% Varnum = the number of variables in the Msg
% Rank = ’first’ for only one ClauseNum element of a given Msg
collect_msg([], []).
collect_msg([Arg-I|Args], Collect) :-
collect_msg(Args, Arg, [I], Collect).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Alternate definition:
% match_most(I, N, A, Head, DHead) :-
% Nl is N-1,
% Nr is N+1,
% match_all(I, Nl, Head, DHead),
% match_all(Nr, A, Head, DHead).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
msg(A, B, G) :-
nonvar(A), nonvar(B), !,
(functor(A, Na, Ar),
functor(B, Na, Ar)
-> A=..[Na|AArgs],
B=..[Na|BArgs],
msg_args(AArgs, BArgs, GArgs),
G=..[Na|GArgs]
; true
).
msg(A, B, A) :- A==B, !.
msg(A, B, _) :- \+(A==B), !.
337
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
338
flatten.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
% Flattening transformation.
% Remove all disjunctions from a predicate.
% Notes:
% 1. Modify new head generation to minimize number of moves.
% 2. Flatten passes mode information into the dummy predicates.
% 3. Flatten provides additional Mode information for the dummy procedures
% it creates. This is implemented by
% collecting all the goals traversed in the disjunction and adding them
% to the modes formula.
% Question: is this really needed? In clause_code,
% a formula will be given that subsumes this, right?
% 4. Depending on the style of transformation, a single clause could result in
% multiple clauses. This is an improvement for later.
% a:-(b;c),d results in multiple clauses, a:-d,(b;c) does not.
% Convert an stree into a flattened stree, i.e. all disjunctions are removed
% from the component procedures and dummy strees are created for them.
% All output strees are in standard form with independent variables.
% The input must be an stree in standard form.
flatten_strees([], []).
flatten_strees([S|Strees], [FS|FStrees]) :-
flatten_stree(S, FS),
flatten_strees(Strees, FStrees).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
341
inline.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
inline_strees(Ss, Rs) :-
compile_option(inline), !,
gather_single(Ss, SA),
inline_replace_strees(Ss, Rs, SA).
inline_strees(Ss, Ss) :-
\+compile_option(inline), !.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
343
mutex.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
% Implication:
% Logical implication
implies(A, B) :- logical_simplify(not(B), NB), mutex(A, NB, left), !.
implies(A, B, Time) :- logical_simplify(not(B), NB), mutex(A, NB, Time), !.
% Prolog implication:
% (A and B are Prolog formulas)
prolog_implies(A, B) :- simplify(not(B), NB), prolog_mutex(A, NB, left), !.
prolog_implies(A, B, Time) :- simplify(not(B), NB), prolog_mutex(A, NB, Time),!.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% If Time=before:
% If the goal mutex(S1, S2) succeeds then there does not exist an
% assignment of values to variables which causes both S1 and S2
% to succeed separately. The values after S1 and after S2 do not have
% to be consistent.
% (problem with encode_relop and (A=B) & (A is B) which both bind)
% If Time=after:
% If the goal mutex(S1, S2) succeeds then there does not exist an
% assignment of values to variables which exists after both S1 and S2
% have succeeded.
% Time=left:
% There does not exist an assignment of values to variables which exists
% after (S1,S2) has successfully executed.
% Time=right:
% There does not exist an assignment of values to variables which exists
% after (S2,S1) has successfully executed.
344
% Notes:
% 1. Two important definitions related to uninitialized variables which are
% recognized here:
% unbound(X) == ( uninit(mem,X) ; uninit(reg,X) ; var(X) )
% uninit(any,X) == ( uninit(mem,X) ; uninit(reg,X) )
% uninit(either,X) --> uninit(mem,X)
% uninit(either,X) --> uninit(reg,X)
% 2. Mutex is defined as a determinate version of mutex2, which does the work.
% 3. For best results the input to mutex should sometimes be simplified before
% the call. I.e. var(x(_)) will simplify to fail, which is mutex to
% anything.
% 4. Relational operators as tests could be assumed to require
% numeric operands, even if they are negated. not(A<3) means A>=3, so
% A must be a number for success. (This point is not completely clear:
% not(A<3) could also succeed for nonnumeric A. Also, A could be
% evaluated before doing the test, which is what C-Prolog does.)
% 5. Mutual exclusion can exist even between predicates that do binding,
% e.g. A=a and A=b are mutually exclusive.
% Possible extensions:
% 1. Incorporation of user-defined predicates by means of a predicate/2
% clause, i.e. predicate(P, Def) succeeds if P is a predicate and Def
% its definition (considered as a disjunction). Watch out for infinite
% loops and binding!
% 2. For tests, more info such as values of small ints or arities of strucs
% can be kept in the bit map to make mutex more powerful.
% A whole data structure can be attached to the bit map to keep
% sets of possible values around for even more power. Is this a useful
% thing to do? In general, a data structure can subsume the
% present distinction between relational operators and tests.
mutex(A, B, T, L) :- compile_option(mutex_comment), !,
comment([mutex(A,B,T,L)]),
mutex2(A, B, T, L), !.
mutex(A, B, T, L) :- \+compile_option(mutex_comment), !,
mutex2(A, B, T, L), !.
345
u_implies_goal(unbound(W), W).
u_implies_goal(uninit(any,W), W).
u_implies_goal(deref(W), W).
u_implies_goal(rderef(W), W).
u_implies_goal(trail(W), W).
u_implies_goal(trail_if_var(W), W).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
encode_relop(A,B,C,D) :- encode_relop(A,B,C,D,K).
% Bit reversal:
flip(N, I) :- I is (N/\2’001)<<2 \/ (N/\2’010) \/ (N/\2’100)>>2.
% Opposite condition:
negate_relop(Order, OppOrder) :- OppOrder is \(Order) /\ 2’111.
and_list([], N, N).
and_list([X|L], I, N) :- I1 is X /\ I, and_list(L, I1, N).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Get the bitmap test or bitmap type of a variable implied by a mode formula:
get_type_test(InF, X, ’$test’(X,B)) :- get_type(InF, X, 2’11111111, B).
% Get the tag of X implied by a mode formula. Fail if there is none such.
get_tag(InF, X, Tag) :-
get_type(InF, X, 2’11111111, B),
bitmap_type(B, Type),
tag(Type, Tag).
and_bits(A, B, C) :- C is A /\ B.
% ! Watch out: often, converting from a regular test into a bitmap test
% broadens the set of values that can succeed. The bitmap test is a necessary
% but not sufficient replacement for the regular test.
conj_test(T1, T2, ’$test’(X,B)) :-
encode_test(T1, B1, _, X, _),
encode_test(T2, B2, _, Y, _),
X==Y, !,
B is B1 /\ B2.
disj_test(T1, T2, ’$test’(X,B)) :-
encode_test(T1, B1, _, X, _),
encode_test(T2, B2, _, Y, _),
X==Y, !,
B is B1 \/ B2.
not_test(T1, ’$test’(X,B)) :- encode_test(T1, _, B, X, _), !.
bitmap_test(T1, ’$test’(X,B)) :- encode_test(T1, B, _, X, _), !.
bitmap_name_arity(2’00000010, _, ’.’, 2) :- !.
bitmap_name_arity(2’00100000, _, [], 0) :- !.
bitmap_name_arity(B, X, X, 0) :- B=\=0, B /\ 2’10000111 =:= 0.
bitmap_to_disj(0, _, _) --> !.
bitmap_to_disj(B, AllB, X) --> {B=\=0},
{exact_bitmap(Test, X, B1)},
{\+complex_bitmap(Test)}, % Check that Test is easy to implement.
{0 =:= (B1 /\ \(AllB))}, % Check that B1 is a subset of the original B.
{NewB is B /\ \(B1)}, % Subtract the bits B1 from the current B.
{NewB =\= B}, % Check that it had some effect.
!,
di(Test),
bitmap_to_disj(NewB, AllB, X).
bitmap_to_disj(B, _, X) -->
{error([’Could not convert bitmap ’’’,B,’’’ to a disjunction.’,nl,
’Replacing by fail.’])}.
exact_bitmap(nonvar(A), A, 2’01111111).
exact_bitmap(atom(A), A, 2’01100000).
exact_bitmap(var(A), A, 2’10000000).
exact_bitmap(cons(A), A, 2’00000010).
exact_bitmap(structure(A), A, 2’00000001).
exact_bitmap(nil(A), A, 2’00100000).
exact_bitmap(A==B, A, 2’00100000) :- B==[].
exact_bitmap(B==A, A, 2’00100000) :- B==[].
exact_bitmap(negative(A), A, 2’00001000).
exact_bitmap(nonnegative(A), A, 2’00010000).
exact_bitmap(float(A), A, 2’00000100).
exact_bitmap(simple(A), A, 2’11111100).
exact_bitmap(compound(A), A, 2’00000011).
353
exact_bitmap(list(A), A, 2’00100010).
exact_bitmap(atomic(A), A, 2’01111100).
exact_bitmap(number(A), A, 2’00011100).
exact_bitmap(integer(A), A, 2’00011000).
bitmap_type(2’10000000, var).
bitmap_type(2’00000010, cons).
bitmap_type(2’00000001, structure).
bitmap_type(2’01100000, atom).
bitmap_type(2’00100000, atom).
bitmap_type(2’01000000, atom).
bitmap_type(2’00001000, negative) :- split_integer.
bitmap_type(2’00010000, nonnegative) :- split_integer.
bitmap_type(2’00011000, integer) :- \+split_integer.
bitmap_type(2’00001000, integer) :- \+split_integer.
bitmap_type(2’00010000, integer) :- \+split_integer.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
ground_bag((A;B)) --> !,
{ground_set(A, NA)},
{ground_set(B, NB)},
{intersectv(NA, NB, N)},
difflist(N).
ground_bag((A,B)) --> !, ground_bag(A), ground_bag(B).
ground_bag(Test) --> {encode_relop(Test, X, _, Y, arith)}, !, [X,Y].
ground_bag(Test) --> {encode_test(Test, F, _, X, _), 0=:=F/\2’10000011}, !, [X].
ground_bag(ground(X)) --> !, [X].
ground_bag(_) --> [].
nonvar_bag((A;B)) --> !,
{nonvar_set(A, NA)},
{nonvar_set(B, NB)},
{intersectv(NA, NB, N)},
difflist(N).
nonvar_bag((A,B)) --> !, nonvar_bag(A), nonvar_bag(B).
nonvar_bag(Test) --> {encode_test(Test, F, _, X, _), 0=:=F/\2’10000000}, !, [X].
nonvar_bag(_) --> [].
deref_bag((A;B)) --> !,
{deref_set(A, NA)},
{deref_set(B, NB)},
{intersectv(NA, NB, N)},
difflist(N).
deref_bag((A,B)) --> !, deref_bag(A), deref_bag(B).
deref_bag(rderef(X)) --> !, [X].
deref_bag(deref(X)) --> !, [X].
deref_bag(_) --> [].
rderef_bag((A;B)) --> !,
{rderef_set(A, NA)},
{rderef_set(B, NB)},
{intersectv(NA, NB, N)},
difflist(N).
rderef_bag((A,B)) --> !, rderef_bag(A), rderef_bag(B).
rderef_bag(rderef(X)) --> !, [X].
rderef_bag(_) --> [].
atomic_bag((A;B)) --> !,
{atomic_set(A, NA)},
355
{atomic_set(B, NB)},
{intersectv(NA, NB, N)},
difflist(N).
atomic_bag((A,B)) --> !, atomic_bag(A), atomic_bag(B).
atomic_bag(T) --> {encode_relop(T, X, _, Y, arith)}, !, [X,Y].
atomic_bag(T) --> {encode_test(T, F, _, X, _), 0 =:= F /\ 2’10000011}, !, [X].
atomic_bag(_) --> [].
simple_bag((A;B)) --> !,
{simple_set(A, NA)},
{simple_set(B, NB)},
{intersectv(NA, NB, N)},
difflist(N).
simple_bag((A,B)) --> !, simple_bag(A), simple_bag(B).
simple_bag(T) --> {encode_relop(T, X, _, Y, arith)}, !, [X,Y].
simple_bag(T) --> {encode_test(T, F, _, X, _), 0 =:= F /\ 2’00000011}, !, [X].
simple_bag(_) --> [].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% *** Returns the subset of all possible terms that a term has AFTER
% a successful test, and the subset of terms that lead to failure.
% Notes:
% 1. The _freeze versions delay execution until A is nonvariable.
% They have a logical meaning. The others have no logical
% meaning since they depend on the var/nonvar instantiation
% of a term.
% 2. Should a float(A) test be distinguished from noninteger_real?
% Currently it is not.
% 3. This predicate assumes that the success of arithmetic relational
% operations implies that the operands are integers.
encode_test(A,B,C,D) :- encode_test(A,B,C,D,_).
compound(A).
encode_test(functor(_,A,_), 2’01111100, 2’11111111, A, y).
encode_test(functor(_,_,A), 2’00010000, 2’11111111, A, y).
encode_test(A=..X, 2’01111111, 2’11111111, A, y).
encode_test(X=..A, 2’00000010, 2’11111111, A, y).
encode_test(var(A), 2’10000000, 2’01111111, A, n).
encode_test(nonvar(A), 2’01111111, 2’10000000, A, n).
encode_test(ground(A), 2’01111111, 2’10000011, A, n).
encode_test(atom(A), 2’01100000, 2’10011111, A, n).
encode_test(nil(A), 2’00100000, 2’11011111, A, n).
encode_test(integer(A), 2’00011000, 2’11100111, A, n).
encode_test(negative(A), 2’00001000, 2’11110111, A, n).
encode_test(nonnegative(A), 2’00010000, 2’11101111, A, n).
encode_test(number(A), 2’00011100, 2’11100011, A, n).
encode_test(float(A), 2’00000100, 2’11111011, A, n).
encode_test(atomic(A), 2’01111100, 2’10000011, A, n).
encode_test(list(A), 2’00100010, 2’11011101, A, n).
encode_test(cons(A), 2’00000010, 2’11111101, A, n).
encode_test(structure(A), 2’00000001, 2’11111110, A, n).
encode_test(compound(A), 2’00000011, 2’11111100, A, n).
% encode_test(composite(A), 2’00000011, 2’11111100, A, n).
encode_test(simple(A), 2’11111100, 2’00000011, A, n).
encode_test(var_freeze(A), 2’00000000, 2’01111111, A, n).
encode_test(nonvar_freeze(A), 2’01111111, 2’00000000, A, n).
encode_test(ground_freeze(A), 2’01111111, 2’00000011, A, n).
encode_test(atom_freeze(A), 2’01100000, 2’00011111, A, n).
encode_test(nil_freeze(A), 2’00100000, 2’01011111, A, n).
encode_test(integer_freeze(A), 2’00011000, 2’01100111, A, n).
encode_test(negative_freeze(A), 2’00001000, 2’01110111, A, n).
encode_test(nonnegative_freeze(A), 2’00010000, 2’01101111, A, n).
encode_test(number_freeze(A), 2’00011100, 2’01100011, A, n).
encode_test(atomic_freeze(A), 2’01111100, 2’00000011, A, n).
encode_test(list_freeze(A), 2’00100010, 2’01011101, A, n).
encode_test(cons_freeze(A), 2’00000010, 2’01111101, A, n).
encode_test(structure_freeze(A), 2’00000001, 2’01111110, A, n).
encode_test(compound_freeze(A), 2’00000011, 2’01111100, A, n).
encode_test(composite_freeze(A), 2’00000011, 2’01111100, A, n).
encode_test(simple_freeze(A), 2’11111100, 2’00000011, A, n).
encode_test(A is _, 2’00011100, 2’01111111, A, y).
encode_test(Rel, S, I, A, n) :- sign_flags(Rel, S, I, A).
encode_test(Rel, 2’00011000, 2’00011000, A, n) :-
\+sign_flags(Rel, _, _, _), arith_test(Rel, A, _).
encode_test(Rel, 2’00011000, 2’00011000, A, n) :-
\+sign_flags(Rel, _, _, _), arith_test(Rel, _, A).
encode_test(A=X, T, I, A, y) :- type_flags(X, S, I), T is 2’00000000\/S.
encode_test(X=A, T, I, A, y) :- type_flags(X, S, I), T is 2’00000000\/S.
encode_test(A\=X, S, F, A, n) :- type_flags(X, I, S), F is 2’10000000\/I.
encode_test(X\=A, S, F, A, n) :- type_flags(X, I, S), F is 2’10000000\/I.
encode_test(A==X, S, F, A, n) :- type_flags(X, S, I), F is 2’10000000\/I.
encode_test(X==A, S, F, A, n) :- type_flags(X, S, I), F is 2’10000000\/I.
encode_test(A\==X, T, I, A, n) :- type_flags(X, I, S), T is 2’10000000\/S.
encode_test(X\==A, T, I, A, n) :- type_flags(X, I, S), T is 2’10000000\/S.
encode_test(not(G), S, I, A, T) :- nonvar(G), encode_test(G, I, S, A, T).
encode_test( \+(G), S, I, A, n) :- nonvar(G), encode_test(G, I, S, A, _).
358
% These formulas take into account what happens when the predicates
% can abort on a type not listed in the success & fail flags:
% (This occurs for relational operations)
left_overlap(S1, F1, S2, F2) :- 0 =\= S1 /\ (S2 \/ \(S2 \/ F2)).
right_overlap(S1, F1, S2, F2) :- 0 =\= S2 /\ (S1 \/ \(S1 \/ F1)).
359
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
encode_name_arity(’$test’(X,B), Y, F, A, true) :-
bitmap_name_arity(B, Y, F, A).
encode_name_arity(X=..Y, Y, ’.’, 2, true).
encode_name_arity( X=Y, X, F, A, true) :- nonvar(Y), functor(Y, F, A).
encode_name_arity( X=Y, Y, F, A, true) :- nonvar(X), functor(X, F, A).
encode_name_arity( X==Y, X, F, A, true) :- nonvar(Y), functor(Y, F, A).
encode_name_arity( X==Y, Y, F, A, true) :- nonvar(X), functor(X, F, A).
encode_name_arity(X=:=Y, X, F, A, true) :- integer(Y), functor(Y, F, A).
encode_name_arity(X=:=Y, Y, F, A, true) :- integer(X), functor(X, F, A).
% These are wrong:
% encode_name_arity( X\=Y, X, F, A, false) :- nonvar(Y), functor(Y, F, A).
% encode_name_arity( X\=Y, Y, F, A, false) :- nonvar(X), functor(X, F, A).
encode_name_arity(X=\=Y, X, not(F), not(A), false) :- integer(Y),functor(Y,F,A).
encode_name_arity(X=\=Y, Y, not(F), not(A), false) :- integer(X),functor(X,F,A).
encode_name_arity(functor(X,F,A), X, F, A, true).
encode_name_arity(functor(X,F,A), F, F, 0, true) :- var(X).
encode_name_arity(functor(X,F,A), F, G, 0, true) :- nonvar(X), functor(X, G, _).
encode_name_arity(functor(X,F,A), A, A, 0, true) :- var(X).
encode_name_arity(functor(X,F,A), A, B, 0, true) :- nonvar(X), functor(X, _, B).
encode_name_arity(’$name_arity’(X,F,A), X, F, A, true).
encode_name_arity(atom(X), X, X, 0, true).
encode_name_arity(nil(X), X, [], 0, true).
encode_name_arity(integer(X), X, X, 0, true).
encode_name_arity(negative(X), X, X, 0, true).
encode_name_arity(nonnegative(X), X, X, 0, true).
encode_name_arity(number(X), X, X, 0, true).
encode_name_arity(atomic(X), X, X, 0, true).
encode_name_arity(cons(X), X, ’.’, 2, true).
encode_name_arity(not(P), X, NotF, NotA, NotJ) :-
nonvar(P),
\+invalid_negation(P),
encode_name_arity(P, X, F, A, J),
negate(F, NotF),
negate(A, NotA),
negate_boolean(J, NotJ).
% Negating these predicates does not imply anything about the main functor
% since the failure could be due to an argument.
invalid_negation(X=..Y).
invalid_negation(X=Y).
invalid_negation(X\=Y).
invalid_negation(X==Y).
invalid_negation(X\==Y).
360
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
361
peephole.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
% Low-level optimizations:
% Notes:
% 1. A transitive closure is done on all the intermediate peephole steps.
% 2. The sequence move(r(1),r(7)) ... move(r(7),r(2)) can be optimized in two
% steps: (1) replace second r(7) by r(1) as above, (2) see that r(7) is
% never read, so remove it. This second part requires a backward pass.
% 3. Order of the peephole optimizations is important. For example,
% peep_inst must be done before instantiation of variables.
% 4. Don’t repeat dereferences, i.e. remember when something has been derefed.
% This is similar to synonym.
% 5. Propagate instruction sequences that are common between all destinations
% of a branch to before the branch if they don’t affect the branch outcome.
% Do this BEFORE dead code elimination.
% For a choice-block must look at ALL choices.
% 6. Propagate instruction sequences that are only used in one direction of a
% branch to inside the branch. This occurs fairly often in if-then-elses.
% 7. Coalesce identical basic blocks. How often are basic blocks slightly
% different in form, but identical in semantics?
% 8. Even if the gain is very small, patterns that achieve it should be put in.
% Philosophy: add patterns that I’ve seen in real code.
xpeephole(NaAr) -->
write_debug(’peephole started’),
stats(p,1),
peep_flat, % Remove nesting of switch(unify)
stats(p,2),
inst_labl(NaAr), % Instantiate labels.
stats(p,3), % At this point the code is ground.
peep_simp, % First optimization (to reduce code size).
% Doing this reduces the number of duplicate basic blocks in prover.pl:
% peep_inst(nocall),
stats(p,4),
peep_closure(NaAr), % Closure on all peephole optimizations.
stats(p,5),
!.
362
inst_labl_list([], _).
inst_labl_list([l(NA,I)|Vars], NA) :- gennum(I), inst_labl_list(Vars, NA).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Replace all duplicate basic blocks except the last one by a jump
% to the last one. This respects the rule that all jumps go forward.
peep_uniq(Code, UCode) :-
make_large_blocks(Code, Blocks),
reverse(Blocks, Rev),
make_unique_array(Rev, UA, 0, F),
363
% Make an array indexed by basic block that contains one entry per
% unique block. The last blocks are inserted first. Also count the
% number of duplicated basic blocks.
make_unique_array([], UA, InF, InF) :- seal(UA).
make_unique_array([L-B|Blocks], UA, InF, OutF) :-
get(UA, B, X), !,
(var(X) -> X=L, MidF=InF; MidF is InF+1),
make_unique_array(Blocks, UA, MidF, OutF).
make_unique_array([_|Blocks], UA, InF, OutF) :-
make_unique_array(Blocks, UA, InF, OutF).
% Replace all duplicated blocks in the code by jumps to the last occurrence.
% No other change is done.
map_blocks([], _, []).
map_blocks([label(L1)|Code], UA, UCode) :-
first_block(Code, B, Rest),
fget(UA, B, L2),
L1\==L2, !,
UCode=[label(L1),jump(L2)|UC],
map_blocks(Rest, UA, UC).
map_blocks([Branch|Code], UA, UCode) :-
branch(Branch), \+distant_branch(Branch),
\+Code=[label(_)|_],
first_block(Code, B, Rest),
fget(UA, B, L), !,
comment([’Able to remove a block after ’,Branch]),
UCode=[Branch,jump(L)|UC],
map_blocks(Rest, UA, UC).
map_blocks([I|Code], UA, [I|UCode]) :-
map_blocks(Code, UA, UCode).
% Collect all blocks that are large enough to make it worthwhile to remove
% the duplicates:
% Don’t collect any blocks that have labels in them; they will almost surely
% not have any duplicates worth collecting. Also, collecting these blocks
% will make the size of stuff that bagof collects go up as O(codesizeˆ2) for
% long stretches of nonbranching code with many labels. This causes bagof
% to choke if there are 100’s of instructions.
make_large_blocks([], []).
make_large_blocks([label(L)|Code], [L-Block|Blocks]) :-
first_block(Code, Block),
\+has_label(Block),
\+small_block(Block),
!,
make_large_blocks(Code, Blocks).
make_large_blocks([_|Code], Blocks) :- make_large_blocks(Code, Blocks).
364
small_block([jump(_)]).
small_block([return]).
small_block([fail]).
has_label([label(_)|_]).
has_label([_|Code]) :- has_label(Code).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Definition of basic block: any block of code starting with a label and
% ending with a distant branch and that contains no distant branches except
% for the last instruction.
% Notes:
% 1. What about jumps that go nowhere (as in unify optimization)?
non_empty_array(BArray), !,
get_uniq_labels(Code, UArray),
rearr_jump(Code, NA, UArray, BArray, JCode).
peep_jump(_, Code, Code).
get_label_bag([]) --> !.
get_label_bag([B|Code]) --> branch(B), !, get_label_bag(Code).
get_label_bag([_|Code]) --> get_label_bag(Code).
% which call this routine because otherwise the size of the data
% that is collected is O(codesizeˆ2) which blows up for long stretches
% of nonbranching code (100’s of instructions) with many labels.
% basic_block([label(L)|Code], [label(L)|Code]).
basic_block(Block, Block) :- Block=[label(_)|_].
basic_block([_|Code], Block) :- basic_block(Code, Block).
get_labels([]) --> !.
get_labels([label(L)|Block]) --> !, [L], get_labels(Block).
get_labels([_|Block]) --> get_labels(Block).
assoc_labels([], []) :- !.
assoc_labels([_|L], [_|A]) :- assoc_labels(L, A).
replace_labels([], _, _) --> !.
replace_labels([I|Block], Lbls, ALbls) -->
replace_labels_one(I, Lbls, ALbls),
replace_labels(Block, Lbls, ALbls).
% *** Succeeds if the block returns or fails and remains straight-line code.
% (It removes the return, but keeps the fail.)
returns_or_fails(Block, Bl) :- returns_or_fails(Block, Bl, []).
returns_or_fails(Block).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Notes:
% 1. This code needs to know all the instructions that can branch.
% 2. This version does not remove any code inside of a switch instruction
% generated by unification code. This is OK since such a switch is
% self-contained.
% 3. Speed = O( (number of labels) * (code size) ). This can be reduced
% to log(number of labels) if profiling shows it to be necessary.
peep_dead(Code, DCode) :-
gather_closure(Code, Lbls),
peep_dead(Code, Lbls, DCode).
peep_dead([], _, []).
peep_dead([Instr|Code], Lbls, [Instr|DCode]) :- distant_branch(Instr), !,
to_next_label(Code, Lbls, MCode),
peep_dead(MCode, Lbls, DCode).
peep_dead([Instr|Code], Lbls, [Instr|DCode]) :-
peep_dead(Code, Lbls, DCode).
% Do closure calculation to find all labels that are reached through any
% number of jumps in succession:
gather_closure(Code, Lbls) :- gather_closure(Code, [], Lbls).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
peep_labl(Code, LCode) :-
branch_dest_bag(Code, Labels),
list_to_key(Labels, KeyLabels),
create_array(KeyLabels, LArray),
remove_label_inst(Code, LArray, LCode).
remove_label_inst([], _, []).
remove_label_inst([Instr|Code], LArray, LCode) :-
((Instr=label(Lbl), \+fget(LArray, Lbl, _))
-> LCode=MCode
; LCode=[Instr|MCode]
),
remove_label_inst(Code, LArray, MCode).
branch_dest_bag([], []).
branch_dest_bag([Instr|Code], Labels) :-
(branch(Instr, Labels, L)
-> branch_dest_bag(Code, L)
; branch_dest_bag(Code, Labels)
).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Added basic block array to make it possible to follow jumps & calls.
% This is a first step towards more complex propagation optimizations.
% Single instruction:
peep1(simple_call(’$sll’/2)) --> {vlsi_plm}, [sll].
peep1(simple_call(’$sra’/2)) --> {vlsi_plm}, [sra].
peep1(deref(X,X)) --> {\+write_once}, [deref(X)].
peep1(move(X,Y)) -->
{X\==Y},
{s_oper(X, PX), s_oper(Y, PY)},
[move(PX,PY)].
peep1(equal(X,Y,L)) -->
{s_oper(X, PX), s_oper(Y, PY)},
[equal(PX,PY,L)].
peep1(unify(X,Y,Tx,Ty,L)) -->
{s_oper(X, PX), s_oper(Y, PY)},
[unify(PX,PY,Tx,Ty,L)].
peep1(switch(unify,T,A,L1,L2,F)) -->
{peep_inst(L1, P1), peep_inst(L2, P2)},
[switch(unify,T,A,P1,P2,F)].
peep1(jump(fail)) -->
[fail].
peep1(switch(_,_,fail,fail,fail)) -->
[fail].
peep1(switch(_,_,L,L,L)) --> {L\==fail},
372
[jump(L)].
peep1(switch(_,R,fail,L,L)) -->
[test(ne,Tvar,R,L),fail], {tag(var,Tvar)}.
peep1(switch(_,R,L,fail,fail)) -->
[test(eq,Tvar,R,L),fail], {tag(var,Tvar)}.
peep1(switch(T,R,fail,L,fail)) -->
[test(eq,T,R,L),fail].
peep1(switch(T,R,L,fail,L)) -->
[test(ne,T,R,L),fail].
peep1(pad(M)) --> {align(A), K is M mod A, K<M, K=\=0}, [pad(K)].
peep1(jump(eq,R,R,fail)) --> [fail].
peep1(jump_nt(eq,R,R,fail)) --> [fail].
% Need to extend this eventually to handle overflows:
peep1(add(I,J,R)) --> {integer(I), integer(J), K is I+J}, [move(K,R)].
peep1(sub(I,J,R)) --> {integer(I), integer(J), K is I-J}, [move(K,R)].
peep1(mul(I,J,R)) --> {integer(I), integer(J), K is I*J}, [move(K,R)].
peep1(div(I,J,R)) --> {integer(I), integer(J), K is I//J}, [move(K,R)].
peep1(and(I,J,R)) --> {integer(I), integer(J), K is I /\ J}, [move(K,R)].
peep1( or(I,J,R)) --> {integer(I), integer(J), K is I \/ J}, [move(K,R)].
peep1(xor(I,J,R)) --> {integer(I), integer(J), xor(I, J, K)}, [move(K,R)].
% peep1( not(I,R)) --> {integer(I), integer(J), K is \(I)}, [move(K,R)].
peep1(sll(I,J,R)) --> {integer(I), integer(J), K is I<<J}, [move(K,R)].
peep1(sra(I,J,R)) --> {integer(I), integer(J), K is I>>J}, [move(K,R)].
% User instructions: (for macro definitions)
peep1(add_nt(I,J,R)) --> {integer(I), integer(J), K is I+J}, [move(K,R)].
peep1(sub_nt(I,J,R)) --> {integer(I), integer(J), K is I-J}, [move(K,R)].
peep1(and_nt(I,J,R)) --> {integer(I), integer(J), K is I /\ J}, [move(K,R)].
peep1( or_nt(I,J,R)) --> {integer(I), integer(J), K is I \/ J}, [move(K,R)].
peep1(xor_nt(I,J,R)) --> {integer(I), integer(J), xor(I, J, K)}, [move(K,R)].
peep1(sll_nt(I,J,R)) --> {integer(I), integer(J), K is I<<J}, [move(K,R)].
peep1(sra_nt(I,J,R)) --> {integer(I), integer(J), K is I>>J}, [move(K,R)].
% Two instructions:
peep2(test(Eq,T,R,fail), jump(L)) --> {lbl(L)},
[test(Ne,T,R,L),fail], {eq_ne(Eq,Ne)}.
peep2(move(A,B), move(B,C)) --> {perm(B), reg(C)},
[move(A,C),move(C,B)].
peep2(deref(A,B), move(B,C)) --> {perm(B), reg(C)},
[deref(A,C),move(C,B)].
peep2(deref(A,B), deref(B,C)) -->
[deref(A,B),move(B,C)].
peep2(move(C,P), move(C,R)) --> {reg(R), perm(P), \+reg(C), \+is_in(P,C)},
[move(C,R),move(R,P)].
peep2(move(C,I), move(C,R)) --> {reg(R), ind(I), \+reg(C), \+is_in(R,I)},
[move(C,R),move(R,I)].
peep2(move(R,I), move(I,C)) --> {reg(R), \+reg(C), ind(I)},
[move(R,I),move(R,C)].
peep2(jump(Cond,R,S,fail), jump(L)) --> {cond(Cond,Inv), lbl(L)},
374
[jump(Inv,R,S,L),fail].
peep2(jump_nt(Cond,R,S,fail), jump(L)) --> {cond(Cond,Inv), lbl(L)},
[jump_nt(Inv,R,S,L),fail].
% peep2(jump(TF,fail), jump(L)) --> {tf_ft(TF,FT)},
% [jump(FT,L),fail].
peep2(switch(T,R,fail,L,L), label(L)) -->
[test(eq,Tvar,R,fail),label(L)], {tag(var,Tvar)}.
peep2(switch(T,R,L,fail,fail),label(L)) -->
[test(ne,Tvar,R,fail),label(L)], {tag(var,Tvar)}.
peep2(switch(T,R,fail,L,fail),label(L)) -->
[test(ne,T,R,fail),label(L)].
peep2(switch(T,R,L,fail,L), label(L)) -->
[test(eq,T,R,fail),label(L)].
peep2(switch(T,R,L1,L2,L1), label(L1)) -->
[test(eq,T,R,L2),label(L1)].
peep2(switch(T,R,L1,L2,L1), label(L2)) -->
[test(ne,T,R,L1),label(L2)].
peep2(switch(T,R,L1,L2,L2), label(L1)) -->
[test(ne,Tvar,R,L2),label(L1)], {tag(var,Tvar)}.
peep2(switch(T,R,L1,L2,L2), label(L2)) -->
[test(eq,Tvar,R,L1),label(L2)], {tag(var,Tvar)}.
peep2(I, adda(R,M,R)) --> {merge_add}, [adda(R,M,R),J], {inc_reg(I,R,M,J)}.
peep2(I, pad(M)) --> [pad(M),J], {inc_reg(I,r(h),M,J)}.
% Three instructions:
peep3(I, label(L), fail) --> {local_instr(I), \+I=cut(_), \+I=fail},
[fail,label(L),fail].
peep3(pragma(tag(IR,T)), move(C,I), move(C,R)) -->
{reg(R), ind(I, IR), a_var(IR), \+reg(C), \+is_in(R,I)},
[move(C,R),pragma(tag(IR,T)),move(R,I)].
375
% *** Up to a cut there are only instructions that always succeed &
% there is no move to the b register (this move marks the point to cut to).
% This routine follows the execution path as far as it can. It follows
% jumps and calls. It could be extended to follow all branches.
% Proc_code has a higher-level version of this predicate.
success_to_cut([cut(_)|_], _) :- !.
success_to_cut([move(_,r(b))|_], _) :- !, fail.
success_to_cut([jump(L)|_], BA) :- fget(BA, L, Blk), !, success_to_cut(Blk, BA).
success_to_cut([call(L)|_], BA) :- fget(BA, L, Blk), !, success_to_cut(Blk, BA).
success_to_cut([I|Code], BA) :- local_instr(I), !, success_to_cut(Code, BA).
all_fails([]).
all_fails([X|L]) :- nonvar(X), X=fail, all_fails(L).
eq_ne(eq, ne).
eq_ne(ne, eq).
tf_ft(true, false).
tf_ft(false, true).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Label:
lbl(fail).
lbl(N/A) :- atom(N), integer(A), A>=0.
lbl(l(N/A,I)) :- atom(N), integer(A), A>=0, integer(I).
% Register:
% (This includes r(h) and r(b))
reg(r(I)) :- atomic(I).
reg(r(I), I) :- integer(I).
perm(p(I)) :- integer(I).
a_var(R) :- reg(R).
a_var(R) :- perm(R).
an_atom(Int) :- integer(Int).
an_atom(TˆA) :- atom(A), tag(atom, T).
an_atom(Tˆ(F/N)) :- atom(F), positive(N), tag(atom, T).
ind([_]).
ind([I], I).
complex(_ˆ_).
complex([_]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
377
% Utilities:
% Simplify an operand:
s_oper(X, S) :- (s_o(X, S) -> true ; X=S).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
378
preamble.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
% Convert a raw list of input Prolog clauses into a list of ptrees ready for
% further compilation.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
pred_info(extract_merge, 3, [dirs,cls]).
first_namearity(Cls2, NA1),
keylist_cls(Cls2, KCls, D, L, NA1).
keylist_cls([], Ks, D, L, _) :-
(D==L -> Ks=[] ; Ks=[key(3,_,end)-pair(D,L,L1,L1)]).
keylist_cls([Cl|Cls], [K-pair(D,L,[Cl|L1],L1)|KCls], D, L, NA1) :-
split(Cl, Head, _),
functor(Head, N, A),
hash_name(N, A, H),
(N/A=NA1 -> R=1 ; R=2),
K = key(R,H,N/A),
extract_directives(Cls, Cls2, D2, L2),
keylist_cls(Cls2, KCls, D2, L2, NA1).
hash_name(N, A, H) :-
X is A*10,
name(N, NL),
diff_sum(NL, X, Y),
random_step(Y, Z),
random_step(Z, H).
merge_cls([], []).
merge_cls(KCls, OutCls) :-
extract_merge(KCls, _, OutKCls, OutCls, MidCls, MidCls, EndCls),
merge_cls(OutKCls, EndCls).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
cls_to_ptrees_loop(Cls, Ptrees) :-
380
% Split one procedure from a list of clauses, and return the remainder:
cls_to_proc([], [], _, []).
cls_to_proc([Cl|Cls], RestCls, NaAr, Proc) :-
split(Cl, Head, _),
functor(Head, N, A), NaAr=N/A, !,
Proc=[Cl|P],
cls_to_proc(Cls, RestCls, NaAr, P).
cls_to_proc([Cl|Cls], [Cl|Cls], NaAr, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
translate_clauses([], []).
translate_clauses([D|Cls], [D|TCls]) :- directive(D), !,
translate_clauses(Cls, TCls).
381
translate_clauses([C|Cls], [(H:-SB)|TCls]) :-
split(C, H, B),
translate(B, TB),
simplify(TB, SB),
translate_clauses(Cls, TCls).
% *** Default:
translate(G, G).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
383
proc_code.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
% (Assumes that all head vars exist--this may not remain true; it will depend
% on the Modes).
proc_code(Head, Disj, Modes, DList, RFlag) -->
{copy(Modes, (Head:-InF))},
code(Head, InF, DList, RFlag, Disj), !.
% Three cases:
% 1. Generate deterministic selection:
code(Head, InF, DList, RFlag, ’$case’(Name,Ident,Case)) --> !,
{stats(d, det_code)},
det_code(Head, InF, DList, RFlag, Name, Ident, Case).
% 2. Generate a choice-block:
code(Head, InF, DList, RFlag, Disj) -->
{disj_p(Disj)}, !,
{stats(d, choice_block)},
choice_block(Head, InF, DList, RFlag, Disj).
% 3. A single clause:
code(Head, InF, DList, RFlag, Goal) -->
{\+disj_p(Goal)},
{\+Goal=’$case’(_,_,_)}, !,
{flat_conj(Goal, SGoal)},
{stats(d, clause_code)},
clause_code((Head:-SGoal), DList, RFlag, InF).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This code calculates the minimal sets of registers that must be saved by the
% first choice and restored by the following choices. The minimal set to be
% saved is all argument variables used in choices after the first
% and all argument variables that are uninit mem vars, minus all argument
% vars that are uninit reg vars.
% The minimal set to be restored by a clause is all argument variables
% used in the clause and all argument variables that are uninit mems, minus all
% argument vars that are uninit reg vars.
% Also inserts dereferencing instructions for those predicates in Disj which
% need them (i.e. those requiring deref arguments). This avoids
% superfluous dereferencing on backtracking.
choice_block(Head, InF, DList, RFlag, Disj) -->
{require_derefs(Disj, InF, DVars)},
insert_derefs(Head, DVars, InF, MidF),
{split_disj(Disj, _, Rest)},
384
{varset_disj(Rest, DisjVars)},
{uninit_set_type(mem, InF, UMSet)},
{uninit_set_type(reg, InF, URSet)},
{unionv(UMSet, DisjVars, SV)},
{diffv(SV, URSet, SaveVars)},
{varset_numset(Head, SaveVars, All)},
choice_block(Head, MidF, All, UMSet,URSet, DList, RFlag, Disj, _, 0, N).
% *** Could be a little sharper by keeping track of SoFar variables too ***
% Gather all required dereferencing instructions in a formula:
% Stop gathering at a unification goal that may be aliased, since any
% dereferencing done before that has to be repeated anyway. Continue
% gathering when encountering other goals, including unification goals
% that are not aliased (i.e. which have an uninit argument).
require_derefs(Disj, Form, ReqDeref) :-
get_kind_d(Disj, Form, deref, DBag, []),
sort(DBag, ReqDeref).
% Traverse a conjunction:
get_kind_c((A,B), F, K) --> !, get_kind_g(A, K), get_kind_c_stop(A, B, F, K).
get_kind_c(A, _, K) --> get_kind_g(A, K).
386
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
atomic_word(N, N) :- number(N), !.
atomic_word(A, TatmˆA) :- atom(A), !, term_tag(atom, Tatm).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
391
regalloc.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
% Register allocation:
% Notes:
% 1. Previously, nested usages had to be done from the inside out for
% correctness. (Partially overlapping usages are not a problem.)
% Presently, alloc_temps/2 has been changed to correct this problem.
% Otherwise the registers allocated in the outer usages are not seen by
% the inner usages. The arrangement of arguments in a usage structure
% together with the call to sort in usage solve this. This is yet another
% manifestation of the variable-instantiation-order problem that could be
% solved by a variant of constraint programming.
% 2. This module is an example of general duality: to create structures
% implicitly using backtracking or to create structures explicitly on the
% heap. The same time order in linear algorithms, but first method uses no
% heap space. What are the true differences? Execution time order?
% 3. Overflows temporaries into permanents if there are not enough registers
% available.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
stats(reg,7),
num_perms(PermUsage, NumPerms),
env_needed(Varlist, NumPerms, EnvNeeded),
stats(reg,8),
!.
clause_allocate(AllocCl, _, _) :-
error([’Register allocation could not be done for the clause:’, nl,
AllocCl, nl,
’because of a bug in the register allocation routine.’]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
trivial_moves([]).
trivial_moves([pref,R,R|Vs]) :- trivial_moves(Vs).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
split_temps([], _, []) :- !.
split_temps([U|Usage], Perms, [U|TempUsage]) :-
get_usage(U, V, _, _), \+inv(V, Perms), !,
split_temps(Usage, Perms, TempUsage).
split_temps([_|Usage], Perms, TempUsage) :-
split_temps(Usage, Perms, TempUsage).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init_usage([], []) :- !.
init_usage([V|Varset], [var(L,F,V)|Usage]) :-
init_usage(Varset, Usage).
first_usage([X|Varlist], I, Usage) :-
var(X), !,
I1 is I+1,
find_usage(X, Usage, First, _),
fix_it(I, First),
first_usage(Varlist, I1, Usage).
first_usage([X|Varlist], I, Usage) :-
nonvar(X), !,
I1 is I+1,
first_usage(Varlist, I1, Usage).
last_usage([], _, _) :- !.
last_usage([X|Reverse], I, Usage) :-
var(X), !,
I1 is I-1,
find_usage(X, Usage, _, Last),
fix_it(I, Last),
last_usage(Reverse, I1, Usage).
last_usage([X|Reverse], I, Usage) :-
nonvar(X), !,
I1 is I-1,
last_usage(Reverse, I1, Usage).
get_usage(var(L,F,V), V, F, L).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
pred_info(permvars, 1, [vars,half,perms]).
pred_info(permstep, 1, [ half,perms]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
naive_alloc_perms([]) --> !.
naive_alloc_perms([V|Vs]) --> alloc_if_var(V), naive_alloc_perms(Vs).
% *** More sophisticated allocation that can’t be used with write-once perms:
% Variables not yet allocated are allocated to permanents.
% Non-overlapping variables are allocated to the same register.
% Sort+reverse ensures that permanents are allocated last-first.
soph_alloc_perms(PermUsage) :-
sort(PermUsage, SUsage),
reverse(SUsage, RUsage),
soph_alloc_perms(RUsage, RUsage).
soph_alloc_perms([], _) :- !.
soph_alloc_perms([X|Usage], AllUsage) :-
get_usage(X, V, First, Last),
low_perm(L), max_int(H), range(L, I, H), R=p(I),
\+usage_overlap(First, Last, AllUsage, R),
V=R,
soph_alloc_perms(Usage, AllUsage).
num_perms([], N, N).
num_perms([U|PermUsage], I, N) :-
get_usage(U, p(J), _, _),
max(I, J, K),
num_perms(PermUsage, K, N).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
396
alloc_temps(Usage, Varlist) :-
pref_closure(Usage, Varlist), % Allocate preferred registers.
rest_temps(Usage, Varlist, Usage). % Allocate other registers.
rest_temps([], _, _) :- !.
rest_temps([X|Usage], Varlist, AllUsage) :-
get_usage(X, V, First, Last),
( var(V), rest_allocate(V, Varlist, AllUsage, First, Last)
-> % Optimization: interleave rest & pref calculation:
397
pref_closure(AllUsage, Varlist)
; true
),
rest_temps(Usage, Varlist, AllUsage).
prefer(V, [P,X,R|Varlist], R) :-
pref(P),
V==X, temp_register(R).
prefer(V, [P,R,X|Varlist], R) :-
pref(P),
V==X, temp_register(R).
prefer(V, [_|Varlist], R) :- prefer(V, Varlist, R).
pref(P) :- P==pref.
temp_register(R) :- nonvar(R), R=r(I), integer(I).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This method is weak because the varlist is merged for the read and write
% branches of unification. However, for pure read or write mode it works
% quite well.
alloc_voids([]).
alloc_voids([U|Usage]) :-
get_usage(U, V, F, L),
(F=L -> V=r(void) ; true),
alloc_voids(Usage).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
399
segment.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
% Notes:
% 1. Only those tests all of whose variables are in the head and are not
% bound are split off.
% This makes point 2 below superfluous since the heads don’t change.
% I’ll keep 2’s code around for now.
% 2. For each choice in the disjunction, add arguments to make a $body head.
% The extra arguments are those that are common between the tests and
% the body of the choice:
% BodyHead = Head U ((TestVars /\ BodyVars) - HeadVars)
% The arguments already in the head are not duplicated.
% The extra arguments link together the selection code and the
% clause body code so that register allocation will be consistent.
% Existing head arguments are kept so that there won’t be any superfluous
% move operations.
% 3. Create_bodies does a lot of effort to simplify the bodies and to give them
% as many modes as possible. This effort speeds up test set selection.
% Should check to see that this effort isn’t duplicated elsewhere.
% 4. Later improvements will also segment user-defined tests.
% 5. An effort is done to arrange goals as follows: non-binding tests,
% var modes, others (incl. uninit modes). Var modes are affected by
% aliasing, uninit modes are not. Compare compilation of ex(ncon) and
% ex(l1).
400
BVs=[BV|BVs2]
),
create_bodies(Disj, SegDisj, LHs2, TVs2, BVs2, HVar, InF, Bs2, Link).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% testvars = variables that may not occur in a test, since their value
% is given by a var or uninit mode unification which has been reordered.
% sf = variables with a value.
pred_info(segment_1, 2, [test,var,uninit,testvars,sf,form]).
pred_info(add_name_arity, 1, [test,form]).
% For debugging:
st(InBody, Head, InF) :-
varset(Head, HVar),
segment_test(InBody, OutB, Ts, HVar, InF),
wn(Ts),
wn(OutB).
% Add ’$name_arity’ for a unify goal when the argument is implied nonvar:
add_name_arity(Goal) -->>
{split_unify(Goal, X, Term)},
{var(X), nonvar(Term)},
Form/form, {implies(Form, nonvar(X))}, !,
{functor(Term, Na, Ar)},
[’$name_arity’(X,Na,Ar)]:test,
[’$name_arity’(X,Na,Ar)]:form.
add_name_arity(Goal) -->> [].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
{filter_vars([X,Y], UBag)},
{sort(UBag, USet)},
{disjointv(USet, UVs)}, !,
insert(USet),
segment_all_conj(B, Ts, Body, UVs).
segment_all_conj((T,B), (T,Ts), Body, UVs) --> {test(T)}, !,
segment_all_conj(B, Ts, Body, UVs).
segment_all_conj((T,B), true, (T,B), UVs) --> {\+test(T)}, !.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
405
selection.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
% Notes:
% 1. It is necessary to retain ordering of tests because of data dependencies,
% e.g. in (h(A) :- A=[X], X<3) the unification must be done before the test.
% Factoring takes care of some cases of this. In other cases a fundamental
% problem remains: (A=[X],X<10) and (A=[Y],Y>=10) are not recognized as
% mutex because X and Y are different variables! The algorithm
% given here is too simplistic. The written notes contain one possible
% solution to the problem using the notion of the ’origin’ of a variable.
% The currently implemented solution is to do a head factorization first,
% and to allow segmenting to take place only for tests all of whose vars
% are in the head. Variables with the same origin are considered identical.
% 2. Even for rather simple predicates and a simple collection of test-sets,
% a large number of mostly useless test-sets is generated in find_testset.
% This can lead to memory problems with large predicates. One solution is
% to allow the measure of goodness parameter of each test-set to influence
% find_testset to gather only test-sets that are sufficiently good.
% 4. Nested testing in code_testset: new modes are (Test,Formula), new prev.vars
% are (TVars U HVars). Selection is called recursively.
% 5. The output has the form $case(Name,Ident,Case)
% where Name & Ident identify the particular test-set,
% Case = ($test(Direc,Pre,Test,BV,Code);$test(..);...;$else(Pre,BV,Code))
% Direc = the direction of each test.
% This form allows straightforward code generation.
% 6. Giving lots of modes will slow down selection significantly. Need to
% optimize the speed of mutex?
% Possible extensions:
% 1. instead of disj only as data struc, better is disj+prev vars
% where prev vars = set of vars existing before the disj.
% Flattening takes care of this at pred. level, but need to consider
% nested case selection.
% 2. internal_testset predicate: user-defined tests can have different
% arg. variables in different clauses - the actual testset depends
% on modes, possibly on def. of the predicate. Unraveling removes
% some of the correspondence between clauses - can worsen results.
% This will be solved later using idea of variable origins.
% At the same time, can remove the use of Prolog variables to represent
% variables in the source code. These are really meta-variables, and
% it may be better to represent them as constants or compound terms.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
406
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Default clause:
clause_gather((_,Body), Head, Cl, PrevKeys, PrevVars, US, Form, Nc, Nb, Ob) -->
{Mb is Nb+1},
clause_gather(Body, Head, Cl, PrevKeys, PrevVars, US, Form, Nc, Mb, Ob).
one_clause_gather(Goal, Head, Cl, PrevKeys, PrevVars, US, Form, Nc, Nb, Ob) -->
{Ob is Nb+1},
{bagof(key(Name,Ident)-val(Goal,Test,Nc,Nb,Direc,_),
valid_testset(Goal, Head, Form, Name, Ident, Direc, Test,
PrevKeys, PrevVars, US),
Bag)}, !,
{make_correct(Goal, Cl, Bag)},
difflist(Bag).
test_option(typecheck) :- compile_option(test),compile_option(test_typecheck),!.
test_option(_) :- \+ compile_option(test).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Method 2:
pick_testset(TestSets, Tests, KCBest) :-
first_test(Tests, FK-FV),
comment([’First test is ’,FK-FV]),
rel_flag(FK, Flag),
pick_eq(none-[], 0, TestSets, KCBest, GBest, Flag),
% Check that one was chosen:
GBest>0, !.
).
pick_eq(KC, G, [], KC, G, _).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
thin_testset(Key-TestSet, Key-Thin) :-
show_tests(TestSet, ShowTs),
keysort(ShowTs, SortTs),
make_thin(SortTs, Thin), !. % Remove duplicate tests.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Bugs:
% 1. Must keep order of tests unchanged, i.e. variables in a test may get
% their values from a previous unification, and that unification must
412
% Notes:
% 1. This version handles tests that bind, although such tests are not presently
% used.
% 2. For each test, collect the clauses that are not mutually exclusive with it.
% Format: $test(Direc,Preamble,Test,BindVars,Disj)
% where:
% Direc = direction of branch
% Preamble = saves and restores needed for tests that bind vars.
% Test = the test
% BindVars = the vars that may be bound in the test
% Disj = the code to be executed if the test succeeds
% Also collect an ’$else’ case of clauses selectable if none of the tests
% succeed.
% Format: $else(Preamble,BindVars,Disj)
% 3. Needs Formula parameter to do correct binding code generation. Note that
% present alg. does not look at body for determining which vars are to be
% saved & restored. A more accurate alg. would set
% BTVars = BTVars /\ (Vars in SDisj).
% 4. The predicates extract_else_disj and merge_disj_list are needed to reduce
% execution time from O(test_clauses ˆ 2) to O(test_clauses * other_clauses).
% This shows up in the compilation of large databases of facts.
% 5. Note that the ’$else’ case is what needs to be executed if all others fail.
% It may be that the others cannot all fail at the same time. In that
% situation, the ’$else’ case may be ignored.
code_testset([val(_,Test,Nc,_,Direc,Cl)|TestSet],
Head, PK, HV, F, BVars, EList, EDisj, Else,
(’$test’(Direc,Preamble,Test,BTVars,Code);Case)) :-
needset(Test, HV, TVars),
bindset(Test, F, BTVars),
restore_block(TVars, BVars, Preamble, T),
save_block(BTVars, T, true),
unionv(BVars, BTVars, BVars2),
merge_disj_list(EList, Nc, EDisj, Cl, MDisj),
% comment([’Merge: ’,EList, Nc, EDisj, Cl, MDisj]),
subsume(Test, MDisj, SDisj),
% comment([’Subsume: ’,Test, SDisj]),
else(Test, Else, NewElse),
% comment([’Else: ’,Else, NewElse]),
% Nested det. extraction of SDisj is done here:
413
number_disj(fail, _, []) :- !.
number_disj((_;Disj), I, [I|List]) :- !, I1 is I+1, number_disj(Disj, I1, List).
number_disj(_, I, [I]).
% *** Merge the current clause into the other (non-testset) clauses:
merge_disj_list(_, none, EDisj, _, EDisj) :- !.
merge_disj_list([I|EList], Nc, (Conj;EDisj), Cl, (Conj;MDisj)) :-
I<Nc, !,
merge_disj_list(EList, Nc, EDisj, Cl, MDisj).
merge_disj_list(_, _, EDisj, Cl, (Cl;EDisj)).
% Do restore(T) if T in Ts/\Bs.
414
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
415
standard.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
% Standard form:
% Utilities for converting Prolog clauses into kernel Prolog.
% Notes:
% 1. In an stree, Head in mode formula and Head of disjunction are the same.
% 2. Goals that are variables must be surrounded by call() and reported.
% 3. The standard form allows easy ’cdring down’ conjunctions and disjunctions.
% 4. Standard form of a procedure is (Head:-Disj) where Disj is a disjunction
% whose choices are all in standard form and all the variables of Head
% are distinct.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 7. \+ is converted to ->.
% 8. Arguments of -> are in standard conjunctive form, i.e. terminated by true.
% 9. (A->B) as a goal in a conjunction is terminated by fail like a disjunction.
% 10. Unify goals are written as X=T where if T is var then X is var.
% Note:
% 1. The single asymmetry between conjunctions and disjunctions exists
% to make removing disjunctions from clauses easier.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Utilities for unraveling a goal into a new goal and a series of unifications:
% Unraveling of the head is done in two steps. First, those arguments that
417
% are known to be nonvar or unbound at run-time are split off from the others.
% The nonvars are put last & the unbounds are put first in the argument list.
% Second, this list is traversed & explicit unify goals are created for args
% that are identical. Since the nonvars are last, they are unified together
% (if they are identical). Since the unbounds are first, their arguments are
% replaced before the others, which means that any other tests in the clause
% DON’T get unbound arguments.
% The nonvar unifications are put first, followed by the other unifications
% and finally the unbound unifications, which always succeed.
% The nonvar unify goals are put before the other unify goals, and the
% unbound unify goals are put last.
% Goals which are not unbound (i.e. nonvar & other) are replaced first at the
% highest argument number, and the result is reversed so that unifications
% of low argument numbers come first.
% This transformation encourages better determinism extraction.
pred_info(split_nonvars, 5, [unbound,other,nonvar]).
pred_info( match_unr, 5, [nonvar_goal,other_goal,unbound_goal]).
pred_info( conj_unr, 4, [nonvar_goal,other_goal,unbound_goal]).
{I1 is I+1},
split_nonvars(I1, Formula, FHead, Head, A).
split_nonvars(I, Formula, FHead, Head, A) -->> {I=<A},
{arg(I, FHead, Y), implies(Formula, unbound(Y))}, !,
[I]:unbound,
{I1 is I+1},
split_nonvars(I1, Formula, FHead, Head, A).
split_nonvars(I, Formula, FHead, Head, A) -->> {I=<A}, !,
[I]:other,
{I1 is I+1},
split_nonvars(I1, Formula, FHead, Head, A).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This code assumes that running unr_head again with the new mode information
% gives the same number of unification goals that it did before.
re_unr_strees(Ss, NSs) :-
compile_option(compile), !,
re_unr_strees_2(Ss, NSs).
re_unr_strees(Ss, Ss) :-
\+compile_option(compile), !.
re_unr_strees_2([], []).
re_unr_strees_2([S|Ss], [NS|NSs]) :-
re_unr_stree(S, NS),
re_unr_strees_2(Ss, NSs).
re_unr_stree(stree(NaAr, (Head:-Disj),Mode,OldHeads,DL,SD),
stree(NaAr,(Head:-NewDisj),Mode,OldHeads,NDL,SD)) :-
cons(OldHeads),
non_trivial(Mode), !,
re_unr_disj(OldHeads, Head, Disj, NewDisj, Mode),
re_unr_strees(DL, NDL).
% There are no old heads to unravel, or the mode is trivial:
re_unr_stree(stree(NaAr,HeadDisj,Mode,OldHeads,DL,SD),
stree(NaAr,HeadDisj,Mode,OldHeads,NDL,SD)) :- !,
re_unr_strees(DL, NDL).
re_unr_stree(D, D) :- directive(D), !.
non_trivial((_:-Formula)) :- \+Formula=true.
420
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
421
synonym.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
% Synonym Optimization:
% Notes:
% 1. This module implements an abstract data type representing a partitioning
% of all possible arguments of the move instruction. The internal
% representation is as a set of equivalence classes, denoted by a list of
% lists. An equivalence class contains all arguments with identical contents.
% Classes of size 1 are not stored. This implementation is sufficient
% because there will almost always be a small number of small equivalence
% classes.
% 2. For best results, this module should be done AFTER superfluous label
% elimination. The reason is that the synonym set is emptied when a
% label is encountered.
% 3. Small improvement possible with call: only remove the registers from the
% synonym set, and keep the environment variables.
% 4. Improvement with all conditional branches: continue down the fall-
% through case. Don’t have to change the SynSet in any way, but doing it
% would be an extra optimization. For example, jump(ne,A,B,L)
% is an equal_maker.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
regset_to_regs([], []).
regset_to_regs([N|Set], [r(N)|Rs]) :- integer(N), !, regset_to_regs(Set, Rs).
regset_to_regs([no|Set], Rs) :- regset_to_regs(Set, Rs).
425
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Operations:
% make_set, add_set, is_syn, intersect_syn, remove_all, less_p, cheapest_list
% *** Remove all synonyms that use an element in the set Els
426
map_ea( R, R, S, S).
map_ea( [R], R, [S], S).
map_ea( [R+N], R, [S+N], S).
map_ea( TˆR, R, TˆS, S) :- pointer_tag(T).
427
minimum_el([], M, M).
minimum_el([El|Els], M1, M) :- less_p(El, M1), !, minimum_el(Els, El, M).
minimum_el([El|Els], M1, M) :- minimum_el(Els, M1, M).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
428
tables.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
% The unify goal A=B is handled specially in the compiler; the entry given here
% only scratches the surface of what is done with it. The special handling
% is necessary because A=B kills its temporary registers only when a call to the
% general unifier is necessary. Look at clause_code.pl, unify.pl, and
% regalloc.pl to see how unify goals are handled.
survive(Goal, y) :- survive(Goal), !.
survive(Goal, n) :- \+survive(Goal), !.
anyregs(Goal, y) :- anyregs(Goal), !.
anyregs(Goal, n) :- \+anyregs(Goal), !.
builtin(Goal, y) :- builtin(Goal), !.
builtin(Goal, n) :- \+builtin(Goal), !.
expanded(Goal, y) :- expanded(Goal), !.
expanded(Goal, n) :- \+expanded(Goal), !.
fixregs(Goal) :- \+anyregs(Goal).
fixregs(Goal) :- \+builtin(Goal).
% This routine does not take the mode formula into account, so it
% gives a conservative answer.
% In conditions.pl is a generalization of this routine which does take
% the mode formula into account.
bindset(Goal, S) :- bindbag(Goal, B), sort(B, S).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% User-defined predicates:
info(UserPred, Surv, n, n, n, Bindbag, Require, After) :-
mode_option(UserPred,Require,Before,After,Surv), !,
UserPred=..[_|Bindbag].
% Standard builtins:
info(copy_term(A,B), n, n, y, n, [B], (deref(A),deref(B)), true).
info( call(P), n, n, y, n, [P], deref(P), nonvar(P)).
info(length(L,N), n, n, y, n, [L,N], (deref(L),deref(N)), (list(L),integer(N))).
info(compare(C,X,Y), n, n, y, n, [C], (deref(C),deref(X),deref(Y)), atom(C)).
info(expand_term(T,X), n, n, y, n, [X], (deref(T),deref(X)),
(nonvar(T),nonvar(X))).
info( sort(L,S), n, n, y, n, [S], (deref(L),deref(S)), (list(L),list(S))).
info(keysort(L,S), n, n, y, n, [S], (deref(L),deref(S)), (list(L),list(S))).
info( name(A,L), n, n, y, n, [A,L], (deref(A),deref(L)),
(atomic(A),list(L))).
info(atom_chars(A,L), n, n, y, n, [A,L], (deref(A),deref(L)),
(atom(A),list(L))).
info(number_chars(A,L), n, n, y, n, [A,L], (deref(A),deref(L)),
(number(A),list(L))).
info( repeat, n, n, y, n, [], true, true).
% expanded_exprs
% Program files:
info( [F|R], n, n, y, y, [], (deref(F),deref(R)), (ground(F),ground(R))).
info( consult(F), n, n, y, n, [], deref(F), atomic(F)).
info( close(F), n, n, y, n, [], deref(F), atomic(F)).
info( exists(F), n, n, y, n, [], deref(F), atomic(F)).
info(reconsult(F), n, n, y, n, [], deref(F), atomic(F)).
info( rename(F,G), n, n, y, n, [], (deref(F),deref(G)), (atomic(F),atomic(G))).
info( save(F), n, n, y, n, [], deref(F), atomic(F)).
info( see(A), n, n, y, n, [], deref(A), atomic(A)).
info( seeing(A), n, n, y, n, [A], deref(A), atomic(A)).
info( seen, n, n, y, n, [], true, true).
info( tell(A), n, n, y, n, [], deref(A), atomic(A)).
info( telling(A), n, n, y, n, [A], deref(A), atomic(A)).
info( told, n, n, y, n, [], true, true).
% Character I/O:
info( get(C), n, n, y, n, [C], deref(C), integer(C)).
info( get0(C), n, n, y, n, [C], deref(C), integer(C)).
info( skip(C), n, n, y, n, [], deref(C), integer(C)).
info( display(T), n, n, y, n, [], deref(T), true).
info( print(T), n, n, y, n, [], deref(T), true).
info( tab(N), n, n, y, n, [], deref(N), integer(N)).
info( nl, n, n, y, n, [], true, true).
info( put(C), n, n, y, n, [], deref(C), integer(C)).
info( read(A), n, n, y, n, [A], deref(A), true).
info( write(A), n, n, y, n, [], deref(A), true).
info( writeq(A), n, n, y, n, [], deref(A), true).
% Internal database:
info(recorda(K,T,R), n, n, y, n, [T,R], (deref(K),deref(T),deref(R)),nonvar(R)).
info(recordz(K,T,R), n, n, y, n, [T,R], (deref(K),deref(T),deref(R)),nonvar(R)).
info(recorded(K,T,R),n, n, y, n,[K,T,R],(deref(K),deref(T),deref(R)),nonvar(R)).
info(erase(R), n, n, y, n, [], deref(R), nonvar(R)).
info(current_key(N,T), n, n, y, n, [N,T], (deref(N),deref(T)),
(atomic(N),nonvar(T))).
Require=(deref(A),deref(B),uninit_reg(C)),
After=(integer(A),integer(B),integer(C),rderef(A),rderef(B),rderef(C)).
ar_m(A, C, Require, After) :-
Require=(deref(A),uninit_reg(C)),
After=(integer(A),integer(C),rderef(A),rderef(C)).
/*
% The same two predicates for a processor that doesn’t have
% type-checking built into its arithmetic:
% (But: Must TRAP if not integer...)
ar_m(A, B, C, Require, After) :-
Require=(deref(A),deref(B),integer(A),integer(B),uninit_reg(C)),
After=(integer(C),rderef(C)).
ar_m(A, C, Require, After) :-
Require=(deref(A),integer(A),uninit_reg(C)),
After=(integer(C),rderef(C)).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Control flow:
control( (_,_)).
control( (_;_)).
control((_->_)).
control((_=>_)).
control( \+(_)).
control(not(_)).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% The tag names and tests for the basic types are given here.
% Program transformation should be able to remove all of the overhead
% of using these predicates instead of using inline constants.
% Get a test (with argument X) that checks for the term’s type:
term_test(Term, X, Test) --> {term_test(Term, X, Test)}.
term_test(Term, X, Test) :- term_tag(Term, Tag), tag_type_test(Tag, _, X, Test).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Arithmetic comparisons:
arith_test(A=:=B, A, B, eq).
arith_test(A=\=B, A, B, ne).
% Signed arithmetic:
arith_test(A<B, A, B, lts).
arith_test(A=<B, A, B, les).
arith_test(A>=B, A, B, ges).
arith_test(A>B, A, B, gts).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
438
testset.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
% Notes:
% 1. A test may be in zero to several test-sets. It is up to the
% higher level selection code to take advantage of this.
% 2. What about user-defined goals? Give them all their own number?
% Only take goals from a clause up to the first one that fails same/1?
% 3. Is it reasonable to generalize test sets to the non-mutual exclusive case?
% Probably not, since the mutual exclusivity allows a straightforward code
% generation.
% 4. Modes are used elsewhere to see which variables in the tests may be bound.
% They are not needed here.
% 5. Negation is handled through the Direc output. A special output is given
% to distinguish positive and negative tests. I’m not sure how to
% distinguish not(..) from \+(..) here.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
439
goodness_key(structure, 120).
goodness_key(negative, 120) :- split_integer.
goodness_key(nonnegative, 120) :- split_integer.
goodness_key(equal, 85).
goodness_key(equal(atomic,_), 80).
goodness_key(comparison(_,_), 80).
goodness_key(integer, 79).
goodness_key(atomic, 79).
goodness_key(compound, 79).
goodness_key(negative, 78) :- \+split_integer.
goodness_key(nonnegative, 78) :- \+split_integer.
goodness_key(equal(structure,_), 60).
goodness_key(simple, 50).
goodness_key(hash(atomic), 41).
goodness_key(hash(structure), 40).
get_direcs([], []).
get_direcs([val(_,_,_,_,D,_)|Values], [D|Direcs]) :- get_direcs(Values, Direcs).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% *** The testsets which may be used when the test is a relational predicate.
% This is necessary to maintain proper trapping behavior for arithmetic
% comparisons.
relational_testset(comparison(_,arith)).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
switch_testset_list(Kind, X,
[val(_,var(X),none,_, var,ErrCl),
val(_, Test,none,_, Kind,ErrCl),
val(_, OTest,none,_,other,ErrCl)]) :-
441
ErrCl = (’$sel_error’,true),
type_test(Kind, X, Test),
disj_test(var(X), Test, NT),
not_test(NT, OTest).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% *** Hashing:
% Assumes the existence of a hashing instruction (similar to WAM)
testset(Goal, ’$name_arity’(A,Na,Ar), hash(Type), v(A), Direc, not(Direc)) :-
test_type_name_arity(Goal, A, T, Na, Ar),
T\==cons,
% Integers are hashed the same as atoms:
(simple_type(T) -> Type=atomic; Type=structure),
(Ar>0 -> Direc=(Na/Ar); Direc=Na).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Local utilities:
% Negation:
negation(\+(Goal), Goal) :- !.
negation(not(Goal), Goal) :- !.
% negation(nonvar(A), var(A)) :- !.
out_negation(not(Goal), Goal) :- !.
out_negation(Goal, not(Goal)) :- !.
% Map multiple choices onto the ones possible with a three-way branch:
map_three(Type, var, var).
map_three(Type, Type, Type).
% map_three(Type, InType, other) :- InType\==var, InType\==Type.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
444
transform_cut.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
% Notes:
% 1. This module increases the average arity of predicates.
cut_p(’$cut_deep’(_)).
cut_p(’$cut_shallow’(_)).
transform_cut_ptrees([], []).
transform_cut_ptrees([P|Ps], [TP|TPs]) :-
transform_cut_ptree(P, TP),
transform_cut_ptrees(Ps, TPs).
transform_cut_ptree(P, TP) :-
P=ptree(NaAr,Cls,M,L), !,
transform_cut_cls(Cls, CCls, DCL, DF, AF),
CP=ptree(NaAr,CCls,M,TL),
transform_cut_ptrees(L, TL),
transform_2(AF, DCL, CP, TP).
transform_cut_ptree(D, D) :-
directive(D), !.
transform_2(false, _, P, P) :- !.
transform_2(true, DCL, P, TP) :-
P=ptree(Name/Arity,Cls,(H:-Formula),DL),
gensym("$cut_", Name/Arity, NewName),
NewArity is Arity+1,
dup_head(H, NewName, NewArity, X, MH),
TP=ptree(Name/Arity,[(H:-’$cut_load’(X),MH)],(H:-Formula),[MP]),
dup_head(H, NewName, NewArity, DCL, NH),
445
% Replace all heads in a procedure by new heads with one extra argument,
% keeping the same body.
replace_heads([], [], _, _, _).
replace_heads([Cl|Cls], [(NH:-B)|NCls], NewName, NewArity, ExtraArg) :-
split(Cl, H, B),
dup_head(H, NewName, NewArity, ExtraArg, NH),
replace_heads(Cls, NCls, NewName, NewArity, ExtraArg).
contains_if(((A->B);_)) :- !.
contains_if((_;D)) :- contains_if(D).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
447
unify.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
% This module reuses code for write mode unification of the last argument.
% This reduces nested code size from O(N*N) to O(N) when there is deep
% recursion in the last argument of nested compound terms. This is especially
% useful for long immediate lists.
% Modes are taken into account to reduce the number of moves, trails, derefs,
% and multiway branches. A mode is given as a logical formula F.
% Unitialized variables are taken into account. They are given as modes
% uninit(reg,V,[]) and uninit(mem,V,[]) in F.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% *** Accumulators:
acc_info( sf, V, InSF, OutSF, includev(V,InSF,OutSF)).
acc_info( gnd, X, InGnd, OutGnd, includev(X,InGnd,OutGnd)).
448
% *** Predicates:
pred_info( unify, 1, [ aun,sf,vl,form,fail,code]).
pred_info( unify_2, 2, [drf,gnd,um,aun,sf,vl,form,fail,code]).
pred_info( unify_3, 2, [drf,gnd,um,aun,sf,vl,form,fail,code]).
pred_info(unify_3_conj, 1, [drf,gnd,um,aun,sf,vl,form,fail,code]).
pred_info(unify_nonvar, 2, [drf,gnd,um,aun,sf,vl,form,fail,code]).
pred_info(unify_nonvar, 4, [drf,gnd,um,aun,sf,vl,form,fail,code]).
pred_info( unify_var, 2, [drf,gnd,um,aun,sf,vl,form,fail,code]).
pred_info( unify_rm, 5, [drf,gnd,um,aun,sf,vl,form,fail,code]).
pred_info( unify_rm, 8, [drf,gnd,um,aun,sf,vl, fail ]).
pred_info( unify_wm, 4, [drf,gnd,um,aun,sf,vl,form,fail,code]).
pred_info( unify_wm, 8, [drf,gnd,um, vl, fail ]).
pred_info( unify_args, 5, [drf,gnd,um,aun,sf,vl,form,fail,code,type]).
pred_info( unify_args, 6, [drf,gnd,um,aun,sf,vl,form,fail,code,type]).
pred_info( unify_arg, 7, [drf,gnd,um,aun,sf,vl,form,fail,code,type]).
pred_info( init_unify, 4, [drf,gnd,um,aun,sf,vl,form,fail,code]).
pred_info( xinit_unify, 4, [drf,gnd,um,aun,sf,vl,form,fail,code]).
pred_info(uninit_unify, 4, [drf,gnd,um,aun,sf,vl,form, code]).
pred_info( unify_var_i, 2, [drf,gnd,um,aun,sf,vl,form,fail,code]).
pred_info( unify_var_i, 4, [drf,gnd,um,aun,sf,vl,form,fail,code]).
pred_info( unify_var_u, 2, [drf,gnd,um, sf,vl,form, code]).
% Writemode routines:
pred_info( create_arg, 2, [ sf,vl,form, code]).
pred_info( new_var_nf, 1, [ sf,vl, code]).
pred_info( new_var_nf, 2, [ sf,vl, code]).
pred_info(new_umemvar_nf, 1, [ sf,vl, code]).
pred_info( new_var, 1, [ sf,vl,form, code]).
pred_info( new_var, 2, [ sf,vl,form, code]).
pred_info( new_vars, 1, [ sf,vl,form, code]).
pred_info( new_var_list, 2, [ sf,vl,form, code]).
pred_info( writemode, 1, [ sf,vl, code]).
pred_info( writemode, 5, [ um,aun,sf,vl,form, code]).
pred_info( b_writemode, 2, [ um,aun,sf,vl,form, code,top,offset]).
pred_info( b_writeargs, 4, [ um,aun,sf,vl,form, code,top,offset]).
pred_info( fill_slots, 2, [ um,aun,sf,vl,form, code,top]).
pred_info(initialize_var, 2, [ um,aun,sf,vl,form, code,top]).
pred_info( push_if_init, 2, [ um,aun,sf,vl,form, code,top]).
449
% Small utilities:
pred_info( move_arg, 5, [drf,gnd,sf,vl,form,code,type]).
pred_info( move_arg, 3, [drf,gnd,sf,vl,form,code,type]).
pred_info( gnd_drf_update, 2, [drf,gnd, form]).
pred_info( drf_update, 2, [drf, form]).
pred_info( pragma_align, 2, [code]).
pred_info( pragma_tag, 2, [code]).
pred_info( cond_pragma_tag, 2, [code]).
pred_info( pragma_tag_form, 1, [form,code]).
pred_info( align_pad, 1, [code]).
pred_info( uninit_dest, 3, [um]).
pred_info( cond_pref, 1, [vl]).
pred_info( uninit_bind, 2, [um,vl,form,code]).
pred_info( uninit_move, 2, [um, form,code]).
pred_info( term_formula, 2, [form]).
pred_info(fence_if_nosurvive, 0, [vl]).
pred_info( unify_depth_2, 3, [conj,newvar]).
pred_info( unify_depth_2, 5, [conj,newvar]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
u(X=T, ISF, OSF, VList, F) :- u(X=T, ISF, OSF, fail, VList, F).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% *** Interface between the unification compiler & rest of compiler ***
% This top level call interfaces between the form of SF needed in unification
451
% compilation (which contains only inits) and in the rest of the compiler
% (which contains all variables with a value, init and uninit).
% Also calculates Gnd & UM, and updates the mode formula.
unify(X=Y) -->>
% Internal InSF contains only initialized variables:
insert(ExtInSF, IntInSF):sf,
IF/form,
{uninit_set(IF, UnAll)},
{diffv(ExtInSF, UnAll, IntInSF)},
% Call to the internal unification compiler:
{grounds_in_form(IF, Gnd)},
{rderef_set(IF, Drf)},
{intersectv(Drf, Gnd, StayDrf)},
{uninit_set_type(mem, IF, UM)},
unify_2(X, Y):[drf(StayDrf,_),gnd(Gnd,_),um(UM)],
% External OutSF contains both init and uninit variables:
insert(IntOutSF, ExtOutSF):sf,
{varset(X=Y, Vars), unionv(IntOutSF, Vars, ExtOutSF)},
% Update the mode formula:
{intersectv(UnAll, IntOutSF, OutInit)},
remove_uninit(OutInit),
update_formula(X=Y, ExtInSF):form.
unify((U1,U2)) -->> unify(U1), unify(U2).
% Take care of the order of terms X and Y and of unifying two nonvars:
unify_2(X, Y) -->> { var(X), var(Y)}, !, unify_var(X, Y).
unify_2(X, Y) -->> { var(X), nonvar(Y)}, !, unify_3(X, Y).
unify_2(X, Y) -->> {nonvar(X), var(Y)}, !, unify_3(Y, X).
unify_2(X, Y) -->> {nonvar(X), nonvar(Y)}, !, unify_nonvar(X, Y).
% wm hook: Ignore output of sf. Return vl, code list and output of form.
% It is assumed that using aun=[] guarantees that wm and rm initialize
% the same variables.
unify_wm(X, T, Last, LLbls, SF, IF, OF, Code) -->>
unify_wm(X,T, Last, LLbls):[aun([]),form(IF,OF),sf(SF,_),code(Code,[])].
pragma_align(X, I) -->>
{align(K), 0 is I mod K}, !,
[pragma(align(X,K))]:code.
pragma_align(X, I) -->> [].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
new_var_list(IVars, UMemVars).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
464
utility.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 1989 Peter Van Roy and Regents of the University of California
:- dynamic(compile_cputime/3).
:- dynamic(gensym_integer/1).
% Numeric parameters:
max_int(32767).
min_int(-32768).
directive((:-_)).
stree(stree(_,_,_,_,_,_)).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
warshall([], _, _, []) :- !.
warshall([X-Neibs|G], V, Y, [X-NewNeibs|NewG]) :-
inv(V, Neibs), !,
unionv(Neibs, Y, NewNeibs),
warshall(G, V, Y, NewG).
warshall([X-Neibs|G], V, Y, [X-Neibs|NewG]) :-
warshall(G, V, Y, NewG).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Set operations:
% Implementation inspired by R. O’Keefe, Practical Prolog.
% Sets must be represented as sorted lists without duplicates.
% Predicates with ’v’ suffix work with sets containing uninstantiated vars.
% *** Intersection
intersectv([], _, []).
intersectv([A|S1], S2, S) :- intersectv_2(S2, A, S1, S).
466
intersectv_2([], _, _, []).
intersectv_2([B|S2], A, S1, S) :-
compare(Order, A, B),
intersectv_3(Order, A, S1, B, S2, S).
intersectv_list([], []).
intersectv_list([InS|Sets], OutS) :- intersectv_list(Sets, InS, OutS).
% ** Disjoint sets
disjointv([], _).
disjointv([A|S1], S2) :- disjointv_2(S2, A, S1).
disjointv_2([], _, _).
disjointv_2([B|S2], A, S1) :-
compare(Order, A, B),
disjointv_3(Order, A, S1, B, S2).
% *** Difference
diffv([], _, []).
diffv([A|S1], S2, S) :- diffv_2(S2, A, S1, S).
% *** Union
unionv([], S2, S2).
unionv([A|S1], S2, S) :- unionv_2(S2, A, S1, S).
includev_2([], A, [A]).
includev_2([B|S1], A, S) :-
compare(Order, A, B),
includev_3(Order, A, B, S1, S).
% *** Subset
subsetv([], _).
subsetv([A|S1], [B|S2]) :-
compare(Order, A, B),
subsetv_2(Order, A, S1, S2).
% *** Membership
inv(A, [B|S]) :-
compare(Order, A, B),
inv_2(Order, A, S).
inv_2(=, _, _).
inv_2(>, A, S) :- inv(A, S).
% *** Filter all nonvariables from a list, keeping only the variables:
filter_vars(L, Vs) :- filter_vars(L, Vs, []).
% Filter all vars & carry along corresponding elements of second list:
filter_vars([], [], [], []).
filter_vars([X|L1], [Y|L2], [X|V1], [Y|V2]) :-
var(X), !,
filter_vars(L1, L2, V1, V2).
filter_vars([X|L1], [_|L2], V1, V2) :-
nonvar(X), !,
filter_vars(L1, L2, V1, V2).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
in(A, [A|_]) :- !.
in(A, [B|S]) :- A@>B, in(A, S).
not_disjoint([A|S1], [A|S2]) :- !.
not_disjoint([A|S1], [B|S2]) :- A@>B, !, not_disjoint([A|S1], S2).
not_disjoint([A|S1], [B|S2]) :- A@<B, !, not_disjoint(S1, [B|S2]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
filter_dups([]) --> !.
filter_dups([V1-_,V2-_,V3-_|KeySet]) --> {V1==V2,V2==V3}, !,
filter_dups([V2-_,V3-_|KeySet]).
filter_dups([V1-_,V2-_|KeySet]) --> {V1==V2}, !,
[V1], filter_dups(KeySet).
filter_dups([V1-_|KeySet]) --> !,
filter_dups(KeySet).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Table utilities:
get(N, V, _, _, I, V) :- I=N, !.
get(N, _, L, R, I, V) :-
compare(Order, I, N),
get(Order, I, V, L, R).
% Access a value:
% This predicate cannot be used to create the array incrementally,
% but it is faster than get/3.
fget(node(N,W,L,R), I, V) :-
compare(Order, I, N),
fget_2(Order, I, V, W, L, R).
seal(leaf) :- !.
seal(node(_,_,L,R)) :- seal(L), seal(R).
list_to_tree([], T) :- seal(T).
list_to_tree([Node-Val|L], T) :- get(T, Node, Val), list_to_tree(L, T).
key_list([]).
key_list([_-_|_]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init_gensym :-
abolish(gensym_integer, 1),
asserta(gensym_integer(1)).
% Create a unique atom with given prefix and with ’Name/Arity’ embedded:
gensym(Pre, Name/Arity, Atom) :-
atomic(Name),
atomic(Arity), !,
name(Name, L1),
name(Arity, L2),
append(Pre, L1, "/", L2, "_", Pre2),
gensym(Pre2, Atom).
gensym(Pre, AnAtom, Atom) :-
472
atomic(AnAtom), !,
name(AnAtom, L),
append(Pre, L, Pre2),
gensym(Pre2, Atom).
gensym(Pre, Other, Atom) :-
warning([’Erroneous second argument to gensym/3: ’,Other]),
gensym(Pre, Atom).
% Create a term NewHead which is Head with new name and extra arguments:
new_head(Prefix, Head, ExtraArgs, NewHead) :-
functor(Head,Name,Arity),
Head=..[Name|Args],
append(Args, ExtraArgs, NewArgs),
gensym(Prefix, Name/Arity, NewName),
NewHead=..[NewName|NewArgs].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
inst_writeq_list([]).
inst_writeq_list([X|L]) :- inst_writeq(X), wn(’.’), inst_writeq_list(L).
% Writeq of a term:
% This version does not handle operator priorities, but the output is
% otherwise nicer:
inst_writeq(Term) :-
% Copy ensures alphabetic order of variable names:
copy(Term, Copy),
varset(Copy, Vars),
inst_vars_names_list(0, Vars, Names),
inst_writeq(Copy, Vars, Names).
inst_writeq_oplist([], _, _, _).
inst_writeq_oplist([Head|Tail], Op, Vars, Names) :- cons(Tail), !,
inst_writeq(Head, Vars, Names),
write(Op),
inst_writeq_oplist(Tail, Op, Vars, Names).
inst_writeq_oplist([Head|Tail], Op, Vars, Names) :- nil(Tail), !,
474
paren_op(’,’).
paren_op(’;’).
paren_op((:-)).
inst_vars_list(_, []) :- !.
475
inst_vars_list(I, [V|Vars]) :- !,
var_name(I, V),
I1 is I+1,
inst_vars_list(I1, Vars).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
global_copy(X),
abolish(global_copy, 1), !,
X=Term2.
make_sym([], []).
make_sym([V|L], [p(V,_)|S]) :- make_sym(L, S).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Same as above and collect the list of AArgs members that were found in AT:
map_terms(AArgs, XArgs, AT, XT) --> {memberv2(AT, AArgs, XT, XArgs)}, !, [AT].
map_terms(AArgs, XArgs, AT, XT) --> {var(AT)}, !, {XT=AT}.
map_terms(AArgs, XArgs, AT, XT) --> {nonvar(AT)}, !,
{functor(AT, Name, Arity)},
{functor(XT, Name, Arity)},
map_terms_seq(1, Arity, AArgs, XArgs, AT, XT).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% List operations:
c_append(true, C, C).
478
d_append(fail, D, D).
d_append((X;D1), D2, (X;D3)) :- d_append(D1, D2, D3).
% Insert a list:
insert(L) --> difflist(L).
insertlist(L) --> difflist(L).
last([X], X).
last([_|L], X) :- last(L, X).
member(X, [X|_]).
member(X, [_|L]) :- member(X, L).
memberv(X, [Y|L]) :-
(X==Y
-> true
; memberv(X, L)
).
ran_keys([], _, []).
ran_keys([I|List], K, [K-I|Ran]) :-
random_step(K, Kp),
ran_keys(List, Kp, Ran).
key_to_list([], []).
key_to_list([_-X|KeyList], [X|List]) :- key_to_list(KeyList, List).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
append_conj(true, L, L).
append_conj((X,L1), L2, (X,L3)) :- append_conj(L1, L2, L3).
last_conj((L,C), L) :- all_true(C), !.
last_conj((_,C), L) :- last_conj(C, L).
flat_conj(true) --> !.
flat_conj((A,B)) --> !, flat_conj(A), flat_conj(B).
flat_conj(A) --> co(A).
all_true(true).
all_true((A,B)) :- all_true(A), all_true(B).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
ground(0, _) :- !.
ground(N, A) :- N>0, !,
arg(N, A, X),
ground(X),
N1 is N-1,
ground(N1, A).
simple(X) :- var(X), !.
simple(X) :- atomic(X), !.
full_list([]).
full_list([_|L]) :- full_list(L).
conj_p((A,B), A, B).
disj_p((A;B), A, B).
unify_p(_=_).
call_p(G) :- \+unify_p(G).
split_unify(X, Y, X, Y).
split_unify(X, Y, Y, X).
482
split_unify_v(X=Y, X, Y) :- var(X).
split_unify_v(X=Y, Y, X) :- var(Y).
split_unify_v(X, Y, X, Y) :- var(X).
split_unify_v(X, Y, Y, X) :- var(Y).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Numeric utilities:
range(L,L,H).
range(L,I,H) :- L<H, L1 is L+1, range(L1,I,H).
downrange(L,H,H).
downrange(L,I,H) :- L<H, H1 is H-1, downrange(L,I,H1).
% This version needs O(N log N) time and O(log N) stack space for C-Prolog
% Old range needs O(N*N) time and O(N) local stack space on C-Prolog.
crange(L,L,L).
crange(L,I,H) :-
L<H,
M is (L+H)//2,
crange(L,I,M).
crange(L,I,H) :-
L<H,
M is (L+H)//2+1,
crange(M,I,H).
min_integer(A, B, A) :- A<B, !.
483
min_integer(A, B, B) :- A>=B, !.
maxlist([], M, M).
maxlist([M1|L], M2, M) :- max(M1, M2, M3), maxlist(L, M3, M).
minlist([], M, M).
minlist([M1|L], M2, M) :- min(M1, M2, M3), minlist(L, M3, M).
shorter_than([], N) :- N>0.
shorter_than([_|L], N) :- N>0, N1 is N-1, shorter_than(L, N1).
longer_than([_|_], N) :- N=<0.
longer_than([_|L], N) :- N>0, N1 is N-1, longer_than(L, N1).
length_disj(fail) --> !.
length_disj((A;B)) --> !, length_disj(A), length_disj(B).
length_disj(G) --> {\+disj_p(G)}, !, add(1).
length_conj(true) --> !.
length_conj((A,B)) --> !, length_conj(A), length_conj(B).
length_conj(Cut) --> {cut_p(Cut)}, !.
length_conj(G) --> {\+conj_p(G)}, !, add(1).
% Logical utilities:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 1. Simple utilities:
t(A) :- A is cputime.
t(A,B) :- B is cputime-A.
ct(A) :- A is cputime.
ct(A,B) :- B is cputime-A.
qt :- statistics(runtime,_).
qt(Time) :- statistics(runtime,[_,Time]).
qt(_,Time) :- statistics(runtime,[_,Time]).
qtime(N, Time) :-
qtime_empty(N, Empty),
qtime_loop(N, Empty, Time).
qtime_empty(N, _) :-
statistics(runtime, _), range(1,_,N), fail.
qtime_empty(_, Empty) :-
statistics(runtime, [_,Empty]).
qtime_loop(N, _, _) :-
statistics(runtime, _), range(1,_,N).
qtime_loop(N, Empty, Time) :-
statistics(runtime, [_,Delta]),
Time is (Delta-Empty)/N,
read(_),
w(’Time per query = ’),w(Time),w(’ ms’),
(Time<10.0
-> w(’ (0.0 - 0.05 ms too high)’)
; true
485
), nl.
ctime_loop(N, _, _) :- crange(1,_,N).
ctime_loop(N, T1, T2) :-
T3 is cputime,
Delta is T3-T2,
Empty is T2-T1,
Time is (Delta-Empty)*1000/N,
w(’Time per query = ’),w(Time),w(’ ms’),
(Time<100.0
-> w(’ (0.0 - 0.5 ms too high)’)
; true
), nl.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
write_code([]) :- !.
write_code(L) :- \+compile_option(write), !.
write_code(L) :-
compile_option(write),
\+compile_option(flat), !,
write_code(L, 4).
write_code(L) :-
compile_option(write),
compile_option(flat), !,
write_output(L).
write_code([], _).
write_code([I|L], N) :- write_code(I, L, N).
N1 is N-4,
tab(N1), wq(Lbl), wn(’:’),
write_code(L, N).
write_code(procedure(Name), L, N) :-
N1 is N-4,
tab(N1), nl, wqn(procedure(Name)), nl,
write_code(L, N).
write_code(Instr, L, N) :-
\+(Instr=label(Lbl)),
\+(Instr=procedure(_)),
\+(Instr=switch(unify,Type,V,Wbr,Rbr,Fail)),
tab(N), wqn(Instr),
write_code(L, N).
tab_if_nonlbl(label(_)) :- !.
tab_if_nonlbl(procedure(_)) :- !.
tab_if_nonlbl(I) :-
\+I=label(_),
\+I=procedure(_), !,
put(9).
nl_if_procedure(procedure(_)) :- !, nl.
nl_if_procedure(_).
xwrite_modes([]) :- !.
xwrite_modes(Modes) :- cons(Modes), !,
nl,
write(’% Modes:’), nl,
xwrite_clauses(Modes), nl,
write(’% Source:’).
xwrite_modes(Mode) :- \+cons(Mode), !,
xwrite_modes([Mode]).
xwrite_source([]) :- !.
xwrite_source(Source) :- cons(Source),
nl,
xwrite_clauses(Source).
xwrite_clauses([]).
487
xwrite_clauses([Cl|Cls]) :-
write(’% ’),write(Cl),write(’.’), nl,
xwrite_clauses(Cls).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Writing utilities:
w(X) :- write(X).
wq(X) :- writeq(X).
wn(X) :- write(X), nl.
wqn(X) :- writeq(X), nl.
msg_list([], _) :- !, nl.
msg_list([X|Msg], K) :- msg_one(X, K), msg_list(Msg, K).
msg_one(tab, _) :- !, put(9).
msg_one(nl, cm) :- !, nl, cms.
msg_one(nl, ae) :- !, nl, aes.
msg_one(nl, w) :- !, nl, ws.
msg_one(nl, e) :- !, nl, es.
msg_one(X, _) :- !, w(X).
f :- wn(’ ***’).
% Debug messages:
wd(Msg) --> {wd(Msg)}.
nl_debug :-
(compile_option(debug)
-> nl
; true
).
write_list([]).
write_list([X|L]) :- w(X), nl, write_list(L).
% Check of correctness:
not_used(Here) :- w(Here), wn(’- not used!’).
not_used(Here) --> {not_used(Here)}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Statistics option:
init_stats :-
abolish(compile_cputime,3),
get_cputime(U), !,
assert(compile_cputime(start,none,U)).
init_stats.
done_stats :-
get_cputime(S), !,
retract(compile_cputime(OldPlace,OldN,OldU)),
nl,w(’% Cputime between ’),w(OldPlace),write_num(OldN),
w(’ and finish is ’),
Time is S-OldU,
wn(Time),
ttyflush_quintus.
done_stats.
stats(Place, N) :-
get_cputime(S),
compile_option(stats(L)),
(member(Place, L); L=Place),
!,
retract(compile_cputime(OldPlace,OldN,OldU)),
w(’% Cputime between ’),w(OldPlace),write_num(OldN),
489
write_num(none) :- !.
write_num(N) :- w(’-’), w(N).
get_cputime(Time) :- the_system(cprolog), !,
Time is cputime.
get_cputime(Time) :- the_system(quintus), !,
statistics(runtime,[T,_]), % First arg is absolute, second is relative.
Time is T/1000.0.
get_cputime(Time) :- \+the_system(_),
wn(’% I do not know how to get the cputime in this system.’),
Time is 0.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%