Files
mercury/compiler/structure_reuse.versions.m
Zoltan Somogyi b000cb322e Provide compiler support for Software Transactional Memory through the new
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.
2008-02-27 07:23:57 +00:00

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.
%------------------------------------------------------------------------------%