[m-dev.] for review: improvements/bug fixes for simplify.m

Simon Taylor stayl at cs.mu.OZ.AU
Wed Oct 27 17:11:26 AEST 1999


 
> > 	Don't optimize a singleton switch into a test followed
> > 	by the case goal if the constructor being tested against
> > 	is existentially quantified. This is necessary because
> > 	the code to produce the test uses type_util__get_cons_id_arg_types,
> > 	which does not correctly handle the existentially quantified
> > 	type variables or the type-infos for those type variables.
> 
> Ouch.
> 
> That change is fine, but I don't think it goes far enough.
> I think type_util__get_cons_id_arg_types should be changed to
> call error/1 if the cons_id is existentially typed, rather than
> quietly returning bogus results.  The documentation for that
> predicate should mention that limitation.  And we need to do something
> about all the other calls to it. 

Done.
 
> Also I think it would be a good idea to have a test case for this
> part of the change.

Done.

> > Index: compiler/simplify.m
> ...
> > +		    ( 
> > +			type_util__get_existq_cons_defn(ModuleInfo1,
> > +					Type, ConsId, _)
> > +		    ->
> 
> That will be a bit inefficient, because type_util__get_existq_cons_defn/4
> does quite a bit of work to construct the unused fourth argument;
> you might consider writing a predicate type_util__is_existq_cons/3.

Done.

> >  simplify__goal_2(some(Vars1, CanRemove0, Goal1), SomeInfo,
> > +		GoalExpr, GoalInfo, Info0, Info) :-
> > +	simplify_info_get_common_info(Info0, Common),
> > +	simplify__goal(Goal1, Goal2, Info0, Info1),
> > +	simplify__nested_somes(CanRemove0, Vars1, Goal2, SomeInfo, Goal),
> > +	Goal = GoalExpr - GoalInfo,
> > +	( Goal = some(_, _, _) - _ ->
> > +		% Don't increase the set of non-locals of a goal within
> > +		% a commit through common structure elimination because
> > +		% that may change the determinism.
> > +		simplify_info_set_common_info(Info1, Common, Info)
> >  	;
> > +		Info = Info1
> >  	).
> 
> >From the comment, I understand roughly what the code is trying to do,
> but it took me a long time to understand how the code (set_common_info)
> relates to the comment (don't increase the set of non-locals).
> I think it would help if you could explain in a little bit more detail
> how leaving the common_info unchanged at that point could lead
> later code to increase the set of non-locals of this goal.
 

+		% Replacing calls, constructions or deconstructions
+		% outside a commit with references to variables created
+		% inside the commit would increase the set of output
+		% variables of the goal inside the commit. This is not
+		% allowed because it could change the determinism.





Estimated hours taken: 2

compiler/simplify.m:
	Remove unnecessary explicit quantification goals before working
	out whether a goal can cause a stack flush.

	Don't increase the non-locals set of an explicit quantification
	goal through common structure elimination because that could change
	the determinism.

	Don't optimize a singleton switch into a test followed
	by the case goal if the constructor being tested against
	is existentially quantified. This is necessary because
	the code to produce the test uses type_util__get_cons_id_arg_types,
	which does not correctly handle the existentially quantified
	type variables or the type-infos for those type variables.

compiler/type_util.m:
	Add type_util__is_existq_cons, which succeeds if a constructor
	is existentially typed.

	type_util__get_cons_id_arg_types now aborts if the cons_id
	is existentially typed rather than silently giving wrong answers.

compiler/goal_util.m:
	Add a comment that goal_util__switch_to_disjunction and
	goal_util__case_to_disjunct abort on existentially typed constructors.

compiler/magic.m:
	Add an XXX comment that we should check for existentially
	typed constructors before calling goal_util__switch_to_disjunction.

tests/hard_coded/Mmakefile:
tests/hard_coded/existential_type_switch_opt.{m,exp}:
	Test case.


Index: compiler/goal_util.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/goal_util.m,v
retrieving revision 1.59
diff -u -u -r1.59 goal_util.m
--- goal_util.m	1999/10/25 03:48:49	1.59
+++ goal_util.m	1999/10/27 03:27:53
@@ -147,12 +147,16 @@
 
 	% Convert a switch back into a disjunction. This is needed 
 	% for the magic set transformation.
+	% This aborts if any of the constructors are existentially typed.
 :- pred goal_util__switch_to_disjunction(prog_var, list(case), instmap, 
 		list(hlds_goal), prog_varset, prog_varset, map(prog_var, type),
 		map(prog_var, type), module_info, module_info).
 :- mode goal_util__switch_to_disjunction(in, in, in, out,
 		in, out, in, out, in, out) is det.
 
+	% Convert a case into a conjunction by adding a tag test 
+	% (deconstruction unification) to the case goal.
+	% This aborts if the constructor is existentially typed.
 :- pred goal_util__case_to_disjunct(prog_var, cons_id, hlds_goal, instmap,
 		hlds_goal, prog_varset, prog_varset, map(prog_var, type),
 		map(prog_var, type), module_info, module_info).
Index: compiler/simplify.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/simplify.m,v
retrieving revision 1.73
diff -u -u -r1.73 simplify.m
--- simplify.m	1999/10/25 03:49:38	1.73
+++ simplify.m	1999/10/27 07:03:04
@@ -417,12 +417,23 @@
 		Goal1 = Goal0,
 		Info3 = Info0
 	),
-	simplify_info_maybe_clear_structs(before, Goal1, Info3, Info4),
-	Goal1 = GoalExpr1 - GoalInfo1,
-	simplify__goal_2(GoalExpr1, GoalInfo1, Goal, GoalInfo2, Info4, Info5),
-	simplify_info_maybe_clear_structs(after, Goal - GoalInfo2,
+
+	%
+	% Remove unnecessary explicit quantifications before working
+	% out whether the goal can cause a stack flush.
+	%
+	( Goal1 = some(SomeVars, CanRemove, SomeGoal1) - GoalInfo1 ->
+		simplify__nested_somes(CanRemove, SomeVars, SomeGoal1,
+			GoalInfo1, Goal2)
+	;
+		Goal2 = Goal1	
+	),
+	simplify_info_maybe_clear_structs(before, Goal2, Info3, Info4),
+	Goal2 = GoalExpr2 - GoalInfo2,
+	simplify__goal_2(GoalExpr2, GoalInfo2, Goal, GoalInfo3, Info4, Info5),
+	simplify_info_maybe_clear_structs(after, Goal - GoalInfo3,
 		Info5, Info6),
-	simplify__enforce_invariant(GoalInfo2, GoalInfo, Info6, Info).
+	simplify__enforce_invariant(GoalInfo3, GoalInfo, Info6, Info).
 
 :- pred simplify__enforce_invariant(hlds_goal_info, hlds_goal_info,
 		simplify_info, simplify_info).
@@ -571,9 +582,33 @@
 		% a possibly can_fail unification with the functor on the front.
 		cons_id_arity(ConsId, Arity),
 		(
-			SwitchCanFail = can_fail,
-			MaybeConsIds \= yes([ConsId])
+		    SwitchCanFail = can_fail,
+		    MaybeConsIds \= yes([ConsId])
 		->
+			%
+			% Don't optimize in the case of an existentially
+			% typed constructor because currently 
+			% simplify__create_test_unification does not
+			% handle the existential type variables
+			% in the types of the constructor arguments
+			% or their type-infos.
+			%
+		    simplify_info_get_var_types(Info1, VarTypes1),
+		    map__lookup(VarTypes1, Var, Type),
+		    simplify_info_get_module_info(Info1, ModuleInfo1),
+		    ( 
+			type_util__is_existq_cons(ModuleInfo1,
+					Type, ConsId)
+		    ->
+		    	Goal = switch(Var, SwitchCanFail, Cases, SM),
+			goal_info_get_nonlocals(GoalInfo0, NonLocals),
+			merge_instmap_deltas(InstMap0, NonLocals, InstMaps,
+				NewDelta, ModuleInfo1, ModuleInfo2),
+			simplify_info_set_module_info(Info1,
+				ModuleInfo2, Info4),
+			goal_info_set_instmap_delta(GoalInfo0,
+				NewDelta, GoalInfo)
+		    ;
 			simplify__create_test_unification(Var, ConsId, Arity,
 				UnifyGoal, Info1, Info2),
 
@@ -587,11 +622,9 @@
 			set__insert(NonLocals0, Var, NonLocals),
 			goal_info_get_instmap_delta(GoalInfo0, InstMapDelta0),
 			simplify_info_get_instmap(Info2, InstMap),
-			simplify_info_get_var_types(Info2, VarTypes),
-			map__lookup(VarTypes, Var, Type),
 			instmap_delta_bind_var_to_functor(Var, Type, ConsId, 	
 				InstMap, InstMapDelta0, InstMapDelta, 
-				ModuleInfo0, ModuleInfo),
+				ModuleInfo1, ModuleInfo),
 			simplify_info_set_module_info(Info2, 
 				ModuleInfo, Info3),	
 			goal_info_get_determinism(GoalInfo0, CaseDetism),
@@ -602,11 +635,12 @@
 			simplify_info_set_requantify(Info3, Info4),
 			Goal = conj(GoalList),
 			GoalInfo = CombinedGoalInfo
+		    )
 		;
-			% The var can only be bound to this cons_id, so
-			% a test is unnecessary.
-			SingleGoal = Goal - GoalInfo,
-			Info4 = Info1
+		    % The var can only be bound to this cons_id, so
+		    % a test is unnecessary.
+		    SingleGoal = Goal - GoalInfo,
+		    Info4 = Info1
 		),
 		pd_cost__eliminate_switch(CostDelta),
 		simplify_info_incr_cost_delta(Info4, CostDelta, Info)
@@ -1027,23 +1061,20 @@
 	).
 
 simplify__goal_2(some(Vars1, CanRemove0, Goal1), SomeInfo,
-		Goal, GoalInfo, Info0, Info) :-
-	simplify__goal(Goal1, Goal2, Info0, Info),
-	simplify__nested_somes(CanRemove0, Vars1, Goal2,
-		CanRemove, Vars, Goal3),
-	Goal3 = GoalExpr3 - GoalInfo3,
-	(
-		goal_info_get_determinism(GoalInfo3, Detism),
-		goal_info_get_determinism(SomeInfo, Detism),
-		CanRemove = can_remove
-	->
-		% If the inner and outer detisms match the `some'
-		% is unnecessary.
-		Goal = GoalExpr3,
-		GoalInfo = GoalInfo3
+		GoalExpr, GoalInfo, Info0, Info) :-
+	simplify_info_get_common_info(Info0, Common),
+	simplify__goal(Goal1, Goal2, Info0, Info1),
+	simplify__nested_somes(CanRemove0, Vars1, Goal2, SomeInfo, Goal),
+	Goal = GoalExpr - GoalInfo,
+	( Goal = some(_, _, _) - _ ->
+		% Replacing calls, constructions or deconstructions
+		% outside a commit with references to variables created
+		% inside the commit would increase the set of output
+		% variables of the goal inside the commit. This is not
+		% allowed because it could change the determinism.
+		simplify_info_set_common_info(Info1, Common, Info)
 	;
-		Goal = some(Vars, CanRemove, Goal3),
-		GoalInfo = SomeInfo
+		Info = Info1
 	).
 
 simplify__goal_2(Goal0, GoalInfo, Goal, GoalInfo, Info0, Info) :-
@@ -1300,10 +1331,29 @@
 
 	% replace nested `some's with a single `some',
 :- pred simplify__nested_somes(can_remove::in, list(prog_var)::in,
+		hlds_goal::in, hlds_goal_info::in, hlds_goal::out) is det.
+
+simplify__nested_somes(CanRemove0, Vars1, Goal0, OrigGoalInfo, Goal) :-
+	simplify__nested_somes_2(CanRemove0, Vars1, Goal0,
+		CanRemove, Vars, Goal1),
+	Goal1 = GoalExpr1 - GoalInfo1,
+	(
+		goal_info_get_determinism(GoalInfo1, Detism),
+		goal_info_get_determinism(OrigGoalInfo, Detism),
+		CanRemove = can_remove
+	->
+		% If the inner and outer detisms match the `some'
+		% is unnecessary.
+		Goal = GoalExpr1 - GoalInfo1
+	;
+		Goal = some(Vars, CanRemove, Goal1) - OrigGoalInfo
+	).
+
+:- pred simplify__nested_somes_2(can_remove::in, list(prog_var)::in,
 		hlds_goal::in, can_remove::out, list(prog_var)::out,
 		hlds_goal::out) is det.
 
-simplify__nested_somes(CanRemove0, Vars0, Goal0, CanRemove, Vars, Goal) :-
+simplify__nested_somes_2(CanRemove0, Vars0, Goal0, CanRemove, Vars, Goal) :-
 	( Goal0 = some(Vars1, CanRemove1, Goal1) - _ ->
 		(
 			( CanRemove0 = cannot_remove
@@ -1315,7 +1365,7 @@
 			CanRemove2 = can_remove
 		),
 		list__append(Vars0, Vars1, Vars2),
-		simplify__nested_somes(CanRemove2, Vars2, Goal1,
+		simplify__nested_somes_2(CanRemove2, Vars2, Goal1,
 			CanRemove, Vars, Goal)
 	;
 		CanRemove = CanRemove0,
@@ -1548,6 +1598,7 @@
 
 	% Create a semidet unification at the start of a singleton case
 	% in a can_fail switch.
+	% This will abort if the cons_id is existentially typed.
 :- pred simplify__create_test_unification(prog_var::in, cons_id::in, int::in,
 		hlds_goal::out, simplify_info::in, simplify_info::out) is det.
 
Index: compiler/type_util.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/type_util.m,v
retrieving revision 1.74
diff -u -u -r1.74 type_util.m
--- type_util.m	1999/10/15 03:45:09	1.74
+++ type_util.m	1999/10/27 03:43:34
@@ -166,6 +166,7 @@
 :- mode type_constructors(in, in, out) is semidet.
 
 	% Work out the types of the arguments of a functor.
+	% Aborts if the functor is existentially typed.
 :- pred type_util__get_cons_id_arg_types(module_info::in, (type)::in,
 		cons_id::in, list(type)::out) is det.
 
@@ -175,6 +176,9 @@
 :- pred type_util__get_existq_cons_defn(module_info::in,
 		(type)::in, cons_id::in, ctor_defn::out) is semidet.
 
+:- pred type_util__is_existq_cons(module_info::in,
+		(type)::in, cons_id::in) is semidet.
+
 	% This type is used to return information about a constructor
 	% definition, extracted from the hlds_type_defn and hlds_cons_defn
 	% data types.
@@ -660,7 +664,7 @@
 				ConsDefn = hlds_cons_defn(_, _, _, TypeId, _)
 			)),
 		list__filter(CorrectCons, ConsDefns,
-			[hlds_cons_defn(_ExistQVars0, _Constraints0, ArgTypes0,
+			[hlds_cons_defn(ExistQVars0, _Constraints0, ArgTypes0,
 				_, _)]),
 		ArgTypes0 \= []
 	->
@@ -668,28 +672,43 @@
 		map__lookup(Types, TypeId, TypeDefn),
 		hlds_data__get_type_defn_tparams(TypeDefn, TypeDefnParams),
 		term__term_list_to_var_list(TypeDefnParams, TypeDefnVars),
+
 		% XXX handle ExistQVars
+		require(unify(ExistQVars0, []),
+	"type_util__get_cons_id_arg_types: existentially typed cons_id"),
+
 		map__from_corresponding_lists(TypeDefnVars, TypeArgs, TSubst),
 		term__apply_substitution_to_list(ArgTypes0, TSubst, ArgTypes)
 	;
 		ArgTypes = []
 	).
 
-	% Given a type and a cons_id, look up the definition of that
-	% constructor; if it is existentially typed, return its definition,
-	% otherwise fail.
-type_util__get_existq_cons_defn(ModuleInfo, VarType, ConsId, CtorDefn) :-
+type_util__is_existq_cons(ModuleInfo, VarType, ConsId) :-
+	type_util__is_existq_cons(ModuleInfo, VarType, ConsId, _). 
+	
+:- pred type_util__is_existq_cons(module_info::in,
+		(type)::in, cons_id::in, hlds_cons_defn::out) is semidet.
+
+type_util__is_existq_cons(ModuleInfo, VarType, ConsId, ConsDefn) :-
 	type_to_type_id(VarType, TypeId, _TypeArgs),
 	module_info_ctors(ModuleInfo, Ctors),
 	% will fail for builtin cons_ids.
 	map__search(Ctors, ConsId, ConsDefns),
-	MatchingCons = lambda([ConsDefn::in] is semidet, (
-			ConsDefn = hlds_cons_defn(_, _, _, TypeId, _)
+	MatchingCons = lambda([ThisConsDefn::in] is semidet, (
+			ThisConsDefn = hlds_cons_defn(_, _, _, TypeId, _)
 		)),
-	list__filter(MatchingCons, ConsDefns,
-		[hlds_cons_defn(ExistQVars, Constraints, ArgTypes, _, _)]),
-	ExistQVars \= [],
+	list__filter(MatchingCons, ConsDefns, [ConsDefn]), 
+	ConsDefn = hlds_cons_defn(ExistQVars, _, _, _, _),
+	ExistQVars \= [].
+
+	% Given a type and a cons_id, look up the definition of that
+	% constructor; if it is existentially typed, return its definition,
+	% otherwise fail.
+type_util__get_existq_cons_defn(ModuleInfo, VarType, ConsId, CtorDefn) :-
+	type_util__is_existq_cons(ModuleInfo, VarType, ConsId, ConsDefn),
+	ConsDefn = hlds_cons_defn(ExistQVars, Constraints, ArgTypes, _, _),
 	module_info_types(ModuleInfo, Types),
+	type_to_type_id(VarType, TypeId, _),
 	map__lookup(Types, TypeId, TypeDefn),
 	hlds_data__get_type_defn_tvarset(TypeDefn, TypeVarSet),
 	hlds_data__get_type_defn_tparams(TypeDefn, TypeDefnParams),
Index: tests/hard_coded/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/tests/hard_coded/Mmakefile,v
retrieving revision 1.68
diff -u -u -r1.68 Mmakefile
--- Mmakefile	1999/10/25 03:53:14	1.68
+++ Mmakefile	1999/10/27 06:15:44
@@ -35,6 +35,7 @@
 	existential_bound_tvar \
 	existential_reordering \
 	existential_type_switch \
+	existential_type_switch_opt \
 	existential_types_test \
 	eqv_type_bug \
 	error_func \
Index: tests/hard_coded/existential_type_switch_opt.exp
===================================================================
RCS file: existential_type_switch_opt.exp
diff -N existential_type_switch_opt.exp
--- /dev/null	Wed Oct 27 17:08:14 1999
+++ existential_type_switch_opt.exp	Wed Oct 27 16:13:54 1999
@@ -0,0 +1 @@
+succeeded
Index: tests/hard_coded/existential_type_switch_opt.m
===================================================================
RCS file: existential_type_switch_opt.m
diff -N existential_type_switch_opt.m
--- /dev/null	Wed Oct 27 17:08:14 1999
+++ existential_type_switch_opt.m	Wed Oct 27 16:13:54 1999
@@ -0,0 +1,47 @@
+% Regression test: rotd-1999-10-27 did not handle optimization of
+% a singleton switch on an existentially typed constructor.
+% Symptom:
+% 	Uncaught exception:
+%	Software Error: instmap_delta_from_mode_list_2
+%------------------------------------------------------------------------------%
+
+:- module existential_type_switch_opt.
+
+:- interface.
+
+:- import_module io.
+
+:- pred main(state::di, state::uo) is det.
+
+:- type maybe
+	--->	e_no
+	;	some [U] e_maybe(U)
+	;	some [T] e_yes(T)
+	.
+
+:- pred p(maybe).
+:- mode p(in) is semidet.
+%------------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module std_util.
+
+main -->
+	{ semidet_fail ->
+		X = 'new e_maybe'(2)
+	;
+		X = 'new e_yes'(1)
+	},
+	(
+		{ p(X) }
+	->
+		io__write_string("succeeded\n")
+	;
+		io__write_string("failed\n")
+	).
+
+:- pragma inline(p/1).
+p(e_no).
+p(e_yes(_)).
+
--------------------------------------------------------------------------
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