Files
mercury/compiler/instmap.m
Julien Fischer 459847a064 Move the univ, maybe, pair and unit types from std_util into their own
Estimated hours taken: 18
Branches: main

Move the univ, maybe, pair and unit types from std_util into their own
modules.  std_util still contains the general purpose higher-order programming
constructs.

library/std_util.m:
	Move univ, maybe, pair and unit (plus any other related types
	and procedures) into their own modules.

library/maybe.m:
	New module.  This contains the maybe and maybe_error types and
	the associated procedures.

library/pair.m:
	New module.  This contains the pair type and associated procedures.

library/unit.m:
	New module. This contains the types unit/0 and unit/1.

library/univ.m:
	New module. This contains the univ type and associated procedures.

library/library.m:
	Add the new modules.

library/private_builtin.m:
	Update the declaration of the type_ctor_info struct for univ.

runtime/mercury.h:
	Update the declaration for the type_ctor_info struct for univ.

runtime/mercury_mcpp.h:
runtime/mercury_hlc_types.h:
	Update the definition of MR_Univ.

runtime/mercury_init.h:
	Fix a comment: ML_type_name is now exported from type_desc.m.

compiler/mlds_to_il.m:
	Update the the name of the module that defines univs (which are
	handled specially by the il code generator.)

library/*.m:
compiler/*.m:
browser/*.m:
mdbcomp/*.m:
profiler/*.m:
deep_profiler/*.m:
	Conform to the above changes.  Import the new modules where they
	are needed; don't import std_util where it isn't needed.

	Fix formatting in lots of modules.  Delete duplicate module
	imports.

tests/*:
	Update the test suite to confrom to the above changes.
2006-03-29 08:09:58 +00:00

1173 lines
44 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 1996-2001, 2003-2006 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: instmap.m.
% Main author: bromage.
% This module contains code which implements the instmap and instmap_delta
% ADTs.
% An instmap stores information on what instantiation states a set of
% variables have. An instmap_delta stores information on how these
% instantiation states change across a goal.
%-----------------------------------------------------------------------------%
:- module hlds.instmap.
:- interface.
:- import_module check_hlds.mode_errors.
:- import_module check_hlds.mode_info.
:- import_module hlds.hlds_module.
:- import_module parse_tree.prog_data.
:- import_module assoc_list.
:- import_module bool.
:- import_module list.
:- import_module map.
:- import_module set.
:- type instmap.
:- type instmap_delta.
% Initialize an empty instmap.
%
:- pred init_reachable(instmap::out) is det.
% Initialize an empty unreachable instmap.
%
:- pred init_unreachable(instmap::out) is det.
% Initialize an empty reachable instmap_delta.
%
:- pred instmap_delta_init_reachable(instmap_delta::out) is det.
% Initialize an empty unreachable instmap_delta.
%
:- pred instmap_delta_init_unreachable(instmap_delta::out) is det.
% For any instmap InstMap, exactly one of
% is_reachable(InstMap) and
% is_unreachable(InstMap) holds.
% Is the instmap reachable?
%
:- pred is_reachable(instmap::in) is semidet.
% Is the instmap unreachable?
%
:- pred is_unreachable(instmap::in) is semidet.
% For any instmap InstMapDelta, exactly one of
% instmap_delta_is_reachable(InstMapDelta) and
% instmap_delta_is_unreachable(InstMapDelta) holds.
% Is the instmap_delta reachable?
%
:- pred instmap_delta_is_reachable(instmap_delta::in) is semidet.
% Is the instmap_delta unreachable?
%
:- pred instmap_delta_is_unreachable(instmap_delta::in) is semidet.
:- pred from_assoc_list(assoc_list(prog_var, mer_inst)::in,
instmap::out) is det.
:- pred instmap_delta_from_assoc_list(assoc_list(prog_var, mer_inst)::in,
instmap_delta::out) is det.
:- pred instmap_delta_from_mode_list(list(prog_var)::in, list(mer_mode)::in,
module_info::in, instmap_delta::out) is det.
%-----------------------------------------------------------------------------%
% Return the set of variables in an instmap.
%
:- pred vars(instmap::in, set(prog_var)::out) is det.
% Return the list of variables in an instmap.
%
:- pred vars_list(instmap::in, list(prog_var)::out) is det.
% Return the set of variables whose instantiations have
% changed (or our knowledge about them has changed) across
% an instmap_delta.
%
% This predicate shouldn't be used if you want your code to
% compile on the alias branch, use instmap_changed_vars instead.
%
:- pred instmap_delta_changed_vars(instmap_delta::in, set(prog_var)::out)
is det.
% instmap_changed_vars(IMA, IMB, MI, CV)
%
% Given an earlier instmap, IMA, and a later instmap, IMB,
% determine what variables, CV, have had their instantiatedness
% information changed.
%
% This predicate is meant to be equivalent to
% instmap_delta_changed_vars, where the instmap_delta is simply
% the one to take IMA to IMB. However this predicate should
% transform more easily to the alias branch.
%
:- pred instmap_changed_vars(instmap::in, instmap::in, vartypes::in,
module_info::in, set(prog_var)::out) is det.
%-----------------------------------------------------------------------------%
% Given an instmap and a variable, determine the inst of that variable.
%
:- pred lookup_var(instmap::in, prog_var::in, mer_inst::out) is det.
% Given an instmap_delta and a variable, determine the new inst
% of that variable (if any).
%
:- pred instmap_delta_search_var(instmap_delta::in, prog_var::in,
mer_inst::out) is semidet.
% Given an instmap and a list of variables, return a list
% containing the insts of those variable.
%
:- pred lookup_vars(list(prog_var)::in, instmap::in,
list(mer_inst)::out) is det.
% Insert an entry into an instmap_delta. Note that you cannot call
% instmap_delta_insert for a variable already present.
%
:- pred instmap_delta_insert(prog_var::in, mer_inst::in,
instmap_delta::in, instmap_delta::out) is det.
% Set an entry in an instmap.
%
:- pred set(prog_var::in, mer_inst::in, instmap::in, instmap::out)
is det.
% Set multiple entries in an instmap.
%
:- pred set_vars(list(prog_var)::in, list(mer_inst)::in,
instmap::in, instmap::out) is det.
:- pred instmap_delta_set(prog_var::in, mer_inst::in,
instmap_delta::in, instmap_delta::out) is det.
% Bind a variable in an instmap to a functor at the beginning
% of a case in a switch. Aborts on compiler generated cons_ids.
%
:- pred instmap_delta_bind_var_to_functor(prog_var::in, mer_type::in,
cons_id::in, instmap::in, instmap_delta::in, instmap_delta::out,
module_info::in, module_info::out) is det.
:- pred bind_var_to_functor(prog_var::in, mer_type::in, cons_id::in,
instmap::in, instmap::out, module_info::in, module_info::out) is det.
% Update the given instmap to include the initial insts of the
% lambda variables.
%
:- pred pre_lambda_update(module_info::in, list(prog_var)::in,
list(mer_mode)::in, instmap::in, instmap::out) is det.
%-----------------------------------------------------------------------------%
:- type overlay_how
---> large_base
; large_overlay
; test_size.
% Given two instmaps and a set of variables, compute an instmap delta
% which records the change in the instantiation state of those variables.
%
:- pred compute_instmap_delta(instmap::in, instmap::in, set(prog_var)::in,
instmap_delta::out) is det.
% Given an instmap and an instmap_delta, overlay the entries in the
% instmap_delta on top of those in the instmap to produce a new instmap.
%
:- pred apply_instmap_delta(instmap::in, instmap_delta::in,
instmap::out) is det.
% Given two instmap_deltas, overlay the entries in the second instmap_delta
% on top of those in the first to produce a new instmap_delta.
%
:- pred instmap_delta_apply_instmap_delta(instmap_delta::in, instmap_delta::in,
overlay_how::in, instmap_delta::out) is det.
% instmap_merge(NonLocalVars, InstMaps, MergeContext, !ModeInfo):
%
% Merge the `InstMaps' resulting from different branches of a disjunction
% or if-then-else, and update the instantiatedness of all the nonlocal
% variables, checking that it is the same for every branch.
%
:- pred instmap_merge(set(prog_var)::in, list(instmap)::in, merge_context::in,
mode_info::in, mode_info::out) is det.
% instmap_unify(NonLocalVars, InstMapNonlocalvarPairs, !ModeInfo):
%
% Unify the `InstMaps' in the list of pairs resulting from different
% branches of a parallel conjunction and update the instantiatedness
% of all the nonlocal variables. The variable locking that is done
% when modechecking the individual conjuncts ensures that variables
% have at most one producer.
%
:- pred instmap_unify(set(prog_var)::in,
assoc_list(instmap, set(prog_var))::in,
mode_info::in, mode_info::out) is det.
% instmap_restrict takes an instmap and a set of vars and returns
% an instmap with its domain restricted to those vars.
%
:- pred instmap_restrict(set(prog_var)::in, instmap::in, instmap::out) is det.
% instmap_delta_restrict takes an instmap and a set of vars and returns
% an instmap_delta with its domain restricted to those vars.
%
:- pred instmap_delta_restrict(set(prog_var)::in,
instmap_delta::in, instmap_delta::out) is det.
% instmap_delta_delete_vars takes an instmap_delta and a list of vars
% and returns an instmap_delta with those vars removed from its domain.
%
:- pred instmap_delta_delete_vars(list(prog_var)::in,
instmap_delta::in, instmap_delta::out) is det.
% `no_output_vars(Instmap, InstmapDelta, Vars, ModuleInfo)'
% is true if none of the vars in the set Vars could have become more
% instantiated when InstmapDelta is applied to Instmap.
%
:- pred no_output_vars(instmap::in, instmap_delta::in,
set(prog_var)::in, vartypes::in, module_info::in) is semidet.
% merge_instmap_delta(InitialInstMap, NonLocals,
% InstMapDeltaA, InstMapDeltaB, !ModuleInfo):
%
% Merge the instmap_deltas of different branches of an if-then-else,
% disj or switch.
%
:- pred merge_instmap_delta(instmap::in, set(prog_var)::in, vartypes::in,
instmap_delta::in, instmap_delta::in, instmap_delta::out,
module_info::in, module_info::out) is det.
% merge_instmap_deltas(Vars, InstMapDeltas, MergedInstMapDelta,
% !ModuleInfo):
%
% Takes a list of instmap deltas from the branches of an if-then-else,
% switch, or disj and merges them. This is used in situations
% where the bindings are known to be compatible.
%
:- pred merge_instmap_deltas(instmap::in, set(prog_var)::in, vartypes::in,
list(instmap_delta)::in, instmap_delta::out,
module_info::in, module_info::out) is det.
% unify_instmap_delta(InitialInstMap, NonLocals,
% InstMapDeltaA, InstMapDeltaB, !ModuleInfo)
%
% Unify the instmap_deltas of different branches of a parallel
% conjunction.
%
:- pred unify_instmap_delta(instmap::in, set(prog_var)::in, instmap_delta::in,
instmap_delta::in, instmap_delta::out,
module_info::in, module_info::out) is det.
%-----------------------------------------------------------------------------%
% `instmap_delta_apply_sub(Must, Sub, InstmapDelta0, InstmapDelta)'
% the variable substitution Sub to InstmapDelta0 to get the new
% instmap_delta InstmapDelta. If there is a variable in
% InstmapDelta0 which does not appear in Sub, it is ignored if
% Must is set to no, otherwise it is an error.
%
:- pred instmap_delta_apply_sub(bool::in, map(prog_var, prog_var)::in,
instmap_delta::in, instmap_delta::out) is det.
:- pred apply_sub(bool::in, map(prog_var, prog_var)::in,
instmap::in, instmap::out) is det.
%-----------------------------------------------------------------------------%
:- pred to_assoc_list(instmap::in,
assoc_list(prog_var, mer_inst)::out) is det.
:- pred instmap_delta_to_assoc_list(instmap_delta::in,
assoc_list(prog_var, mer_inst)::out) is det.
% Apply the specified procedure to all insts in an instmap_delta.
%
:- pred instmap_delta_map_foldl(
pred(prog_var, mer_inst, mer_inst, T, T)::in(pred(in, in, out, in, out)
is det),
instmap_delta::in, instmap_delta::out, T::in, T::out) is det.
:- pred var_is_ground_in_instmap(module_info::in, instmap::in, prog_var::in)
is semidet.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module check_hlds.inst_match.
:- import_module check_hlds.inst_util.
:- import_module check_hlds.mode_util.
:- import_module check_hlds.type_util.
:- import_module hlds.goal_util.
:- import_module hlds.hlds_data.
:- import_module libs.compiler_util.
:- import_module parse_tree.prog_data.
:- import_module int.
:- import_module maybe.
:- import_module pair.
:- import_module string.
:- import_module svmap.
:- import_module term.
%-----------------------------------------------------------------------------%
:- type instmap_delta == instmap.
:- type instmap
---> reachable(instmapping)
; unreachable.
:- type instmapping == map(prog_var, mer_inst).
%-----------------------------------------------------------------------------%
% Initialize an empty instmap and instmap_delta.
init_reachable(reachable(InstMapping)) :-
map.init(InstMapping).
init_unreachable(unreachable).
instmap_delta_init_reachable(reachable(InstMapping)) :-
map.init(InstMapping).
instmap_delta_init_unreachable(unreachable).
%-----------------------------------------------------------------------------%
is_reachable(reachable(_)).
is_unreachable(unreachable).
instmap_delta_is_reachable(reachable(_)).
instmap_delta_is_unreachable(unreachable).
%-----------------------------------------------------------------------------%
from_assoc_list(AL, reachable(Instmapping)) :-
map.from_assoc_list(AL, Instmapping).
instmap_delta_from_assoc_list(AL, reachable(Instmapping)) :-
map.from_assoc_list(AL, Instmapping).
instmap_delta_map_foldl(_, unreachable, unreachable, !T).
instmap_delta_map_foldl(P, reachable(Instmapping0), reachable(Instmapping),
!T) :-
map.map_foldl(P, Instmapping0, Instmapping, !T).
%-----------------------------------------------------------------------------%
instmap_delta_from_mode_list(Var, Modes, ModuleInfo, InstMapDelta) :-
instmap_delta_init_reachable(InstMapDelta0),
instmap_delta_from_mode_list_2(Var, Modes, ModuleInfo,
InstMapDelta0, InstMapDelta).
:- pred instmap_delta_from_mode_list_2(list(prog_var)::in, list(mer_mode)::in,
module_info::in, instmap_delta::in, instmap_delta::out) is det.
instmap_delta_from_mode_list_2([], [], _, !InstMapDelta).
instmap_delta_from_mode_list_2([], [_ | _], _, !InstMapDelta) :-
unexpected(this_file, "instmap_delta_from_mode_list_2").
instmap_delta_from_mode_list_2([_ | _], [], _, !InstMapDelta) :-
unexpected(this_file, "instmap_delta_from_mode_list_2").
instmap_delta_from_mode_list_2([Var | Vars], [Mode | Modes], ModuleInfo,
!InstMapDelta) :-
mode_get_insts(ModuleInfo, Mode, Inst1, Inst2),
( Inst1 = Inst2 ->
instmap_delta_from_mode_list_2(Vars, Modes, ModuleInfo, !InstMapDelta)
;
instmap_delta_set(Var, Inst2, !InstMapDelta),
instmap_delta_from_mode_list_2(Vars, Modes, ModuleInfo, !InstMapDelta)
).
%-----------------------------------------------------------------------------%
vars(Instmap, Vars) :-
vars_list(Instmap, VarsList),
set.list_to_set(VarsList, Vars).
vars_list(unreachable, []).
vars_list(reachable(InstMapping), VarsList) :-
map.keys(InstMapping, VarsList).
instmap_delta_changed_vars(unreachable, EmptySet) :-
set.init(EmptySet).
instmap_delta_changed_vars(reachable(InstMapping), ChangedVars) :-
map.keys(InstMapping, ChangedVarsList),
set.sorted_list_to_set(ChangedVarsList, ChangedVars).
%-----------------------------------------------------------------------------%
instmap_changed_vars(InstMapA, InstMapB, VarTypes, ModuleInfo, ChangedVars) :-
vars_list(InstMapB, VarsB),
changed_vars_2(VarsB, InstMapA, InstMapB, VarTypes, ModuleInfo,
ChangedVars).
:- pred changed_vars_2(prog_vars::in, instmap::in, instmap::in, vartypes::in,
module_info::in, set(prog_var)::out) is det.
changed_vars_2([], _InstMapA, _InstMapB, _Types, _ModuleInfo, ChangedVars) :-
set.init(ChangedVars).
changed_vars_2([VarB | VarBs], InstMapA, InstMapB, VarTypes, ModuleInfo,
ChangedVars) :-
changed_vars_2(VarBs, InstMapA, InstMapB, VarTypes, ModuleInfo,
ChangedVars0),
lookup_var(InstMapA, VarB, InitialInst),
lookup_var(InstMapB, VarB, FinalInst),
map.lookup(VarTypes, VarB, Type),
( inst_matches_final(InitialInst, FinalInst, Type, ModuleInfo) ->
ChangedVars = ChangedVars0
;
set.insert(ChangedVars0, VarB, ChangedVars)
).
%-----------------------------------------------------------------------------%
lookup_var(unreachable, _Var, not_reached).
lookup_var(reachable(InstMap), Var, Inst) :-
instmapping_lookup_var(InstMap, Var, Inst).
:- pred instmapping_lookup_var(instmapping::in, prog_var::in, mer_inst::out)
is det.
instmapping_lookup_var(InstMap, Var, Inst) :-
( map.search(InstMap, Var, VarInst) ->
Inst = VarInst
;
Inst = free
).
instmap_delta_search_var(unreachable, _, not_reached).
instmap_delta_search_var(reachable(InstMap), Var, Inst) :-
map.search(InstMap, Var, Inst).
lookup_vars([], _InstMap, []).
lookup_vars([Arg|Args], InstMap, [Inst|Insts]) :-
lookup_var(InstMap, Arg, Inst),
lookup_vars(Args, InstMap, Insts).
set(_Var, _Inst, unreachable, unreachable).
set(Var, Inst, reachable(InstMapping0), reachable(InstMapping)) :-
map.set(InstMapping0, Var, Inst, InstMapping).
set_vars([], [], !InstMap).
set_vars([V | Vs], [I | Is], !InstMap) :-
set(V, I, !InstMap),
set_vars(Vs, Is, !InstMap).
set_vars([_ | _], [], !InstMap) :-
unexpected(this_file, "set_vars: length mismatch (1)").
set_vars([], [_ | _], !InstMap) :-
unexpected(this_file, "set_vars: length mismatch (2)").
instmap_delta_set(_Var, _Inst, unreachable, unreachable).
instmap_delta_set(Var, Inst, reachable(InstMapping0), Instmap) :-
( Inst = not_reached ->
Instmap = unreachable
;
map.set(InstMapping0, Var, Inst, InstMapping),
Instmap = reachable(InstMapping)
).
instmap_delta_insert(_Var, _Inst, unreachable, unreachable).
instmap_delta_insert(Var, Inst, reachable(InstMapping0), Instmap) :-
( Inst = not_reached ->
Instmap = unreachable
;
map.det_insert(InstMapping0, Var, Inst, InstMapping),
Instmap = reachable(InstMapping)
).
%-----------------------------------------------------------------------------%
instmap_delta_bind_var_to_functor(Var, Type, ConsId, InstMap, !InstmapDelta,
!ModuleInfo) :-
(
!.InstmapDelta = unreachable
;
!.InstmapDelta = reachable(InstmappingDelta0),
% Get the initial inst from the InstMap
lookup_var(InstMap, Var, OldInst),
% Compute the new inst by taking the old inst, applying the instmap
% delta to it, and then unifying with bound(ConsId, ...).
( map.search(InstmappingDelta0, Var, NewInst0) ->
NewInst1 = NewInst0
;
NewInst1 = OldInst
),
bind_inst_to_functor(Type, ConsId, NewInst1, NewInst, !ModuleInfo),
% Add `Var :: OldInst -> NewInst' to the instmap delta.
( NewInst \= OldInst ->
instmap_delta_set(Var, NewInst, !InstmapDelta)
;
true
)
).
bind_var_to_functor(Var, Type, ConsId, !InstMap, !ModuleInfo) :-
lookup_var(!.InstMap, Var, Inst0),
bind_inst_to_functor(Type, ConsId, Inst0, Inst, !ModuleInfo),
set(Var, Inst, !InstMap).
:- pred bind_inst_to_functor(mer_type::in, cons_id::in,
mer_inst::in, mer_inst::out, module_info::in, module_info::out) is det.
bind_inst_to_functor(Type, ConsId, !Inst, !ModuleInfo) :-
Arity = cons_id_adjusted_arity(!.ModuleInfo, Type, ConsId),
list.duplicate(Arity, dead, ArgLives),
list.duplicate(Arity, free, ArgInsts),
(
abstractly_unify_inst_functor(dead, !.Inst, ConsId, ArgInsts,
ArgLives, real_unify, Type, !:Inst, _Det, !ModuleInfo)
->
true
;
unexpected(this_file, "bind_inst_to_functor: mode error")
).
%-----------------------------------------------------------------------------%
pre_lambda_update(ModuleInfo, Vars, Modes, InstMap0, InstMap) :-
mode_list_get_initial_insts(ModuleInfo, Modes, Insts),
assoc_list.from_corresponding_lists(Vars, Insts, VarInsts),
instmap_delta_from_assoc_list(VarInsts, InstMapDelta),
apply_instmap_delta(InstMap0, InstMapDelta, InstMap).
%-----------------------------------------------------------------------------%
apply_instmap_delta(unreachable, _, unreachable).
apply_instmap_delta(reachable(_), unreachable, unreachable).
apply_instmap_delta(reachable(InstMapping0),
reachable(InstMappingDelta), reachable(InstMapping)) :-
map.overlay(InstMapping0, InstMappingDelta, InstMapping).
instmap_delta_apply_instmap_delta(InstMap1, InstMap2, How, InstMap) :-
(
InstMap1 = unreachable,
InstMap = unreachable
;
InstMap1 = reachable(_),
InstMap2 = unreachable,
InstMap = unreachable
;
InstMap1 = reachable(InstMappingDelta1),
InstMap2 = reachable(InstMappingDelta2),
(
How = large_base,
map.overlay(InstMappingDelta1, InstMappingDelta2,
InstMappingDelta)
;
How = large_overlay,
map.overlay_large_map(InstMappingDelta1,
InstMappingDelta2, InstMappingDelta)
;
How = test_size,
(
map.count(InstMappingDelta1, Count1),
map.count(InstMappingDelta2, Count2),
Count1 >= Count2
->
map.overlay(InstMappingDelta1,
InstMappingDelta2, InstMappingDelta)
;
map.overlay_large_map(InstMappingDelta1,
InstMappingDelta2, InstMappingDelta)
)
),
InstMap = reachable(InstMappingDelta)
).
%-----------------------------------------------------------------------------%
instmap_restrict(_, unreachable, unreachable).
instmap_restrict(Vars, reachable(InstMapping0), reachable(InstMapping)) :-
map.select(InstMapping0, Vars, InstMapping).
instmap_delta_restrict(_, unreachable, unreachable).
instmap_delta_restrict(Vars,
reachable(InstMapping0), reachable(InstMapping)) :-
map.select(InstMapping0, Vars, InstMapping).
instmap_delta_delete_vars(_, unreachable, unreachable).
instmap_delta_delete_vars(Vars,
reachable(InstMapping0), reachable(InstMapping)) :-
map.delete_list(InstMapping0, Vars, InstMapping).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
instmap_merge(NonLocals, InstMapList, MergeContext, !ModeInfo) :-
mode_info_get_instmap(!.ModeInfo, InstMap0),
mode_info_get_module_info(!.ModeInfo, ModuleInfo0),
get_reachable_instmaps(InstMapList, InstMappingList),
(
% We can reach the code after the branched control structure only if
% (a) we can reach its start, and (b) some branch can reach the end.
InstMap0 = reachable(InstMapping0),
InstMappingList = [_ | _]
->
set.to_sorted_list(NonLocals, NonLocalsList),
mode_info_get_var_types(!.ModeInfo, VarTypes),
merge_2(NonLocalsList, InstMapList, VarTypes,
InstMapping0, InstMapping, ModuleInfo0, ModuleInfo, ErrorList),
mode_info_set_module_info(ModuleInfo, !ModeInfo),
(
ErrorList = [FirstError | _],
FirstError = Var - _,
set.singleton_set(WaitingVars, Var),
mode_info_error(WaitingVars,
mode_error_disj(MergeContext, ErrorList), !ModeInfo)
;
ErrorList = []
),
InstMap = reachable(InstMapping)
;
InstMap = unreachable
),
mode_info_set_instmap(InstMap, !ModeInfo).
:- pred get_reachable_instmaps(list(instmap)::in, list(instmapping)::out)
is det.
get_reachable_instmaps([], []).
get_reachable_instmaps([InstMap | InstMaps], Reachables) :-
(
InstMap = reachable(InstMapping),
get_reachable_instmaps(InstMaps, ReachablesTail),
Reachables = [InstMapping | ReachablesTail]
;
InstMap = unreachable,
get_reachable_instmaps(InstMaps, Reachables)
).
%-----------------------------------------------------------------------------%
% merge_2(Vars, InstMapList, VarTypes, !InstMapping, !ModuleInfo,
% Errors):
%
% Given Vars, a list of variables, and InstMapList, a list of instmaps
% giving the insts of those variables (and possibly others) at the ends of
% a branched control structure such as a disjunction or if-then-else,
% update !InstMapping, which initially gives the insts of variables at the
% start of the branched control structure, to reflect their insts at its
% end.
%
% For variables mentioned in Vars, merge their insts and put the merged
% inst into !:InstMapping. For variables not in Vars, leave their insts in
% !.InstMapping alone.
%
% If some variables in Vars have incompatible insts in two or more instmaps
% in InstMapList, return them in `Errors'.
%
:- pred merge_2(list(prog_var)::in, list(instmap)::in, vartypes::in,
instmapping::in, instmapping::out, module_info::in, module_info::out,
merge_errors::out) is det.
merge_2([], _, _, !InstMap, !ModuleInfo, []).
merge_2([Var | Vars], InstMapList, VarTypes, !InstMapping,
!ModuleInfo, !:ErrorList) :-
merge_2(Vars, InstMapList, VarTypes, !InstMapping, !ModuleInfo,
!:ErrorList),
map.lookup(VarTypes, Var, VarType),
list.map(lookup_var_in_instmap(Var), InstMapList, InstList),
merge_var(InstList, Var, VarType, !ModuleInfo, MaybeInst),
(
MaybeInst = no,
!:ErrorList = [Var - InstList | !.ErrorList],
svmap.set(Var, not_reached, !InstMapping)
;
MaybeInst = yes(Inst),
svmap.set(Var, Inst, !InstMapping)
).
:- pred lookup_var_in_instmap(prog_var::in, instmap::in, mer_inst::out) is det.
lookup_var_in_instmap(Var, InstMap, Inst) :-
lookup_var(InstMap, Var, Inst).
% merge_var(Insts, Var, Type, Inst, !ModuleInfo, !Error):
%
% Given a list of insts of the given variable that reflect the inst of that
% variable at the ends of a branched control structure such as a
% disjunction or if-then-else, return the final inst of that variable
% after the branched control structure as a whole.
%
% Set !:Error to yes if two insts of the variable are incompatible.
%
% We used to use a straightforward algorithm that, given a list of N insts,
% merged the tail N-1 insts, and merged the result with the head inst.
% While this is simple and efficient for small N, it has very bad
% performance for large N. The reason is that its complexity can be N^2,
% since in many cases each arm of the branched control structure binds
% Var to a different function symbol, and this means that the inst of Var
% evolves like this:
%
% bound(f)
% bound(f; g)
% bound(f; g; h)
% bound(f; g; h; i)
%
% Our current algorithm uses a number of passes, each of which divides the
% number of insts in half by merging adjacent pairs of insts. The overall
% complexity is thus N log N, not N^2.
%
:- pred merge_var(list(mer_inst)::in, prog_var::in, mer_type::in,
module_info::in, module_info::out, maybe(mer_inst)::out) is det.
merge_var(Insts, Var, Type, !ModuleInfo, MaybeMergedInst) :-
merge_var_2(Insts, Var, Type, [], MergedInsts, !ModuleInfo, no, Error),
(
Error = yes,
MaybeMergedInst = no
;
Error = no,
(
MergedInsts = [],
MaybeMergedInst = yes(not_reached)
;
MergedInsts = [MergedInst],
MaybeMergedInst = yes(MergedInst)
;
MergedInsts = [_, _ | _],
merge_var(MergedInsts, Var, Type, !ModuleInfo, MaybeMergedInst)
)
).
:- pred merge_var_2(list(mer_inst)::in, prog_var::in, mer_type::in,
list(mer_inst)::in, list(mer_inst)::out, module_info::in, module_info::out,
bool::in, bool::out) is det.
merge_var_2([], _, _, !MergedInsts, !ModuleInfo, !Error).
merge_var_2([Inst], _Var, _Type, !MergedInsts, !ModuleInfo, !Error) :-
!:MergedInsts = [Inst | !.MergedInsts].
merge_var_2([Inst1, Inst2 | Insts], Var, Type, !MergedInsts, !ModuleInfo,
!Error) :-
( inst_merge(Inst1, Inst2, yes(Type), MergedInst, !ModuleInfo) ->
!:MergedInsts = [MergedInst | !.MergedInsts],
merge_var_2(Insts, Var, Type, !MergedInsts, !ModuleInfo, !Error)
;
!:Error = yes
).
%-----------------------------------------------------------------------------%
% We use the same technique for merge_instmap_deltas as for merge_var,
% and for the same reason.
merge_instmap_deltas(InstMap, NonLocals, VarTypes, Deltas,
MergedDelta, !ModuleInfo) :-
merge_instmap_deltas_2(InstMap, NonLocals, VarTypes, Deltas,
[], MergedDeltas, !ModuleInfo),
(
MergedDeltas = [],
unexpected(this_file,
"merge_instmap_deltas: empty instmap_delta list.")
;
MergedDeltas = [MergedDelta]
;
MergedDeltas = [_, _ | _],
merge_instmap_deltas(InstMap, NonLocals, VarTypes, MergedDeltas,
MergedDelta, !ModuleInfo)
).
:- pred merge_instmap_deltas_2(instmap::in, set(prog_var)::in, vartypes::in,
list(instmap_delta)::in, list(instmap_delta)::in, list(instmap_delta)::out,
module_info::in, module_info::out) is det.
merge_instmap_deltas_2(_InstMap, _NonLocals, _VarTypes, [], !MergedDeltas,
!ModuleInfo).
merge_instmap_deltas_2(_InstMap, _NonLocals, _VarTypes, [Delta], !MergedDeltas,
!ModuleInfo) :-
!:MergedDeltas = [Delta | !.MergedDeltas].
merge_instmap_deltas_2(InstMap, NonLocals, VarTypes, [Delta1, Delta2 | Deltas],
!MergedDeltas, !ModuleInfo) :-
merge_instmap_delta(InstMap, NonLocals, VarTypes, Delta1, Delta2,
MergedDelta, !ModuleInfo),
!:MergedDeltas = [MergedDelta | !.MergedDeltas],
merge_instmap_deltas_2(InstMap, NonLocals, VarTypes, Deltas,
!MergedDeltas, !ModuleInfo).
%-----------------------------------------------------------------------------%
instmap_unify(NonLocals, InstMapList, !ModeInfo) :-
(
% If any of the instmaps is unreachable, then the final instmap
% is unreachable.
list.member(unreachable - _, InstMapList)
->
mode_info_set_instmap(unreachable, !ModeInfo)
;
% If there is only one instmap, then we just stick it in the mode_info.
InstMapList = [InstMap - _]
->
mode_info_set_instmap(InstMap, !ModeInfo)
;
InstMapList = [InstMap0 - _|InstMapList1],
InstMap0 = reachable(InstMapping0)
->
% Having got the first instmapping, to use as an accumulator,
% all unify_2 which unifies each of the nonlocals from
% each instmap with the corresponding inst in the accumulator.
mode_info_get_module_info(!.ModeInfo, ModuleInfo0),
set.to_sorted_list(NonLocals, NonLocalsList),
unify_2(NonLocalsList, InstMap0, InstMapList1,
ModuleInfo0, ModuleInfo, InstMapping0, InstMapping, ErrorList),
mode_info_set_module_info(ModuleInfo, !ModeInfo),
% If there were any errors, then add the error to the list
% of possible errors in the mode_info.
(
ErrorList = [FirstError | _],
FirstError = Var - _,
set.singleton_set(WaitingVars, Var),
mode_info_error(WaitingVars,
mode_error_par_conj(ErrorList), !ModeInfo)
;
ErrorList = []
),
mode_info_set_instmap(reachable(InstMapping), !ModeInfo)
;
true
).
%-----------------------------------------------------------------------------%
% unify_2(Vars, InitialInstMap, InstMaps, !ModuleInfo,
% !Instmap, ErrorList):
%
% Let `ErrorList' be the list of variables in `Vars' for which there are
% two instmaps in `InstMaps' for which the insts of the variable is
% incompatible.
%
:- pred unify_2(list(prog_var)::in, instmap::in,
list(pair(instmap, set(prog_var)))::in, module_info::in, module_info::out,
map(prog_var, mer_inst)::in, map(prog_var, mer_inst)::out,
merge_errors::out) is det.
unify_2([], _, _, !ModuleInfo, !InstMap, []).
unify_2([Var|Vars], InitialInstMap, InstMapList,
!ModuleInfo, !InstMap, ErrorList) :-
unify_2(Vars, InitialInstMap, InstMapList, !ModuleInfo, !InstMap,
ErrorListTail),
lookup_var(InitialInstMap, Var, InitialVarInst),
unify_var(InstMapList, Var, [], Insts, InitialVarInst, Inst,
!ModuleInfo, no, Error),
(
Error = yes,
ErrorList = [Var - Insts | ErrorListTail]
;
Error = no,
ErrorList = ErrorListTail
),
map.set(!.InstMap, Var, Inst, !:InstMap).
% unify_var(InstMaps, Var, InitialInstMap, ModuleInfo,
% Insts, Error):
%
% Let `Insts' be the list of the inst of `Var' in each of the
% corresponding `InstMaps'. Let `Error' be yes iff there are two
% instmaps for which the inst of `Var' is incompatible.
%
:- pred unify_var(list(pair(instmap, set(prog_var)))::in,
prog_var::in, list(mer_inst)::in, list(mer_inst)::out,
mer_inst::in, mer_inst::out, module_info::in, module_info::out,
bool::in, bool::out) is det.
unify_var([], _, !Insts, !Inst, !ModuleInfo, !Error).
unify_var([InstMap - Nonlocals| Rest], Var, !InstList, !Inst,
!ModuleInfo, !Error) :-
( set.member(Var, Nonlocals) ->
lookup_var(InstMap, Var, VarInst),
(
% We can ignore the determinism of the unification:
% if it isn't det, then there will be a mode error
% or a determinism error in one of the parallel conjuncts.
abstractly_unify_inst(live, !.Inst, VarInst,
fake_unify, !:Inst, _Det, !ModuleInfo)
->
true
;
!:Error = yes,
!:Inst = not_reached
)
;
VarInst = free
),
!:InstList = [VarInst | !.InstList],
unify_var(Rest, Var, !InstList, !Inst, !ModuleInfo, !Error).
%-----------------------------------------------------------------------------%
compute_instmap_delta(unreachable, _, _, unreachable).
compute_instmap_delta(reachable(_), unreachable, _, unreachable).
compute_instmap_delta(reachable(InstMapA), reachable(InstMapB), NonLocals,
reachable(DeltaInstMap)) :-
set.to_sorted_list(NonLocals, NonLocalsList),
compute_instmap_delta_2(NonLocalsList, InstMapA, InstMapB, AssocList),
map.from_sorted_assoc_list(AssocList, DeltaInstMap).
:- pred compute_instmap_delta_2(list(prog_var)::in, instmapping::in,
instmapping::in, assoc_list(prog_var, mer_inst)::out) is det.
compute_instmap_delta_2([], _, _, []).
compute_instmap_delta_2([Var | Vars], InstMapA, InstMapB, AssocList) :-
instmapping_lookup_var(InstMapA, Var, InstA),
instmapping_lookup_var(InstMapB, Var, InstB),
( InstA = InstB ->
AssocList1 = AssocList
;
AssocList = [ Var - InstB | AssocList1 ]
),
compute_instmap_delta_2(Vars, InstMapA, InstMapB, AssocList1).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
no_output_vars(_, unreachable, _, _, _).
no_output_vars(InstMap0, reachable(InstMapDelta), Vars, VT, M) :-
set.to_sorted_list(Vars, VarList),
no_output_vars_2(VarList, InstMap0, InstMapDelta, VT, M).
:- pred no_output_vars_2(list(prog_var)::in, instmap::in,
instmapping::in, vartypes::in, module_info::in) is semidet.
no_output_vars_2([], _, _, _, _).
no_output_vars_2([Var | Vars], InstMap0, InstMapDelta, VarTypes,
ModuleInfo) :-
% We use `inst_matches_binding' to check that the new inst has only
% added information or lost uniqueness, not bound anything.
% If the instmap delta contains the variable, the variable may still
% not be output, if the change is just an increase in information
% rather than an increase in instantiatedness. If the instmap delta
% doesn't contain the variable, it may still have been (partially) output,
% if its inst is (or contains) `any'.
lookup_var(InstMap0, Var, Inst0),
( map.search(InstMapDelta, Var, Inst1) ->
Inst = Inst1
;
Inst = Inst0
),
map.lookup(VarTypes, Var, Type),
inst_matches_binding(Inst, Inst0, Type, ModuleInfo),
no_output_vars_2(Vars, InstMap0, InstMapDelta, VarTypes, ModuleInfo).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
merge_instmap_delta(_, _, _, unreachable, InstMapDelta, InstMapDelta,
!ModuleInfo).
merge_instmap_delta(_, _, _, reachable(InstMapping), unreachable,
reachable(InstMapping), !ModuleInfo).
merge_instmap_delta(InstMap, NonLocals, VarTypes, reachable(InstMappingA),
reachable(InstMappingB), reachable(InstMapping), !ModuleInfo) :-
merge_instmapping_delta(InstMap, NonLocals, VarTypes,
InstMappingA, InstMappingB, InstMapping, !ModuleInfo).
:- pred merge_instmapping_delta(instmap::in, set(prog_var)::in, vartypes::in,
instmapping::in, instmapping::in, instmapping::out,
module_info::in, module_info::out) is det.
merge_instmapping_delta(InstMap, NonLocals, VarTypes,
InstMappingA, InstMappingB, InstMapping, !ModuleInfo) :-
map.keys(InstMappingA, VarsInA),
map.keys(InstMappingB, VarsInB),
set.sorted_list_to_set(VarsInA, SetofVarsInA),
set.insert_list(SetofVarsInA, VarsInB, SetofVars0),
set.intersect(SetofVars0, NonLocals, SetofVars),
set.to_sorted_list(SetofVars, ListofVars),
merge_instmapping_delta_2(ListofVars, InstMap, VarTypes,
InstMappingA, InstMappingB, map.init, InstMapping, !ModuleInfo).
:- pred merge_instmapping_delta_2(list(prog_var)::in, instmap::in,
vartypes::in, instmapping::in, instmapping::in,
instmapping::in, instmapping::out,
module_info::in, module_info::out) is det.
merge_instmapping_delta_2([], _, _, _, _, !InstMapping, !ModuleInfo).
merge_instmapping_delta_2([Var | Vars], InstMap, VarTypes,
InstMappingA, InstMappingB, !InstMapping, !ModuleInfo) :-
( map.search(InstMappingA, Var, InstInA) ->
InstA = InstInA
;
lookup_var(InstMap, Var, InstA)
),
( map.search(InstMappingB, Var, InstInB) ->
InstB = InstInB
;
lookup_var(InstMap, Var, InstB)
),
(
inst_merge(InstA, InstB, yes(VarTypes ^ det_elem(Var)), Inst1,
!ModuleInfo)
->
% XXX Given lookup_var(InstMap, Var, OldInst),
% we should probably set Inst not directly from Inst1, but
% from a conjunction of OldInst and Inst1. If OldInst says that
% Var is bound to f, and Inst1 says that it is bound to g,
% Inst should be `unreachable', not bound(g). If OldInst says
% that Var is bound to f or g, and Inst1 says that it is bound
% to g or h, Inst should say that it is bound(g).
%
% If there is an invariant to the effect that such situations
% are not supposed to arise, then it is being broken, due to
% the XXX in recompute_instmap_delta_unify in mode_util.m.
%
% At present, I believe that the cases we mishandle here can
% arise only after inlining, as in puzzle_detism_bug.m in
% tests/hard_coded. -zs
Inst = Inst1,
svmap.det_insert(Var, Inst, !InstMapping)
;
term.var_to_int(Var, VarInt),
string.format("merge_instmapping_delta_2: error merging var %i",
[i(VarInt)], Msg),
unexpected(this_file, Msg)
),
merge_instmapping_delta_2(Vars, InstMap, VarTypes,
InstMappingA, InstMappingB, !InstMapping, !ModuleInfo).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
% Given two instmap deltas, unify them to produce a new instmap_delta.
%
unify_instmap_delta(_, _, unreachable, InstMapDelta, InstMapDelta,
!ModuleInfo).
unify_instmap_delta(_, _, reachable(InstMapping), unreachable,
reachable(InstMapping), !ModuleInfo).
unify_instmap_delta(InstMap, NonLocals, reachable(InstMappingA),
reachable(InstMappingB), reachable(InstMapping), !ModuleInfo) :-
unify_instmapping_delta(InstMap, NonLocals, InstMappingA, InstMappingB,
InstMapping, !ModuleInfo).
:- pred unify_instmapping_delta(instmap::in, set(prog_var)::in,
instmapping::in, instmapping::in, instmapping::out,
module_info::in, module_info::out) is det.
unify_instmapping_delta(InstMap, NonLocals, InstMappingA, InstMappingB,
InstMapping, !ModuleInfo) :-
map.keys(InstMappingA, VarsInA),
map.keys(InstMappingB, VarsInB),
set.sorted_list_to_set(VarsInA, SetofVarsInA),
set.insert_list(SetofVarsInA, VarsInB, SetofVars0),
set.intersect(SetofVars0, NonLocals, SetofVars),
set.to_sorted_list(SetofVars, ListofVars),
unify_instmapping_delta_2(ListofVars, InstMap, InstMappingA, InstMappingB,
map.init, InstMapping, !ModuleInfo).
:- pred unify_instmapping_delta_2(list(prog_var)::in, instmap::in,
instmapping::in, instmapping::in, instmapping::in, instmapping::out,
module_info::in, module_info::out) is det.
unify_instmapping_delta_2([], _, _, _, !InstMapping, !ModuleInfo).
unify_instmapping_delta_2([Var | Vars], InstMap, InstMappingA, InstMappingB,
!InstMapping, !ModuleInfo) :-
( map.search(InstMappingA, Var, InstA) ->
( map.search(InstMappingB, Var, InstB) ->
(
% We can ignore the determinism of the unification: if it
% isn't det, then there will be a mode error or a determinism
% error in one of the parallel conjuncts.
abstractly_unify_inst(live, InstA, InstB,
fake_unify, Inst, _Det, !ModuleInfo)
->
svmap.det_insert(Var, Inst, !InstMapping)
;
unexpected(this_file,
"unify_instmapping_delta_2: unexpected error")
)
;
svmap.det_insert(Var, InstA, !InstMapping)
)
;
( map.search(InstMappingB, Var, InstB) ->
svmap.det_insert(Var, InstB, !InstMapping)
;
true
)
),
unify_instmapping_delta_2(Vars, InstMap, InstMappingA, InstMappingB,
!InstMapping, !ModuleInfo).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
instmap_delta_apply_sub(_Must, _Sub, unreachable, unreachable).
instmap_delta_apply_sub(Must, Sub,
reachable(OldInstMapping), reachable(InstMapping)) :-
map.to_assoc_list(OldInstMapping, InstMappingAL),
instmap_delta_apply_sub_2(InstMappingAL, Must, Sub,
map.init, InstMapping).
apply_sub(Must, Sub, InstMap0, InstMap) :-
instmap_delta_apply_sub(Must, Sub, InstMap0, InstMap).
:- pred instmap_delta_apply_sub_2(assoc_list(prog_var, mer_inst)::in, bool::in,
map(prog_var, prog_var)::in, instmapping::in, instmapping::out) is det.
instmap_delta_apply_sub_2([], _Must, _Sub, IM, IM).
instmap_delta_apply_sub_2([V - I | AL], Must, Sub, IM0, IM) :-
( map.search(Sub, V, N0) ->
N = N0
;
(
Must = no,
N = V
;
Must = yes,
unexpected(this_file, "instmap_delta_apply_sub_2: no substitute")
)
),
% XXX temporary hack alert XXX
% This should be a call to to map.det_insert, rather than a call
% to map.set. However, if we do that, then the compiler breaks,
% due to a problem with excess.m not preserving super-homogenous form.
map.set(IM0, N, I, IM1),
instmap_delta_apply_sub_2(AL, Must, Sub, IM1, IM).
%-----------------------------------------------------------------------------%
to_assoc_list(unreachable, []).
to_assoc_list(reachable(InstMapping), AL) :-
map.to_assoc_list(InstMapping, AL).
instmap_delta_to_assoc_list(unreachable, []).
instmap_delta_to_assoc_list(reachable(InstMapping), AL) :-
map.to_assoc_list(InstMapping, AL).
%-----------------------------------------------------------------------------%
var_is_ground_in_instmap(ModuleInfo, InstMap, Var) :-
lookup_var(InstMap, Var, Inst),
inst_is_ground(ModuleInfo, Inst).
%-----------------------------------------------------------------------------%
:- func this_file = string.
this_file = "instmap.m".
%-----------------------------------------------------------------------------%