Files
mercury/compiler/delay_construct.m
Zoltan Somogyi 585c1d623c Fix a problem with from_ground_term scopes.
Estimated hours taken: 20
Branches: main

Fix a problem with from_ground_term scopes. When they are built, the scopes
are tentantively marked as from_ground_term_construct scopes, and the
unifications inside them are in a top down order. Mode analysis therefore
expected the unifications inside from_ground_term_construct scopes to have
that order.

The problem was that mode analysis, when it confirmed that a
from_ground_term scope is indeed a from_ground_term_construct scope,
itself reversed the order of the unifications, putting them in a bottom up
order. When mode analysis is reinvoked, either for unique mode checking, or
after cse_detection finds common subexpressions, this meant that
mode analysis found the unifications in the "wrong" order, and therefore
disregarded the scope, discarding all its performance benefits.

This diff separates out the two notions that we previously conflated.
The scope kind from_ground_term_construct now refers only to scopes
which are definitely known to construct ground terms. We can know that
only after mode analysis. Until then, from_ground_term scopes are now marked
as from_ground_term_initial. The two kinds have different though overlapping
sets of invariants; in particular, they promise different orderings of the
unifications in the scope.

This diff reduces the time needed to compile mas_objects.data.m
from about 221 seconds to about 8.

compiler/hlds_goal.m:
	Add the from_ground_term_initial kind. Document the invariants
	that each kind of from_ground_term scope promises.

compiler/superhomogeneous.m:
	Mark from_ground_term scopes initially as from_ground_term_initial,
	not from_ground_term_construct.

compiler/post_typecheck.m:
	Make the predicate that converts function calls that look like
	unifications (such as X = int.min) into actual function calls
	say whether it performed such a conversion.

compiler/purity.m:
	Use the new functionality in post_typecheck.m to convert
	from_ground_term_initial scopes into from_ground_term_other scopes
	if the conversion of a unification into a call means that we have to
	break an invariant expected of from_ground_term_initial scopes.

compiler/cse_detection.m:
compiler/switch_detection.m:
	Maintain the invariants we now expect of from_ground_term_deconstruct
	scopes.

compiler/modecheck_goal.m:
	Maintain the invariants we now expect of the different
	from_ground_term scopes.

	Avoid traversing such scopes if a previous invocation of mode analysis
	says we can.

	Optimize away from_ground_term_construct scopes if the variable being
	constructed is not needed later.

compiler/quantification.m:
	If the variable named in a from_ground_term_initial or
	from_ground_term_construct scope is not referred to outside the scope,
	set the nonlocals set of the scope to empty, which allows later
	compiler passes to optimize it away.

	Avoid some unnecessary work by the compiler.

compiler/add_trail_ops.m:
compiler/closure_analysis.m:
compiler/constraint.m:
compiler/dead_proc_elim.m:
compiler/deep_profile.m:
compiler/deforest.m:
compiler/delay_construct.m:
compiler/delay_partial_inst.m:
compiler/dep_par_conj.m:
compiler/dependency_graph.m:
compiler/exception_analysis.m:
compiler/follow_code.m:
compiler/follow_vars.m:
compiler/goal_form.m:
compiler/goal_util.m:
compiler/granularity.m:
compiler/inlining.m:
compiler/interval.m:
compiler/lambda.m:
compiler/lco.m:
compiler/middle_rec.m:
compiler/mode_util.m:
compiler/parallel_to_plain.m:
compiler/simplify.m:
compiler/stm_expand.m:
compiler/stratify.m:
compiler/tabling_analysis.m:
compiler/term_pass1.m:
compiler/try_expand.m:
compiler/tupling.m:
compiler/untupling.m:
compiler/unused_args.m:
	Avoid traversing from_ground_term_deconstruct scopes in cases
	where the invariants that now hold (mainly the absence of anything
	but deconstruct unifications) make such traversals unnecessary.

compiler/live_vars.m:
compiler/liveness.m:
compiler/structure_reuse.lbu.m:
	Add comments about exploiting from_ground_term_deconstruct scopes.

compiler/det_analysis.m:
compiler/hlds_out_goal.m:
compiler/polymorphism.m:
compiler/saved_vars.m:
compiler/unique_modes.m:
	Handle from_ground_term_initial scopes.

compiler/handle_options.m:
	Add a dump verbosity option that is useful for comparing HLDS dumps
	created by two different compilers.

compiler/type_util.m:
	Minor speedup.

compiler/mode_info.m:
compiler/modecheck_conj.m:
compiler/prog_data.m:
compiler/rbmm.region_transformation.m:
compiler/typecheck.m:
	Improve documentation.
2011-08-31 07:59:35 +00:00

284 lines
12 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2001-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: delay_construct.m.
% Author: zs.
%
% This module transforms sequences of goals in procedure bodies. It looks for
% a unification that constructs a ground term followed by primitive goals, at
% least one of which can fail, and none of which take the variable
% representing the cell as their input. Such code sequences cause the cell to
% be constructed even if the following goal would fail, which is wasteful.
% This module therefore reorders the sequence, moving the construction
% unification past all the semidet primitives it can.
%
% The reason we don't move the construction past calls or composite goals is
% that this may require storing the input arguments of the construction on the
% stack, which may cause a slowdown bigger than the speedup available from not
% having to construct the cell on some execution paths.
%
%-----------------------------------------------------------------------------%
:- module transform_hlds.delay_construct.
:- interface.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
%-----------------------------------------------------------------------------%
:- pred delay_construct_proc(module_info::in, pred_proc_id::in,
proc_info::in, proc_info::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module check_hlds.inst_match.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_rtti.
:- import_module hlds.instmap.
:- import_module hlds.passes_aux.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.set_of_var.
:- import_module bool.
:- import_module list.
:- import_module require.
:- import_module set.
%-----------------------------------------------------------------------------%
delay_construct_proc(ModuleInfo, proc(PredId, ProcId), !ProcInfo) :-
trace [io(!IO)] (
write_proc_progress_message("% Delaying construction unifications in ",
PredId, ProcId, ModuleInfo, !IO)
),
module_info_get_globals(ModuleInfo, Globals),
module_info_pred_info(ModuleInfo, PredId, PredInfo),
body_should_use_typeinfo_liveness(PredInfo, Globals, BodyTypeinfoLiveness),
proc_info_get_vartypes(!.ProcInfo, VarTypes),
proc_info_get_rtti_varmaps(!.ProcInfo, RttiVarMaps),
proc_info_get_initial_instmap(!.ProcInfo, ModuleInfo, InstMap0),
DelayInfo = delay_construct_info(ModuleInfo, BodyTypeinfoLiveness,
VarTypes, RttiVarMaps),
proc_info_get_goal(!.ProcInfo, Goal0),
delay_construct_in_goal(Goal0, InstMap0, DelayInfo, Goal),
proc_info_set_goal(Goal, !ProcInfo).
:- type delay_construct_info
---> delay_construct_info(
dci_module_info :: module_info,
dci_body_typeinfo_liveness :: bool,
dci_vartypes :: vartypes,
dci_rtti_varmaps :: rtti_varmaps
).
%-----------------------------------------------------------------------------%
:- pred delay_construct_in_goal(hlds_goal::in, instmap::in,
delay_construct_info::in, hlds_goal::out) is det.
delay_construct_in_goal(Goal0, InstMap0, DelayInfo, Goal) :-
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
(
GoalExpr0 = conj(ConjType, Goals0),
(
ConjType = plain_conj,
Detism = goal_info_get_determinism(GoalInfo0),
determinism_components(Detism, CanFail, MaxSoln),
(
% If the conjunction cannot fail, then its conjuncts cannot
% fail either, so we have no hope of pushing a construction
% past a failing goal.
%
% If the conjuntion contains goals that can succeed more than
% once, which is possible if MaxSoln is at_most_many or
% at_most_many_cc, then moving a construction to the right
% may increase the number of times the construction is
% executed. We are therefore careful to make sure
% delay_construct_in_conj doesn't move constructions
% across goals that succeed more than once. If the conjunction
% cannot succeed, i.e. MaxSoln is at_most_zero, there is no
% point in trying to speed it up.
CanFail = can_fail,
MaxSoln \= at_most_zero
->
delay_construct_in_conj(Goals0, InstMap0, DelayInfo, set.init,
[], Goals1)
;
Goals1 = Goals0
)
;
ConjType = parallel_conj,
Goals1 = Goals0
),
delay_construct_in_goals(Goals1, InstMap0, DelayInfo, Goals),
Goal = hlds_goal(conj(ConjType, Goals), GoalInfo0)
;
GoalExpr0 = disj(Goals0),
delay_construct_in_goals(Goals0, InstMap0, DelayInfo, Goals),
Goal = hlds_goal(disj(Goals), GoalInfo0)
;
GoalExpr0 = negation(NegGoal0),
delay_construct_in_goal(NegGoal0, InstMap0, DelayInfo, NegGoal),
Goal = hlds_goal(negation(NegGoal), GoalInfo0)
;
GoalExpr0 = switch(Var, CanFail, Cases0),
delay_construct_in_cases(Cases0, InstMap0, DelayInfo, Cases),
Goal = hlds_goal(switch(Var, CanFail, Cases), GoalInfo0)
;
GoalExpr0 = if_then_else(Vars, Cond0, Then0, Else0),
Cond0 = hlds_goal(_, CondInfo0),
CondInstMapDelta = goal_info_get_instmap_delta(CondInfo0),
instmap.apply_instmap_delta(InstMap0, CondInstMapDelta, InstMapThen),
delay_construct_in_goal(Cond0, InstMap0, DelayInfo, Cond),
delay_construct_in_goal(Then0, InstMapThen, DelayInfo, Then),
delay_construct_in_goal(Else0, InstMap0, DelayInfo, Else),
Goal = hlds_goal(if_then_else(Vars, Cond, Then, Else), GoalInfo0)
;
GoalExpr0 = scope(Reason, SubGoal0),
(
Reason = from_ground_term(_, FGT),
( FGT = from_ground_term_construct
; FGT = from_ground_term_deconstruct
)
->
Goal = Goal0
;
delay_construct_in_goal(SubGoal0, InstMap0, DelayInfo, SubGoal),
Goal = hlds_goal(scope(Reason, SubGoal), GoalInfo0)
)
;
( GoalExpr0 = generic_call(_, _, _, _)
; GoalExpr0 = plain_call(_, _, _, _, _, _)
; GoalExpr0 = unify(_, _, _, _, _)
; GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _)
),
Goal = Goal0
;
GoalExpr0 = shorthand(_),
% These should have been expanded out by now.
unexpected($module, $pred, "shorthand")
).
%-----------------------------------------------------------------------------%
% We maintain a list of delayed construction unifications that construct
% ground terms, and the set of variables they define.
%
% When we find other construction unifications, we add them to the list. It
% does not matter if they depend on other delayed construction unifications;
% when we put them back into the conjunction, we do so in the original order.
%
% There are several reasons why we may not be able to delay a construction
% unification past a conjunct. The conjunct may not be a primitive goal, or it
% may be impure; in either case, we must insert all the delayed construction
% unifications before it. The conjunct may also require the value of a
% variable defined by a construction unification. In such cases, we could drop
% before that goal only the construction unifications that define the
% variables needed by the conjunct, either directly or indirectly through the
% values required by some of those construction unifications. However,
% separating out this set of delayed constructions from the others would
% require somewhat complex code, and it is not clear that there would be any
% significant benefit. We therefore insert *all* the delayed constructions
% before a goal if the goal requires *any* of the variables bound by the
% constructions.
%
% The instmap we pass around is the one that we construct from the original
% conjunction order. At each point, it reflects the bindings made by the
% conjuncts so far *plus* the bindings made by the delayed goals.
:- pred delay_construct_in_conj(list(hlds_goal)::in, instmap::in,
delay_construct_info::in, set(prog_var)::in, list(hlds_goal)::in,
list(hlds_goal)::out) is det.
delay_construct_in_conj([], _, _, _, RevDelayedGoals, DelayedGoals) :-
list.reverse(RevDelayedGoals, DelayedGoals).
delay_construct_in_conj([Goal0 | Goals0], InstMap0, DelayInfo,
ConstructedVars0, RevDelayedGoals0, Goals) :-
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
InstMapDelta0 = goal_info_get_instmap_delta(GoalInfo0),
instmap.apply_instmap_delta(InstMap0, InstMapDelta0, InstMap1),
(
GoalExpr0 = unify(_, _, _, Unif, _),
Unif = construct(Var, _, Args, _, _, _, _),
Args = [_ | _], % We are constructing a cell, not a constant
instmap_lookup_var(InstMap0, Var, Inst0),
inst_is_free(DelayInfo ^ dci_module_info, Inst0),
instmap_lookup_var(InstMap1, Var, Inst1),
inst_is_ground(DelayInfo ^ dci_module_info, Inst1)
->
set.insert(Var, ConstructedVars0, ConstructedVars1),
RevDelayedGoals1 = [Goal0 | RevDelayedGoals0],
delay_construct_in_conj(Goals0, InstMap1, DelayInfo,
ConstructedVars1, RevDelayedGoals1, Goals)
;
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
delay_construct_skippable(GoalExpr0, GoalInfo0),
NonLocals = goal_info_get_nonlocals(GoalInfo0),
maybe_complete_with_typeinfo_vars(NonLocals,
DelayInfo ^ dci_body_typeinfo_liveness,
DelayInfo ^ dci_vartypes,
DelayInfo ^ dci_rtti_varmaps, CompletedNonLocals),
set_of_var.intersect(CompletedNonLocals,
set_to_bitset(ConstructedVars0), Intersection),
set_of_var.is_empty(Intersection),
goal_info_get_purity(GoalInfo0) = purity_pure
->
delay_construct_in_conj(Goals0, InstMap1, DelayInfo,
ConstructedVars0, RevDelayedGoals0, Goals1),
Goals = [Goal0 | Goals1]
;
list.reverse(RevDelayedGoals0, DelayedGoals),
delay_construct_in_conj(Goals0, InstMap1, DelayInfo,
set.init, [], Goals1),
list.append(DelayedGoals, [Goal0 | Goals1], Goals)
).
:- pred delay_construct_skippable(hlds_goal_expr::in, hlds_goal_info::in)
is semidet.
delay_construct_skippable(GoalExpr, GoalInfo) :-
(
GoalExpr = unify(_, _, _, _, _)
;
GoalExpr = plain_call(_, _, _, inline_builtin, _, _)
),
Detism = goal_info_get_determinism(GoalInfo),
determinism_components(Detism, _CanFail, MaxSoln),
MaxSoln \= at_most_many.
%-----------------------------------------------------------------------------%
:- pred delay_construct_in_goals(list(hlds_goal)::in, instmap::in,
delay_construct_info::in, list(hlds_goal)::out) is det.
delay_construct_in_goals([], _, _, []).
delay_construct_in_goals([Goal0 | Goals0], InstMap0, DelayInfo,
[Goal | Goals]) :-
delay_construct_in_goal(Goal0, InstMap0, DelayInfo, Goal),
delay_construct_in_goals(Goals0, InstMap0, DelayInfo, Goals).
:- pred delay_construct_in_cases(list(case)::in, instmap::in,
delay_construct_info::in, list(case)::out) is det.
delay_construct_in_cases([], _, _, []).
delay_construct_in_cases([Case0 | Cases0], InstMap0, DelayInfo,
[Case | Cases]) :-
Case0 = case(MainConsId, OtherConsIds, Goal0),
delay_construct_in_goal(Goal0, InstMap0, DelayInfo, Goal),
Case = case(MainConsId, OtherConsIds, Goal),
delay_construct_in_cases(Cases0, InstMap0, DelayInfo, Cases).
%-----------------------------------------------------------------------------%
:- end_module transform_hlds.delay_construct.
%-----------------------------------------------------------------------------%