[m-rev.] for review: lcmc for high-level data
Peter Wang
novalazy at gmail.com
Tue Sep 15 13:31:21 AEST 2009
Branches: main
Support --optimize-constructor-last-call in the java grade, and possibly other
grades using high-level data.
compiler/handle_options.m:
Don't disable --optimize-constructor-last-call on high-level data, nor
if the target language is Java.
compiler/lco.m:
Use a modified transformation when targetting high-level data that
doesn't require taking the address of fields nor using the store_at_ref
builtin.
compiler/ml_unify_gen.m:
Handle the `take_address_fields' part of a construction unification
differently when targetting high-level data.
tests/valid/Mercury.options:
tests/valid/Mmakefile:
tests/valid/lco_term.m:
Add a test case.
diff --git a/compiler/handle_options.m b/compiler/handle_options.m
index 8ce66e7..a34295f 100644
--- a/compiler/handle_options.m
+++ b/compiler/handle_options.m
@@ -1478,11 +1478,6 @@ postprocess_options_2(OptionTable0, Target, GC_Method, TagsMethod0,
ProfileDeep = yes
),
- % --optimize-constructor-last-call is currently not compatible with
- % --highlevel-data.
- option_implies(highlevel_data, optimize_constructor_last_call,
- bool(no), !Globals),
-
% --no-reorder-conj implies --no-deforestation,
% --no-constraint-propagation and --no-local-constraint-propagation.
option_neg_implies(reorder_conj, deforestation, bool(no), !Globals),
@@ -1999,8 +1994,7 @@ postprocess_options_2(OptionTable0, Target, GC_Method, TagsMethod0,
BackendForeignLanguages = ["c"]
;
Target = target_java,
- BackendForeignLanguages = ["java"],
- set_option(optimize_constructor_last_call, bool(no), !Globals)
+ BackendForeignLanguages = ["java"]
;
Target = target_x86_64,
BackendForeignLanguages = ["c"]
diff --git a/compiler/lco.m b/compiler/lco.m
index 5dad6f9..846314f 100644
--- a/compiler/lco.m
+++ b/compiler/lco.m
@@ -8,6 +8,7 @@
%
% File: lco.m.
% Author: zs.
+% Modifications by wangp.
%
% Transform predicates with calls that are tail recursive modulo construction
% where (1) all recursive calls have the same args participating in the "modulo
@@ -34,8 +35,13 @@
% C <= [H | NT]
% )
%
+%-----------------------------------------------------------------------------%
+%
+% TRANSFORMATION FOR LOW-LEVEL DATA
+%
% Concrete example of what the original predicate and its return-via-memory
-% variant should look like for append:
+% variant should look like for append, in grades for which it is possible to
+% take the address of a field:
%
% app(list(T)::in, list(T)::in, list(T)::out)
% app(A, B, C) :-
@@ -44,7 +50,7 @@
% C := B
% ;
% A => [H | T],
-% C <= [H | _NT] capture &HT in AddrHT
+% C <= [H | _HT] capture &HT in AddrHT
% app'(T, B, AddrHT)
% )
%
@@ -56,7 +62,7 @@
% store_at_ref(AddrC, C)
% ;
% A => [H | T],
-% C <= [H | _NT] capture &HT in AddrHT
+% C <= [H | _HT] capture &HT in AddrHT
% store_at_ref(AddrC, C)
% app'(T, B, AddrHT)
% )
@@ -101,6 +107,49 @@
% p'(In1, ... InN, Addr1, Out2... OutM)
%
%-----------------------------------------------------------------------------%
+%
+% TRANSFORMATION FOR HIGH-LEVEL DATA
+%
+% In grades where it is impossible to take the address of a field (we assume
+% this is so when using --highlevel-data), the transformed procedures are
+% passed partially instantiated cells, whose holes need to be filled.
+% The append example looks like:
+%
+% app(list(T)::in, list(T)::in, list(T)::out)
+% app(A, B, C) :-
+% (
+% A == [],
+% C := B
+% ;
+% A => [H | T],
+% C <= [H | _], % with hole
+% app'(T, B, C)
+% )
+%
+% app'(list(T)::in, list(T)::in, T::in(bound('[|]'(ground, free))))
+% app'(A, B, C) :-
+% (
+% A == [],
+% C => [_ | []] % fill in hole
+% ;
+% A => [H | T],
+% C <= [H | _HT], % bind C to AddrC
+% app'(T, B, AddrC)
+% )
+%
+% The differences are:
+%
+% 1 The output arguments become partially instantiated input arguments
+% instead of store_at_ref_type(T) arguments.
+%
+% 2 The holes in the output arguments are filled in with unifications
+% instead of a store_at_ref builtin.
+%
+% 3 Variant procedures need to know the functor and position of the argument in
+% the partially instantiated structures, so many more variants could be
+% produced. The number of variants is capped.
+%
+%-----------------------------------------------------------------------------%
:- module transform_hlds.lco.
:- interface.
@@ -116,6 +165,7 @@
:- import_module check_hlds.inst_match.
:- import_module check_hlds.mode_util.
+:- import_module check_hlds.type_util.
:- import_module hlds.arg_info.
:- import_module hlds.goal_util.
:- import_module hlds.hlds_code_util.
@@ -131,6 +181,7 @@
:- import_module mdbcomp.prim_data.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_mode.
+:- import_module parse_tree.prog_util.
:- import_module transform_hlds.dependency_graph.
:- import_module assoc_list.
@@ -139,6 +190,7 @@
:- import_module int.
:- import_module list.
:- import_module map.
+:- import_module multi_map.
:- import_module maybe.
:- import_module pair.
:- import_module set.
@@ -153,13 +205,31 @@
:- type variant_id
---> variant_id(
- list(int), % Positions of output arguments returned in
- % memory.
+ list(variant_arg), % The output arguments returned in memory.
pred_proc_id, % The id of the variant.
string % The name of the variant predicate.
).
-:- type variant_map == map(pred_proc_id, variant_id).
+:- type variant_arg
+ ---> variant_arg(
+ % Position of the output argument.
+ va_pos :: int,
+
+ % For low-level data this is `no'.
+ % For high-level data this is `yes(FieldId)' where FieldId
+ % indicates the functor that the argument will be bound to, and
+ % the argument of that functor which is to be filled.
+ va_field :: maybe(field_id)
+ ).
+
+:- type field_id
+ ---> field_id(
+ fi_type :: mer_type,
+ fi_cons_id :: cons_id,
+ fi_arg :: int
+ ).
+
+:- type variant_map == multi_map(pred_proc_id, variant_id).
:- type permitted
---> permitted
@@ -172,6 +242,7 @@
:- type lco_info
---> lco_info(
lco_module_info :: module_info,
+ lco_highlevel_data :: bool,
lco_cur_scc_variants :: variant_map,
lco_var_set :: prog_varset,
lco_var_types :: vartypes,
@@ -190,6 +261,14 @@
lci_cur_proc_detism :: determinism
).
+:- type var_to_target == assoc_list(prog_var, store_target).
+
+:- type store_target
+ ---> store_target(
+ prog_var,
+ maybe(field_id)
+ ).
+
%-----------------------------------------------------------------------------%
lco_modulo_constructors(!ModuleInfo) :-
@@ -205,7 +284,7 @@ lco_scc(SCC, !VariantMap, !ModuleInfo) :-
list.foldl4(lco_proc(!.VariantMap, SCC), SCC, !ModuleInfo,
map.init, CurSCCVariantMap, map.init, CurSCCUpdateMap,
permitted, Permitted),
- map.to_assoc_list(CurSCCVariantMap, CurSCCVariants),
+ multi_map.to_flat_assoc_list(CurSCCVariantMap, CurSCCVariants),
map.to_assoc_list(CurSCCUpdateMap, CurSCCUpdates),
(
Permitted = permitted,
@@ -237,14 +316,14 @@ process_proc_update(PredProcId - NewProcInfo, !ModuleInfo) :-
module_info::in, module_info::out) is det.
process_proc_variant(PredProcId - VariantId, !ModuleInfo) :-
- VariantId = variant_id(AddrOutArgPosns, VariantPredProcId, VariantName),
+ VariantId = variant_id(AddrOutArgs, VariantPredProcId, VariantName),
VariantPredProcId = proc(VariantPredId, VariantProcId),
PredProcId = proc(PredId, ProcId),
module_info_pred_proc_info(!.ModuleInfo, PredId, ProcId,
_PredInfo, ProcInfo),
- transform_variant_proc(!.ModuleInfo, AddrOutArgPosns,
- ProcInfo, VariantProcInfo),
+ transform_variant_proc(AddrOutArgs, ProcInfo, VariantProcInfo,
+ !ModuleInfo),
proc_info_get_headvars(VariantProcInfo, HeadVars),
proc_info_get_vartypes(VariantProcInfo, VarTypes),
@@ -263,6 +342,7 @@ process_proc_variant(PredProcId - VariantId, !ModuleInfo) :-
!VariantPredInfo),
pred_info_get_origin(!.VariantPredInfo, Origin0),
+ AddrOutArgPosns = list.map(va_pos, AddrOutArgs),
Transform = transform_return_via_ptr(ProcId, AddrOutArgPosns),
Origin = origin_transformed(Transform, Origin0, PredId),
pred_info_set_origin(Origin, !VariantPredInfo),
@@ -275,6 +355,8 @@ process_proc_variant(PredProcId - VariantId, !ModuleInfo) :-
module_info_set_preds(!.PredTable, !ModuleInfo)
).
+:- func va_pos(variant_arg) = int.
+
%-----------------------------------------------------------------------------%
:- pred lco_proc(variant_map::in, list(pred_proc_id)::in,
@@ -312,12 +394,14 @@ lco_proc(LowerSCCVariants, SCC, CurProc, !ModuleInfo, !CurSCCVariants,
proc_info_get_inferred_determinism(ProcInfo0, CurProcDetism),
ConstInfo = lco_const_info(LowerSCCVariants, list_to_set(SCC),
CurProc, PredInfo, ProcInfo0, OutputHeadVars, CurProcDetism),
- Info0 = lco_info(!.ModuleInfo, !.CurSCCVariants,
+ module_info_get_globals(!.ModuleInfo, Globals),
+ globals.lookup_bool_option(Globals, highlevel_data, HighLevelData),
+ Info0 = lco_info(!.ModuleInfo, HighLevelData, !.CurSCCVariants,
VarSet0, VarTypes0, permitted, not_changed),
proc_info_get_goal(ProcInfo0, Goal0),
lco_in_goal(Goal0, Goal, Info0, Info, ConstInfo),
- Info = lco_info(!:ModuleInfo, !:CurSCCVariants, VarSet, VarTypes,
- !:Permitted, Changed),
+ Info = lco_info(!:ModuleInfo, _, !:CurSCCVariants, VarSet,
+ VarTypes, !:Permitted, Changed),
(
!.Permitted = permitted,
Changed = changed
@@ -530,10 +614,15 @@ lco_in_conj([RevGoal | RevGoals], !.Unifies, !.UnifyInputVars, MaybeGoals,
% The variants we create return each output in only one place in
% memory.
all_true(occurs_once(!.UnifyInputVars), MismatchedCallArgs),
- ensure_variant_exists(PredId, ProcId, assoc_list.keys(Mismatches),
+
+ list.map_foldl2(update_construct(Subst), !.Unifies,
+ UpdatedUnifies, map.init, AddrFieldIds, !Info),
+ HighLevelData = !.Info ^ lco_highlevel_data,
+ make_variant_args(HighLevelData, AddrFieldIds, Mismatches,
+ VariantArgs),
+ ensure_variant_exists(PredId, ProcId, VariantArgs,
VariantPredProcId, SymName, VariantSymName, !Info)
->
- list.map(update_construct(Subst), !.Unifies, UpdatedUnifies),
proc_info_get_argmodes(CalleeProcInfo, CalleeModes),
update_call_args(ModuleInfo, VarTypes, CalleeModes, Args,
UpdatedCallOutArgs, UpdatedArgs),
@@ -639,13 +728,22 @@ find_args_to_pass_by_addr([CallArg - HeadArg | CallHeadArgs], ArgNum,
lco_info::in, lco_info::out) is det.
make_address_var(Var, AddrVar, !Info) :-
+ HighLevelData = !.Info ^ lco_highlevel_data,
VarSet0 = !.Info ^ lco_var_set,
VarTypes0 = !.Info ^ lco_var_types,
varset.lookup_name(VarSet0, Var, "SCCcallarg", Name),
AddrName = "Addr" ++ Name,
varset.new_named_var(VarSet0, AddrName, AddrVar, VarSet),
+ (
+ HighLevelData = no,
map.lookup(VarTypes0, Var, FieldType),
- map.det_insert(VarTypes0, AddrVar, make_ref_type(FieldType), VarTypes),
+ AddrVarType = make_ref_type(FieldType),
+ map.det_insert(VarTypes0, AddrVar, AddrVarType, VarTypes)
+ ;
+ HighLevelData = yes,
+ % We set the type later when it is more convenient.
+ VarTypes = VarTypes0
+ ),
!:Info = !.Info ^ lco_var_set := VarSet,
!:Info = !.Info ^ lco_var_types := VarTypes.
@@ -658,46 +756,81 @@ make_ref_type(FieldType) = PtrType :-
%-----------------------------------------------------------------------------%
-:- pred ensure_variant_exists(pred_id::in, proc_id::in, list(int)::in,
+:- pred make_variant_args(bool::in, map(prog_var, field_id)::in,
+ assoc_list(int, prog_var)::in, list(variant_arg)::out) is det.
+
+make_variant_args(HighLevelData, AddrVarFieldIds, Mismatches, VariantArgs) :-
+ (
+ HighLevelData = no,
+ MakeArg = (func(Pos - _Var) = variant_arg(Pos, no))
+ ;
+ HighLevelData = yes,
+ MakeArg = (func(Pos - Var) = variant_arg(Pos, yes(FieldId)) :-
+ map.lookup(AddrVarFieldIds, Var, FieldId)
+ )
+ ),
+ VariantArgs = list.map(MakeArg, Mismatches).
+
+:- pred ensure_variant_exists(pred_id::in, proc_id::in, list(variant_arg)::in,
pred_proc_id::out, sym_name::in, sym_name::out,
lco_info::in, lco_info::out) is semidet.
ensure_variant_exists(PredId, ProcId, AddrArgNums, VariantPredProcId,
SymName, VariantSymName, !Info) :-
+ PredProcId = proc(PredId, ProcId),
CurSCCVariants0 = !.Info ^ lco_cur_scc_variants,
- ( map.search(CurSCCVariants0, proc(PredId, ProcId), ExistingVariant) ->
- ExistingVariant = variant_id(ExistingAddrArgNums, VariantPredProcId,
- VariantName),
+ (
+ multi_map.search(CurSCCVariants0, PredProcId, ExistingVariants),
+ match_existing_variant(ExistingVariants, AddrArgNums, ExistingVariant)
+ ->
+ ExistingVariant = variant_id(_, VariantPredProcId, VariantName),
(
SymName = unqualified(_Name),
VariantSymName = unqualified(VariantName)
;
SymName = qualified(ModuleName, _Name),
VariantSymName = qualified(ModuleName, VariantName)
- ),
- AddrArgNums = ExistingAddrArgNums
+ )
;
ModuleInfo0 = !.Info ^ lco_module_info,
clone_pred_proc(PredId, ClonePredId, PredOrFunc,
ModuleInfo0, ModuleInfo),
VariantPredProcId = proc(ClonePredId, ProcId),
!:Info = !.Info ^ lco_module_info := ModuleInfo,
+ ( multi_map.search(CurSCCVariants0, PredProcId, ExistingVariants) ->
+ VariantNumber = list.length(ExistingVariants) + 1
+ ;
+ VariantNumber = 1
+ ),
+ VariantNumber =< max_variants_per_proc,
(
SymName = unqualified(Name),
- create_variant_name(PredOrFunc, AddrArgNums, Name, VariantName),
+ create_variant_name(PredOrFunc, VariantNumber, Name, VariantName),
VariantSymName = unqualified(VariantName)
;
SymName = qualified(ModuleName, Name),
- create_variant_name(PredOrFunc, AddrArgNums, Name, VariantName),
+ create_variant_name(PredOrFunc, VariantNumber, Name, VariantName),
VariantSymName = qualified(ModuleName, VariantName)
),
- NewVariant = variant_id(AddrArgNums, VariantPredProcId,
- VariantName),
- map.det_insert(CurSCCVariants0, proc(PredId, ProcId), NewVariant,
- CurSCCVariants),
+ NewVariant = variant_id(AddrArgNums, VariantPredProcId, VariantName),
+ multi_map.set(CurSCCVariants0, PredProcId, NewVariant, CurSCCVariants),
!:Info = !.Info ^ lco_cur_scc_variants := CurSCCVariants
).
+:- pred match_existing_variant(list(variant_id)::in, list(variant_arg)::in,
+ variant_id::out) is semidet.
+
+match_existing_variant([Variant0 | Variants], AddrArgNums, Variant) :-
+ ( Variant0 = variant_id(AddrArgNums, _, _) ->
+ Variant = Variant0
+ ;
+ match_existing_variant(Variants, AddrArgNums, Variant)
+ ).
+
+:- func max_variants_per_proc = int.
+
+max_variants_per_proc = 4.
+
:- pred clone_pred_proc(pred_id::in, pred_id::out, pred_or_func::out,
module_info::in, module_info::out) is det.
@@ -708,26 +841,26 @@ clone_pred_proc(PredId, ClonePredId, PredOrFunc, !ModuleInfo) :-
predicate_table_insert(PredInfo, ClonePredId, PredTable0, PredTable),
module_info_set_predicate_table(PredTable, !ModuleInfo).
-:- pred create_variant_name(pred_or_func::in, list(int)::in, string::in,
+:- pred create_variant_name(pred_or_func::in, int::in, string::in,
string::out) is det.
-create_variant_name(PredOrFunc, ArgPoss, OrigName, VariantName) :-
- list.map(int_to_string, ArgPoss, ArgPosStrs),
- ArgPosDesc = string.join_list("_", ArgPosStrs),
+create_variant_name(PredOrFunc, VariantNumber, OrigName, VariantName) :-
(
PredOrFunc = pf_function,
- VariantName = "LCMCfn_" ++ OrigName ++ "_" ++ ArgPosDesc
+ Prefix = "LCMCfn_"
;
PredOrFunc = pf_predicate,
- VariantName = "LCMCpr_" ++ OrigName ++ "_" ++ ArgPosDesc
- ).
+ Prefix = "LCMCpr_"
+ ),
+ VariantName = Prefix ++ OrigName ++ "_" ++ int_to_string(VariantNumber).
%-----------------------------------------------------------------------------%
-:- pred update_construct(map(prog_var, prog_var)::in,
- hlds_goal::in, hlds_goal::out) is det.
+:- pred update_construct(map(prog_var, prog_var)::in, hlds_goal::in,
+ hlds_goal::out, map(prog_var, field_id)::in, map(prog_var, field_id)::out,
+ lco_info::in, lco_info::out) is det.
-update_construct(Subst, Goal0, Goal) :-
+update_construct(Subst, Goal0, Goal, !AddrVarFieldIds, !Info) :-
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
(
GoalExpr0 = unify(LHS, RHS0, Mode, Unification0, UnifyContext),
@@ -740,9 +873,19 @@ update_construct(Subst, Goal0, Goal) :-
SubInfo0 = construct_sub_info(no, TermSizeSlot)
)
->
+ % For high-level data, we should not be using the `take_address_fields'
+ % feature in `construct_sub_info', but simply assign the new cell to
+ % each of the variables that takes its address. But as support for
+ % partial instantiation is incomplete, instmaps for the assignments are
+ % likely to be recomputed incorrectly.
+ HighLevelData = !.Info ^ lco_highlevel_data,
+ VarTypes0 = !.Info ^ lco_var_types,
+ map.lookup(VarTypes0, Var, VarType),
InstMapDelta0 = goal_info_get_instmap_delta(GoalInfo0),
- update_construct_args(Subst, 1, ArgVars, UpdatedArgVars, AddrFields,
- InstMapDelta0, InstMapDelta),
+ update_construct_args(Subst, HighLevelData, VarType, ConsId, 1,
+ ArgVars, UpdatedArgVars, AddrFields, InstMapDelta0, InstMapDelta,
+ !AddrVarFieldIds, VarTypes0, VarTypes),
+ !Info ^ lco_var_types := VarTypes,
(
AddrFields = [],
Goal = Goal0
@@ -768,6 +911,9 @@ update_construct(Subst, Goal0, Goal) :-
),
GoalExpr = unify(LHS, RHS, Mode, Unification, UnifyContext),
+ % For high-level data, there is a lie in this instmap_delta: the
+ % new cell is not yet ground, although it will become ground after
+ % the call that follows the construction.
goal_info_set_instmap_delta(InstMapDelta, GoalInfo0, GoalInfo),
Goal = hlds_goal(GoalExpr, GoalInfo)
)
@@ -775,24 +921,49 @@ update_construct(Subst, Goal0, Goal) :-
unexpected(this_file, "update_construct: not construct")
).
-:- pred update_construct_args(map(prog_var, prog_var)::in, int::in,
- list(prog_var)::in, list(prog_var)::out, list(int)::out,
- instmap_delta::in, instmap_delta::out) is det.
-
-update_construct_args(_, _, [], [], [], !InstMapDelta).
-update_construct_args(Subst, ArgNum, [OrigVar | OrigVars],
- [UpdatedVar | UpdatedVars], AddrArgs, !InstMapDelta) :-
- update_construct_args(Subst, ArgNum + 1, OrigVars, UpdatedVars,
- AddrArgsTail, !InstMapDelta),
+:- pred update_construct_args(map(prog_var, prog_var)::in, bool::in,
+ mer_type::in, cons_id::in, int::in, list(prog_var)::in,
+ list(prog_var)::out, list(int)::out, instmap_delta::in, instmap_delta::out,
+ map(prog_var, field_id)::in, map(prog_var, field_id)::out,
+ vartypes::in, vartypes::out) is det.
+
+update_construct_args(_, _, _, _, _, [], [], [],
+ !InstMapDelta, !AddrFieldIds, !VarTypes).
+update_construct_args(Subst, HighLevelData, VarType, ConsId, ArgNum,
+ [OrigVar | OrigVars], [UpdatedVar | UpdatedVars], AddrArgs,
+ !InstMapDelta, !AddrFieldIds, !VarTypes) :-
+ update_construct_args(Subst, HighLevelData, VarType, ConsId, ArgNum + 1,
+ OrigVars, UpdatedVars, AddrArgsTail, !InstMapDelta, !AddrFieldIds,
+ !VarTypes),
( map.search(Subst, OrigVar, AddrVar) ->
UpdatedVar = AddrVar,
- instmap_delta_set_var(AddrVar, ground(shared, none), !InstMapDelta),
+ (
+ HighLevelData = no,
+ FinalInst = ground_inst
+ ;
+ HighLevelData = yes,
+ BoundInst = bound_inst_with_free_arg(ConsId, ArgNum),
+ FinalInst = bound(shared, [BoundInst]),
+ % We didn't do this when we initially created the variable.
+ svmap.det_insert(AddrVar, VarType, !VarTypes)
+ ),
+ instmap_delta_set_var(AddrVar, FinalInst, !InstMapDelta),
+ svmap.det_insert(OrigVar, field_id(VarType, ConsId, ArgNum),
+ !AddrFieldIds),
AddrArgs = [ArgNum | AddrArgsTail]
;
UpdatedVar = OrigVar,
AddrArgs = AddrArgsTail
).
+:- func bound_inst_with_free_arg(cons_id, int) = bound_inst.
+
+bound_inst_with_free_arg(ConsId, FreeArg) = Inst :-
+ Arity = cons_id_arity(ConsId),
+ list.duplicate(Arity, ground_inst, ArgInsts0),
+ list.replace_nth_det(ArgInsts0, FreeArg, free_inst, ArgInsts),
+ Inst = bound_functor(ConsId, ArgInsts).
+
%-----------------------------------------------------------------------------%
:- pred acceptable_construct_mode(module_info::in, uni_mode::in) is semidet.
@@ -811,46 +982,59 @@ occurs_once(Bag, Var) :-
%-----------------------------------------------------------------------------%
-:- pred transform_variant_proc(module_info::in, list(int)::in,
- proc_info::in, proc_info::out) is det.
+:- pred transform_variant_proc(list(variant_arg)::in,
+ proc_info::in, proc_info::out, module_info::in, module_info::out) is det.
-transform_variant_proc(ModuleInfo, AddrOutArgPosns, ProcInfo,
- !:VariantProcInfo) :-
+transform_variant_proc(AddrOutArgs, ProcInfo, !:VariantProcInfo,
+ !ModuleInfo) :-
!:VariantProcInfo = ProcInfo,
proc_info_get_varset(ProcInfo, VarSet0),
proc_info_get_vartypes(ProcInfo, VarTypes0),
proc_info_get_headvars(ProcInfo, HeadVars0),
proc_info_get_argmodes(ProcInfo, ArgModes0),
make_addr_vars(HeadVars0, ArgModes0, HeadVars, ArgModes,
- AddrOutArgPosns, 1, ModuleInfo, VarToAddr,
+ AddrOutArgs, 1, !.ModuleInfo, VarToAddr,
VarSet0, VarSet, VarTypes0, VarTypes),
proc_info_set_headvars(HeadVars, !VariantProcInfo),
proc_info_set_argmodes(ArgModes, !VariantProcInfo),
proc_info_set_varset(VarSet, !VariantProcInfo),
proc_info_set_vartypes(VarTypes, !VariantProcInfo),
- proc_info_get_initial_instmap(ProcInfo, ModuleInfo, InstMap0),
+ proc_info_get_initial_instmap(ProcInfo, !.ModuleInfo, InstMap0),
proc_info_get_goal(ProcInfo, Goal0),
- transform_variant_goal(ModuleInfo, VarToAddr, InstMap0, Goal0, Goal, _),
+ transform_variant_goal(!.ModuleInfo, VarToAddr, InstMap0, Goal0, Goal,
+ _Changed, !VariantProcInfo),
proc_info_set_goal(Goal, !VariantProcInfo),
% We changed the scopes of the headvars we now return via pointers.
- requantify_proc_general(ordinary_nonlocals_no_lambda, !VariantProcInfo).
+ requantify_proc_general(ordinary_nonlocals_no_lambda, !VariantProcInfo),
+
+ % The high-level data transformation requires instmap deltas to be
+ % recomputed.
+ module_info_get_globals(!.ModuleInfo, Globals),
+ globals.lookup_bool_option(Globals, highlevel_data, HighLevelData),
+ (
+ HighLevelData = yes,
+ recompute_instmap_delta_proc(do_not_recompute_atomic_instmap_deltas,
+ !VariantProcInfo, !ModuleInfo)
+ ;
+ HighLevelData = no
+ ).
:- pred make_addr_vars(list(prog_var)::in, list(mer_mode)::in,
- list(prog_var)::out, list(mer_mode)::out, list(int)::in,
- int::in, module_info::in, assoc_list(prog_var)::out,
+ list(prog_var)::out, list(mer_mode)::out, list(variant_arg)::in,
+ int::in, module_info::in, var_to_target::out,
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out) is det.
-make_addr_vars([], [], [], [], AddrOutArgPosns, _, _, [],
+make_addr_vars([], [], [], [], AddrOutArgs, _, _, [],
!VarSet, !VarTypes) :-
- expect(unify(AddrOutArgPosns, []), this_file,
- "make_addr_vars: AddrOutArgPosns != []").
+ expect(unify(AddrOutArgs, []), this_file,
+ "make_addr_vars: AddrOutArgs != []").
make_addr_vars([], [_ | _], _, _, _, _, _, _, !VarSet, !VarTypes) :-
unexpected(this_file, "make_addr_vars: mismatched lists").
make_addr_vars([_ | _], [], _, _, _, _, _, _, !VarSet, !VarTypes) :-
unexpected(this_file, "make_addr_vars: mismatched lists").
make_addr_vars([HeadVar0 | HeadVars0], [Mode0 | Modes0],
- [HeadVar | HeadVars], [Mode | Modes], !.AddrOutArgPosns,
+ [HeadVar | HeadVars], [Mode | Modes], !.AddrOutArgs,
NextOutArgNum, ModuleInfo, VarToAddr, !VarSet, !VarTypes) :-
map.lookup(!.VarTypes, HeadVar0, HeadVarType),
mode_to_arg_mode(ModuleInfo, Mode0, HeadVarType, ArgMode),
@@ -858,27 +1042,45 @@ make_addr_vars([HeadVar0 | HeadVars0], [Mode0 | Modes0],
ArgMode = top_in,
HeadVar = HeadVar0,
Mode = Mode0,
- make_addr_vars(HeadVars0, Modes0, HeadVars, Modes, !.AddrOutArgPosns,
+ make_addr_vars(HeadVars0, Modes0, HeadVars, Modes, !.AddrOutArgs,
NextOutArgNum, ModuleInfo, VarToAddr, !VarSet, !VarTypes)
;
ArgMode = top_out,
- ( !.AddrOutArgPosns = [NextOutArgNum | !:AddrOutArgPosns] ->
+ (
+ !.AddrOutArgs = [AddrOutArg | !:AddrOutArgs],
+ AddrOutArg = variant_arg(NextOutArgNum, MaybeFieldId)
+ ->
varset.lookup_name(!.VarSet, HeadVar0, Name),
AddrName = "AddrOf" ++ Name,
svvarset.new_named_var(AddrName, AddrVar, !VarSet),
+ HeadVar = AddrVar,
map.lookup(!.VarTypes, HeadVar0, OldType),
+ (
+ MaybeFieldId = no,
+ % For low-level data we replace the output argument with a
+ % store_at_ref_type(T) input argument.
svmap.det_insert(AddrVar, make_ref_type(OldType), !VarTypes),
- HeadVar = AddrVar,
- Mode = in_mode,
+ Mode = in_mode
+ ;
+ MaybeFieldId = yes(field_id(AddrVarType, ConsId, ArgNum)),
+ % For high-level data we replace the output argument with a
+ % partially instantiated structure. The structure has one
+ % argument left unfilled.
+ svmap.det_insert(AddrVar, AddrVarType, !VarTypes),
+ BoundInst = bound_inst_with_free_arg(ConsId, ArgNum),
+ InitialInst = bound(shared, [BoundInst]),
+ Mode = (InitialInst -> ground_inst)
+ ),
make_addr_vars(HeadVars0, Modes0, HeadVars, Modes,
- !.AddrOutArgPosns, NextOutArgNum + 1, ModuleInfo,
+ !.AddrOutArgs, NextOutArgNum + 1, ModuleInfo,
VarToAddrTail, !VarSet, !VarTypes),
- VarToAddr = [HeadVar0 - AddrVar | VarToAddrTail]
+ VarToAddrHead = HeadVar0 - store_target(AddrVar, MaybeFieldId),
+ VarToAddr = [VarToAddrHead | VarToAddrTail]
;
HeadVar = HeadVar0,
Mode = Mode0,
make_addr_vars(HeadVars0, Modes0, HeadVars, Modes,
- !.AddrOutArgPosns, NextOutArgNum + 1, ModuleInfo,
+ !.AddrOutArgs, NextOutArgNum + 1, ModuleInfo,
VarToAddr, !VarSet, !VarTypes)
)
;
@@ -886,18 +1088,19 @@ make_addr_vars([HeadVar0 | HeadVars0], [Mode0 | Modes0],
unexpected(this_file, "make_addr_vars: top_unused")
).
-:- pred transform_variant_goal(module_info::in, assoc_list(prog_var)::in,
- instmap::in, hlds_goal::in, hlds_goal::out, bool::out) is det.
+:- pred transform_variant_goal(module_info::in, var_to_target::in,
+ instmap::in, hlds_goal::in, hlds_goal::out, bool::out,
+ proc_info::in, proc_info::out) is det.
transform_variant_goal(ModuleInfo, VarToAddr, InstMap0, Goal0, Goal,
- Changed) :-
+ Changed, !ProcInfo) :-
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
(
GoalExpr0 = conj(ConjType, Goals0),
(
ConjType = plain_conj,
transform_variant_conj(ModuleInfo, VarToAddr, InstMap0,
- Goals0, Goals, Changed),
+ Goals0, Goals, Changed, !ProcInfo),
GoalExpr = conj(ConjType, Goals)
;
ConjType = parallel_conj,
@@ -905,23 +1108,25 @@ transform_variant_goal(ModuleInfo, VarToAddr, InstMap0, Goal0, Goal,
)
;
GoalExpr0 = disj(Goals0),
- list.map2(transform_variant_goal(ModuleInfo, VarToAddr, InstMap0),
- Goals0, Goals, DisjsChanged),
+ list.map2_foldl(
+ transform_variant_goal(ModuleInfo, VarToAddr, InstMap0),
+ Goals0, Goals, DisjsChanged, !ProcInfo),
Changed = bool.or_list(DisjsChanged),
GoalExpr = disj(Goals)
;
GoalExpr0 = switch(Var, CanFail, Cases0),
- list.map2(transform_variant_case(ModuleInfo, VarToAddr, InstMap0),
- Cases0, Cases, CasesChanged),
+ list.map2_foldl(
+ transform_variant_case(ModuleInfo, VarToAddr, InstMap0),
+ Cases0, Cases, CasesChanged, !ProcInfo),
Changed = bool.or_list(CasesChanged),
GoalExpr = switch(Var, CanFail, Cases)
;
GoalExpr0 = if_then_else(Vars, Cond, Then0, Else0),
update_instmap(Cond, InstMap0, InstMap1),
transform_variant_goal(ModuleInfo, VarToAddr, InstMap1, Then0, Then,
- ThenChanged),
+ ThenChanged, !ProcInfo),
transform_variant_goal(ModuleInfo, VarToAddr, InstMap0, Else0, Else,
- ElseChanged),
+ ElseChanged, !ProcInfo),
Changed = bool.or(ThenChanged, ElseChanged),
GoalExpr = if_then_else(Vars, Cond, Then, Else)
;
@@ -931,7 +1136,7 @@ transform_variant_goal(ModuleInfo, VarToAddr, InstMap0, Goal0, Goal,
Changed = no
;
transform_variant_goal(ModuleInfo, VarToAddr, InstMap0,
- SubGoal0, SubGoal, Changed),
+ SubGoal0, SubGoal, Changed, !ProcInfo),
GoalExpr = scope(Reason, SubGoal)
)
;
@@ -941,20 +1146,20 @@ transform_variant_goal(ModuleInfo, VarToAddr, InstMap0, Goal0, Goal,
;
GoalExpr0 = generic_call(_, _, _, _),
transform_variant_atomic_goal(ModuleInfo, VarToAddr, InstMap0,
- GoalInfo0, GoalExpr0, GoalExpr, Changed)
+ GoalInfo0, GoalExpr0, GoalExpr, Changed, !ProcInfo)
;
GoalExpr0 = plain_call(_, _, _, _, _, _),
% XXX We could handle recursive calls better.
transform_variant_atomic_goal(ModuleInfo, VarToAddr, InstMap0,
- GoalInfo0, GoalExpr0, GoalExpr, Changed)
+ GoalInfo0, GoalExpr0, GoalExpr, Changed, !ProcInfo)
;
GoalExpr0 = unify(_, _, _, _, _),
transform_variant_atomic_goal(ModuleInfo, VarToAddr, InstMap0,
- GoalInfo0, GoalExpr0, GoalExpr, Changed)
+ GoalInfo0, GoalExpr0, GoalExpr, Changed, !ProcInfo)
;
GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _),
transform_variant_atomic_goal(ModuleInfo, VarToAddr, InstMap0,
- GoalInfo0, GoalExpr0, GoalExpr, Changed)
+ GoalInfo0, GoalExpr0, GoalExpr, Changed, !ProcInfo)
;
GoalExpr0 = shorthand(_),
% These should have been expanded out by now.
@@ -969,17 +1174,18 @@ transform_variant_goal(ModuleInfo, VarToAddr, InstMap0, Goal0, Goal,
Goal = Goal0
).
-:- pred transform_variant_conj(module_info::in, assoc_list(prog_var)::in,
- instmap::in, list(hlds_goal)::in, list(hlds_goal)::out, bool::out) is det.
+:- pred transform_variant_conj(module_info::in, var_to_target::in,
+ instmap::in, list(hlds_goal)::in, list(hlds_goal)::out, bool::out,
+ proc_info::in, proc_info::out) is det.
-transform_variant_conj(_, _, _, [], [], no).
+transform_variant_conj(_, _, _, [], [], no, !ProcInfo).
transform_variant_conj(ModuleInfo, VarToAddr, InstMap0, [Goal0 | Goals0],
- Conj, Changed) :-
+ Conj, Changed, !ProcInfo) :-
transform_variant_goal(ModuleInfo, VarToAddr, InstMap0, Goal0, Goal,
- HeadChanged),
+ HeadChanged, !ProcInfo),
update_instmap(Goal0, InstMap0, InstMap1),
transform_variant_conj(ModuleInfo, VarToAddr, InstMap1, Goals0, Goals,
- TailChanged),
+ TailChanged, !ProcInfo),
Changed = bool.or(HeadChanged, TailChanged),
( Goal = hlds_goal(conj(plain_conj, SubConj), _) ->
Conj = SubConj ++ Goals
@@ -987,22 +1193,23 @@ transform_variant_conj(ModuleInfo, VarToAddr, InstMap0, [Goal0 | Goals0],
Conj = [Goal | Goals]
).
-:- pred transform_variant_case(module_info::in, assoc_list(prog_var)::in,
- instmap::in, case::in, case::out, bool::out) is det.
+:- pred transform_variant_case(module_info::in, var_to_target::in, instmap::in,
+ case::in, case::out, bool::out, proc_info::in, proc_info::out) is det.
transform_variant_case(ModuleInfo, VarToAddr, InstMap0, Case0, Case,
- Changed) :-
+ Changed, !ProcInfo) :-
Case0 = case(MainConsId, OtherConsIds, Goal0),
transform_variant_goal(ModuleInfo, VarToAddr, InstMap0, Goal0, Goal,
- Changed),
+ Changed, !ProcInfo),
Case = case(MainConsId, OtherConsIds, Goal).
:- pred transform_variant_atomic_goal(module_info::in,
- assoc_list(prog_var)::in, instmap::in, hlds_goal_info::in,
- hlds_goal_expr::in, hlds_goal_expr::out, bool::out) is det.
+ var_to_target::in, instmap::in, hlds_goal_info::in,
+ hlds_goal_expr::in, hlds_goal_expr::out, bool::out,
+ proc_info::in, proc_info::out) is det.
transform_variant_atomic_goal(ModuleInfo, VarToAddr, InstMap0, GoalInfo,
- GoalExpr0, GoalExpr, Changed) :-
+ GoalExpr0, GoalExpr, Changed, !ProcInfo) :-
update_instmap(hlds_goal(GoalExpr0, GoalInfo), InstMap0, InstMap1),
list.filter(is_grounding(ModuleInfo, InstMap0, InstMap1), VarToAddr,
GroundingVarToAddr),
@@ -1012,28 +1219,99 @@ transform_variant_atomic_goal(ModuleInfo, VarToAddr, InstMap0, GoalInfo,
Changed = no
;
GroundingVarToAddr = [_ | _],
- list.map(make_store_goal(ModuleInfo), GroundingVarToAddr, StoreGoals),
+ list.map_foldl(make_store_goal(ModuleInfo, InstMap1),
+ GroundingVarToAddr, StoreGoals, !ProcInfo),
GoalExpr = conj(plain_conj,
[hlds_goal(GoalExpr0, GoalInfo) | StoreGoals]),
Changed = yes
).
:- pred is_grounding(module_info::in, instmap::in, instmap::in,
- pair(prog_var)::in) is semidet.
+ pair(prog_var, store_target)::in) is semidet.
-is_grounding(ModuleInfo, InstMap0, InstMap, Var - _AddrVar) :-
+is_grounding(ModuleInfo, InstMap0, InstMap, Var - _StoreTarget) :-
instmap_lookup_var(InstMap0, Var, Inst0),
not inst_is_ground(ModuleInfo, Inst0),
instmap_lookup_var(InstMap, Var, Inst),
inst_is_ground(ModuleInfo, Inst).
-:- pred make_store_goal(module_info::in, pair(prog_var)::in,
- hlds_goal::out) is det.
+:- pred make_store_goal(module_info::in, instmap::in,
+ pair(prog_var, store_target)::in, hlds_goal::out,
+ proc_info::in, proc_info::out) is det.
+
+make_store_goal(ModuleInfo, InstMap, GroundVar - StoreTarget, Goal,
+ !ProcInfo) :-
+ StoreTarget = store_target(AddrVar, MaybeFieldId),
+ (
+ % Low-level data.
+ MaybeFieldId = no,
+ generate_simple_call(mercury_private_builtin_module,
+ "store_at_ref_impure",
+ pf_predicate, only_mode, detism_det, purity_impure,
+ [AddrVar, GroundVar],
+ [], instmap_delta_bind_vars([]), ModuleInfo, term.context_init,
+ Goal)
+ ;
+ % High-level data.
+ MaybeFieldId = yes(field_id(AddrVarType, ConsId, ArgNum)),
+ get_cons_id_arg_types(ModuleInfo, AddrVarType, ConsId, ArgTypes),
+ make_unification_args(GroundVar, ArgNum, 1, ArgTypes,
+ ArgVars, ArgModes, !ProcInfo),
+
+ IsExistConstr = no,
+ RHS = rhs_functor(ConsId, IsExistConstr, ArgVars),
+
+ instmap_lookup_var(InstMap, AddrVar, AddrVarInst0),
+ inst_expand(ModuleInfo, AddrVarInst0, AddrVarInst),
+ UniMode = (AddrVarInst -> ground_inst) - (ground_inst -> ground_inst),
+
+ Unification = deconstruct(AddrVar, ConsId, ArgVars, ArgModes,
+ cannot_fail, cannot_cgc),
+ UnifyContext = unify_context(umc_implicit("lcmc"), []),
+
+ GoalExpr = unify(AddrVar, RHS, UniMode, Unification, UnifyContext),
-make_store_goal(ModuleInfo, Var - AddrVar, Goal) :-
- generate_simple_call(mercury_private_builtin_module, "store_at_ref_impure",
- pf_predicate, only_mode, detism_det, purity_impure, [AddrVar, Var],
- [], instmap_delta_bind_vars([]), ModuleInfo, term.context_init, Goal).
+ goal_info_init(GoalInfo0),
+ goal_info_set_determinism(detism_det, GoalInfo0, GoalInfo1),
+ goal_info_set_instmap_delta(instmap_delta_bind_var(AddrVar),
+ GoalInfo1, GoalInfo),
+
+ Goal = hlds_goal(GoalExpr, GoalInfo)
+ ).
+
+:- pred make_unification_args(prog_var::in, int::in, int::in,
+ list(mer_type)::in, list(prog_var)::out, list(uni_mode)::out,
+ proc_info::in, proc_info::out) is det.
+
+make_unification_args(GroundVar, TargetArgNum, CurArgNum, ArgTypes,
+ ArgVars, ArgModes, !ProcInfo) :-
+ (
+ ArgTypes = [],
+ ArgVars = [],
+ ArgModes = []
+ ;
+ ArgTypes = [ArgType | ArgTypesTail],
+ make_unification_args(GroundVar, TargetArgNum, CurArgNum + 1,
+ ArgTypesTail, ArgVarsTail, ArgModesTail, !ProcInfo),
+ make_unification_arg(GroundVar, TargetArgNum, CurArgNum,
+ ArgType, Var, UniMode, !ProcInfo),
+ ArgVars = [Var | ArgVarsTail],
+ ArgModes = [UniMode | ArgModesTail]
+ ).
+
+:- pred make_unification_arg(prog_var::in, int::in, int::in, mer_type::in,
+ prog_var::out, uni_mode::out, proc_info::in, proc_info::out) is det.
+
+make_unification_arg(GroundVar, TargetArgNum, CurArgNum, ArgType,
+ Var, UniMode, !ProcInfo) :-
+ ( CurArgNum = TargetArgNum ->
+ Var = GroundVar,
+ UniMode = ((free_inst - ground_inst) -> (ground_inst - ground_inst))
+ ;
+ % Bind other arguments to fresh variables.
+ proc_info_create_var_from_type(ArgType, no, Var, !ProcInfo),
+ UniMode = ((ground_inst - free_inst) -> (ground_inst - ground_inst))
+ ).
%-----------------------------------------------------------------------------%
diff --git a/compiler/ml_unify_gen.m b/compiler/ml_unify_gen.m
index 40795f6..ed02148 100644
--- a/compiler/ml_unify_gen.m
+++ b/compiler/ml_unify_gen.m
@@ -893,6 +893,12 @@ ml_gen_field_take_address_assigns([], _, _, _, _, _, []).
ml_gen_field_take_address_assigns([TakeAddrInfo | TakeAddrInfos],
CellLval, CellType, MaybeTag, Context, Info, [Assign | Assigns]) :-
TakeAddrInfo = take_addr_info(AddrVar, Offset, ConsArgType, FieldType),
+
+ ml_gen_info_get_module_info(Info, ModuleInfo),
+ module_info_get_globals(ModuleInfo, Globals),
+ globals.lookup_bool_option(Globals, highlevel_data, HighLevelData),
+ (
+ HighLevelData = no,
% XXX
% I am not sure that the types specified here are always the right ones,
% particularly in cases where the field whose address we are taking has
@@ -904,7 +910,15 @@ ml_gen_field_take_address_assigns([TakeAddrInfo | TakeAddrInfos],
ml_field_offset(ml_const(mlconst_int(Offset))), FieldType, CellType)),
ml_gen_var(Info, AddrVar, AddrLval),
CastSourceRval = ml_unop(cast(mlds_ptr_type(ConsArgType)), SourceRval),
- Assign = ml_gen_assign(AddrLval, CastSourceRval, Context),
+ Assign = ml_gen_assign(AddrLval, CastSourceRval, Context)
+ ;
+ HighLevelData = yes,
+ % For high-level data lco.m uses a different transformation where we
+ % simply pass the base address of the cell. The transformation does not
+ % generate unifications.
+ ml_gen_var(Info, AddrVar, AddrLval),
+ Assign = ml_gen_assign(AddrLval, ml_lval(CellLval), Context)
+ ),
ml_gen_field_take_address_assigns(TakeAddrInfos, CellLval, CellType,
MaybeTag, Context, Info, Assigns).
diff --git a/tests/valid/Mercury.options b/tests/valid/Mercury.options
index cc56453..ab37092 100644
--- a/tests/valid/Mercury.options
+++ b/tests/valid/Mercury.options
@@ -97,6 +97,7 @@ MCFLAGS-intermod_user_sharing_2 = --intermodule-optimization
MCFLAGS-lambda_inference = --infer-all
MCFLAGS-livevals_seq = -O5 --opt-space
MCFLAGS-livevars_shallow = --grade none --trace shallow
+MCFLAGS-lco_term = --optimize-constructor-last-call
MCFLAGS-loop_inv_bug = --common-struct --loop-invariants
MCFLAGS-mc_bag = --prop-mode-constraints
MCFLAGS-mc_extra_nonlocals = --prop-mode-constraints
diff --git a/tests/valid/Mmakefile b/tests/valid/Mmakefile
index e654243..e0107ee 100644
--- a/tests/valid/Mmakefile
+++ b/tests/valid/Mmakefile
@@ -153,6 +153,7 @@ OTHER_PROGS= \
lambda_switch \
lambda_type \
lazy_list \
+ lco_term \
liveness_nonlocals \
livevals_seq \
long_name \
diff --git a/tests/valid/lco_term.m b/tests/valid/lco_term.m
new file mode 100644
index 0000000..ae4f7f3
--- /dev/null
+++ b/tests/valid/lco_term.m
@@ -0,0 +1,48 @@
+%-----------------------------------------------------------------------------%
+% An interim version of the compiler aborted when performing the LCMC
+% transformation for high-level data.
+
+:- module lco_term.
+:- interface.
+
+:- import_module list.
+:- import_module univ.
+
+%-----------------------------------------------------------------------------%
+
+:- type lco_term(T)
+ ---> functor(
+ const,
+ list(lco_term(T))
+ )
+ ; variable.
+
+:- type const
+ ---> atom(string)
+ ; integer(int).
+
+:- pred univ_to_term(univ::in, lco_term(_)::out) is det.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module deconstruct.
+
+%-----------------------------------------------------------------------------%
+
+univ_to_term(Univ, Term) :-
+ deconstruct(univ_value(Univ), canonicalize, FunctorString,
+ _FunctorArity, FunctorArgs),
+ univ_list_to_term_list(FunctorArgs, TermArgs),
+ Term = functor(atom(FunctorString), TermArgs).
+
+:- pred univ_list_to_term_list(list(univ)::in, list(lco_term(T))::out) is det.
+
+univ_list_to_term_list([], []).
+univ_list_to_term_list([Value|Values], [Term|Terms]) :-
+ univ_to_term(Value, Term),
+ univ_list_to_term_list(Values, Terms).
+
+%-----------------------------------------------------------------------------%
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to: mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions: mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------
More information about the reviews
mailing list