[m-rev.] for review: untupling transformation

Peter Wang wangp at students.cs.mu.OZ.AU
Tue Feb 1 15:37:27 AEDT 2005


On Tuesday 01 February 2005 12:28 pm, Julien Fischer wrote:
> On Mon, 31 Jan 2005, Peter Wang wrote:
> > +%
> > +% After all the procedures have been processed in that way, a second pass is
> > +% made to update all the calls in the module which refer to the old procedures
> > +% to call the transformed procedures.  This is done by adding deconstruction
> > +% and construction unifications as needed, which can later be simplified by a
> > +% simplification pass.
> > +%
> Are the simplifications done in this module or are they done
> eleswhere?

Elsewhere (I just rely on a later simplification pass).  I don't know what
would be better.

> > +:- pred expand_args_in_pred(pred_id::in, module_info::in, module_info::out,
> > +	transform_map::in, transform_map::out) is det.
> > +
> > +expand_args_in_pred(PredId, !ModuleInfo, !TransformMap) :-
> > +	module_info_types(!.ModuleInfo, TypeTable),
> > +	module_info_pred_info(!.ModuleInfo, PredId, PredInfo),
> > +	(
> > +		% Only perform the transformation on predicates which
> > +		% satisfy the following criteria.
> Is it possible that some of the restrictions below could eventually
> be lifted?

I don't know.

> If so, you should mention this.

Ok.

> > +:- pred expandable_arg_mode((mode)::in) is semidet.
> > +
> > +expandable_arg_mode(in_mode).
> > +expandable_arg_mode(out_mode).
> > +
> 
> Does this compiler bootstrap when this transformation is turned on?

Yes, and it runs slower too :-)

A relative diff follows.  In addition to the changes relating to
your comments, I also made it:

- perform the transformation for procedures which are exported (this was not
possible in my first attempt at it, but now there is no reason not to);

- not go into an infinite loop trying to expand recursive types, such as
	:- type t ---> t(t).
or
	:- type u ---> u(v).
	:- type v ---> v(u).


Thanks, Julien.

Peter


diff -u -r ws3.old/compiler/options.m ws3/compiler/options.m
--- ws3.old/compiler/options.m	2005-02-01 14:42:45.000000000 +1100
+++ ws3/compiler/options.m	2005-02-01 14:21:22.000000000 +1100
@@ -3704,6 +3704,11 @@
 		"\tEnable exception analysis.  Identify those",
 		"\tprocedures that will not throw an exception.",
 		"\tSome optimizations can make use of this information."
+		% ,
+		% "--untuple",
+		% "\tExpand out procedure arguments when the argument type",
+		% "\tis a tuple or a type with exactly one functor.",
+		% "\tNote this is almost always a pessimization."
 	]).
 
 :- pred options_help_hlds_llds_optimization(io::di, io::uo) is det.
diff -u -r ws3.old/compiler/untupling.m ws3/compiler/untupling.m
--- ws3.old/compiler/untupling.m	2005-02-01 14:42:45.000000000 +1100
+++ ws3/compiler/untupling.m	2005-02-01 15:28:07.000000000 +1100
@@ -8,14 +8,13 @@
 %
 % Author: wangp.
 %
-% This module takes an HLDS structure as its input and transforms the
-% locally-defined procedures as follows: if the formal parameter of a
-% procedure has a type consisting of a single function symbol then that
-% parameter is expanded into multiple parameters (one for each field of the
-% functor).  Tuple types are also expanded.  The argument lists are expanded
-% as deeply (flatly) as possible.
+% This module takes the HLDS and transforms the locally-defined procedures as
+% follows: if the formal parameter of a procedure has a type consisting of a
+% single function symbol then that parameter is expanded into multiple
+% parameters (one for each field of the functor).  Tuple types are also
+% expanded.  The argument lists are expanded as deeply (flatly) as possible.
 %
-% e.g. for the following module,
+% e.g. for the following predicate,
 %
 %	:- type t ---> t(u).
 %	:- type u ---> u(v, w).
@@ -34,7 +33,51 @@
 % made to update all the calls in the module which refer to the old procedures
 % to call the transformed procedures.  This is done by adding deconstruction
 % and construction unifications as needed, which can later be simplified by a
-% simplification pass.
+% simplification pass (not called from this module).
+%
+% e.g. a call to the predicate above,
+%
+%	:- pred g(T::in) is det.
+%	g(_) :-
+%		A = 1,
+%		B = "foo",
+%		C = w(A, B),
+%		D = v1,
+%		E = u(D, C),
+%		F = t(E),
+%		f(F).
+%
+% is changed to this:
+%
+%	g(_) :-
+%		A = 1,
+%		B = "foo",
+%		C = w(A, B),
+%		D = v1,
+%		E = u(D, C),
+%		F = t(E),
+%		F = t(G),	% added deconstructions
+%		G = u(H, I),
+%		I = w(J, K),
+%		f_untupled(H, J, K).
+%
+% which, after simplication, should become:
+%
+%	g(_) :-
+%		A = 1,
+%		B = "foo",
+%		D = v1,
+%		f_untupled(D, A, B).
+%
+% Limitations:
+%
+% - When a formal parameter is expanded, both the parameter's type and mode
+% have to be expanded.  Currently only arguments with in and out modes can
+% be expanded at present, as I don't know how to do it for the general case.
+% It should be enough for the majority of code.
+%
+% - Some predicates may or may not be expandable but won't be right now,
+% because I don't understand the features they use (see expand_args_in_pred).
 %
 %-----------------------------------------------------------------------------%
 
@@ -77,11 +120,11 @@
 :- type transformed_proc
 	--->	transformed_proc(
 			pred_proc_id,
-				% The predicate and procedure that the old
-				% procedure was transformed into.
+				% A procedure that was generated by the
+				% untupling transformation.
 			hlds_goal
 				% A call goal template that is used to update
-				% calls refering to the old procedure to the
+				% calls referring to the old procedure to the
 				% new procedure.
 		).
 
@@ -90,7 +133,9 @@
 	fix_calls_to_expanded_procs(TransformMap, !ModuleInfo).
 
 %-----------------------------------------------------------------------------%
-%-----------------------------------------------------------------------------%
+%
+% Pass 1
+%
 
 	% This is the top level of the first pass.  It expands procedure
 	% arguments where possible, adding new versions of the transformed
@@ -114,8 +159,11 @@
 	(
 		% Only perform the transformation on predicates which
 		% satisfy the following criteria.
-		pred_info_import_status(PredInfo, local),
+		pred_info_import_status(PredInfo, ImportStatus),
+		status_defined_in_this_module(ImportStatus, yes),
 		pred_info_get_goal_type(PredInfo, clauses),
+		% Some of these limitations may be able to be lifted later.
+		% For now, take the safe option and don't touch them.
 		pred_info_get_exist_quant_tvars(PredInfo, []),
 		pred_info_get_head_type_params(PredInfo, []),
 		pred_info_get_class_context(PredInfo, constraints([], [])),
@@ -137,7 +185,7 @@
 	is semidet.
 
 at_least_one_expandable_type([Type | Types], TypeTable) :-
-	( expand_type(Type, TypeTable, yes(_))
+	( expand_type(Type, [], TypeTable, expansion(_, _))
 	; at_least_one_expandable_type(Types, TypeTable)
 	).
 
@@ -199,42 +247,44 @@
 expand_args_in_proc_2(HeadVars0, ArgModes0, HeadVars, ArgModes,
 		!Goal, !VarSet, !VarTypes, TypeTable, UntupleMap) :-
 	expand_args_in_proc_3(HeadVars0, ArgModes0, ListOfHeadVars,
-		ListOfArgModes, !Goal, !VarSet, !VarTypes, TypeTable),
+		ListOfArgModes, !Goal, !VarSet, !VarTypes, [], TypeTable),
 	list__condense(ListOfHeadVars, HeadVars),
 	list__condense(ListOfArgModes, ArgModes),
 	build_untuple_map(HeadVars0, ListOfHeadVars, map__init, UntupleMap).
 
-:- pred expand_args_in_proc_3(list(prog_var)::in, list(mode)::in, 
+:- pred expand_args_in_proc_3(list(prog_var)::in, list(mode)::in,
 	list(list(prog_var))::out, list(list(mode))::out,
 	hlds_goal::in, hlds_goal::out, prog_varset::in, prog_varset::out,
-	vartypes::in, vartypes::out, type_table::in) is det.
+	vartypes::in, vartypes::out, list(type)::in, type_table::in) is det.
 
-expand_args_in_proc_3([], [], [], [], !_, !_, !_, _).
+expand_args_in_proc_3([], [], [], [], !_, !_, !_, _, _).
 expand_args_in_proc_3([HeadVar0 | HeadVars0], [ArgMode0 | ArgModes0],
 		[HeadVar | HeadVars], [ArgMode | ArgModes],
-		!Goal, !VarSet, !VarTypes, TypeTable) :-
+		!Goal, !VarSet, !VarTypes, ContainerTypes, TypeTable) :-
 	expand_one_arg_in_proc(HeadVar0, ArgMode0, HeadVar, ArgMode,
-		!Goal, !VarSet, !VarTypes, TypeTable),
+		!Goal, !VarSet, !VarTypes, ContainerTypes, TypeTable),
 	expand_args_in_proc_3(HeadVars0, ArgModes0, HeadVars, ArgModes,
-		!Goal, !VarSet, !VarTypes, TypeTable).
-expand_args_in_proc_3([], [_|_], _, _, !_, !_, !_, _) :-
+		!Goal, !VarSet, !VarTypes, ContainerTypes, TypeTable).
+expand_args_in_proc_3([], [_|_], _, _, !_, !_, !_, _, _) :-
 	unexpected(this_file, "expand_args_in_proc_3: length mismatch").
-expand_args_in_proc_3([_|_], [], _, _, !_, !_, !_, _) :-
+expand_args_in_proc_3([_|_], [], _, _, !_, !_, !_, _, _) :-
 	unexpected(this_file, "expand_args_in_proc_3: length mismatch").
 
 :- pred expand_one_arg_in_proc(prog_var::in, (mode)::in, prog_vars::out,
 	list(mode)::out, hlds_goal::in, hlds_goal::out, prog_varset::in,
-	prog_varset::out, vartypes::in, vartypes::out, type_table::in)
-	is det.
+	prog_varset::out, vartypes::in, vartypes::out, list(type)::in,
+	type_table::in) is det.
 
 expand_one_arg_in_proc(HeadVar0, ArgMode0, HeadVars, ArgModes,
-		!Goal, !VarSet, !VarTypes, TypeTable) :-
+		!Goal, !VarSet, !VarTypes, ContainerTypes0, TypeTable) :-
 	expand_one_arg_in_proc_2(HeadVar0, ArgMode0, MaybeHeadVarsAndArgModes,
-		!Goal, !VarSet, !VarTypes, TypeTable),
+		!Goal, !VarSet, !VarTypes, ContainerTypes0, ContainerTypes,
+		TypeTable),
 	(
 		MaybeHeadVarsAndArgModes = yes(HeadVars1 - ArgModes1),
 		expand_args_in_proc_3(HeadVars1, ArgModes1, ListOfHeadVars,
-			ListOfArgModes, !Goal, !VarSet, !VarTypes, TypeTable),
+			ListOfArgModes, !Goal, !VarSet, !VarTypes,
+			ContainerTypes, TypeTable),
 		HeadVars = list__condense(ListOfHeadVars),
 		ArgModes = list__condense(ListOfArgModes)
 	;
@@ -246,15 +296,17 @@
 :- pred expand_one_arg_in_proc_2(prog_var::in, (mode)::in,
 	maybe(pair(list(prog_var), list(mode)))::out,
 	hlds_goal::in, hlds_goal::out, prog_varset::in, prog_varset::out,
-	vartypes::in, vartypes::out, type_table::in) is det.
+	vartypes::in, vartypes::out, list(type)::in, list(type)::out,
+	type_table::in) is det.
 
 expand_one_arg_in_proc_2(HeadVar0, ArgMode0, MaybeHeadVarsAndArgModes,
-		!Goal, !VarSet, !VarTypes, TypeTable) :-
+		!Goal, !VarSet, !VarTypes, ContainerTypes0, ContainerTypes,
+		TypeTable) :-
 	map__lookup(!.VarTypes, HeadVar0, Type),
+	expand_argument(ArgMode0, Type, ContainerTypes0, TypeTable,
+		Expansion),
 	(
-		expand_argument(ArgMode0, Type, TypeTable,
-			yes(ConsId - NewTypes))
-	->
+		Expansion = expansion(ConsId, NewTypes),
 		NumVars = list__length(NewTypes),
 		svvarset__new_vars(NumVars, NewHeadVars, !VarSet),
 		svmap__det_insert_from_corresponding_lists(
@@ -271,12 +323,15 @@
 				UnifGoal),
 			conjoin_goals_keep_detism(!.Goal, UnifGoal, !:Goal)
 		;
-			unexpected(this_file, 
+			unexpected(this_file,
 				"expand_one_arg_in_proc_2: " ++
 				"unsupported mode encountered")
-		)
+		),
+		ContainerTypes = [Type | ContainerTypes0]
 	;
-		MaybeHeadVarsAndArgModes = no
+		Expansion = no_expansion,
+		MaybeHeadVarsAndArgModes = no,
+		ContainerTypes = ContainerTypes0
 	).
 
 :- pred conjoin_goals_keep_detism(hlds_goal::in, hlds_goal::in,
@@ -310,11 +365,12 @@
 
 	% Similar to the create_aux_pred in loop_inv.m.
 	%
-:- pred create_aux_pred(pred_id::in, proc_id::in, pred_info::in, proc_info::in,
-	pred_id::out, proc_id::out, hlds_goal::out, pred_info::out,
-	proc_info::out, module_info::in, module_info::out) is det.
+:- pred create_aux_pred(pred_id::in, proc_id::in, pred_info::in,
+	proc_info::in, pred_id::out, proc_id::out, hlds_goal::out,
+	pred_info::out, proc_info::out, module_info::in, module_info::out)
+	is det.
 
-create_aux_pred(PredId, ProcId, PredInfo, ProcInfo, 
+create_aux_pred(PredId, ProcId, PredInfo, ProcInfo,
 		AuxPredId, AuxProcId, CallAux, AuxPredInfo, AuxProcInfo,
 		ModuleInfo0, ModuleInfo) :-
 	module_info_name(ModuleInfo0, ModuleName),
@@ -376,7 +432,9 @@
 		AuxPredInfo, AuxProcInfo).
 
 %-----------------------------------------------------------------------------%
-%-----------------------------------------------------------------------------%
+%
+% Pass 2
+%
 
 	% This is the top level of the second pass.  It takes the transform
 	% map built during the first pass as input.  For every call to a
@@ -577,67 +635,88 @@
 	hlds_goals::out, hlds_goals::out, prog_varset::in, prog_varset::out,
 	vartypes::in, vartypes::out, type_table::in) is det.
 
-expand_call_args([], [], [], [], [], !VarSet, !VarTypes, _).
-expand_call_args([Arg0 | Args0], [ArgMode | ArgModes], Args,
-		EnterUnifs, ExitUnifs, !VarSet, !VarTypes, TypeTable) :-
+expand_call_args(Args0, ArgModes0, Args, EnterUnifs, ExitUnifs,
+		!VarSet, !VarTypes, TypeTable) :-
+	expand_call_args_2(Args0, ArgModes0, Args, EnterUnifs, ExitUnifs,
+		!VarSet, !VarTypes, [], TypeTable).
+
+:- pred expand_call_args_2(prog_vars::in, list(mode)::in, prog_vars::out,
+	hlds_goals::out, hlds_goals::out, prog_varset::in, prog_varset::out,
+	vartypes::in, vartypes::out, list(type)::in, type_table::in) is det.
+
+expand_call_args_2([], [], [], [], [], !VarSet, !VarTypes, _, _).
+expand_call_args_2([Arg0 | Args0], [ArgMode | ArgModes], Args,
+		EnterUnifs, ExitUnifs, !VarSet, !VarTypes,
+		ContainerTypes0, TypeTable) :-
 	map__lookup(!.VarTypes, Arg0, Arg0Type),
+	expand_argument(ArgMode, Arg0Type, ContainerTypes0, TypeTable,
+		Expansion),
 	(
-		expand_argument(ArgMode, Arg0Type, TypeTable,
-			yes(ConsId - Types))
-	->
+		Expansion = expansion(ConsId, Types),
 		NumVars = list__length(Types),
 		svvarset__new_vars(NumVars, ReplacementArgs, !VarSet),
 		svmap__det_insert_from_corresponding_lists(
 			ReplacementArgs, Types, !VarTypes),
 		list__duplicate(NumVars, ArgMode, ReplacementModes),
+		ContainerTypes = [Arg0Type | ContainerTypes0],
 		( ArgMode = in_mode ->
 			deconstruct_functor(Arg0, ConsId,
 				ReplacementArgs, Unif),
 			EnterUnifs = [Unif | EnterUnifs1],
-			expand_call_args(ReplacementArgs ++ Args0,
+			expand_call_args_2(ReplacementArgs ++ Args0,
 				ReplacementModes ++ ArgModes,
-				Args, EnterUnifs1, ExitUnifs,
-				!VarSet, !VarTypes, TypeTable)
+				Args, EnterUnifs1, ExitUnifs, !VarSet,
+				!VarTypes, ContainerTypes, TypeTable)
 		; ArgMode = out_mode ->
 			construct_functor(Arg0, ConsId,
 				ReplacementArgs, Unif),
 			ExitUnifs = ExitUnifs1 ++ [Unif],
-			expand_call_args(ReplacementArgs ++ Args0,
+			expand_call_args_2(ReplacementArgs ++ Args0,
 				ReplacementModes ++ ArgModes,
-				Args, EnterUnifs, ExitUnifs1,
-				!VarSet, !VarTypes, TypeTable)
+				Args, EnterUnifs, ExitUnifs1, !VarSet,
+				!VarTypes, ContainerTypes, TypeTable)
 		;
-			unexpected(this_file, 
+			unexpected(this_file,
 				"expand_call_args: unsupported mode")
 		)
 	;
+		Expansion = no_expansion,
 		Args = [Arg0 | Args1],
 		expand_call_args(Args0, ArgModes, Args1, EnterUnifs,
 			ExitUnifs, !VarSet, !VarTypes, TypeTable)
 	).
 
-expand_call_args([], [_|_], _, _, _, !_, !_, _) :-
+expand_call_args_2([], [_|_], _, _, _, !_, !_, _, _) :-
 	unexpected(this_file, "expand_call_args: length mismatch").
-expand_call_args([_|_], [], _, _, _, !_, !_, _) :-
+expand_call_args_2([_|_], [], _, _, _, !_, !_, _, _) :-
 	unexpected(this_file, "expand_call_args: length mismatch").
 
 %-----------------------------------------------------------------------------%
 %-----------------------------------------------------------------------------%
 
-	% expand_argument(ArgMode, ArgType, TypeTable, MaybeConsIdAndTypes)
+:- type expansion_result
+	--->	expansion(
+			cons_id,
+				% the cons_id of the expanded constructor
+			list(type)
+				% the types of the arguments for the
+				% expanded constructor
+		)
+	;	no_expansion.
+
 	% This predicate tries to expand the argument of the given mode and
-	% type.  If this is possible then MaybeConsIdAndTypes is unified with
-	% a pair consisting of the cons_id of the constructor which was
-	% expanded and the types for that constructor.
-	% 
-:- pred expand_argument((mode)::in, (type)::in, type_table::in,
-	maybe(pair(cons_id, list(type)))::out) is det.
+	% type.  If this is possible then Expansion is unified with the
+	% `expansion' functor, giving the details of the expansion.
+	% Otherwise it is unified with `no_expansion'.
+	%
+:- pred expand_argument((mode)::in, (type)::in, list(type)::in,
+	type_table::in, expansion_result::out) is det.
 
-expand_argument(ArgMode, ArgType, TypeTable, MaybeConsIdAndTypes) :-
+expand_argument(ArgMode, ArgType, ContainerTypes, TypeTable, Expansion) :-
 	( expandable_arg_mode(ArgMode) ->
-		expand_type(ArgType, TypeTable, MaybeConsIdAndTypes)
+		expand_type(ArgType, ContainerTypes, TypeTable, Expansion)
 	;
-		MaybeConsIdAndTypes = no
+		Expansion = no_expansion
 	).
 
 	% This module so far only knows how to expand arguments which have
@@ -648,10 +727,10 @@
 expandable_arg_mode(in_mode).
 expandable_arg_mode(out_mode).
 
-:- pred expand_type((type)::in, type_table::in,
-	maybe(pair(cons_id, list(type)))::out) is det.
+:- pred expand_type((type)::in, list(type)::in, type_table::in,
+	expansion_result::out) is det.
 
-expand_type(Type, TypeTable, MaybeConsIdAndTypes) :-
+expand_type(Type, ContainerTypes, TypeTable, Expansion) :-
 	(
 		% Always expand tuple types.
 		type_to_ctor_and_args(Type, TypeCtor, TypeArgs),
@@ -659,7 +738,7 @@
 	->
 		Arity = list__length(TypeArgs),
 		ConsId = cons(unqualified("{}"), Arity),
-		MaybeConsIdAndTypes = yes(ConsId - TypeArgs)
+		Expansion = expansion(ConsId, TypeArgs)
 	;
 		% Expand a discriminated union type if it has only a
 		% single functor and the type has no parameters.
@@ -669,17 +748,18 @@
 		get_type_defn_body(TypeDefn, TypeBody),
 		TypeBody ^ du_type_ctors = [SingleCtor],
 		SingleCtor ^ cons_exist = [],
-
 		SingleCtorName = SingleCtor ^ cons_name,
 		SingleCtorArgs = SingleCtor ^ cons_args,
-		SingleCtorArgs \= []
+		SingleCtorArgs \= [],
+		% Prevent infinite loop with recursive types.
+		\+ list__member(Type, ContainerTypes)
 	->
 		Arity = list__length(SingleCtorArgs),
 		ConsId = cons(SingleCtorName, Arity),
 		ExpandedTypes = list__map(snd, SingleCtorArgs),
-		MaybeConsIdAndTypes = yes(ConsId - ExpandedTypes)
+		Expansion = expansion(ConsId, ExpandedTypes)
 	;
-		MaybeConsIdAndTypes = no
+		Expansion = no_expansion
 	).
 
 %-----------------------------------------------------------------------------%
diff -u -r ws3.old/doc/user_guide.texi ws3/doc/user_guide.texi
--- ws3.old/doc/user_guide.texi	2005-02-01 14:40:29.000000000 +1100
+++ ws3/doc/user_guide.texi	2005-02-01 14:43:11.000000000 +1100
@@ -6764,6 +6764,13 @@
 exception.  This information can be used by some 
 optimization passes.
 
+ at c @sp 1
+ at c @item --untuple
+ at c @findex --untuple
+ at c Expand out procedure arguments when the argument type
+ at c is a tuple or a type with exactly one functor.
+ at c Note this is almost always a pessimization.
+
 @end table
 
 @node MLDS backend (MLDS -> MLDS) optimization options


--------------------------------------------------------------------------
mercury-reviews mailing list
post:  mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the reviews mailing list