mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-21 00:39:37 +00:00
Estimated hours taken: 80 by zs, and lots more by lmika Branches: main Provide compiler support for Software Transactional Memory through the new atomic goal. This work was done by Leon Mika; I merely brought it up to date, resolved conflicts, and cleaned up a few things. There are still several aspects that are as yet incomplete. library/ops.m: Add the operators needed for the syntax of atomic scopes. library/stm_builtin.m: Add the builtin operations needed for the implementation of atomic goals. compiler/hlds_goal.m: Add a new HLDS goal type, which represents an atomic goal and its possible fallbacks (in case an earlier goal throws an exception). Rename the predicate goal_is_atomic as goal_expr_has_subgoals, since now its old name would be misleading. compiler/prog_data.m: compiler/prog_item.m: Add a parse tree representation of the new kind of goal. compiler/prog_io_goal.m: Parse the new kind of goal. compiler/add_clause.m: Translate atomic goals from parse tree form to HLDS. compiler/typecheck.m: compiler/typecheck_errors.m: Do type checking of atomic goals. compiler/modes.m: Do mode checking of atomic goals, and determine whether they are nested or not. compiler/unique_modes.m: Do unique mode checking of atomic goals. compiler/stm_expand.m: New module to expand atomic goals into sequences of simpler goals. library/stm_builtin.m: Add the primitives needed by the transformation. Improve the existing debugging support. mdbcomp/prim_data.m: Add utility functions to allow stm_expand.m to refer to modules in the library. mdbcomp/program_representation.m: Expand the goal_path type to allow the representation of components of atomic goals. compiler/notes/compiler_design.html: Document the new module. compiler/transform_hlds.m: Include the new module in the compiler. compiler/mercury_compile.m: Invoke the STM transformation. compiler/hlds_module.m: Add an auxiliary counter used by the STM transformation. compiler/hlds_pred.m: Add a new predicate origin: the STM transformation. compiler/modules.m: Import the STM builtin module automatically if the module contains any atomic goals. compiler/assertion.m: compiler/bytecode_gen.m: compiler/clause_to_proc.m: compiler/code_gen.m: compiler/code_info.m: compiler/code_util.m: compiler/constraint.m: compiler/cse_detection.m: compiler/deep_profiling.m: compiler/code_util.m: compiler/delay_construct.m: compiler/delay_partial_inst.m: compiler/dep_par_conj.m: compiler/dependency_graph.m: compiler/det_analysis.m: compiler/det_report.m: compiler/distance_granularity.m: compiler/equiv_type_hlds.m: compiler/erl_code_gen.m: compiler/exception_analysis.m: compiler/follow_code.m: compiler/format_call.m: compiler/goal_form.m: compiler/goal_path.m: compiler/goal_util.m: compiler/granularity.m: compiler/hlds_out.m: compiler/implicit_parallelism.m: compiler/inlining.m: compiler/intermod.m: compiler/lambda.m: compiler/layout_out.m: compiler/lco.m: compiler/lookup_switch.m: compiler/make_hlds_warn.m: compiler/mark_static_terms.m: compiler/mercury_to_mercury.m: compiler/middle_rec.m: compiler/ml_code_gen.m: compiler/mode_constraint_robdd.m: compiler/mode_constraints.m: compiler/mode_errors.m: compiler/mode_info.m: compiler/mode_util.m: compiler/ordering_mode_constraints.m: compiler/pd_cost.m: compiler/pd_util.m: compiler/polymorphism.m: compiler/post_typecheck.m: compiler/prog_rep.m: compiler/prog_type.m: compiler/prop_mode_constraints.m: compiler/rbmm.actual_region_arguments.m: compiler/rbmm.add_rbmm_goal_info.m: compiler/rbmm.condition_renaming.m: compiler/rbmm.execution_path.m: compiler/rbmm.points_to_analysis.m: compiler/rbmm.region_transformation.m: compiler/saved_vars.m: compiler/simplify.m: compiler/size_prog.m: compiler/smm_common.m: compiler/structure_reuse.direct.choose_reuse.m: compiler/structure_reuse.direct.detect_garbage.m: compiler/structure_reuse.indirect.m: compiler/structure_reuse.lbu.m: compiler/structure_reuse.lfu.m: compiler/structure_reuse.versions.m: compiler/structure_sharing.analysis.m: compiler/switch_detection.m: compiler/unused_imports.m: compiler/granularity.m: compiler/granularity.m: Conform to the changes above. Mostly this means handling the new kind of goal. compiler/add_heap_ops.m: compiler/add_trail_ops.m: compiler/build_mode_constraints.m: compiler/closure_analysis.m: compiler/dead_proc_elim.m: compiler/deforest.m: compiler/follow_vars.m: compiler/higher_order.m: compiler/live_vars.m: compiler/liveness.m: compiler/loop_inv.m: compiler/module_qual.m: compiler/prog_util.m: compiler/purity.m: compiler/quantification.m: compiler/store_alloc.m: compiler/stratify.m: compiler/tabling_analysis.m: compiler/term_constr_build.m: compiler/term_pass1.m: compiler/term_traversal.m: compiler/trailing_analysis.m: Conform to the changes above. Mostly this means handling the new kind of goal. Switch syntax from clauses to disj. runtime/mercury_stm.[ch]: Implement the primitives needed by the STM transformation. Add more debugging support to the existing primitives. library/term.m: Generalize get_term_context to work on terms of all kinds.
341 lines
14 KiB
Mathematica
341 lines
14 KiB
Mathematica
%------------------------------------------------------------------------------%
|
|
% vim: ft=mercury ff=unix ts=4 sw=4 et
|
|
%------------------------------------------------------------------------------%
|
|
% Copyright (C) 2006-2008 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 check_hlds.
|
|
:- import_module check_hlds.mode_util.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.passes_aux.
|
|
:- import_module hlds.pred_table.
|
|
:- import_module hlds.quantification.
|
|
:- 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),
|
|
ReusePPIds = ReuseCondPPIds ++ UncondPPIds,
|
|
list.foldl2(process_proc(ReuseMap), ReusePPIds, !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),
|
|
some [!ProcInfo] (
|
|
module_info_pred_proc_info(!.ModuleInfo, PPId, PredInfo0, !:ProcInfo),
|
|
proc_info_get_goal(!.ProcInfo, Goal0),
|
|
process_goal(ReuseMap, Goal0, Goal, !IO),
|
|
proc_info_set_goal(Goal, !ProcInfo),
|
|
|
|
% A dead variable needs to appear in the non-local set of the
|
|
% construction unification in which its space is reused, so we
|
|
% requantify. Then we recompute instmap deltas with the updated
|
|
% non-local sets.
|
|
requantify_proc(!ProcInfo),
|
|
recompute_instmap_delta_proc(do_not_recompute_atomic_instmap_deltas,
|
|
!ProcInfo, !ModuleInfo),
|
|
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 says "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(_),
|
|
% These should have been expanded out by now.
|
|
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,
|
|
CellsToUpdate),
|
|
HowToConstruct = reuse_cell(CellToReuse),
|
|
!:Unification = construct(A, B, C, D, HowToConstruct, F, G)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- 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(MainConsId, OtherConsIds, Goal0),
|
|
process_goal(ReuseMap, Goal0, Goal, !IO),
|
|
!:Case = case(MainConsId, OtherConsIds, Goal).
|
|
|
|
%------------------------------------------------------------------------------%
|
|
|
|
:- func this_file = string.
|
|
|
|
this_file = "structure_reuse.versions.m".
|
|
|
|
%------------------------------------------------------------------------------%
|
|
:- end_module structure_reuse.versions.
|
|
%------------------------------------------------------------------------------%
|