register TAGGED *tmp;
tmp = w->heap_top + VARSIZE;
PushOnHeap(w->heap_top,Tagify(tmp,STR));
PushOnHeap(w->heap_top,F);
unify_list
This represents a last structure argument that is a list.
read mode:
{ register TAGGED Ds, lst;
DerefHVA(Ds,Ref(s));
if(IsVar(Ds)) {
Make_LST(w->heap_top,lst);
Bind(Ds,lst,goto fail;);
goto write_instructions;
} else {
if(IsLST(Ds)) { s = GetCar(Ds);
goto instructions;
} else
goto fail;
} }
write mode:
{ register TAGGED *tmp;
tmp = w->heap_top + VARSIZE;
PushOnHeap(w->heap_top,Tagify(tmp,LST));
}goto write_instructions;
the environment for the following parallel phase and are as such executed by the sequential worker.
A list recursive program such as
map([],[]).
map([X|Xs],[Y|Ys]) :- rel(X,Y), map(Xs, Ys).
is compiled to extended WAM code according to the following schema:.
map/2: switch_on_term Lv La Ll fail Lv: try La
trust L1
La: /* sequential code for first clause */
L1: /* sequential code for second clause */
Ll: build_rec_poslist X0 X2 X3 X0 % create vector list Xs build_poslist X1 X2 X4 X1 % create vector list Ys /* code to execute rel/2 by each worker */
execute map/2 % execute the base case
build_rec_poslist(a,n,v,t)
This instruction is used for the main recursive argument when compiling list recursion.
It veries that the term referred to by Xa is a list. The list is traversed and a vector list is built (on the heap) of its elements. A pointer to the vector list is stored in Xv.
The length of the vector list is stored in Xn (it is used later when constructing other vector lists and by each worker to determine the bounds of the recursion).
/* Traverse recursion list and build vector */
Make_LST(w->heap_top,X(v));
/* build vector */
register TAGGED List;
register s32 len;
Deref(List, X(a));
PushOnHeap(w->heap_top,Ref(GetCar(List)));
Deref(List,Ref(GetCdr(List)));
for(len = 1 ; IsLST(List) ; len++)
PushOnHeap(w->heap_top,Tagify(w->heap_top+VARSIZE,LST));
PushOnHeap(w->heap_top,Ref(GetCar(List)));
Deref(List,Ref(GetCdr(List)));
PushOnHeap(w->heap_top,List);
/* Last tail */
X(t) = List;
/* Vector length */
X(n) = Make_Integer(len);
goto instructions;
build_poslist(a,n,v,t)
This instruction is used for an argument that performs list recursion but is not the main recursive argument. The term pointed to by Xa is veried to be a list of length at lest Xn. If Xa contains a list shorter than Xn elements, ending with an unbound variable, then it is extended with distinct variables to the length Xn. A vector list is constructed (on the heap) of itsXn rst elements. A pointer to the vector list is stored in Xv. The Xnth tail of the vector list is set to theXnth tail of the list referred to by
Xa. A reference to the Xnth tail is also stored in Xt. (This reference is needed when executing the base case of the predicate.)
/* Traverse recursion list and build vector */
Make_LST(w->heap_top,X(v));
/* build vector */
{ register TAGGED List;
register s32 len, vectorsize;
vectorsize = GetNumber(X(n));
Deref(List, X(a));
len = 0;
for( ; IsLST(List) && len < (vectorsize - 1) ; len++) { PushOnHeap(w->heap_top,Ref(GetCar(List)));
PushOnHeap(w->heap_top,Tagify(w->heap_top+VARSIZE,LST));
Deref(List,Ref(GetCdr(List)));
}
if(IsLST(List))
{ PushOnHeap(w->heap_top,Ref(GetCar(List)));
X(t) = Ref(GetCdr(List));
PushOnHeap(w->heap_top,X(t));
else}
{ if(!unify(List,Tagify(w->heap_top,LST),w)) goto fail;
for( ; len < (vectorsize - 1) ; len++) { CreateHVA(w->heap_top,w);
PushOnHeap(w->heap_top,Tagify(w->heap_top+VARSIZE,LST));
} CreateHVA(w->heap_top,w);
LoadHVA(w->heap_top,X(t),w);
} }
goto instructions;
build_poslist_value(a,n,v,t)
This instruction is used for a subsequent argument that performs list recursion, using the same element as an earlier list recusive argument, e.g., the third argument of the second clause of append/3.
append([X|Xs],Y,[X|Zs]) :- append(Xs,Y,Zs).
The term referred to by Xa is veried to be a list. The rstXn elements of the list Xa are unied with the corresponding elements of the vector list referred to by Xv. If the list Xa is shorter thanXn elements then the last tail of the list is unied with a list of the remaining elements of the vector list, ending with an unbound variable. The Xnth tail of Xa is stored inXt.
/* Match list with vector */
{ TAGGED VectorList, List;
s32 vectorsize;
Deref(List,X(a));
Deref(VectorList,X(v));
vectorsize = GetNumber(X(n));
while(vectorsize) { if(IsLST(List)) {
Unify(Ref(GetCar(List)),Ref(GetCar(VectorList)));
Deref(List,Ref(GetCdr(List)));
VectorList = Ref(GetCdr(VectorList));
} else break;
vectorsize--;
}
if(vectorsize) {
register TAGGED Copy;
Make_LST(w->heap_top,Copy);
PushOnHeap(w->heap_top,Ref(GetCar(VectorList)));
vectorsize -= 1;
while(vectorsize--) {
PushOnHeap(w->heap_top,Tagify(w->heap_top+VARSIZE,LST));
VectorList = Ref(GetCdr(VectorList));
PushOnHeap(w->heap_top,Ref(GetCar(VectorList)));
}
LoadHVA(w->heap_top,X(t),w);
if(!unify(List,Copy,w)) goto fail;
} else { X(t) = List;
} }
goto instructions;
build_neglist(a,n,v,t)
This instruction is used for a list that is built in the clause body using an accumulator, e.g., the second argument of the predicate below.
p([X|Xs],Acc,[Y|Ys]) :- p(Xs,[Y|Acc],Ys).
A `reverse list' vector list of lengthXn is created on the heap. A reference to the vector list is stored in Xv. A reference to the rst element of the vector list (viewed as a list) is stored in Xt. The last tail of the vector list (viewed as a list) is set toXa.
vectorsize = GetNumber(X(n)) - 1;
X(v) = Tagify(w->heap_top,LST);
CreateHVA(w->heap_top,w);
PushOnHeap(w->heap_top,X(a));
while(vectorsize--)
{ CreateHVA(w->heap_top,w);
PushOnHeap(w->heap_top,Tagify(w->heap_top-3*VARSIZE,LST));
}
X(t) = Tagify(w->heap_top-2*VARSIZE,LST);
goto instructions;
build_neglist_value(a,n,v,m,t)
This instruction is used for a subsequent list that is built (using the same element as an earlier list argument) in the clause body using an accumulator, e.g., the second argument of reverse/3 below.
reverse([X|Xs],Acc,Rev) :- reverse(Xs,[X|Acc],Rev).
The instruction, analogous tobuild neglist, creates a `reverse list' vector listXw, but elements from the vector list referred to by Xv are stored at the corresponding vector list positions in Xw.
vectorsize = GetNumber(X(n)) - 1;
Vector = RemoveTag(X(m),LST);
X(v) = Tagify(w->heap_top,LST);
PushOnHeap(w->heap_top,Ref(Vector));
Vector += 2*VARSIZE;
PushOnHeap(w->heap_top,X(a));
while(vectorsize--)
{ PushOnHeap(w->heap_top,Ref(Vector));
Vector += 2*VARSIZE;
PushOnHeap(w->heap_top,Tagify(w->heap_top-3*VARSIZE,LST));
}
X(t) = Tagify(w->heap_top-2*VARSIZE,LST);
goto instructions;
build_variables(a,n,v,t)
This instruction is used for a list that is built in the clause body using an accumulator, e.g., the second argument of the predicate below.
p([X|Xs],Acc,[Y|Ys]) :- p(Xs,[Y|Acc],Ys).
A `reverse list' vector list of lengthXn is created on the heap. A reference to the vector list is stored in Xv. A reference to the rst element of the vector list (viewed as a list) is stored in Xt. The last tail of the vector list (viewed as a list) is set toXa.
vectorsize = GetNumber(X(n));
Make_LST(w->heap_top,X(v));
{ register TAGGED varvar;
Deref(varvar,X(a));
if(IsSVA(varvar))
{ register TAGGED newvar;
LoadHVA(w->heap_top,newvar,w);
Bind_SVA(varvar,newvar);
else}
PushOnHeap(w->heap_top,varvar);
}
for(x = 1 ; x < vectorsize ; x++) {
PushOnHeap(w->heap_top,Tagify(w->heap_top+VARSIZE,LST));
CreateHVA(w->heap_top,w);
}
PushOnHeap(w->heap_top,Tagify(w->heap_top+VARSIZE,LST));
LoadHVA(w->heap_top,X(t),w);
CreateHVA(w->heap_top,w);
goto instructions;
4.7.2 PutInstructions
These instructions are used by each worker in the parallel phase to fetch the arguments of the recursion level from the sequential worker's heap. The program
map([],[]).
map([X|Xs],[Y|Ys]) :- rel(X,Y), map(Xs,Ys).
is compiled to extended WAM code according to the following schema:
map/2: switch_on_term Lv La Ll fail Lv: try La
trust L1
La: /* code for first clause */
L1: /* code for second clause */
Ll: build_rec_poslist X0 X2 X3 X0 % build vector list Xs build_poslist X0 X2 X4 X1 % build vector list Ys start_left_body L2 3 X2 X3 X4 % start par. phase
execute map/2 % execute base case
L2: initialize_left 1 % initialize worker L3: spawn_left 1 X2 G2 % while X2 < G2 do
put_nth_head G3 X2 0 X0 % X <- Xs[X2+0]
put_nth_head G4 X2 0 X1 % Y <- Ys[X2+0]
call rel/2 0 % rel(X,Y)
jump L3 % next rec. level
put_nth_head(v,l,o,i)
This instruction stores element number Xl + o of the vector list, pointed to by the sequential worker's register Xv, in Xi. Nonzero values of o are used by clauses that reduce the recursion argument by more than 1, or by clauses that need to refer to values produced by other recursion levels.
X(i) = Ref(GetNthHead(G(v),
(GetNumber(X(l)) + o)));
goto instructions;
put_nth_tail(v,l,o,i)
This instruction stores tail numberXl +o of the vector list, pointed to by the sequential worker's register Xv, inXi.
X(i) = Ref(GetNthTail(G(v),
(GetNumber(X(l)) + o)));
goto instructions;
put_global_arg(g,i)
This instruction stores the value of the sequential worker's register Xg in Xi.
X(g) = G(i);
goto instructions;
unify_nth_head(v,l,o)
This instruction writes element number Xl + o of the vector list, pointed to by the sequential worker's register Xv, onto the heap. This instruction never occurs in read mode.
Unify(Ref(s),Ref(GetNthHead(G(v),
(GetNumber(X(f))+ o))));
s += VARSIZE;
goto instructions;
unify_nth_tail(v,l,o)
This instruction writes tail numberXl +o of the vector list, pointed to by the sequential worker's register Xv, (viewed as a list) on the heap. This instruction never occurs in read mode.
Unify(Ref(s),Ref(GetNthTail(G(v),
(GetNumber(X(l))+o))));
s += VARSIZE;
goto instructions;
unify_global_arg(g)
This instruction writes the value of the sequential worker's register Xg on the heap.
This instruction never occurs in read mode.
Unify(Ref(s), G(g));
s += VARSIZE;
goto instructions;
4.7.3 ProceduralInstructions
These instructions are used to initiate parallel execution and to switch from one recursion level to the next. Thespawn instructions corresponds to thewhileinstruction of C. The program
p([],Y,Y,0).
p([X|Xs], Ys, Zs, S1) :-f(X, Y),
p(Xs, [Y|Ys], Zs, S2), q(S2, S1).
is compiled to extended WAM code according to the following schema:.
p/4: switch_on_term Lv La Ll fail Lv: try La
trust L1
La: /* sequential code for first clause */
L1: /* sequential code for second clause */
Ll: allocate
build_rec_poslist X0 X4 X5 X0 % create vector list Xs build_neglist X1 X4 X6 X1 % create vector list Ys build_variables X3 X4 X7 X3 % create var. vector list /* code to save X4, X5, X6, X7 in Y-registers */
% start par. phase start_left_body L2 8 X0 X1 X2 X3 X4 X5 X6 X7 4
call p/4, 4 % execute base case
/* code to restore X4, X5, X6, X7 from Y-registers */
% start par. phase start_right_body X4 L4 1 X4 0
deallocate proceed
L2: initialize_left 1 % initialize worker L3: spawn_left 1 X2 G4 % while ++X2 < G4 do
/* code to set up the arguments for the call to f/2 */
call f/2, 0
jump L3 % next recursion level
L4: initialize_right 1 G4 % initialize worker L5: spawn_right 1 X2 % while --X2 > 0 do
/* code to set up the arguments for the call to q/2 */
call q/2, 0
jump L5 % next recursion level
start_right_body(n,L,NrLive,LiveList,i)
This instruction initiates parallel execution of the `right body' of a predicate. The length of the recursion list is given in Xn. The code at label L is run in parallel by all active workers. The sequential execution continues with the next instruction when the parallel phase is nished. To support garbage collection during a parallel execution all live registers are given in a sorted list LiveList of length NrLive. The size of the environment, i, is an optional argument..
Deref(Num, X(n));
if(!IsNUM(Num)) goto fail;
if(w->global->scheduling == DYNAMIC)
{ w->global->sched_level = GetNumber(Num);
}
/* Reset level count, this could be done in parallel but
* would require synchronization.
*/
for(i=0 ;
i < w->global->active_workers ; w->level[i++] = GetNumber(Num));
goto start_body;
start_left_body(L,NrLive,LiveList,i)
This instruction starts parallel execution of the `left body' of a predicate. The code at label L is run in parallel by all active workers. The sequential execution continues with the next instruction when the parallel phase is nished. To support garbage collection during a parallel execution all live registers are given in a sorted list LiveList of length NrLive. The size of the environment, i, is an optional argument..
if(w->global->scheduling == DYNAMIC) { w->global->sched_level = 0;
}
/* Reset level count, this could be done in parallel but
* would require synchronization.
*/
for(i=0 ; i < w->global->active_workers ; w->level[i++] = 0);
start_body:
{ w->global->parallel_start.type = W_EXECUTE_CODE;
w->global->parallel_start.code = L;
w->global->global_fail = FALSE;
/* Activate worker backtracking, mainly to restore their
* heaps.
PushOnTrail(w->trail_top,Tagify(NULL,STR));*/
ActivateWorkers(w);
if(w->global->global_fail) goto fail;
#if defined(TIMESTAMP) || defined(UNBOUND) { register int i;
i = w->global->active_workers;
while(i--) {
if(w[i].time > w->time) { w->time = w[i].time;
} }
#endif}
}goto instructions;
initialize_right(S,n)
This instruction initializes a worker for parallel execution. The step S, the number of recursion levels (stored in the sequential worker's register Xn), and the worker num-ber are used for calculating the initial recursion level in static schedule mode. When dynamic scheduling is used this instruction is ignored.
if(w->global->scheduling == STATIC)
{ w->level[w->pid-1] = GetNumber(G(n)) 1 -S*(w->pid-1) +
S * w->global->active_workers;
w->direction = RIGHT_BODY;} goto instructions;
initialize_left(S)
This instruction initializes a worker for parallel execution. The step S and the worker number is used for calculating the number of the initial recursion level in static schedul-ing mode. In dynamic schedulschedul-ing mode this instruction is ignored.
if(w->global->scheduling == STATIC) { w->level[w->pid-1] =S * (w>pid1)
-S * w->global->active_workers;
w->direction = LEFT_BODY;} goto instructions;
spawn_right(S,i)
This instruction calculates the number of the next recursion level and stores it in Xi.
The new level is calculated from S and the internal level count. If the number of the new level is negative, the parallel computation is nished and the worker awaits the next parallel phase, otherwise processing continues with the next instruction.
if(w->global->global_fail) goto done;
if(w->global->scheduling == STATIC)
{ level = w->level[w->pid-1] - S*w->global->active_workers;
else}
{ GrabLevel(level,w);
w->global->sched_level = level - S;
}
w->level[w->pid-1] = level;
X(i) = Make_Integer(level);
if (level < 0) goto done;
goto instructions;
spawn_left(S,i,n)
This instruction calculates the number of the next recursion level and stores it in Xi.
The number of the new level is calculated from S and the internal level count. If the number of the new level is greater than the value stored in the sequential worker's registerXn, the parallel computation is nished and the worker awaits the next parallel phase, otherwise processing continues with the next instruction.
if(w->global->global_fail) goto done;
if(w->global->scheduling == STATIC)
{ level = w->level[w->pid-1] + S*w->global->active_workers;
else}
{ GrabLevel(level,w)
w->global->sched_level = level + S;
}
w->level[w->pid-1] = level;
X(i) = Make_Integer(level);
if (level >= GetNumber(G(n))) goto done;
goto instructions;