[m-dev.] For review: compiler support for parallel conjunction

Fergus Henderson fjh at cs.mu.oz.au
Mon Oct 20 16:10:27 AEST 1997


Thomas Charles CONWAY, you wrote:
> +:- type slot_contents 
> +	--->	ticket			% a ticket (trail pointer)
> +	;	ticket_counter		% a copy of the ticket counter
> +	;	trace_data
> +	;	sync_term
> +	;	lval(lval).

Please document `sync_term'.

> +det_infer_par_conj([], _InstMap0, _SolnContext, _DetInfo, [], det, []).
> +det_infer_par_conj([Goal0 | Goals0], InstMap0, SolnContext, DetInfo, 
> +		[Goal | Goals], Detism, Msgs) :-
> +
> +	det_infer_goal(Goal0, InstMap0, SolnContext, DetInfo,
> +			Goal, DetismA, MsgsA),
> +	
> +	det_infer_par_conj(Goals0, InstMap0, SolnContext, DetInfo,
> +			Goals, DetismB, MsgsB),
> +	determinism_components(DetismB, CanFailB, MaxSolnsB),
> +
> +	determinism_components(DetismA, CanFailA, MaxSolnsA),
> +
> +	det_conjunction_canfail(CanFailA, CanFailB, CanFail),
> +	det_conjunction_maxsoln(MaxSolnsA, MaxSolnsB, MaxSolns),
> +	determinism_components(Detism, CanFail, MaxSolns),
>  	list__append(MsgsA, MsgsB, Msgs).

The code would be easier to read if it was ordered more consistently.
Either put the two calls to determinism_compenents together,
with the one for DetismA first, or put the call to
determinism_components(DetismA, ...) after the call to det_infer_goal.

> +det_report_msg(par_conj_not_det(InferredDetism, GoalInfo), _) -->
> +	{ goal_info_get_context(GoalInfo, Context) },
> +	prog_out__write_context(Context),
> +	{ determinism_components(InferredDetism, CanFail, MaxSoln) },
> +	(
> +		{ CanFail \= cannot_fail }
> +	->
> +		io__write_string("Error: parallel conjunct may fail.\n")
> +	;
> +		{ MaxSoln = at_most_many }
> +	->
> +		prog_out__write_context(Context),
> +		io__write_string("Error: parallel conjunct may have multiple solutions.\n")
> +	;
> +		{ error("strange determinism error for parallel conjunction") }
> +	),
> +	prog_out__write_context(Context),
> +	io__write_string(
> +		"  The current implementation supports only single-solutioned\n"
> +	),
> +	prog_out__write_context(Context),
> +	io__write_string("  non-failing parallel conjunctions.\n").

It is true that in English, any word can be verbed, but I think
"single-solution" would be better than "single-solutioned" here.

Also, shouldn't you report which part of the conjunct can fail
or can have multiple solutions?

> diff -u -r1.42 hlds_goal.m
> --- hlds_goal.m	1997/10/13 08:09:41	1.42
> +++ hlds_goal.m	1997/10/17 04:20:17
> @@ -166,7 +166,15 @@
>  			extra_pragma_info
>  					% Extra information for model_non
>  					% pragma_c_codes; none for others.
> -		).
> +		)
> +
> +	;	par_conj(hlds_goals, store_map)
> +					% parallel conjunction
> +					% The store_map specifies the locations
> +					% in which live variables should be
> +					% stored at the start of the parallel
> +					% conjunction.

At the start?  Are you sure that's correct?

> +	% hlds_out__write_disj is used to write both disjunctions and
> +	% parallel conjunctions.

I think you should rename it as hlds_out__write_goal_list or something
like that.

> +instmap__unify(NonLocals, InstMapList, ModeInfo0, ModeInfo) :-
> +	(
> +		list__member(unreachable - _, InstMapList)
> +	->
> +		mode_info_set_instmap(unreachable, ModeInfo0, ModeInfo)
> +	;
> +		InstMapList = [InstMap - _]
> +	->
> +		mode_info_set_instmap(InstMap, ModeInfo0, ModeInfo)
> +	;
> +		InstMapList = [InstMap0 - _|InstMapList1],
> +		InstMap0 = reachable(InstMapping0)
> +	->
> +		mode_info_get_module_info(ModeInfo0, ModuleInfo0),
> +		set__to_sorted_list(NonLocals, NonLocalsList),
> +		instmap__unify_2(NonLocalsList, InstMap0, InstMapList1,
> +			ModuleInfo0, InstMapping0, ModuleInfo,
> +			InstMapping, ErrorList),
> +		mode_info_set_module_info(ModeInfo0, ModuleInfo, ModeInfo1),
> +		( ErrorList = [FirstError | _] ->
> +			FirstError = Var - _,
> +			set__singleton_set(WaitingVars, Var),
> +			mode_info_error(WaitingVars,
> +				mode_error_par_conj(ErrorList),
> +				ModeInfo1, ModeInfo2)
> +		;
> +			ModeInfo2 = ModeInfo1
> +		),
> +		mode_info_set_instmap(reachable(InstMapping),
> +			ModeInfo2, ModeInfo)
> +	;
> +		ModeInfo = ModeInfo0
> +	).

Please add some comments here.
Procedures that are longer than about 5 lines long tend to be
much easier to read if you break them up into smaller parts
separated by comments describing what each part does.

> +	% instmap__unify_2(Vars, InitialInstMap, InstMaps, ModuleInfo,
> +	%		ErrorList):
> +	%       Let `ErrorList' be the list of variables in `Vars' for
> +	%       there are two instmaps in `InstMaps' for which the inst
> +	%       the variable is incompatible.

... for _which_ there are two instmaps ...
... the inst _of_ the variable ...

> +instmap__unify_2([], _, _, ModuleInfo, InstMap, ModuleInfo, InstMap, []).
> +instmap__unify_2([Var|Vars], InitialInstMap, InstMapList, ModuleInfo0, InstMap0,
> +			ModuleInfo, InstMap, ErrorList) :-
> +	instmap__unify_2(Vars, InitialInstMap, InstMapList, ModuleInfo0,
> +			InstMap0, ModuleInfo1, InstMap1, ErrorList1),
> +	instmap__lookup_var(InitialInstMap, Var, InitialVarInst),
> +	instmap__unify_var(InstMapList, Var, [], Insts, InitialVarInst, Inst,
> +		ModuleInfo1, ModuleInfo, no, Error),
> +	( Error = yes ->
> +		ErrorList = [Var - Insts | ErrorList1],
> +		map__set(InstMap1, Var, not_reached, InstMap)

Please add a comment explaining that line.

> +	% instmap__unify_var(InstMaps, Var, InitialInstMap, ModuleInfo,
> +	%		Insts, Error):
> +	%       Let `Insts' be the list of the inst of `Var' in the
> +	%       corresponding `InstMaps'.  Let `Error' be yes iff
> +	%       there are two instmaps for which the inst of `Var'
> +	%       is incompatible.

The following might be a little clearer:

	%       Let `Insts' be the list containing the inst of `Var'
	%	in each of the corresponding `InstMaps'.  ...

> +build_live_sets_in_goal_2(par_conj(Goals0, _SM), Liveness0, ResumeVars0,
> +		LiveSets0, GoalInfo, ModuleInfo, ProcInfo, Liveness,
> +		ResumeVars, LiveSets) :-
> +	goal_info_get_nonlocals(GoalInfo, NonLocals),
> +	set__union(NonLocals, Liveness0, LiveSet),
> +		% we insert all the union of the live vars and the nonlocals
> +		% each variables is one of:
> +		% 	bound and should be saved;
> +		%	an output of the par conj, so we need a stackslot
> +		%		for the reference;
> +		%	is mentioned, but unused and we wasted a stack slot.

For me, this comment raised more questions than it answered.
Could you try rephrasing it more clearly?

> +		% XXX Value numbering doesn't handle fork [yet] so
> +		% set DontValueNumber to yes.
...
> +		% XXX Value numbering doesn't handle join_and_terminate [yet] so
> +		% set DontValueNumber to yes.
...
> +		% XXX Value numbering doesn't handle join_and_continue [yet] so
> +		% set DontValueNumber to yes.

This is not a good solution...

> +	;	join_and_terminate(lval)
> +			% Signal that this thread of execution has finished in
> +			% the current parallel conjunction, then terminate it.
> +			% The synchronisation term is pointed to by the
> +			% given lval.

Is the lval really a pointer??

I think "is specified by" would be clearer than "is pointed to by".

Or, if the argument really is a pointer, it should be an rval, not an lval.

> +	;	join_and_continue(lval, label)
> +			% Signal that this thread of execution has finished
> +			% in the current parallel conjunction, then branch to
> +			% the given label. The synchronisation
> +			% term is pointed to by the given lval.

Ditto.

> -	% Locate all the labels which are the continutation labels for calls
> -	% or nondet disjunctions, and store them in ContLabelSet.
> +	% Locate all the labels which are the continutation labels for calls,
> +	% nondet disjunctions forks or joins, and store them in ContLabelSet.

s/continutation/continuation/
s/ forks/, forks/


> +	;	mode_error_bind_var(var, inst, inst, lock_context)
>  			% attempt to bind a non-local variable inside
> +			% a negated context, or attempt to re-bind a variable
> +			% in a parallel conjunct
...
> +	;	mode_error_parallel_var(var, inst, inst)
> +			% attempt to bind a non-local variable that has already
> +			% been bound in another parallel conjunct.
> +	.

I don't understand -- what is the difference between these two?
Are they both used?  Which is used when?

> +:- pred report_mode_error_par_conj(mode_info, merge_errors,
> +				io__state, io__state).
> +:- mode report_mode_error_par_conj(mode_info_no_io, in, di, uo) is det.
> +
> +report_mode_error_par_conj(ModeInfo, ErrorList) -->
> +	{ mode_info_get_context(ModeInfo, Context) },
> +	mode_info_write_context(ModeInfo),
> +	prog_out__write_context(Context),
> +	io__write_string("  incompatible bindings in parallel conjunction.\n"),
> +	write_merge_error_list(ErrorList, ModeInfo).

Every error message should include an indication of what sort of error
it is.  Hence that should be

	io__write_string("  mode error: incompatible bindings ...

> +report_mode_error_parallel_var(ModeInfo, Var, _VarInst, _Inst) -->
> +	{ mode_info_get_context(ModeInfo, Context) },
> +	{ mode_info_get_varset(ModeInfo, VarSet) },
> +	mode_info_write_context(ModeInfo),
> +	prog_out__write_context(Context),
> +	io__write_string("  error: attempt to bind a variable already bound in another\n"),
> +	prog_out__write_context(Context),
> +	io__write_string("  parallel conjunct.\n"),

s/error/mode error/

Also wrap the line a few words earlier.

> +	prog_out__write_context(Context),
> +	io__write_string("  the variable concerned was `"),

s/the/The/

> +:- type lock_context
> +		--->	negation
> +		;	if_then_else
> +		;	par_conj
> +		.

Hmm, I think there should be another alternative, `lambda_goal'.

> --- modecheck_unify.m	1997/10/13 10:24:18	1.24
> +++ modecheck_unify.m	1997/10/17 04:20:31
> @@ -381,10 +381,11 @@
>  	% lock the non-locals
>  	% (a lambda goal is not allowed to bind any of the non-local
>  	% variables, since it could get called more than once)
> +	% `negation' as the locked_var_context is a place holder.
>  	Goal0 = _ - GoalInfo0,
>  	goal_info_get_nonlocals(GoalInfo0, NonLocals0),
>  	set__delete_list(NonLocals0, Vars, NonLocals),
> -	mode_info_lock_vars(NonLocals, ModeInfo2, ModeInfo3),
> +	mode_info_lock_vars(NonLocals, negation, ModeInfo2, ModeInfo3),

You should use `lambda_goal' here rather than `negation'.

> Index: compiler/modes.m
> ===================================================================
> RCS file: /home/staff/zs/imp/mercury/compiler/modes.m,v
> retrieving revision 1.205
> diff -u -r1.205 modes.m
> --- modes.m	1997/09/15 21:12:24	1.205
> +++ modes.m	1997/09/16 00:14:30
> @@ -711,6 +711,34 @@
>  	),
>  	mode_checkpoint(exit, "conj").
>  
> +	% To modecheck a parallel conjunction, we modecheck each
> +	% conjunct independently (just like for disjunctions).
> +	% To make sure that we don't try to bind a variable more than
> +	% once (by binding it in more than one conjunct), we maintain a
> +	% datastructure that keeps track of three things:
> +	%	the set of variables that are nonlocal to the conjuncts
> +	%	(which may be a superset of the nonlocals of the par_conj
> +	%	as a whole);
> +	%	the set of nonlocal variables that have been bound in the
> +	%	current conjunct; and
> +	%	the set of variables that were bound in previous conjuncts.
> +	% When binding a variable, we check that it wasn't in the set of
> +	% variables bound in other conjuncts, and we add it to the set of
> +	% variables bound in this conjunct.
> +	% At the end of the conjunct, we add the set of variables bound in
> +	% this conjunct to the set of variables bound in previous conjuncts
> +	% and set the set of variables bound in the current conjunct to
> +	% empty.

Hmm, what about examples like

	:- pred p(out) is det.
	p(X) :-
		(
			X = a,
			...
			error("blah")
		&
			X = b,
			...
		).

?

> Index: library/nc_builtin.nl
> ===================================================================
> RCS file: /home/staff/zs/imp/mercury/library/nc_builtin.nl,v
> retrieving revision 1.15
> diff -u -r1.15 nc_builtin.nl
> --- nc_builtin.nl	1997/07/27 15:06:59	1.15
> +++ nc_builtin.nl	1997/09/08 22:54:57
> @@ -50,6 +50,8 @@
>  :- op(1179, xfy, (--->)).
>  :- op(1175, xfx, (::)).
>  
> +:- op(1025, xfy, (&)).
> +
>  :- op(950, fxy, (lambda)).
>  
>  :- op(400, yfx, (rem)).
> Index: library/sp_builtin.nl
> ===================================================================
> RCS file: /home/staff/zs/imp/mercury/library/sp_builtin.nl,v
> retrieving revision 1.13
> diff -u -r1.13 sp_builtin.nl
> --- sp_builtin.nl	1997/07/27 15:07:13	1.13
> +++ sp_builtin.nl	1997/09/08 22:54:57
> @@ -53,6 +53,7 @@
>  :- op(1199, fx, (mode)).
>  :- op(1199, fx, (inst)).
>  :- op(1179, xfy, (--->)).
> +:- op(1025, xfy, (&)).
>  :- op(975, xfx, ('::')).
>  :- op(700, xfx, ( \= ) ).
>  :- op(500, fx, ( \ ) ).

You also need to modify library/ops.m and compiler/mercury_to_mercury.m.

-- 
Fergus Henderson <fjh at cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3         |     -- the last words of T. S. Garp.



More information about the developers mailing list