mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-18 07:15:19 +00:00
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.
284 lines
12 KiB
Mathematica
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.
|
|
%-----------------------------------------------------------------------------%
|