Files
mercury/compiler/graph_colour.m
Zoltan Somogyi 7487590f2d Predicates with many variables, such as some of those in zm_enums.m,
Estimated hours taken: 24
Branches: main

Predicates with many variables, such as some of those in zm_enums.m,
tickle pretty bad behavior in the liveness and stack_alloc passes.
This is because those passes manipulate sets of variables, which in
such cases are large sets of variables, and the quadratic behavior
of repeated operations on sets represents as sorted lists hurts us.

This diff changes the representation of the sets of variables involved
in those two passes, which are the prebirth, postbirth, predeath and postdeath
sets in goal_infos, to be values of an abstract type (set_of_progvar).
By default, these are implemented using tree_bitsets, which have much better
worst case behaviour that set_ordlists.

When compiling zm_enums with debugging enabled, this diff speeds up
the liveness pass by about half and the stack alloc pass by about a third,
with the overall speedup being about 6% (due to some other expensive passes).

On tools/speedtest -l, the result is a 3.4% slowdown. Since the slowdown
worsens slightly if I make the abstract representation of sets of prog_vars
be the existing representation (an ordinary set), I think this slowdown is
due to the conversions that are now required in some places between the
abstract representation and an explicit set(prog_var) representation.
As such, as other uses of set(progvar) get converted to set_of_progvar,
this slowdown should disappear.

compiler/set_of_var.m:
	The new module that contains the set_of_progvar abstract data type.

	This module also contains a copy of the code of the graph_colour
	module. Since the set_of_progvar type is private, this is necessary
	if we want all the set operations done by graph colouring (which does
	the bulk of the work of the stack alloc pass) to use the preferred
	set representation.

compiler/graph_colour.m:
	Note that this module is no longer used.

compiler/stack_alloc.m:
compiler/liveness.m:
	Switch over to using the new module.

compiler/parse_tree.m:
	Include set_of_var among the modules of this package. (It is in this
	package because the prog_var type is defined in this package.)

compiler/test_bitset.m:
	A module that allows new set implementations to be tested. It is
	an extended and specialized version of the bitset_tester module
	from tests/hard_coded.

compiler/hlds_llds.m:
	Use the set_of_progvar type for the prebirth, postbirth, predeath
	and postdeath sets in goal_infos, and for other liveness-related
	sets of variables.

compiler/code_info.m:
	Some of the fields of the code_info structure represent sets of
	variables, and some of the predicates defined by this module have
	arguments that are sets of variables. If these sets represent entities
	that are computed from prebirth, postbirth, predeath and postdeath
	sets or from other goal_info fields that have been changed to the
	set_of_progvar representation, change them to use the set_of_progvar
	representation as well, or, in a few cases, to plain sorted lists.

	Conform to the above change.

compiler/proc_type.m:
	Add a utility predicate to operate of set_of_progvar.

	Replace a lambda expression with a named predicate.

compiler/quantification.m:
	Until now, quantification.m used its own private abstract type
	(defined as tree_bitset) to represent sets. Make it use set_of_progvar
	instead, since it has the same purpose. This eliminates a potential
	maintenance problem.

compiler/call_gen.m:
compiler/code_gen.m:
compiler/commit_gen.m:
compiler/delay_construct.m:
compiler/disj_gen.m:
compiler/hlds_out_goal.m:
compiler/hlds_rtti.m:
compiler/interval.m:
compiler/ite_gen.m:
compiler/live_vars.m:
compiler/lookup_switch.m:
compiler/lookup_util.m:
compiler/matching.m:
compiler/pd_util.m:
compiler/polymorphism.m:
compiler/pragma_c_gen.m:
compiler/proc_gen.m:
compiler/simplify.m:
compiler/stack_opt.m:
compiler/store_alloc.m:
compiler/string_switch.m:
compiler/structure_reuse.lbu.m:
compiler/structure_reuse.lfu.m:
compiler/structure_sharing.domain.m:
compiler/switch_util.m:
compiler/trace_gen.m:
compiler/tupling.m:
compiler/unify_gen.m:
compiler/unused_args.m:
	Conform to the above change.

library/map.m:
	Add a utility predicate, map.select_sorted_list, which functions the
	same way as map.select, but takes a sorted list as argument instead of
	a set.

library/set_ordlist.m:
	Bring the interface of this module closer to set.m and tree_bitset.m
	to make them more easily interchangeable. This required adding the
	predicates is_non_empty and is_singleton, as well as adding predicate
	forms of union_list and intersect_list.

	I also added missing type_spec pragmas for some predicates frequently
	used by the compiler.

library/tree_bitset.m:
	Bring the interface of this module closer to set.m and set_ordlist.m
	to make them more easily interchangeable. This required adding the
	predicates is_non_empty and is_singleton, and both function and
	predicate forms of union_list and intersect_list.

	Fix an old bug in the difference operation. Given SetA - SetB,
	if SetA was the empty set, then this operation would correctly
	return the empty set if SetB was small (represented by a leaf list),
	but would incorrectly return SetB if it was large (represented by
	an interior node list).
2011-07-21 06:58:34 +00:00

190 lines
7.3 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 1995-1996, 2004-2006, 2010-2011 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.
%-----------------------------------------------------------------------------%
%
% File: graph_colour.m.
% Main author: conway.
%
% This file contains functionality to find a 'good' colouring of a graph.
% The predicate group_elements(set(set(T)), set(set(T))),
% takes a set of sets each containing elements that touch, and returns
% a set of sets each containing elements that can be assigned the same
% colour, ensuring that touching elements have different colours.
% ("Good" means using as few colours as possible.)
%
% XXX We do not use this module anymore. Instead, we use set_of_var.m,
% which uses a more efficient representation of sets of elements. Since that
% more efficient representation depends on knowing that the elements are
% variables and therefore in the enum type class, a generic module like this
% cannot use that representation.
%
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- module libs.graph_colour.
:- interface.
:- import_module set.
%-----------------------------------------------------------------------------%
:- pred group_elements(set(set(T))::in, set(set(T))::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module list.
:- import_module require.
%-----------------------------------------------------------------------------%
group_elements(!.Constraints, Colours) :-
set.power_union(!.Constraints, AllVars),
set.init(EmptySet),
set.delete(EmptySet, !Constraints),
set.to_sorted_list(!.Constraints, ConstraintList),
find_all_colours(ConstraintList, AllVars, ColourList),
set.list_to_set(ColourList, Colours).
% % performance reducing sanity check....
% (
% set.power_union(Colours, AllColours),
% (set.member(Var, AllVars) => set.member(Var, AllColours))
% ->
% error("group_elements: sanity check failed")
% ;
% true
% ).
%-----------------------------------------------------------------------------%
% Iterate the assignment of a new colour until all constraints
% are satisfied.
%
:- pred find_all_colours(list(set(T))::in, set(T)::in,
list(set(T))::out) is det.
find_all_colours(ConstraintList, Vars, ColourList) :-
(
ConstraintList = [],
ColourList = []
;
ConstraintList = [_ | _],
next_colour(Vars, ConstraintList, RemainingConstraints, Colour),
set.difference(Vars, Colour, RestVars),
find_all_colours(RemainingConstraints, RestVars, ColourList0),
ColourList = [Colour | ColourList0]
).
%-----------------------------------------------------------------------------%
:- pred next_colour(set(T)::in, list(set(T))::in,
list(set(T))::out, set(T)::out) is det.
next_colour(Vars0, ConstraintList, Remainder, SameColour) :-
% Check if there are any constraints left to be satisfied.
(
ConstraintList = [_ | _],
% Select a variable to assign a colour, ...
choose_var(Vars0, Var, Vars1),
% ... and divide the constraints into those that
% may be the same colour as that var and those
% that may not.
divide_constraints(Var, ConstraintList, WereContaining, NotContaining,
Vars1, RestVars),
(
% See if there are sets that can share a colour with the
% selected var.
NotContaining = [_ | _],
( set.empty(RestVars) ->
% There were no variables left that could share a colour,
% so create a singleton set containing this variable.
set.singleton_set(SameColour, Var),
ResidueSets = NotContaining
;
% If there is at least one variable that can share a colour
% with the selected variable, then recursively use the
% remaining constraints to assign a colour to one of the
% remaining vars, and assemble the constraint residues.
next_colour(RestVars, NotContaining, ResidueSets, SameColour0),
% Add this variable to the variables of the current colour.
set.insert(Var, SameColour0, SameColour)
)
;
NotContaining = [],
% There were no more constraints which could be satisfied
% by assigning any variable a colour the same as the current
% variable, so create a signleton set with the current var,
% and assign the residue to the empty set.
set.singleton_set(SameColour, Var),
ResidueSets = []
),
% The remaining constraints are the residue sets that could not be
% satisfied by assigning any variable to the current colour, and the
% constraints that were already satisfied by the assignment of the
% current variable to this colour.
list.append(ResidueSets, WereContaining, Remainder)
;
% If there were no constraints, then no colours were needed.
ConstraintList = [],
Remainder = [],
set.init(SameColour)
).
%-----------------------------------------------------------------------------%
% Divide_constraints takes a var and a list of sets of var, and divides
% the list into two lists: a list of sets containing the given variable
% and a list of sets not containing that variable. The sets in the list
% containing the variable have that variable removed. Additionally, a set
% of variables is threaded through the computation, and any variables that
% were in sets that also contained the given variables are removed from
% the threaded set.
%
:- pred divide_constraints(T::in, list(set(T))::in,
list(set(T))::out, list(set(T))::out, set(T)::in, set(T)::out) is det.
divide_constraints(_Var, [], [], [], !Vars).
divide_constraints(Var, [S | Ss], C, NC, !Vars) :-
divide_constraints(Var, Ss, C0, NC0, !Vars),
( set.member(Var, S) ->
set.delete(Var, S, T),
( set.empty(T) ->
C = C0
;
C = [T | C0]
),
NC = NC0,
set.difference(!.Vars, T, !:Vars)
;
C = C0,
NC = [S | NC0]
).
%-----------------------------------------------------------------------------%
% Choose_var/3, given a set of variables, chooses one, returns it
% and the set with that variable removed.
%
:- pred choose_var(set(T)::in, T::out, set(T)::out) is det.
choose_var(Vars0, Var, Vars) :-
( set.remove_least(VarPrime, Vars0, VarsPrime) ->
Var = VarPrime,
Vars = VarsPrime
;
unexpected($module, $pred, "no vars!")
).
%-----------------------------------------------------------------------------%
:- end_module libs.graph_colour.
%-----------------------------------------------------------------------------%