for review: Aditi [5]
Simon Taylor
stayl at cs.mu.OZ.AU
Tue Jul 7 13:44:09 AEST 1998
%-----------------------------------------------------------------------------%
% Copyright (C) 1998 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
%
% Supplementary magic sets transformation, sort of as described in
% Beeri, C. and Ramakrishnan, R.,
% On the power of magic,
% Journal of Logic Programming,
% volume 10, 1991, pp. 255-299.
%
%-----------------------------------------------------------------------------%
% 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 = base_type_info("", "int", 0), % input type_info
% TypeInfo_14 = base_type_info("", "int", 0), % output type_info
%
% % anc__do_aditi_call has the `aditi_interface' marker,
% % which causes call_gen.m to generate it as a call
% % to `do_nondet_aditi_call', which is hand-coded C
% % in extras/aditi/aditi.m
% anc__do_aditi_call(V_15, V_16, V_17, V_18,
% TypeInfo_13, HeadVar__2,
% TypeInfo_14, 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.
%
% 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.
%
%-----------------------------------------------------------------------------%
:- module magic.
:- interface.
:- import_module 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 magic_util, context.
:- import_module dependency_graph, hlds_pred, hlds_goal, hlds_data, prog_data.
:- import_module passes_aux, mode_util, (inst), instmap, rl_gen, rl, rl_out.
:- import_module globals, options, hlds_out, prog_out, goal_util, type_util.
:- import_module polymorphism.
:- import_module int, list, map, require, set, std_util, string, term, varset.
:- import_module assoc_list, bool, simplify.
magic__process_module(ModuleInfo0, ModuleInfo) -->
{ module_info_ensure_aditi_dependency_info(ModuleInfo0, ModuleInfo1) },
{ module_info_aditi_dependency_ordering(ModuleInfo1, Ordering) },
{ magic_info_init(ModuleInfo1, Info0) },
{ module_info_predids(ModuleInfo1, PredIds) },
{ magic__process_imported_procs(PredIds, 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__preprocess_sub_module, Ordering, Info1, Info2) },
% Do the transformation.
maybe_write_string(Verbose, "% processing module\n"),
maybe_flush_output(Verbose),
list__foldl2(magic__process_sub_module, Ordering, Info2, Info3),
{ magic_info_get_module_info(ModuleInfo2, Info3, Info) },
{ magic_info_get_errors(Errors, Info, _) },
{ set__to_sorted_list(Errors, ErrorList) },
( { ErrorList = [] } ->
{ ModuleInfo3 = ModuleInfo2 }
;
globals__io_lookup_bool_option(verbose_errors, VerboseErrors),
magic_util__report_errors(ErrorList,
ModuleInfo2, VerboseErrors),
{ module_info_incr_errors(ModuleInfo2, ModuleInfo3) },
io__set_exit_status(1)
),
% New procedures were created, so the dependency_info
% is out of date.
{ module_info_clobber_dependency_info(ModuleInfo3, ModuleInfo) }.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
% Convert imported Aditi procedures for the magic sets interface.
:- pred magic__process_imported_procs(list(pred_id)::in,
magic_info::in, magic_info::out) is det.
magic__process_imported_procs([]) --> [].
magic__process_imported_procs([PredId | PredIds]) -->
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) }
->
{ pred_info_procids(PredInfo, ProcIds) },
magic__process_imported_procs_2(PredId, ProcIds)
;
{ hlds_pred__pred_info_is_base_relation(PredInfo) }
->
{ pred_info_procids(PredInfo, ProcIds) },
list__foldl(magic__process_base_relation(PredId), ProcIds)
;
[]
),
magic__process_imported_procs(PredIds).
:- 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_sub_module_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),
{ map__search(PredMap, proc(PredId0, ProcId0), PredProcId1) ->
PredProcId = PredProcId1
;
PredProcId = proc(PredId0, ProcId0)
},
magic__do_process_base_relation(proc(PredId0, ProcId0), PredProcId).
:- pred magic__do_process_base_relation(pred_proc_id::in, pred_proc_id::in,
magic_info::in, magic_info::out) is det.
magic__do_process_base_relation(CPredProcId, PredProcId) -->
magic_info_get_module_info(ModuleInfo0),
{ module_info_pred_proc_info(ModuleInfo0, PredProcId,
PredInfo0, ProcInfo0) },
% Remove aditi:states, convert arguments to output.
{ pred_info_arg_types(PredInfo0, TVarSet, ArgTypes0) },
{ proc_info_argmodes(ProcInfo0, ArgModes0) },
{ proc_info_headvars(ProcInfo0, HeadVars0) },
{ magic_util__remove_aditi_state(ArgTypes0, ArgTypes0, ArgTypes) },
{ magic_util__remove_aditi_state(ArgTypes0, ArgModes0, ArgModes1) },
{ list__map(magic_util__mode_to_output_mode(ModuleInfo0),
ArgModes1, ArgModes) },
{ magic_util__remove_aditi_state(ArgTypes0, HeadVars0, HeadVars) },
{ pred_info_set_arg_types(PredInfo0, TVarSet, ArgTypes, PredInfo) },
{ proc_info_set_argmodes(ProcInfo0, ArgModes, ProcInfo1) },
{ proc_info_set_headvars(ProcInfo1, HeadVars, ProcInfo) },
{ module_info_set_pred_proc_info(ModuleInfo0,
PredProcId, PredInfo, ProcInfo, 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_sub_module(aditi_sub_module::in,
magic_info::in, magic_info::out) is det.
magic__preprocess_sub_module(aditi_sub_module(SubModule0, EntryPoints)) -->
list__foldl(magic__check_scc, SubModule0),
{ list__condense(SubModule0, SubModule) },
magic__get_sub_module_inputs(EntryPoints, InputTypes, InputModes),
list__foldl(magic__adjust_pred_info(EntryPoints,
InputTypes, InputModes), SubModule).
:- pred magic__check_scc(list(pred_proc_id)::in,
magic_info::in, magic_info::out) is det.
magic__check_scc(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(
lambda([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(
lambda([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).
%-----------------------------------------------------------------------------%
% Work out the types and modes of the input relations that need
% to be passed around the sub-module.
:- pred magic__get_sub_module_inputs(list(pred_proc_id)::in, list(type)::out,
list(mode)::out, magic_info::in, magic_info::out) is det.
magic__get_sub_module_inputs([], [], []) --> [].
magic__get_sub_module_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) },
{ magic_util__remove_aditi_state(ArgTypes0, ArgTypes0, ArgTypes) },
{ magic_util__remove_aditi_state(ArgTypes0, ArgModes0, ArgModes) },
{ partition_args(ModuleInfo, ArgModes, ArgModes, InputModes, _) },
{ partition_args(ModuleInfo, ArgModes, ArgTypes, InputTypes, _) },
{ term__context_init(Context) },
{ Type = term__functor(term__atom("pred"), InputTypes, Context) },
{ GetOutputMode = lambda([ArgMode::in, OutputMode::out] is det, (
mode_get_insts(ModuleInfo, ArgMode, _, OutputInst),
OutputMode = (free -> OutputInst)
)) },
{ list__map(GetOutputMode, InputModes, InputRelModes) },
{ Inst = ground(unique, yes(pred_inst_info(predicate,
InputRelModes, nondet))) },
{ Mode = (Inst -> Inst) },
magic__get_sub_module_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_info(ModuleInfo0, PredId, PredInfo0) },
magic_info_set_curr_pred_proc_id(proc(PredId, ProcId)),
{ pred_info_procedures(PredInfo0, Procs0) },
{ map__lookup(Procs0, ProcId, ProcInfo0) },
%
% Create a new pred_info for the procedure.
%
% Produce a unique name for the procedure.
{ pred_info_module(PredInfo0, Module) },
{ pred_info_name(PredInfo0, Name) },
{ pred_info_get_markers(PredInfo0, Markers) },
{ proc_id_to_int(ProcId, ProcInt) },
( { check_marker(Markers, base_relation) } ->
{ NewName = Name }
;
{ string__format("%s__aditi%i",
[s(Name), i(ProcInt)], NewName) }
),
{ pred_info_arg_types(PredInfo0, TVarSet, ArgTypes) },
{ pred_info_context(PredInfo0, Context) },
{ pred_info_import_status(PredInfo0, Status) },
{ pred_info_get_is_pred_or_func(PredInfo0, PredOrFunc) },
{ pred_info_get_aditi_owner(PredInfo0, Owner) },
% type classes aren't supported in Aditi.
{ ClassConstraints = [] },
{ pred_info_create(Module, qualified(Module, NewName),
TVarSet, ArgTypes, true, Context, Status, Markers,
PredOrFunc, ClassConstraints, Owner, ProcInfo0,
NewProcId, NewPredInfo) },
{ module_info_get_predicate_table(ModuleInfo0, PredTable0) },
{ predicate_table_insert(PredTable0, NewPredInfo, NewPredId,
PredTable) },
{ module_info_set_predicate_table(ModuleInfo0, PredTable,
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__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
},
magic__adjust_args(CPredProcId, AditiPredProcId, InterfaceRequired,
Index, MagicTypes, MagicModes, PredInfo0, ProcInfo1,
InputArgTypes, InputArgModes),
( { pred_info_is_imported(PredInfo0) } ->
[]
;
% Create a new procedure to collect the input
% for the current procedure.
magic__create_magic_pred(CPredProcId, AditiPredProcId,
MagicTypes, MagicModes, InputArgTypes, InputArgModes,
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, AditiPredProcId).
%-----------------------------------------------------------------------------%
% Given a pred_info and a proc_info return two versions of each, one
% with just the `aditi:state' removed, and one with the `aditi:state'
% removed and the magic input arguments for the sub-module added.
:- 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,
magic_info::in, magic_info::out) is det.
magic__adjust_args(CPredProcId, AditiPredProcId, InterfaceRequired,
MaybeIndex, MagicTypes, MagicModes, PredInfo0, ProcInfo0,
InputArgTypes, InputArgModes) -->
%
% Check that the argument types and modes
% are legal for Aditi procedures.
%
{ pred_info_arg_types(PredInfo0, TVarSet, 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.
%
{ magic_util__remove_aditi_state(ArgTypes0, ArgTypes0, ArgTypes1) },
{ magic_util__remove_aditi_state(ArgTypes0, HeadVars0, HeadVars1) },
{ magic_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(ProcInfo0, MagicTypes,
MagicVars, 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(ProcInfo1, Goal0 - GoalInfo, 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(ProcInfo2, nondet, ProcInfo3) },
{ partition_args(ModuleInfo0, ArgModes1,
ArgModes1, InputArgModes, _) },
{ partition_args(ModuleInfo0, ArgModes1,
ArgTypes1, InputArgTypes, _) },
{ ThisProcInfo = magic_proc_info(ArgModes1, MagicVars,
MagicTypes, MagicModes, MaybeIndex) },
magic_info_get_magic_proc_info(MagicProcInfo0),
{ map__det_insert(MagicProcInfo0, AditiPredProcId,
ThisProcInfo, MagicProcInfo) },
magic_info_set_magic_proc_info(MagicProcInfo),
{ pred_info_set_arg_types(PredInfo0, TVarSet, ArgTypes, PredInfo) },
{ proc_info_set_headvars(ProcInfo3, HeadVars, ProcInfo4) },
{ proc_info_set_argmodes(ProcInfo4, ArgModes, ProcInfo) },
( { InterfaceRequired = yes(Index) } ->
magic__create_interface_proc(Index, CPredProcId,
AditiPredProcId, PredInfo0, ProcInfo3, ProcInfo,
HeadVars1, ArgTypes1, ArgModes1,
MagicVars, MagicTypes, MagicModes)
;
magic_info_get_module_info(ModuleInfo5),
{ module_info_set_pred_proc_info(ModuleInfo5, AditiPredProcId,
PredInfo, ProcInfo, ModuleInfo) },
magic_info_set_module_info(ModuleInfo)
).
%
% 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(var)::in, list(type)::in, list(mode)::in,
list(var)::in, list(type)::in, list(mode)::in,
magic_info::in, magic_info::out) is det.
magic__create_interface_proc(Index, CPredProcId, AditiPredProcId,
ExportedPredInfo0, ExportedProcInfo0, LocalProcInfo,
HeadVars1, ArgTypes1, ArgModes1, MagicVars,
MagicTypes, MagicModes) -->
%
% Create the local version.
%
{ proc_info_goal(LocalProcInfo, Goal) },
magic_info_get_module_info(ModuleInfo1),
{ proc_info_get_initial_instmap(LocalProcInfo, ModuleInfo1, InstMap) },
{ pred_info_name(ExportedPredInfo0, PredName0) },
{ string__append(PredName0, "__local", PredName) },
{ proc_info_headvars(LocalProcInfo, HeadVars) },
{ proc_info_vartypes(LocalProcInfo, VarTypes) },
{ proc_info_varset(LocalProcInfo, VarSet) },
{ pred_info_get_markers(ExportedPredInfo0, Markers) },
{ pred_info_get_aditi_owner(ExportedPredInfo0, Owner) },
{ ClassContext = [] },
{ map__init(TVarMap) },
{ map__init(TCVarMap) },
{ varset__init(TVarSet) },
{ hlds_pred__define_new_pred(Goal, CallGoal, HeadVars, InstMap,
PredName, TVarSet, VarTypes, ClassContext, TVarMap,
TCVarMap, VarSet, Markers, Owner,
ModuleInfo1, ModuleInfo2, LocalPredProcId) },
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(ExportedProcInfo0,
ExportedHeadVars, ExportedProcInfo1) },
{ proc_info_set_argmodes(ExportedProcInfo1,
ExportedArgModes, ExportedProcInfo2) },
{ pred_info_set_arg_types(ExportedPredInfo0, TVarSet, ExportedArgTypes,
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(ExportedProcInfo3,
ExportedGoal, ExportedProcInfo) },
{ pred_info_set_import_status(ExportedPredInfo2, exported,
ExportedPredInfo) },
magic_info_get_module_info(ModuleInfo5),
{ module_info_set_pred_proc_info(ModuleInfo5, AditiPredProcId,
ExportedPredInfo, ExportedProcInfo, ModuleInfo6) },
magic_info_set_module_info(ModuleInfo6).
%-----------------------------------------------------------------------------%
:- pred magic__interface_call_args(list(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 is 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, predicate, ArgTypes1) ->
ArgTypes = ArgTypes1
;
error("magic__interface_call_args")
},
magic_info_get_proc_info(ProcInfo0),
{ proc_info_create_vars_from_types(ProcInfo0, ArgTypes,
Args, 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) },
(
{ check_marker(Markers, base_relation) },
{ pred_info_module(PredInfo0, ModuleName) }
->
{ pred_info_set_import_status(PredInfo0,
exported, PredInfo1) },
{ module_info_set_pred_proc_info(ModuleInfo0, CPredProcId,
PredInfo1, ProcInfo0, 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 and set the import_status to imported.
%
{ true_goal(Goal) },
{ proc_info_set_goal(ProcInfo0, Goal, ProcInfo) },
{ pred_info_set_import_status(PredInfo1, imported, PredInfo) },
{ module_info_set_pred_proc_info(ModuleInfo1, CPredProcId,
PredInfo, ProcInfo, ModuleInfo) },
magic_info_set_module_info(ModuleInfo)
;
magic__create_input_join_proc(CPredProcId, AditiPredProcId,
JoinPredProcId),
%
% Create a procedure which is just a synonym
% for do_*_aditi_call.
%
magic__create_aditi_call_proc(CPredProcId, JoinPredProcId)
).
% 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, magic_info::in, magic_info::out) is det.
magic__create_input_join_proc(CPredProcId, AditiPredProcId, JoinPredProcId) -->
magic_info_get_module_info(ModuleInfo0),
{ module_info_pred_proc_info(ModuleInfo0, CPredProcId,
CPredInfo, CProcInfo) },
{ proc_info_argmodes(CProcInfo, ArgModes0) },
{ pred_info_arg_types(CPredInfo, _, ArgTypes) },
{ magic_util__remove_aditi_state(ArgTypes, ArgModes0, ArgModes) },
{ partition_args(ModuleInfo0, ArgModes, ArgModes,
InputArgModes, OutputArgModes) },
(
% If the Aditi procedure has no inputs we don't need to
% do anything unless it is a base relation.
{ InputArgModes = [] },
{ \+ hlds_pred__pred_info_is_base_relation(CPredInfo) }
->
{ JoinPredProcId = AditiPredProcId }
;
{ InputArgModes = [] }
->
% No join with the input is needed for a base relation
% with no input arguments.
{ proc_info_headvars(CProcInfo, HeadVars0) },
{ magic_util__remove_aditi_state(ArgTypes,
HeadVars0, HeadVars) },
% Build a call to the original C proc.
{ set__list_to_set(HeadVars, NonLocals) },
{ instmap_delta_from_mode_list(HeadVars0, ArgModes0,
ModuleInfo0, Delta) },
{ proc_info_inferred_determinism(CProcInfo, Detism) },
{ goal_info_init(NonLocals, Delta, Detism, CallGoalInfo) },
{ pred_info_module(CPredInfo, PredModule) },
{ pred_info_name(CPredInfo, PredName) },
{ AditiPredProcId = proc(AditiPredId, AditiProcId) },
{ CallGoal = call(AditiPredId, AditiProcId, HeadVars,
not_builtin, no, qualified(PredModule, PredName))
- CallGoalInfo },
{ JoinProcInfo0 = CProcInfo },
{ proc_info_set_headvars(JoinProcInfo0,
HeadVars, JoinProcInfo1) },
{ proc_info_set_argmodes(JoinProcInfo1,
ArgModes, JoinProcInfo2) },
% Imported procedures such as base relations have nothing
% in their vartypes field, so create it here.
{ map__from_corresponding_lists(HeadVars0,
ArgTypes, VarTypes) },
{ proc_info_set_vartypes(JoinProcInfo2,
VarTypes, JoinProcInfo3) },
{ proc_info_set_goal(JoinProcInfo3, CallGoal, JoinProcInfo) },
magic__build_join_pred_info(CPredInfo, JoinProcInfo,
HeadVars, JoinPredProcId, _JoinPredInfo1)
;
% The interface procedure on the Aditi side must have
% only one input closure argument.
{ proc_info_vartypes(CProcInfo, VarTypes0) },
{ proc_info_headvars(CProcInfo, HeadVars0) },
{ magic_util__remove_aditi_state(ArgTypes,
HeadVars0, HeadVars) },
{ partition_args(ModuleInfo0, ArgModes, HeadVars,
InputArgs, OutputArgs) },
{ map__apply_to_list(InputArgs, VarTypes0, InputVarTypes) },
{ list__length(InputVarTypes, NumInputVars) },
{ construct_type(unqualified("pred") - NumInputVars,
InputVarTypes, ClosureVarType) },
{ list__map(magic_util__mode_to_output_mode(ModuleInfo0),
InputArgModes, MagicArgModes) },
{ JoinProcInfo0 = CProcInfo },
{ proc_info_create_var_from_type(JoinProcInfo0,
ClosureVarType, ClosureVar, 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,
InputGoalInfo) },
{ InputGoal = higher_order_call(ClosureVar,
InputArgs, InputVarTypes, MagicArgModes,
nondet, predicate) - InputGoalInfo },
% Build a call to the original C proc.
{ CPredProcId = proc(CPredId, CProcId) },
{ proc_info_goal(CProcInfo, _ - CallGoalInfo) },
{ pred_info_module(CPredInfo, PredModule) },
{ pred_info_name(CPredInfo, PredName) },
{ CallGoal = call(CPredId, CProcId, HeadVars0, not_builtin,
no, qualified(PredModule, PredName)) - CallGoalInfo },
magic_info_get_magic_map(MagicMap),
(
{ magic_util__goal_is_aditi_call(ModuleInfo0,
MagicMap, CallGoal, DBCall0, _) }
->
{ DBCall = DBCall0 }
;
{ error("magic__create_input_join_proc: not db_call") }
),
{ ClosureInst = ground(shared,
yes(pred_inst_info(predicate, MagicArgModes, nondet))) },
{ ClosureMode = (ClosureInst -> ClosureInst) },
{ proc_info_set_argmodes(JoinProcInfo1,
[ClosureMode | OutputArgModes], JoinProcInfo2) },
{ proc_info_set_headvars(JoinProcInfo2,
[ClosureVar | OutputArgs], JoinProcInfo3) },
magic__build_join_pred_info(CPredInfo, JoinProcInfo3,
[ClosureVar | OutputArgs], JoinPredProcId,
JoinPredInfo1),
% Transform the new goal.
% We don't want the call to be treated as recursive.
magic_info_set_magic_vars([ClosureVar]),
magic_info_set_sub_module([]),
magic_info_set_pred_info(JoinPredInfo1),
magic_info_set_proc_info(JoinProcInfo3),
% Use the ho-call goal as input to the C proc, transforming
% the call to the C proc into a call to the corresponding
% Aditi proc.
{ set__list_to_set([ClosureVar | HeadVars], CallNonLocals) },
magic_util__setup_call([InputGoal], DBCall,
CallNonLocals, Goals),
magic_info_get_module_info(ModuleInfo10),
{ instmap_delta_from_mode_list(OutputArgs, OutputArgModes,
ModuleInfo10, GoalDelta) },
{ set__list_to_set([ClosureVar | OutputArgs],
GoalNonLocals) },
{ goal_info_init(GoalNonLocals, GoalDelta, nondet, GoalInfo) },
{ conj_list_to_goal(Goals, GoalInfo, Goal) },
magic_info_get_proc_info(JoinProcInfo10),
{ proc_info_set_goal(JoinProcInfo10, Goal, JoinProcInfo) },
magic_info_get_pred_info(JoinPredInfo),
{ module_info_set_pred_proc_info(ModuleInfo10, JoinPredProcId,
JoinPredInfo, JoinProcInfo, ModuleInfo) },
magic_info_set_module_info(ModuleInfo)
).
:- pred magic__build_join_pred_info(pred_info::in, proc_info::in,
list(var)::in, pred_proc_id::out, pred_info::out,
magic_info::in, magic_info::out) is det.
magic__build_join_pred_info(CPredInfo, JoinProcInfo,
Args, JoinPredProcId, JoinPredInfo1) -->
{ proc_info_vartypes(JoinProcInfo, JoinVarTypes) },
{ map__apply_to_list(Args, JoinVarTypes,
NewArgTypes) },
{ pred_info_module(CPredInfo, PredModule) },
{ pred_info_name(CPredInfo, PredName) },
{ string__append(PredName, "__c_interface", NewPredName) },
{ init_markers(Markers0) },
{ add_marker(Markers0, aditi, Markers1) },
{ add_marker(Markers1, no_memo, Markers2) },
{ add_marker(Markers2, naive, Markers) },
{ ClassContext = [] },
{ pred_info_get_aditi_owner(CPredInfo, User) },
{ varset__init(TVarSet) }, % must be empty.
{ term__context_init(DummyContext) },
{ pred_info_create(PredModule,
qualified(PredModule, NewPredName),
TVarSet, NewArgTypes, true, DummyContext,
exported, Markers, predicate, ClassContext, User,
JoinProcInfo, JoinProcId, JoinPredInfo1) },
{ JoinPredProcId = proc(JoinPredId, JoinProcId) },
magic_info_get_module_info(ModuleInfo0),
{ module_info_get_predicate_table(ModuleInfo0, Preds0) },
{ predicate_table_insert(Preds0, JoinPredInfo1,
JoinPredId, Preds) },
{ module_info_set_predicate_table(ModuleInfo0,
Preds, ModuleInfo) },
magic_info_set_module_info(ModuleInfo).
% The new procedure consists of a single call to
% <predName>__aditi_interface, which call_gen generates
% as a call to do_*_aditi_aditi in extras/aditi/aditi.m.
% This procedure must use the `compact' argument convention.
% The arguments are:
% 1 -> RL procedure name
% 2 -> number of input arguments
% 3 -> input schema
% 4 -> number of output arguments
% type_infos for input arguments
% input arguments
% type_infos for output arguments
% output arguments
:- pred magic__create_aditi_call_proc(pred_proc_id::in, pred_proc_id::in,
magic_info::in, magic_info::out) is det.
magic__create_aditi_call_proc(CPredProcId, AditiPredProcId) -->
magic__interface_pred_info(CPredProcId, AditiPredProcId,
AditiProcName, InputSchema),
magic_info_get_module_info(ModuleInfo0),
{ module_info_pred_proc_info(ModuleInfo0, CPredProcId,
CPredInfo0, CProcInfo0) },
{ pred_info_module(CPredInfo0, PredModule) },
{ pred_info_name(CPredInfo0, PredName) },
{ pred_info_arg_types(CPredInfo0, TVarSet, ArgTypes) },
{ proc_info_argmodes(CProcInfo0, ArgModes) },
{ proc_info_headvars(CProcInfo0, HeadVars) },
% Base relations will have an empty vartypes field, so fill it in here.
{ map__from_corresponding_lists(HeadVars, ArgTypes, VarTypes0) },
{ proc_info_set_vartypes(CProcInfo0, VarTypes0, CProcInfo0a) },
%
% Build type-infos for the arguments so do_*_aditi_call
% can do the required data conversions.
%
{ magic_util__remove_aditi_state(ArgTypes, ArgTypes, ArgTypes1) },
{ magic_util__remove_aditi_state(ArgTypes, ArgModes, ArgModes1) },
{ magic_util__remove_aditi_state(ArgTypes, HeadVars, HeadVars1) },
magic__make_type_info_vars(ArgTypes1, TypeInfoVars, TypeInfoGoals,
CPredInfo0, CPredInfo1, CProcInfo0a, CProcInfo1),
magic_info_get_module_info(ModuleInfo1),
{ partition_args(ModuleInfo1, ArgModes1, ArgTypes1,
InputArgTypes, OutputArgTypes) },
{ partition_args(ModuleInfo1, ArgModes1, ArgModes1,
InputArgModes, OutputArgModes) },
{ partition_args(ModuleInfo1, ArgModes1, TypeInfoVars,
InputTypeInfoVars, OutputTypeInfoVars) },
{ partition_args(ModuleInfo1, ArgModes1,
HeadVars1, InputArgs, OutputArgs) },
{ construct_type(unqualified("int") - 0, [], IntType) },
{ construct_type(unqualified("string") - 0, [], StringType) },
%
% Build up some other information that do_*_aditi_call needs.
%
% Procedure name.
{ magic__make_const(StringType, string_const(AditiProcName),
ProcNameVar, ProcNameGoal, CProcInfo1, CProcInfo2) },
% Number of input arguments.
{ list__length(InputArgTypes, NumInputArgs) },
{ magic__make_const(IntType, int_const(NumInputArgs), InputArgsVar,
InputArgsGoal, CProcInfo2, CProcInfo3) },
% Input schema.
{ magic__make_const(StringType, string_const(InputSchema),
InputSchemaVar, InputSchemaGoal, CProcInfo3, CProcInfo4) },
{ list__length(OutputArgTypes, NumOutputArgs) },
% Number of output arguments.
{ magic__make_const(IntType, int_const(NumOutputArgs), OutputArgsVar,
OutputArgsGoal, CProcInfo4, CProcInfo5) },
% Argument variables.
{ list__condense([[ProcNameVar, InputArgsVar, InputSchemaVar,
OutputArgsVar], InputTypeInfoVars, InputArgs,
OutputTypeInfoVars, OutputArgs], DoCallAditiArgs) },
% Argument types.
{ proc_info_vartypes(CProcInfo5, VarTypes) },
{ map__apply_to_list(InputTypeInfoVars,
VarTypes, InputTypeInfoTypes) },
{ map__apply_to_list(OutputTypeInfoVars,
VarTypes, OutputTypeInfoTypes) },
{ list__condense([[StringType, IntType, StringType, IntType],
InputTypeInfoTypes, InputArgTypes, OutputTypeInfoTypes,
OutputArgTypes], DoCallAditiArgTypes) },
% Argument modes.
{ in_mode(InMode) },
{ list__duplicate(NumInputArgs, InMode, InputTypeInfoModes) },
{ list__duplicate(NumOutputArgs, InMode, OutputTypeInfoModes) },
{ list__condense([[InMode, InMode, InMode, InMode],
InputTypeInfoModes, InputArgModes, OutputTypeInfoModes,
OutputArgModes], DoCallAditiArgModes) },
%
% Create a new procedure which is just an alias for do_*_aditi_call.
%
{ varset__init(VarSet0) },
{ list__length(DoCallAditiArgTypes, Arity) },
{ varset__new_vars(VarSet0, Arity, DoCallAditiHeadVars, VarSet) },
{ map__from_corresponding_lists(DoCallAditiHeadVars,
DoCallAditiArgTypes, DoCallAditiVarTypes) },
{ true_goal(DummyGoal) },
{ term__context_init(DummyContext) },
{ proc_info_inferred_determinism(CProcInfo5, Detism) },
{ map__init(TVarMap) },
{ map__init(TCVarMap) },
{ proc_info_create(VarSet, DoCallAditiVarTypes, DoCallAditiHeadVars,
DoCallAditiArgModes, Detism, DummyGoal, DummyContext,
TVarMap, TCVarMap, compact, DoCallAditiProcInfo) },
{ string__append(PredName, "__do_aditi_call", CallPredName) },
{ init_markers(Markers0) },
{ add_marker(Markers0, aditi_interface, Markers) },
{ ClassContext = [] },
{ pred_info_get_aditi_owner(CPredInfo1, User) },
{ pred_info_create(PredModule, qualified(PredModule, CallPredName),
TVarSet, DoCallAditiArgTypes, true, DummyContext,
imported, Markers, predicate, ClassContext, User,
DoCallAditiProcInfo, DoCallAditiProcId, DoCallAditiPredInfo) },
{ module_info_get_predicate_table(ModuleInfo1, PredTable0) },
{ predicate_table_insert(PredTable0, DoCallAditiPredInfo,
DoCallAditiPredId, PredTable) },
{ module_info_set_predicate_table(ModuleInfo1,
PredTable, ModuleInfo2) },
%
% Make the C procedure call the new alias for do_*_aditi_call.
%
{ set__list_to_set(DoCallAditiArgs, CallNonLocals) },
{ instmap_delta_from_mode_list(DoCallAditiArgs, DoCallAditiArgModes,
ModuleInfo2, GoalDelta) },
{ goal_info_init(CallNonLocals, GoalDelta, Detism, CallGoalInfo) },
{ DoCallAditiGoal = call(DoCallAditiPredId, DoCallAditiProcId,
DoCallAditiArgs, not_builtin, no,
qualified(PredModule, CallPredName)) - CallGoalInfo },
{ list__condense([[ProcNameGoal, InputArgsGoal, InputSchemaGoal,
OutputArgsGoal], TypeInfoGoals, [DoCallAditiGoal]], Goals) },
{ set__list_to_set(HeadVars, GoalNonLocals) },
{ goal_list_determinism(Goals, GoalDetism) },
{ goal_info_init(GoalNonLocals, GoalDelta, GoalDetism, GoalInfo) },
{ Goal = conj(Goals) - GoalInfo },
{ proc_info_set_goal(CProcInfo5, Goal, CProcInfo) },
% The procedure is now just a normal C procedure.
{ pred_info_get_markers(CPredInfo1, CMarkers1) },
{ remove_marker(CMarkers1, aditi, CMarkers2) },
{ remove_marker(CMarkers2, base_relation, CMarkers) },
{ pred_info_set_markers(CPredInfo1, CMarkers, CPredInfo) },
{ CPredProcId = proc(CPredId, CProcId) },
{ module_info_set_pred_proc_info(ModuleInfo2, CPredId, CProcId,
CPredInfo, CProcInfo, ModuleInfo) },
magic_info_set_module_info(ModuleInfo).
:- pred magic__interface_pred_info(pred_proc_id::in, pred_proc_id::in,
string::out, string::out, magic_info::in, magic_info::out) is det.
magic__interface_pred_info(CPredProcId, PredProcId,
ProcNameStr, InputSchema) -->
magic_info_get_module_info(ModuleInfo),
{ rl_gen__get_entry_proc_name(ModuleInfo, PredProcId, ProcName) },
{ rl__proc_name_to_string(ProcName, ProcNameStr) },
{ module_info_pred_proc_info(ModuleInfo, CPredProcId,
CPredInfo, CProcInfo) },
{ pred_info_arg_types(CPredInfo, _, ArgTypes0) },
{ proc_info_argmodes(CProcInfo, ArgModes0) },
{ magic_util__remove_aditi_state(ArgTypes0, ArgTypes0, ArgTypes) },
{ magic_util__remove_aditi_state(ArgTypes0, ArgModes0, ArgModes) },
{ partition_args(ModuleInfo, ArgModes, ArgTypes, InputArgTypes, _) },
{ rl_out__schema_to_string(ModuleInfo, InputArgTypes, InputSchema) }.
:- pred magic__make_type_info_vars(list(type)::in, list(var)::out,
list(hlds_goal)::out, pred_info::in, pred_info::out,
proc_info::in, proc_info::out, magic_info::in, magic_info::out) is det.
magic__make_type_info_vars(Types, TypeInfoVars, TypeInfoGoals,
PredInfo0, PredInfo, ProcInfo0, ProcInfo) -->
magic_info_get_module_info(ModuleInfo0),
{ poly_info_init(ModuleInfo0, PredInfo0, ProcInfo0, PolyInfo0) },
{ polymorphism__make_type_info_vars(Types,
TypeInfoVars, TypeInfoGoals, PolyInfo0, PolyInfo) },
{ poly_info_extract(PolyInfo, PredInfo0, PredInfo,
ProcInfo0, ProcInfo, ModuleInfo) },
magic_info_set_module_info(ModuleInfo).
:- pred magic__make_const((type)::in, cons_id::in, var::out, hlds_goal::out,
proc_info::in, proc_info::out) is det.
magic__make_const(Type, ConsId, Var, Goal, ProcInfo0, ProcInfo) :-
proc_info_create_var_from_type(ProcInfo0, Type, Var, ProcInfo),
set__singleton_set(NonLocals, Var),
Inst = bound(unique, [functor(ConsId, [])]),
instmap_delta_init_reachable(Delta0),
instmap_delta_insert(Delta0, Var, Inst, Delta),
UnifyMode = (free -> Inst) - (Inst -> Inst),
Uni = construct(Var, ConsId, [], []),
Context = unify_context(explicit, []),
goal_info_init(NonLocals, Delta, det, GoalInfo),
Goal = unify(Var, functor(ConsId, []), UnifyMode, Uni, Context) -
GoalInfo.
%-----------------------------------------------------------------------------%
% 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, maybe(int)::in,
magic_info::in, magic_info::out) is det.
magic__create_magic_pred(CPredProcId, PredProcId, MagicTypes, MagicModes,
InputTypes0, InputModes0, 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, GoalInfo0) },
{ Goal0 = higher_order_call(CurrPredVar, InputArgs0,
InputTypes, OutputModes0,
nondet, predicate) - 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,
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) },
{ DummyArgsMethod = compact }, % never used
{ proc_info_create(VarSet, VarTypes, AllArgs, AllArgModes, nondet,
Goal, Context, TVarMap, TCVarMap, DummyArgsMethod, ProcInfo) },
%
% Fill in the pred_info.
%
{ CPredProcId = proc(CPredId, _) },
{ module_info_name(ModuleInfo0, ModuleName) },
{ predicate_module(ModuleInfo0, CPredId, PredModule) },
{ predicate_name(ModuleInfo0, CPredId, PredName) },
{ string__append(PredName, "__magic", NewPredName) },
{ SymName = qualified(PredModule, NewPredName) },
{ list__append(MagicTypes, InputTypes, AllArgTypes) },
{ varset__init(TVarSet) },
{ pred_info_get_aditi_owner(PredInfo, Owner) },
{ ClassConstraints = [] }, % types must all be ground
{ pred_info_create(ModuleName, SymName, TVarSet, AllArgTypes,
true, Context, local, Markers, predicate, ClassConstraints,
Owner, ProcInfo, MagicProcId, MagicPredInfo) },
{ module_info_get_predicate_table(ModuleInfo0, PredTable0) },
{ predicate_table_insert(PredTable0,
MagicPredInfo, MagicPredId, PredTable) },
{ module_info_set_predicate_table(ModuleInfo0, PredTable,
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(var, 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, 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, ProcInfo0, ProcInfo) -->
{ proc_info_goal(ProcInfo0, Goal0) },
%
% Convert if-then-elses and switches to disjunctions.
%
( { Goal0 = if_then_else(_Vars, Cond, Then, Else, _SM) - GoalInfo } ->
{ goal_util__if_then_else_to_disjunction(Cond, Then, Else,
GoalInfo, Disj) },
{ Goal1 = Disj - GoalInfo },
{ ProcInfo2 = ProcInfo0 }
; { Goal0 = switch(Var, _Canfail, Cases, _SM) - GoalInfo } ->
{ proc_info_varset(ProcInfo0, VarSet0) },
{ proc_info_vartypes(ProcInfo0, VarTypes0) },
magic_info_get_module_info(ModuleInfo0),
{ proc_info_get_initial_instmap(ProcInfo0,
ModuleInfo0, InstMap) },
{ goal_util__switch_to_disjunction(Var, Cases,
InstMap, Disjuncts, VarSet0, VarSet1,
VarTypes0, VarTypes1, ModuleInfo0, ModuleInfo1) },
magic_info_set_module_info(ModuleInfo1),
{ proc_info_set_varset(ProcInfo0, VarSet1, ProcInfo1) },
{ proc_info_set_vartypes(ProcInfo1, VarTypes1, ProcInfo2) },
{ map__init(SM) },
{ Goal1 = disj(Disjuncts, SM) - GoalInfo }
;
{ Goal1 = Goal0 },
{ ProcInfo2 = ProcInfo0 }
),
{ proc_info_set_goal(ProcInfo2, Goal1, ProcInfo3) },
%
% Run simplification, mainly to get rid of
% nested explicit quantifications.
%
magic_info_get_module_info(ModuleInfo2),
{ module_info_globals(ModuleInfo2, Globals) },
{ simplify__find_simplifications(no, Globals, Simplifications) },
{ PredProcId = proc(PredId, ProcId) },
{ simplify__proc_2(Simplifications, PredId, ProcId,
ModuleInfo2, ModuleInfo3, ProcInfo3, ProcInfo4, _) },
{ proc_info_goal(ProcInfo4, Goal2) },
magic_info_set_curr_pred_proc_id(PredProcId),
magic_info_set_pred_info(PredInfo),
magic_info_set_proc_info(ProcInfo4),
magic_info_set_module_info(ModuleInfo3),
{ Goal2 = _ - GoalInfo2 },
{ goal_to_disj_list(Goal2, GoalList2) },
list__map_foldl(magic__preprocess_disjunct,
GoalList2, GoalList),
{ disj_list_to_goal(GoalList, GoalInfo2, Goal) },
magic_info_get_proc_info(ProcInfo5),
{ proc_info_set_goal(ProcInfo5, 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.
% 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(var, hlds_goal)::in, map(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(var, hlds_goal)::in, map(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(class_method_call(_, _, _, _, _, _) - _, _, _, _) -->
{ error("Sorry, not yet implemented: class method calls in Aditi procedures") }.
magic__preprocess_goal_2(higher_order_call(_, _, _, _, _, _) - _, _, _, _) -->
{ error("Sorry, not yet implemented: higher-order calls in Aditi procedures") }.
magic__preprocess_goal_2(pragma_c_code(_, _, _, _, _, _, _) - _, _, _, _) -->
{ error("Sorry, not yet implemented: pragma c_code 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, _, Args, _, _, _) - _ },
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] }
;
{ Goals = [Goal0] }
).
magic__preprocess_goal_2(some(Vars, Goal0) - Info, [some(Vars, 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 }
).
:- pred magic__preprocess_conj(list(hlds_goal)::in, list(hlds_goal)::in,
list(hlds_goal)::out, map(var, hlds_goal)::in,
map(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), _, _),
(
map__contains(PredMap, proc(PredId, ProcId))
;
hlds_pred__is_aditi_relation(ModuleInfo,
PredId)
)
}
->
[]
;
{ Goal = _ - GoalInfo },
{ goal_info_get_nonlocals(GoalInfo, NonLocals0) },
{ set__to_sorted_list(NonLocals0, NonLocals) },
magic_info_get_proc_info(ProcInfo),
{ proc_info_vartypes(ProcInfo, VarTypes) },
{ IsAditiVar = lambda([Var::in] is semidet, (
map__lookup(VarTypes, Var, Type),
type_is_aditi_state(Type)
)) },
{ list__filter(IsAditiVar, NonLocals, 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(var)::in,
list(hlds_goal)::out, hlds_goal::in, hlds_goal::out,
map(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(ProcInfo0,
Type, NewArg, 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_sub_module(aditi_sub_module::in, magic_info::in,
magic_info::out, io__state::di, io__state::uo) is det.
magic__process_sub_module(aditi_sub_module(SubModule0, _), Info0, Info) -->
{ list__condense(SubModule0, SubModule) },
{ magic_info_set_sub_module(SubModule, Info0, Info1) },
{ list__foldl(magic__process_proc, SubModule, 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(Inputs, InputSet) },
magic__process_disjuncts(InputSet, 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(ProcInfo1, Goal, ProcInfo) },
magic_info_get_module_info(ModuleInfo1),
{ module_info_set_pred_proc_info(ModuleInfo1, PredProcId,
PredInfo, ProcInfo, ModuleInfo) },
magic_info_set_module_info(ModuleInfo)
).
%-----------------------------------------------------------------------------%
:- pred magic__process_disjuncts(set(var)::in, list(hlds_goal)::in,
list(hlds_goal)::out, magic_info::in, magic_info::out) is det.
magic__process_disjuncts(_, [], []) --> [].
magic__process_disjuncts(Inputs, [Disjunct0 | Disjuncts0],
[Disjunct | Disjuncts]) -->
magic__process_disjunct(Inputs, Disjunct0, Disjunct),
magic__process_disjuncts(Inputs, Disjuncts0, Disjuncts).
:- pred magic__process_disjunct(set(var)::in, hlds_goal::in, hlds_goal::out,
magic_info::in, magic_info::out) is det.
magic__process_disjunct(_Inputs, 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) },
{ goal_info_get_nonlocals(DisjInfo, NonLocals0) },
{ set__union(NonLocals0, 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(var)::in, list(hlds_goal)::out,
magic_info::in, magic_info::out) is det.
magic__process_disjunct_2(RevBeforeGoals1, DBCall1, NonLocals0, Goals) -->
% Work out the nonlocals of the goals after the part of the
% disjunct being processed here.
% 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, GoalInfo) },
{ MagicCall = call(MagicPredId, MagicProcId, MagicArgs,
not_builtin, no, PredName) - GoalInfo }.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
More information about the developers
mailing list