Files
mercury/compiler/magic.m
Zoltan Somogyi a2bf36e49a This diff contains no changes in algorithms whatsoever.
Estimated hours taken: 4
Branches: main

This diff contains no changes in algorithms whatsoever.

browser/*.m:
compiler/*.m:
library/*.m:
	Replace old-style lambdas with new-style lambdas or with named
	procedures.
2003-11-05 03:17:49 +00:00

1847 lines
67 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1998-2003 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: magic.m
% Main author: stayl
%
% This module implements the supplementary magic set transformation,
% sort of as described in
% C. Beeri and R. Ramakrishnan,
% On the power of magic,
% Journal of Logic Programming,
% volume 10, 1991, pp. 255-299.
% The main difference is that the input relation is explicitly passed
% as a closure.
%
% The magic set transformation is used to transform queries into a form
% suitable for the Aditi deductive database code generator in rl_gen.m.
% The magic set transformation or the context transformation (defined
% in context.m) must be applied to all Aditi predicates. The magic sets
% and context transformations are mutually exclusive.
%
% It is important that no optimization which could optimize away calls
% to Aditi procedures (e.g. simplify.m) be run between magic.m and rl_gen.m.
% If Aditi calls are removed, the code in dependency_graph.m which merges
% the SCCs containing Aditi predicates could become confused about which
% predicates can be compiled together.
%
%-----------------------------------------------------------------------------%
% Short example:
%
% :- module a.
%
% :- interface.
%
% :- import_module aditi.
%
% :- pred call_anc(aditi__state::in, int::out) is nondet.
% :- pragma aditi(call_anc/3).
%
% :- implementation.
%
% :- pred anc(aditi__state::in, int::in, int::out) is nondet.
% :- pragma aditi(anc/3).
%
% :- pred p(aditi__state::in, int::out, int::out) is nondet.
% :- pragma base_relation(p/3).
%
% anc(DB, X, Y) :-
% p(DB, X, Y).
% anc(DB, X, Y) :-
% p(DB, X, Z),
% anc(DB, Z, Y).
%
%-----------------------------------------------------------------------------%
% Transformed version:
%
% % The original predicate is converted into a goal which
% % calls do_call_aditi_nondet to do all the work.
% % The type_infos are used for data conversion.
% % The base relation p/3 is given an interface procedure
% % which looks basically the same as this one, except that
% % there are no inputs and two outputs.
% % This procedure is compiled to C, not Aditi-RL.
% anc(HeadVar__1, HeadVar__2, HeadVar__3) :-
% V_15 = "stayl/a/a__anc__c_interface_2_0/2", % RL proc name
% V_16 = 1, % number of inputs
% V_17 = "(:I)", % input relation schema
% V_18 = 1, % number of outputs
% TypeInfo_13 = type_ctor_info("", "int", 0), % input type_info
% TypeInfo_14 = type_ctor_info("", "int", 0), % output type_info
%
% % The aditi__state is not needed (it contains no information),
% % so it is not passed.
% % aditi_private_builtin__do_nondet_call(PredName,
% % InputSchema, InputTuple, OutputTuple)
% aditi_private_builtin__do_nondet_call(
% TypeInfo_13, TypeInfo_14,
% "stayl/a/a__anc__c_interface_2_0/2", "(:I)",
% {HeadVar__2}, {HeadVar__3}).
%
% :- pred anc__c_interface(pred(int)::(pred(out) is nondet),
% int::out) is nondet.
% :- pragma aditi(anc__c_interface/2).
%
% % This predicate calls the Aditi version of anc, joins the result
% % with the input to the calls and then projects the join result onto
% % the output arguments.
% anc__c_interface(InAnc, Y) :-
% anc__c_interface__supp1(InAnc, X),
% V_15 = anc__c_interface__supp1(InAnc),
% anc__aditi0(InAnc, V_1, Y),
% X == V_1.
%
% % The aditi__state arguments are removed and all modes are
% % converted to output. An input closure is added for each
% % predicate in the SCC.
% :- pred anc__aditi0(pred(int)::(pred(out) is nondet),
% int::out, int::out) is nondet.
% :- pragma aditi(anc__aditi0/3).
%
% anc__aditi0(InAnc, X, Y) :-
% anc__magic(InAnc, X),
% p(V_1, Y),
% V1 == X.
% anc__aditi0(InAnc, X, Y) :-
% anc__supp1(InAnc, X),
% anc__aditi0(InAnc, V1, Y),
% V1 == X.
%
% % `anc__magic' collects all tuples which could be input to
% % a call to `anc' in a top-down execution.
% :- pred anc__magic(pred(int)::(pred(out) is nondet), int::out) is nondet.
% :- pragma aditi(anc__magic/2).
%
% anc__magic(InAnc, X) :-
% % Collect the input from a higher sub-module.
% call(InAnc, X).
% anc__magic(InAnc, Z) :-
% % Collect the input from recursive calls.
% anc__supp1(InAnc, _, Z).
%
% % `anc__supp1' is introduced to do common sub-expression -
% % this join would otherwise be done in both `anc__aditi0'
% % and `anc__magic'. This is also necessary because rl_gen.m
% % only handles rules with at most two database calls.
% :- pred anc__supp1(pred(int)::(pred(out) is nondet, int::out) is nondet.
% :- pragma aditi(anc__supp1/2).
%
% anc__supp1(InAnc, Z) :-
% anc__magic(InAnc, X),
% p(V_1, Z),
% X == V1.
%
%-----------------------------------------------------------------------------%
%
% context.m is called to handle predicates with a `:- pragma context'
% declaration.
%
% Input relations are explicitly passed using closures.
%
% While it processes the module it checks that there are no higher-order,
% partially instantiated, polymorphic or abstract arguments since Aditi
% cannot handle these.
%
% Any closures occurring in Aditi procedures must not have curried arguments.
% Closures may only be used for aggregates.
%
% XXX This should attempt to reorder within rules so that no supplementary
% predicates are created with partially instantiated arguments, since Aditi
% can only handle ground terms in relations. The problem occurs if there are
% partially instantiated terms live across a database predicate call. At the
% moment an error is reported.
%
% Note that the transformation introduces new mangled predicate names,
% but these should not show up in the generated C code so util/mdemangle does
% not need to handle them.
%
%-----------------------------------------------------------------------------%
:- module aditi_backend__magic.
:- interface.
:- import_module hlds__hlds_module.
:- import_module io.
:- pred magic__process_module(module_info, module_info, io__state, io__state).
:- mode magic__process_module(in, out, di, uo) is det.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module aditi_backend__aditi_builtin_ops.
:- import_module aditi_backend__context.
:- import_module aditi_backend__magic_util.
:- import_module aditi_backend__rl.
:- import_module aditi_backend__rl_gen.
:- import_module check_hlds__mode_util.
:- import_module check_hlds__polymorphism.
:- import_module check_hlds__type_util.
:- import_module hlds__goal_util.
:- import_module hlds__hlds_data.
:- import_module hlds__hlds_goal.
:- import_module hlds__hlds_out.
:- import_module hlds__hlds_pred.
:- import_module hlds__instmap.
:- import_module hlds__passes_aux.
:- import_module hlds__quantification.
:- import_module libs__globals.
:- import_module libs__options.
:- import_module ll_backend__saved_vars.
:- import_module parse_tree__inst.
:- import_module parse_tree__prog_data.
:- import_module parse_tree__prog_out.
:- import_module transform_hlds__dead_proc_elim.
:- import_module transform_hlds__dependency_graph.
:- import_module int, list, map, require, set, std_util, string, term, varset.
:- import_module assoc_list, bool, check_hlds__simplify.
magic__process_module(ModuleInfo0, ModuleInfo) -->
%
% Run simplification on Aditi procedures, mainly to get rid of
% nested explicit quantifications.
%
globals__io_get_globals(Globals),
{ simplify__find_simplifications(no, Globals, Simplifications) },
process_matching_nonimported_procs(
update_module_io(
magic__ite_to_disj_and_simplify(Simplifications)),
_, hlds_pred__pred_info_is_aditi_relation,
ModuleInfo0, ModuleInfo1),
% We need to run dead_proc_elim before working out the
% Aditi dependency ordering because any calls from dead
% procedures could confuse the code to merge SCCs (because
% procedures called from multiple places are never merged).
%
% No optimizations which could optimize away calls to Aditi
% procedures (e.g. simplify.m) should be run after this is done.
dead_proc_elim(final_optimization_pass, ModuleInfo1, ModuleInfo2),
{ module_info_ensure_aditi_dependency_info(ModuleInfo2, ModuleInfo3) },
{ module_info_aditi_dependency_ordering(ModuleInfo3, Ordering) },
{ magic_info_init(ModuleInfo3, Info0) },
{ module_info_predids(ModuleInfo3, PredIds) },
%
% Only preprocess imported Aditi predicates which are used,
% to avoid performing error checking (e.g. checking for abstract
% types) on predicates which are not used. The check for abstract
% types needs to be done in importing modules because an imported
% predicate's declaration may use types which are indirectly imported
% from another module. Discriminated union types are written as
% abstract types to `.int2' files.
%
{ set__init(UsedImportedPreds0) },
{ list__foldl(magic__find_used_imported_aditi_preds(ModuleInfo3),
Ordering, UsedImportedPreds0, UsedImportedPreds) },
{ magic__process_imported_procs(PredIds, UsedImportedPreds,
Info0, Info1) },
globals__io_lookup_bool_option(very_verbose, Verbose),
% Add magic procedures, do some transformation on the goals.
maybe_write_string(Verbose, "% preprocessing module\n"),
maybe_flush_output(Verbose),
{ list__foldl(magic__check_scc, Ordering, Info1, Info2) },
{ list__foldl(magic__preprocess_scc, Ordering, Info2, Info3) },
% Do the transformation.
maybe_write_string(Verbose, "% processing module\n"),
maybe_flush_output(Verbose),
list__foldl2(magic__process_scc, Ordering, Info3, Info4),
{ list__foldl(magic__update_pred_status, PredIds, Info4, Info5) },
{ magic_info_get_module_info(ModuleInfo4, Info5, Info) },
{ magic_info_get_errors(Errors, Info, _) },
{ set__to_sorted_list(Errors, ErrorList) },
( { ErrorList = [] } ->
{ ModuleInfo5 = ModuleInfo4 }
;
globals__io_lookup_bool_option(verbose_errors, VerboseErrors),
magic_util__report_errors(ErrorList,
ModuleInfo4, VerboseErrors),
{ module_info_incr_errors(ModuleInfo4, ModuleInfo5) },
io__set_exit_status(1)
),
% New procedures were created, so the dependency_info
% is out of date.
{ module_info_clobber_dependency_info(ModuleInfo5, ModuleInfo) }.
%-----------------------------------------------------------------------------%
%
% Convert if-then-elses and switches to disjunctions,
% then run simplification to flatten goals and remove
% unnecessary existential quantifications.
%
:- pred magic__ite_to_disj_and_simplify(list(simplification)::in, pred_id::in,
proc_id::in, proc_info::in, proc_info::out, module_info::in,
module_info::out, io__state::di, io__state::uo) is det.
magic__ite_to_disj_and_simplify(Simplifications, PredId, ProcId,
!ProcInfo, !ModuleInfo) -->
{ proc_info_goal(!.ProcInfo, Goal0) },
{ Goal0 = if_then_else(_Vars, Cond, Then, Else) - GoalInfo ->
goal_util__if_then_else_to_disjunction(Cond, Then, Else,
GoalInfo, Disj),
Goal1 = Disj - GoalInfo,
proc_info_set_goal(Goal1, !ProcInfo),
% Requantify the goal to rename apart the variables
% in the copies of the condition.
requantify_proc(!ProcInfo)
; Goal0 = switch(Var, _Canfail, Cases) - GoalInfo ->
proc_info_varset(!.ProcInfo, VarSet0),
proc_info_vartypes(!.ProcInfo, VarTypes0),
proc_info_get_initial_instmap(!.ProcInfo,
!.ModuleInfo, InstMap),
% XXX check for existentially typed constructors first -
% they will cause an abort.
goal_util__switch_to_disjunction(Var, Cases,
InstMap, Disjuncts, VarSet0, VarSet1,
VarTypes0, VarTypes1, !ModuleInfo),
proc_info_set_varset(VarSet1, !ProcInfo),
proc_info_set_vartypes(VarTypes1, !ProcInfo),
Goal1 = disj(Disjuncts) - GoalInfo,
proc_info_set_goal(Goal1, !ProcInfo)
;
true
},
simplify__proc(Simplifications, PredId, ProcId,
!ModuleInfo, !ProcInfo),
%
% Run saved_vars so that constructions of constants are close
% to their uses, and constant attributes aren't unnecessarily
% added to relations. We should be more aggressive about this -
% constructions of constant compound terms should also be pushed.
%
saved_vars_proc(PredId, ProcId, !ProcInfo, !ModuleInfo).
%-----------------------------------------------------------------------------%
:- pred magic__check_scc(aditi_scc::in,
magic_info::in, magic_info::out) is det.
magic__check_scc(aditi_scc(SCC0, _)) -->
list__foldl(magic__check_scc_2, SCC0).
:- pred magic__check_scc_2(list(pred_proc_id)::in,
magic_info::in, magic_info::out) is det.
magic__check_scc_2(SCC) -->
magic_info_get_errors(Errors0),
magic_info_get_module_info(ModuleInfo),
{ SCC = [_] ->
Errors1 = Errors0
;
% Add errors for context procedures which are mutually
% recursive with other procedures.
solutions((pred(ProcAndContext::out) is nondet :-
list__member(ContextProc, SCC),
ContextProc = proc(ContextPredId, _),
module_info_pred_info(ModuleInfo,
ContextPredId, ContextPredInfo),
pred_info_get_markers(ContextPredInfo,
ContextMarkers),
check_marker(ContextMarkers, context),
pred_info_context(ContextPredInfo,
Context),
ProcAndContext = ContextProc - Context
), ContextProcs),
list__map((pred(BadContextProc::in, Error::out) is det :-
BadContextProc = TheContextProc - TheContext,
Error = mutually_recursive_context(
TheContextProc, SCC) - TheContext
), ContextProcs, ContextErrors),
set__insert_list(Errors0, ContextErrors, Errors1)
},
{
% Add errors if a procedure compiled to C is mutually
% recursive with an Aditi procedure.
list__member(proc(PredId, _), SCC),
module_info_pred_info(ModuleInfo, PredId, PredInfo),
pred_info_get_markers(PredInfo, Markers),
\+ check_marker(Markers, aditi)
->
term__context_init(InitContext),
set__insert(Errors1, mixed_scc(SCC) - InitContext, Errors)
;
Errors = Errors1
},
magic_info_set_errors(Errors).
%-----------------------------------------------------------------------------%
% All the procedures which previously had Aditi markers should
% have been renamed apart, leaving the old versions to be called
% from C. These old versions must have their aditi/base relation
% markers removed.
:- pred magic__update_pred_status(pred_id::in,
magic_info::in, magic_info::out) is det.
magic__update_pred_status(PredId, !MagicInfo) :-
magic_info_get_module_info(ModuleInfo0, !MagicInfo),
module_info_pred_info(ModuleInfo0, PredId, PredInfo0),
pred_info_get_markers(PredInfo0, Markers0),
( check_marker(Markers0, aditi) ->
remove_marker(aditi, Markers0, Markers1),
remove_marker(base_relation, Markers1, Markers),
pred_info_set_markers(Markers, PredInfo0, PredInfo),
module_info_set_pred_info(PredId, PredInfo,
ModuleInfo0, ModuleInfo),
magic_info_set_module_info(ModuleInfo, !MagicInfo)
;
true
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
% Find all imported procedures which are called within
% a local Aditi procedure. The magic sets version of their
% interface must be produced.
:- pred magic__find_used_imported_aditi_preds(module_info::in,
aditi_scc::in, set(pred_id)::in, set(pred_id)::out) is det.
magic__find_used_imported_aditi_preds(ModuleInfo, SCC, Preds0, Preds) :-
SCC = aditi_scc(SCCPredProcIds0, _EntryPoints),
list__condense(SCCPredProcIds0, SCCPredProcIds),
list__foldl(magic__find_used_imported_aditi_preds_2(ModuleInfo),
SCCPredProcIds, Preds0, Preds).
:- pred magic__find_used_imported_aditi_preds_2(module_info::in,
pred_proc_id::in, set(pred_id)::in, set(pred_id)::out) is det.
magic__find_used_imported_aditi_preds_2(ModuleInfo,
PredProcId, Preds0, Preds) :-
module_info_pred_proc_info(ModuleInfo, PredProcId, _, ProcInfo),
proc_info_goal(ProcInfo, Goal),
% Generate all pred_ids called by a goal.
Generator = (pred(P::out) is nondet :- goal_calls_pred_id(Goal, P)),
% Add all used imported Aditi predicates to the accumulator.
Accumulator =
(pred(CalledPredId::in, UsedPreds0::in, UsedPreds::out) is det :-
module_info_pred_info(ModuleInfo,
CalledPredId, CalledPredInfo),
(
pred_info_is_imported(CalledPredInfo),
pred_info_is_aditi_relation(CalledPredInfo)
->
set__insert(UsedPreds0, CalledPredId, UsedPreds)
;
UsedPreds = UsedPreds0
)
),
Preds = promise_only_solution(
(pred(Preds1::out) is cc_multi :-
unsorted_aggregate(Generator, Accumulator,
Preds0, Preds1)
)).
% Convert imported Aditi procedures for the magic sets interface.
:- pred magic__process_imported_procs(list(pred_id)::in, set(pred_id)::in,
magic_info::in, magic_info::out) is det.
magic__process_imported_procs([], _) --> [].
magic__process_imported_procs([PredId | PredIds], UsedPreds) -->
magic_info_get_module_info(ModuleInfo),
{ module_info_pred_info(ModuleInfo, PredId, PredInfo) },
(
{ pred_info_is_imported(PredInfo) },
{ hlds_pred__is_derived_relation(ModuleInfo, PredId) },
{ set__member(PredId, UsedPreds) }
->
{ ProcIds = pred_info_procids(PredInfo) },
magic__process_imported_procs_2(PredId, ProcIds)
;
{ hlds_pred__pred_info_is_base_relation(PredInfo) },
{
% Always preprocess base relations defined in
% this module.
module_info_name(ModuleInfo, ModuleName),
PredModuleName = pred_info_module(PredInfo),
ModuleName = PredModuleName
;
set__member(PredId, UsedPreds)
}
->
{ ProcIds = pred_info_procids(PredInfo) },
list__foldl(magic__process_base_relation(PredId), ProcIds)
;
[]
),
magic__process_imported_procs(PredIds, UsedPreds).
:- pred magic__process_imported_procs_2(pred_id::in, list(proc_id)::in,
magic_info::in, magic_info::out) is det.
magic__process_imported_procs_2(_, []) --> [].
magic__process_imported_procs_2(PredId, [ProcId | ProcIds]) -->
{ PredProcId = proc(PredId, ProcId) },
magic__get_scc_inputs([PredProcId], InputTypes, InputModes),
magic__adjust_pred_info([PredProcId], InputTypes,
InputModes, PredProcId),
magic__process_imported_procs_2(PredId, ProcIds).
%-----------------------------------------------------------------------------%
% Create a version without the aditi__states, and with
% all modes output.
:- pred magic__process_base_relation(pred_id::in, proc_id::in,
magic_info::in, magic_info::out) is det.
magic__process_base_relation(PredId0, ProcId0) -->
magic__separate_proc(PredId0, ProcId0),
magic_info_get_pred_map(PredMap),
{ CPredProcId = proc(PredId0, ProcId0) },
{ map__lookup(PredMap, CPredProcId, PredProcId) },
magic_info_get_module_info(ModuleInfo0),
{ module_info_pred_proc_info(ModuleInfo0, PredProcId,
PredInfo0, ProcInfo0) },
{ pred_info_arg_types(PredInfo0, TVarSet, ExistQVars, ArgTypes0) },
{ proc_info_argmodes(ProcInfo0, ArgModes0) },
{ proc_info_headvars(ProcInfo0, HeadVars0) },
magic_info_set_error_pred_proc_id(CPredProcId),
{ set__init(ErrorVars) },
magic_info_set_error_vars(ErrorVars),
(
{ ModuleName = pred_info_module(PredInfo0) },
{ module_info_name(ModuleInfo0, ModuleName) }
->
{ pred_info_context(PredInfo0, Context) },
magic_util__check_args(HeadVars0, ArgModes0, ArgTypes0,
Context, arg_number)
;
[]
),
% Remove aditi:states, convert arguments to output.
{ type_util__remove_aditi_state(ArgTypes0, ArgTypes0, ArgTypes) },
{ type_util__remove_aditi_state(ArgTypes0, ArgModes0, ArgModes1) },
{ list__map(magic_util__mode_to_output_mode(ModuleInfo0),
ArgModes1, ArgModes) },
{ type_util__remove_aditi_state(ArgTypes0, HeadVars0, HeadVars) },
{ pred_info_get_indexes(PredInfo0, Indexes0) },
{ list__map(magic_util__adjust_index(ArgTypes0), Indexes0, Indexes) },
{ pred_info_set_indexes(Indexes, PredInfo0, PredInfo1) },
{ pred_info_set_arg_types(TVarSet, ExistQVars, ArgTypes,
PredInfo1, PredInfo) },
{ proc_info_set_argmodes(ArgModes, ProcInfo0, ProcInfo1) },
{ proc_info_set_headvars(HeadVars, ProcInfo1, ProcInfo) },
{ module_info_set_pred_proc_info(PredProcId, PredInfo, ProcInfo,
ModuleInfo0, ModuleInfo) },
magic_info_set_module_info(ModuleInfo),
magic__interface_from_c([CPredProcId], CPredProcId, PredProcId).
%-----------------------------------------------------------------------------%
% Go over each sub-module adding in the input arguments for each
% procedure, allocating the magic predicates, filling in the
% magic_map, pred_map and magic_proc_info fields of the magic_info.
:- pred magic__preprocess_scc(aditi_scc::in,
magic_info::in, magic_info::out) is det.
magic__preprocess_scc(aditi_scc(SCC0, EntryPoints)) -->
{ list__condense(SCC0, SCC) },
magic__get_scc_inputs(EntryPoints, InputTypes, InputModes),
list__foldl(magic__adjust_pred_info(EntryPoints,
InputTypes, InputModes), SCC).
%-----------------------------------------------------------------------------%
% Work out the types and modes of the input relations that need
% to be passed around the sub-module.
:- pred magic__get_scc_inputs(list(pred_proc_id)::in, list(type)::out,
list(mode)::out, magic_info::in, magic_info::out) is det.
magic__get_scc_inputs([], [], []) --> [].
magic__get_scc_inputs([PredProcId | PredProcIds],
[Type | Types], [Mode | Modes]) -->
magic_info_get_module_info(ModuleInfo),
{ module_info_pred_proc_info(ModuleInfo, PredProcId,
PredInfo, ProcInfo) },
{ proc_info_argmodes(ProcInfo, ArgModes0) },
{ pred_info_arg_types(PredInfo, ArgTypes0) },
{ type_util__remove_aditi_state(ArgTypes0, ArgTypes0, ArgTypes) },
{ type_util__remove_aditi_state(ArgTypes0, ArgModes0, ArgModes) },
{ partition_args(ModuleInfo, ArgModes, ArgModes, InputModes, _) },
{ partition_args(ModuleInfo, ArgModes, ArgTypes, InputTypes, _) },
{ construct_higher_order_type((pure), predicate, (aditi_bottom_up),
InputTypes, Type) },
{ GetOutputMode = (pred(ArgMode::in, OutputMode::out) is det :-
mode_get_insts(ModuleInfo, ArgMode, _, OutputInst),
OutputMode = (free -> OutputInst)
) },
{ list__map(GetOutputMode, InputModes, InputRelModes) },
{ Inst = ground(unique, higher_order(pred_inst_info(predicate,
InputRelModes, nondet))) },
{ Mode = (Inst -> Inst) },
magic__get_scc_inputs(PredProcIds, Types, Modes).
%-----------------------------------------------------------------------------%
:- pred magic__adjust_pred_info(list(pred_proc_id)::in, list(type)::in,
list(mode)::in, pred_proc_id::in,
magic_info::in, magic_info::out) is det.
magic__adjust_pred_info(EntryPoints, MagicTypes,
MagicModes, PredProcId0) -->
{ PredProcId0 = proc(PredId0, ProcId0) },
magic__separate_proc(PredId0, ProcId0),
magic_info_get_pred_map(PredMap1),
{ map__lookup(PredMap1, PredProcId0, PredProcId) },
magic__adjust_proc_info(EntryPoints, PredProcId0, PredProcId,
MagicTypes, MagicModes).
%-----------------------------------------------------------------------------%
% Separate out the procedures for each predicate so that each
% pred_info for a derived database predicate contains only one
% proc_info. This is necessary because the different procedures
% have different numbers of input arguments and are members of
% different sub-modules, so the transformed procedures will have
% different numbers and types of input relation arguments. We also need
% to leave the original declarations so that predicates compiled
% to C can call the procedure.
:- pred magic__separate_proc(pred_id::in, proc_id::in,
magic_info::in, magic_info::out) is det.
magic__separate_proc(PredId, ProcId) -->
magic_info_get_module_info(ModuleInfo0),
{ module_info_pred_proc_info(ModuleInfo0, PredId, ProcId,
PredInfo0, ProcInfo0) },
magic_info_set_curr_pred_proc_id(proc(PredId, ProcId)),
magic_info_set_error_pred_proc_id(proc(PredId, ProcId)),
{ set__init(ErrorVars) },
magic_info_set_error_vars(ErrorVars),
%
% Create a new pred_info for the procedure.
%
% Produce a unique name for the procedure.
{ Module = pred_info_module(PredInfo0) },
{ Name = pred_info_name(PredInfo0) },
{ pred_info_get_markers(PredInfo0, Markers) },
( { check_marker(Markers, base_relation) } ->
% Base relations must keep the old name.
{ NewName = qualified(Module, Name) }
;
magic_util__make_pred_name(PredInfo0, ProcId,
"Aditi_Proc_For", no, NewName)
),
{ pred_info_arg_types(PredInfo0, TVarSet, ExistQVars, ArgTypes) },
{ pred_info_context(PredInfo0, Context) },
{ pred_info_import_status(PredInfo0, Status) },
{ PredOrFunc = pred_info_is_pred_or_func(PredInfo0) },
{ pred_info_get_aditi_owner(PredInfo0, Owner) },
{ pred_info_get_indexes(PredInfo0, Indexes) },
% type classes aren't supported in Aditi.
{ ClassConstraints = constraints([], []) },
{ set__init(Assertions) },
{ pred_info_create(Module, NewName, TVarSet,
ExistQVars, ArgTypes, true, Context, Status, Markers,
PredOrFunc, ClassConstraints, Owner, Assertions, ProcInfo0,
NewProcId, NewPredInfo0) },
{ pred_info_set_indexes(Indexes, NewPredInfo0, NewPredInfo) },
magic_info_get_module_info(ModuleInfo1),
{ module_info_get_predicate_table(ModuleInfo1, PredTable0) },
{ predicate_table_insert(PredTable0, NewPredInfo, NewPredId,
PredTable) },
{ module_info_set_predicate_table(PredTable,
ModuleInfo0, ModuleInfo) },
magic_info_set_module_info(ModuleInfo),
%
% Later we need to convert all calls to the old
% procedure to calls to the new.
%
magic_info_get_pred_map(PredMap0),
{ map__det_insert(PredMap0, proc(PredId, ProcId),
proc(NewPredId, NewProcId), PredMap) },
magic_info_set_pred_map(PredMap).
%-----------------------------------------------------------------------------%
% Preprocess the procedure
% - preprocess the goal
% - convert input arguments to output
% - add input closure arguments
:- pred magic__adjust_proc_info(list(pred_proc_id)::in, pred_proc_id::in,
pred_proc_id::in, list(type)::in, list(mode)::in,
magic_info::in, magic_info::out) is det.
magic__adjust_proc_info(EntryPoints, CPredProcId, AditiPredProcId,
MagicTypes, MagicModes) -->
magic_info_get_module_info(ModuleInfo0),
{ module_info_pred_proc_info(ModuleInfo0, AditiPredProcId,
PredInfo0, ProcInfo0) },
magic_info_set_error_pred_proc_id(CPredProcId),
{ set__init(ErrorVars) },
magic_info_set_error_vars(ErrorVars),
magic__preprocess_proc(CPredProcId, PredInfo0,
ProcInfo0, ProcInfo1),
%
% Find which of the arguments of the SCC carries the
% input for the current procedure.
%
{ list__nth_member_search(EntryPoints, CPredProcId, N) ->
Index = yes(N),
( EntryPoints \= [_], pred_info_is_exported(PredInfo0) ->
InterfaceRequired = yes(N)
;
InterfaceRequired = no
)
;
Index = no,
InterfaceRequired = no
},
{ proc_info_inst_varset(ProcInfo1, InstVarSet) },
magic__adjust_args(CPredProcId, AditiPredProcId, InterfaceRequired,
Index, MagicTypes, MagicModes, PredInfo0, ProcInfo1,
InputArgTypes, InputArgModes, LocalAditiPredProcId),
( { pred_info_is_imported(PredInfo0) } ->
[]
;
% Create a new procedure to collect the input
% for the current procedure.
magic__create_magic_pred(CPredProcId, LocalAditiPredProcId,
MagicTypes, MagicModes, InputArgTypes, InputArgModes,
InstVarSet, Index)
),
%
% Replace the goal for the C procedure with a goal to
% interface with the Aditi procedure from C, unless --aditi-only
% was specified or the procedure is imported.
%
magic__interface_from_c(EntryPoints, CPredProcId,
LocalAditiPredProcId).
%-----------------------------------------------------------------------------%
% Given a pred_info and a proc_info remove `aditi__state's from the
% arguments and set up the input closure arguments.
% Create an interface procedure if the SCC has multiple entry points
% and is exported.
:- pred magic__adjust_args(pred_proc_id::in, pred_proc_id::in, maybe(int)::in,
maybe(int)::in, list(type)::in, list(mode)::in,
pred_info::in, proc_info::in, list(type)::out, list(mode)::out,
pred_proc_id::out, magic_info::in, magic_info::out) is det.
magic__adjust_args(CPredProcId, AditiPredProcId, InterfaceRequired,
MaybeIndex, MagicTypes, MagicModes, PredInfo0, ProcInfo0,
InputArgTypes, InputArgModes, LocalAditiPredProcId) -->
%
% Check that the argument types and modes
% are legal for Aditi procedures.
%
{ pred_info_arg_types(PredInfo0, TVarSet, ExistQVars, ArgTypes0) },
{ proc_info_headvars(ProcInfo0, HeadVars0) },
{ pred_info_context(PredInfo0, Context) },
{ proc_info_argmodes(ProcInfo0, ArgModes0) },
magic_util__check_args(HeadVars0, ArgModes0, ArgTypes0, Context,
arg_number),
%
% Strip out the aditi__state argument.
%
{ type_util__remove_aditi_state(ArgTypes0, ArgTypes0, ArgTypes1) },
{ type_util__remove_aditi_state(ArgTypes0, HeadVars0, HeadVars1) },
{ type_util__remove_aditi_state(ArgTypes0, ArgModes0, ArgModes1) },
%
% Convert all of the original modes to output. The input
% will be carried in with the input closures.
%
magic_info_get_module_info(ModuleInfo0),
{ list__map(magic_util__mode_to_output_mode(ModuleInfo0),
ArgModes1, ArgModes2) },
% Create variables for the magic input.
{ proc_info_create_vars_from_types(MagicTypes, MagicVars,
ProcInfo0, ProcInfo1) },
%
% Add the input relation variables to the arguments.
%
{ list__append(MagicVars, HeadVars1, HeadVars) },
{ list__append(MagicModes, ArgModes2, ArgModes) },
{ list__append(MagicTypes, ArgTypes1, ArgTypes) },
%
% Ensure that the exported interface procedure gets the
% correct argmodes.
%
{ instmap_delta_from_mode_list(HeadVars, ArgModes,
ModuleInfo0, InstMapDelta) },
{ proc_info_goal(ProcInfo1, Goal0 - GoalInfo0) },
{ goal_info_set_instmap_delta(GoalInfo0, InstMapDelta, GoalInfo) },
{ proc_info_set_goal(Goal0 - GoalInfo, ProcInfo1, ProcInfo2) },
% All Aditi procedures are considered nondet. The C interface
% procedures retain the old determinism, and abort if the number
% of answers returned doesn't match the determinism.
{ proc_info_set_inferred_determinism(nondet, ProcInfo2, ProcInfo3) },
{ partition_args(ModuleInfo0, ArgModes1,
ArgModes1, InputArgModes, _) },
{ partition_args(ModuleInfo0, ArgModes1,
ArgTypes1, InputArgTypes, _) },
{ pred_info_set_arg_types(TVarSet, ExistQVars, ArgTypes,
PredInfo0, PredInfo) },
{ proc_info_set_headvars(HeadVars, ProcInfo3, ProcInfo4) },
{ proc_info_set_argmodes(ArgModes, ProcInfo4, ProcInfo) },
( { InterfaceRequired = yes(Index) } ->
magic__create_interface_proc(Index, CPredProcId,
AditiPredProcId, PredInfo0, ProcInfo3, ProcInfo,
HeadVars1, ArgTypes1, ArgModes2, MagicVars,
MagicTypes, MagicModes, LocalAditiPredProcId)
;
magic_info_get_module_info(ModuleInfo5),
{ module_info_set_pred_proc_info(AditiPredProcId,
PredInfo, ProcInfo, ModuleInfo5, ModuleInfo) },
magic_info_set_module_info(ModuleInfo),
{ LocalAditiPredProcId = AditiPredProcId }
),
{ ThisProcInfo = magic_proc_info(ArgModes1, MagicVars,
MagicTypes, MagicModes, MaybeIndex) },
magic_info_get_magic_proc_info(MagicProcInfo0),
{ map__det_insert(MagicProcInfo0, LocalAditiPredProcId,
ThisProcInfo, MagicProcInfo) },
magic_info_set_magic_proc_info(MagicProcInfo).
%
% Create an interface procedure to a sub-module for a particular
% entry-point, used by Mercury compiled to C and Aditi procedures
% in other modules.
% A local version is created which takes all the input
% arguments. The exported version calls this version
% with empty relations for all except one of the
% input arguments.
%
:- pred magic__create_interface_proc(int::in, pred_proc_id::in,
pred_proc_id::in, pred_info::in, proc_info::in, proc_info::in,
list(prog_var)::in, list(type)::in, list(mode)::in,
list(prog_var)::in, list(type)::in, list(mode)::in,
pred_proc_id::out, magic_info::in, magic_info::out) is det.
magic__create_interface_proc(Index, CPredProcId, AditiPredProcId,
ExportedPredInfo0, ExportedProcInfo0, LocalProcInfo,
HeadVars1, ArgTypes1, ArgModes1, MagicVars,
MagicTypes, MagicModes, LocalPredProcId) -->
%
% Create the local version.
%
{ proc_info_goal(LocalProcInfo, Goal) },
magic_info_get_module_info(ModuleInfo1),
{ proc_info_get_initial_instmap(LocalProcInfo, ModuleInfo1, InstMap) },
{ PredName0 = pred_info_name(ExportedPredInfo0) },
{ string__append(PredName0, "__local", PredName) },
{ proc_info_headvars(LocalProcInfo, HeadVars) },
{ proc_info_vartypes(LocalProcInfo, VarTypes) },
{ proc_info_varset(LocalProcInfo, VarSet) },
{ proc_info_inst_varset(LocalProcInfo, InstVarSet) },
{ pred_info_get_markers(ExportedPredInfo0, Markers) },
{ pred_info_get_aditi_owner(ExportedPredInfo0, Owner) },
{ ClassContext = constraints([], []) },
{ map__init(TVarMap) },
{ map__init(TCVarMap) },
{ varset__init(TVarSet) },
{ hlds_pred__define_new_pred(Goal, CallGoal, HeadVars, ExtraArgs,
InstMap, PredName, TVarSet, VarTypes, ClassContext, TVarMap,
TCVarMap, VarSet, InstVarSet, Markers, Owner,
address_is_not_taken, ModuleInfo1, ModuleInfo2,
LocalPredProcId) },
{ ExtraArgs = [] ->
true
;
error("magic__create_interface_proc: typeinfo arguments")
},
magic_info_set_module_info(ModuleInfo2),
% Calls in this module should be redirected to point to
% the local version.
magic_info_get_pred_map(PredMap0),
{ map__det_update(PredMap0, CPredProcId, LocalPredProcId, PredMap) },
magic_info_set_pred_map(PredMap),
%
% Add the single magic input relation to the argument list of
% the exported version.
%
{ list__index1_det(MagicVars, Index, MagicInputVar) },
{ list__index1_det(MagicTypes, Index, MagicInputType) },
{ list__index1_det(MagicModes, Index, MagicInputMode) },
{ ExportedArgModes = [MagicInputMode | ArgModes1] },
{ ExportedArgTypes = [MagicInputType | ArgTypes1] },
{ ExportedHeadVars = [MagicInputVar | HeadVars1] },
{ proc_info_set_headvars(ExportedHeadVars,
ExportedProcInfo0, ExportedProcInfo1) },
{ proc_info_set_argmodes(ExportedArgModes,
ExportedProcInfo1, ExportedProcInfo2) },
{ pred_info_set_arg_types(TVarSet, [], ExportedArgTypes,
ExportedPredInfo0, ExportedPredInfo1) },
%
% Construct the input for the call to the local version.
%
magic_info_set_pred_info(ExportedPredInfo1),
magic_info_set_proc_info(ExportedProcInfo2),
magic__interface_call_args(MagicVars, MagicTypes, MagicModes,
Index, 1, InputGoals),
magic_info_get_pred_info(ExportedPredInfo2),
magic_info_get_proc_info(ExportedProcInfo3),
{ CallGoal = _ - CallGoalInfo },
{ list__append(InputGoals, [CallGoal], ExportedConj) },
{ conj_list_to_goal(ExportedConj, CallGoalInfo, ExportedGoal) },
{ proc_info_set_goal(ExportedGoal,
ExportedProcInfo3, ExportedProcInfo) },
{ pred_info_set_import_status(exported,
ExportedPredInfo2, ExportedPredInfo) },
magic_info_get_module_info(ModuleInfo5),
{ module_info_set_pred_proc_info(AditiPredProcId,
ExportedPredInfo, ExportedProcInfo,
ModuleInfo5, ModuleInfo6) },
magic_info_set_module_info(ModuleInfo6).
%-----------------------------------------------------------------------------%
:- pred magic__interface_call_args(list(prog_var)::in, list(type)::in,
list(mode)::in, int::in, int::in, list(hlds_goal)::out,
magic_info::in, magic_info::out) is det.
magic__interface_call_args([], _, _, _, _, []) --> [].
magic__interface_call_args([MagicInput | MagicInputs], MagicTypes, MagicModes,
CalledPredIndex, CurrVar, InputGoals) -->
{ NextVar = CurrVar + 1 },
magic__interface_call_args(MagicInputs, MagicTypes, MagicModes,
CalledPredIndex, NextVar, InputGoals1),
( { CurrVar = CalledPredIndex } ->
%
% Just pass through the closure passed in
% from the calling module.
%
{ InputGoals = InputGoals1 }
;
%
% Create an empty input closure.
%
{ list__index1_det(MagicTypes, CurrVar, MagicType) },
{
type_is_higher_order(MagicType, (pure), predicate,
(aditi_bottom_up), ArgTypes1)
->
ArgTypes = ArgTypes1
;
error("magic__interface_call_args")
},
magic_info_get_proc_info(ProcInfo0),
{ proc_info_create_vars_from_types(ArgTypes, Args,
ProcInfo0, ProcInfo) },
magic_info_set_proc_info(ProcInfo),
{ fail_goal(LambdaGoal) },
{ list__index1_det(MagicModes, CurrVar, InputMode) },
magic_util__create_closure(CurrVar, MagicInput, InputMode,
LambdaGoal, [], Args, InputGoal),
{ InputGoals = [InputGoal | InputGoals1] }
).
%-----------------------------------------------------------------------------%
:- pred magic__interface_from_c(list(pred_proc_id)::in, pred_proc_id::in,
pred_proc_id::in, magic_info::in, magic_info::out) is det.
magic__interface_from_c(EntryPoints, CPredProcId, AditiPredProcId) -->
magic_info_get_module_info(ModuleInfo0),
{ module_info_globals(ModuleInfo0, Globals) },
{ globals__lookup_bool_option(Globals, aditi_only, AditiOnly) },
{ module_info_pred_proc_info(ModuleInfo0,
CPredProcId, PredInfo0, ProcInfo0) },
{ pred_info_get_markers(PredInfo0, Markers) },
{ module_info_name(ModuleInfo0, ModuleName) },
(
{ AditiOnly = no },
{ check_marker(Markers, base_relation) },
{ ModuleName = pred_info_module(PredInfo0) }
->
{ pred_info_set_import_status(exported,
PredInfo0, PredInfo1) },
{ module_info_set_pred_proc_info(CPredProcId,
PredInfo1, ProcInfo0, ModuleInfo0, ModuleInfo1) },
magic_info_set_module_info(ModuleInfo1)
;
{ PredInfo1 = PredInfo0 },
{ ModuleInfo1 = ModuleInfo0 }
),
magic_info_get_errors(Errors),
( { pred_info_is_imported(PredInfo1) } ->
[]
; { \+ set__empty(Errors) } ->
[]
; { AditiOnly = yes ; \+ list__member(CPredProcId, EntryPoints) } ->
%
% If no interface procedure is required we just throw
% away the goal. The predicate is now an ordinary Mercury
% predicate.
%
{ true_goal(Goal) },
{ proc_info_set_goal(Goal, ProcInfo0, ProcInfo) },
{ module_info_set_pred_proc_info(CPredProcId,
PredInfo1, ProcInfo, ModuleInfo1, ModuleInfo) },
magic_info_set_module_info(ModuleInfo)
;
{ magic__create_input_join_proc(CPredProcId, AditiPredProcId,
_JoinPredProcId, ModuleInfo1, ModuleInfo2) },
%
% Change the goal for the original procedure to
% call the database procedure.
%
{ aditi_builtin_ops__create_aditi_call_proc(CPredProcId,
ModuleInfo2, ModuleInfo) },
magic_info_set_module_info(ModuleInfo)
).
% Make a procedure which calls the Aditi predicate, then joins
% the result with the input and projects out the input arguments.
:- pred magic__create_input_join_proc(pred_proc_id::in, pred_proc_id::in,
pred_proc_id::out, module_info::in, module_info::out) is det.
magic__create_input_join_proc(CPredProcId, AditiPredProcId, JoinPredProcId,
ModuleInfo0, ModuleInfo) :-
module_info_pred_proc_info(ModuleInfo0, CPredProcId,
CPredInfo, CProcInfo),
proc_info_argmodes(CProcInfo, ArgModes0),
pred_info_arg_types(CPredInfo, ArgTypes),
type_util__remove_aditi_state(ArgTypes, ArgModes0, ArgModes),
partition_args(ModuleInfo0, ArgModes, ArgModes,
InputArgModes, OutputArgModes),
%
% The interface procedure on the Aditi side must have
% only one input closure argument.
%
proc_info_vartypes(CProcInfo, VarTypes0),
proc_info_headvars(CProcInfo, HeadVars0),
type_util__remove_aditi_state(ArgTypes,
HeadVars0, HeadVars),
partition_args(ModuleInfo0, ArgModes, HeadVars,
InputArgs, OutputArgs),
map__apply_to_list(InputArgs, VarTypes0, InputVarTypes),
construct_higher_order_type((pure), predicate, (aditi_bottom_up),
InputVarTypes, ClosureVarType),
list__map(magic_util__mode_to_output_mode(ModuleInfo0),
InputArgModes, MagicArgModes),
JoinProcInfo0 = CProcInfo,
proc_info_create_var_from_type(ClosureVarType, no, ClosureVar,
JoinProcInfo0, JoinProcInfo1),
%
% Build a goal to call the input closure.
%
set__list_to_set([ClosureVar | InputArgs], HOCallNonLocals),
instmap_delta_from_mode_list(InputArgs, MagicArgModes,
ModuleInfo0, HOCallDelta),
goal_info_init(HOCallNonLocals, HOCallDelta, nondet, pure,
InputGoalInfo),
list__length(InputArgs, Arity),
InputGoal = generic_call(
higher_order(ClosureVar, (pure), predicate, Arity),
InputArgs, MagicArgModes, nondet) - InputGoalInfo,
ClosureInst = ground(shared,
higher_order(pred_inst_info(predicate, MagicArgModes, nondet))),
ClosureMode = (ClosureInst -> ClosureInst),
proc_info_set_argmodes([ClosureMode | OutputArgModes],
JoinProcInfo1, JoinProcInfo2),
proc_info_set_headvars([ClosureVar | OutputArgs],
JoinProcInfo2, JoinProcInfo3),
magic__build_join_pred_info(CPredProcId, CPredInfo,
JoinProcInfo3, [ClosureVar | OutputArgs],
JoinPredProcId, JoinPredInfo, ModuleInfo0, ModuleInfo1),
%
% Build a call to the Aditi procedure.
%
AditiPredProcId = proc(AditiPredId, AditiProcId),
proc_info_goal(CProcInfo, _ - CallGoalInfo0),
% Convert input arguments to output arguments, producing
% the tests which will make up the join condition.
magic_util__create_input_test_unifications(ModuleInfo1, HeadVars,
InputArgs, ArgModes, CallArgs0, [], Tests,
CallGoalInfo0, CallGoalInfo, JoinProcInfo3, JoinProcInfo4),
( hlds_pred__pred_info_is_base_relation(CPredInfo) ->
CallArgs = CallArgs0
;
CallArgs = [ClosureVar | CallArgs0]
),
module_info_pred_info(ModuleInfo1, AditiPredId, AditiPredInfo),
PredModule = pred_info_module(AditiPredInfo),
PredName = pred_info_name(AditiPredInfo),
CallGoal = call(AditiPredId, AditiProcId, CallArgs, not_builtin,
no, qualified(PredModule, PredName)) - CallGoalInfo,
instmap_delta_from_mode_list(OutputArgs, OutputArgModes,
ModuleInfo1, GoalDelta),
set__list_to_set([ClosureVar | OutputArgs],
GoalNonLocals),
goal_info_init(GoalNonLocals, GoalDelta, nondet, pure, GoalInfo),
conj_list_to_goal([InputGoal, CallGoal | Tests], GoalInfo,
JoinGoal),
proc_info_set_goal(JoinGoal, JoinProcInfo4, JoinProcInfo),
module_info_set_pred_proc_info(JoinPredProcId,
JoinPredInfo, JoinProcInfo, ModuleInfo1, ModuleInfo).
:- pred magic__build_join_pred_info(pred_proc_id::in, pred_info::in,
proc_info::in, list(prog_var)::in, pred_proc_id::out,
pred_info::out, module_info::in, module_info::out) is det.
magic__build_join_pred_info(CPredProcId, CPredInfo, JoinProcInfo,
Args, JoinPredProcId, JoinPredInfo, !ModuleInfo) :-
proc_info_vartypes(JoinProcInfo, JoinVarTypes),
map__apply_to_list(Args, JoinVarTypes, NewArgTypes),
PredModule = pred_info_module(CPredInfo),
rl__get_c_interface_proc_name(!.ModuleInfo, CPredProcId, NewPredName),
init_markers(Markers0),
add_marker(aditi, Markers0, Markers1),
add_marker(aditi_no_memo, Markers1, Markers2),
add_marker(naive, Markers2, Markers),
ClassContext = constraints([], []),
pred_info_get_aditi_owner(CPredInfo, User),
varset__init(TVarSet), % must be empty.
term__context_init(DummyContext),
ExistQVars = [],
set__init(Assertions),
pred_info_create(PredModule, qualified(PredModule, NewPredName),
TVarSet, ExistQVars, NewArgTypes, true, DummyContext,
exported, Markers, predicate, ClassContext, User, Assertions,
JoinProcInfo, JoinProcId, JoinPredInfo),
module_info_get_predicate_table(!.ModuleInfo, Preds0),
predicate_table_insert(Preds0, JoinPredInfo, JoinPredId, Preds),
JoinPredProcId = proc(JoinPredId, JoinProcId),
module_info_set_predicate_table(Preds, !ModuleInfo).
%-----------------------------------------------------------------------------%
% Allocate a predicate to collect the input for the current predicate.
:- pred magic__create_magic_pred(pred_proc_id::in, pred_proc_id::in,
list(type)::in, list(mode)::in, list(type)::in,
list(mode)::in, inst_varset::in, maybe(int)::in,
magic_info::in, magic_info::out) is det.
magic__create_magic_pred(CPredProcId, PredProcId, MagicTypes, MagicModes,
InputTypes0, InputModes0, InstVarSet, Index) -->
magic_info_get_module_info(ModuleInfo0),
{ varset__init(VarSet0) },
{ map__init(VarTypes0) },
% Get some new vars to carry the magic input.
{ list__length(MagicTypes, NumMagicArgs) },
{ varset__new_vars(VarSet0, NumMagicArgs, MagicArgs, VarSet1) },
{ map__det_insert_from_corresponding_lists(VarTypes0, MagicArgs,
MagicTypes, VarTypes1) },
% Get some new vars for the outputs.
{ list__length(InputModes0, NumInputArgs) },
{ varset__new_vars(VarSet1, NumInputArgs, InputArgs0, VarSet2) },
{ map__det_insert_from_corresponding_lists(VarTypes1, InputArgs0,
InputTypes0, VarTypes2) },
{ list__map(magic_util__mode_to_output_mode(ModuleInfo0),
InputModes0, OutputModes0) },
{ module_info_pred_proc_info(ModuleInfo0, PredProcId,
PredInfo, _) },
{ pred_info_get_markers(PredInfo, Markers) },
{ check_marker(Markers, context) ->
% For magic context predicates, we get two copies of
% the outputs. (See the paper cited at the top of context.m)
varset__new_vars(VarSet2, NumInputArgs, InputArgs1, VarSet),
map__det_insert_from_corresponding_lists(VarTypes2,
InputArgs1, InputTypes0, VarTypes),
list__append(InputArgs0, InputArgs1, InputArgs),
list__append(InputTypes0, InputTypes0, InputTypes),
list__append(OutputModes0, OutputModes0, OutputModes),
assoc_list__from_corresponding_lists(InputArgs0, InputArgs1,
ArgsAL0),
IsContext = yes(ArgsAL0)
;
VarSet = VarSet2,
VarTypes = VarTypes2,
InputArgs = InputArgs0,
InputTypes = InputTypes0,
OutputModes = OutputModes0,
IsContext = no
},
{ list__append(MagicArgs, InputArgs, AllArgs) },
( { Index = yes(N) } ->
%
% If this predicate is an entry point to the sub-module,
% create a rule in the magic predicate to collect
% the input relation.
%
{ list__index1_det(MagicArgs, N, CurrPredVar) },
{ set__list_to_set([CurrPredVar | InputArgs0], NonLocals0) },
{ mode_list_get_final_insts(OutputModes0, ModuleInfo0,
OutputInsts0) },
{ assoc_list__from_corresponding_lists(InputArgs0,
OutputInsts0, InstAL0) },
{ instmap_delta_from_assoc_list(InstAL0, InstMapDelta0) },
{ goal_info_init(NonLocals0, InstMapDelta0,
nondet, pure, GoalInfo0) },
{ list__length(InputArgs0, Arity) },
{ Goal0 = generic_call(
higher_order(CurrPredVar, (pure), predicate, Arity),
InputArgs0, OutputModes0, nondet) - GoalInfo0 },
( { IsContext = yes(ArgsAL) } ->
% Create assignments to assign to the extra arguments.
{ magic__create_assignments(ModuleInfo0, ArgsAL,
OutputModes0, Assigns) },
{ list__append(OutputInsts0,
OutputInsts0, OutputInsts) },
{ assoc_list__from_corresponding_lists(InputArgs,
OutputInsts, InstAL) },
{ instmap_delta_from_assoc_list(InstAL, InstMapDelta) },
{ set__list_to_set([CurrPredVar | InputArgs],
NonLocals) },
{ goal_info_init(NonLocals, InstMapDelta, nondet, pure,
GoalInfo) },
{ conj_list_to_goal([Goal0 | Assigns],
GoalInfo, Goal) }
;
{ Goal = Goal0 }
)
;
% This predicate is not an entry point, so there's
% no input to collect.
{ fail_goal(Goal) }
),
{ list__append(MagicModes, OutputModes, AllArgModes) },
{ term__context_init(Context) },
% types must all be ground.
{ map__init(TVarMap) },
{ map__init(TCVarMap) },
{ proc_info_create(VarSet, VarTypes, AllArgs, AllArgModes, InstVarSet,
nondet, Goal, Context, TVarMap, TCVarMap, address_is_not_taken,
ProcInfo) },
%
% Fill in the pred_info.
%
{ CPredProcId = proc(CPredId, CProcId) },
{ module_info_pred_info(ModuleInfo0, CPredId, CPredInfo) },
{ ModuleName = pred_info_module(CPredInfo) },
magic_util__make_pred_name(CPredInfo, CProcId,
"Magic_Proc_For", no, SymName),
{ list__append(MagicTypes, InputTypes, AllArgTypes) },
{ varset__init(TVarSet) },
{ pred_info_get_aditi_owner(PredInfo, Owner) },
{ ClassConstraints = constraints([], []) },
{ ExistQVars = [] },
{ set__init(Assertions) },
{ pred_info_create(ModuleName, SymName, TVarSet, ExistQVars,
AllArgTypes, true, Context, local, Markers, predicate,
ClassConstraints, Owner, Assertions, ProcInfo, MagicProcId,
MagicPredInfo) },
{ module_info_get_predicate_table(ModuleInfo0, PredTable0) },
{ predicate_table_insert(PredTable0,
MagicPredInfo, MagicPredId, PredTable) },
{ module_info_set_predicate_table(PredTable,
ModuleInfo0, ModuleInfo) },
magic_info_set_module_info(ModuleInfo),
% Record that the magic predicate in the magic_info.
{ MagicPredProcId = proc(MagicPredId, MagicProcId) },
magic_info_get_magic_map(MagicMap0),
{ map__det_insert(MagicMap0, PredProcId, MagicPredProcId, MagicMap) },
magic_info_set_magic_map(MagicMap).
% Produce assignments to the duplicate outputs
% of a context magic predicate.
:- pred magic__create_assignments(module_info::in,
assoc_list(prog_var, prog_var)::in,
list(mode)::in, list(hlds_goal)::out) is det.
magic__create_assignments(_, [], [], []).
magic__create_assignments(_, [], [_|_], _) :-
error("magic__create_assignments").
magic__create_assignments(_, [_|_], [], _) :-
error("magic__create_assignments").
magic__create_assignments(ModuleInfo, [Arg0 - Arg | ArgsAL],
[Mode | Modes], [Goal - GoalInfo | Assigns]) :-
mode_get_insts(ModuleInfo, Mode, _, Inst),
Goal = unify(Arg, var(Arg0), (free -> Inst) - (Inst -> Inst),
assign(Arg, Arg0), unify_context(explicit, [])),
set__list_to_set([Arg0, Arg], NonLocals),
instmap_delta_from_assoc_list([Arg - Inst], Delta),
goal_info_init(NonLocals, Delta, det, pure, GoalInfo),
magic__create_assignments(ModuleInfo, ArgsAL, Modes, Assigns).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
% Put the goal for a procedure in a form suitable for processing.
:- pred magic__preprocess_proc(pred_proc_id::in, pred_info::in,
proc_info::in, proc_info::out,
magic_info::in, magic_info::out) is det.
magic__preprocess_proc(PredProcId, PredInfo, !ProcInfo) -->
{ proc_info_goal(!.ProcInfo, Goal0) },
magic_info_set_curr_pred_proc_id(PredProcId),
magic_info_set_pred_info(PredInfo),
magic_info_set_proc_info(!.ProcInfo),
{ Goal0 = _ - GoalInfo0 },
{ goal_to_disj_list(Goal0, GoalList0) },
list__map_foldl(magic__preprocess_disjunct, GoalList0, GoalList),
{ disj_list_to_goal(GoalList, GoalInfo0, Goal) },
magic_info_get_proc_info(!:ProcInfo),
{ proc_info_set_goal(Goal, !ProcInfo) }.
% Undo common structure elimination of higher-order terms in an
% attempt to avoid creating procedures with higher-order arguments
% in the case where one closure is used by multiple aggregate calls.
% Restore superhomogeneous form for database predicates by introducing
% new variables for duplicate input arguments.
% Also remove assignments of `aditi:state's and report errors
% for goals other than database calls which have an `aditi:state'
% as a nonlocal.
:- pred magic__preprocess_disjunct(hlds_goal::in, hlds_goal::out,
magic_info::in, magic_info::out) is det.
magic__preprocess_disjunct(Disjunct0, Disjunct) -->
{ map__init(HOMap0) },
{ Disjunct0 = _ - DisjInfo },
magic__preprocess_goal(Disjunct0, Disjunct1, HOMap0, _),
{ conj_list_to_goal(Disjunct1, DisjInfo, Disjunct) }.
:- pred magic__preprocess_goal(hlds_goal::in, list(hlds_goal)::out,
map(prog_var, hlds_goal)::in, map(prog_var, hlds_goal)::out,
magic_info::in, magic_info::out) is det.
magic__preprocess_goal(Goal, Goals, HOMap0, HOMap) -->
magic__preprocess_goal_2(Goal, Goals, HOMap0, HOMap),
list__foldl(magic__check_goal_nonlocals, Goals).
:- pred magic__preprocess_goal_2(hlds_goal::in, list(hlds_goal)::out,
map(prog_var, hlds_goal)::in, map(prog_var, hlds_goal)::out,
magic_info::in, magic_info::out) is det.
% Switches, if-then-elses and disjunctions involving database calls
% should have been transformed into separate procedures by dnf.m.
magic__preprocess_goal_2(Goal, [Goal], HOMap, HOMap) -->
{ Goal = disj(_) - _ }.
magic__preprocess_goal_2(Goal, [Goal], HOMap, HOMap) -->
{ Goal = switch(_, _, _) - _ }.
magic__preprocess_goal_2(Goal, [Goal], HOMap, HOMap) -->
{ Goal = if_then_else(_, _, _, _) - _ }.
magic__preprocess_goal_2(par_conj(_) - _, _, _, _) -->
{ error("Sorry, not yet implemented: parallel conjunction in Aditi procedures") }.
magic__preprocess_goal_2(generic_call(_, _, _, _) - _, _, _, _) -->
{ error("Sorry, not yet implemented: higher-order or class-method calls in Aditi procedures") }.
magic__preprocess_goal_2(foreign_proc(_, _, _, _, _, _, _) -
_, _, _, _) -->
{ error("Sorry, not yet implemented: foreign_proc calls in Aditi procedures") }.
magic__preprocess_goal_2(conj(Goals0) - GoalInfo, [conj(Goals) - GoalInfo],
HOMap0, HOMap) -->
magic__preprocess_conj(Goals0, [], Goals, HOMap0, HOMap).
magic__preprocess_goal_2(Goal0, Goals, HOMap, HOMap) -->
{ Goal0 = call(PredId, B, Args, C, D, E) - GoalInfo },
magic_info_get_module_info(ModuleInfo),
( { hlds_pred__is_aditi_aggregate(ModuleInfo, PredId) } ->
% Put the closures and the aggregate call in a sub-conjunction
% of the top-level conjunction.
magic__rename_and_generate_closures(Args, ExtraGoals,
Goal0, Goal1, HOMap),
{ list__append(ExtraGoals, [Goal1], Goals1) },
{ Goal0 = _ - GoalInfo0 },
{ conj_list_to_goal(Goals1, GoalInfo0, Goal) },
{ Goals = [Goal] }
; { hlds_pred__is_aditi_relation(ModuleInfo, PredId) } ->
% The predicates in magic_util.m to deal with input arguments
% expect that there are no duplicates.
{ set__init(SeenArgs) },
magic__preprocess_call_args(Args, NewArgs, SeenArgs,
[], IntroducedArgs, [], ExtraGoals),
{ goal_info_get_nonlocals(GoalInfo, NonLocals) },
{ set__insert_list(NonLocals, IntroducedArgs, NewNonLocals) },
{ goal_info_set_nonlocals(GoalInfo,
NewNonLocals, NewGoalInfo) },
{ NewCall = call(PredId, B, NewArgs, C, D, E) - NewGoalInfo },
{ list__append(ExtraGoals, [NewCall], Goals) }
;
{ Goals = [Goal0] }
).
magic__preprocess_goal_2(some(Vars, CanRemove, Goal0) - Info,
[some(Vars, CanRemove, Goal) - Info], HOMap0, HOMap) -->
{ Goal0 = _ - SomeGoalInfo },
magic__preprocess_goal(Goal0, SomeGoals, HOMap0, HOMap),
{ conj_list_to_goal(SomeGoals, SomeGoalInfo, Goal) }.
magic__preprocess_goal_2(not(Goal0) - Info, [not(Goal) - Info],
HOMap0, HOMap) -->
{ Goal0 = _ - NegGoalInfo },
magic__preprocess_goal(Goal0, NegGoals, HOMap0, HOMap),
{ conj_list_to_goal(NegGoals, NegGoalInfo, Goal) }.
magic__preprocess_goal_2(Goal0, Goals, HOMap0, HOMap) -->
{ Goal0 = unify(_, _, _, Uni, _) - GoalInfo },
(
{ Uni = construct(Var, pred_const(_, _, _), Args, _, _, _, _) }
->
% Collect up the closure construction so that it can be
% placed next to the aggregate goal that uses it.
%
% XXX What about if someone puts a closure inside
% a structure? At the moment we don't handle it and
% we don't give an error message.
magic_info_get_proc_info(ProcInfo),
(
{ Args = [] }
->
[]
;
{ Args = [Arg] },
{ proc_info_vartypes(ProcInfo, VarTypes) },
{ map__lookup(VarTypes, Arg, ArgType) },
{ type_is_aditi_state(ArgType) }
->
[]
;
% XXX we don't yet allow curried arguments.
{ goal_info_get_context(GoalInfo, Context) },
magic_info_get_curr_pred_proc_id(PredProcId),
magic_info_get_errors(Errors0),
{ Error = curried_argument(PredProcId) - Context },
{ set__insert(Errors0, Error, Errors) },
magic_info_set_errors(Errors)
),
{ map__det_insert(HOMap0, Var, Goal0, HOMap) },
{ Goals = [] }
;
{ Uni = assign(Var1, Var2) }
->
magic_info_get_proc_info(ProcInfo),
{ proc_info_vartypes(ProcInfo, VarTypes) },
{ map__lookup(VarTypes, Var1, Var1Type) },
( { type_is_aditi_state(Var1Type) } ->
% Remove assignments of `aditi:state's.
{ HOMap = HOMap0 },
{ Goals = [] }
; { map__search(HOMap0, Var2, Entry) } ->
{ Goals = [] },
{ map__det_insert(HOMap0, Var1, Entry, HOMap) }
;
{ Goals = [Goal0] },
{ HOMap = HOMap0 }
)
;
{ Goals = [Goal0] },
{ HOMap = HOMap0 }
).
magic__preprocess_goal_2(shorthand(_) - _, _, _, _) -->
% these should have been expanded out by now
{ error("magic__preprocess_goal_2: unexpected shorthand") }.
% Introduce new variables and assignments to them for any
% duplicates in the list.
:- pred magic__preprocess_call_args(list(prog_var)::in, list(prog_var)::out,
set(prog_var)::in, list(prog_var)::in, list(prog_var)::out,
list(hlds_goal)::in, list(hlds_goal)::out,
magic_info::in, magic_info::out) is det.
magic__preprocess_call_args([], [], _, IntroducedArgs,
IntroducedArgs, ExtraGoals, ExtraGoals) --> [].
magic__preprocess_call_args([Arg | Args], [NewArg | NewArgs], SeenArgs,
IntroducedArgs0, IntroducedArgs, ExtraGoals0, ExtraGoals) -->
( { set__member(Arg, SeenArgs) } ->
{ SeenArgs1 = SeenArgs },
magic_info_get_proc_info(ProcInfo0),
{ proc_info_vartypes(ProcInfo0, VarTypes) },
{ map__lookup(VarTypes, Arg, ArgType) },
{ proc_info_create_var_from_type(ArgType, no, NewArg,
ProcInfo0, ProcInfo) },
magic_info_set_proc_info(ProcInfo),
{ IntroducedArgs1 = [NewArg | IntroducedArgs0] },
{ in_mode(InMode) },
{ out_mode(OutMode) },
{ Inst = ground(shared, none) },
{ set__list_to_set([Arg, NewArg], NonLocals) },
{ instmap_delta_from_assoc_list([NewArg - Inst], Delta) },
{ goal_info_init(NonLocals, Delta, det, pure, GoalInfo) },
{ ExtraGoal = unify(NewArg, var(Arg), OutMode - InMode,
assign(NewArg, Arg), unify_context(explicit, []))
- GoalInfo },
{ ExtraGoals1 = [ExtraGoal | ExtraGoals0] }
;
{ NewArg = Arg },
{ set__insert(SeenArgs, Arg, SeenArgs1) },
{ IntroducedArgs1 = IntroducedArgs0 },
{ ExtraGoals1 = ExtraGoals0 }
),
magic__preprocess_call_args(Args, NewArgs, SeenArgs1, IntroducedArgs1,
IntroducedArgs, ExtraGoals1, ExtraGoals).
:- pred magic__preprocess_conj(list(hlds_goal)::in, list(hlds_goal)::in,
list(hlds_goal)::out, map(prog_var, hlds_goal)::in,
map(prog_var, hlds_goal)::out, magic_info::in, magic_info::out) is det.
magic__preprocess_conj([], RevGoals, Goals, HOMap, HOMap) -->
{ list__reverse(RevGoals, Goals) }.
magic__preprocess_conj([Goal0 | Goals0], RevGoals0, Goals, HOMap0, HOMap) -->
magic__preprocess_goal(Goal0, Goals1, HOMap0, HOMap1),
{ list__reverse(Goals1, RevGoals1) },
{ list__append(RevGoals1, RevGoals0, RevGoals) },
magic__preprocess_conj(Goals0, RevGoals, Goals, HOMap1, HOMap).
% If the goal is not a database call and does not contain
% a database call, it cannot have an `aditi:state' as a non-local.
:- pred magic__check_goal_nonlocals(hlds_goal::in,
magic_info::in, magic_info::out) is det.
magic__check_goal_nonlocals(Goal) -->
magic_info_get_module_info(ModuleInfo),
magic_info_get_pred_map(PredMap),
(
% We check inside not, some and conj goals for calls, so don't
% report errors at the top-level of those goals.
{
Goal = not(_) - _
;
Goal = some(_, _, _) - _
;
Goal = conj(_) - _
;
Goal = call(_, _, _, _, _, _) - _,
magic_util__goal_is_aditi_call(ModuleInfo, PredMap,
Goal, _, _)
;
Goal = unify(_, _, _, Uni, _) - _,
Uni = construct(_, pred_const(PredId, ProcId, _),
_, _, _, _, _),
% XXX once the implementation of aggregates has
% been updated to use `aditi_bottom_up' closures,
% this can be done by just checking the eval_method.
(
map__contains(PredMap, proc(PredId, ProcId))
;
hlds_pred__is_aditi_relation(ModuleInfo,
PredId)
)
}
->
[]
;
{ Goal = _ - GoalInfo },
% We don't use the non-locals because in some circumstances
% involving mode analysis or simplification optimizing
% away unifications that set can be an overestimate.
{ quantification__goal_vars(Goal, GoalVars0) },
{ set__to_sorted_list(GoalVars0, GoalVars) },
magic_info_get_proc_info(ProcInfo),
{ proc_info_vartypes(ProcInfo, VarTypes) },
{ IsAditiVar = (pred(Var::in) is semidet :-
map__lookup(VarTypes, Var, Type),
type_is_aditi_state(Type)
) },
{ list__filter(IsAditiVar, GoalVars, IllegalNonLocals) },
( { IllegalNonLocals = [] } ->
[]
;
magic_info_get_errors(Errors0),
{ goal_info_get_context(GoalInfo, Context) },
magic_info_get_curr_pred_proc_id(PredProcId),
{ proc_info_varset(ProcInfo, VarSet) },
{ Error = non_removeable_aditi_state(PredProcId,
VarSet, IllegalNonLocals) - Context },
{ set__insert(Errors0, Error, Errors) },
magic_info_set_errors(Errors)
)
).
%-----------------------------------------------------------------------------%
% Generate goals to create the closures needed by this call.
:- pred magic__rename_and_generate_closures(list(prog_var)::in,
list(hlds_goal)::out, hlds_goal::in, hlds_goal::out,
map(prog_var, hlds_goal)::in, magic_info::in, magic_info::out) is det.
magic__rename_and_generate_closures([], [], Goal, Goal, _) --> [].
magic__rename_and_generate_closures([Arg | Args], ExtraGoals,
Goal0, Goal, HOMap) -->
magic__rename_and_generate_closures(Args, ExtraGoals1,
Goal0, Goal1, HOMap),
( { map__search(HOMap, Arg, ClosureGoal0) } ->
magic_info_get_proc_info(ProcInfo0),
{ proc_info_vartypes(ProcInfo0, VarTypes0) },
{ map__lookup(VarTypes0, Arg, Type) },
{ proc_info_create_var_from_type(Type, no, NewArg,
ProcInfo0, ProcInfo) },
magic_info_set_proc_info(ProcInfo),
{ map__init(Subn0) },
{ map__det_insert(Subn0, Arg, NewArg, Subn) },
{ goal_util__rename_vars_in_goal(ClosureGoal0,
Subn, ClosureGoal) },
{ goal_util__rename_vars_in_goal(Goal1, Subn, Goal) },
{ ExtraGoals = [ClosureGoal | ExtraGoals1] }
;
{ ExtraGoals = ExtraGoals1 },
{ Goal = Goal1 }
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- pred magic__process_scc(aditi_scc::in, magic_info::in,
magic_info::out, io__state::di, io__state::uo) is det.
magic__process_scc(aditi_scc(SCC0, _), Info0, Info) -->
{ list__condense(SCC0, SCC) },
{ magic_info_set_scc(SCC, Info0, Info1) },
{ list__foldl(magic__process_proc, SCC, Info1, Info) }.
%-----------------------------------------------------------------------------%
:- pred magic__process_proc(pred_proc_id::in,
magic_info::in, magic_info::out) is det.
magic__process_proc(PredProcId0) -->
magic_info_get_pred_map(PredMap),
{ map__search(PredMap, PredProcId0, PredProcId1) ->
PredProcId = PredProcId1
;
PredProcId = PredProcId0
},
magic_info_get_module_info(ModuleInfo0),
{ module_info_pred_proc_info(ModuleInfo0, PredProcId,
PredInfo0, ProcInfo0) },
(
{ pred_info_is_imported(PredInfo0)
; pred_info_is_pseudo_imported(PredInfo0)
}
->
[]
;
magic_info_set_curr_pred_proc_id(PredProcId),
magic_info_set_pred_info(PredInfo0),
magic_info_set_proc_info(ProcInfo0),
magic_info_get_magic_proc_info(MagicProcInfo),
{ map__lookup(MagicProcInfo, PredProcId, ThisProcInfo) },
{ ThisProcInfo = magic_proc_info(OldArgModes, MagicInputs,
_, _, _) },
magic_info_set_magic_vars(MagicInputs),
{ set__init(ErrorVars) },
magic_info_set_error_vars(ErrorVars),
{ proc_info_headvars(ProcInfo0, HeadVars) },
{ list__length(MagicInputs, NumMagicInputs) },
{ list__drop(NumMagicInputs, HeadVars, OldHeadVars) ->
partition_args(ModuleInfo0,
OldArgModes, OldHeadVars, Inputs, Outputs)
;
error("magic__process_proc: list__drop failed")
},
{ pred_info_get_markers(PredInfo0, Markers) },
{ proc_info_goal(ProcInfo0, Goal0) },
{ Goal0 = _ - GoalInfo0 },
{ goal_to_disj_list(Goal0, DisjList0) },
( { check_marker(Markers, context) } ->
context__process_disjuncts(PredProcId0, Inputs,
Outputs, DisjList0, DisjList)
;
{ set__list_to_set(HeadVars, HeadVarSet) },
magic__process_disjuncts(HeadVarSet,
DisjList0, DisjList)
),
{ disj_list_to_goal(DisjList, GoalInfo0, Goal) },
magic_info_get_pred_info(PredInfo),
magic_info_get_proc_info(ProcInfo1),
{ proc_info_set_goal(Goal, ProcInfo1, ProcInfo) },
magic_info_get_module_info(ModuleInfo1),
{ module_info_set_pred_proc_info(PredProcId,
PredInfo, ProcInfo, ModuleInfo1, ModuleInfo) },
magic_info_set_module_info(ModuleInfo)
).
%-----------------------------------------------------------------------------%
:- pred magic__process_disjuncts(set(prog_var)::in, list(hlds_goal)::in,
list(hlds_goal)::out, magic_info::in, magic_info::out) is det.
magic__process_disjuncts(_, [], []) --> [].
magic__process_disjuncts(HeadVars, [Disjunct0 | Disjuncts0],
[Disjunct | Disjuncts]) -->
magic__process_disjunct(HeadVars, Disjunct0, Disjunct),
magic__process_disjuncts(HeadVars, Disjuncts0, Disjuncts).
:- pred magic__process_disjunct(set(prog_var)::in, hlds_goal::in,
hlds_goal::out, magic_info::in, magic_info::out) is det.
magic__process_disjunct(HeadVars, Disjunct0, Disjunct) -->
{ Disjunct0 = _ - DisjInfo },
{ goal_to_conj_list(Disjunct0, GoalList0) },
{ list__reverse(GoalList0, RevGoalList0) },
magic__get_next_db_pred(RevGoalList0, BeforeGoals,
MaybeDBCall, [], AfterGoals),
( { MaybeDBCall = yes(DBCall1) } ->
{ magic_util__db_call_nonlocals(DBCall1, NonLocals1) },
{ goal_list_nonlocals(AfterGoals, AfterNonLocals) },
{ set__union(HeadVars, AfterNonLocals, SubConjNonLocals0) },
{ set__union(SubConjNonLocals0, NonLocals1,
SubConjNonLocals1) },
magic_util__restrict_nonlocals(SubConjNonLocals1,
SubConjNonLocals),
magic__process_disjunct_2(BeforeGoals, DBCall1,
SubConjNonLocals, GoalList1),
{ list__append(GoalList1, AfterGoals, GoalList) }
;
magic__create_magic_call(MagicCall),
{ GoalList = [MagicCall | GoalList0] }
),
{ conj_list_to_goal(GoalList, DisjInfo, Disjunct) }.
% Search backwards through the goal list for a disjunct.
% When a call is found, recursively process the goals before
% it.
:- pred magic__process_disjunct_2(list(hlds_goal)::in, db_call::in,
set(prog_var)::in, list(hlds_goal)::out,
magic_info::in, magic_info::out) is det.
magic__process_disjunct_2(RevBeforeGoals1, DBCall1, NonLocals0, Goals) -->
% Find the next database call.
magic__get_next_db_pred(RevBeforeGoals1, RevBeforeGoals2,
MaybeDBCall2, [], AfterGoals2),
( { MaybeDBCall2 = yes(DBCall2) } ->
% Recursively process the goals before the call we just found.
{ magic_util__db_call_nonlocals(DBCall2, CallNonLocals2) },
{ goal_list_nonlocals(AfterGoals2, AfterNonLocals) },
{ set__union(NonLocals0, AfterNonLocals, NonLocals1) },
{ set__union(NonLocals1, CallNonLocals2, NonLocals2) },
magic_util__restrict_nonlocals(NonLocals2, NonLocals),
magic__process_disjunct_2(RevBeforeGoals2, DBCall2,
NonLocals, Goals2),
{ list__append(Goals2, AfterGoals2, Goals3) },
% Turn those goals into a supplementary predicate, and
% use that to create the input for the first call.
magic_util__setup_call(Goals3, DBCall1, NonLocals0, Goals)
;
% We've run out of calls to process, so get the magic
% input for this procedure to feed the other calls.
magic__create_magic_call(MagicCall),
{ list__reverse(RevBeforeGoals1, BeforeGoals1) },
magic_util__setup_call([MagicCall | BeforeGoals1],
DBCall1, NonLocals0, Goals)
).
%-----------------------------------------------------------------------------%
% Skip along the reversed list of goals to the first database call,
% returning the list of goals before and after the call as well.
:- pred magic__get_next_db_pred(list(hlds_goal)::in, list(hlds_goal)::out,
maybe(db_call)::out, list(hlds_goal)::in,
list(hlds_goal)::out, magic_info::in, magic_info::out) is det.
magic__get_next_db_pred([], [], no, Goals, Goals) --> [].
magic__get_next_db_pred([Goal | RevGoals], RevBeforeGoals,
MaybeCall, AfterGoals0, AfterGoals) -->
magic_info_get_module_info(ModuleInfo),
magic_info_get_pred_map(PredMap),
(
{ magic_util__goal_is_aditi_call(ModuleInfo, PredMap,
Goal, Call, AfterGoals1) }
->
{ MaybeCall = yes(Call) },
{ RevBeforeGoals = RevGoals },
{ list__append(AfterGoals1, AfterGoals0, AfterGoals) }
;
magic__get_next_db_pred(RevGoals, RevBeforeGoals,
MaybeCall, [Goal | AfterGoals0], AfterGoals)
).
%-----------------------------------------------------------------------------%
% Create a call to the magic procedure for the current procedure.
:- pred magic__create_magic_call(hlds_goal::out,
magic_info::in, magic_info::out) is det.
magic__create_magic_call(MagicCall) -->
magic_util__magic_call_info(MagicPredId, MagicProcId, PredName,
InputRels, InputArgs, MagicOutputModes),
{ list__append(InputRels, InputArgs, MagicArgs) },
{ set__list_to_set(MagicArgs, NonLocals) },
magic_info_get_module_info(ModuleInfo),
{ instmap_delta_from_mode_list(InputArgs, MagicOutputModes,
ModuleInfo, InstMapDelta) },
{ goal_info_init(NonLocals, InstMapDelta, nondet, pure, GoalInfo) },
{ MagicCall = call(MagicPredId, MagicProcId, MagicArgs,
not_builtin, no, PredName) - GoalInfo }.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%