[m-dev.] for review: accumulator introduction

Peter Ross petdr at cs.mu.OZ.AU
Fri Jun 4 14:14:18 AEST 1999


Hi,

Simon could you please review this?

Pete.


===================================================================



Estimated hours taken: 500

Add a new pass to the compiler, that attempts to introduce accumulators
into a procedure so as to make that procedure tail recursive.

compiler/accumulator.m:
    The transformation.

compiler/mercury_compile.m:
    Call the transformation.

compiler/options.m:
    Add the option to turn the transformation on.

doc/user_guide.texi:
    Document the option.

profiler/demangle.m:
util/mdemangle.c:
    Demangle the accumulator version of the procedure labels.

compiler/notes/compiler_design.html:
    Add the new pass to the documentation.

Index: compiler/accumulator.m
===================================================================
RCS file: accumulator.m
diff -N accumulator.m
--- /dev/null	Fri Jun  4 14:07:44 1999
+++ accumulator.m	Fri Jun  4 13:57:40 1999
@@ -0,0 +1,1785 @@
+%-----------------------------------------------------------------------------%
+% Copyright (C) 1999 The University of Melbourne.
+% This file may only be copied under the terms of the GNU General
+% Public License - see the file COPYING in the Mercury distribution.
+%-----------------------------------------------------------------------------%
+%
+% Module:	accumulator
+% Main authors: petdr
+%
+% Attempts to transform a single proc to a tail recursive form by
+% introducing accumlators.
+%
+% The transformation is described more fully in papers/tail_recursive.
+%
+% Basically the transformation is to locate predicates in the following
+% form.
+%
+% p(X,Ys) :-
+%         minimal(X),
+%         base(Ys).
+% p(X, Ys) :-
+%         decompose(X, Xhead, Xrest),
+%         process(Xhead, Hs),
+%         p(Xrest, Y0s),
+%         compose(Hs, Y0s, Ys).
+%
+% and transform them into this form
+%
+% p(X,Ys) :-
+%         minimal(X),
+%         base(Ys).
+% p(X, Ys) :-
+%         decompose(X, Xhead, Xrest),
+%         process(Xhead, Hs),
+%         p'(Xrest, Ys, Hs).
+% 
+% p'(X, Ys, As) :-
+%         minimal(X),
+%         base(Y0s),
+%         compose(As, Y0s, Ys).
+% p'(X, Ys, As) :-
+%         decompose(X, Xhead, Xrest),
+%         process(Xhead, Hs),
+%         compose(As, Hs, A1s),
+%         p'(Xrest, Ys, A1s).
+%
+% Any variable that ends with an 's' represents a set of variables.
+% The constraint on the transformation is that the compose goal must
+% obey the following law
+%      
+%	compose(I, B, C, BC), compose(I, A, BC, ABC)
+%		<=> compose(I, A, B, AB), compose(I, AB, C, ABC)
+%
+% Note that the transformation will leave construction unifications
+% after the recursive call if '--optimize-constructor-last-call' is
+% enabled.
+%
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- module accumulator.
+
+:- interface.
+
+:- import_module hlds_module, hlds_pred, io.
+
+:- pred accumulator__process_proc(pred_id::in, proc_id::in, proc_info::in,
+		proc_info::out, module_info::in, module_info::out,
+		io__state::di, io__state::uo) is det.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module goal_util, globals, hlds_data, hlds_goal, hlds_out.
+:- import_module inst_match, instmap, mode_util, options, prog_data, prog_util.
+
+:- import_module assoc_list, bool, list, map, multi_map.
+:- import_module require, set, std_util, term, varset.
+
+%-----------------------------------------------------------------------------%
+
+:- type base_goal
+	--->	base(
+			hlds_goals
+		).
+
+:- type rec_goal
+	--->	recursive(
+			a_goals,	% Decompose, Process
+			a_goal,		% Recursive call
+			a_goals		% Compose calls
+		).
+
+:- type split_result
+	--->	recursive(
+			hlds_goals,	% Decompose, Process
+			hlds_goal,	% Recursive call
+			hlds_goals	% Compose calls
+		).
+
+:- type a_goal  == goal(hlds_goal).
+:- type a_goals == goal(hlds_goals).
+
+:- type goal(T)
+	--->	goal(
+			T,		% goal/s
+			instmap		% instmap at the start of the goal
+		).
+
+:- type top_level
+	--->	switch_base_rec
+	;	switch_rec_base
+	;	disj_base_rec
+	;	disj_rec_base
+	;	ite_base_rec
+	;	ite_rec_base.
+
+:- type subst == map(prog_var, prog_var).
+
+:- type assoc_info
+	--->	assoc_info(
+			set(prog_var),	% Static set
+					% A static variable is one whose
+					% value is set before the
+					% recursive call.
+
+			set(prog_var),	% Dynamic set
+					% If it isn't static, it's dynamic.
+			module_info,
+			prev_call_map,
+			orig_dynvar_map,
+			subst,		% Y0s -> As
+			subst,		% Hs -> As
+			set(prog_var)	% Ys
+		).
+
+	% is the pred commutative?
+:- type commutative == bool.
+
+:- type prev_call
+	--->	prev_call(
+			pred_id,
+			proc_id,
+			commutative
+		).
+
+	% If a variable is constructed from a chain of calls, what are
+	% the details of the previous call in the chain.
+:- type prev_call_map == map(prog_var, prev_call).
+	
+	% Given a dynamic variable, from which dynamic variable was it
+	% descended (ie which variable in Y0s).
+:- type orig_dynvar_map == map(prog_var, prog_var).
+
+%-----------------------------------------------------------------------------%
+
+
+	%
+	% accumulator__process_proc
+	%
+	% Attempt to transform one procedure into a accumulator
+	% recursive form.
+	%
+accumulator__process_proc(PredId, ProcId, ProcInfo0, ProcInfo, 
+		ModuleInfo0, ModuleInfo) -->
+	globals__io_lookup_bool_option(optimize_constructor_last_call,
+		DoLCO),
+	(
+		{ module_info_pred_info(ModuleInfo0, PredId, PredInfo) },
+		{ accumulator__attempt_transform(ProcId, ProcInfo0, PredId,
+				PredInfo, DoLCO, ModuleInfo0, ProcInfo1,
+				ModuleInfo1) }
+	->
+		globals__io_lookup_bool_option(very_verbose, VeryVerbose),
+		( 
+			{ VeryVerbose = yes }
+		->
+			io__write_string("% Accumulators introduced into "),
+			hlds_out__write_pred_id(ModuleInfo1, PredId),
+			io__write_string("\n")
+		;
+			[]
+		),
+
+		{ ProcInfo   = ProcInfo1 },
+		{ ModuleInfo = ModuleInfo1 }
+	;
+		{ ProcInfo   = ProcInfo0 },
+		{ ModuleInfo = ModuleInfo0 }
+	).
+
+	%
+	% accumulator__attempt_transform/8 is only true if the current
+	% proc has been transformed to call the newly created
+	% accumulator proc.
+	%
+:- pred accumulator__attempt_transform(proc_id::in, proc_info::in,
+		pred_id::in, pred_info::in, bool::in, module_info::in,
+		proc_info::out, module_info::out) is semidet.
+
+accumulator__attempt_transform(ProcId, ProcInfo0, PredId, PredInfo0,
+				DoLCO, ModuleInfo0, ProcInfo, ModuleInfo) :-
+	proc_info_goal(ProcInfo0, Goal0),
+	proc_info_headvars(ProcInfo0, HeadVars),
+	proc_info_get_initial_instmap(ProcInfo0, ModuleInfo0, InitialInstMap),
+
+	accumulator__simplify(Goal0, Goal),
+	accumulator__rearrange_goal(PredId, ProcId, Goal, InitialInstMap,
+			ModuleInfo0, GoalType, Base, Rec),
+
+	accumulator__create_accumulator_pred(Rec, PredInfo0, ProcInfo0,
+			HstoAs_Subst, NewPredId, NewProcId, NewPredName,
+			ModuleInfo0, ModuleInfo1),
+
+	accumulator__transform(GoalType, Base, Rec, Goal, DoLCO, ModuleInfo1,
+			HeadVars, HstoAs_Subst, NewPredId, NewProcId,
+			NewPredName, OrigGoal, AccGoal),
+
+	accumulator__update_accumulator_pred(NewPredId, NewProcId, AccGoal,
+			ModuleInfo1, ModuleInfo),
+
+	proc_info_set_goal(ProcInfo0, OrigGoal, ProcInfo).
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+	%
+	% accumulator__simplify
+	%
+	% Simplify the goal to make it more amenable to introducing
+	% accumulators.
+	%
+	% At the moment all this does is remove any extra disj/conj
+	% wrappers around the top level goal.
+	%
+	% Future work is for this code to rearrange code with multiple
+	% base and recursive cases into a single base and recursive
+	% case.
+	%
+:- pred accumulator__simplify(hlds_goal, hlds_goal).
+:- mode accumulator__simplify(in, out) is det.
+
+accumulator__simplify(Goal0, Goal) :-
+	(
+		(
+			Goal0 = conj([Goal1]) - _
+		;
+			Goal0 = disj([Goal1], _) - _
+		)
+	->
+		accumulator__simplify(Goal1, Goal)
+	;
+		Goal = Goal0
+	).
+
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+	%
+	% This predicate is meant to take the original goal and
+	% rearrange it into a standard form that can be used in the rest
+	% of module.
+	%
+:- pred accumulator__rearrange_goal(pred_id::in, proc_id::in,
+		hlds_goal::in, instmap::in, module_info::in,
+		top_level::out, base_goal::out, rec_goal::out) is semidet.
+
+accumulator__rearrange_goal(PredId, ProcId, Goal, InitialInstMap, ModuleInfo,
+		Type, Base, Rec) :-
+	(
+		Goal = switch(_Var, _CanFail, Cases, _StoreMap) - _GoalInfo,
+		Cases = [case(_IdA, GoalA), case(_IdB, GoalB)],
+		goal_to_conj_list(GoalA, GoalAList),
+		goal_to_conj_list(GoalB, GoalBList)
+	->
+		(
+			accumulator__split_recursive_case(PredId, ProcId,
+					InitialInstMap, ModuleInfo,
+					GoalAList, Rec0)
+		->
+			Type = switch_rec_base,
+			Base = base(GoalBList),
+			Rec = Rec0
+		;
+			accumulator__split_recursive_case(PredId, ProcId,
+					InitialInstMap, ModuleInfo,
+					GoalBList, Rec0)
+		->
+			Type = switch_base_rec,
+			Base = base(GoalAList),
+			Rec = Rec0
+		;
+			fail
+		)
+	;
+		fail
+	).
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+	%
+	% accumulator__create_accumulator_pred
+	%
+	% Create a new predicate which is an accumulator version of the
+	% current proc being looked at.
+	%
+:- pred accumulator__create_accumulator_pred(rec_goal::in, pred_info::in,
+		proc_info::in, subst::out, pred_id::out, proc_id::out,
+		sym_name::out, module_info::in, module_info::out) is det.
+
+accumulator__create_accumulator_pred(RecGoal, PredInfo, ProcInfo, HstoAs_Subst,
+		NewPredId, NewProcId, NewPredName, ModuleInfo0, ModuleInfo) :-
+
+	proc_info_get_initial_instmap(ProcInfo, ModuleInfo0, InstMap0),
+
+	accumulator__acc_proc_info(RecGoal, InstMap0, ProcInfo, HstoAs_Subst,
+			NewTypes, NewProcInfo),
+	accumulator__acc_pred_info(NewTypes, NewProcInfo, PredInfo, NewProcId,
+			NewPredInfo),
+
+	pred_info_name(NewPredInfo, NewPredName0),
+	NewPredName = unqualified(NewPredName0),
+
+	module_info_get_predicate_table(ModuleInfo0, PredTable0),
+	predicate_table_insert(PredTable0, NewPredInfo, NewPredId, PredTable),
+	module_info_set_predicate_table(ModuleInfo0, PredTable, ModuleInfo).
+
+
+	%
+	% accumulator__acc_proc_info
+	%
+	% Construct a proc_info for the introduced predicate, it also
+	% creates the substitutions that maps between each variable that
+	% is a member of Hs and those in As.
+	%
+:- pred accumulator__acc_proc_info(rec_goal::in, instmap::in, proc_info::in,
+		subst::out, list(type)::out, proc_info::out) is det.
+
+accumulator__acc_proc_info(recursive(DP, _R, C), InstMap0,
+		ProcInfo, HstoAs_Subst, NewTypes, NewProcInfo) :-
+
+		% ProcInfo Stuff that must change.
+	proc_info_varset(ProcInfo, VarSet0),
+	proc_info_vartypes(ProcInfo, VarTypes0),
+	proc_info_headvars(ProcInfo, HeadVars0),
+	proc_info_argmodes(ProcInfo, HeadModes0),
+
+	proc_info_inferred_determinism(ProcInfo, Detism),
+	proc_info_goal(ProcInfo, Goal),
+	proc_info_context(ProcInfo, Context),
+	proc_info_typeinfo_varmap(ProcInfo, TVarMap),
+	proc_info_typeclass_info_varmap(ProcInfo, TCVarsMap),
+	proc_info_args_method(ProcInfo, ArgsMethod),
+
+	accumulator__extra_vars_for_recursive_call(DP, C, Vars),
+
+	DP = goal(DPGoals, _DPInstMap),
+	goal_list_instmap_delta(DPGoals, InstMapDelta),
+	instmap__apply_instmap_delta(InstMap0, InstMapDelta, InstMap),
+
+	accumulator__new_acc_var(Vars, InstMap, VarSet0, VarTypes0,
+			HstoAs_Subst, NewHeadVars, VarSet,
+			VarTypes, NewHeadModes),
+
+	list__append(HeadVars0, NewHeadVars, HeadVars),
+	list__append(HeadModes0, NewHeadModes, HeadModes),
+
+	list__map(map__lookup(VarTypes), NewHeadVars, NewTypes),
+	
+	proc_info_create(VarSet, VarTypes, HeadVars, HeadModes, Detism, Goal,
+	                Context, TVarMap, TCVarsMap, ArgsMethod, NewProcInfo).
+
+	%
+	% accumulator__new_acc_var(Hs, IM, VS0, VT0, HstoAs0, HstoAs,
+	% 		As, VS, VT, Ms)
+	%
+	% For each variable, Hs, that needs to be accumulated create a
+	% corresponding variable in As updating the varset, VS, var-type
+	% mapping, VT, and recording the mode in Ms.  Also record the
+	% mapping from each H to A in HstoAs.
+	%
+:- pred accumulator__new_acc_var(prog_vars::in, instmap::in,
+		prog_varset::in, map(prog_var, type)::in,
+		map(prog_var, prog_var)::out, prog_vars::out, prog_varset::out,
+		map(prog_var, type)::out, list(mode)::out) is det.
+
+accumulator__new_acc_var([], _InstMap, VarSet, VarTypes,
+		Subst, [], VarSet, VarTypes, []) :-
+	map__init(Subst).
+accumulator__new_acc_var([Var | Vars], InstMap, VarSet0, VarTypes0,
+		Subst, HeadVars, VarSet, VarTypes, Modes) :-
+	accumulator__new_acc_var(Vars, InstMap, VarSet0, VarTypes0,
+			Subst0, HeadVars0, VarSet1, VarTypes1, Modes0),
+
+	varset__new_var(VarSet1, NewVar, VarSet),
+
+	map__det_insert(Subst0, Var, NewVar, Subst),
+
+	HeadVars = [NewVar | HeadVars0],
+
+	map__lookup(VarTypes0, Var, Type),
+	map__det_insert(VarTypes1, NewVar, Type, VarTypes),
+
+	instmap__lookup_var(InstMap, Var, Inst),
+	inst_lists_to_mode_list([Inst], [Inst], Mode),
+	list__append(Mode, Modes0, Modes).
+
+	%
+	% accumulator__acc_pred_info
+	%
+	% Construct the pred_info for the introduced predicate
+	%
+:- pred accumulator__acc_pred_info(list(type)::in, proc_info::in, pred_info::in,
+		proc_id::out, pred_info::out) is det.
+
+accumulator__acc_pred_info(NewTypes, NewProcInfo, PredInfo,
+		NewProcId, NewPredInfo) :-
+		
+		% PredInfo stuff that must change.
+	pred_info_arg_types(PredInfo, TypeVarSet, ExistQVars, Types0),
+
+	pred_info_module(PredInfo, ModuleName),
+	pred_info_name(PredInfo, Name),
+	Cond = true,
+	pred_info_context(PredInfo, PredContext),
+	pred_info_get_markers(PredInfo, Markers),
+	pred_info_get_is_pred_or_func(PredInfo, PredOrFunc),
+	pred_info_get_class_context(PredInfo, ClassContext),
+	pred_info_get_aditi_owner(PredInfo, Owner),
+
+
+	proc_info_context(NewProcInfo, Context),
+	term__context_line(Context, Line),
+	Counter = 0,
+
+	list__append(Types0, NewTypes, Types),
+
+	make_pred_name_with_context(ModuleName, "AccFrom", PredOrFunc, Name,
+		Line, Counter, SymName),
+
+	pred_info_create(ModuleName, SymName, TypeVarSet, ExistQVars, Types,
+			Cond, PredContext, local, Markers, PredOrFunc,
+			ClassContext, Owner, NewProcInfo, NewProcId,
+			NewPredInfo).
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+	%
+	% accumulator__transform
+	%
+	% Actually do the transformation.  This predicate may fail if
+	% for some reason the transformation cannot be completed.
+	%
+:- pred accumulator__transform(top_level::in, base_goal::in, rec_goal::in,
+		hlds_goal::in, bool::in, module_info::in, prog_vars::in,
+		subst::in, pred_id::in, proc_id::in, sym_name::in,
+		hlds_goal::out, hlds_goal::out) is semidet.
+
+accumulator__transform(switch_rec_base, base(BaseGoalList), recursive(DP, R, C),
+		Goal, DoLCO, ModuleInfo, HeadVars, HstoAs_Subst,
+		NewPredId, NewProcId, NewPredName, OrigGoal, NewGoal) :-
+
+	accumulator__Ys_descended_from_Y0s(HeadVars, DP, ModuleInfo),
+
+	accumulator__orig_base_case(BaseGoalList, OrigBaseGoal),
+
+	accumulator__extra_vars_for_recursive_call(DP, C, Vars),
+	accumulator__orig_recursive_case(DP, R, HeadVars, NewPredId, NewProcId,
+			NewPredName, Vars, Y0stoYs_Subst, OrigRecGoal),
+
+	accumulator__new_base_case(BaseGoalList, C,
+			Y0stoYs_Subst, HstoAs_Subst, NewBaseGoal),
+	accumulator__new_recursive_case(DP, C, R, DoLCO, ModuleInfo, NewPredId,
+			NewProcId, NewPredName, Vars, HeadVars,
+			Y0stoYs_Subst, HstoAs_Subst, NewRecGoal),
+
+	(
+		Goal = switch(Var, CanFail, Cases0, StoreMap) - GoalInfo,
+		Cases0 = [case(IdA, _), case(IdB, _)]
+	->
+		OrigCases = [case(IdA, OrigRecGoal), case(IdB, OrigBaseGoal)],
+		OrigGoal = switch(Var, CanFail, OrigCases, StoreMap) - GoalInfo,
+
+		NewCases = [case(IdA, NewRecGoal), case(IdB, NewBaseGoal)],
+		NewGoal = switch(Var, CanFail, NewCases, StoreMap) - GoalInfo
+	;
+		fail
+	).
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+	%
+	% accumulator__update_accumulator_pred
+	%
+	% Place the accumulator version of the predicate in the
+	% module_info structure.
+	%
+:- pred accumulator__update_accumulator_pred(pred_id::in, proc_id::in,
+		hlds_goal::in, module_info::in, module_info::out) is det.
+
+accumulator__update_accumulator_pred(NewPredId, NewProcId, AccGoal,
+		ModuleInfo0, ModuleInfo) :-
+	module_info_pred_proc_info(ModuleInfo0, NewPredId, NewProcId,
+			PredInfo, ProcInfo0),
+	proc_info_set_goal(ProcInfo0, AccGoal, ProcInfo),
+	module_info_set_pred_proc_info(ModuleInfo0, NewPredId, NewProcId,
+			PredInfo, ProcInfo, ModuleInfo).
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+	%
+	% accumulator__split_recursive_case(Gs, DP, P, C)
+	%
+	% Split the goals, Gs, which make up the recursive case into the
+	% decompose and process list of goals, DP, and the compose
+	% goals, C, and the recursive goal P.
+	%
+:- pred accumulator__split_recursive_case(pred_id::in, proc_id::in,
+		instmap::in, module_info::in,
+		hlds_goals::in, rec_goal::out) is semidet.
+
+accumulator__split_recursive_case(PredId, ProcId,
+		InitialInstMap, ModuleInfo, Goals, RecGoal) :-
+	solutions(accumulator__split_goals(Goals, PredId, ProcId), Solns),
+	Solns = [recursive(DP0, R, C0)],
+	calculate_instmap(DP0, InitialInstMap, InitialInstMapBeforeR),
+	move_goals(R, InitialInstMapBeforeR, ModuleInfo, C0, PreC0, PostC0),
+	list__append(DP0, PreC0, DP),
+	C = PostC0,
+
+	calculate_instmap(DP, InitialInstMap, InstMapBeforeR),
+	calculate_instmap([R], InstMapBeforeR, InstMapBeforeC),
+
+	DecomposeProcess = goal(DP, InitialInstMap),
+	Recursive 	 = goal(R, InstMapBeforeR),
+	Compose 	 = goal(C, InstMapBeforeC),
+
+	RecGoal = recursive(DecomposeProcess, Recursive, Compose).
+
+:- pred accumulator__split_goals(hlds_goals::in, pred_id::in, proc_id::in,
+		split_result::out) is nondet.
+
+accumulator__split_goals(Goals, PredId, ProcId, RecGoal) :-
+	list__append(DecomposeProcess, [RecursiveCall | Compose], Goals),
+	RecursiveCall = call(PredId, ProcId, _, _, _, _) - _,
+
+		% An empty compose means the predicate is already tail
+		% recursive.
+	Compose \= [],
+	RecGoal = recursive(DecomposeProcess, RecursiveCall, Compose).
+
+
+	%
+	% Given a list of goals and an instmap before the list of goals,
+	% work out what the instmap at the end of the goals is.
+	%
+:- pred calculate_instmap(hlds_goals::in, instmap::in, instmap::out) is det.
+
+calculate_instmap(Goals, InstMap0, InstMap) :-
+	GetGoalInfos = (pred(Goal::in, GoalInfo::out) is det :-
+			Goal = _ - GoalInfo),
+	list__map(GetGoalInfos, Goals, GoalInfos),
+	list__map(goal_info_get_instmap_delta, GoalInfos, InstMapDeltas),
+	ApplyInstMapDelta = (pred(Delta::in, Map0::in, Map::out) is det :-
+			instmap__apply_instmap_delta(Map0, Delta, Map)),
+	list__foldl(ApplyInstMapDelta, InstMapDeltas, InstMap0, InstMap).
+
+
+%-----------------------------------------------------------------------------%
+
+	%
+	% move_goals(G, IM, Gs, BGs, AGs)
+	%
+	% Seperate the list of Goals, Gs, into the goals, BGs, that can be
+	% placed before the goal, G, and the goals, AGs, which must be
+	% placed after the goal, G.  IM is the instmap before the goal
+	% G.
+	%
+	% XXX This should be able to be transformed to accumulator
+	% recursive form, much harder to do though.  Look into this
+	% later. NB you need LCO for the else case.
+	%
+:- pred move_goals(hlds_goal::in, instmap::in, module_info::in, hlds_goals::in,
+		hlds_goals::out, hlds_goals::out) is det.
+
+move_goals(_StartGoal, _InstMapBeforeStartGoal, _ModuleInfo, [], [], []).
+move_goals(StartGoal, InstMapBeforeStartGoal, ModuleInfo,
+		[Goal|Goals], PreGoals, PostGoals) :-
+	(
+		checked_goal_depends_on_earlier_goal(Goal, StartGoal,
+				InstMapBeforeStartGoal, ModuleInfo)
+		% goal_depends_on_earlier_goal(Goal, StartGoal)
+	->
+		StartGoal = _GoalExpr - GoalInfo,
+		goal_info_get_instmap_delta(GoalInfo, InstMapDelta),
+		instmap__apply_instmap_delta(InstMapBeforeStartGoal,
+				InstMapDelta, InstMapBeforeGoal),
+
+		move_goals(Goal, InstMapBeforeGoal, ModuleInfo,
+				Goals, PreGoalsForGoal, PostGoalsForGoal),
+		move_goals(StartGoal, InstMapBeforeStartGoal,
+				ModuleInfo, PreGoalsForGoal,
+				PreGoals, PostGoalsForStartGoal),
+		
+		list__append(PostGoalsForStartGoal, [Goal | PostGoalsForGoal],
+				PostGoals)
+	;
+		move_goals(StartGoal, InstMapBeforeStartGoal,
+				ModuleInfo, Goals, PreGoals0, PostGoals),
+		PreGoals = [Goal | PreGoals0]
+	).
+
+	%
+	% wrapper to test my code.
+	%
+:- pred checked_goal_depends_on_earlier_goal(hlds_goal::in,
+		hlds_goal::in, instmap::in, module_info::in) is semidet.
+
+checked_goal_depends_on_earlier_goal(LaterGoal, EarlierGoal,
+		InstMapBeforeEarlierGoal, ModuleInfo) :-
+	(
+		goal_depends_on_earlier_goal(LaterGoal, EarlierGoal)
+	->
+		X = yes
+	;
+		X = no
+	),
+	(
+		goal_depends_on_earlier_goal_2(LaterGoal, EarlierGoal,
+				InstMapBeforeEarlierGoal, ModuleInfo)
+	->
+		Y = yes
+	;
+		Y = no
+	),
+	(
+			% Just putting X = Y here gives a warning,
+			% I think it is because X = bound(yes) ; bound(no)
+			% and Y = bound(yes); bound(no) so it thinks
+			% that the test is useless.
+			% XXX fix this in the compiler.
+		(
+			X = yes,
+			Y = yes
+		;
+			X = no,
+			Y = no
+		)
+	->
+			% will fail if X = Y = false
+		X = yes
+	;
+		error("checked_goal_depends_on_earlier_goal: gives different results")
+	).
+
+
+	%
+	% If the earlier goal changes the instatiatedness of a variable
+	% that is used in the later goal, then the later goal depends on
+	% the earlier goal.
+	%
+	% This code doesn't work on the alias branch.
+	%
+:- pred goal_depends_on_earlier_goal(hlds_goal::in, hlds_goal::in) is semidet.
+
+goal_depends_on_earlier_goal(_ - LaterGoalInfo, _ - EarlierGoalInfo) :-
+	goal_info_get_instmap_delta(EarlierGoalInfo, EarlierInstMapDelta),
+	instmap_delta_changed_vars(EarlierInstMapDelta, EarlierChangedVars),
+	goal_info_get_nonlocals(LaterGoalInfo, LaterGoalNonLocals),
+	set__intersect(EarlierChangedVars, LaterGoalNonLocals, Intersection),
+	not set__empty(Intersection).
+		
+
+	
+	%
+	% If the earlier goal changes the instatiatedness of a variable
+	% that is used in the later goal, then the later goal depends on
+	% the earlier goal.
+	%
+	% This code does work on the alias branch.
+	%
+:- pred goal_depends_on_earlier_goal_2(hlds_goal::in,
+		hlds_goal::in, instmap::in, module_info::in) is semidet.
+
+goal_depends_on_earlier_goal_2(_ - LaterGoalInfo, _ - EarlierGoalInfo,
+		InstMapBeforeEarlierGoal, ModuleInfo) :-
+	goal_info_get_instmap_delta(EarlierGoalInfo, EarlierInstMapDelta),
+	instmap__apply_instmap_delta(InstMapBeforeEarlierGoal,
+			EarlierInstMapDelta, InstMapAfterEarlierGoal),
+
+	changed_vars(InstMapBeforeEarlierGoal, InstMapAfterEarlierGoal,
+			ModuleInfo, EarlierChangedVars),
+
+	goal_info_get_nonlocals(LaterGoalInfo, LaterGoalNonLocals),
+	set__intersect(EarlierChangedVars, LaterGoalNonLocals, Intersection),
+	not set__empty(Intersection).
+
+	%
+	% changed_vars(IMA, IMB, MI, CV)
+	%
+	% Given an earlier instmap, IMA, and a later instmap, IMB,
+	% determine what variables, CV, have had their instantiatedness
+	% information changed.
+	%
+	% This predicate is meant to be equivalent to
+	% instmap_delta_changed_vars, where the instmap_delta is simply
+	% the one to take IMA to IMB.  However this predicate should
+	% transform more easily to the alias branch.
+	%
+:- pred changed_vars(instmap::in, instmap::in, module_info::in,
+		set(prog_var)::out) is det.
+
+changed_vars(InstMapA, InstMapB, ModuleInfo, ChangedVars) :-
+	instmap__vars_list(InstMapB, VarsB),
+	changed_vars_2(VarsB, InstMapA, InstMapB, ModuleInfo, ChangedVars).
+
+:- pred changed_vars_2(prog_vars::in, instmap::in,
+		instmap::in, module_info::in, set(prog_var)::out) is det.
+
+changed_vars_2([], _InstMapA, _InstMapB, _ModuleInfo, ChangedVars) :-
+	set__init(ChangedVars).
+changed_vars_2([VarB|VarBs], InstMapA, InstMapB, ModuleInfo, ChangedVars) :-
+	changed_vars_2(VarBs, InstMapA, InstMapB, ModuleInfo, ChangedVars0),
+
+	instmap__lookup_var(InstMapA, VarB, InitialInst),
+	instmap__lookup_var(InstMapB, VarB, FinalInst),
+
+	(
+		inst_matches_final(InitialInst, FinalInst, ModuleInfo)
+	->
+		ChangedVars = ChangedVars0
+	;
+		set__insert(ChangedVars0, VarB, ChangedVars)
+	).
+
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+	%
+	% accumulator__Ys_descended_from_Y0s(HV, DP)
+	%
+	% If any head variable is calculated in the decompose/process,
+	% DP, list of goals then it cannot be descended from the Y0s.
+	%
+:- pred accumulator__Ys_descended_from_Y0s(prog_vars::in,
+		a_goals::in, module_info::in) is semidet.
+
+accumulator__Ys_descended_from_Y0s(HeadVars, DecomposeProcess, ModuleInfo) :-
+	accumulator__vars_to_accumulate(HeadVars, DecomposeProcess,
+			ModuleInfo, ChangedHeadVars),
+
+	ChangedHeadVars = [].
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+	%
+	% accumulator__vars_to_accumulate(HV, C, VA)
+	%
+	% Given the list of goals, C, which represent the compose
+	% goal and the list of the head variables, HV, determine the
+	% head variables that will need to be accumulated, VA.
+	%
+	% The variables that will need to be accumulated are simply
+	% those which instantiatedness change in the compose goals and
+	% that are headvars.
+	%
+:- pred accumulator__vars_to_accumulate(prog_vars::in, a_goals::in,
+		module_info::in, prog_vars::out) is det.
+
+accumulator__vars_to_accumulate(HeadVars, C, ModuleInfo, VarsToAccumulate) :-
+	C = goal(ComposeGoals, InstMapBeforeCompose),
+		
+		% Determine the instmap-delta of the goal list.
+	GetGoalInfos = (pred(Goal::in, GoalInfo::out) is det :-
+				Goal = _ - GoalInfo),
+	list__map(GetGoalInfos, ComposeGoals, GoalInfos),
+	list__map(goal_info_get_instmap_delta, GoalInfos, InstMapDeltas),
+	instmap_delta_init_reachable(EmptyInstMapDelta),
+	list__foldl(instmap_delta_apply_instmap_delta,
+			InstMapDeltas, EmptyInstMapDelta, InstMapDelta),
+
+	instmap__apply_instmap_delta(InstMapBeforeCompose,
+			InstMapDelta,InstMapAfterCompose),
+
+	changed_vars(InstMapBeforeCompose,
+			InstMapAfterCompose, ModuleInfo, ChangedVars),
+
+	Member = (pred(M::in) is semidet :- set__member(M, ChangedVars)),
+	list__filter(Member, HeadVars, VarsToAccumulate).
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+	%
+	% accumulator__extra_vars_for_recursive_call(DP, C, Hs)
+	%
+	% If the decompose/process list of goals, DP, produce a value
+	% that is needed in the compose list of goals, C, then that
+	% value will need to be passed via the introduced recursive
+	% call.  This predicate identifies these variables, Hs.
+	%
+:- pred accumulator__extra_vars_for_recursive_call(a_goals::in,
+		a_goals::in, prog_vars::out) is det.
+
+accumulator__extra_vars_for_recursive_call(
+		goal(DecomposeProcess, _InstMapBeforeDecomposeProcess),
+		goal(Compose, _InstMapBeforeCompose), Vars) :-
+	GetGoalInfos = (pred(Goal::in, GoalInfo::out) is det :-
+				Goal = _ - GoalInfo),
+	list__map(GetGoalInfos, DecomposeProcess, DPGoalInfos),
+	list__map(goal_info_get_nonlocals, DPGoalInfos, DPNonLocals),
+	set__list_to_set(DPNonLocals, DPNonLocalsPowerSet),
+	set__power_union(DPNonLocalsPowerSet, DPNonLocalsSet),
+
+	list__map(GetGoalInfos, Compose, CGoalInfos),
+	list__map(goal_info_get_nonlocals, CGoalInfos, CNonLocals),
+	set__list_to_set(CNonLocals, CNonLocalsPowerSet),
+	set__power_union(CNonLocalsPowerSet, CNonLocalsSet),
+
+	set__intersect(DPNonLocalsSet, CNonLocalsSet, VarsSet),
+	set__to_sorted_list(VarsSet, Vars).
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+	%
+	% accumulator__static_vars_in_recursive_call
+	%
+	% Identify the variables which are static only appear in the
+	% recursive call.
+	%
+	% This predicate is currently unused.
+	%
+:- pred accumulator__static_vars_in_recursive_call(a_goal::in,
+		a_goals::in, module_info::in, prog_vars::out) is det.
+
+accumulator__static_vars_in_recursive_call(Recursive, Compose,
+		ModuleInfo, Vars) :-
+
+	Recursive = goal(_RecGoalExpr - RecGoalInfo, InstMapBeforeRec),
+	Compose = goal(ComposeGoals, _InstMapBeforeCompose),
+
+	goal_info_get_instmap_delta(RecGoalInfo, RecInstMapDelta),
+	goal_info_get_nonlocals(RecGoalInfo, RecNonLocals),
+	instmap__apply_instmap_delta(InstMapBeforeRec, RecInstMapDelta,
+			InstMapAfterRec),
+	changed_vars(InstMapBeforeRec, InstMapAfterRec,
+			ModuleInfo, ChangedVars),
+
+	set__difference(RecNonLocals, ChangedVars, PossibleStaticVars),
+
+	GetGoalInfos = (pred(Goal::in, GoalInfo::out) is det :-
+				Goal = _ - GoalInfo),
+	list__map(GetGoalInfos, ComposeGoals, CGoalInfos),
+	list__map(goal_info_get_nonlocals, CGoalInfos, CNonLocals),
+	set__list_to_set(CNonLocals, CNonLocalsPowerSet),
+	set__power_union(CNonLocalsPowerSet, CNonLocalsSet),
+
+	set__intersect(CNonLocalsSet, PossibleStaticVars, VarsSet),
+	set__to_sorted_list(VarsSet, Vars).
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+	%
+	% accumulator__orig_base_case
+	%
+	% Determine the base case of the original goal.
+	%
+:- pred accumulator__orig_base_case(hlds_goals::in, hlds_goal::out) is det.
+
+accumulator__orig_base_case(BaseGoalList, BaseGoal) :-
+	calculate_goal_info(conj(BaseGoalList), BaseGoal).
+
+%-----------------------------------------------------------------------------%
+
+	%
+	% accumulator__orig_recursive_case
+	%
+	% Determine the recursive case of the original goal.
+	%
+:- pred accumulator__orig_recursive_case(a_goals::in, a_goal::in,
+		prog_vars::in, pred_id::in, proc_id::in, sym_name::in,
+		prog_vars::in, subst::out, hlds_goal::out) is det.
+
+accumulator__orig_recursive_case(DP, R0, HeadVars, PredId, ProcId, Name,
+		ExtraVars, Y0stoYs_Subst, Goal) :-
+	DP = goal(DecomposeProcess, _InstMapBeforeDecomposeProcess),
+	R0 = goal(Recursive, _InstMapBeforeRecursive),
+	(
+		Recursive = GoalExpr0 - GoalInfo,
+		GoalExpr0 = call(_, _, Vars, Builtin, Context, _)
+	->
+			% Calculate what vars the new call should use.
+		goal_list_nonlocals(DecomposeProcess, NonLocals),
+		assoc_list__from_corresponding_lists(HeadVars, Vars, Pairs),
+		list__map(accumulator__which_var(NonLocals), Pairs, CallVars0),
+		list__append(CallVars0, ExtraVars, CallVars),
+		GoalExpr = call(PredId, ProcId, CallVars,
+				Builtin, Context, Name),
+
+			% Rename the variables in the goal.
+		map__init(Subst0),
+		map__det_insert_from_corresponding_lists(Subst0, Vars,
+				CallVars0, Y0stoYs_Subst),
+		goal_util__rename_vars_in_goal(GoalExpr - GoalInfo,
+				Y0stoYs_Subst, R),
+				
+
+		list__append(DecomposeProcess, [R], GoalList),
+		calculate_goal_info(conj(GoalList), Goal)
+	;
+		error("accumulator__orig_recursive_case: Not a call.")
+	).
+
+
+	%
+	% If a var is a member of the nonlocal set of variables then it
+	% must be that variable we are to use in the call, otherwise we
+	% use the corresponding head variable.
+	%
+:- pred accumulator__which_var(set(prog_var)::in,
+		pair(prog_var, prog_var)::in, prog_var::out) is det.
+
+accumulator__which_var(NonLocals, HeadVar - CallVar, Var) :-
+	(
+		set__member(CallVar, NonLocals)
+	->
+		Var = CallVar
+	;
+		Var = HeadVar
+	).
+
+
+%-----------------------------------------------------------------------------%
+
+	%
+	% accumulator__new_base_case
+	%
+	% Determine the base case of the introduced predicate.
+	%
+:- pred accumulator__new_base_case(hlds_goals::in, a_goals::in,
+		subst::in, subst::in, hlds_goal::out) is det.
+
+accumulator__new_base_case(Base, C, Y0stoYs_Subst, HstoAs_Subst, Goal) :-
+	C = goal(Compose, _InstMapBeforeCompose),
+	reverse_subst(Y0stoYs_Subst, YstoY0s_Subst),
+
+	goal_util__rename_vars_in_goals(Base, no, YstoY0s_Subst, NewBase),
+	goal_util__rename_vars_in_goals(Compose, no, HstoAs_Subst, NewCompose),
+
+	list__append(NewBase, NewCompose, GoalList),
+	calculate_goal_info(conj(GoalList), Goal).
+
+%-----------------------------------------------------------------------------%
+
+	%
+	% accumulator__new_recursive_case
+	%
+	% Determine the recursive case of the introduced predicate.
+	%
+:- pred accumulator__new_recursive_case(a_goals::in, a_goals::in,
+		a_goal::in, bool::in, module_info::in, pred_id::in, proc_id::in,
+		sym_name::in, prog_vars::in, prog_vars::in, subst::in,
+		subst::in, hlds_goal::out) is semidet.
+
+accumulator__new_recursive_case(DP, C, R0, DoLCO,
+		ModuleInfo, PredId, ProcId, Name, Hs, HeadVars,
+		Y0stoYs_Subst, HstoAs_Subst, Goal) :-
+	DP = goal(DecomposeProcess, _InstMapBeforeDecomposeProcess),
+	C  = goal(Compose, InstMapBeforeCompose),
+	R0 = goal(Recursive0, _InstMapBeforeRecursive0),
+
+	assoc_info_init(ModuleInfo, HeadVars, DP, C, R0,
+		Y0stoYs_Subst, HstoAs_Subst, AssocInfo0),
+        accumulator__check_assoc(Compose, InstMapBeforeCompose, ModuleInfo,
+			PreRecGoal0, PostRecGoal0, AssocInfo0, AssocInfo),
+
+	(
+		DoLCO = yes,
+
+			% If there are no goals that can be moved before
+			% the recursive call, then there is nothing to
+			% accumulate so fail.
+		PreRecGoal0 \= [],
+
+		map__init(LCO_Subst0),
+		assoc_info_dynamic_set(DynamicSet, AssocInfo, _),
+		accumulator__check_post_rec_goals(PostRecGoal0, DynamicSet,
+				LCO_Subst0, LCO_Subst)
+	;
+		DoLCO = no,
+		map__init(LCO_Subst),
+		PostRecGoal0 = []
+	),
+
+	assoc_info_Y0stoAs(Y0stoAs_Subst, AssocInfo, _),
+
+	accumulator__rename_prerec_goals(PreRecGoal0, Y0stoAs_Subst,
+			Y0stoYs_Subst, LCO_Subst, HstoAs_Subst, PreRecGoal),
+
+	accumulator__rename_recursive_goal(Recursive0, Hs, PredId, ProcId,
+			Name, HstoAs_Subst, Y0stoAs_Subst,
+			Y0stoYs_Subst, LCO_Subst, Recursive),
+
+	accumulator__rename_postrec_goals(PostRecGoal0, HstoAs_Subst,
+			PostRecGoal),
+
+	list__append(PreRecGoal, [Recursive | PostRecGoal], GoalList0),
+	list__append(DecomposeProcess, GoalList0, GoalList),
+	calculate_goal_info(conj(GoalList), Goal).
+
+%-----------------------------------------------------------------------------%
+
+
+	%
+	% accumulator__check_post_rec_goals
+	%
+	% Ensure that each goal which is to be placed after the
+	% recursive goal is construction unification.
+	%
+	% Also create a substition which records from which dynamic var
+	% each headvar is descended.
+	%
+:- pred accumulator__check_post_rec_goals(hlds_goals::in, set(prog_var)::in,
+		subst::in, subst::out) is semidet.
+
+accumulator__check_post_rec_goals([], _DynamicSet, Subst, Subst).
+accumulator__check_post_rec_goals([Goal | Goals], DynamicSet, Subst0, Subst) :-
+	Goal = unify(_TermL, _TermR, _Mode, Unify, _Context) - _GoalInfo,
+	Unify = construct(Var, _ConsId, Vars, _Modes),
+	set__list_to_set(Vars, VarsSet),
+	set__intersect(VarsSet, DynamicSet, DynamicVarsSet),
+	set__singleton_set(DynamicVarsSet, DynamicVar),
+	(
+		map__search(Subst0, DynamicVar, DescendedFrom)
+	->
+		map__delete(Subst0, DynamicVar, Subst1),
+		map__insert(Subst1, Var, DescendedFrom, Subst2)
+	;
+		map__insert(Subst0, Var, DynamicVar, Subst2)
+	),
+	accumulator__check_post_rec_goals(Goals, DynamicSet, Subst2, Subst).
+	
+
+%-----------------------------------------------------------------------------%
+
+:- pred accumulator__rename_prerec_goals(hlds_goals::in, subst::in,
+		subst::in, subst::in, subst::in, hlds_goals::out) is det.
+
+accumulator__rename_prerec_goals(Goal0, Y0stoAs_Subst, Y0stoYs_Subst,
+		LCO_Subst, HstoAs_Subst0, Goal) :-
+	reverse_subst(Y0stoYs_Subst, YstoY0s_Subst),
+	reverse_subst(LCO_Subst, LCOtoYs_Subst),
+	chain_subst(LCOtoYs_Subst, YstoY0s_Subst, LCOtoY0s_Subst),
+
+	delete_used_as(HstoAs_Subst0, Y0stoAs_Subst, HstoAs_Subst),
+
+	goal_util__rename_vars_in_goals(Goal0, no, Y0stoAs_Subst, Goal1),
+	goal_util__rename_vars_in_goals(Goal1, no, LCOtoY0s_Subst, Goal2),
+	goal_util__rename_vars_in_goals(Goal2, no, YstoY0s_Subst, Goal3),
+	goal_util__rename_vars_in_goals(Goal3, no, HstoAs_Subst, Goal).
+
+:- pred delete_used_as(subst::in, subst::in, subst::out) is det.
+delete_used_as(HstoAs_Subst0, Y0stoAs_Subst, HstoAs_Subst) :-
+	reverse_subst(HstoAs_Subst0, AstoHs_Subst0),
+	reverse_subst(Y0stoAs_Subst, AstoY0s_Subst),
+
+	map__keys(AstoY0s_Subst, AstoDelete),
+	map__delete_list(AstoHs_Subst0, AstoDelete, AstoHs_Subst),
+
+	reverse_subst(AstoHs_Subst, HstoAs_Subst).
+
+:- pred accumulator__rename_recursive_goal(hlds_goal::in, prog_vars::in,
+		pred_id::in, proc_id::in, sym_name::in, subst::in, subst::in,
+		subst::in, subst::in, hlds_goal::out) is det.
+
+accumulator__rename_recursive_goal(Goal0, Hs, PredId, ProcId, Name,
+		HstoAs_Subst, Y0stoAs_Subst, Y0stoYs_Subst, LCO_Subst, Goal) :-
+
+	Goal0 = GoalExpr0 - GoalInfo0,
+	(
+		GoalExpr0 = call(_, _, Args0, Builtin, Context, _)
+	->
+		reverse_subst(Y0stoAs_Subst, AstoY0s_Subst),
+		reverse_subst(HstoAs_Subst, AstoHs_Subst),
+
+		list__append(Args0, Hs, Args),
+		GoalExpr1 = call(PredId, ProcId, Args, Builtin, Context, Name),
+
+		goal_info_get_nonlocals(GoalInfo0, NonLocals0),
+		set__list_to_set(Args, ArgsSet),
+		set__union(ArgsSet, NonLocals0, NonLocals),
+		goal_info_set_nonlocals(GoalInfo0, NonLocals, GoalInfo1),
+
+		Goal1 = GoalExpr1 - GoalInfo1,
+
+		goal_util__rename_vars_in_goal(Goal1, Y0stoYs_Subst, Goal2),
+		goal_util__rename_vars_in_goal(Goal2, HstoAs_Subst, Goal3),
+		goal_util__rename_vars_in_goal(Goal3, AstoY0s_Subst, Goal4),
+		goal_util__rename_vars_in_goal(Goal4, LCO_Subst, Goal5),
+		goal_util__rename_vars_in_goal(Goal5, AstoHs_Subst, Goal)
+	;
+		error("accumulator__rename_recursive_goal: to make det.")
+	).
+
+:- pred accumulator__rename_postrec_goals(hlds_goals::in, subst::in,
+		hlds_goals::out) is det.
+
+accumulator__rename_postrec_goals(Goals0, HstoAs_Subst, Goals) :-
+	goal_util__rename_vars_in_goals(Goals0, no, HstoAs_Subst, Goals).
+	
+%-----------------------------------------------------------------------------%
+
+:- pred calculate_goal_info(hlds_goal_expr::in, hlds_goal::out) is det.
+
+calculate_goal_info(GoalExpr, GoalExpr - GoalInfo) :-
+	(
+		GoalExpr = conj(GoalList)
+	->
+		goal_list_nonlocals(GoalList, NonLocals),
+		goal_list_instmap_delta(GoalList, InstMapDelta),
+		goal_list_determinism(GoalList, Determinism),
+
+		goal_info_init(NonLocals, InstMapDelta, Determinism, GoalInfo)
+	;
+		error("calculate_goal_info: not a conj.")
+	).
+	
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+	%
+	% accumulator__check_assoc(Gs, MGs, AGs)
+	%
+	% Given a list of goals, Gs, check to ensure that the list of
+	% goals Gs is associative rearranging a goal if necessary to
+	% ensure associativity.  These goals are placed into MGs.
+	%
+	% It is not possible for construction unfications to be
+	% associative.  However if only construction unfications remain
+	% after the recursive call, it is possible to do the lco (see
+	% lco.m) optimisation.  So whenever the predicate encounters a
+	% construction unfication, it places it any goals that depend on
+	% that goal into AGs.
+	%
+:- pred accumulator__check_assoc(hlds_goals::in, instmap::in,
+		module_info::in, hlds_goals::out,
+		hlds_goals::out, assoc_info::in, assoc_info::out) is semidet.
+
+accumulator__check_assoc([], _InstMap, _ModuleInfo, [], []) --> [].
+accumulator__check_assoc([Goal0 | Goal0s],
+		InstMapBeforeGoal0, ModuleInfo, MovedGoals, AfterGoals) -->
+	(
+		{ goal_is_construction_unification(Goal0) }
+	->
+		{ move_goals(Goal0, InstMapBeforeGoal0, ModuleInfo,
+				Goal0s, PreGoal0s, PostGoal0s) },
+		accumulator__check_assoc(PreGoal0s, InstMapBeforeGoal0,
+				ModuleInfo, MovedGoals, AfterGoal0s),
+		{ list__append(AfterGoal0s, [Goal0 | PostGoal0s], AfterGoals) }
+	;
+		{ Goal0 = _ - GoalInfo0 },
+		{ goal_info_get_instmap_delta(GoalInfo0, InstMapDelta) },
+		{ instmap__apply_instmap_delta(InstMapBeforeGoal0 ,
+				InstMapDelta, InstMapBeforeGoal0s) },
+
+
+		accumulator__check_goal(Goal0, Goal),
+		accumulator__check_assoc(Goal0s, InstMapBeforeGoal0s,
+				ModuleInfo, MovedGoal0s, AfterGoals),
+		{ MovedGoals = [Goal | MovedGoal0s] }
+	).
+
+
+%-----------------------------------------------------------------------------%
+
+	%
+	% Is the goal a construction unification?
+	%
+:- pred goal_is_construction_unification(hlds_goal::in) is semidet.
+
+goal_is_construction_unification(Goal - _GoalInfo) :-
+	Goal = unify(_, _, _, Unification, _),
+	Unification = construct(_, _, _, _).
+
+%-----------------------------------------------------------------------------%
+
+	%
+	% Is the current goal associative?
+	%
+:- pred accumulator__check_goal(hlds_goal::in, hlds_goal::out,
+		assoc_info::in, assoc_info::out) is semidet.
+
+accumulator__check_goal(conj(Goals0) - GoalInfo, conj(Goals) - GoalInfo) -->
+	accumulator__check_goallist(Goals0, Goals).
+
+accumulator__check_goal(par_conj(_, _) - _, _) -->
+	{ fail }.
+
+	% Branched Goals
+	%
+	% XXX the previous version of accumulator ensured the condition
+	% that each arm of the disjunct only updated static variables.
+	% However this will not handle the vectorising case.
+accumulator__check_goal(disj(_, _) - _, _) --> { fail }.
+accumulator__check_goal(switch(_, _, _, _) - _, _) --> { fail }.
+accumulator__check_goal(if_then_else(_, _, _, _, _) - _, _) --> { fail }.
+
+accumulator__check_goal(not(_) - _, _) --> { fail }.
+
+accumulator__check_goal(some(Vars, Goal0) - GoalInfo,
+		some(Vars, Goal) - GoalInfo) -->
+	accumulator__check_goal(Goal0, Goal).
+
+	% All of these must fail because there is no way we can know
+	% whether or not these calls are associative.
+	% XXX this may not be true for class_method_call, it may be
+	% possible to make it a condition that this method is always
+	% associative.
+accumulator__check_goal(higher_order_call(_,_,_,_,_,_) - _, _) --> { fail }.
+accumulator__check_goal(pragma_c_code(_,_,_,_,_,_,_) - _, _) --> { fail }.
+accumulator__check_goal(class_method_call(_,_,_,_,_,_) - _, _) --> { fail }.
+
+accumulator__check_goal(unify(TermL, TermR, Mode, Unify, Context) - GoalInfo, 
+		unify(TermL, TermR, Mode, Unify, Context) - GoalInfo) -->
+	{ accumulator__check_assoc_unify_rhs(TermR) },
+	accumulator__check_assoc_unify(Unify).
+
+accumulator__check_goal(call(PredId, ProcId, Arg0s, Builtin, Context, Sym)
+			- GoalInfo,
+		call(PredId, ProcId, Args, Builtin, Context, Sym)
+			- GoalInfo) -->
+	assoc_info_module_info(ModuleInfo),
+	accumulator__call_dynamic_var(Arg0s, DynamicCallVar),
+
+	{ accumulator__is_associative(PredId, ProcId, ModuleInfo, Arg0s, Args,
+		PossibleStaticVars, Commutative) },
+
+	(
+			% Make sure that after rearrangement of the
+			% arguments the new proc will be at least as
+			% efficient as the old pred.
+		{ Commutative = no },
+		assoc_info_static_set(StaticSet),
+		{ accumulator__obey_heuristic(PredId, ModuleInfo,
+				Args, StaticSet) }
+	;
+		{ Commutative = yes }
+	),
+
+	accumulator__check_previous_calls(DynamicCallVar,
+			PredId, ProcId, Commutative),
+	accumulator__new_dynamic_var(Arg0s, DynamicCallVar, NewDynamicVar),
+	accumulator__update_orig_var_map(DynamicCallVar, NewDynamicVar),
+	accumulator__update_Y0stoAs_subst(DynamicCallVar, [PossibleStaticVars]).
+
+%-----------------------------------------------------------------------------%
+
+:- pred accumulator__check_goallist(hlds_goals::in, hlds_goals::out,
+		assoc_info::in, assoc_info::out) is semidet.
+
+accumulator__check_goallist([], []) --> [].
+accumulator__check_goallist([Goal0 | Goals0], [Goal | Goals]) -->
+	accumulator__check_goal(Goal0, Goal),
+	accumulator__check_goallist(Goals0, Goals).
+
+%-----------------------------------------------------------------------------%
+
+	%
+	% This predicate is meant to fail if the rhs of the unification
+	% is a lambda goal, because I am not convinced that I know how
+	% to handle this correctly.
+	%
+:- pred accumulator__check_assoc_unify_rhs(unify_rhs::in) is semidet.
+
+accumulator__check_assoc_unify_rhs(var(_)).
+accumulator__check_assoc_unify_rhs(functor(_, _)).
+accumulator__check_assoc_unify_rhs(lambda_goal(_, _, _, _, _, _)) :-
+		%
+		% For the moment just fail, as I am not sure how to
+		% handle this.
+		%
+	fail.
+
+	%
+	% accumulator__check_assoc_unify_rhs
+	%
+	% The only safe unifications are assignment unifications.
+	%
+:- pred accumulator__check_assoc_unify(unification::in,
+		assoc_info::in, assoc_info::out) is semidet.
+
+accumulator__check_assoc_unify(construct(_Var, _ConsId, _Vars, _Modes)) -->
+		%
+		% We shouldn't fail if we have this case as all the
+		% construction unification does is put a wrapper 
+		% around the dynamic variables.  What we need to
+		% recognise is that the construction/deconstruction
+		% pair do nothing.
+		%
+		% f(X,Y) :-
+		% 	decompose(X,Xh,Xr),
+		% 	f(Xr,Y0),
+		% 	Y0 = c(A0, B0),
+		%	composeA(Xh,A0,A),
+		%	composeB(Xh,B0,B),
+		%	Y = c(A, B).
+		%
+		% I think that the way to recognise these
+		% situations is when the type of Y is flat
+		% (non-recursive).
+		%
+	{ fail }.
+accumulator__check_assoc_unify(
+		deconstruct(_Var, _ConsId, _Vars, _Modes, _Cat)) -->
+		% see comment in construct
+	{ fail }.
+accumulator__check_assoc_unify(assign(L, _R)) -->
+	assoc_info_dynamic_set(DynamicSet0),
+	{ set__insert(DynamicSet0, L, DynamicSet) },
+	assoc_info_set_dynamic_set(DynamicSet).
+accumulator__check_assoc_unify(simple_test(_L, _R)) --> 
+	{ fail }.
+accumulator__check_assoc_unify(complicated_unify(_Modes, _Cat)) -->
+	{ fail }.	% XXX not sure what this should be.
+
+%-----------------------------------------------------------------------------%
+
+	%
+	% accumulator__call_dynamic_var(As, DV)
+	%
+	% Given the list of arguments to a call try and determine which
+	% of the arguments is the current dynamic variable.
+	%
+	% Fails if there is more then one dynamic variable in a call.
+	% This is because in general more then one dynamic variable
+	% being used in a call prevents the goals from being
+	% associative.
+	%
+:- pred accumulator__call_dynamic_var(prog_vars::in, prog_var::out,
+		assoc_info::in, assoc_info::out) is semidet.
+
+accumulator__call_dynamic_var(Args, DynamicCallVar) -->
+	assoc_info_dynamic_set(DynamicSet),
+	{ set__list_to_set(Args, ArgSet) },
+	{ set__intersect(ArgSet, DynamicSet, DynamicCallArgs) },
+	{ set__singleton_set(DynamicCallArgs, DynamicCallVar) }.
+
+	
+	%
+	% accumulator__new_dynamic_var(As, DV, O)
+	%
+	% Given the original arguments to a call, As, and the dynamic
+	% var, DV, determine the new dynamic var, O, also update the
+	% dynamic set at the same time.
+	%
+:- pred accumulator__new_dynamic_var(prog_vars::in, prog_var::in,
+		prog_var::out, assoc_info::in, assoc_info::out) is semidet.
+
+accumulator__new_dynamic_var(Args0, DynamicCallVar, NewDynamicVar) -->
+	assoc_info_static_set(StaticSet0),
+	assoc_info_dynamic_set(DynamicSet0),
+
+	{ set__list_to_set(Args0, ArgSet) },
+	{ set__difference(ArgSet, StaticSet0, ArgDynamicSet) },
+	{ set__union(ArgDynamicSet, DynamicSet0, DynamicSet) },
+
+	assoc_info_set_dynamic_set(DynamicSet),
+
+	{ set__delete(ArgDynamicSet, DynamicCallVar, OutputDynamicCallArgs) },
+	{ set__singleton_set(OutputDynamicCallArgs, NewDynamicVar) }.
+
+	%
+	% accumulator__check_previous_calls
+	%
+	% Ensure that the previous calls have been commutative and calling
+	% the same procedure, or if it is associative that there has
+	% been no previous calls.
+	%
+:- pred accumulator__check_previous_calls(prog_var::in,
+		pred_id::in, proc_id::in, commutative::in,
+		assoc_info::in, assoc_info::out) is semidet.
+
+accumulator__check_previous_calls(DynamicCallVar,
+		PredId, ProcId, Commutative) -->
+	assoc_info_orig_dynvar_map(OrigDynMap),
+	assoc_info_prev_call_map(PrevCallMap0),
+
+	{ map__lookup(OrigDynMap, DynamicCallVar, OrigVar) },
+	{ accumulator__check_prevcalls(OrigVar, PredId, ProcId, Commutative,
+		PrevCallMap0, PrevCallMap) },
+
+	assoc_info_set_prev_call_map(PrevCallMap).
+
+	%
+	% accumulator__update_orig_var_map(DV, Ns)
+	%
+	% Record for each variable Ns which variable that variable
+	% is descended from.
+	%
+:- pred accumulator__update_orig_var_map(prog_var::in, prog_var::in,
+		assoc_info::in, assoc_info::out) is det.
+
+accumulator__update_orig_var_map(DynamicCallVar, NewDynamicVar) -->
+	assoc_info_orig_dynvar_map(OrigDynMap0),
+
+	{ map__lookup(OrigDynMap0, DynamicCallVar, OrigVar) },
+	{ map__det_insert(OrigDynMap0, NewDynamicVar, OrigVar, OrigDynMap) },
+
+	assoc_info_set_orig_dynvar_map(OrigDynMap).
+
+	%
+	% accumulator__update_Y0stoAs_subst
+	%
+	% Update the Y0s -> As Substitution.
+	%
+:- pred accumulator__update_Y0stoAs_subst(prog_var::in, list(set(prog_var))::in,
+		assoc_info::in, assoc_info::out) is semidet.
+
+accumulator__update_Y0stoAs_subst(DynamicCallVar, PossibleStaticVarsList) -->
+	assoc_info_orig_dynvar_map(OrigDynVarMap),
+	assoc_info_static_set(StaticSet),
+	assoc_info_HstoAs(HstoAs_Subst),
+
+	{ map__lookup(OrigDynVarMap, DynamicCallVar, OrigDynVar) },
+
+	assoc_info_Y0stoAs(Y0stoAs_Subst0),
+	{ accumulator__update_Y0stoAs_subst_2(PossibleStaticVarsList,
+			OrigDynVar, StaticSet, HstoAs_Subst,
+			Y0stoAs_Subst0, Y0stoAs_Subst) },
+	assoc_info_set_Y0stoAs(Y0stoAs_Subst).
+
+
+:- pred accumulator__update_Y0stoAs_subst_2(list(set(prog_var))::in,
+		prog_var::in, set(prog_var)::in,
+		subst::in, subst::in, subst::out) is semidet.
+
+accumulator__update_Y0stoAs_subst_2([], _, _, _, Y0stoAs_Subst, Y0stoAs_Subst).
+accumulator__update_Y0stoAs_subst_2([PossibleStaticVars | T], OrigDynVar,
+		StaticSet, HstoAs_Subst, Y0stoAs_Subst0, Y0stoAs_Subst) :-
+
+	set__intersect(PossibleStaticVars, StaticSet, StaticVarSet),
+	set__to_sorted_list(StaticVarSet, StaticVar),
+	list__map(map__lookup(HstoAs_Subst), StaticVar, As),
+
+		% There should only be one variable that is being
+		% accumulated.
+	As = [AccVar],
+
+		%
+		% If you have a chain of commutative calls,
+		% only the first one will need to have the
+		% accumulator placed in the call.
+		%
+	(
+		map__insert(Y0stoAs_Subst0, OrigDynVar, AccVar,
+				Y0stoAs_Subst1)
+	->
+		Y0stoAs_Subst2 = Y0stoAs_Subst1
+	;
+		Y0stoAs_Subst2 = Y0stoAs_Subst0
+	),
+
+	accumulator__update_Y0stoAs_subst_2(T, OrigDynVar, StaticSet,
+			HstoAs_Subst, Y0stoAs_Subst2, Y0stoAs_Subst).
+
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+	%
+	% accumulator__check_prevcalls
+	%
+	% We must ensure that if the dynamic variables which the current
+	% variable is descended from have been used in any other
+	% calls previously that all these calls have been commutative
+	% including the current call.
+	%
+	% If this is true update the prev call map.
+	%
+:- pred accumulator__check_prevcalls(prog_var::in,
+		pred_id::in, proc_id::in, commutative::in,
+		prev_call_map::in, prev_call_map::out) is semidet.
+
+accumulator__check_prevcalls(OrigVar, PredId, ProcId, Commutative,
+		PrevCallMap0, PrevCallMap) :-
+	(
+		map__search(PrevCallMap0, OrigVar, PrevCall)
+	->
+			%
+			% Only succeed if the current call is
+			% commutative and all the previous calls are
+			% commutative and to the same procedure.
+			%
+		PrevCall = prev_call(PredId, ProcId, yes),
+		PrevCallMap = PrevCallMap0
+	;
+			%
+			% There are no previous calls so set whether or
+			% not the current call is commutative for all
+			% the original variables.
+			%
+		map__det_insert(PrevCallMap0, OrigVar,
+				prev_call(PredId, ProcId, Commutative),
+				PrevCallMap)
+	).
+
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+	%
+	% If accumulator_is_associative is true, it returns a reordering
+	% of the args to make it associative when executed left to right
+	% and an indicator of whether or not the predicate is
+	% commutative.
+	%
+:- pred accumulator__is_associative(pred_id::in, proc_id::in, module_info::in,
+		prog_vars::in, prog_vars::out, set(prog_var)::out,
+		commutative::out) is semidet.
+
+accumulator__is_associative(PredId, ProcId, ModuleInfo,
+		Args0, Args, PossibleStaticVars, Commutative):-
+	module_info_pred_proc_info(ModuleInfo, PredId, ProcId, 
+			PredInfo, ProcInfo),
+	pred_info_module(PredInfo, ModuleName),
+	pred_info_name(PredInfo, PredName),
+	pred_info_arity(PredInfo, Arity),
+	proc_info_argmodes(ProcInfo, Modes),
+	assoc_fact(ModuleName, PredName, Arity, Modes, ModuleInfo, Args0, Args,
+		PossibleStaticVars, Reordered),
+	bool__not(Reordered, Commutative).
+
+	%
+	% XXX this fact table is only a temporary solution to whether or
+	% not a particular procedure is associative.  In the long term
+	% the user should be able to annotate their code to indicate
+	% which predicates are associative.
+	%
+	% The set is simply the set of vars that must be static.  It is
+	% a simple heuristic to ensure that the O() behaviour only
+	% improves.  ie for append after swapping the arguments the
+	% static variable must be in the first location.
+	%
+:- pred assoc_fact(module_name::in, string::in, arity::in,
+		list(mode)::in, module_info::in, prog_vars::in,
+		prog_vars::out, set(prog_var)::out, bool::out) is semidet.
+
+assoc_fact(unqualified("int"), "+", 3, [In, In, Out], ModuleInfo, 
+		[A, B, C], [A, B, C], PossibleStaticVars, no) :-
+	set__list_to_set([A, B], PossibleStaticVars),
+	mode_is_input(ModuleInfo, In),
+	mode_is_output(ModuleInfo, Out).
+
+assoc_fact(unqualified("int"), "*", 3, [In, In, Out], ModuleInfo, 
+		[A, B, C], [A, B, C], PossibleStaticVars, no) :-
+	set__list_to_set([A, B], PossibleStaticVars),
+	mode_is_input(ModuleInfo, In),
+	mode_is_output(ModuleInfo, Out).
+
+/* XXX introducing accumulators for floating point numbers can be bad.
+assoc_fact(unqualified("float"), "+", 3, [In, In, Out], ModuleInfo, 
+		[A, B, C], [A, B, C], PossibleStaticVars, no) :-
+	set__list_to_set([A, B], PossibleStaticVars),
+	mode_is_input(ModuleInfo, In),
+	mode_is_output(ModuleInfo, Out).
+
+assoc_fact(unqualified("float"), "*", 3, [In, In, Out], ModuleInfo, 
+		[A, B, C], [A, B, C], PossibleStaticVars, no) :-
+	set__list_to_set([A, B], PossibleStaticVars),
+	mode_is_input(ModuleInfo, In),
+	mode_is_output(ModuleInfo, Out).
+*/
+
+assoc_fact(unqualified("list"), "append", 3, [TypeInfoIn, In, In, Out], 
+		ModuleInfo, [TypeInfo, A, B, C], 
+		[TypeInfo, B, A, C], PossibleStaticVars, yes) :-
+	set__list_to_set([A, B], PossibleStaticVars),
+	mode_is_input(ModuleInfo, TypeInfoIn),
+	mode_is_input(ModuleInfo, In),
+	mode_is_output(ModuleInfo, Out).
+
+/*
+	XXX this no longer works.  We need to go back to my original code for
+	this to work.
+assoc_fact(unqualified("set"), "insert", 3, [TypeInfoIn, In, In, Out], 
+		Moduleinfo, [TypeInfo, A, B, C], 
+		[TypeInfo, A, B, C], PossibleStaticVars, no) :-
+	set__list_to_set([A, B], PossibleStaticVars),
+	mode_is_input(Moduleinfo, TypeInfoIn),
+	mode_is_input(Moduleinfo, In),
+	mode_is_output(Moduleinfo, Out).
+*/
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+	% 
+	% accumulator__obey_heuristic
+	%
+	% for calls which rearrange the order of variables ensure that
+	% the call obeys the heuristic that the static variables are in 
+	% certain positions.
+	%
+:- pred accumulator__obey_heuristic(pred_id::in, module_info::in,
+		prog_vars::in, set(prog_var)::in) is semidet.
+
+accumulator__obey_heuristic(Predid, ModuleInfo, Args, StaticSet) :-
+	module_info_pred_info(ModuleInfo, Predid, PredInfo),
+	pred_info_module(PredInfo, ModuleName),
+	pred_info_name(PredInfo, PredName),
+	pred_info_arity(PredInfo, Arity),
+	heuristic(ModuleName, PredName, Arity, Args, MustBeStaticVars),
+	set__intersect(StaticSet, MustBeStaticVars, Intersection),
+	set__equal(MustBeStaticVars, Intersection).
+
+:- pred heuristic(module_name::in, string::in, arity::in, prog_vars::in,
+		set(prog_var)::out) is semidet.
+
+heuristic(unqualified("list"), "append", 3, [_typeinfo, A, _b, _c], Set) :-
+	set__list_to_set([A], Set).
+
+%-----------------------------------------------------------------------------%
+
+:- pred assoc_info_init(module_info::in, prog_vars::in, a_goals::in,
+		a_goals::in, a_goal::in,
+		subst::in, subst::in, assoc_info::out) is det.
+
+assoc_info_init(ModuleInfo, HeadVars, DP, Compose, R, Y0stoYs_Subst,
+		HstoAs_Subst, AssocInfo) :-
+	DP = goal(Decompose, _InstMapBeforeDecomposeProcess),
+	R = goal(_RecGoalExpr - RecGoalInfo, _InstMapBeforeRecursive),
+
+	map__init(PrevCallMap),
+
+		%
+		% Set up the OrigDynVarMap
+		%
+	reverse_subst(Y0stoYs_Subst, YstoY0s_Subst),
+	accumulator__vars_to_accumulate(HeadVars, Compose, ModuleInfo, Ys),
+	list__map(map__lookup(YstoY0s_Subst), Ys, Y0s),
+
+	assoc_list__from_corresponding_lists(Y0s, Y0s, AssocDynamicList),
+	map__from_assoc_list(AssocDynamicList, OrigDynVarMap),
+
+		%
+		% Dynamic Set
+		%
+	set__list_to_set(Y0s, DynamicSet),
+
+		%
+		% Static Set
+		%
+	GetGoalInfos = (pred(Goal::in, GoalInfo::out) is det :-
+				Goal = _ - GoalInfo),
+	list__map(GetGoalInfos, Decompose, DPGoalInfos),
+	list__map(goal_info_get_nonlocals, DPGoalInfos, DPNonLocals),
+	set__list_to_set(DPNonLocals, DPNonLocalsPowerSet),
+	set__power_union(DPNonLocalsPowerSet, StaticSet0),
+
+	goal_info_get_nonlocals(RecGoalInfo, RecNonLocals),
+	set__delete_list(RecNonLocals, Y0s, StaticSet1),
+
+	set__union(StaticSet0, StaticSet1, StaticSet),
+
+		%
+		% Y0stoAs
+		%
+	map__init(Y0stoAs_Subst),
+
+		%
+		% Ys
+		%
+	set__list_to_set(Ys, YsSet),
+
+	AssocInfo = assoc_info(StaticSet, DynamicSet,
+			ModuleInfo, PrevCallMap, OrigDynVarMap,
+			Y0stoAs_Subst, HstoAs_Subst, YsSet).
+
+:- pred assoc_info_static_set(set(prog_var)::out,
+		assoc_info::in, assoc_info::out) is det.
+assoc_info_static_set(StaticSet, AssocInfo, AssocInfo) :-
+	AssocInfo = assoc_info(StaticSet, _, _, _, _, _, _, _).
+
+:- pred assoc_info_dynamic_set(set(prog_var)::out,
+		assoc_info::in, assoc_info::out) is det.
+assoc_info_dynamic_set(DynamicSet, AssocInfo, AssocInfo) :-
+	AssocInfo = assoc_info(_, DynamicSet, _, _, _, _, _, _).
+
+:- pred assoc_info_module_info(module_info::out,
+		assoc_info::in, assoc_info::out) is det.
+assoc_info_module_info(ModuleInfo, AssocInfo, AssocInfo) :-
+	AssocInfo = assoc_info(_, _, ModuleInfo, _, _, _, _, _).
+
+:- pred assoc_info_prev_call_map(prev_call_map::out,
+		assoc_info::in, assoc_info::out) is det.
+assoc_info_prev_call_map(PrevCallMap, AssocInfo, AssocInfo) :-
+	AssocInfo = assoc_info(_, _, _, PrevCallMap, _, _, _, _).
+
+:- pred assoc_info_orig_dynvar_map(orig_dynvar_map::out,
+		assoc_info::in, assoc_info::out) is det.
+assoc_info_orig_dynvar_map(OrigDynVarMap, AssocInfo, AssocInfo) :-
+	AssocInfo = assoc_info(_, _, _, _, OrigDynVarMap, _, _, _).
+
+:- pred assoc_info_Y0stoAs(subst::out,
+		assoc_info::in, assoc_info::out) is det.
+assoc_info_Y0stoAs(Y0stoAs_Subst, AssocInfo, AssocInfo) :-
+	AssocInfo = assoc_info(_, _, _, _, _, Y0stoAs_Subst, _, _).
+
+:- pred assoc_info_HstoAs(subst::out,
+		assoc_info::in, assoc_info::out) is det.
+assoc_info_HstoAs(HstoAs_Subst, AssocInfo, AssocInfo) :-
+	AssocInfo = assoc_info(_, _, _, _, _, _, HstoAs_Subst, _).
+
+:- pred assoc_info_Ys(set(prog_var)::out,
+		assoc_info::in, assoc_info::out) is det.
+assoc_info_Ys(Ys, AssocInfo, AssocInfo) :-
+	AssocInfo = assoc_info(_, _, _, _, _, _, _, Ys).
+
+/*
+:- pred assoc_info_set_static_set(set(prog_var)::in, assoc_info::in,
+		assoc_info::out) is det.
+assoc_info_set_static_set(StaticSet, assoc_info(_, B, C, D, E, F, G, H),
+		assoc_info(StaticSet, B, C, D, E, F, G, H)).
+*/
+
+:- pred assoc_info_set_dynamic_set(set(prog_var)::in, assoc_info::in,
+		assoc_info::out) is det.
+assoc_info_set_dynamic_set(DynamicSet, assoc_info(A, _, C, D, E, F, G, H),
+		assoc_info(A, DynamicSet, C, D, E, F, G, H)).
+
+:- pred assoc_info_set_prev_call_map(prev_call_map::in, assoc_info::in,
+		assoc_info::out) is det.
+assoc_info_set_prev_call_map(PrevCallMap, assoc_info(A, B, C, _, E, F, G, H),
+		assoc_info(A, B, C, PrevCallMap, E, F, G, H)).
+
+:- pred assoc_info_set_orig_dynvar_map(orig_dynvar_map::in, assoc_info::in,
+		assoc_info::out) is det.
+assoc_info_set_orig_dynvar_map(OrigDynMap, assoc_info(A, B, C, D, _, F, G, H),
+		assoc_info(A, B, C, D, OrigDynMap, F, G, H)).
+
+:- pred assoc_info_set_Y0stoAs(subst::in, assoc_info::in,
+		assoc_info::out) is det.
+assoc_info_set_Y0stoAs(Y0stoAs_Subst, assoc_info(A, B, C, D, E, _, G, H),
+		assoc_info(A, B, C, D, E, Y0stoAs_Subst, G, H)).
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- pred reverse_subst(subst::in, subst::out) is det.
+
+reverse_subst(Subst0, Subst) :-
+	map__to_assoc_list(Subst0, List0),
+	assoc_list__reverse_members(List0, List),
+	map__from_assoc_list(List, Subst).
+
+:- pred chain_subst(subst::in, subst::in, subst::out) is det.
+
+chain_subst(AtoB, BtoC, AtoC) :-
+	map__keys(AtoB, Keys),
+	chain_subst_2(Keys, AtoB, BtoC, AtoC).
+
+:- pred chain_subst_2(list(A)::in, map(A, B)::in, map(B, C)::in,
+		map(A, C)::out) is det.
+
+chain_subst_2([], _, _, AtoC) :-
+	map__init(AtoC).
+chain_subst_2([A|As], AtoB, BtoC, AtoC) :-
+	chain_subst_2(As, AtoB, BtoC, AtoC0),
+	map__lookup(AtoB, A, B),
+	map__lookup(BtoC, B, C),
+	map__det_insert(AtoC0, A, C, AtoC).
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
Index: compiler/mercury_compile.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/mercury_compile.m,v
retrieving revision 1.127
diff -u -b -r1.127 mercury_compile.m
--- mercury_compile.m	1999/05/28 05:26:26	1.127
+++ mercury_compile.m	1999/06/01 03:04:36
@@ -37,7 +37,7 @@
 :- import_module bytecode_gen, bytecode.
 :- import_module (lambda), polymorphism, termination, higher_order, inlining.
 :- import_module deforest, dnf, unused_args, magic, dead_proc_elim.
-:- import_module lco, saved_vars, liveness.
+:- import_module accumulator, lco, saved_vars, liveness.
 :- import_module follow_code, live_vars, arg_info, store_alloc, goal_path.
 :- import_module code_gen, optimize, export, base_type_info, base_type_layout.
 :- import_module rl_gen, rl_opt, rl_out.
@@ -1004,7 +1004,11 @@
 	mercury_compile__maybe_unused_args(HLDS36, Verbose, Stats, HLDS38), !,
 	mercury_compile__maybe_dump_hlds(HLDS38, "38", "unused_args"), !,
 
-	mercury_compile__maybe_lco(HLDS38, Verbose, Stats, HLDS40), !,
+	mercury_compile__maybe_introduce_accumulators(HLDS38,
+			Verbose, Stats, HLDS39), !,
+	mercury_compile__maybe_dump_hlds(HLDS39, "39", "accum"), !,
+
+	mercury_compile__maybe_lco(HLDS39, Verbose, Stats, HLDS40), !,
 	mercury_compile__maybe_dump_hlds(HLDS40, "40", "lco"), !,
 
 	% DNF transformations should be after inlining.
@@ -1781,6 +1785,28 @@
 	;
 		{ HLDS0 = HLDS }
 	).
+
+
+:- pred mercury_compile__maybe_introduce_accumulators(module_info, bool, bool,
+	module_info, io__state, io__state).
+:- mode mercury_compile__maybe_introduce_accumulators(in, in, in, out, di, uo)
+	is det.
+
+mercury_compile__maybe_introduce_accumulators(HLDS0, Verbose, Stats, HLDS) -->
+	globals__io_lookup_bool_option(introduce_accumulators, Optimize),
+	( { Optimize = yes } ->
+		maybe_write_string(Verbose,
+				"% Attempting to introduce accumulators...\n"),
+		maybe_flush_output(Verbose),
+		process_all_nonimported_procs(
+			update_module_io(accumulator__process_proc),
+			HLDS0, HLDS),
+		maybe_write_string(Verbose, "% done.\n"),
+		maybe_report_stats(Stats)
+	;
+		{ HLDS0 = HLDS }
+	).
+
 
 :- pred mercury_compile__maybe_lco(module_info, bool, bool,
 	module_info, io__state, io__state).
Index: compiler/options.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/options.m,v
retrieving revision 1.264
diff -u -b -r1.264 options.m
--- options.m	1999/05/27 05:14:33	1.264
+++ options.m	1999/06/03 08:36:16
@@ -256,6 +256,7 @@
 		;	type_specialization
 		;	user_guided_type_specialization
 		;	higher_order_size_limit
+		;	introduce_accumulators
 		;	optimize_constructor_last_call
 		;	optimize_duplicate_calls
 		;	constant_propagation
@@ -604,6 +605,7 @@
 	type_specialization	-	bool(no),
 	user_guided_type_specialization	-	bool(no),
 	higher_order_size_limit	-	int(20),
+	introduce_accumulators -	bool(no),
 	optimize_constructor_last_call -	bool(no),
 	optimize_dead_procs	-	bool(no),
 	deforestation		-	bool(no),
@@ -942,6 +944,7 @@
 long_option("user-guided-type-specialisation",
 					user_guided_type_specialization).
 long_option("higher-order-size-limit",	higher_order_size_limit).
+long_option("introduce-accumulators",	introduce_accumulators).
 long_option("optimise-constructor-last-call",	optimize_constructor_last_call).
 long_option("optimize-constructor-last-call",	optimize_constructor_last_call).
 long_option("optimize-dead-procs",	optimize_dead_procs).
@@ -2009,6 +2012,9 @@
 		"\t`--optimize-higher-order' and `--type-specialization'.",
 		"\tGoal size is measured as the number of calls, unifications",
 		"\tand branched goals.",
+		"--introduce-accumulators",
+		"\tAttempt to introduce accumulating variables into",
+		"\tprocedures, so as to make the procedure tail recursive.",
 		"--optimize-constructor-last-call",
 		"\tEnable the optimization of ""last"" calls that are followed by",
 		"\tconstructor application.",
Index: compiler/notes/compiler_design.html
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/notes/compiler_design.html,v
retrieving revision 1.24
diff -u -b -r1.24 compiler_design.html
--- compiler_design.html	1999/03/30 05:38:14	1.24
+++ compiler_design.html	1999/06/04 04:10:59
@@ -460,6 +460,14 @@
   specialized versions without them (unused_args.m); type_infos are
   often unused.
 
+<li> attempt to introduce accumulators (accumulator.m).  This optimizes
+  procedures whose tail consists of independent associative computations
+  or independant chains of commutative computations into a tail
+  recursive form by the introduction of accumulators.  If lco is turned
+  on it can also transform some procedures so that only construction
+  unifications are after the recursive call.  This pass must come before
+  lco.
+
 <li> elimination of dead procedures (dead_proc_elim.m). Inlining, higher-order
   specialization and the elimination of unused args can make procedures dead
   even the user doesn't, and automatically constructed unification and
Index: doc/user_guide.texi
===================================================================
RCS file: /home/staff/zs/imp/mercury/doc/user_guide.texi,v
retrieving revision 1.175
diff -u -b -r1.175 user_guide.texi
--- user_guide.texi	1999/05/31 01:46:17	1.175
+++ user_guide.texi	1999/06/03 08:37:05
@@ -3370,6 +3370,11 @@
 Evaluate constant expressions at compile time.
 
 @sp 1
+ at item --introduce-accumulators
+Attempt to introduce accumulating variables into
+procedures, so as to make the procedure tail recursive.
+
+ at sp 1
 @item --optimize-constructor-last-call
 Enable the optimization of ``last'' calls that are followed by
 constructor application.
Index: profiler/demangle.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/profiler/demangle.m,v
retrieving revision 1.8
diff -u -b -r1.8 demangle.m
--- demangle.m	1999/03/22 08:08:52	1.8
+++ demangle.m	1999/06/03 08:21:57
@@ -41,6 +41,7 @@
 :- type introduced_pred_type
 	--->	(lambda)
 	;	deforestation
+	;	accumulator
 	.
 
 :- type data_category
@@ -209,11 +210,11 @@
 	( { Category0 \= ordinary } ->
 		remove_prefix("_"),
 		remove_maybe_module_prefix(MaybeModule,
-			["IntroducedFrom__", "DeforestationIn__"]),
+			["IntroducedFrom__", "DeforestationIn__", "AccFrom__"]),
 		{ MaybeModule \= yes("") }
 	;
 		remove_maybe_module_prefix(MaybeModule,
-			["IntroducedFrom__", "DeforestationIn__"])
+			["IntroducedFrom__", "DeforestationIn__", "AccFrom__"])
 	),
 
 	%
@@ -224,11 +225,17 @@
 	=(PredName0),
 
 	(
-		( remove_prefix("IntroducedFrom__") ->
+		( 
+			remove_prefix("IntroducedFrom__") 
+		->
 			{ IntroducedPredType = (lambda) }
 		;
-			remove_prefix("DeforestationIn__"),
+			remove_prefix("DeforestationIn__")
+		->
 			{ IntroducedPredType = deforestation }
+		;
+			remove_prefix("AccFrom__"),
+			{ IntroducedPredType = accumulator }
 		)
 	->
 		( remove_prefix("pred__") ->
@@ -299,6 +306,11 @@
 			string__format(
 				"deforestation procedure (#%d) from %s line %d",
 				[i(Seq), s(QualifiedName), i(Line)], MainPart)
+		;
+			Type = accumulator,
+			string__format(
+				"accumulator procedure from %s line %d",
+				[s(QualifiedName), i(Line)], MainPart)
 		)
 	},
 	[MainPart],
Index: util/mdemangle.c
===================================================================
RCS file: /home/staff/zs/imp/mercury/util/mdemangle.c,v
retrieving revision 1.34
diff -u -b -r1.34 mdemangle.c
--- mdemangle.c	1999/03/22 08:09:52	1.34
+++ mdemangle.c	1999/06/03 08:11:32
@@ -111,6 +111,7 @@
 
 	static const char introduced[]  = "IntroducedFrom__";
 	static const char deforestation[]  = "DeforestationIn__";
+	static const char accumulator[]  = "AccFrom__";
 	static const char pred[]  = "pred__";
 	static const char func[]  = "func__";
 
@@ -130,6 +131,7 @@
 	static const char * trailing_context_1[] = {
 		introduced,
 		deforestation,
+		accumulator,
 		NULL
 	};
 
@@ -162,7 +164,8 @@
 	int lambda_seq_number = 0;
 	char *lambda_pred_name = NULL;
 	const char *lambda_kind = NULL;
-	enum { ORDINARY, UNIFY, COMPARE, INDEX, LAMBDA, DEFORESTATION }
+	enum { ORDINARY, UNIFY, COMPARE, INDEX,
+		LAMBDA, DEFORESTATION, ACCUMULATOR }
 		category;
 	enum { COMMON, INFO, LAYOUT, FUNCTORS } data_category;
 	const char * class_name;
@@ -339,17 +342,20 @@
 	module = strip_module_name(&start, end, trailing_context_1);
 
 	/*
-	** look for "IntroducedFrom" or "DeforestationIn"
+	** look for "IntroducedFrom" or "DeforestationIn" or "AccFrom"
 	*/
 	if (category == ORDINARY) {
 		if (strip_prefix(&start, introduced)) {
 			category = LAMBDA;
 		} else if (strip_prefix(&start, deforestation)) {
 			category = DEFORESTATION;
+		} else if (strip_prefix(&start, accumulator)) {
+			category = ACCUMULATOR;
 		}
 	}
 
-	if (category == LAMBDA || category == DEFORESTATION) {
+	if (category == LAMBDA || category == DEFORESTATION || 
+			category == ACCUMULATOR) {
 		if (strip_prefix(&start, pred)) {
 			lambda_kind = "pred";
 		} else if (strip_prefix(&start, func)) {
@@ -397,6 +403,10 @@
 	case LAMBDA:
 		printf("%s goal (#%d) from '%s' in module '%s' line %d",
 			lambda_kind, lambda_seq_number,
+			lambda_pred_name, module, lambda_line);
+		break;
+	case ACCUMULATOR:
+		printf("accumulator procedure from '%s' in module '%s' line %d",
 			lambda_pred_name, module, lambda_line);
 		break;
 	case DEFORESTATION:

----
 +----------------------------------------------------------------------+
 | Peter Ross      M Sci/Eng Melbourne Uni                              |
 | petdr at cs.mu.oz.au  WWW: www.cs.mu.oz.au/~petdr/ ph: +61 3 9344 9158  |
 +----------------------------------------------------------------------+
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to:       mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions:          mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------



More information about the developers mailing list