mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-14 05:12:33 +00:00
Estimated hours taken: 5 Branches: main Fix the failure of the invalid/modes_erroneous test case, whose symptom was an error message about a "mode error in unification of `X' and `X'". The root cause of the problem was that the renaming of head variables computed by headvar_names.m was being applied too early, during typechecking. The fix is to apply it after the frontend (all the passes that can generate error messages). To avoid slowdowns from larger pred_infos, this diff also moves the least frequently used fields of pred_infos to a subterm. (Proc_infos already had a subterm.) This leads to an almost 3% speedup. compiler/headvar_names.m: Store the renaming instead of applying it. compiler/simplify.m: Apply the renaming in invocations after the front end, since doing so may allow some excess assignments to be eliminated. compiler/hlds_pred.m: Add fields to pred_infos and proc_infos for the renaming. Move the least frequently used fields of pred_infos into a pred_sub_info. Some fields of pred_infos were being accessed using predicates that did not follow our naming conventions, and some were accessed using field access functions that are now inappropriate; fix them all. Require the caller to provide the renaming when creating new pred_infos and proc_infos. This is to force the compiler components that do this to propagate the renaming fields of the original predicates and/or procedures to their modified versions. Convert that some old code that used if-then-elses to use switches instead. compiler/hlds_out.m: Write out the new pred_info and proc_info fields. compiler/*.m: Conform to the changes in hlds_pred.m. compiler/hlds_clauses.m: Avoid ambiguity by giving a prefix to the fields of the clauses_info type. tests/invalid/ho_type_mode_bug.err_exp: tests/invalid/merge_ground_any.err_exp: Don't expect error messages about "X = X" anymore.
329 lines
13 KiB
Mathematica
329 lines
13 KiB
Mathematica
%------------------------------------------------------------------------------%
|
|
% vim: ft=mercury ff=unix ts=4 sw=4 et
|
|
%------------------------------------------------------------------------------%
|
|
% Copyright (C) 2006-2007 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: structure_reuse.versions.m.
|
|
% Main authors: nancy.
|
|
%
|
|
% Provide the functionality to create optimised versions of those procedures
|
|
% for which reuse was detected.
|
|
%
|
|
%------------------------------------------------------------------------------%
|
|
|
|
:- module structure_reuse.versions.
|
|
:- interface.
|
|
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module transform_hlds.ctgc.structure_reuse.domain.
|
|
|
|
:- import_module io.
|
|
|
|
%------------------------------------------------------------------------------%
|
|
|
|
|
|
% For each of the entries in the reuse table:
|
|
% * if the listed reuse is conditional, then duplicate the
|
|
% pred-info/proc-info of the original procedure, changing all potential
|
|
% reuse annotation to real reuses;
|
|
% * if the listed reuse is unconditional, then no duplication is needed,
|
|
% yet the goal needs to be traversed to correctly replace all
|
|
% procedure calls to calls to reuse versions whenever needed.
|
|
% * if the listed reuse is "no reuse", then obviously, nothing needs to
|
|
% be done.
|
|
%
|
|
% This process updates the module information by adding the new predicates,
|
|
% and recording the pred-proc-id to the reuse pred-proc-id mappings in
|
|
% module_info.
|
|
%
|
|
:- pred create_reuse_procedures(reuse_as_table::in, module_info::in,
|
|
module_info::out, io::di, io::uo) is det.
|
|
|
|
% Create a copy of the predicate/procedure information specified by the
|
|
% given pred_proc_id, and return the pred_proc_id of that copy. This
|
|
% operation also updates the structure_reuse_map in the HLDS. Note that the
|
|
% copy is not altered w.r.t. structure reuse. It is a plain copy, nothing
|
|
% more than that.
|
|
%
|
|
:- pred create_fresh_pred_proc_info_copy(pred_proc_id::in, pred_proc_id::out,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
%------------------------------------------------------------------------------%
|
|
%------------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.passes_aux.
|
|
:- import_module hlds.pred_table.
|
|
:- import_module libs.compiler_util.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module parse_tree.prog_util.
|
|
|
|
:- import_module bool.
|
|
:- import_module map.
|
|
:- import_module pair.
|
|
:- import_module list.
|
|
|
|
%------------------------------------------------------------------------------%
|
|
|
|
:- type reuse_name == sym_name.
|
|
|
|
:- func generate_reuse_name(module_info, pred_proc_id) = reuse_name.
|
|
generate_reuse_name(ModuleInfo, PPId) = ReuseName :-
|
|
module_info_pred_proc_info(ModuleInfo, PPId, PredInfo, _ProcInfo),
|
|
PPId = proc(_, ProcId),
|
|
Line = 0,
|
|
Counter = proc_id_to_int(ProcId),
|
|
make_pred_name_with_context(pred_info_module(PredInfo), "ctgc",
|
|
pred_info_is_pred_or_func(PredInfo), pred_info_name(PredInfo),
|
|
Line, Counter, ReuseName).
|
|
|
|
%------------------------------------------------------------------------------%
|
|
|
|
% This process can be split into separate steps:
|
|
% - determine all the pred-proc-ids of procedure with conditional reuse;
|
|
% - create duplicates of these procedures (and record the mapping in
|
|
% the structure_reuse_map in module_info);
|
|
% - traverse all these procedures + the procedures with unconditional reuse
|
|
% to correctly update the reuse annotations.
|
|
%
|
|
create_reuse_procedures(ReuseTable, !ModuleInfo, !IO):-
|
|
PPIds = map.keys(ReuseTable),
|
|
CondPPIds = list.filter(has_conditional_reuse(ReuseTable), PPIds),
|
|
UncondPPIds = list.filter(has_unconditional_reuse(ReuseTable), PPIds),
|
|
|
|
% Create all the duplicates:
|
|
list.map_foldl(create_fresh_pred_proc_info_copy,
|
|
CondPPIds, ReuseCondPPIds, !ModuleInfo),
|
|
|
|
% Process all the goals to update the reuse annotations:
|
|
module_info_get_structure_reuse_map(!.ModuleInfo, ReuseMap),
|
|
list.foldl2(process_proc(ReuseMap), list.append(ReuseCondPPIds,
|
|
UncondPPIds), !ModuleInfo, !IO).
|
|
|
|
:- pred has_conditional_reuse(reuse_as_table::in, pred_proc_id::in) is semidet.
|
|
|
|
has_conditional_reuse(ReuseTable, PPId) :-
|
|
ReuseAs = reuse_as_table_search(PPId, ReuseTable),
|
|
reuse_as_conditional_reuses(ReuseAs).
|
|
|
|
:- pred has_unconditional_reuse(reuse_as_table::in, pred_proc_id::in)
|
|
is semidet.
|
|
has_unconditional_reuse(ReuseTable, PPId) :-
|
|
ReuseAs = reuse_as_table_search(PPId, ReuseTable),
|
|
reuse_as_all_unconditional_reuses(ReuseAs).
|
|
|
|
%------------------------------------------------------------------------------%
|
|
|
|
create_fresh_pred_proc_info_copy(PPId, NewPPId, !ModuleInfo):-
|
|
module_info_pred_proc_info(!.ModuleInfo, PPId, PredInfo0, ProcInfo0),
|
|
ReusePredName = generate_reuse_name(!.ModuleInfo, PPId),
|
|
create_fresh_pred_proc_info_copy_2(PredInfo0, ProcInfo0, ReusePredName,
|
|
ReusePredInfo, ReuseProcId),
|
|
|
|
module_info_get_predicate_table(!.ModuleInfo, PredTable0),
|
|
predicate_table_insert(ReusePredInfo, ReusePredId, PredTable0, PredTable),
|
|
NewPPId = proc(ReusePredId, ReuseProcId),
|
|
module_info_set_predicate_table(PredTable, !ModuleInfo),
|
|
|
|
module_info_get_structure_reuse_map(!.ModuleInfo, ReuseMap0),
|
|
map.det_insert(ReuseMap0, PPId, NewPPId - ReusePredName, ReuseMap),
|
|
module_info_set_structure_reuse_map(ReuseMap, !ModuleInfo).
|
|
|
|
|
|
:- pred create_fresh_pred_proc_info_copy_2(pred_info::in, proc_info::in,
|
|
reuse_name::in, pred_info::out, proc_id::out) is det.
|
|
|
|
create_fresh_pred_proc_info_copy_2(PredInfo, ProcInfo, ReusePredName,
|
|
ReusePredInfo, ReuseProcId):-
|
|
ModuleName = pred_info_module(PredInfo),
|
|
PredOrFunc = pred_info_is_pred_or_func(PredInfo),
|
|
pred_info_get_context(PredInfo, ProgContext),
|
|
pred_info_get_origin(PredInfo, PredOrigin),
|
|
pred_info_get_import_status(PredInfo, ImportStatus),
|
|
pred_info_get_markers(PredInfo, PredMarkers),
|
|
pred_info_get_arg_types(PredInfo, MerTypes),
|
|
pred_info_get_typevarset(PredInfo, TVarset),
|
|
pred_info_get_exist_quant_tvars(PredInfo, ExistQTVars),
|
|
pred_info_get_class_context(PredInfo, ProgConstraints),
|
|
pred_info_get_assertions(PredInfo, AssertIds),
|
|
pred_info_get_var_name_remap(PredInfo, VarNameRemap),
|
|
pred_info_create(ModuleName, ReusePredName, PredOrFunc, ProgContext,
|
|
PredOrigin, ImportStatus, PredMarkers, MerTypes, TVarset,
|
|
ExistQTVars, ProgConstraints, AssertIds, VarNameRemap,
|
|
ProcInfo, ReuseProcId, ReusePredInfo).
|
|
|
|
%------------------------------------------------------------------------------%
|
|
|
|
% Process the goal of the procedure with the given pred_proc_id so that
|
|
% all potential reuses are replaced by real reuses, and all calls to
|
|
% procedures that have a reuse version are replaced by calls to their
|
|
% reuse version (if of course, that is in accordance with the reuse
|
|
% annotations).
|
|
%
|
|
:- pred process_proc(structure_reuse_map::in,
|
|
pred_proc_id::in, module_info::in, module_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
process_proc(ReuseMap, PPId, !ModuleInfo, !IO):-
|
|
write_proc_progress_message("(reuse version) ", PPId, !.ModuleInfo, !IO),
|
|
module_info_pred_proc_info(!.ModuleInfo, PPId, PredInfo0, ProcInfo0),
|
|
proc_info_get_goal(ProcInfo0, Goal0),
|
|
process_goal(ReuseMap, Goal0, Goal, !IO),
|
|
proc_info_set_goal(Goal, ProcInfo0, ProcInfo),
|
|
module_info_set_pred_proc_info(PPId, PredInfo0, ProcInfo, !ModuleInfo).
|
|
|
|
:- pred process_goal(structure_reuse_map::in, hlds_goal::in, hlds_goal::out,
|
|
io::di, io::uo) is det.
|
|
|
|
process_goal(ReuseMap, !Goal, !IO) :-
|
|
!.Goal = hlds_goal(GoalExpr0, GoalInfo0),
|
|
(
|
|
GoalExpr0 = conj(ConjType, Goals0),
|
|
list.map_foldl(process_goal(ReuseMap), Goals0, Goals, !IO),
|
|
GoalExpr = conj(ConjType, Goals),
|
|
!:Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
GoalExpr0 = plain_call(CalleePredId, CalleeProcId, Args, BI, UC,
|
|
CalleePredName),
|
|
ReuseDescription0 = goal_info_get_reuse(GoalInfo0),
|
|
(
|
|
% If the reuse description already sais "reuse", then this is
|
|
% a call to a procedure which might have specified conditions, yet
|
|
% whose conditions are always met, hence do not imply conditions on
|
|
% the procedure in which this call appears. We must therefore
|
|
% make sure to call the appropriate version of the called
|
|
% procedure.
|
|
ReuseDescription0 = reuse(reuse_call(_CondDescr))
|
|
->
|
|
determine_reuse_version(ReuseMap, CalleePredId, CalleeProcId,
|
|
CalleePredName, ReuseCalleePredId, ReuseCalleeProcId,
|
|
ReuseCalleePredName),
|
|
GoalExpr = plain_call(ReuseCalleePredId, ReuseCalleeProcId,
|
|
Args, BI, UC, ReuseCalleePredName),
|
|
!:Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
ReuseDescription0 = potential_reuse(reuse_call(CondDescr))
|
|
->
|
|
% Replace the call to the reuse version, and change the
|
|
% potential reuse annotation to a real annotation.
|
|
determine_reuse_version(ReuseMap, CalleePredId, CalleeProcId,
|
|
CalleePredName, ReuseCalleePredId, ReuseCalleeProcId,
|
|
ReuseCalleePredName),
|
|
GoalExpr = plain_call(ReuseCalleePredId, ReuseCalleeProcId,
|
|
Args, BI, UC, ReuseCalleePredName),
|
|
ReuseDescription = reuse(reuse_call(CondDescr)),
|
|
goal_info_set_reuse(ReuseDescription, GoalInfo0, GoalInfo),
|
|
!:Goal = hlds_goal(GoalExpr, GoalInfo)
|
|
;
|
|
true
|
|
)
|
|
;
|
|
GoalExpr0 = generic_call(_, _, _, _)
|
|
;
|
|
GoalExpr0 = unify(_, _, _, Unification0, _),
|
|
ReuseDescription0 = goal_info_get_reuse(GoalInfo0),
|
|
(
|
|
ReuseDescription0 = potential_reuse(Descr)
|
|
->
|
|
ReuseDescription = reuse(Descr),
|
|
unification_set_reuse(Descr, Unification0,
|
|
Unification),
|
|
GoalExpr = GoalExpr0 ^ unify_kind := Unification,
|
|
goal_info_set_reuse(ReuseDescription, GoalInfo0, GoalInfo),
|
|
!:Goal = hlds_goal(GoalExpr, GoalInfo)
|
|
;
|
|
true
|
|
)
|
|
;
|
|
GoalExpr0 = disj(Goals0),
|
|
list.map_foldl(process_goal(ReuseMap), Goals0, Goals, !IO),
|
|
GoalExpr = disj(Goals),
|
|
!:Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
GoalExpr0 = switch(A, B, Cases0),
|
|
list.map_foldl(process_case(ReuseMap), Cases0, Cases, !IO),
|
|
GoalExpr = switch(A, B, Cases),
|
|
!:Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
% XXX To check and compare with the theory.
|
|
GoalExpr0 = negation(_Goal)
|
|
;
|
|
GoalExpr0 = scope(A, SubGoal0),
|
|
process_goal(ReuseMap, SubGoal0, SubGoal, !IO),
|
|
GoalExpr = scope(A, SubGoal),
|
|
!:Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
GoalExpr0 = if_then_else(A, IfGoal0, ThenGoal0, ElseGoal0),
|
|
process_goal(ReuseMap, IfGoal0, IfGoal, !IO),
|
|
process_goal(ReuseMap, ThenGoal0, ThenGoal, !IO),
|
|
process_goal(ReuseMap, ElseGoal0, ElseGoal, !IO),
|
|
GoalExpr = if_then_else(A, IfGoal, ThenGoal, ElseGoal),
|
|
!:Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
GoalExpr0 = call_foreign_proc(_Attrs, _ForeignPredId, _ForeignProcId,
|
|
_Args, _ExtraArgs, _MaybeTraceRuntimeCond, _Impl)
|
|
;
|
|
GoalExpr0 = shorthand(_),
|
|
unexpected(this_file, "process_goal: shorthand goal.")
|
|
).
|
|
|
|
:- pred unification_set_reuse(short_reuse_description::in,
|
|
unification::in, unification::out) is det.
|
|
|
|
unification_set_reuse(ShortReuseDescription, !Unification) :-
|
|
(
|
|
!.Unification = construct(A, B, C, D, _HowToConstruct, F, G),
|
|
ShortReuseDescription = cell_reused(DeadVar, _, PossibleConsIds,
|
|
CellsToUpdate)
|
|
->
|
|
CellToReuse = cell_to_reuse(DeadVar, PossibleConsIds,
|
|
list.map(needs_update_to_bool, CellsToUpdate)),
|
|
HowToConstruct = reuse_cell(CellToReuse),
|
|
!:Unification = construct(A, B, C, D, HowToConstruct, F, G)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- func needs_update_to_bool(needs_update) = bool.
|
|
needs_update_to_bool(needs_update) = no.
|
|
needs_update_to_bool(does_not_need_update) = yes.
|
|
|
|
:- pred determine_reuse_version(structure_reuse_map::in, pred_id::in,
|
|
proc_id::in, sym_name::in, pred_id::out, proc_id::out,
|
|
reuse_name::out) is det.
|
|
|
|
determine_reuse_version(ReuseMap, PredId, ProcId, PredName,
|
|
ReusePredId, ReuseProcId, ReusePredName) :-
|
|
( map.search(ReuseMap, proc(PredId, ProcId), Result) ->
|
|
Result = proc(ReusePredId, ReuseProcId) - ReusePredName
|
|
;
|
|
ReusePredId = PredId,
|
|
ReuseProcId = ProcId,
|
|
ReusePredName = PredName
|
|
).
|
|
|
|
:- pred process_case(structure_reuse_map::in, case::in, case::out,
|
|
io::di, io::uo) is det.
|
|
|
|
process_case(ReuseMap, !Case, !IO) :-
|
|
!.Case = case(ConsId, Goal0),
|
|
process_goal(ReuseMap, Goal0, Goal, !IO),
|
|
!:Case = case(ConsId, Goal).
|
|
|
|
%------------------------------------------------------------------------------%
|
|
|
|
:- func this_file = string.
|
|
|
|
this_file = "structure_reuse.versions.m".
|
|
|
|
%------------------------------------------------------------------------------%
|
|
:- end_module structure_reuse.versions.
|
|
%------------------------------------------------------------------------------%
|