mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-14 21:35:49 +00:00
Estimated hours taken: 5 Branches: main Fix the failure of the invalid/modes_erroneous test case, whose symptom was an error message about a "mode error in unification of `X' and `X'". The root cause of the problem was that the renaming of head variables computed by headvar_names.m was being applied too early, during typechecking. The fix is to apply it after the frontend (all the passes that can generate error messages). To avoid slowdowns from larger pred_infos, this diff also moves the least frequently used fields of pred_infos to a subterm. (Proc_infos already had a subterm.) This leads to an almost 3% speedup. compiler/headvar_names.m: Store the renaming instead of applying it. compiler/simplify.m: Apply the renaming in invocations after the front end, since doing so may allow some excess assignments to be eliminated. compiler/hlds_pred.m: Add fields to pred_infos and proc_infos for the renaming. Move the least frequently used fields of pred_infos into a pred_sub_info. Some fields of pred_infos were being accessed using predicates that did not follow our naming conventions, and some were accessed using field access functions that are now inappropriate; fix them all. Require the caller to provide the renaming when creating new pred_infos and proc_infos. This is to force the compiler components that do this to propagate the renaming fields of the original predicates and/or procedures to their modified versions. Convert that some old code that used if-then-elses to use switches instead. compiler/hlds_out.m: Write out the new pred_info and proc_info fields. compiler/*.m: Conform to the changes in hlds_pred.m. compiler/hlds_clauses.m: Avoid ambiguity by giving a prefix to the fields of the clauses_info type. tests/invalid/ho_type_mode_bug.err_exp: tests/invalid/merge_ground_any.err_exp: Don't expect error messages about "X = X" anymore.
3628 lines
157 KiB
Mathematica
3628 lines
157 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1997-2007 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: table_gen.m.
|
|
% Main authors: zs, ohutch.
|
|
%
|
|
% This module transforms HLDS code to implement loop detection, memoing,
|
|
% minimal model evaluation, or I/O idempotence. The transformation involves
|
|
% adding calls to predicates defined in library/table_builtin.m and in
|
|
% runtime/mercury_minimal_model.c.
|
|
%
|
|
% The loop detection transformation adds code to a procedure that allows
|
|
% early detection of infinite loops. If such loops are detected the program
|
|
% will terminate with a helpful error message.
|
|
%
|
|
% The memo transformation adds code that allows a procedure to "memo"
|
|
% (remember) answers once they have been generated using program clause
|
|
% resolution.
|
|
%
|
|
% The minimal model transformation changes the semantics of the procedure
|
|
% being transformed. See the PhD thesis of K. Sagonas: `The SLG-WAM: A
|
|
% Search-Efficient Engine for Well-Founded Evaluation of Normal Logic
|
|
% Programs' from SUNY at Stony Brook in 1996 for a description of
|
|
% the semantics behind the transformation. Currently only SLGd is
|
|
% implemented.
|
|
%
|
|
% The current implementation attempts to detect cases where tabling has
|
|
% undesirable interactions with if-then-else, solutions, and (possibly)
|
|
% negated contexts in general. However, the detection is done at runtime,
|
|
% since there is no known way of doing this compile time.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module transform_hlds.table_gen.
|
|
:- interface.
|
|
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module parse_tree.error_util.
|
|
|
|
:- import_module list.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred table_gen_process_module(module_info::in, module_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds.mode_util.
|
|
:- import_module check_hlds.polymorphism.
|
|
:- import_module check_hlds.purity.
|
|
:- import_module check_hlds.type_util.
|
|
:- import_module hlds.code_model.
|
|
:- import_module hlds.goal_util.
|
|
:- import_module hlds.hlds_data.
|
|
:- import_module hlds.hlds_error_util.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module hlds.instmap.
|
|
:- import_module hlds.pred_table.
|
|
:- import_module libs.compiler_util.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.options.
|
|
:- import_module ll_backend.
|
|
:- import_module ll_backend.continuation_info.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.prog_mode.
|
|
:- import_module parse_tree.prog_out.
|
|
:- import_module parse_tree.prog_type.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- import_module int.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module pair.
|
|
:- import_module set.
|
|
:- import_module solutions.
|
|
:- import_module string.
|
|
:- import_module term.
|
|
:- import_module unit.
|
|
:- import_module varset.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Values of this type map the pred_id of a minimal_model tabled
|
|
% predicate to the pred_id of its generator variant.
|
|
%
|
|
:- type generator_map == map(pred_id, pred_id).
|
|
|
|
% NOTE: following preds seem to duplicate the code in passes_aux.m.
|
|
% The reason for this duplication is that this module needs a variant
|
|
% of this code that is able to handle passing a module_info to
|
|
% polymorphism and getting an updated module_info back.
|
|
%
|
|
table_gen_process_module(!ModuleInfo, !Specs) :-
|
|
module_info_preds(!.ModuleInfo, Preds0),
|
|
map.keys(Preds0, PredIds),
|
|
map.init(GenMap0),
|
|
table_gen_process_preds(PredIds, !ModuleInfo, GenMap0, _, !Specs).
|
|
|
|
:- pred table_gen_process_preds(list(pred_id)::in,
|
|
module_info::in, module_info::out,
|
|
generator_map::in, generator_map::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
table_gen_process_preds([], !ModuleInfo, !GenMap, !Specs).
|
|
table_gen_process_preds([PredId | PredIds], !ModuleInfo, !GenMap, !Specs) :-
|
|
table_gen_process_pred(PredId, !ModuleInfo, !GenMap, !Specs),
|
|
table_gen_process_preds(PredIds, !ModuleInfo, !GenMap, !Specs).
|
|
|
|
:- pred table_gen_process_pred(pred_id::in, module_info::in, module_info::out,
|
|
generator_map::in, generator_map::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
table_gen_process_pred(PredId, !ModuleInfo, !GenMap, !Specs) :-
|
|
module_info_pred_info(!.ModuleInfo, PredId, PredInfo),
|
|
ProcIds = pred_info_procids(PredInfo),
|
|
table_gen_process_procs(PredId, ProcIds, !ModuleInfo, !GenMap, !Specs).
|
|
|
|
:- pred table_gen_process_procs(pred_id::in, list(proc_id)::in,
|
|
module_info::in, module_info::out,
|
|
generator_map::in, generator_map::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
table_gen_process_procs(_PredId, [], !ModuleInfo, !GenMap, !Specs).
|
|
table_gen_process_procs(PredId, [ProcId | ProcIds], !ModuleInfo, !GenMap,
|
|
!Specs) :-
|
|
module_info_preds(!.ModuleInfo, PredTable),
|
|
map.lookup(PredTable, PredId, PredInfo),
|
|
pred_info_get_procedures(PredInfo, ProcTable),
|
|
map.lookup(ProcTable, ProcId, ProcInfo0),
|
|
table_gen_process_proc(PredId, ProcId, ProcInfo0, PredInfo,
|
|
!ModuleInfo, !GenMap, !Specs),
|
|
table_gen_process_procs(PredId, ProcIds, !ModuleInfo, !GenMap, !Specs).
|
|
|
|
:- pred table_gen_process_proc(pred_id::in, proc_id::in, proc_info::in,
|
|
pred_info::in, module_info::in, module_info::out,
|
|
generator_map::in, generator_map::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
table_gen_process_proc(PredId, ProcId, ProcInfo0, PredInfo0, !ModuleInfo,
|
|
!GenMap, !Specs) :-
|
|
proc_info_get_eval_method(ProcInfo0, EvalMethod),
|
|
( eval_method_requires_tabling_transform(EvalMethod) = yes ->
|
|
table_gen_transform_proc_if_possible(EvalMethod, PredId,
|
|
ProcId, ProcInfo0, _, PredInfo0, _, !ModuleInfo, !GenMap, !Specs)
|
|
;
|
|
module_info_get_globals(!.ModuleInfo, Globals),
|
|
globals.lookup_bool_option(Globals, trace_table_io, yes),
|
|
proc_info_has_io_state_pair(!.ModuleInfo, ProcInfo0,
|
|
_InArgNum, _OutArgNum)
|
|
->
|
|
proc_info_interface_code_model(ProcInfo0, CodeModel),
|
|
( CodeModel = model_det ->
|
|
true
|
|
;
|
|
pred_id_to_int(PredId, PredIdInt),
|
|
Msg = string.format("I/O procedure pred id %d not model_det",
|
|
[i(PredIdInt)]),
|
|
unexpected(this_file, Msg)
|
|
),
|
|
globals.lookup_bool_option(Globals, trace_table_io_all, TransformAll),
|
|
globals.lookup_bool_option(Globals, trace_table_io_require, Require),
|
|
proc_info_get_goal(ProcInfo0, BodyGoal),
|
|
PredModuleName = predicate_module(!.ModuleInfo, PredId),
|
|
should_io_procedure_be_transformed(TransformAll, Require, BodyGoal,
|
|
PredModuleName, AnnotationIsMissing, TransformPrimitive),
|
|
(
|
|
AnnotationIsMissing = yes,
|
|
Spec = report_missing_tabled_for_io(PredInfo0, PredId, ProcId,
|
|
!.ModuleInfo),
|
|
!:Specs = [Spec | !.Specs]
|
|
;
|
|
AnnotationIsMissing = no
|
|
),
|
|
(
|
|
TransformPrimitive = no
|
|
;
|
|
TransformPrimitive = yes(Unitize),
|
|
globals.lookup_bool_option(Globals, trace_table_io_only_retry,
|
|
TraceTableIoOnlyRetry),
|
|
(
|
|
TraceTableIoOnlyRetry = no,
|
|
Decl = table_io_decl
|
|
;
|
|
TraceTableIoOnlyRetry = yes,
|
|
Decl = table_io_proc
|
|
),
|
|
TableIoMethod = eval_table_io(Decl, Unitize),
|
|
proc_info_set_eval_method(TableIoMethod, ProcInfo0, ProcInfo1),
|
|
table_gen_transform_proc_if_possible(TableIoMethod,
|
|
PredId, ProcId, ProcInfo1, _, PredInfo0, _, !ModuleInfo,
|
|
!GenMap, !Specs)
|
|
)
|
|
;
|
|
true
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred should_io_procedure_be_transformed(bool::in, bool::in, hlds_goal::in,
|
|
sym_name::in, bool::out, maybe(table_io_is_unitize)::out) is det.
|
|
|
|
should_io_procedure_be_transformed(TransformAll, Require, BodyGoal,
|
|
PredModuleName, AnnotationIsMissing, TransformInfo) :-
|
|
tabled_for_io_attributes(BodyGoal, TabledForIoAttrs),
|
|
(
|
|
TabledForIoAttrs = [],
|
|
AnnotationIsMissing = no,
|
|
TransformInfo = no
|
|
;
|
|
TabledForIoAttrs = [TabledForIoAttr],
|
|
(
|
|
TabledForIoAttr = proc_not_tabled_for_io,
|
|
(
|
|
Require = yes,
|
|
\+ any_mercury_builtin_module(PredModuleName)
|
|
->
|
|
AnnotationIsMissing = yes,
|
|
TransformInfo = no
|
|
;
|
|
AnnotationIsMissing = no,
|
|
(
|
|
TransformAll = no,
|
|
TransformInfo = no
|
|
;
|
|
TransformAll = yes,
|
|
may_call_mercury_attributes(BodyGoal, MayCallMercuryAttrs),
|
|
( MayCallMercuryAttrs = [proc_may_call_mercury] ->
|
|
TransformInfo = no
|
|
;
|
|
TransformInfo = yes(table_io_alone)
|
|
)
|
|
)
|
|
)
|
|
;
|
|
TabledForIoAttr = proc_tabled_for_descendant_io,
|
|
AnnotationIsMissing = no,
|
|
% The procedure itself doesn't do any I/O, so don't transform it.
|
|
TransformInfo = no
|
|
;
|
|
TabledForIoAttr = proc_tabled_for_io,
|
|
AnnotationIsMissing = no,
|
|
TransformInfo = yes(table_io_alone)
|
|
;
|
|
TabledForIoAttr = proc_tabled_for_io_unitize,
|
|
AnnotationIsMissing = no,
|
|
TransformInfo = yes(table_io_unitize)
|
|
)
|
|
;
|
|
TabledForIoAttrs = [_, _ | _],
|
|
% Since table_gen is run before inlining, each procedure
|
|
% should contain at most one foreign_proc goal.
|
|
unexpected(this_file, "should_io_procedure_be_transformed: " ++
|
|
"different tabled_for_io attributes in one procedure")
|
|
).
|
|
|
|
:- pred may_call_mercury_attributes(hlds_goal::in,
|
|
list(proc_may_call_mercury)::out) is det.
|
|
|
|
may_call_mercury_attributes(Goal, MayCallMercuryAttrs) :-
|
|
solutions.solutions(subgoal_may_call_mercury_attribute(Goal),
|
|
MayCallMercuryAttrs).
|
|
|
|
:- pred subgoal_may_call_mercury_attribute(hlds_goal::in,
|
|
proc_may_call_mercury::out) is nondet.
|
|
|
|
subgoal_may_call_mercury_attribute(Goal, MayCallMercuryAttr) :-
|
|
some [SubGoal, Attrs] (
|
|
goal_contains_goal(Goal, SubGoal),
|
|
SubGoal = hlds_goal(call_foreign_proc(Attrs, _, _, _, _, _, _), _),
|
|
MayCallMercuryAttr = get_may_call_mercury(Attrs)
|
|
).
|
|
|
|
:- pred tabled_for_io_attributes(hlds_goal::in, list(proc_tabled_for_io)::out)
|
|
is det.
|
|
|
|
tabled_for_io_attributes(Goal, TabledForIoAttrs) :-
|
|
solutions.solutions(subgoal_tabled_for_io_attribute(Goal),
|
|
TabledForIoAttrs).
|
|
|
|
:- pred subgoal_tabled_for_io_attribute(hlds_goal::in, proc_tabled_for_io::out)
|
|
is nondet.
|
|
|
|
subgoal_tabled_for_io_attribute(Goal, TabledForIoAttr) :-
|
|
some [SubGoal, Attrs] (
|
|
goal_contains_goal(Goal, SubGoal),
|
|
SubGoal = hlds_goal(call_foreign_proc(Attrs, _, _, _, _, _, _), _),
|
|
TabledForIoAttr = get_tabled_for_io(Attrs),
|
|
\+ TabledForIoAttr = proc_not_tabled_for_io
|
|
).
|
|
|
|
:- func report_missing_tabled_for_io(pred_info, pred_id, proc_id, module_info)
|
|
= error_spec.
|
|
|
|
report_missing_tabled_for_io(PredInfo, PredId, ProcId, ModuleInfo) = Spec :-
|
|
pred_info_get_context(PredInfo, Context),
|
|
ProcPieces = describe_one_proc_name(ModuleInfo,
|
|
should_not_module_qualify, proc(PredId, ProcId)),
|
|
Pieces = ProcPieces ++ [words("contains untabled I/O primitive."), nl],
|
|
Msg = simple_msg(Context, [always(Pieces)]),
|
|
Spec = error_spec(severity_error, phase_code_gen, [Msg]).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred table_gen_transform_proc_if_possible(eval_method::in,
|
|
pred_id::in, proc_id::in, proc_info::in, proc_info::out,
|
|
pred_info::in, pred_info::out, module_info::in, module_info::out,
|
|
generator_map::in, generator_map::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
table_gen_transform_proc_if_possible(EvalMethod, PredId, ProcId,
|
|
!ProcInfo, !PredInfo, !ModuleInfo, !GenMap, !Specs) :-
|
|
module_info_get_globals(!.ModuleInfo, Globals),
|
|
globals.get_target(Globals, Target),
|
|
globals.get_gc_method(Globals, GC_Method),
|
|
( Target = target_c, GC_Method \= gc_accurate ->
|
|
table_gen_transform_proc(EvalMethod, PredId, ProcId,
|
|
!ProcInfo, !PredInfo, !ModuleInfo, !GenMap)
|
|
;
|
|
pred_info_get_context(!.PredInfo, Context),
|
|
ProcPieces = describe_one_proc_name(!.ModuleInfo,
|
|
should_module_qualify, proc(PredId, ProcId)),
|
|
EvalMethodStr = eval_method_to_string(EvalMethod),
|
|
Pieces = [words("Ignoring the pragma"), fixed(EvalMethodStr),
|
|
words("for")] ++ ProcPieces ++
|
|
[words("due to lack of support on this back end."), nl],
|
|
Msg = simple_msg(Context, [always(Pieces)]),
|
|
% We don't want to increment the error count, since that would combine
|
|
% with --halt-at-warn to prevent the clean compilation of the library.
|
|
Spec = error_spec(severity_informational, phase_code_gen, [Msg]),
|
|
!:Specs = [Spec | !.Specs],
|
|
|
|
% XXX We set the evaluation method to eval_normal here to prevent
|
|
% problems in the ml code generator if we are compiling in a grade
|
|
% that does not support tabling. (See ml_gen_maybe_add_table_var/6
|
|
% in ml_code_gen.m for further details.)
|
|
%
|
|
% We do this here rather than when processing the tabling pragmas
|
|
% (in add_pragma.m) so that we can still generate error messages
|
|
% for misuses of the tabling pragmas.
|
|
|
|
proc_info_set_eval_method(eval_normal, !ProcInfo),
|
|
module_info_set_pred_proc_info(PredId, ProcId, !.PredInfo,
|
|
!.ProcInfo, !ModuleInfo)
|
|
).
|
|
|
|
:- pred table_gen_transform_proc(eval_method::in, pred_id::in, proc_id::in,
|
|
proc_info::in, proc_info::out, pred_info::in, pred_info::out,
|
|
module_info::in, module_info::out,
|
|
generator_map::in, generator_map::out) is det.
|
|
|
|
table_gen_transform_proc(EvalMethod, PredId, ProcId, !ProcInfo, !PredInfo,
|
|
!ModuleInfo, !GenMap) :-
|
|
table_info_init(!.ModuleInfo, !.PredInfo, !.ProcInfo, TableInfo0),
|
|
|
|
% Grab the appropriate fields from the pred_info and proc_info.
|
|
proc_info_interface_determinism(!.ProcInfo, Detism),
|
|
determinism_to_code_model(Detism, CodeModel),
|
|
proc_info_get_headvars(!.ProcInfo, HeadVars),
|
|
proc_info_get_varset(!.ProcInfo, VarSet0),
|
|
proc_info_get_vartypes(!.ProcInfo, VarTypes0),
|
|
proc_info_get_goal(!.ProcInfo, OrigGoal),
|
|
proc_info_get_argmodes(!.ProcInfo, ArgModes),
|
|
proc_info_get_table_attributes(!.ProcInfo, MaybeAttributes),
|
|
(
|
|
MaybeAttributes = yes(Attributes)
|
|
;
|
|
MaybeAttributes = no,
|
|
Attributes = default_memo_table_attributes
|
|
),
|
|
|
|
(
|
|
EvalMethod = eval_normal,
|
|
% This should have been caught by our caller.
|
|
unexpected(this_file, "table_gen_transform_proc: eval_normal")
|
|
;
|
|
EvalMethod = eval_table_io(_, _),
|
|
expect(unify(MaybeAttributes, no), this_file,
|
|
"table_gen_transform_proc: eval_table_io and Attributes"),
|
|
% Since we don't actually create a call table for I/O tabled
|
|
% procedures, the value of MaybeSpecMethod doesn't really matter.
|
|
MaybeSpecMethod = all_same(arg_value),
|
|
Statistics = table_dont_gather_statistics,
|
|
MaybeSizeLimit = no
|
|
;
|
|
( EvalMethod = eval_loop_check
|
|
; EvalMethod = eval_memo
|
|
; EvalMethod = eval_minimal(_)
|
|
),
|
|
CallStrictness = Attributes ^ table_attr_strictness,
|
|
Statistics = Attributes ^ table_attr_statistics,
|
|
MaybeSizeLimit = Attributes ^ table_attr_size_limit,
|
|
(
|
|
CallStrictness = all_strict,
|
|
MaybeSpecMethod = all_same(arg_value)
|
|
;
|
|
CallStrictness = all_fast_loose,
|
|
MaybeSpecMethod = all_same(arg_addr)
|
|
;
|
|
CallStrictness = specified(ArgMethods),
|
|
MaybeSpecMethod = specified(ArgMethods)
|
|
),
|
|
( EvalMethod = eval_minimal(_) ->
|
|
expect(unify(MaybeSizeLimit, no), this_file,
|
|
"eval_minimal with size limit"),
|
|
expect(unify(MaybeSpecMethod, all_same(arg_value)), this_file,
|
|
"eval_minimal without all_strict")
|
|
;
|
|
true
|
|
)
|
|
),
|
|
get_input_output_vars(HeadVars, ArgModes, !.ModuleInfo, MaybeSpecMethod, _,
|
|
InputVarModeMethods, OutputVarModeMethods),
|
|
allocate_slot_numbers(InputVarModeMethods, 0, NumberedInputVars),
|
|
allocate_slot_numbers(OutputVarModeMethods, 0, NumberedOutputVars),
|
|
% The case EvalMethod = eval_normal was caught by the code above.
|
|
(
|
|
EvalMethod = eval_table_io(Decl, Unitize),
|
|
module_info_get_globals(!.ModuleInfo, Globals),
|
|
globals.lookup_bool_option(Globals, trace_table_io_states,
|
|
TableIoStates),
|
|
assoc_list.from_corresponding_lists(HeadVars, ArgModes, HeadVarModes),
|
|
create_new_io_goal(OrigGoal, Decl, Unitize, TableIoStates,
|
|
PredId, ProcId, HeadVarModes, NumberedInputVars,
|
|
NumberedOutputVars, VarSet0, VarSet, VarTypes0, VarTypes,
|
|
TableInfo0, TableInfo, Goal, MaybeProcTableInfo),
|
|
MaybeCallTableTip = no
|
|
;
|
|
EvalMethod = eval_loop_check,
|
|
create_new_loop_goal(Detism, OrigGoal, Statistics,
|
|
PredId, ProcId, HeadVars, NumberedInputVars, NumberedOutputVars,
|
|
VarSet0, VarSet, VarTypes0, VarTypes,
|
|
TableInfo0, TableInfo, CallTableTip, Goal, InputSteps),
|
|
generate_gen_proc_table_info(TableInfo, InputSteps, no,
|
|
InputVarModeMethods, OutputVarModeMethods, ProcTableInfo),
|
|
MaybeCallTableTip = yes(CallTableTip),
|
|
MaybeProcTableInfo = yes(ProcTableInfo)
|
|
;
|
|
EvalMethod = eval_memo,
|
|
( CodeModel = model_non ->
|
|
create_new_memo_non_goal(Detism, OrigGoal, Statistics,
|
|
MaybeSizeLimit, PredId, ProcId,
|
|
HeadVars, NumberedInputVars, NumberedOutputVars,
|
|
VarSet0, VarSet, VarTypes0, VarTypes, TableInfo0, TableInfo,
|
|
CallTableTip, Goal, InputSteps, OutputSteps),
|
|
MaybeOutputSteps = yes(OutputSteps)
|
|
;
|
|
create_new_memo_goal(Detism, OrigGoal, Statistics, MaybeSizeLimit,
|
|
PredId, ProcId,
|
|
HeadVars, NumberedInputVars, NumberedOutputVars,
|
|
VarSet0, VarSet, VarTypes0, VarTypes,
|
|
TableInfo0, TableInfo, CallTableTip, Goal, InputSteps),
|
|
MaybeOutputSteps = no
|
|
),
|
|
generate_gen_proc_table_info(TableInfo, InputSteps, MaybeOutputSteps,
|
|
InputVarModeMethods, OutputVarModeMethods, ProcTableInfo),
|
|
MaybeCallTableTip = yes(CallTableTip),
|
|
MaybeProcTableInfo = yes(ProcTableInfo)
|
|
;
|
|
EvalMethod = eval_minimal(MinimalMethod),
|
|
expect(unify(CodeModel, model_non), this_file,
|
|
"table_gen_transform_proc: minimal model but not model_non"),
|
|
(
|
|
MinimalMethod = stack_copy,
|
|
create_new_mm_goal(Detism, OrigGoal, Statistics, PredId, ProcId,
|
|
HeadVars, NumberedInputVars, NumberedOutputVars,
|
|
VarSet0, VarSet, VarTypes0, VarTypes, TableInfo0, TableInfo,
|
|
CallTableTip, Goal, InputSteps, OutputSteps),
|
|
MaybeCallTableTip = yes(CallTableTip),
|
|
MaybeOutputSteps = yes(OutputSteps),
|
|
generate_gen_proc_table_info(TableInfo, InputSteps,
|
|
MaybeOutputSteps, InputVarModeMethods, OutputVarModeMethods,
|
|
ProcTableInfo),
|
|
MaybeProcTableInfo = yes(ProcTableInfo)
|
|
;
|
|
MinimalMethod = own_stacks_consumer,
|
|
do_own_stack_transform(Detism, OrigGoal, Statistics,
|
|
PredId, ProcId, !.PredInfo, !.ProcInfo,
|
|
HeadVars, NumberedInputVars, NumberedOutputVars,
|
|
VarSet0, VarSet, VarTypes0, VarTypes, TableInfo0, TableInfo,
|
|
!GenMap, Goal, _InputSteps, _OutputSteps),
|
|
MaybeCallTableTip = no,
|
|
MaybeProcTableInfo = no
|
|
;
|
|
MinimalMethod = own_stacks_generator,
|
|
% The own_stacks_generator minimal_method is only ever introduced
|
|
% by the transformation in this module; a procedure that hasn't
|
|
% been transformed yet should not have this eval_method.
|
|
unexpected(this_file,
|
|
"table_gen_transform_proc: own stacks generator")
|
|
)
|
|
),
|
|
|
|
table_info_extract(TableInfo, !:ModuleInfo, !:PredInfo, !:ProcInfo),
|
|
|
|
% Set the new values of the fields in proc_info and pred_info
|
|
% and save in the module info.
|
|
proc_info_set_goal(Goal, !ProcInfo),
|
|
proc_info_set_varset(VarSet, !ProcInfo),
|
|
proc_info_set_vartypes(VarTypes, !ProcInfo),
|
|
proc_info_set_call_table_tip(MaybeCallTableTip, !ProcInfo),
|
|
proc_info_set_maybe_proc_table_info(MaybeProcTableInfo, !ProcInfo),
|
|
|
|
% Some of the instmap_deltas generated in this module are pretty dodgy
|
|
% (especially those for if-then-elses), so recompute them here.
|
|
RecomputeAtomic = no,
|
|
recompute_instmap_delta_proc(RecomputeAtomic, !ProcInfo, !ModuleInfo),
|
|
|
|
pred_info_get_procedures(!.PredInfo, ProcTable1),
|
|
map.det_update(ProcTable1, ProcId, !.ProcInfo, ProcTable),
|
|
pred_info_set_procedures(ProcTable, !PredInfo),
|
|
|
|
% The transformation doesn't pay attention to the purity of compound goals,
|
|
% so recompute the purity here.
|
|
repuritycheck_proc(!.ModuleInfo, proc(PredId, ProcId), !PredInfo),
|
|
module_info_preds(!.ModuleInfo, PredTable1),
|
|
map.det_update(PredTable1, PredId, !.PredInfo, PredTable),
|
|
module_info_set_preds(PredTable, !ModuleInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Example of transformation for model_det loopcheck:
|
|
%
|
|
% :- pred p(int::in, int::out) is semidet.
|
|
%
|
|
% p(A, B) :-
|
|
% <original code>.
|
|
%
|
|
% The transformed code would be:
|
|
%
|
|
% p(A, B) :-
|
|
% T0 = <table pointer for p/2>,
|
|
% impure table_lookup_insert_int(T0, A, T),
|
|
% impure table_loop_setup(T, Status),
|
|
% (
|
|
% Status = loop_active,
|
|
% error("detected infinite recursion in ...")
|
|
% ;
|
|
% Status = loop_inactive,
|
|
% % status has been changed to active by the setup predicate
|
|
% <original code>,
|
|
% impure table_loop_mark_as_inactive(T)
|
|
% ).
|
|
%
|
|
% Example of transformation for model_semi loopcheck:
|
|
%
|
|
% :- pred p(int::in, int::out) is semidet.
|
|
%
|
|
% p(A, B) :-
|
|
% <original code>.
|
|
%
|
|
% The transformed code would be:
|
|
%
|
|
% p(A, B) :-
|
|
% T0 = <table pointer for p/2>,
|
|
% impure table_lookup_insert_int(T0, A, T),
|
|
% impure table_loop_setup(T, Status),
|
|
% (
|
|
% Status = loop_active,
|
|
% error("detected infinite recursion in ...")
|
|
% ;
|
|
% Status = loop_inactive,
|
|
% % status has been changed to active by the setup predicate
|
|
% (if
|
|
% <original code>, with B replaced by C
|
|
% then
|
|
% B = C,
|
|
% impure table_loop_mark_as_inactive(T)
|
|
% else
|
|
% impure table_loop_mark_as_inactive_and_fail(T),
|
|
% )
|
|
% ).
|
|
%
|
|
% Example of transformation for model_non loopcheck:
|
|
%
|
|
% :- pred p(int::in, int::out) is semidet.
|
|
%
|
|
% p(A, B) :-
|
|
% <original code>.
|
|
%
|
|
% The transformed code would be:
|
|
%
|
|
% p(A, B) :-
|
|
% T0 = <table pointer for p/2>,
|
|
% impure table_lookup_insert_int(T0, A, T),
|
|
% impure table_loop_setup(T, Status),
|
|
% (
|
|
% Status = loop_active,
|
|
% error("detected infinite recursion in ...")
|
|
% ;
|
|
% Status = loop_inactive,
|
|
% % status has been changed to active by the setup predicate
|
|
% (
|
|
% <original code>,
|
|
% (
|
|
% impure table_loop_mark_as_inactive(T)
|
|
% ;
|
|
% impure table_loop_mark_as_active_and_fail(T),
|
|
% fail
|
|
% )
|
|
% ;
|
|
% impure table_loop_mark_as_inactive_and_fail(T)
|
|
% )
|
|
% ).
|
|
|
|
:- pred create_new_loop_goal(determinism::in, hlds_goal::in,
|
|
table_attr_statistics::in, pred_id::in, proc_id::in, list(prog_var)::in,
|
|
list(var_mode_pos_method)::in, list(var_mode_pos_method)::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, prog_var::out, hlds_goal::out,
|
|
list(table_trie_step)::out) is det.
|
|
|
|
create_new_loop_goal(Detism, OrigGoal, Statistics, PredId, ProcId,
|
|
HeadVars, NumberedInputVars, NumberedOutputVars, !VarSet, !VarTypes,
|
|
!TableInfo, TableTipVar, Goal, Steps) :-
|
|
% Even if the original goal doesn't use all of the headvars,
|
|
% the code generated by the tabling transformation does,
|
|
% so we need to compute the nonlocals from the headvars rather
|
|
% than getting it from the nonlocals field in the original goal.
|
|
set.list_to_set(HeadVars, OrigNonLocals),
|
|
OrigGoal = hlds_goal(_, OrigGoalInfo),
|
|
goal_info_get_instmap_delta(OrigGoalInfo, OrigInstMapDelta),
|
|
goal_info_get_context(OrigGoalInfo, Context),
|
|
|
|
ModuleInfo = !.TableInfo ^ table_module_info,
|
|
generate_simple_call_table_lookup_goal(loop_status_type,
|
|
"table_loop_setup_shortcut", "MR_tbl_loop_setup",
|
|
NumberedInputVars, PredId, ProcId, Statistics, Context,
|
|
!VarSet, !VarTypes, !TableInfo, TableTipVar, StatusVar,
|
|
LookUpGoal, Steps),
|
|
|
|
generate_error_goal(!.TableInfo, Context, infinite_recursion_msg,
|
|
!VarSet, !VarTypes, ActiveGoal),
|
|
|
|
TableTipArg = foreign_arg(TableTipVar,
|
|
yes(cur_table_node_name - in_mode), trie_node_type,
|
|
native_if_possible),
|
|
|
|
MarkInactivePredName = "table_loop_mark_as_inactive",
|
|
MarkInactiveMacroName = "MR_tbl_loop_mark_as_inactive",
|
|
MarkInactiveFailPredName = "table_loop_mark_as_inactive_and_fail",
|
|
MarkInactiveFailMacroName = "MR_tbl_loop_mark_as_inactive_and_fail",
|
|
MarkActiveFailPredName = "table_loop_mark_as_active_and_fail",
|
|
MarkActiveFailMacroName = "MR_tbl_loop_mark_as_active_and_fail",
|
|
|
|
DebugArgStr = get_debug_arg_string(!.TableInfo),
|
|
MarkInactiveCode = "\t" ++ MarkInactiveMacroName ++
|
|
"(" ++ DebugArgStr ++ ", " ++ cur_table_node_name ++ ");\n",
|
|
MarkInactiveFailCode = "\t" ++ MarkInactiveFailMacroName ++
|
|
"(" ++ DebugArgStr ++ ", " ++ cur_table_node_name ++ ");\n",
|
|
MarkActiveFailCode = "\t" ++ MarkActiveFailMacroName ++
|
|
"(" ++ DebugArgStr ++ ", " ++ cur_table_node_name ++ ");\n",
|
|
|
|
table_generate_foreign_proc(MarkInactivePredName, detism_det,
|
|
tabling_c_attributes_dupl, [TableTipArg], [],
|
|
MarkInactiveCode, purity_impure, [],
|
|
ModuleInfo, Context, MarkInactiveGoal),
|
|
table_generate_foreign_proc(MarkInactiveFailPredName, detism_failure,
|
|
tabling_c_attributes_dupl, [TableTipArg], [],
|
|
MarkInactiveFailCode, purity_impure, [],
|
|
ModuleInfo, Context, MarkInactiveFailGoal),
|
|
table_generate_foreign_proc(MarkActiveFailPredName, detism_failure,
|
|
tabling_c_attributes_dupl, [TableTipArg], [],
|
|
MarkActiveFailCode, purity_impure, [],
|
|
ModuleInfo, Context, MarkActiveFailGoal),
|
|
|
|
determinism_to_code_model(Detism, CodeModel),
|
|
set.list_to_set([TableTipVar | HeadVars], InactiveNonLocals),
|
|
OutputVars = list.map(project_var, NumberedOutputVars),
|
|
InactiveInstmapDelta = bind_vars(OutputVars),
|
|
(
|
|
CodeModel = model_det,
|
|
InactiveGoalExpr = conj(plain_conj, [OrigGoal, MarkInactiveGoal])
|
|
;
|
|
CodeModel = model_semi,
|
|
goal_info_get_instmap_delta(OrigGoalInfo, InstMapDelta),
|
|
create_renaming(OutputVars, InstMapDelta, !VarSet, !VarTypes,
|
|
Unifies, NewVars, Renaming),
|
|
rename_some_vars_in_goal(Renaming, OrigGoal, RenamedOrigGoal),
|
|
|
|
ThenGoalExpr = conj(plain_conj, Unifies ++ [MarkInactiveGoal]),
|
|
ThenVars = [TableTipVar | OutputVars] ++ NewVars,
|
|
set.list_to_set(ThenVars, ThenNonLocals),
|
|
goal_info_init_hide(ThenNonLocals, InactiveInstmapDelta, Detism,
|
|
purity_impure, Context, ThenGoalInfo),
|
|
ThenGoal = hlds_goal(ThenGoalExpr, ThenGoalInfo),
|
|
|
|
InactiveGoalExpr = if_then_else([], RenamedOrigGoal,
|
|
ThenGoal, MarkInactiveFailGoal)
|
|
;
|
|
CodeModel = model_non,
|
|
AfterGoalExpr = disj([MarkInactiveGoal, MarkActiveFailGoal]),
|
|
instmap_delta_init_reachable(AfterInstMapDelta),
|
|
goal_info_init_hide(set.make_singleton_set(TableTipVar),
|
|
AfterInstMapDelta, detism_multi, purity_impure, Context,
|
|
AfterGoalInfo),
|
|
AfterGoal = hlds_goal(AfterGoalExpr, AfterGoalInfo),
|
|
FirstGoalExpr = conj(plain_conj, [OrigGoal, AfterGoal]),
|
|
goal_info_get_nonlocals(OrigGoalInfo, OrigGINonLocals),
|
|
set.insert(OrigGINonLocals, TableTipVar, FirstNonlocals),
|
|
goal_info_set_nonlocals(FirstNonlocals, OrigGoalInfo, FirstGoalInfo),
|
|
FirstGoal = hlds_goal(FirstGoalExpr, FirstGoalInfo),
|
|
InactiveGoalExpr = disj([FirstGoal, MarkInactiveFailGoal])
|
|
),
|
|
goal_info_init_hide(InactiveNonLocals, InactiveInstmapDelta, Detism,
|
|
purity_impure, Context, InactiveGoalInfo),
|
|
InactiveGoal = hlds_goal(InactiveGoalExpr, InactiveGoalInfo),
|
|
|
|
TB = mercury_table_builtin_module,
|
|
SwitchArms = [
|
|
case(cons(qualified(TB, "loop_active"), 0), ActiveGoal),
|
|
case(cons(qualified(TB, "loop_inactive"), 0), InactiveGoal)
|
|
],
|
|
SwitchExpr = switch(StatusVar, cannot_fail, SwitchArms),
|
|
set.insert_list(InactiveNonLocals, [StatusVar, TableTipVar],
|
|
SwitchNonLocals),
|
|
goal_info_init_hide(SwitchNonLocals, InactiveInstmapDelta, Detism,
|
|
purity_impure, Context, SwitchGoalInfo),
|
|
SwitchGoal = hlds_goal(SwitchExpr, SwitchGoalInfo),
|
|
|
|
GoalExpr = conj(plain_conj, [LookUpGoal, SwitchGoal]),
|
|
goal_info_init_hide(OrigNonLocals, OrigInstMapDelta, Detism,
|
|
purity_impure, Context, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Example of transformation for model_det memo:
|
|
%
|
|
% :- pred p(int::in, int::out) is semidet.
|
|
%
|
|
% p(A, B) :-
|
|
% <original code>.
|
|
%
|
|
% The transformed code would be:
|
|
%
|
|
% p(A, B) :-
|
|
% T0 = <table pointer for p/2>,
|
|
% impure table_lookup_insert_int(T0, A, T),
|
|
% impure table_memo_setup(T, Status),
|
|
% (
|
|
% Status = memo_det_succeeded,
|
|
% semipure table_memo_get_answer_block(T, Block),
|
|
% semipure table_restore_int_answer(Block, 0, B)
|
|
% ;
|
|
% Status = memo_det_active,
|
|
% error("detected infinite recursion in ...")
|
|
% ;
|
|
% Status = memo_det_inactive,
|
|
% % status has been changed to active by the setup predicate
|
|
% <original code>
|
|
% impure table_memo_create_answer_block(T, 1, Block),
|
|
% impure table_save_int_answer(Block, 0, B)
|
|
% ).
|
|
%
|
|
% Example of transformation for model_semi memo:
|
|
%
|
|
% :- pred p(int::in, int::out) is semidet.
|
|
%
|
|
% p(A, B) :-
|
|
% <original code>.
|
|
%
|
|
% The transformed code would be:
|
|
%
|
|
% p(A, B) :-
|
|
% T0 = <table pointer for p/2>,
|
|
% impure table_lookup_insert_int(T0, A, T),
|
|
% impure table_memo_setup(T, Status),
|
|
% (
|
|
% Status = memo_semi_failed,
|
|
% fail
|
|
% ;
|
|
% Status = memo_semi_succeeded,
|
|
% semipure table_memo_get_answer_block(T, Block),
|
|
% semipure table_restore_int_answer(Block, 0, B)
|
|
% ;
|
|
% Status = memo_semi_active,
|
|
% error("detected infinite recursion in ...")
|
|
% ;
|
|
% Status = memo_semi_inactive,
|
|
% % status has been changed to active by the setup predicate
|
|
% (if
|
|
% <original code>, with B replaced by C
|
|
% then
|
|
% B = C,
|
|
% impure table_memo_create_answer_block(T, 1, Block),
|
|
% impure table_save_int_answer(Block, 0, B)
|
|
% else
|
|
% impure table_memo_mark_as_failed(T),
|
|
% fail
|
|
% )
|
|
% ).
|
|
%
|
|
% Example of transformation for model_non memo:
|
|
%
|
|
% :- pred p(int::in, int::out) is semidet.
|
|
%
|
|
% p(A, B) :-
|
|
% <original code>.
|
|
%
|
|
% The transformed code would be:
|
|
%
|
|
% p(A, B) :-
|
|
% CT0 = <table pointer for p/2>,
|
|
% impure table_lookup_insert_int(CT0, A, CT1),
|
|
% impure table_memo_non_setup(CT1, Record, Status),
|
|
% (
|
|
% Status = memo_non_complete,
|
|
% semipure table_memo_return_all_nondet(T, Block),
|
|
% semipure table_restore_int_answer(Block, 0, B)
|
|
% ;
|
|
% Status = memo_non_incomplete,
|
|
% error("detected need for minimal model tabling in ...")
|
|
% ;
|
|
% Status = memo_non_active,
|
|
% error("detected infinite recursion in ...")
|
|
% ;
|
|
% Status = memo_non_inactive,
|
|
% (
|
|
% <original code>,
|
|
%
|
|
% % Check for duplicate answers.
|
|
% semipure table_memo_get_answer_table(Record, AT0),
|
|
% impure table_lookup_insert_int(AT0, B, AT1),
|
|
% % Fail if the answer is already in the table;
|
|
% % otherwise, put it into the table.
|
|
% impure table_mm_answer_is_not_duplicate(AT1),
|
|
%
|
|
% % Save the new answer in the table.
|
|
% impure table_memo_create_answer_block(Record, 1, Block),
|
|
% impure table_save_int_answer(Block, 0, B),
|
|
% (
|
|
% impure table_memo_mark_as_incomplete(R)
|
|
% ;
|
|
% impure table_memo_mark_as_active_and_fail(R),
|
|
% fail
|
|
% )
|
|
% ;
|
|
% impure table_memo_mark_as_complete_and_fail(R)
|
|
% )
|
|
% ).
|
|
%
|
|
% If there are no output variables, then instead of creating an answer block
|
|
% and filling it in, we call table_memo_mark_as_succeeded.
|
|
|
|
:- pred create_new_memo_goal(determinism::in, hlds_goal::in,
|
|
table_attr_statistics::in, maybe(int)::in,
|
|
pred_id::in, proc_id::in, list(prog_var)::in,
|
|
list(var_mode_pos_method)::in, list(var_mode_pos_method)::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, prog_var::out, hlds_goal::out,
|
|
list(table_trie_step)::out) is det.
|
|
|
|
create_new_memo_goal(Detism, OrigGoal, Statistics, _MaybeSizeLimit,
|
|
PredId, ProcId, HeadVars, NumberedInputVars, NumberedOutputVars,
|
|
!VarSet, !VarTypes, !TableInfo, TableTipVar, Goal, Steps) :-
|
|
% Even if the original goal doesn't use all of the headvars,
|
|
% the code generated by the tabling transformation does,
|
|
% so we need to compute the nonlocals from the headvars rather
|
|
% than getting it from the nonlocals field in the original goal.
|
|
set.list_to_set(HeadVars, OrigNonLocals),
|
|
OrigGoal = hlds_goal(_, OrigGoalInfo),
|
|
goal_info_get_instmap_delta(OrigGoalInfo, OrigInstMapDelta),
|
|
goal_info_get_context(OrigGoalInfo, Context),
|
|
|
|
ModuleInfo = !.TableInfo ^ table_module_info,
|
|
determinism_to_code_model(Detism, CodeModel),
|
|
(
|
|
CodeModel = model_det,
|
|
StatusType = memo_det_status_type,
|
|
SetupPredName = "table_memo_det_setup_shortcut",
|
|
SetupMacroName = "MR_tbl_memo_det_setup"
|
|
;
|
|
CodeModel = model_semi,
|
|
StatusType = memo_semi_status_type,
|
|
SetupPredName = "table_memo_semi_setup_shortcut",
|
|
SetupMacroName = "MR_tbl_memo_semi_setup"
|
|
;
|
|
CodeModel = model_non,
|
|
unexpected(this_file, "create_new_memo_goal: model_non")
|
|
),
|
|
generate_simple_call_table_lookup_goal(StatusType,
|
|
SetupPredName, SetupMacroName, NumberedInputVars,
|
|
PredId, ProcId, Statistics, Context, !VarSet, !VarTypes,
|
|
!TableInfo, TableTipVar, StatusVar, LookUpGoal, Steps),
|
|
|
|
generate_error_goal(!.TableInfo, Context, infinite_recursion_msg,
|
|
!VarSet, !VarTypes, ActiveGoal),
|
|
|
|
list.length(NumberedOutputVars, BlockSize),
|
|
generate_memo_save_goal(NumberedOutputVars, TableTipVar, BlockSize,
|
|
Context, !VarSet, !VarTypes, !TableInfo, SaveAnswerGoals),
|
|
generate_memo_restore_goal(NumberedOutputVars, OrigInstMapDelta,
|
|
TableTipVar, Context, !VarSet, !VarTypes, !.TableInfo,
|
|
RestoreAnswerGoal),
|
|
SucceededGoal = RestoreAnswerGoal,
|
|
|
|
set.list_to_set([TableTipVar | HeadVars], InactiveNonLocals),
|
|
OutputVars = list.map(project_var, NumberedOutputVars),
|
|
InactiveInstmapDelta = bind_vars(OutputVars),
|
|
|
|
% The case CodeModel = model_non was caught by the code above.
|
|
(
|
|
CodeModel = model_det,
|
|
InactiveGoalExpr = conj(plain_conj, [OrigGoal | SaveAnswerGoals]),
|
|
goal_info_init_hide(InactiveNonLocals, InactiveInstmapDelta,
|
|
Detism, purity_impure, Context, InactiveGoalInfo),
|
|
InactiveGoal = hlds_goal(InactiveGoalExpr, InactiveGoalInfo),
|
|
|
|
TB = mercury_table_builtin_module,
|
|
SwitchArms = [
|
|
case(cons(qualified(TB, "memo_det_active"), 0), ActiveGoal),
|
|
case(cons(qualified(TB, "memo_det_inactive"), 0), InactiveGoal),
|
|
case(cons(qualified(TB, "memo_det_succeeded"), 0), SucceededGoal)
|
|
]
|
|
;
|
|
CodeModel = model_semi,
|
|
create_renaming(OutputVars, OrigInstMapDelta, !VarSet, !VarTypes,
|
|
Unifies, NewVars, Renaming),
|
|
rename_some_vars_in_goal(Renaming, OrigGoal, RenamedOrigGoal),
|
|
|
|
ThenGoalExpr = conj(plain_conj, Unifies ++ SaveAnswerGoals),
|
|
ThenVars = [TableTipVar | OutputVars] ++ NewVars,
|
|
set.list_to_set(ThenVars, ThenNonLocals),
|
|
goal_info_init_hide(ThenNonLocals, InactiveInstmapDelta,
|
|
detism_det, purity_impure, Context, ThenGoalInfo),
|
|
ThenGoal = hlds_goal(ThenGoalExpr, ThenGoalInfo),
|
|
|
|
MarkAsFailedPredName = "table_memo_mark_as_failed",
|
|
MarkAsFailedMacroName = "MR_tbl_memo_mark_as_failed",
|
|
TableTipArg = foreign_arg(TableTipVar,
|
|
yes(cur_table_node_name - in_mode), trie_node_type,
|
|
native_if_possible),
|
|
DebugArgStr = get_debug_arg_string(!.TableInfo),
|
|
MarkAsFailedCode = MarkAsFailedMacroName ++
|
|
"(" ++ DebugArgStr ++ ", " ++ cur_table_node_name ++ ");",
|
|
table_generate_foreign_proc(MarkAsFailedPredName, detism_failure,
|
|
tabling_c_attributes_dupl, [TableTipArg], [],
|
|
MarkAsFailedCode, purity_impure, [],
|
|
ModuleInfo, Context, ElseGoal),
|
|
InactiveGoalExpr = if_then_else([], RenamedOrigGoal,
|
|
ThenGoal, ElseGoal),
|
|
goal_info_init_hide(InactiveNonLocals, InactiveInstmapDelta, Detism,
|
|
purity_impure, Context, InactiveGoalInfo),
|
|
InactiveGoal = hlds_goal(InactiveGoalExpr, InactiveGoalInfo),
|
|
FailedGoal = fail_goal,
|
|
|
|
TB = mercury_table_builtin_module,
|
|
SwitchArms = [
|
|
case(cons(qualified(TB, "memo_semi_active"), 0), ActiveGoal),
|
|
case(cons(qualified(TB, "memo_semi_inactive"), 0), InactiveGoal),
|
|
case(cons(qualified(TB, "memo_semi_succeeded"), 0), SucceededGoal),
|
|
case(cons(qualified(TB, "memo_semi_failed"), 0), FailedGoal)
|
|
]
|
|
),
|
|
|
|
SwitchExpr = switch(StatusVar, cannot_fail, SwitchArms),
|
|
set.insert(InactiveNonLocals, StatusVar, SwitchNonLocals),
|
|
goal_info_init_hide(SwitchNonLocals, InactiveInstmapDelta,
|
|
Detism, purity_impure, Context, SwitchGoalInfo),
|
|
SwitchGoal = hlds_goal(SwitchExpr, SwitchGoalInfo),
|
|
|
|
GoalExpr = conj(plain_conj, [LookUpGoal, SwitchGoal]),
|
|
goal_info_init_hide(OrigNonLocals, OrigInstMapDelta, Detism, purity_impure,
|
|
Context, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
:- pred create_new_memo_non_goal(determinism::in, hlds_goal::in,
|
|
table_attr_statistics::in, maybe(int)::in,
|
|
pred_id::in, proc_id::in, list(prog_var)::in,
|
|
list(var_mode_pos_method)::in, list(var_mode_pos_method)::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, prog_var::out, hlds_goal::out,
|
|
list(table_trie_step)::out, list(table_trie_step)::out) is det.
|
|
|
|
create_new_memo_non_goal(Detism, OrigGoal, Statistics, _MaybeSizeLimit,
|
|
PredId, ProcId, HeadVars, NumberedInputVars, NumberedOutputVars,
|
|
!VarSet, !VarTypes, !TableInfo, RecordVar, Goal,
|
|
InputSteps, OutputSteps) :-
|
|
% Even if the original goal doesn't use all of the headvars,
|
|
% the code generated by the tabling transformation does,
|
|
% so we need to compute the nonlocals from the headvars rather
|
|
% than getting it from the nonlocals field in the original goal.
|
|
set.list_to_set(HeadVars, OrigNonLocals),
|
|
OrigGoal = hlds_goal(_, OrigGoalInfo),
|
|
goal_info_get_instmap_delta(OrigGoalInfo, OrigInstMapDelta),
|
|
goal_info_get_context(OrigGoalInfo, Context),
|
|
|
|
ModuleInfo = !.TableInfo ^ table_module_info,
|
|
list.length(NumberedOutputVars, BlockSize),
|
|
|
|
generate_error_goal(!.TableInfo, Context, infinite_recursion_msg,
|
|
!VarSet, !VarTypes, InfiniteRecursionGoal),
|
|
generate_error_goal(!.TableInfo, Context, need_minimal_model_msg,
|
|
!VarSet, !VarTypes, NeedMinModelGoal),
|
|
|
|
generate_memo_non_call_table_lookup_goal(NumberedInputVars,
|
|
PredId, ProcId, Statistics, Context, !VarSet, !VarTypes,
|
|
!TableInfo, RecordVar, StatusVar, LookUpGoal, InputSteps),
|
|
generate_memo_non_save_goals(NumberedOutputVars, PredId, ProcId,
|
|
RecordVar, BlockSize, Statistics, Context, !VarSet, !VarTypes,
|
|
!TableInfo, OutputSteps, SaveAnswerGoals),
|
|
|
|
generate_memo_non_restore_goal(Detism, NumberedOutputVars,
|
|
OrigInstMapDelta, RecordVar, Context,
|
|
!VarSet, !VarTypes, !.TableInfo, RestoreAllAnswerGoal),
|
|
|
|
RecordVarName = memo_non_record_name,
|
|
RecordArg = foreign_arg(RecordVar,
|
|
yes(RecordVarName - in_mode), memo_non_record_type,
|
|
native_if_possible),
|
|
|
|
MarkIncompletePredName = "table_memo_mark_as_incomplete",
|
|
MarkIncompleteMacroName = "MR_tbl_memo_mark_as_incomplete",
|
|
MarkActivePredName = "table_memo_mark_as_active_and_fail",
|
|
MarkActiveMacroName = "MR_tbl_memo_mark_as_active_and_fail",
|
|
MarkCompletePredName = "table_memo_mark_as_complete_and_fail",
|
|
MarkCompleteMacroName = "MR_tbl_memo_mark_as_complete_and_fail",
|
|
|
|
DebugArgStr = get_debug_arg_string(!.TableInfo),
|
|
MarkIncompleteCode = MarkIncompleteMacroName ++
|
|
"(" ++ DebugArgStr ++ ", " ++ RecordVarName ++ ");\n",
|
|
MarkActiveCode = MarkActiveMacroName ++
|
|
"(" ++ DebugArgStr ++ ", " ++ RecordVarName ++ ");\n",
|
|
MarkCompleteCode = MarkCompleteMacroName ++
|
|
"(" ++ DebugArgStr ++ ", " ++ RecordVarName ++ ");\n",
|
|
|
|
table_generate_foreign_proc(MarkIncompletePredName, detism_det,
|
|
tabling_c_attributes_dupl, [RecordArg], [],
|
|
MarkIncompleteCode, purity_impure, [],
|
|
ModuleInfo, Context, MarkIncompleteGoal),
|
|
table_generate_foreign_proc(MarkActivePredName, detism_failure,
|
|
tabling_c_attributes_dupl, [RecordArg], [],
|
|
MarkActiveCode, purity_impure, [],
|
|
ModuleInfo, Context, MarkActiveGoal),
|
|
table_generate_foreign_proc(MarkCompletePredName, detism_failure,
|
|
tabling_c_attributes_dupl, [RecordArg], [],
|
|
MarkCompleteCode, purity_impure, [],
|
|
ModuleInfo, Context, MarkCompleteGoal),
|
|
|
|
OrigSaveExpr = conj(plain_conj, [OrigGoal | SaveAnswerGoals]),
|
|
set.insert(OrigNonLocals, RecordVar, OrigSaveNonLocals),
|
|
create_instmap_delta([OrigGoal | SaveAnswerGoals], OrigSaveIMD0),
|
|
instmap_delta_restrict(OrigSaveNonLocals, OrigSaveIMD0, OrigSaveIMD),
|
|
goal_info_init_hide(OrigSaveNonLocals, OrigSaveIMD, detism_non,
|
|
purity_impure, Context, OrigSaveGoalInfo),
|
|
OrigSaveGoal = hlds_goal(OrigSaveExpr, OrigSaveGoalInfo),
|
|
|
|
AfterExpr = disj([MarkIncompleteGoal, MarkActiveGoal]),
|
|
AfterNonLocals = set.make_singleton_set(RecordVar),
|
|
create_instmap_delta([], AfterInstMapDelta),
|
|
goal_info_init_hide(AfterNonLocals, AfterInstMapDelta, detism_non,
|
|
purity_impure, Context, AfterGoalInfo),
|
|
AfterGoal = hlds_goal(AfterExpr, AfterGoalInfo),
|
|
|
|
OrigSaveAfterExpr = conj(plain_conj, [OrigSaveGoal, AfterGoal]),
|
|
OrigSaveAfterGoal = hlds_goal(OrigSaveAfterExpr, OrigSaveGoalInfo),
|
|
|
|
InactiveExpr = disj([OrigSaveAfterGoal, MarkCompleteGoal]),
|
|
InactiveGoal = hlds_goal(InactiveExpr, OrigSaveGoalInfo),
|
|
|
|
set.list_to_set([RecordVar | HeadVars], InactiveNonLocals),
|
|
OutputVars = list.map(project_var, NumberedOutputVars),
|
|
InactiveInstmapDelta = bind_vars(OutputVars),
|
|
|
|
TB = mercury_table_builtin_module,
|
|
SwitchArms = [
|
|
case(cons(qualified(TB, "memo_non_active"), 0), InfiniteRecursionGoal),
|
|
case(cons(qualified(TB, "memo_non_inactive"), 0), InactiveGoal),
|
|
case(cons(qualified(TB, "memo_non_incomplete"), 0), NeedMinModelGoal),
|
|
case(cons(qualified(TB, "memo_non_complete"), 0), RestoreAllAnswerGoal)
|
|
],
|
|
|
|
SwitchExpr = switch(StatusVar, cannot_fail, SwitchArms),
|
|
set.insert(InactiveNonLocals, StatusVar, SwitchNonLocals),
|
|
goal_info_init_hide(SwitchNonLocals, InactiveInstmapDelta, Detism,
|
|
purity_impure, Context, SwitchGoalInfo),
|
|
SwitchGoal = hlds_goal(SwitchExpr, SwitchGoalInfo),
|
|
|
|
GoalExpr = conj(plain_conj, [LookUpGoal, SwitchGoal]),
|
|
goal_info_init_hide(OrigNonLocals, OrigInstMapDelta, Detism, purity_impure,
|
|
Context, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Example of transformation for tabling I/O, for I/O primitives (i.e.
|
|
% predicates defined by foreign_procs that take an input/output pair of
|
|
% io_state arguments) that have the tabled_for_io feature:
|
|
%
|
|
% :- pred p(int, string, io, io).
|
|
% :- mode p(in, out, di, uo) is det.
|
|
%
|
|
% p(A, B, S0, S) :-
|
|
% <original code>
|
|
%
|
|
% The transformed code would be:
|
|
%
|
|
% p(A, B, S0, S) :-
|
|
% (if
|
|
% % Get the global I/O table, the global I/O
|
|
% % counter, and the starting point for tabling
|
|
% % I/O actions, if we are in the tabled range.
|
|
% table_io_in_range(T0, Counter, Start)
|
|
% then
|
|
% % Look up the input arguments.
|
|
% impure table_lookup_insert_start_int(T0, Counter,
|
|
% Start, T),
|
|
% (if
|
|
% semipure table_io_has_occurred(T)
|
|
% then
|
|
% semipure table_memo_get_answer_block(T, Block),
|
|
% impure table_restore_string_answer(Block, 0, B),
|
|
% table_io_copy_io_state(S0, S)
|
|
% else
|
|
% <original code>
|
|
% % Save the answers in the table.
|
|
% impure table_io_create_answer_block(T, 1, Block),
|
|
% impure table_save_string_answer(Block, 0, B)
|
|
% )
|
|
% else
|
|
% <original code>
|
|
% ).
|
|
%
|
|
% Note that copying the I/O state works only because variables of type
|
|
% io.state don't actually contain any information; the information is actually
|
|
% stored in global variables. However, if this ever changes, the transformation
|
|
% can be fixed simply by changing the value of --trace-table-io-states to yes,
|
|
% which will cause such values to be tabled along with the other output
|
|
% arguments.
|
|
%
|
|
% For I/O primitives that do not have tabled_for_io, we should require that
|
|
% they do not do any I/O in their own code, meaning that all their I/O is
|
|
% inside any Mercury code they call. We can then leave such primitives
|
|
% untransformed; the I/O primitives called from the inner Mercury engine
|
|
% will do the right thing. For now, this requirement is not enforced,
|
|
% which means that enabling I/O tabling (with --trace-table-io) does not
|
|
% guarantee that *all* I/O actions are tabled. This can cause inconsistent
|
|
% behavior after retry commands in mdb. This is the reason why retry across
|
|
% I/O is experimental for now.
|
|
%
|
|
% The reason why we require I/O primitives to be marked manually by a
|
|
% programmer with the tabled_for_io feature is to get the programmer to make
|
|
% sure that the primitive meets the requirement. Unfortunately, this cannot be
|
|
% automated, since automation would require analysis of arbitrary C code.
|
|
%
|
|
% The transformation for tabling I/O for declarative debugging is a variant
|
|
% of the usual transformation of I/O primitives. In this variant, the answer
|
|
% block contains not only the answers, but also a pointer to the primitive's
|
|
% proc layout structure and the values of the input aruments. The code we
|
|
% generate will fill in the slots containing this extra information before
|
|
% it executes the original goal.
|
|
|
|
:- pred create_new_io_goal(hlds_goal::in, table_io_is_decl::in,
|
|
table_io_is_unitize::in, bool::in, pred_id::in, proc_id::in,
|
|
assoc_list(prog_var, mer_mode)::in,
|
|
list(var_mode_pos_method)::in, list(var_mode_pos_method)::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out,
|
|
hlds_goal::out, maybe(proc_table_info)::out) is det.
|
|
|
|
create_new_io_goal(OrigGoal, TableDecl, Unitize, TableIoStates,
|
|
PredId, ProcId, HeadVarModes, OrigInputVars, OrigOutputVars,
|
|
!VarSet, !VarTypes, !TableInfo, Goal, MaybeProcTableInfo) :-
|
|
OrigGoal = hlds_goal(_, OrigGoalInfo),
|
|
ModuleInfo0 = !.TableInfo ^ table_module_info,
|
|
module_info_pred_info(ModuleInfo0, PredId, PredInfo),
|
|
pred_info_get_markers(PredInfo, Markers),
|
|
( check_marker(Markers, marker_user_marked_no_inline) ->
|
|
%
|
|
% If the predicate should not be inlined, then we create a new
|
|
% predicate with the same body as the original predicate, which is
|
|
% called wherever the original goal would appear in the transformed
|
|
% code. This is necessary when the original goal is foreign C code
|
|
% that uses labels. The original goal would otherwise be duplicated
|
|
% by the transformation, resulting in duplicate label errors from
|
|
% the C compiler.
|
|
%
|
|
clone_proc_and_create_call(PredInfo, ProcId, CallExpr, ModuleInfo0,
|
|
ModuleInfo),
|
|
NewGoal = hlds_goal(CallExpr, OrigGoalInfo),
|
|
!:TableInfo = !.TableInfo ^ table_module_info := ModuleInfo
|
|
;
|
|
NewGoal = OrigGoal,
|
|
ModuleInfo = ModuleInfo0
|
|
),
|
|
goal_info_get_nonlocals(OrigGoalInfo, OrigNonLocals),
|
|
goal_info_get_context(OrigGoalInfo, Context),
|
|
|
|
(
|
|
TableIoStates = yes,
|
|
IoStateAssignToVars = [],
|
|
IoStateAssignFromVars = [],
|
|
SavedOutputVars = OrigOutputVars,
|
|
SavedHeadVars = HeadVarModes
|
|
;
|
|
TableIoStates = no,
|
|
list.filter(var_mode_pos_is_io_state(!.VarTypes),
|
|
OrigOutputVars, IoStateAssignToVars, MisNumberedSavedOutputVars),
|
|
reallocate_slot_numbers(MisNumberedSavedOutputVars, 0,
|
|
SavedOutputVars),
|
|
list.filter(var_mode_pos_is_io_state(!.VarTypes),
|
|
OrigInputVars, IoStateAssignFromVars, _MisNumberedSavedInputVars),
|
|
list.filter(var_mode_is_io_state(!.VarTypes),
|
|
HeadVarModes, _, SavedHeadVars)
|
|
),
|
|
generate_new_table_var("TableVar", trie_node_type, !VarSet, !VarTypes,
|
|
TableVar),
|
|
generate_new_table_var("CounterVar", int_type, !VarSet, !VarTypes,
|
|
CounterVar),
|
|
generate_new_table_var("StartVar", int_type, !VarSet, !VarTypes,
|
|
StartVar),
|
|
table_generate_call("table_io_in_range", detism_semi,
|
|
[TableVar, CounterVar, StartVar], purity_impure,
|
|
ground_vars([TableVar, CounterVar, StartVar]),
|
|
ModuleInfo, Context, InRangeGoal),
|
|
generate_new_table_var("TipVar", trie_node_type, !VarSet, !VarTypes,
|
|
TipVar),
|
|
table_generate_call("table_lookup_insert_start_int", detism_det,
|
|
[TableVar, StartVar, CounterVar, TipVar], purity_impure,
|
|
ground_vars([TipVar]), ModuleInfo, Context, LookupGoal),
|
|
table_generate_call("table_io_has_occurred", detism_semi, [TipVar],
|
|
purity_impure, [], ModuleInfo, Context, OccurredGoal),
|
|
(
|
|
TableDecl = table_io_decl,
|
|
ShroudedPredProcId = shroud_pred_proc_id(proc(PredId, ProcId)),
|
|
TableIoDeclConsId = table_io_decl(ShroudedPredProcId),
|
|
make_const_construction_alloc(TableIoDeclConsId, c_pointer_type,
|
|
yes("TableIoDeclPtr"), TableIoDeclGoal, TableIoDeclPtrVar,
|
|
!VarSet, !VarTypes),
|
|
allocate_plain_slot_numbers(SavedHeadVars, 1, NumberedSavedHeadVars),
|
|
NumberedSaveVars = [
|
|
var_mode_pos_method(TableIoDeclPtrVar, in_mode, 0, unit)
|
|
| NumberedSavedHeadVars],
|
|
UnnumberedSavedOutputVars = list.map(project_var, SavedOutputVars),
|
|
list.filter(var_belong_to_list(UnnumberedSavedOutputVars),
|
|
NumberedSaveVars, NumberedSavedOutputVars),
|
|
NumberedRestoreVars = NumberedSavedOutputVars,
|
|
|
|
ProcInfo0 = !.TableInfo ^ table_cur_proc_info,
|
|
continuation_info.generate_table_arg_type_info(ProcInfo0,
|
|
list.map(project_var_pos, NumberedSavedHeadVars),
|
|
TableArgTypeInfo),
|
|
ProcTableInfo = table_io_decl_info(TableArgTypeInfo),
|
|
MaybeProcTableInfo = yes(ProcTableInfo)
|
|
;
|
|
TableDecl = table_io_proc,
|
|
TableIoDeclGoal = true_goal,
|
|
NumberedRestoreVars =
|
|
list.map(project_out_arg_method, SavedOutputVars),
|
|
NumberedSaveVars = list.map(project_out_arg_method, SavedOutputVars),
|
|
MaybeProcTableInfo = no
|
|
),
|
|
list.length(NumberedSaveVars, BlockSize),
|
|
goal_info_get_instmap_delta(OrigGoalInfo, OrigInstMapDelta),
|
|
generate_memo_restore_goal(NumberedRestoreVars, OrigInstMapDelta, TipVar,
|
|
Context, !VarSet, !VarTypes, !.TableInfo, RestoreAnswerGoal0),
|
|
(
|
|
TableIoStates = yes,
|
|
RestoreAnswerGoal = RestoreAnswerGoal0
|
|
;
|
|
TableIoStates = no,
|
|
(
|
|
IoStateAssignFromVars = [IoStateAssignFromVarPrime],
|
|
IoStateAssignToVars = [IoStateAssignToVarPrime]
|
|
->
|
|
IoStateAssignFromVar = project_var(IoStateAssignFromVarPrime),
|
|
IoStateAssignToVar = project_var(IoStateAssignToVarPrime)
|
|
;
|
|
% The call to proc_info_has_io_state_pair in
|
|
% table_gen_process_procs should ensure that we never get here.
|
|
unexpected(this_file,
|
|
"create_new_io_goal: one in / one out violation")
|
|
),
|
|
table_generate_call("table_io_copy_io_state", detism_det,
|
|
[IoStateAssignFromVar, IoStateAssignToVar], purity_pure,
|
|
[IoStateAssignFromVar - ground(clobbered, none),
|
|
IoStateAssignToVar - ground(unique, none)],
|
|
ModuleInfo, Context, IoStateAssignGoal),
|
|
RestoreAnswerGoalExpr = conj(plain_conj,
|
|
[RestoreAnswerGoal0, IoStateAssignGoal]),
|
|
create_instmap_delta([RestoreAnswerGoal0, IoStateAssignGoal],
|
|
RestoreAnswerInstMapDelta0),
|
|
RestoreAnswerGoal0 = hlds_goal(_, RestoreAnswerGoal0Info),
|
|
goal_info_get_nonlocals(RestoreAnswerGoal0Info,
|
|
RestoreAnswer0NonLocals),
|
|
set.insert_list(RestoreAnswer0NonLocals,
|
|
[IoStateAssignFromVar, IoStateAssignToVar],
|
|
RestoreAnswerNonLocals),
|
|
instmap_delta_restrict(RestoreAnswerNonLocals,
|
|
RestoreAnswerInstMapDelta0, RestoreAnswerInstMapDelta),
|
|
goal_info_init_hide(RestoreAnswerNonLocals,
|
|
RestoreAnswerInstMapDelta, detism_det, purity_semipure, Context,
|
|
RestoreAnswerGoalInfo),
|
|
RestoreAnswerGoal = hlds_goal(RestoreAnswerGoalExpr,
|
|
RestoreAnswerGoalInfo)
|
|
),
|
|
generate_memo_save_goal(NumberedSaveVars, TipVar, BlockSize,
|
|
Context, !VarSet, !VarTypes, !TableInfo, SaveAnswerGoals),
|
|
(
|
|
Unitize = table_io_alone,
|
|
CallSaveAnswerGoalList = [NewGoal, TableIoDeclGoal | SaveAnswerGoals]
|
|
;
|
|
Unitize = table_io_unitize,
|
|
generate_new_table_var("SavedTraceEnabled", int_type,
|
|
!VarSet, !VarTypes, SavedTraceEnabledVar),
|
|
table_generate_call("table_io_left_bracket_unitized_goal", detism_det,
|
|
[SavedTraceEnabledVar], purity_impure,
|
|
ground_vars([SavedTraceEnabledVar]),
|
|
ModuleInfo, Context, LeftBracketGoal),
|
|
table_generate_call("table_io_right_bracket_unitized_goal", detism_det,
|
|
[SavedTraceEnabledVar], purity_impure, [],
|
|
ModuleInfo, Context, RightBracketGoal),
|
|
CallSaveAnswerGoalList = [LeftBracketGoal, NewGoal,
|
|
RightBracketGoal, TableIoDeclGoal | SaveAnswerGoals]
|
|
),
|
|
CallSaveAnswerGoalExpr = conj(plain_conj, CallSaveAnswerGoalList),
|
|
create_instmap_delta(CallSaveAnswerGoalList, CallSaveAnswerInstMapDelta0),
|
|
set.insert(OrigNonLocals, TipVar, CallSaveAnswerNonLocals),
|
|
instmap_delta_restrict(CallSaveAnswerNonLocals,
|
|
CallSaveAnswerInstMapDelta0, CallSaveAnswerInstMapDelta),
|
|
goal_info_init_hide(CallSaveAnswerNonLocals, CallSaveAnswerInstMapDelta,
|
|
detism_det, purity_impure, Context, CallSaveAnswerGoalInfo0),
|
|
goal_info_add_feature(feature_hide_debug_event,
|
|
CallSaveAnswerGoalInfo0, CallSaveAnswerGoalInfo),
|
|
CallSaveAnswerGoal = hlds_goal(CallSaveAnswerGoalExpr,
|
|
CallSaveAnswerGoalInfo),
|
|
|
|
GenIfNecGoalExpr = if_then_else([], OccurredGoal,
|
|
RestoreAnswerGoal, CallSaveAnswerGoal),
|
|
create_instmap_delta([OccurredGoal, RestoreAnswerGoal,
|
|
CallSaveAnswerGoal], GenIfNecInstMapDelta0),
|
|
set.insert(OrigNonLocals, TipVar, GenIfNecNonLocals),
|
|
instmap_delta_restrict(GenIfNecNonLocals,
|
|
GenIfNecInstMapDelta0, GenIfNecInstMapDelta),
|
|
goal_info_init_hide(GenIfNecNonLocals, GenIfNecInstMapDelta, detism_det,
|
|
purity_impure, Context, GenIfNecGoalInfo),
|
|
GenIfNecGoal = hlds_goal(GenIfNecGoalExpr, GenIfNecGoalInfo),
|
|
|
|
CheckAndGenAnswerGoalExpr = conj(plain_conj, [LookupGoal, GenIfNecGoal]),
|
|
create_instmap_delta([LookupGoal, GenIfNecGoal],
|
|
CheckAndGenAnswerInstMapDelta0),
|
|
set.insert_list(OrigNonLocals, [TableVar, CounterVar, StartVar],
|
|
CheckAndGenAnswerNonLocals),
|
|
instmap_delta_restrict(CheckAndGenAnswerNonLocals,
|
|
CheckAndGenAnswerInstMapDelta0, CheckAndGenAnswerInstMapDelta),
|
|
goal_info_init_hide(CheckAndGenAnswerNonLocals,
|
|
CheckAndGenAnswerInstMapDelta, detism_det, purity_impure, Context,
|
|
CheckAndGenAnswerGoalInfo),
|
|
CheckAndGenAnswerGoal = hlds_goal(CheckAndGenAnswerGoalExpr,
|
|
CheckAndGenAnswerGoalInfo),
|
|
|
|
BodyGoalExpr = if_then_else([], InRangeGoal, CheckAndGenAnswerGoal,
|
|
NewGoal),
|
|
create_instmap_delta([InRangeGoal, CheckAndGenAnswerGoal, NewGoal],
|
|
BodyInstMapDelta0),
|
|
instmap_delta_restrict(OrigNonLocals, BodyInstMapDelta0, BodyInstMapDelta),
|
|
goal_info_init_hide(OrigNonLocals, BodyInstMapDelta, detism_det,
|
|
purity_impure, Context, BodyGoalInfo),
|
|
Goal = hlds_goal(BodyGoalExpr, BodyGoalInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Example of transformation for nondet minimal_model:
|
|
%
|
|
% :- pred p(int, int).
|
|
% :- mode p(in, out) is nondet.
|
|
%
|
|
% p(A, B) :-
|
|
% <original code>.
|
|
%
|
|
% The transformed code would be:
|
|
%
|
|
% p(A, B) :-
|
|
% % Get a handle on the table.
|
|
% CT0 = <table pointer for p/2>,
|
|
%
|
|
% % Look up the input arguments, and set up the table.
|
|
% impure table_lookup_insert_int(CT0, A, CT1),
|
|
% impure table_mm_setup(CT1, Subgoal, Status),
|
|
% (
|
|
% Status = complete,
|
|
% % Return all the answers from the complete table.
|
|
% semipure table_mm_return_all_nondet(Subgoal, Block),
|
|
% semipure table_restore_int_answer(Block, 0, B)
|
|
% ;
|
|
% Status = active,
|
|
% % Suspend the current computational branch.
|
|
% % Resume when the generator has computed some answers.
|
|
% impure table_mm_suspend_consumer(Subgoal, Block),
|
|
% semipure table_restore_int_answer(Block, 0, B)
|
|
% ;
|
|
% Status = inactive,
|
|
% (
|
|
% <original code>,
|
|
%
|
|
% % Check for duplicate answers.
|
|
% semipure table_mm_get_answer_table(Subgoal, AT0),
|
|
% impure table_lookup_insert_int(AT0, B, AT1),
|
|
% % Fail if the answer is already in the table;
|
|
% % otherwise, put it into the table.
|
|
% impure table_mm_answer_is_not_duplicate(AT1),
|
|
%
|
|
% % Save the new answer in the table.
|
|
% impure table_mm_create_answer_block(Subgoal, 1, Block),
|
|
% impure table_save_int_answer(Block, 0, B)
|
|
% ;
|
|
% % Mark this subgoal as completely evaluated,
|
|
% % modulo any dependencies on other subgoals.
|
|
% impure table_mm_completion(Subgoal),
|
|
% fail
|
|
% )
|
|
% ).
|
|
|
|
:- pred create_new_mm_goal(determinism::in, hlds_goal::in,
|
|
table_attr_statistics::in, pred_id::in, proc_id::in, list(prog_var)::in,
|
|
list(var_mode_pos_method)::in, list(var_mode_pos_method)::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, prog_var::out, hlds_goal::out,
|
|
list(table_trie_step)::out, list(table_trie_step)::out) is det.
|
|
|
|
create_new_mm_goal(Detism, OrigGoal, Statistics, PredId, ProcId,
|
|
HeadVars, NumberedInputVars, NumberedOutputVars, !VarSet, !VarTypes,
|
|
!TableInfo, SubgoalVar, Goal, InputSteps, OutputSteps) :-
|
|
% Even if the original goal doesn't use all of the headvars,
|
|
% the code generated by the tabling transformation does,
|
|
% so we need to compute the nonlocals from the headvars rather
|
|
% than getting it from the nonlocals field in the original goal.
|
|
set.list_to_set(HeadVars, OrigNonLocals),
|
|
OrigGoal = hlds_goal(_, OrigGoalInfo),
|
|
goal_info_get_instmap_delta(OrigGoalInfo, OrigInstMapDelta),
|
|
goal_info_get_context(OrigGoalInfo, Context),
|
|
|
|
ModuleInfo = !.TableInfo ^ table_module_info,
|
|
list.length(NumberedOutputVars, BlockSize),
|
|
generate_mm_call_table_lookup_goal(NumberedInputVars, PredId, ProcId,
|
|
Statistics, Context, !VarSet, !VarTypes, !TableInfo,
|
|
SubgoalVar, StatusVar, LookUpGoal, InputSteps),
|
|
generate_mm_save_goals(NumberedOutputVars, SubgoalVar, PredId, ProcId,
|
|
BlockSize, Statistics, Context, !VarSet, !VarTypes, !TableInfo,
|
|
OutputSteps, SaveAnswerGoals),
|
|
generate_mm_restore_goal(Detism, NumberedOutputVars, OrigInstMapDelta,
|
|
SubgoalVar, Context, !VarSet, !VarTypes, !.TableInfo,
|
|
RestoreAllAnswerGoal),
|
|
generate_mm_suspend_goal(NumberedOutputVars, OrigInstMapDelta,
|
|
SubgoalVar, Context, !VarSet, !VarTypes, !.TableInfo, SuspendGoal),
|
|
|
|
MainExpr = conj(plain_conj, [OrigGoal | SaveAnswerGoals]),
|
|
set.insert_list(OrigNonLocals, [SubgoalVar, StatusVar], MainNonLocals),
|
|
create_instmap_delta([OrigGoal | SaveAnswerGoals], MainIMD0),
|
|
instmap_delta_restrict(MainNonLocals, MainIMD0, MainIMD),
|
|
goal_info_init_hide(MainNonLocals, MainIMD, detism_non, purity_impure,
|
|
Context, MainGoalInfo),
|
|
MainGoal = hlds_goal(MainExpr, MainGoalInfo),
|
|
|
|
table_generate_call("table_mm_completion", detism_det, [SubgoalVar],
|
|
purity_impure, [], ModuleInfo, Context, ResumeGoal0),
|
|
append_fail(ResumeGoal0, ResumeGoal),
|
|
InactiveExpr = disj([MainGoal, ResumeGoal]),
|
|
InactiveGoal = hlds_goal(InactiveExpr, MainGoalInfo),
|
|
|
|
TB = mercury_table_builtin_module,
|
|
SwitchArms = [
|
|
case(cons(qualified(TB, "mm_inactive"), 0), InactiveGoal),
|
|
case(cons(qualified(TB, "mm_complete"), 0), RestoreAllAnswerGoal),
|
|
case(cons(qualified(TB, "mm_active"), 0), SuspendGoal)
|
|
],
|
|
SwitchExpr = switch(StatusVar, cannot_fail, SwitchArms),
|
|
goal_info_add_feature(feature_hide_debug_event,
|
|
MainGoalInfo, SwitchGoalInfo),
|
|
SwitchGoal = hlds_goal(SwitchExpr, SwitchGoalInfo),
|
|
|
|
GoalExpr = conj(plain_conj, [LookUpGoal, SwitchGoal]),
|
|
goal_info_init_hide(OrigNonLocals, OrigInstMapDelta, detism_non,
|
|
purity_impure, Context, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Example of transformation for nondet minimal_model_own_stack:
|
|
%
|
|
% :- pred p(int, int).
|
|
% :- mode p(in, out) is nondet
|
|
%
|
|
% p(A, B) :- e(A, B).
|
|
% p(A, B) :- p(A, C), e(C, B).
|
|
%
|
|
% The transformed code would be something like this; see also forest.ps
|
|
% in the tabling directory in the papers CVS module.
|
|
%
|
|
% p2_gen(A, B) :-
|
|
% impure table_mmos_pickup_generator(Generator),
|
|
% (
|
|
% (
|
|
% %
|
|
% % Original goals
|
|
% %
|
|
% ),
|
|
%
|
|
% % Check for duplicate answers.
|
|
% semipure table_mmos_get_answer_table(Generator, AT0),
|
|
% impure table_lookup_insert_int(AT0, B, AT1),
|
|
%
|
|
% % Fail if the answer is already in the table;
|
|
% % otherwise, put it into the table.
|
|
% impure table_mmos_answer_is_not_duplicate(AT1),
|
|
%
|
|
% % Save the new answer in the table.
|
|
% impure table_mmos_create_answer_block(Generator, 1, AnswerBlock),
|
|
% impure table_save_int_ans(AnswerBlock, 0, B),
|
|
% ;
|
|
% impure table_mmos_completion(Generator)
|
|
% ).
|
|
% MR_succeed is replaced by table_mmos_return_answer(Generator).
|
|
%
|
|
% p(A, B) :-
|
|
% CT0 = <table pointer for p/2>,
|
|
% impure table_lookup_insert_int(CT0, A, CT1),
|
|
% ( CT1 = NULL ->
|
|
% table_mmos_save_inputs(0, A), % into global var MR_mmos_input_arg
|
|
% Generator = table_mmos_setup_generator(CT1, 1, p2_gen, "p"),
|
|
% ;
|
|
% Generator = trie_node_to_generator(CT1)
|
|
% ),
|
|
% impure table_mmos_setup_consumer(Generator, Consumer),
|
|
% impure table_mmos_consume_next_answer_nondet(Consumer, AnswerBlock),
|
|
% impure table_restore_int_ans(AnswerBlock, 0, B).
|
|
|
|
:- pred do_own_stack_transform(determinism::in, hlds_goal::in,
|
|
table_attr_statistics::in, pred_id::in, proc_id::in,
|
|
pred_info::in, proc_info::in, list(prog_var)::in,
|
|
list(var_mode_pos_method)::in, list(var_mode_pos_method)::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, generator_map::in, generator_map::out,
|
|
hlds_goal::out, list(table_trie_step)::out, list(table_trie_step)::out)
|
|
is det.
|
|
|
|
do_own_stack_transform(Detism, OrigGoal, Statistics, PredId, ProcId,
|
|
PredInfo0, ProcInfo0, HeadVars, NumberedInputVars, NumberedOutputVars,
|
|
!VarSet, !VarTypes, !TableInfo, !GenMap, Goal,
|
|
InputSteps, OutputSteps) :-
|
|
PredName = pred_info_name(PredInfo0),
|
|
( map.search(!.GenMap, PredId, GeneratorPredIdPrime) ->
|
|
GeneratorPredId = GeneratorPredIdPrime
|
|
;
|
|
clone_pred_info(PredId, PredInfo0, HeadVars, NumberedOutputVars,
|
|
GeneratorPredId, !TableInfo),
|
|
map.det_insert(!.GenMap, PredId, GeneratorPredId, !:GenMap)
|
|
),
|
|
|
|
% Even if the original goal doesn't use all of the headvars,
|
|
% the code generated by the tabling transformation does,
|
|
% so we need to compute the nonlocals from the headvars rather
|
|
% than getting it from the nonlocals field in the original goal.
|
|
set.list_to_set(HeadVars, OrigNonLocals),
|
|
OrigGoal = hlds_goal(_, OrigGoalInfo),
|
|
goal_info_get_instmap_delta(OrigGoalInfo, OrigInstMapDelta),
|
|
goal_info_get_context(OrigGoalInfo, Context),
|
|
|
|
list.length(NumberedInputVars, NumInputVars),
|
|
ModuleInfo = !.TableInfo ^ table_module_info,
|
|
|
|
% The type is a lie, but a safe one: the variable we create
|
|
% is handled only by C code. The reason why we lie is that
|
|
% creating the right type would be quite complicated.
|
|
GeneratorPredType = c_pointer_type,
|
|
|
|
generate_new_table_var("GeneratorPredVar", GeneratorPredType,
|
|
!VarSet, !VarTypes, GeneratorPredVar),
|
|
generate_new_table_var("Consumer", consumer_type,
|
|
!VarSet, !VarTypes, ConsumerVar),
|
|
|
|
ShroudedPredProcId = shroud_pred_proc_id(proc(GeneratorPredId, ProcId)),
|
|
GeneratorConsId = pred_const(ShroudedPredProcId, lambda_normal),
|
|
make_const_construction(GeneratorPredVar, GeneratorConsId,
|
|
MakeGeneratorVarGoal),
|
|
|
|
generate_call_table_lookup_goals(NumberedInputVars,
|
|
GeneratorPredId, ProcId,
|
|
Statistics, Context, !VarSet, !VarTypes, !TableInfo, InputSteps,
|
|
_TableTipVar, _TableTipArg, InfoArg, LookupForeignArgs,
|
|
LookupPrefixGoals, LookupCodeStr, _CallTableTipAssignStr),
|
|
|
|
InputVarModes = list.map(project_mode, NumberedInputVars),
|
|
assoc_list.from_corresponding_lists(LookupForeignArgs, InputVarModes,
|
|
LookupForeignArgModes),
|
|
generate_save_input_vars_code(LookupForeignArgModes, ModuleInfo, 0,
|
|
PickupForeignArgs, SaveInputVarCode, PickupInputVarCode),
|
|
|
|
GeneratorPredVarName = generator_pred_name,
|
|
ConsumerVarName = consumer_name,
|
|
|
|
GeneratorPredArg = foreign_arg(GeneratorPredVar,
|
|
yes(generator_pred_name - in_mode), GeneratorPredType,
|
|
native_if_possible),
|
|
ConsumerArg = foreign_arg(ConsumerVar,
|
|
yes(ConsumerVarName - out_mode), consumer_type, native_if_possible),
|
|
|
|
LookupDeclCodeStr =
|
|
"\tMR_TrieNode " ++ cur_table_node_name ++ ";\n" ++
|
|
"\tMR_TrieNode " ++ next_table_node_name ++ ";\n" ++
|
|
"\tMR_GeneratorPtr " ++ generator_name ++ ";\n\n" ++
|
|
LookupCodeStr,
|
|
|
|
SetupPredName = "table_mmos_setup_consumer",
|
|
SetupCode = "\t" ++ generator_name ++ " = " ++
|
|
cur_table_node_name ++ "->MR_generator;\n" ++
|
|
"\tif (" ++ generator_name ++ " == NULL) {\n" ++
|
|
SaveInputVarCode ++
|
|
"\t\t" ++ generator_name ++ " = MR_table_mmos_setup_generator(" ++
|
|
cur_table_node_name ++ ",\n\t\t\t"
|
|
++ int_to_string(NumInputVars) ++ ", "
|
|
++ GeneratorPredVarName ++ ", " ++
|
|
"""" ++ PredName ++ """);\n" ++
|
|
"\t\tMR_mmos_new_generator = " ++ generator_name ++ ";\n" ++
|
|
"\t}\n" ++
|
|
"\t" ++ consumer_name ++ " = " ++
|
|
"MR_table_mmos_setup_consumer(" ++ generator_name ++
|
|
", """ ++ PredName ++ """);\n",
|
|
table_generate_foreign_proc(SetupPredName, detism_det,
|
|
make_generator_c_attributes,
|
|
[InfoArg, GeneratorPredArg, ConsumerArg], LookupForeignArgs,
|
|
LookupDeclCodeStr ++ SetupCode, purity_impure,
|
|
ground_vars([ConsumerVar]), ModuleInfo, Context, SetupGoal),
|
|
% We don't attach the call_table_tip attribute to the setup goal, since
|
|
% retrying across the creation of the generator should not require undoing
|
|
% the creation of the generator. Of course, for this to work *properly*,
|
|
% the runtime system and the debugger will need to cooperate to effectively
|
|
% present to the user a distinct sequence of trace events for each context.
|
|
% attach_call_table_tip(SetupGoal0, SetupGoal),
|
|
LookupSetupGoals = [MakeGeneratorVarGoal | LookupPrefixGoals]
|
|
++ [SetupGoal],
|
|
|
|
generate_new_table_var("AnswerBlock", answer_block_type,
|
|
!VarSet, !VarTypes, AnswerBlockVar),
|
|
( Detism = detism_multi ->
|
|
ConsumePredName = "table_mmos_consume_next_answer_multi"
|
|
; Detism = detism_non ->
|
|
ConsumePredName = "table_mmos_consume_next_answer_nondet"
|
|
;
|
|
unexpected(this_file, "do_own_stack_transform: invalid determinism")
|
|
),
|
|
% XXX consider inlining the predicate being called
|
|
table_generate_call(ConsumePredName, Detism, [ConsumerVar, AnswerBlockVar],
|
|
purity_impure, ground_vars([AnswerBlockVar]), ModuleInfo, Context,
|
|
GetNextAnswerGoal),
|
|
DebugArgStr = get_debug_arg_string(!.TableInfo),
|
|
generate_restore_goals(NumberedOutputVars, OrigInstMapDelta,
|
|
DebugArgStr, ModuleInfo, !VarSet, !VarTypes, RestoreInstMapDeltaSrc,
|
|
RestoreArgs, RestoreCodeStr),
|
|
AnswerBlockArg = foreign_arg(AnswerBlockVar,
|
|
yes(answer_block_name - in_mode), answer_block_type,
|
|
native_if_possible),
|
|
RestoreAllPredName = "table_mmos_restore_answers",
|
|
table_generate_foreign_proc(RestoreAllPredName, detism_det,
|
|
tabling_c_attributes_no_dupl, [AnswerBlockArg],
|
|
RestoreArgs, RestoreCodeStr, purity_semipure, RestoreInstMapDeltaSrc,
|
|
ModuleInfo, Context, RestoreGoal),
|
|
|
|
GoalExpr = conj(plain_conj,
|
|
LookupSetupGoals ++ [GetNextAnswerGoal, RestoreGoal]),
|
|
goal_info_init(OrigNonLocals, OrigInstMapDelta, Detism, purity_impure,
|
|
Context, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo),
|
|
|
|
module_info_pred_info(ModuleInfo, GeneratorPredId, GeneratorPredInfo),
|
|
table_info_init(ModuleInfo, GeneratorPredInfo, ProcInfo0,
|
|
GeneratorTableInfo0),
|
|
do_own_stack_create_generator(GeneratorPredId, ProcId, GeneratorPredInfo,
|
|
ProcInfo0, Statistics, Context, GeneratorPredVar, DebugArgStr,
|
|
PickupInputVarCode, PickupForeignArgs,
|
|
NumberedInputVars, NumberedOutputVars,
|
|
OrigNonLocals, OrigInstMapDelta, !.VarTypes, !.VarSet,
|
|
GeneratorTableInfo0, GeneratorTableInfo, InputSteps, OutputSteps),
|
|
!:TableInfo = !.TableInfo ^ table_module_info :=
|
|
GeneratorTableInfo ^ table_module_info.
|
|
|
|
:- pred generate_save_input_vars_code(assoc_list(foreign_arg, mer_mode)::in,
|
|
module_info::in, int::in, list(foreign_arg)::out, string::out, string::out)
|
|
is det.
|
|
|
|
generate_save_input_vars_code([], _, _, [], "", "").
|
|
generate_save_input_vars_code([InputArg - Mode | InputArgModes], ModuleInfo,
|
|
Pos, [PickupArg | PickupArgs], SaveVarCode ++ SaveVarCodes,
|
|
PickupVarCode ++ PickupVarCodes) :-
|
|
InputArg = foreign_arg(InputVar, MaybeArgNameMode, Type, _),
|
|
(
|
|
MaybeArgNameMode = yes(InputVarName - _InMode)
|
|
;
|
|
MaybeArgNameMode = no,
|
|
unexpected(this_file, "generate_save_input_vars_code: no InputVarName")
|
|
),
|
|
mode_get_insts(ModuleInfo, Mode, InitInst, _FinalInst),
|
|
PickupMode = (free -> InitInst),
|
|
PickupArg = foreign_arg(InputVar, yes(InputVarName - PickupMode), Type,
|
|
native_if_possible),
|
|
SaveVarCode = "\t\tMR_table_mmos_save_input_arg(" ++
|
|
int_to_string(Pos) ++ ", " ++ InputVarName ++ ");\n",
|
|
PickupVarCode = "\t\tMR_table_mmos_pickup_input_arg(" ++
|
|
int_to_string(Pos) ++ ", " ++ InputVarName ++ ");\n",
|
|
generate_save_input_vars_code(InputArgModes, ModuleInfo, Pos + 1,
|
|
PickupArgs, SaveVarCodes, PickupVarCodes).
|
|
|
|
:- pred do_own_stack_create_generator(pred_id::in, proc_id::in,
|
|
pred_info::in, proc_info::in,
|
|
table_attr_statistics::in, term.context::in,
|
|
prog_var::in, string::in, string::in, list(foreign_arg)::in,
|
|
list(var_mode_pos_method)::in, list(var_mode_pos_method)::in,
|
|
set(prog_var)::in, instmap_delta::in,
|
|
vartypes::in, prog_varset::in, table_info::in, table_info::out,
|
|
list(table_trie_step)::in, list(table_trie_step)::out) is det.
|
|
|
|
do_own_stack_create_generator(PredId, ProcId, !.PredInfo, !.ProcInfo,
|
|
Statistics, Context, GeneratorVar, DebugArgStr, PickupVarCode,
|
|
PickupForeignArgs, NumberedInputVars, NumberedOutputVars,
|
|
OrigNonLocals, OrigInstMapDelta, !.VarTypes, !.VarSet, !TableInfo,
|
|
InputSteps, OutputSteps) :-
|
|
ModuleInfo0 = !.TableInfo ^ table_module_info,
|
|
|
|
proc_info_set_headvars(list.map(project_var, NumberedOutputVars),
|
|
!ProcInfo),
|
|
proc_info_set_argmodes(list.map(project_mode, NumberedOutputVars),
|
|
!ProcInfo),
|
|
PickupInstMapDeltaSrc0 = list.map(project_var_init_inst(ModuleInfo0),
|
|
NumberedInputVars),
|
|
PickupInstMapDeltaSrc = [pair_with_ground(GeneratorVar)
|
|
| PickupInstMapDeltaSrc0],
|
|
PickupGeneratorCode = "\t\t" ++ generator_name ++
|
|
" = MR_mmos_new_generator;\n",
|
|
PickupGeneratorArg = foreign_arg(GeneratorVar,
|
|
yes(generator_name - out_mode), generator_type, native_if_possible),
|
|
table_generate_foreign_proc("table_mmos_pickup_inputs", detism_det,
|
|
tabling_c_attributes_no_dupl, [PickupGeneratorArg], PickupForeignArgs,
|
|
PickupGeneratorCode ++ PickupVarCode, purity_impure,
|
|
PickupInstMapDeltaSrc, ModuleInfo0, Context, PickupGoal),
|
|
|
|
list.length(NumberedOutputVars, BlockSize),
|
|
generate_own_stack_save_return_goal(NumberedOutputVars, GeneratorVar,
|
|
PredId, ProcId, BlockSize, Statistics, Context, !VarSet, !VarTypes,
|
|
!TableInfo, OutputSteps, SaveReturnAnswerGoals),
|
|
|
|
proc_info_get_goal(!.ProcInfo, OrigGoal),
|
|
OrigGoal = hlds_goal(_, OrigGoalInfo),
|
|
|
|
MainGoalExpr = conj(plain_conj, [OrigGoal | SaveReturnAnswerGoals]),
|
|
goal_info_get_determinism(OrigGoalInfo, Detism),
|
|
set.insert(OrigNonLocals, GeneratorVar, NonLocals),
|
|
goal_info_init(NonLocals, OrigInstMapDelta, Detism, purity_impure, Context,
|
|
MainGoalInfo0),
|
|
goal_info_add_feature(feature_hide_debug_event,
|
|
MainGoalInfo0, MainGoalInfo),
|
|
MainGoal = hlds_goal(MainGoalExpr, MainGoalInfo),
|
|
|
|
CompletionCode = "\t\t" ++ "MR_tbl_mmos_completion(" ++
|
|
DebugArgStr ++ ", " ++ generator_name ++ ");\n",
|
|
CompletionArg = foreign_arg(GeneratorVar,
|
|
yes(generator_name - in_mode), generator_type, native_if_possible),
|
|
table_generate_foreign_proc("table_mmos_completion", detism_failure,
|
|
tabling_c_attributes_no_dupl, [CompletionArg], [],
|
|
CompletionCode, purity_impure,
|
|
[], ModuleInfo0, Context, CompletionGoal),
|
|
|
|
DisjGoalExpr = disj([MainGoal, CompletionGoal]),
|
|
DisjGoal = hlds_goal(DisjGoalExpr, MainGoalInfo),
|
|
|
|
GoalExpr = conj(plain_conj, [PickupGoal, DisjGoal]),
|
|
Goal = hlds_goal(GoalExpr, OrigGoalInfo),
|
|
proc_info_set_goal(Goal, !ProcInfo),
|
|
|
|
proc_info_set_vartypes(!.VarTypes, !ProcInfo),
|
|
proc_info_set_varset(!.VarSet, !ProcInfo),
|
|
|
|
InputVarModeMethods = list.map(project_out_pos, NumberedInputVars),
|
|
OutputVarModeMethods = list.map(project_out_pos, NumberedOutputVars),
|
|
generate_gen_proc_table_info(!.TableInfo, InputSteps, yes(OutputSteps),
|
|
InputVarModeMethods, OutputVarModeMethods, ProcTableInfo),
|
|
proc_info_set_maybe_proc_table_info(yes(ProcTableInfo), !ProcInfo),
|
|
|
|
SpecialReturn = generator_return(returning_generator_locn, DebugArgStr),
|
|
proc_info_set_maybe_special_return(yes(SpecialReturn), !ProcInfo),
|
|
proc_info_set_eval_method(eval_minimal(own_stacks_generator), !ProcInfo),
|
|
|
|
pred_info_get_procedures(!.PredInfo, ProcTable0),
|
|
map.det_insert(ProcTable0, ProcId, !.ProcInfo, ProcTable),
|
|
pred_info_set_procedures(ProcTable, !PredInfo),
|
|
|
|
module_info_preds(ModuleInfo0, PredTable0),
|
|
map.det_update(PredTable0, PredId, !.PredInfo, PredTable),
|
|
module_info_set_preds(PredTable, ModuleInfo0, ModuleInfo),
|
|
!:TableInfo = !.TableInfo ^ table_module_info := ModuleInfo.
|
|
|
|
:- pred clone_pred_info(pred_id::in, pred_info::in, list(prog_var)::in,
|
|
list(var_mode_pos_method)::in, pred_id::out,
|
|
table_info::in, table_info::out) is det.
|
|
|
|
clone_pred_info(OrigPredId, PredInfo0, HeadVars, NumberedOutputVars,
|
|
GeneratorPredId, !TableInfo) :-
|
|
% We don't have any procedures for the generator yet. We will copy
|
|
% the consumers' procedures later, one by one, as they are transformed.
|
|
|
|
ModuleName = pred_info_module(PredInfo0),
|
|
PredName0 = pred_info_name(PredInfo0),
|
|
PredOrFunc = pred_info_is_pred_or_func(PredInfo0),
|
|
pred_info_get_context(PredInfo0, Context),
|
|
% The generator is local even if the original predicate is exported.
|
|
Status = status_local,
|
|
pred_info_get_goal_type(PredInfo0, GoalType),
|
|
pred_info_get_markers(PredInfo0, Markers0),
|
|
pred_info_get_arg_types(PredInfo0, ArgTypes0),
|
|
pred_info_get_typevarset(PredInfo0, TypeVarSet),
|
|
pred_info_get_exist_quant_tvars(PredInfo0, ExistQVars),
|
|
pred_info_get_class_context(PredInfo0, ClassContext),
|
|
pred_info_get_constraint_proofs(PredInfo0, ClassProofs),
|
|
pred_info_get_constraint_map(PredInfo0, ClassConstraintMap),
|
|
pred_info_get_origin(PredInfo0, OrigOrigin),
|
|
pred_info_get_clauses_info(PredInfo0, ClausesInfo),
|
|
pred_info_get_var_name_remap(PredInfo0, VarNameRemap),
|
|
|
|
PredName = qualified(ModuleName, "GeneratorFor_" ++ PredName0),
|
|
assoc_list.from_corresponding_lists(HeadVars, ArgTypes0, HeadVarTypes),
|
|
keep_only_output_arg_types(HeadVarTypes, NumberedOutputVars, ArgTypes),
|
|
Arity = list.length(ArgTypes),
|
|
|
|
markers_to_marker_list(Markers0, MarkerList0),
|
|
list.filter(filter_marker, MarkerList0, MarkerList),
|
|
marker_list_to_markers(MarkerList, Markers),
|
|
|
|
Origin = origin_transformed(transform_table_generator,
|
|
OrigOrigin, OrigPredId),
|
|
pred_info_init(ModuleName, PredName, Arity, PredOrFunc, Context,
|
|
Origin, Status, GoalType, Markers, ArgTypes, TypeVarSet,
|
|
ExistQVars, ClassContext, ClassProofs, ClassConstraintMap,
|
|
ClausesInfo, VarNameRemap, PredInfo),
|
|
|
|
ModuleInfo0 = !.TableInfo ^ table_module_info,
|
|
module_info_get_predicate_table(ModuleInfo0, PredTable0),
|
|
predicate_table_insert(PredInfo, GeneratorPredId, PredTable0, PredTable),
|
|
module_info_set_predicate_table(PredTable, ModuleInfo0, ModuleInfo),
|
|
!:TableInfo = !.TableInfo ^ table_module_info := ModuleInfo.
|
|
|
|
% clone_proc_and_create_call(PredInfo, ProcId, CallExpr, !ModuleInfo).
|
|
% This predicate creates a new procedure with the same body as the
|
|
% procedure with ProcId in PredInfo. It then creates a call goal
|
|
% expression which calls the new procedure with its formal arguments as the
|
|
% actual arguments.
|
|
%
|
|
:- pred clone_proc_and_create_call(pred_info::in, proc_id::in,
|
|
hlds_goal_expr::out, module_info::in, module_info::out) is det.
|
|
|
|
clone_proc_and_create_call(PredInfo, ProcId, CallExpr, !ModuleInfo) :-
|
|
pred_info_proc_info(PredInfo, ProcId, ProcInfo),
|
|
proc_info_get_context(ProcInfo, ProcContext),
|
|
proc_info_get_varset(ProcInfo, ProcVarSet),
|
|
proc_info_get_vartypes(ProcInfo, ProcVarTypes),
|
|
proc_info_get_headvars(ProcInfo, ProcHeadVars),
|
|
proc_info_get_inst_varset(ProcInfo, ProcInstVarSet),
|
|
proc_info_get_argmodes(ProcInfo, ProcHeadModes),
|
|
proc_info_get_inferred_determinism(ProcInfo, ProcDetism),
|
|
proc_info_get_goal(ProcInfo, ProcGoal),
|
|
proc_info_get_rtti_varmaps(ProcInfo, ProcRttiVarMaps),
|
|
proc_info_get_var_name_remap(ProcInfo, VarNameRemap),
|
|
proc_info_create(ProcContext, ProcVarSet, ProcVarTypes, ProcHeadVars,
|
|
ProcInstVarSet, ProcHeadModes, ProcDetism, ProcGoal, ProcRttiVarMaps,
|
|
address_is_not_taken, VarNameRemap, NewProcInfo),
|
|
ModuleName = pred_info_module(PredInfo),
|
|
OrigPredName = pred_info_name(PredInfo),
|
|
PredOrFunc = pred_info_is_pred_or_func(PredInfo),
|
|
pred_info_get_context(PredInfo, PredContext),
|
|
NewPredName = qualified(ModuleName, "OutlinedForIOTablingFrom_" ++
|
|
OrigPredName),
|
|
pred_info_get_arg_types(PredInfo, PredArgTypes),
|
|
pred_info_get_typevarset(PredInfo, PredTypeVarSet),
|
|
pred_info_get_exist_quant_tvars(PredInfo, PredExistQVars),
|
|
pred_info_get_class_context(PredInfo, PredClassContext),
|
|
pred_info_get_assertions(PredInfo, PredAssertions),
|
|
pred_info_get_markers(PredInfo, Markers),
|
|
pred_info_create(ModuleName, NewPredName, PredOrFunc, PredContext,
|
|
origin_created(io_tabling), status_local, Markers, PredArgTypes,
|
|
PredTypeVarSet, PredExistQVars, PredClassContext, PredAssertions,
|
|
VarNameRemap, NewProcInfo, NewProcId, NewPredInfo),
|
|
module_info_get_predicate_table(!.ModuleInfo, PredicateTable0),
|
|
predicate_table_insert(NewPredInfo, NewPredId,
|
|
PredicateTable0, PredicateTable),
|
|
module_info_set_predicate_table(PredicateTable, !ModuleInfo),
|
|
CallExpr = plain_call(NewPredId, NewProcId, ProcHeadVars, not_builtin, no,
|
|
NewPredName).
|
|
|
|
:- pred keep_only_output_arg_types(assoc_list(prog_var, mer_type)::in,
|
|
list(var_mode_pos_method)::in, list(mer_type)::out) is det.
|
|
|
|
keep_only_output_arg_types([], _, []).
|
|
keep_only_output_arg_types([_ | _], [], []).
|
|
keep_only_output_arg_types([Var - Type | VarTypes], [Out | Outs], OutTypes) :-
|
|
Out = var_mode_pos_method(OutVar, _, _, _),
|
|
( Var = OutVar ->
|
|
keep_only_output_arg_types(VarTypes, Outs, OutTypesTail),
|
|
OutTypes = [Type | OutTypesTail]
|
|
;
|
|
keep_only_output_arg_types(VarTypes, [Out | Outs], OutTypes)
|
|
).
|
|
|
|
:- pred filter_marker(marker::in) is semidet.
|
|
|
|
filter_marker(Marker) :-
|
|
keep_marker(Marker) = yes.
|
|
|
|
:- func keep_marker(marker) = bool.
|
|
|
|
keep_marker(marker_stub) = no.
|
|
keep_marker(marker_infer_type) = no.
|
|
keep_marker(marker_infer_modes) = no.
|
|
keep_marker(marker_obsolete) = no.
|
|
keep_marker(marker_user_marked_inline) = no.
|
|
keep_marker(marker_user_marked_no_inline) = no.
|
|
keep_marker(marker_heuristic_inline) = no.
|
|
keep_marker(marker_class_method) = no.
|
|
keep_marker(marker_class_instance_method) = no.
|
|
keep_marker(marker_named_class_instance_method) = no.
|
|
keep_marker(marker_is_impure) = yes.
|
|
keep_marker(marker_is_semipure) = yes.
|
|
keep_marker(marker_promised_pure) = yes.
|
|
keep_marker(marker_promised_semipure) = yes.
|
|
keep_marker(marker_promised_equivalent_clauses) = yes.
|
|
keep_marker(marker_terminates) = yes.
|
|
keep_marker(marker_does_not_terminate) = yes.
|
|
keep_marker(marker_check_termination) = no.
|
|
keep_marker(marker_calls_are_fully_qualified) = yes.
|
|
keep_marker(marker_mode_check_clauses) = yes.
|
|
keep_marker(marker_mutable_access_pred) = yes.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred generate_gen_proc_table_info(table_info::in,
|
|
list(table_trie_step)::in, maybe(list(table_trie_step))::in,
|
|
list(var_mode_method)::in, list(var_mode_method)::in,
|
|
proc_table_info::out) is det.
|
|
|
|
generate_gen_proc_table_info(TableInfo, InputSteps, MaybeOutputSteps,
|
|
InputVars, OutputVars, ProcTableInfo) :-
|
|
ProcInfo = TableInfo ^ table_cur_proc_info,
|
|
InOutHeadVars = InputVars ++ OutputVars,
|
|
allocate_slot_numbers(InOutHeadVars, 1, NumberedInOutHeadVars),
|
|
ArgInfos = list.map(project_var_pos, NumberedInOutHeadVars),
|
|
continuation_info.generate_table_arg_type_info(ProcInfo, ArgInfos,
|
|
TableArgTypeInfo),
|
|
NumInputs = list.length(InputVars),
|
|
NumOutputs = list.length(OutputVars),
|
|
ProcTableInfo = table_gen_info(NumInputs, NumOutputs, InputSteps,
|
|
MaybeOutputSteps, TableArgTypeInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Generate a goal for doing lookups in call tables for
|
|
% loopcheck and memo predicates.
|
|
%
|
|
:- pred generate_simple_call_table_lookup_goal(mer_type::in,
|
|
string::in, string::in, list(var_mode_pos_method)::in,
|
|
pred_id::in, proc_id::in, table_attr_statistics::in, term.context::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, prog_var::out, prog_var::out,
|
|
hlds_goal::out, list(table_trie_step)::out) is det.
|
|
|
|
generate_simple_call_table_lookup_goal(StatusType, PredName,
|
|
SetupMacroName, NumberedVars, PredId, ProcId, Statistics, Context,
|
|
!VarSet, !VarTypes, !TableInfo, TableTipVar, StatusVar, Goal, Steps) :-
|
|
generate_call_table_lookup_goals(NumberedVars, PredId, ProcId,
|
|
Statistics, Context, !VarSet, !VarTypes, !TableInfo, Steps,
|
|
TableTipVar, TableTipArg, InfoArg, LookupForeignArgs,
|
|
LookupPrefixGoals, LookupCodeStr, CallTableTipAssignStr),
|
|
generate_new_table_var("Status", StatusType, !VarSet, !VarTypes,
|
|
StatusVar),
|
|
ModuleInfo = !.TableInfo ^ table_module_info,
|
|
|
|
StatusVarName = status_name,
|
|
StatusArg = foreign_arg(StatusVar,
|
|
yes(StatusVarName - out_mode), StatusType, native_if_possible),
|
|
DebugArgStr = get_debug_arg_string(!.TableInfo),
|
|
BackArgStr = get_back_arg_string(!.TableInfo),
|
|
MainPredCodeStr = "\t" ++ SetupMacroName ++ "(" ++
|
|
DebugArgStr ++ ", " ++ BackArgStr ++ ", " ++
|
|
cur_table_node_name ++ ", " ++ StatusVarName ++ ");\n",
|
|
Args = [InfoArg, TableTipArg, StatusArg],
|
|
BoundVars = [TableTipVar, StatusVar],
|
|
CodeStr =
|
|
"\tMR_TrieNode " ++ cur_table_node_name ++ ";\n" ++
|
|
"\tMR_TrieNode " ++ next_table_node_name ++ ";\n\n" ++
|
|
LookupCodeStr ++
|
|
CallTableTipAssignStr ++
|
|
MainPredCodeStr,
|
|
table_generate_foreign_proc(PredName, detism_det,
|
|
tabling_c_attributes_no_dupl,
|
|
Args, LookupForeignArgs, CodeStr, purity_impure,
|
|
ground_vars(BoundVars), ModuleInfo, Context, SetupGoal0),
|
|
attach_call_table_tip(SetupGoal0, SetupGoal),
|
|
LookupSetupGoals = LookupPrefixGoals ++ [SetupGoal],
|
|
|
|
GoalExpr = conj(plain_conj, LookupSetupGoals),
|
|
Vars = list.map(project_var, NumberedVars),
|
|
set.list_to_set([StatusVar, TableTipVar | Vars], NonLocals),
|
|
goal_info_init_hide(NonLocals, bind_vars([TableTipVar, StatusVar]),
|
|
detism_det, purity_impure, Context, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
% Generate a goal for doing lookups in call tables for
|
|
% model_non memo predicates.
|
|
%
|
|
:- pred generate_memo_non_call_table_lookup_goal(list(var_mode_pos_method)::in,
|
|
pred_id::in, proc_id::in, table_attr_statistics::in, term.context::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, prog_var::out, prog_var::out,
|
|
hlds_goal::out, list(table_trie_step)::out) is det.
|
|
|
|
generate_memo_non_call_table_lookup_goal(NumberedVars, PredId, ProcId,
|
|
Statistics, Context, !VarSet, !VarTypes, !TableInfo,
|
|
RecordVar, StatusVar, Goal, Steps) :-
|
|
generate_call_table_lookup_goals(NumberedVars, PredId, ProcId,
|
|
Statistics, Context, !VarSet, !VarTypes, !TableInfo, Steps,
|
|
_TableTipVar, _TableTipArg, InfoArg, LookupForeignArgs,
|
|
LookupPrefixGoals, LookupCodeStr, _CallTableTipAssignStr),
|
|
ModuleInfo = !.TableInfo ^ table_module_info,
|
|
generate_new_table_var("Record", memo_non_record_type, !VarSet, !VarTypes,
|
|
RecordVar),
|
|
generate_new_table_var("Status", memo_non_status_type, !VarSet, !VarTypes,
|
|
StatusVar),
|
|
SetupPredName = "table_memo_non_setup",
|
|
SetupMacroName = "MR_tbl_memo_non_setup",
|
|
BoundVars = [RecordVar, StatusVar],
|
|
RecordVarName = memo_non_record_name,
|
|
StatusVarName = status_name,
|
|
RecordArg = foreign_arg(RecordVar,
|
|
yes(RecordVarName - out_mode), memo_non_record_type,
|
|
native_if_possible),
|
|
StatusArg = foreign_arg(StatusVar,
|
|
yes(StatusVarName - out_mode), memo_non_status_type,
|
|
native_if_possible),
|
|
Args = [InfoArg, RecordArg, StatusArg],
|
|
LookupDeclCodeStr =
|
|
"\tMR_TrieNode " ++ cur_table_node_name ++ ";\n" ++
|
|
"\tMR_TrieNode " ++ next_table_node_name ++ ";\n\n" ++
|
|
LookupCodeStr,
|
|
DebugArgStr = get_debug_arg_string(!.TableInfo),
|
|
BackArgStr = get_back_arg_string(!.TableInfo),
|
|
PredCodeStr = "\t" ++ SetupMacroName ++ "(" ++
|
|
DebugArgStr ++ ", " ++ BackArgStr ++ ", " ++
|
|
cur_table_node_name ++ ", " ++ RecordVarName ++ ", " ++
|
|
StatusVarName ++ ");\n",
|
|
table_generate_foreign_proc(SetupPredName, detism_det,
|
|
tabling_c_attributes_no_dupl, Args, LookupForeignArgs,
|
|
LookupDeclCodeStr ++ PredCodeStr, purity_impure,
|
|
ground_vars(BoundVars), ModuleInfo, Context, SetupGoal0),
|
|
attach_call_table_tip(SetupGoal0, SetupGoal),
|
|
LookupSetupGoals = LookupPrefixGoals ++ [SetupGoal],
|
|
|
|
GoalExpr = conj(plain_conj, LookupSetupGoals),
|
|
Vars = list.map(project_var, NumberedVars),
|
|
set.list_to_set([StatusVar, RecordVar | Vars], NonLocals),
|
|
goal_info_init_hide(NonLocals, bind_vars([RecordVar, StatusVar]),
|
|
detism_det, purity_impure, Context, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
% Generate a goal for doing lookups in call tables for
|
|
% minimal model predicates.
|
|
%
|
|
:- pred generate_mm_call_table_lookup_goal(list(var_mode_pos_method)::in,
|
|
pred_id::in, proc_id::in, table_attr_statistics::in, term.context::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, prog_var::out, prog_var::out,
|
|
hlds_goal::out, list(table_trie_step)::out) is det.
|
|
|
|
generate_mm_call_table_lookup_goal(NumberedVars, PredId, ProcId,
|
|
Statistics, Context, !VarSet, !VarTypes, !TableInfo,
|
|
SubgoalVar, StatusVar, Goal, Steps) :-
|
|
generate_call_table_lookup_goals(NumberedVars, PredId, ProcId,
|
|
Statistics, Context, !VarSet, !VarTypes, !TableInfo, Steps,
|
|
_TableTipVar, _TableTipArg, InfoArg, LookupForeignArgs,
|
|
LookupPrefixGoals, LookupCodeStr, _CallTableTipAssignStr),
|
|
generate_new_table_var("Subgoal", subgoal_type, !VarSet, !VarTypes,
|
|
SubgoalVar),
|
|
generate_new_table_var("Status", mm_status_type, !VarSet, !VarTypes,
|
|
StatusVar),
|
|
SetupPredName = "table_mm_setup",
|
|
SetupMacroName = "MR_tbl_mm_setup",
|
|
BoundVars = [SubgoalVar, StatusVar],
|
|
|
|
SubgoalVarName = subgoal_name,
|
|
StatusVarName = status_name,
|
|
SubgoalArg = foreign_arg(SubgoalVar,
|
|
yes(SubgoalVarName - out_mode), subgoal_type, native_if_possible),
|
|
StatusArg = foreign_arg(StatusVar,
|
|
yes(StatusVarName - out_mode), mm_status_type, native_if_possible),
|
|
Args = [InfoArg, SubgoalArg, StatusArg],
|
|
LookupDeclStr =
|
|
"\tMR_TrieNode " ++ cur_table_node_name ++ ";\n" ++
|
|
"\tMR_TrieNode " ++ next_table_node_name ++ ";\n\n",
|
|
DebugArgStr = get_debug_arg_string(!.TableInfo),
|
|
BackArgStr = get_back_arg_string(!.TableInfo),
|
|
SetupCodeStr = "\t" ++ SetupMacroName ++ "(" ++
|
|
DebugArgStr ++ ", " ++ BackArgStr ++ ", " ++
|
|
cur_table_node_name ++ ", " ++ SubgoalVarName ++ ", " ++
|
|
StatusVarName ++ ");\n",
|
|
CodeStr = LookupDeclStr ++ LookupCodeStr ++ SetupCodeStr,
|
|
ModuleInfo = !.TableInfo ^ table_module_info,
|
|
table_generate_foreign_proc(SetupPredName, detism_det,
|
|
tabling_c_attributes_no_dupl, Args, LookupForeignArgs, CodeStr,
|
|
purity_impure, ground_vars(BoundVars), ModuleInfo, Context,
|
|
SetupGoal0),
|
|
attach_call_table_tip(SetupGoal0, SetupGoal),
|
|
LookupSetupGoals = LookupPrefixGoals ++ [SetupGoal],
|
|
|
|
GoalExpr = conj(plain_conj, LookupSetupGoals),
|
|
Vars = list.map(project_var, NumberedVars),
|
|
set.list_to_set([StatusVar, SubgoalVar | Vars], NonLocals),
|
|
goal_info_init_hide(NonLocals, bind_vars([SubgoalVar, StatusVar]),
|
|
detism_det, purity_impure, Context, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Utility predicates used when creating table lookup goals.
|
|
|
|
:- pred generate_call_table_lookup_goals(list(var_mode_pos_method)::in,
|
|
pred_id::in, proc_id::in, table_attr_statistics::in, term.context::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, list(table_trie_step)::out,
|
|
prog_var::out, foreign_arg::out, foreign_arg::out, list(foreign_arg)::out,
|
|
list(hlds_goal)::out, string::out, string::out) is det.
|
|
|
|
generate_call_table_lookup_goals(NumberedVars, PredId, ProcId,
|
|
Statistics, Context, !VarSet, !VarTypes, !TableInfo, InputSteps,
|
|
CallTableTipVar, CallTableTipArg, InfoArg, LookupArgs,
|
|
PrefixGoals, MainCodeStr, CallTableTipVarCodeStr) :-
|
|
InfoToPtrCodeStr = "\t" ++ cur_table_node_name ++ " = " ++
|
|
"&" ++ proc_table_info_name ++ "->MR_pt_tablenode;\n",
|
|
generate_get_table_info_goal(PredId, ProcId, !VarSet, !VarTypes,
|
|
proc_table_info_name, InfoArg, GetTableInfoGoal),
|
|
DebugArgStr = get_debug_arg_string(!.TableInfo),
|
|
BackArgStr = get_back_arg_string(!.TableInfo),
|
|
generate_table_lookup_goals(NumberedVars, Statistics, call_table,
|
|
DebugArgStr, BackArgStr, Context, !VarSet, !VarTypes, !TableInfo,
|
|
InputSteps, LookupArgs, LookupPrefixGoals, LookupCodeStr),
|
|
PrefixGoals = [GetTableInfoGoal] ++ LookupPrefixGoals,
|
|
% We ignore _StatsPrefixGoals and _StatsExtraArgs because we always
|
|
% include ProcTableInfoVar in the arguments.
|
|
maybe_lookup_not_dupl_code_args(PredId, ProcId,
|
|
proc_table_info_name, cur_table_node_name,
|
|
Statistics, call_table, !VarSet, !VarTypes,
|
|
_StatsPrefixGoals, _StatsExtraArgs, StatsCodeStr),
|
|
MainCodeStr = InfoToPtrCodeStr ++ LookupCodeStr ++ StatsCodeStr,
|
|
CallTableTipVarName = "CallTableTipVar",
|
|
generate_new_table_var(CallTableTipVarName, trie_node_type,
|
|
!VarSet, !VarTypes, CallTableTipVar),
|
|
CallTableTipArg = foreign_arg(CallTableTipVar,
|
|
yes(CallTableTipVarName - out_mode), trie_node_type,
|
|
native_if_possible),
|
|
CallTableTipVarCodeStr =
|
|
"\t" ++ CallTableTipVarName ++ " = " ++ cur_table_node_name ++ ";\n".
|
|
|
|
:- pred generate_answer_table_lookup_goals(list(var_mode_pos_method)::in,
|
|
pred_id::in, proc_id::in, table_attr_statistics::in, term.context::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, list(table_trie_step)::out,
|
|
list(foreign_arg)::out, list(hlds_goal)::out, string::out) is det.
|
|
|
|
generate_answer_table_lookup_goals(NumberedVars, PredId, ProcId, Statistics,
|
|
Context, !VarSet, !VarTypes, !TableInfo, OutputSteps, ForeignArgs,
|
|
PrefixGoals, CodeStr) :-
|
|
DebugArgStr = get_debug_arg_string(!.TableInfo),
|
|
BackArgStr = "MR_FALSE",
|
|
generate_table_lookup_goals(NumberedVars, Statistics, answer_table,
|
|
DebugArgStr, BackArgStr, Context, !VarSet, !VarTypes, !TableInfo,
|
|
OutputSteps, LookupArgs, LookupPrefixGoals, LookupCodeStr),
|
|
maybe_lookup_not_dupl_code_args(PredId, ProcId,
|
|
proc_table_info_name, cur_table_node_name,
|
|
Statistics, call_table, !VarSet, !VarTypes,
|
|
StatsPrefixGoals, StatsExtraArgs, StatsCodeStr),
|
|
CodeStr = LookupCodeStr ++ StatsCodeStr,
|
|
ForeignArgs = StatsExtraArgs ++ LookupArgs,
|
|
PrefixGoals = StatsPrefixGoals ++ LookupPrefixGoals.
|
|
|
|
:- pred maybe_lookup_not_dupl_code_args(pred_id::in, proc_id::in,
|
|
string::in, string::in, table_attr_statistics::in,
|
|
call_or_answer_table::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
list(hlds_goal)::out, list(foreign_arg)::out, string::out) is det.
|
|
|
|
maybe_lookup_not_dupl_code_args(PredId, ProcId, InfoVarName, TipVarName,
|
|
Statistics, Kind, !VarSet, !VarTypes, PrefixGoals, Args, CodeStr) :-
|
|
(
|
|
Statistics = table_dont_gather_statistics,
|
|
PrefixGoals = [],
|
|
Args = [],
|
|
CodeStr = ""
|
|
;
|
|
Statistics = table_gather_statistics,
|
|
generate_get_table_info_goal(PredId, ProcId, !VarSet, !VarTypes,
|
|
InfoVarName, Arg, Goal),
|
|
PrefixGoals = [Goal],
|
|
Args = [Arg],
|
|
CodeStr = lookup_not_dupl_code(InfoVarName, TipVarName, Kind)
|
|
).
|
|
|
|
:- func lookup_not_dupl_code(string, string, call_or_answer_table) = string.
|
|
|
|
lookup_not_dupl_code(InfoVar, TipVar, call_table) =
|
|
"\t" ++ InfoVar ++ "->MR_pt_call_table_lookups++;\n" ++
|
|
"\tif (" ++ TipVar ++ "->MR_integer != 0) {\n" ++
|
|
"\t\t" ++ InfoVar ++ "->MR_pt_call_table_not_dupl++;\n" ++
|
|
"\t}\n".
|
|
lookup_not_dupl_code(InfoVar, TipVar, answer_table) =
|
|
"\t" ++ InfoVar ++ "->MR_pt_answer_table_lookups++;\n" ++
|
|
"\tif (" ++ TipVar ++ "->MR_integer != 0) {\n" ++
|
|
"\t\t" ++ InfoVar ++ "->MR_pt_answer_table_not_dupl++;\n" ++
|
|
"\t}\n".
|
|
|
|
:- pred generate_get_table_info_goal(pred_id::in, proc_id::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
string::in, foreign_arg::out, hlds_goal::out) is det.
|
|
|
|
generate_get_table_info_goal(PredId, ProcId, !VarSet, !VarTypes,
|
|
InfoVarName, Arg, Goal) :-
|
|
generate_new_table_var("ProcTableInfo", proc_table_info_type,
|
|
!VarSet, !VarTypes, ProcTableInfoVar),
|
|
Arg = foreign_arg(ProcTableInfoVar,
|
|
yes(InfoVarName - in_mode), proc_table_info_type,
|
|
native_if_possible),
|
|
ShroudedPredProcId = shroud_pred_proc_id(proc(PredId, ProcId)),
|
|
InfoConsId = tabling_info_const(ShroudedPredProcId),
|
|
make_const_construction(ProcTableInfoVar, InfoConsId,
|
|
hlds_goal(GoalExpr, GoalInfo0)),
|
|
goal_info_set_purity(purity_impure, GoalInfo0, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
:- pred attach_call_table_tip(hlds_goal::in, hlds_goal::out) is det.
|
|
|
|
attach_call_table_tip(hlds_goal(GoalExpr, GoalInfo0),
|
|
hlds_goal(GoalExpr, GoalInfo)) :-
|
|
goal_info_get_features(GoalInfo0, Features0),
|
|
set.insert(Features0, feature_call_table_gen, Features),
|
|
goal_info_set_features(Features, GoalInfo0, GoalInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Generate a sequence of lookup goals for the given variables.
|
|
% The generated code is used for lookups in both call tables
|
|
% and answer tables.
|
|
%
|
|
:- pred generate_table_lookup_goals(list(var_mode_pos_method)::in,
|
|
table_attr_statistics::in, call_or_answer_table::in,
|
|
string::in, string::in, term.context::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, list(table_trie_step)::out,
|
|
list(foreign_arg)::out, list(hlds_goal)::out, string::out) is det.
|
|
|
|
generate_table_lookup_goals([], _, _, _, _, _, !VarSet, !VarTypes, !TableInfo,
|
|
[], [], [], "").
|
|
generate_table_lookup_goals([VarModePos | NumberedVars], Statistics,
|
|
Kind, DebugArgStr, BackArgStr, Context, !VarSet, !VarTypes, !TableInfo,
|
|
[Step | Steps], ForeignArgs ++ RestForeignArgs,
|
|
PrefixGoals ++ RestPrefixGoals, CodeStr ++ RestCodeStr) :-
|
|
VarModePos = var_mode_pos_method(Var, _, VarSeqNum, ArgMethod),
|
|
ModuleInfo = !.TableInfo ^ table_module_info,
|
|
map.lookup(!.VarTypes, Var, VarType),
|
|
classify_type(ModuleInfo, VarType) = TypeCat,
|
|
(
|
|
ArgMethod = arg_promise_implied,
|
|
Step = table_trie_step_promise_implied,
|
|
ForeignArgs = [],
|
|
PrefixGoals = [],
|
|
CodeStr = "\t/* promise_implied for " ++ arg_name(VarSeqNum) ++ " */\n"
|
|
;
|
|
( ArgMethod = arg_value
|
|
; ArgMethod = arg_addr
|
|
),
|
|
gen_lookup_call_for_type(ArgMethod, TypeCat, VarType, Var,
|
|
VarSeqNum, Statistics, Kind, DebugArgStr, BackArgStr, Context,
|
|
!VarSet, !VarTypes, !TableInfo, Step, ForeignArgs,
|
|
PrefixGoals, CodeStr)
|
|
),
|
|
generate_table_lookup_goals(NumberedVars, Statistics, Kind,
|
|
DebugArgStr, BackArgStr, Context, !VarSet, !VarTypes, !TableInfo,
|
|
Steps, RestForeignArgs, RestPrefixGoals, RestCodeStr).
|
|
|
|
:- pred gen_lookup_call_for_type(arg_tabling_method::in, type_category::in,
|
|
mer_type::in, prog_var::in, int::in, table_attr_statistics::in,
|
|
call_or_answer_table::in, string::in, string::in, term.context::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out,
|
|
table_trie_step::out, list(foreign_arg)::out, list(hlds_goal)::out,
|
|
string::out) is det.
|
|
|
|
gen_lookup_call_for_type(ArgTablingMethod, TypeCat, Type, ArgVar,
|
|
VarSeqNum, Statistics, Kind, DebugArgStr, BackArgStr, Context,
|
|
!VarSet, !VarTypes, !TableInfo, Step, ExtraArgs, PrefixGoals,
|
|
CodeStr) :-
|
|
ModuleInfo = !.TableInfo ^ table_module_info,
|
|
ArgName = arg_name(VarSeqNum),
|
|
ForeignArg = foreign_arg(ArgVar, yes(ArgName - in_mode), Type,
|
|
native_if_possible),
|
|
( TypeCat = type_cat_enum ->
|
|
( type_to_ctor_and_args(Type, TypeCtor, _) ->
|
|
module_info_get_type_table(ModuleInfo, TypeDefnTable),
|
|
map.lookup(TypeDefnTable, TypeCtor, TypeDefn),
|
|
hlds_data.get_type_defn_body(TypeDefn, TypeBody),
|
|
(
|
|
Ctors = TypeBody ^ du_type_ctors,
|
|
TypeBody ^ du_type_is_enum = is_enum,
|
|
TypeBody ^ du_type_usereq = no
|
|
->
|
|
list.length(Ctors, EnumRange)
|
|
;
|
|
unexpected(this_file,
|
|
"gen_lookup_call_for_type: enum type is not du_type?")
|
|
),
|
|
LookupMacroName = "MR_tbl_lookup_insert_enum",
|
|
Step = table_trie_step_enum(EnumRange),
|
|
PrefixGoals = [],
|
|
ExtraArgs = [ForeignArg],
|
|
StatsArgStr = stats_arg(Statistics, Kind, VarSeqNum),
|
|
CodeStr0 = "\t" ++ LookupMacroName ++ "(" ++ StatsArgStr ++ ", " ++
|
|
DebugArgStr ++ ", " ++ BackArgStr ++ ", " ++
|
|
cur_table_node_name ++ ", " ++ int_to_string(EnumRange) ++
|
|
", " ++ ArgName ++ ", " ++ next_table_node_name ++ ");\n"
|
|
;
|
|
unexpected(this_file,
|
|
"gen_lookup_call_for_type: unexpected enum type")
|
|
)
|
|
; TypeCat = type_cat_dummy ->
|
|
Step = table_trie_step_dummy,
|
|
PrefixGoals = [],
|
|
ExtraArgs = [],
|
|
CodeStr0 = next_table_node_name ++ " = "
|
|
++ cur_table_node_name ++ ";\n "
|
|
;
|
|
lookup_tabling_category(TypeCat, MaybeCatStringStep),
|
|
(
|
|
MaybeCatStringStep = no,
|
|
type_vars(Type, TypeVars),
|
|
(
|
|
ArgTablingMethod = arg_value,
|
|
(
|
|
TypeVars = [],
|
|
LookupMacroName = "MR_tbl_lookup_insert_user",
|
|
Step = table_trie_step_user(Type)
|
|
;
|
|
TypeVars = [_ | _],
|
|
LookupMacroName = "MR_tbl_lookup_insert_poly",
|
|
Step = table_trie_step_poly
|
|
)
|
|
;
|
|
ArgTablingMethod = arg_addr,
|
|
(
|
|
TypeVars = [],
|
|
LookupMacroName = "MR_tbl_lookup_insert_user_addr",
|
|
Step = table_trie_step_user_fast_loose(Type)
|
|
;
|
|
TypeVars = [_ | _],
|
|
LookupMacroName = "MR_tbl_lookup_insert_poly_addr",
|
|
Step = table_trie_step_poly_fast_loose
|
|
)
|
|
;
|
|
ArgTablingMethod = arg_promise_implied,
|
|
unexpected(this_file,
|
|
"gen_lookup_call_for_type: arg_promise_implied")
|
|
),
|
|
table_gen_make_type_info_var(Type, Context, !VarSet, !VarTypes,
|
|
!TableInfo, TypeInfoVar, PrefixGoals),
|
|
TypeInfoArgName = "input_typeinfo" ++ int_to_string(VarSeqNum),
|
|
map.lookup(!.VarTypes, TypeInfoVar, TypeInfoType),
|
|
ForeignTypeInfoArg = foreign_arg(TypeInfoVar,
|
|
yes(TypeInfoArgName - in_mode), TypeInfoType,
|
|
native_if_possible),
|
|
ExtraArgs = [ForeignTypeInfoArg, ForeignArg],
|
|
StatsArgStr = stats_arg(Statistics, Kind, VarSeqNum),
|
|
CodeStr0 = "\t" ++ LookupMacroName ++ "(" ++ StatsArgStr ++ ", " ++
|
|
DebugArgStr ++ ", " ++ BackArgStr ++ ", " ++
|
|
cur_table_node_name ++ ", " ++ TypeInfoArgName ++ ", " ++
|
|
ArgName ++ ", " ++ next_table_node_name ++ ");\n"
|
|
;
|
|
MaybeCatStringStep = yes(CatString - Step),
|
|
LookupMacroName = "MR_tbl_lookup_insert_" ++ CatString,
|
|
PrefixGoals = [],
|
|
ExtraArgs = [ForeignArg],
|
|
StatsArgStr = stats_arg(Statistics, Kind, VarSeqNum),
|
|
CodeStr0 = "\t" ++ LookupMacroName ++ "(" ++ StatsArgStr ++ ", " ++
|
|
DebugArgStr ++ ", " ++ BackArgStr ++ ", " ++
|
|
cur_table_node_name ++ ", " ++ ArgName ++ ", " ++
|
|
next_table_node_name ++ ");\n"
|
|
)
|
|
),
|
|
CodeStr = CodeStr0 ++ "\t" ++ cur_table_node_name ++ " = " ++
|
|
next_table_node_name ++ ";\n".
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Generate a goal for saving the output arguments in an answer block
|
|
% in memo predicates.
|
|
%
|
|
:- pred generate_memo_save_goal(list(var_mode_pos_method(T))::in,
|
|
prog_var::in, int::in, term.context::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, list(hlds_goal)::out) is det.
|
|
|
|
generate_memo_save_goal(NumberedSaveVars, TableTipVar, BlockSize,
|
|
Context, !VarSet, !VarTypes, !TableInfo, Goals) :-
|
|
ModuleInfo = !.TableInfo ^ table_module_info,
|
|
TipVarName = cur_table_node_name,
|
|
TableArg = foreign_arg(TableTipVar, yes(TipVarName - in_mode),
|
|
trie_node_type, native_if_possible),
|
|
( BlockSize > 0 ->
|
|
CreatePredName = "table_memo_fill_answer_block_shortcut",
|
|
CreateMacroName = "MR_tbl_memo_create_answer_block",
|
|
generate_all_save_goals(NumberedSaveVars, TipVarName,
|
|
BlockSize, CreateMacroName, Context, !VarSet, !VarTypes,
|
|
!TableInfo, SaveArgs, SavePrefixGoals, SaveDeclCode, SaveCode),
|
|
table_generate_foreign_proc(CreatePredName, detism_det,
|
|
tabling_c_attributes_dupl, [TableArg], SaveArgs,
|
|
SaveDeclCode ++ SaveCode, purity_impure, [],
|
|
ModuleInfo, Context, SaveGoal),
|
|
Goals = SavePrefixGoals ++ [SaveGoal]
|
|
;
|
|
DebugArgStr = get_debug_arg_string(!.TableInfo),
|
|
MarkAsSucceededPredName = "table_memo_mark_as_succeeded",
|
|
MarkAsSucceededMacroName = "MR_tbl_memo_mark_as_succeeded",
|
|
MarkAsSucceededCode = MarkAsSucceededMacroName ++
|
|
"(" ++ DebugArgStr ++ ", " ++ cur_table_node_name ++ ");",
|
|
table_generate_foreign_proc(MarkAsSucceededPredName, detism_det,
|
|
tabling_c_attributes_dupl, [TableArg], [],
|
|
MarkAsSucceededCode, purity_impure, [],
|
|
ModuleInfo, Context, SaveGoal),
|
|
Goals = [SaveGoal]
|
|
).
|
|
|
|
% Generate a goal for saving the output arguments in an answer block
|
|
% in model_non memo predicates.
|
|
%
|
|
:- pred generate_memo_non_save_goals(list(var_mode_pos_method)::in,
|
|
pred_id::in, proc_id::in, prog_var::in, int::in,
|
|
table_attr_statistics::in, term.context::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, list(table_trie_step)::out,
|
|
list(hlds_goal)::out) is det.
|
|
|
|
generate_memo_non_save_goals(NumberedSaveVars, PredId, ProcId,
|
|
RecordVar, BlockSize, Statistics, Context, !VarSet, !VarTypes,
|
|
!TableInfo, OutputSteps, Goals) :-
|
|
ModuleInfo = !.TableInfo ^ table_module_info,
|
|
RecordName = memo_non_record_name,
|
|
RecordArg = foreign_arg(RecordVar,
|
|
yes(RecordName - in_mode), memo_non_record_type, native_if_possible),
|
|
|
|
generate_answer_table_lookup_goals(NumberedSaveVars, PredId, ProcId,
|
|
Statistics, Context, !VarSet, !VarTypes, !TableInfo, OutputSteps,
|
|
LookupForeignArgs, LookupPrefixGoals, LookupCodeStr),
|
|
|
|
CreateAnswerBlockMacroName = "MR_tbl_memo_non_create_answer_block",
|
|
generate_all_save_goals(NumberedSaveVars, memo_non_record_name, BlockSize,
|
|
CreateAnswerBlockMacroName, Context, !VarSet, !VarTypes, !TableInfo,
|
|
_SaveForeignArgs, _SavePrefixGoals, SaveDeclCodeStr, CreateSaveCode),
|
|
|
|
GetMacroName = "MR_tbl_memo_non_get_answer_table",
|
|
DuplCheckPredName = "table_memo_non_answer_is_not_duplicate_shortcut",
|
|
DuplCheckMacroName = "MR_tbl_memo_non_answer_is_not_duplicate",
|
|
|
|
DebugArgStr = get_debug_arg_string(!.TableInfo),
|
|
SuccName = "succeeded",
|
|
LookupDeclCodeStr =
|
|
"\tMR_TrieNode " ++ cur_table_node_name ++ ";\n" ++
|
|
"\tMR_TrieNode " ++ next_table_node_name ++ ";\n" ++
|
|
"\tMR_bool " ++ SuccName ++ ";\n",
|
|
GetCodeStr =
|
|
"\t" ++ GetMacroName ++ "(" ++ DebugArgStr ++ ", " ++
|
|
RecordName ++ ", " ++ cur_table_node_name ++ ");\n" ++
|
|
LookupCodeStr,
|
|
DuplCheckCodeStr =
|
|
"\t" ++ DuplCheckMacroName ++ "(" ++ DebugArgStr ++ ", " ++
|
|
cur_table_node_name ++ ", " ++ SuccName ++ ");\n",
|
|
AssignSuccessCodeStr =
|
|
"\t" ++ success_indicator_name ++ " = " ++ SuccName ++ ";\n",
|
|
CodeStr = LookupDeclCodeStr ++ SaveDeclCodeStr ++ "\n" ++
|
|
GetCodeStr ++ LookupCodeStr ++
|
|
DuplCheckCodeStr ++
|
|
"\tif (" ++ SuccName ++ ") {\n" ++ CreateSaveCode ++ "\t}\n" ++
|
|
AssignSuccessCodeStr,
|
|
table_generate_foreign_proc(DuplCheckPredName, detism_semi,
|
|
tabling_c_attributes_dupl, [RecordArg], LookupForeignArgs, CodeStr,
|
|
purity_impure, [], ModuleInfo, Context, DuplicateCheckSaveGoal),
|
|
Goals = LookupPrefixGoals ++ [DuplicateCheckSaveGoal].
|
|
|
|
% Generate a goal for saving the output arguments in an answer block
|
|
% in minimal model predicates.
|
|
%
|
|
:- pred generate_mm_save_goals(list(var_mode_pos_method)::in,
|
|
prog_var::in, pred_id::in, proc_id::in, int::in,
|
|
table_attr_statistics::in, term.context::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, list(table_trie_step)::out,
|
|
list(hlds_goal)::out) is det.
|
|
|
|
generate_mm_save_goals(NumberedSaveVars, SubgoalVar, PredId, ProcId, BlockSize,
|
|
Statistics, Context, !VarSet, !VarTypes, !TableInfo, OutputSteps,
|
|
Goals) :-
|
|
ModuleInfo = !.TableInfo ^ table_module_info,
|
|
DebugArgStr = get_debug_arg_string(!.TableInfo),
|
|
|
|
generate_answer_table_lookup_goals(NumberedSaveVars, PredId, ProcId,
|
|
Statistics, Context, !VarSet, !VarTypes, !TableInfo, OutputSteps,
|
|
LookupForeignArgs, LookupPrefixGoals, LookupCodeStr),
|
|
|
|
GetMacroName = "MR_tbl_mm_get_answer_table",
|
|
CreateMacroName = "MR_tbl_mm_create_answer_block",
|
|
DuplCheckPredName = "table_mm_answer_is_not_duplicate_shortcut",
|
|
DuplCheckMacroName = "MR_tbl_mm_answer_is_not_duplicate",
|
|
|
|
generate_all_save_goals(NumberedSaveVars, subgoal_name, BlockSize,
|
|
CreateMacroName, Context, !VarSet, !VarTypes, !TableInfo,
|
|
_SaveArgs, _SavePrefixGoals, SaveDeclCode, CreateSaveCode),
|
|
|
|
SubgoalName = subgoal_name,
|
|
Args = [foreign_arg(SubgoalVar, yes(SubgoalName - in_mode),
|
|
subgoal_type, native_if_possible)],
|
|
SuccName = "succeeded",
|
|
LookupDeclCodeStr =
|
|
"\tMR_TrieNode " ++ cur_table_node_name ++ ";\n" ++
|
|
"\tMR_TrieNode " ++ next_table_node_name ++ ";\n" ++
|
|
"\tMR_bool " ++ SuccName ++ ";\n",
|
|
GetCodeStr =
|
|
"\t" ++ GetMacroName ++ "(" ++ DebugArgStr ++ ", " ++
|
|
SubgoalName ++ ", " ++ cur_table_node_name ++ ");\n",
|
|
DuplCheckCodeStr =
|
|
"\t" ++ DuplCheckMacroName ++ "(" ++ DebugArgStr ++ ", " ++
|
|
cur_table_node_name ++ ", " ++ SuccName ++ ");\n",
|
|
CondSaveStr = "\tif (" ++ SuccName ++ ") {\n" ++
|
|
CreateSaveCode ++ "\t}\n",
|
|
AssignSuccessCodeStr =
|
|
"\t" ++ success_indicator_name ++ " = " ++ SuccName ++ ";\n",
|
|
CodeStr = LookupDeclCodeStr ++ SaveDeclCode ++
|
|
GetCodeStr ++ LookupCodeStr ++ DuplCheckCodeStr ++
|
|
CondSaveStr ++ AssignSuccessCodeStr,
|
|
table_generate_foreign_proc(DuplCheckPredName, detism_semi,
|
|
tabling_c_attributes_dupl, Args, LookupForeignArgs,
|
|
CodeStr, purity_impure, [],
|
|
ModuleInfo, Context, DuplicateCheckSaveGoal),
|
|
Goals = LookupPrefixGoals ++ [DuplicateCheckSaveGoal].
|
|
|
|
% Generate a save goal for the given variables.
|
|
%
|
|
:- pred generate_all_save_goals(list(var_mode_pos_method(T))::in,
|
|
string::in, int::in, string::in, term.context::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, list(foreign_arg)::out,
|
|
list(hlds_goal)::out, string::out, string::out) is det.
|
|
|
|
generate_all_save_goals(NumberedSaveVars, BaseVarName, BlockSize,
|
|
CreateMacroName, Context, !VarSet, !VarTypes, !TableInfo,
|
|
SaveArgs, SavePrefixGoals, SaveDeclCodeStr, CreateSaveCodeStr) :-
|
|
DebugArgStr = get_debug_arg_string(!.TableInfo),
|
|
generate_save_goals(NumberedSaveVars, DebugArgStr, Context,
|
|
!VarSet, !VarTypes, !TableInfo, SaveArgs,
|
|
SavePrefixGoals, SaveCodeStr),
|
|
SaveDeclCodeStr = "\tMR_AnswerBlock " ++ answer_block_name ++ ";\n",
|
|
CreateCodeStr = "\t" ++ CreateMacroName ++ "(" ++ DebugArgStr ++ ", " ++
|
|
BaseVarName ++ ", " ++ int_to_string(BlockSize) ++ ", " ++
|
|
answer_block_name ++ ");\n",
|
|
CreateSaveCodeStr = CreateCodeStr ++ SaveCodeStr.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Generate a sequence of save goals for the given variables.
|
|
%
|
|
:- pred generate_own_stack_save_return_goal(list(var_mode_pos_method)::in,
|
|
prog_var::in, pred_id::in, proc_id::in, int::in,
|
|
table_attr_statistics::in, term.context::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, list(table_trie_step)::out,
|
|
list(hlds_goal)::out) is det.
|
|
|
|
generate_own_stack_save_return_goal(NumberedOutputVars, GeneratorVar,
|
|
PredId, ProcId, BlockSize, Statistics, Context, !VarSet, !VarTypes,
|
|
!TableInfo, OutputSteps, Goals) :-
|
|
GeneratorName = generator_name,
|
|
GeneratorArg = foreign_arg(GeneratorVar, yes(GeneratorName - in_mode),
|
|
generator_type, native_if_possible),
|
|
DebugArgStr = get_debug_arg_string(!.TableInfo),
|
|
|
|
generate_answer_table_lookup_goals(NumberedOutputVars, PredId, ProcId,
|
|
Statistics, Context, !VarSet, !VarTypes, !TableInfo, OutputSteps,
|
|
LookupForeignArgs, LookupPrefixGoals, LookupCodeStr),
|
|
|
|
generate_save_goals(NumberedOutputVars, DebugArgStr, Context,
|
|
!VarSet, !VarTypes, !TableInfo, _SaveArgs,
|
|
SavePrefixGoals, SaveCodeStr),
|
|
|
|
GetMacroName = "MR_table_mmos_get_answer_table",
|
|
CreateMacroName = "MR_tbl_mmos_create_answer_block",
|
|
DuplCheckPredName = "table_mmos_answer_is_not_duplicate_shortcut",
|
|
DuplCheckMacroName = "MR_tbl_mmos_answer_is_not_duplicate",
|
|
|
|
Args = [GeneratorArg],
|
|
SuccName = "succeeded",
|
|
LookupSaveDeclCodeStr =
|
|
"\tMR_TrieNode " ++ cur_table_node_name ++ ";\n" ++
|
|
"\tMR_TrieNode " ++ next_table_node_name ++ ";\n" ++
|
|
"\tMR_AnswerBlock " ++ answer_block_name ++ ";\n" ++
|
|
"\tMR_bool " ++ SuccName ++ ";\n\n",
|
|
GetCodeStr = "\t" ++ cur_table_node_name ++ " = " ++
|
|
GetMacroName ++ "(" ++ GeneratorName ++ ");\n",
|
|
DuplCheckCodeStr =
|
|
"\t" ++ DuplCheckMacroName ++ "(" ++ DebugArgStr ++ ", " ++
|
|
cur_table_node_name ++ ", " ++ SuccName ++ ");\n",
|
|
AssignSuccessCodeStr =
|
|
"\t" ++ success_indicator_name ++ " = " ++ SuccName ++ ";\n",
|
|
CreateCodeStr = "\t" ++ CreateMacroName ++ "(" ++ DebugArgStr ++ ", " ++
|
|
GeneratorName ++ ", " ++ int_to_string(BlockSize) ++ ", " ++
|
|
answer_block_name ++ ");\n",
|
|
SetupReturnCodeStr = "\t" ++ returning_generator_locn ++ " = " ++
|
|
GeneratorName ++ ";\n",
|
|
CreateSaveSetupReturnCodeStr = CreateCodeStr ++ SaveCodeStr ++
|
|
SetupReturnCodeStr,
|
|
CondSaveCodeStr = "\tif (" ++ SuccName ++ ") {\n" ++
|
|
CreateSaveSetupReturnCodeStr ++ "\t}\n",
|
|
CodeStr = LookupSaveDeclCodeStr ++ GetCodeStr ++ LookupCodeStr ++
|
|
DuplCheckCodeStr ++ CondSaveCodeStr ++ AssignSuccessCodeStr,
|
|
ModuleInfo = !.TableInfo ^ table_module_info,
|
|
table_generate_foreign_proc(DuplCheckPredName, detism_semi,
|
|
tabling_c_attributes_dupl, Args, LookupForeignArgs,
|
|
CodeStr, purity_impure, [],
|
|
ModuleInfo, Context, DuplicateCheckSaveGoal),
|
|
Goals = LookupPrefixGoals ++ SavePrefixGoals ++
|
|
[DuplicateCheckSaveGoal].
|
|
|
|
:- pred generate_save_goals(list(var_mode_pos_method(T))::in, string::in,
|
|
term.context::in, prog_varset::in, prog_varset::out,
|
|
vartypes::in, vartypes::out, table_info::in, table_info::out,
|
|
list(foreign_arg)::out, list(hlds_goal)::out, string::out) is det.
|
|
|
|
generate_save_goals([], _, _, !VarSet, !VarTypes, !TableInfo, [], [], "").
|
|
generate_save_goals([NumberedVar | NumberedRest], DebugArgStr, Context,
|
|
!VarSet, !VarTypes, !TableInfo, Args ++ RestArgs,
|
|
PrefixGoals ++ RestPrefixGoals, CodeStr ++ RestCodeStr) :-
|
|
NumberedVar = var_mode_pos_method(Var, _Mode, Offset, _),
|
|
ModuleInfo = !.TableInfo ^ table_module_info,
|
|
map.lookup(!.VarTypes, Var, VarType),
|
|
classify_type(ModuleInfo, VarType) = TypeCat,
|
|
gen_save_call_for_type(TypeCat, VarType, Var, Offset, DebugArgStr, Context,
|
|
!VarSet, !VarTypes, !TableInfo, Args, PrefixGoals, CodeStr),
|
|
generate_save_goals(NumberedRest, DebugArgStr, Context, !VarSet, !VarTypes,
|
|
!TableInfo, RestArgs, RestPrefixGoals, RestCodeStr).
|
|
|
|
:- pred gen_save_call_for_type(type_category::in, mer_type::in,
|
|
prog_var::in, int::in, string::in, term.context::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, list(foreign_arg)::out,
|
|
list(hlds_goal)::out, string::out) is det.
|
|
|
|
gen_save_call_for_type(TypeCat, Type, Var, Offset, DebugArgStr, Context,
|
|
!VarSet, !VarTypes, !TableInfo, Args, PrefixGoals, CodeStr) :-
|
|
Name = arg_name(Offset),
|
|
ForeignArg = foreign_arg(Var, yes(Name - in_mode), Type,
|
|
native_if_possible),
|
|
( type_is_io_state(Type) ->
|
|
SaveMacroName = "MR_tbl_save_io_state_answer",
|
|
Args = [ForeignArg],
|
|
PrefixGoals = [],
|
|
CodeStr = "\t" ++ SaveMacroName ++ "(" ++ DebugArgStr ++ ", " ++
|
|
answer_block_name ++ ", " ++ int_to_string(Offset) ++ ", "
|
|
++ Name ++ ");\n"
|
|
; builtin_type(TypeCat) = no ->
|
|
% If we used ForeignArg instead of GenericForeignArg, then
|
|
% Var would be unboxed when assigned to Name, which we don't want.
|
|
GenericForeignArg = foreign_arg(Var, yes(Name - in_mode),
|
|
dummy_type_var, native_if_possible),
|
|
table_gen_make_type_info_var(Type, Context, !VarSet, !VarTypes,
|
|
!TableInfo, TypeInfoVar, PrefixGoals),
|
|
TypeInfoName = "save_arg_typeinfo" ++ int_to_string(Offset),
|
|
map.lookup(!.VarTypes, TypeInfoVar, TypeInfoType),
|
|
TypeInfoForeignArg = foreign_arg(TypeInfoVar,
|
|
yes(TypeInfoName - in_mode), TypeInfoType, native_if_possible),
|
|
SaveMacroName = "MR_tbl_save_any_answer",
|
|
Args = [GenericForeignArg, TypeInfoForeignArg],
|
|
CodeStr = "\t" ++ SaveMacroName ++ "(" ++ DebugArgStr ++ ", " ++
|
|
answer_block_name ++ ", " ++ int_to_string(Offset) ++ ", " ++
|
|
TypeInfoName ++ ", " ++ Name ++ ");\n"
|
|
;
|
|
type_save_category(TypeCat, CatString),
|
|
SaveMacroName = "MR_tbl_save_" ++ CatString ++ "_answer",
|
|
Args = [ForeignArg],
|
|
PrefixGoals = [],
|
|
CodeStr = "\t" ++ SaveMacroName ++ "(" ++ DebugArgStr ++ ", " ++
|
|
answer_block_name ++ ", " ++ int_to_string(Offset) ++ ", "
|
|
++ Name ++ ");\n"
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Generate a goal for restoring the output arguments from
|
|
% an answer block in memo predicates.
|
|
%
|
|
:- pred generate_memo_restore_goal(list(var_mode_pos_method(T))::in,
|
|
instmap_delta::in, prog_var::in, term.context::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, hlds_goal::out) is det.
|
|
|
|
generate_memo_restore_goal(NumberedOutputVars, OrigInstMapDelta, TipVar,
|
|
Context, !VarSet, !VarTypes, TableInfo, Goal) :-
|
|
(
|
|
NumberedOutputVars = [_ | _],
|
|
DebugArgStr = get_debug_arg_string(TableInfo),
|
|
ModuleInfo = TableInfo ^ table_module_info,
|
|
generate_restore_goals(NumberedOutputVars, OrigInstMapDelta,
|
|
DebugArgStr, ModuleInfo, !VarSet, !VarTypes,
|
|
RestoreInstMapDeltaSrc, RestoreArgs, RestoreCodeStr),
|
|
BaseVarName = base_name,
|
|
Arg = foreign_arg(TipVar, yes(BaseVarName - in_mode),
|
|
trie_node_type, native_if_possible),
|
|
Args = [Arg],
|
|
GetPredName = "table_memo_get_answer_block_shortcut",
|
|
GetMacroName = "MR_tbl_memo_get_answer_block",
|
|
DeclCodeStr = "\tMR_AnswerBlock " ++ answer_block_name ++ ";\n",
|
|
GetRestoreCodeStr = "\t" ++ GetMacroName ++ "(" ++
|
|
DebugArgStr ++ ", " ++ BaseVarName ++ ", " ++
|
|
answer_block_name ++ ");\n" ++
|
|
RestoreCodeStr,
|
|
table_generate_foreign_proc(GetPredName, detism_det,
|
|
tabling_c_attributes_dupl, Args, RestoreArgs,
|
|
DeclCodeStr ++ GetRestoreCodeStr, purity_semipure,
|
|
RestoreInstMapDeltaSrc, ModuleInfo, Context, ShortcutGoal),
|
|
Goal = ShortcutGoal
|
|
;
|
|
NumberedOutputVars = [],
|
|
Goal = true_goal
|
|
).
|
|
|
|
% Generate a goal for restoring the output arguments from
|
|
% an answer block in model_non memo predicates.
|
|
%
|
|
:- pred generate_memo_non_restore_goal(determinism::in,
|
|
list(var_mode_pos_method)::in, instmap_delta::in, prog_var::in,
|
|
term.context::in, prog_varset::in, prog_varset::out,
|
|
vartypes::in, vartypes::out, table_info::in, hlds_goal::out) is det.
|
|
|
|
generate_memo_non_restore_goal(Detism, NumberedOutputVars, OrigInstMapDelta,
|
|
RecordVar, Context, !VarSet, !VarTypes, TableInfo, Goal) :-
|
|
( Detism = detism_multi ->
|
|
ReturnAllAns = "table_memo_return_all_answers_multi"
|
|
; Detism = detism_non ->
|
|
ReturnAllAns = "table_memo_return_all_answers_nondet"
|
|
;
|
|
unexpected(this_file, "generate_mm_restore_goal: invalid determinism")
|
|
),
|
|
generate_new_table_var("AnswerBlock", answer_block_type,
|
|
!VarSet, !VarTypes, AnswerBlockVar),
|
|
ModuleInfo = TableInfo ^ table_module_info,
|
|
table_generate_call(ReturnAllAns, Detism, [RecordVar, AnswerBlockVar],
|
|
purity_semipure, ground_vars([AnswerBlockVar]), ModuleInfo,
|
|
Context, ReturnAnswerBlocksGoal),
|
|
DebugArgStr = get_debug_arg_string(TableInfo),
|
|
generate_restore_goals(NumberedOutputVars, OrigInstMapDelta,
|
|
DebugArgStr, ModuleInfo, !VarSet, !VarTypes, RestoreInstMapDeltaSrc,
|
|
RestoreArgs, RestoreCodeStr),
|
|
OutputVars = list.map(project_var, NumberedOutputVars),
|
|
Arg = foreign_arg(AnswerBlockVar, yes(answer_block_name - in_mode),
|
|
answer_block_type, native_if_possible),
|
|
Args = [Arg],
|
|
PredName = "table_memo_non_return_all_shortcut",
|
|
table_generate_foreign_proc(PredName, detism_det,
|
|
tabling_c_attributes_no_dupl, Args, RestoreArgs, RestoreCodeStr,
|
|
purity_semipure, RestoreInstMapDeltaSrc, ModuleInfo, Context,
|
|
ShortcutGoal),
|
|
|
|
GoalExpr = conj(plain_conj, [ReturnAnswerBlocksGoal, ShortcutGoal]),
|
|
set.list_to_set([RecordVar | OutputVars], NonLocals),
|
|
goal_info_init_hide(NonLocals, OrigInstMapDelta, Detism, purity_semipure,
|
|
Context, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
% Generate a goal for restoring the output arguments from
|
|
% an answer block in minimal model predicates without a suspension.
|
|
%
|
|
:- pred generate_mm_restore_goal(determinism::in,
|
|
list(var_mode_pos_method)::in, instmap_delta::in, prog_var::in,
|
|
term.context::in, prog_varset::in, prog_varset::out,
|
|
vartypes::in, vartypes::out, table_info::in, hlds_goal::out) is det.
|
|
|
|
generate_mm_restore_goal(Detism, NumberedOutputVars, OrigInstMapDelta,
|
|
SubgoalVar, Context, !VarSet, !VarTypes, TableInfo, Goal) :-
|
|
( Detism = detism_multi ->
|
|
ReturnAllAns = "table_mm_return_all_multi"
|
|
; Detism = detism_non ->
|
|
ReturnAllAns = "table_mm_return_all_nondet"
|
|
;
|
|
unexpected(this_file, "generate_mm_restore_goal: invalid determinism")
|
|
),
|
|
generate_mm_restore_or_suspend_goal(ReturnAllAns, Detism, purity_semipure,
|
|
NumberedOutputVars, OrigInstMapDelta, SubgoalVar, Context,
|
|
!VarSet, !VarTypes, TableInfo, Goal).
|
|
|
|
% Generate a goal for restoring the output arguments from
|
|
% an answer block in minimal model predicates after a suspension.
|
|
%
|
|
:- pred generate_mm_suspend_goal(list(var_mode_pos_method)::in,
|
|
instmap_delta::in, prog_var::in, term.context::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, hlds_goal::out) is det.
|
|
|
|
generate_mm_suspend_goal(NumberedOutputVars, OrigInstMapDelta, SubgoalVar,
|
|
Context, !VarSet, !VarTypes, TableInfo, Goal) :-
|
|
generate_mm_restore_or_suspend_goal("table_mm_suspend_consumer",
|
|
detism_non, purity_impure, NumberedOutputVars, OrigInstMapDelta,
|
|
SubgoalVar, Context, !VarSet, !VarTypes, TableInfo, Goal).
|
|
|
|
% Generate a goal for restoring the output arguments from
|
|
% an answer block in minimal model predicates. Whether the restore
|
|
% is after a suspension depends on the arguments.
|
|
%
|
|
:- pred generate_mm_restore_or_suspend_goal(string::in, determinism::in,
|
|
purity::in, list(var_mode_pos_method)::in, instmap_delta::in,
|
|
prog_var::in, term.context::in, prog_varset::in, prog_varset::out,
|
|
vartypes::in, vartypes::out, table_info::in, hlds_goal::out) is det.
|
|
|
|
generate_mm_restore_or_suspend_goal(PredName, Detism, Purity,
|
|
NumberedOutputVars, OrigInstMapDelta, SubgoalVar, Context,
|
|
!VarSet, !VarTypes, TableInfo, Goal) :-
|
|
generate_new_table_var("AnswerBlock", answer_block_type,
|
|
!VarSet, !VarTypes, AnswerBlockVar),
|
|
ModuleInfo = TableInfo ^ table_module_info,
|
|
table_generate_call(PredName, Detism, [SubgoalVar, AnswerBlockVar],
|
|
Purity, ground_vars([AnswerBlockVar]), ModuleInfo,
|
|
Context, ReturnAnswerBlocksGoal),
|
|
DebugArgStr = get_debug_arg_string(TableInfo),
|
|
generate_restore_goals(NumberedOutputVars, OrigInstMapDelta,
|
|
DebugArgStr, ModuleInfo, !VarSet, !VarTypes, RestoreInstMapDeltaSrc,
|
|
RestoreArgs, RestoreCodeStr),
|
|
OutputVars = list.map(project_var, NumberedOutputVars),
|
|
|
|
Arg = foreign_arg(AnswerBlockVar, yes(answer_block_name - in_mode),
|
|
answer_block_type, native_if_possible),
|
|
Args = [Arg],
|
|
ReturnAllPredName = "table_mm_return_all_shortcut",
|
|
table_generate_foreign_proc(ReturnAllPredName, detism_det,
|
|
tabling_c_attributes_no_dupl, Args, RestoreArgs, RestoreCodeStr,
|
|
purity_semipure, RestoreInstMapDeltaSrc, ModuleInfo, Context,
|
|
ReturnAllGoal),
|
|
GoalExpr = conj(plain_conj, [ReturnAnswerBlocksGoal, ReturnAllGoal]),
|
|
|
|
set.list_to_set([SubgoalVar | OutputVars], NonLocals),
|
|
goal_info_init_hide(NonLocals, OrigInstMapDelta, Detism, Purity,
|
|
Context, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Generate a sequence of restore goals for the given variables.
|
|
%
|
|
:- pred generate_restore_goals(list(var_mode_pos_method(T))::in,
|
|
instmap_delta::in, string::in, module_info::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
assoc_list(prog_var, mer_inst)::out, list(foreign_arg)::out,
|
|
string::out) is det.
|
|
|
|
generate_restore_goals([], _, _, _, !VarSet, !VarTypes, [], [], "").
|
|
generate_restore_goals([NumberedVar | NumberedRest], OrigInstmapDelta,
|
|
DebugArgStr, ModuleInfo, !VarSet, !VarTypes, [VarInst | VarInsts],
|
|
[Arg | Args], CodeStr ++ RestCodeStr) :-
|
|
NumberedVar = var_mode_pos_method(Var, _Mode, Offset, _),
|
|
map.lookup(!.VarTypes, Var, VarType),
|
|
classify_type(ModuleInfo, VarType) = TypeCat,
|
|
gen_restore_call_for_type(DebugArgStr, TypeCat, VarType, OrigInstmapDelta,
|
|
Var, Offset, VarInst, Arg, CodeStr),
|
|
generate_restore_goals(NumberedRest, OrigInstmapDelta, DebugArgStr,
|
|
ModuleInfo, !VarSet, !VarTypes, VarInsts, Args, RestCodeStr).
|
|
|
|
:- pred gen_restore_call_for_type(string::in, type_category::in, mer_type::in,
|
|
instmap_delta::in, prog_var::in, int::in,
|
|
pair(prog_var, mer_inst)::out, foreign_arg::out, string::out) is det.
|
|
|
|
gen_restore_call_for_type(DebugArgStr, TypeCat, Type, OrigInstmapDelta, Var,
|
|
Offset, Var - Inst, Arg, CodeStr) :-
|
|
Name = "restore_arg" ++ int_to_string(Offset),
|
|
( type_is_io_state(Type) ->
|
|
RestoreMacroName = "MR_tbl_restore_io_state_answer",
|
|
ArgType = Type
|
|
; builtin_type(TypeCat) = no ->
|
|
RestoreMacroName = "MR_tbl_restore_any_answer",
|
|
ArgType = dummy_type_var
|
|
;
|
|
type_save_category(TypeCat, CatString),
|
|
RestoreMacroName = "MR_tbl_restore_" ++ CatString ++ "_answer",
|
|
ArgType = Type
|
|
),
|
|
( instmap_delta_search_var(OrigInstmapDelta, Var, InstPrime) ->
|
|
Inst = InstPrime
|
|
;
|
|
unexpected(this_file, "gen_restore_call_for_type: no inst")
|
|
),
|
|
Arg = foreign_arg(Var, yes(Name - (free -> Inst)), ArgType,
|
|
native_if_possible),
|
|
CodeStr = "\t" ++ RestoreMacroName ++ "(" ++ DebugArgStr ++ ", " ++
|
|
answer_block_name ++ ", " ++ int_to_string(Offset) ++ ", " ++
|
|
Name ++ ");\n".
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- func infinite_recursion_msg = string.
|
|
|
|
infinite_recursion_msg = "detected infinite recursion".
|
|
|
|
:- func need_minimal_model_msg = string.
|
|
|
|
need_minimal_model_msg = "detected need for minimal model".
|
|
|
|
:- pred generate_error_goal(table_info::in, term.context::in, string::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
hlds_goal::out) is det.
|
|
|
|
generate_error_goal(TableInfo, Context, Msg, !VarSet, !VarTypes, Goal) :-
|
|
ModuleInfo = TableInfo ^ table_module_info,
|
|
PredInfo = TableInfo ^ table_cur_pred_info,
|
|
|
|
Module = pred_info_module(PredInfo),
|
|
Name = pred_info_name(PredInfo),
|
|
Arity = pred_info_orig_arity(PredInfo),
|
|
PredOrFunc = pred_info_is_pred_or_func(PredInfo),
|
|
PredOrFuncStr = pred_or_func_to_str(PredOrFunc),
|
|
NameStr = sym_name_to_string(qualified(Module, Name)),
|
|
string.int_to_string(Arity, ArityStr),
|
|
Message = Msg ++ " in " ++ PredOrFuncStr ++ " " ++ NameStr
|
|
++ "/" ++ ArityStr,
|
|
|
|
gen_string_construction("Message", Message, !VarSet, !VarTypes,
|
|
MessageVar, MessageStrGoal),
|
|
table_generate_call("table_error", detism_erroneous, [MessageVar],
|
|
purity_pure, [], ModuleInfo, Context, CallGoal),
|
|
|
|
GoalExpr = conj(plain_conj, [MessageStrGoal, CallGoal]),
|
|
goal_info_init_hide(set.init, bind_vars([]), detism_erroneous,
|
|
purity_impure, Context, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred generate_new_table_var(string::in, mer_type::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
prog_var::out) is det.
|
|
|
|
generate_new_table_var(Name, Type, !VarSet, !VarTypes, Var) :-
|
|
varset.new_named_var(!.VarSet, Name, Var, !:VarSet),
|
|
map.set(!.VarTypes, Var, Type, !:VarTypes).
|
|
|
|
:- pred table_generate_call(string::in, determinism::in, list(prog_var)::in,
|
|
purity::in, assoc_list(prog_var, mer_inst)::in,
|
|
module_info::in, term.context::in, hlds_goal::out) is det.
|
|
|
|
table_generate_call(PredName, Detism, Args, Purity, InstMapSrc, ModuleInfo,
|
|
Context, Goal) :-
|
|
BuiltinModule = mercury_table_builtin_module,
|
|
( Purity = purity_pure ->
|
|
Features0 = []
|
|
;
|
|
Features0 = [feature_not_impure_for_determinism]
|
|
),
|
|
( Detism = detism_failure ->
|
|
Features = [feature_preserve_backtrack_into | Features0]
|
|
;
|
|
Features = Features0
|
|
),
|
|
goal_util.generate_simple_call(BuiltinModule, PredName, pf_predicate,
|
|
only_mode, Detism, Purity, Args, Features, InstMapSrc, ModuleInfo,
|
|
Context, Goal).
|
|
|
|
:- pred table_generate_foreign_proc(string::in, determinism::in,
|
|
pragma_foreign_proc_attributes::in,
|
|
list(foreign_arg)::in, list(foreign_arg)::in, string::in,
|
|
purity::in, assoc_list(prog_var, mer_inst)::in,
|
|
module_info::in, term.context::in, hlds_goal::out) is det.
|
|
|
|
table_generate_foreign_proc(PredName, Detism, Attributes, Args, ExtraArgs,
|
|
Code, Purity, InstMapSrc, ModuleInfo, Context, Goal) :-
|
|
( Purity = purity_pure ->
|
|
Features0 = []
|
|
;
|
|
Features0 = [feature_not_impure_for_determinism]
|
|
),
|
|
( Detism = detism_failure ->
|
|
Features = [feature_preserve_backtrack_into | Features0]
|
|
;
|
|
Features = Features0
|
|
),
|
|
BuiltinModule = mercury_table_builtin_module,
|
|
MaybeTraceRuntimCond = no,
|
|
goal_util.generate_foreign_proc(BuiltinModule, PredName, pf_predicate,
|
|
only_mode, Detism, Purity, Attributes, Args, ExtraArgs,
|
|
MaybeTraceRuntimCond, Code, Features, InstMapSrc, ModuleInfo,
|
|
Context, Goal).
|
|
|
|
:- pred append_fail(hlds_goal::in, hlds_goal::out) is det.
|
|
|
|
append_fail(Goal, GoalAndThenFail) :-
|
|
Goal = hlds_goal(_, GoalInfo),
|
|
goal_info_get_nonlocals(GoalInfo, NonLocals),
|
|
goal_info_get_context(GoalInfo, Context),
|
|
instmap_delta_init_unreachable(UnreachInstMapDelta),
|
|
goal_info_init_hide(NonLocals, UnreachInstMapDelta, detism_failure,
|
|
purity_impure, Context, ConjGoalInfo),
|
|
GoalAndThenFail =
|
|
hlds_goal(conj(plain_conj, [Goal, fail_goal]), ConjGoalInfo).
|
|
|
|
:- pred gen_int_construction(string::in, int::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
prog_var::out, hlds_goal::out) is det.
|
|
|
|
gen_int_construction(VarName, VarValue, !VarSet, !VarTypes, Var, Goal) :-
|
|
make_int_const_construction_alloc(VarValue, yes(VarName), Goal, Var,
|
|
!VarSet, !VarTypes).
|
|
|
|
:- pred gen_string_construction(string::in, string::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
prog_var::out, hlds_goal::out) is det.
|
|
|
|
gen_string_construction(VarName, VarValue, !VarSet, !VarTypes, Var, Goal) :-
|
|
make_string_const_construction_alloc(VarValue, yes(VarName), Goal, Var,
|
|
!VarSet, !VarTypes).
|
|
|
|
:- func proc_table_info_type = mer_type.
|
|
:- func trie_node_type = mer_type.
|
|
:- func memo_non_record_type = mer_type.
|
|
:- func subgoal_type = mer_type.
|
|
:- func answer_block_type = mer_type.
|
|
:- func loop_status_type = mer_type.
|
|
:- func memo_det_status_type = mer_type.
|
|
:- func memo_semi_status_type = mer_type.
|
|
:- func memo_non_status_type = mer_type.
|
|
:- func mm_status_type = mer_type.
|
|
|
|
proc_table_info_type = Type :-
|
|
TB = mercury_table_builtin_module,
|
|
construct_type(type_ctor(qualified(TB, "ml_proc_table_info"), 0),
|
|
[], Type).
|
|
|
|
trie_node_type = Type :-
|
|
TB = mercury_table_builtin_module,
|
|
construct_type(type_ctor(qualified(TB, "ml_trie_node"), 0), [], Type).
|
|
|
|
memo_non_record_type = Type :-
|
|
TB = mercury_table_builtin_module,
|
|
construct_type(type_ctor(qualified(TB, "memo_non_record"), 0), [], Type).
|
|
|
|
subgoal_type = Type :-
|
|
TB = mercury_table_builtin_module,
|
|
construct_type(type_ctor(qualified(TB, "ml_subgoal"), 0), [], Type).
|
|
|
|
answer_block_type = Type :-
|
|
TB = mercury_table_builtin_module,
|
|
construct_type(type_ctor(qualified(TB, "ml_answer_block"), 0), [], Type).
|
|
|
|
loop_status_type = Type :-
|
|
TB = mercury_table_builtin_module,
|
|
construct_type(type_ctor(qualified(TB, "loop_status"), 0), [], Type).
|
|
|
|
memo_det_status_type = Type :-
|
|
TB = mercury_table_builtin_module,
|
|
construct_type(type_ctor(qualified(TB, "memo_det_status"), 0), [], Type).
|
|
|
|
memo_semi_status_type = Type :-
|
|
TB = mercury_table_builtin_module,
|
|
construct_type(type_ctor(qualified(TB, "memo_semi_status"), 0), [], Type).
|
|
|
|
memo_non_status_type = Type :-
|
|
TB = mercury_table_builtin_module,
|
|
construct_type(type_ctor(qualified(TB, "memo_non_status"), 0), [], Type).
|
|
|
|
mm_status_type = Type :-
|
|
TB = mercury_table_builtin_module,
|
|
construct_type(type_ctor(qualified(TB, "mm_status"), 0), [], Type).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- func consumer_type = mer_type.
|
|
|
|
consumer_type = Type :-
|
|
TB = mercury_table_builtin_module,
|
|
construct_type(type_ctor(qualified(TB, "ml_consumer"), 0), [], Type).
|
|
|
|
:- func generator_type = mer_type.
|
|
|
|
generator_type = Type :-
|
|
TB = mercury_table_builtin_module,
|
|
construct_type(type_ctor(qualified(TB, "ml_generator"), 0), [], Type).
|
|
|
|
:- type maybe_specified_method
|
|
---> all_same(arg_tabling_method)
|
|
; specified(list(maybe(arg_tabling_method))).
|
|
|
|
:- pred get_input_output_vars(list(prog_var)::in, list(mer_mode)::in,
|
|
module_info::in, maybe_specified_method::in, maybe_specified_method::out,
|
|
list(var_mode_method)::out, list(var_mode_method)::out) is det.
|
|
|
|
get_input_output_vars([], [], _, !MaybeSpecMethod, [], []).
|
|
get_input_output_vars([_ | _], [], _, !MaybeSpecMethod, _, _) :-
|
|
unexpected(this_file, "get_input_output_vars: lists not same length").
|
|
get_input_output_vars([], [_ | _], _, !MaybeSpecMethod, _, _) :-
|
|
unexpected(this_file, "get_input_output_vars: lists not same length").
|
|
get_input_output_vars([Var | Vars], [Mode | Modes], ModuleInfo,
|
|
!MaybeSpecMethod, InVarModes, OutVarModes) :-
|
|
( mode_is_fully_input(ModuleInfo, Mode) ->
|
|
get_input_output_vars(Vars, Modes, ModuleInfo, !MaybeSpecMethod,
|
|
InVarModes0, OutVarModes),
|
|
(
|
|
!.MaybeSpecMethod = all_same(ArgMethod)
|
|
;
|
|
!.MaybeSpecMethod = specified(MaybeArgMethods0),
|
|
(
|
|
list.split_last(MaybeArgMethods0, MaybeArgMethods,
|
|
LastMaybeArgMethod)
|
|
->
|
|
(
|
|
LastMaybeArgMethod = yes(ArgMethod)
|
|
;
|
|
LastMaybeArgMethod = no,
|
|
unexpected(this_file,
|
|
"get_input_output_vars: bad method for input var")
|
|
),
|
|
!:MaybeSpecMethod = specified(MaybeArgMethods)
|
|
;
|
|
% We have run out of specified arg_methods, which means the
|
|
% variable we are looking at right now is one that was added
|
|
% by the polymorphism transformation.
|
|
ArgMethod = arg_value,
|
|
!:MaybeSpecMethod = all_same(arg_value)
|
|
)
|
|
),
|
|
InVarModes = [var_mode_method(Var, Mode, ArgMethod) | InVarModes0]
|
|
; mode_is_fully_output(ModuleInfo, Mode) ->
|
|
get_input_output_vars(Vars, Modes, ModuleInfo, !MaybeSpecMethod,
|
|
InVarModes, OutVarModes0),
|
|
(
|
|
!.MaybeSpecMethod = all_same(_ArgMethod)
|
|
% The tabling methods that use answer tables always use arg_value
|
|
% to look up computed output arguments in them. The argument of
|
|
% all_same refers only to the treatment of input arguments.
|
|
;
|
|
!.MaybeSpecMethod = specified(MaybeArgMethods0),
|
|
(
|
|
list.split_last(MaybeArgMethods0, MaybeArgMethods,
|
|
LastMaybeArgMethod)
|
|
->
|
|
expect(unify(LastMaybeArgMethod, no), this_file,
|
|
"get_input_output_vars: bad method for output var"),
|
|
!:MaybeSpecMethod = specified(MaybeArgMethods)
|
|
;
|
|
% We have run out of specified arg_methods, which means the
|
|
% variable we are looking at right now is one that was added
|
|
% by the polymorphism transformation.
|
|
!:MaybeSpecMethod = all_same(arg_value)
|
|
)
|
|
),
|
|
OutVarModes = [var_mode_method(Var, Mode, arg_value) | OutVarModes0]
|
|
;
|
|
% We should have caught this when we added the tabling pragma
|
|
% to the proc_info.
|
|
unexpected(this_file, "get_input_output_vars: bad var")
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred create_instmap_delta(hlds_goals::in, instmap_delta::out) is det.
|
|
|
|
create_instmap_delta([], IMD) :-
|
|
instmap_delta_from_assoc_list([], IMD).
|
|
create_instmap_delta([Goal | Rest], IMD) :-
|
|
Goal = hlds_goal(_, GoalInfo),
|
|
goal_info_get_instmap_delta(GoalInfo, IMD0),
|
|
create_instmap_delta(Rest, IMD1),
|
|
instmap_delta_apply_instmap_delta(IMD0, IMD1, test_size, IMD).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type var_mode_method
|
|
---> var_mode_method(
|
|
prog_var, % The head variable.
|
|
mer_mode, % The mode of the head variable.
|
|
arg_tabling_method
|
|
% For input arguments, this is the arg method to
|
|
% use in looking up the argument in the call table.
|
|
% For output arguments, this is the arg method to
|
|
% use in looking up the argument in the answer
|
|
% table (if any).
|
|
).
|
|
|
|
:- type var_mode_pos_method == var_mode_pos_method(arg_tabling_method).
|
|
|
|
:- type var_mode_pos_method(T)
|
|
---> var_mode_pos_method(
|
|
prog_var, % The head variable.
|
|
mer_mode, % The mode of the head variable.
|
|
int, % The position of the head variable in the list of
|
|
% inputs or outputs; first element is in the
|
|
% position numbered 0.
|
|
T
|
|
% For input arguments, this is the arg method to
|
|
% use in looking up the argument in the call table.
|
|
% For output arguments, this is the arg method to
|
|
% use in looking up the argument in the answer
|
|
% table (if any), which for now is always
|
|
% arg_value.
|
|
%
|
|
% When T is unit, there is no info about how to
|
|
% look up the variable in a call or return table.
|
|
% This is useful for recording the structure of
|
|
% answer blocks and sequences of arguments
|
|
% for I/O tabling.
|
|
).
|
|
|
|
:- func project_var(var_mode_pos_method(T)) = prog_var.
|
|
|
|
project_var(var_mode_pos_method(Var, _, _, _)) = Var.
|
|
|
|
:- func project_var_pos(var_mode_pos_method(T)) = pair(prog_var, int).
|
|
|
|
project_var_pos(var_mode_pos_method(Var, _, Pos, _)) = Var - Pos.
|
|
|
|
:- func project_var_init_inst(module_info, var_mode_pos_method(T)) =
|
|
pair(prog_var, mer_inst).
|
|
|
|
project_var_init_inst(ModuleInfo, var_mode_pos_method(Var, Mode, _, _))
|
|
= Var - Inst :-
|
|
mode_get_insts(ModuleInfo, Mode, Inst, _).
|
|
|
|
:- func project_mode(var_mode_pos_method(T)) = mer_mode.
|
|
|
|
project_mode(var_mode_pos_method(_, Mode, _, _)) = Mode.
|
|
|
|
:- func project_out_arg_method(var_mode_pos_method) =
|
|
var_mode_pos_method(unit).
|
|
|
|
project_out_arg_method(var_mode_pos_method(Var, Mode, Pos, _)) =
|
|
var_mode_pos_method(Var, Mode, Pos, unit).
|
|
|
|
:- func project_out_pos(var_mode_pos_method) = var_mode_method.
|
|
|
|
project_out_pos(var_mode_pos_method(Var, Mode, _Pos, ArgMethod)) =
|
|
var_mode_method(Var, Mode, ArgMethod).
|
|
|
|
:- pred allocate_slot_numbers(list(var_mode_method)::in,
|
|
int::in, list(var_mode_pos_method)::out) is det.
|
|
|
|
allocate_slot_numbers([], _, []).
|
|
allocate_slot_numbers([var_mode_method(Var, Mode, ArgMethod) | VarModes],
|
|
Offset0, [VarModePos | VarModePoss]) :-
|
|
VarModePos = var_mode_pos_method(Var, Mode, Offset0, ArgMethod),
|
|
allocate_slot_numbers(VarModes, Offset0 + 1, VarModePoss).
|
|
|
|
:- pred allocate_plain_slot_numbers(assoc_list(prog_var, mer_mode)::in,
|
|
int::in, list(var_mode_pos_method(unit))::out) is det.
|
|
|
|
allocate_plain_slot_numbers([], _, []).
|
|
allocate_plain_slot_numbers([Var - Mode | VarModes],
|
|
Offset0, [VarModePos | VarModePoss]) :-
|
|
VarModePos = var_mode_pos_method(Var, Mode, Offset0, unit),
|
|
allocate_plain_slot_numbers(VarModes, Offset0 + 1, VarModePoss).
|
|
|
|
:- pred reallocate_slot_numbers(list(var_mode_pos_method(T))::in, int::in,
|
|
list(var_mode_pos_method(T))::out) is det.
|
|
|
|
reallocate_slot_numbers([], _, []).
|
|
reallocate_slot_numbers([VarModePos0 | VarModes], Offset0,
|
|
[VarModePos | VarModePoss]) :-
|
|
VarModePos0 = var_mode_pos_method(Var, Mode, _, ArgMethod),
|
|
VarModePos = var_mode_pos_method(Var, Mode, Offset0, ArgMethod),
|
|
reallocate_slot_numbers(VarModes, Offset0 + 1, VarModePoss).
|
|
|
|
:- pred var_belong_to_list(list(prog_var)::in, var_mode_pos_method(T)::in)
|
|
is semidet.
|
|
|
|
var_belong_to_list(List, var_mode_pos_method(Var, _, _, _)) :-
|
|
list.member(Var, List).
|
|
|
|
:- func bind_vars(list(prog_var)) = instmap_delta.
|
|
|
|
bind_vars(Vars) = InstMapDelta :-
|
|
VarsAndGround = ground_vars(Vars),
|
|
instmap_delta_from_assoc_list(VarsAndGround, InstMapDelta).
|
|
|
|
:- func ground_vars(list(prog_var)) = assoc_list(prog_var, mer_inst).
|
|
|
|
ground_vars(Vars) = VarsAndGround :-
|
|
VarsAndGround = list.map(pair_with_ground, Vars).
|
|
|
|
:- func pair_with_ground(prog_var) = pair(prog_var, mer_inst).
|
|
|
|
pair_with_ground(Var) = Var - ground(shared, none).
|
|
|
|
:- pred goal_info_init_hide(set(prog_var)::in, instmap_delta::in,
|
|
determinism::in, purity::in, prog_context::in, hlds_goal_info::out)
|
|
is det.
|
|
|
|
goal_info_init_hide(NonLocals, InstmapDelta, Detism, Purity, Context,
|
|
GoalInfo) :-
|
|
goal_info_init(NonLocals, InstmapDelta, Detism, Purity, Context,
|
|
GoalInfo0),
|
|
goal_info_add_feature(feature_hide_debug_event, GoalInfo0, GoalInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% For backward compatibility, we treat type_info_type as user_type.
|
|
% This used to make the tabling of type_infos more expensive than
|
|
% necessary, since we essentially tabled the information in the type_info
|
|
% twice, once by tabling the type represented by the type_info (since this
|
|
% was the value of the type argument of the type constructor
|
|
% private_builtin.type_info/1), and then tabling the type_info itself.
|
|
% However, since we made type_info have arity zero, this overhead
|
|
% should be gone.
|
|
%
|
|
:- func builtin_type(type_category) = bool.
|
|
|
|
builtin_type(type_cat_int) = yes.
|
|
builtin_type(type_cat_char) = yes.
|
|
builtin_type(type_cat_string) = yes.
|
|
builtin_type(type_cat_float) = yes.
|
|
builtin_type(type_cat_void) = yes.
|
|
builtin_type(type_cat_type_info) = no.
|
|
builtin_type(type_cat_type_ctor_info) = yes.
|
|
builtin_type(type_cat_typeclass_info) = yes.
|
|
builtin_type(type_cat_base_typeclass_info) = yes.
|
|
builtin_type(type_cat_higher_order) = no.
|
|
builtin_type(type_cat_enum) = no.
|
|
builtin_type(type_cat_dummy) = no.
|
|
builtin_type(type_cat_variable) = no.
|
|
builtin_type(type_cat_tuple) = no.
|
|
builtin_type(type_cat_user_ctor) = no.
|
|
|
|
% Figure out what kind of data structure implements the lookup table
|
|
% for values of a given builtin type.
|
|
%
|
|
:- pred lookup_tabling_category(type_category::in,
|
|
maybe(pair(string, table_trie_step))::out) is det.
|
|
|
|
lookup_tabling_category(type_cat_int,
|
|
yes("int" - table_trie_step_int)).
|
|
lookup_tabling_category(type_cat_char,
|
|
yes("char" - table_trie_step_char)).
|
|
lookup_tabling_category(type_cat_string,
|
|
yes("string" - table_trie_step_string)).
|
|
lookup_tabling_category(type_cat_float,
|
|
yes("float" - table_trie_step_float)).
|
|
lookup_tabling_category(type_cat_void, _) :-
|
|
unexpected(this_file, "lookup_tabling_category: void").
|
|
lookup_tabling_category(type_cat_dummy, _) :-
|
|
unexpected(this_file, "lookup_tabling_category: dummy_type").
|
|
lookup_tabling_category(type_cat_type_info,
|
|
yes("typeinfo" - table_trie_step_typeinfo)).
|
|
lookup_tabling_category(type_cat_type_ctor_info,
|
|
yes("typeinfo" - table_trie_step_typeinfo)).
|
|
lookup_tabling_category(type_cat_typeclass_info, _) :-
|
|
unexpected(this_file, "lookup_tabling_category: typeclass_info_type").
|
|
lookup_tabling_category(type_cat_base_typeclass_info, _) :-
|
|
unexpected(this_file, "lookup_tabling_category: base_typeclass_info_type").
|
|
lookup_tabling_category(type_cat_enum, no).
|
|
lookup_tabling_category(type_cat_higher_order, no).
|
|
lookup_tabling_category(type_cat_tuple, no).
|
|
lookup_tabling_category(type_cat_variable, no).
|
|
lookup_tabling_category(type_cat_user_ctor, no).
|
|
|
|
% Figure out which save and restore predicates in library/table_builtin.m
|
|
% we need to use for values of types belonging the type category given by
|
|
% the first argument. The returned value replaces CAT in
|
|
% table_save_CAT_answer and table_restore_CAT_answer.
|
|
|
|
:- pred type_save_category(type_category::in, string::out) is det.
|
|
|
|
type_save_category(type_cat_enum, "enum").
|
|
type_save_category(type_cat_int, "int").
|
|
type_save_category(type_cat_char, "char").
|
|
type_save_category(type_cat_string, "string").
|
|
type_save_category(type_cat_float, "float").
|
|
type_save_category(type_cat_higher_order, "pred").
|
|
type_save_category(type_cat_tuple, "any").
|
|
type_save_category(type_cat_user_ctor, "any"). % could do better
|
|
type_save_category(type_cat_variable, "any"). % could do better
|
|
type_save_category(type_cat_dummy, _) :-
|
|
unexpected(this_file, "type_save_category: dummy").
|
|
type_save_category(type_cat_void, _) :-
|
|
unexpected(this_file, "type_save_category: void").
|
|
type_save_category(type_cat_type_info, "any"). % could do better
|
|
type_save_category(type_cat_type_ctor_info, _) :-
|
|
unexpected(this_file, "type_save_category: type_ctor_info").
|
|
type_save_category(type_cat_typeclass_info, _) :-
|
|
unexpected(this_file, "type_save_category: typeclass_info").
|
|
type_save_category(type_cat_base_typeclass_info, _) :-
|
|
unexpected(this_file, "type_save_category: base_typeclass_info").
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- func get_debug_arg_string(table_info) = string.
|
|
|
|
get_debug_arg_string(TableInfo) = DebugArgStr :-
|
|
ModuleInfo = TableInfo ^ table_module_info,
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.lookup_bool_option(Globals, table_debug, TableDebug),
|
|
(
|
|
TableDebug = yes,
|
|
DebugArgStr = "MR_TRUE"
|
|
;
|
|
TableDebug = no,
|
|
DebugArgStr = "MR_FALSE"
|
|
).
|
|
|
|
:- func get_back_arg_string(table_info) = string.
|
|
|
|
get_back_arg_string(TableInfo) = BackArgStr :-
|
|
ProcInfo = TableInfo ^ table_cur_proc_info,
|
|
proc_info_get_table_attributes(ProcInfo, MaybeAttributes),
|
|
(
|
|
MaybeAttributes = yes(Attributes),
|
|
MaybeSizeLimit = Attributes ^ table_attr_size_limit,
|
|
(
|
|
MaybeSizeLimit = yes(_),
|
|
BackArgStr = "MR_TRUE"
|
|
;
|
|
MaybeSizeLimit = no,
|
|
BackArgStr = "MR_FALSE"
|
|
)
|
|
;
|
|
MaybeAttributes = no,
|
|
BackArgStr = "MR_FALSE"
|
|
).
|
|
|
|
:- type call_or_answer_table
|
|
---> call_table
|
|
; answer_table.
|
|
|
|
:- func stats_arg(table_attr_statistics, call_or_answer_table, int) = string.
|
|
|
|
stats_arg(Statistics, Kind, SeqNum) = ArgStr :-
|
|
(
|
|
Statistics = table_dont_gather_statistics,
|
|
ArgStr = "NULL"
|
|
;
|
|
Statistics = table_gather_statistics,
|
|
(
|
|
Kind = call_table,
|
|
ArgStr = "&" ++ proc_table_info_name ++
|
|
"->MR_pt_call_table_stats[" ++ int_to_string(SeqNum) ++ "]"
|
|
;
|
|
Kind = answer_table,
|
|
ArgStr = "&" ++ proc_table_info_name ++
|
|
"->MR_pt_answer_table_stats[" ++ int_to_string(SeqNum) ++ "]"
|
|
)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred table_gen_make_type_info_var(mer_type::in, term.context::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, prog_var::out,
|
|
list(hlds_goal)::out) is det.
|
|
|
|
table_gen_make_type_info_var(Type, Context, !VarSet, !VarTypes, !TableInfo,
|
|
TypeInfoVar, TypeInfoGoals) :-
|
|
table_gen_make_type_info_vars([Type], Context, !VarSet, !VarTypes,
|
|
!TableInfo, TypeInfoVars, TypeInfoGoals),
|
|
( TypeInfoVars = [TypeInfoVar0] ->
|
|
TypeInfoVar = TypeInfoVar0
|
|
;
|
|
unexpected(this_file,
|
|
"table_gen_make_type_info_var: list length != 1")
|
|
).
|
|
|
|
:- pred table_gen_make_type_info_vars(list(mer_type)::in, term.context::in,
|
|
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
|
|
table_info::in, table_info::out, list(prog_var)::out,
|
|
list(hlds_goal)::out) is det.
|
|
|
|
table_gen_make_type_info_vars(Types, Context, !VarSet, !VarTypes, !TableInfo,
|
|
TypeInfoVars, TypeInfoGoals) :-
|
|
% Extract the information from table_info.
|
|
table_info_extract(!.TableInfo, ModuleInfo0, PredInfo0, ProcInfo0),
|
|
|
|
% Put the varset and vartypes from the simplify_info
|
|
% back in the proc_info.
|
|
proc_info_set_vartypes(!.VarTypes, ProcInfo0, ProcInfo1),
|
|
proc_info_set_varset(!.VarSet, ProcInfo1, ProcInfo2),
|
|
|
|
% Call polymorphism.m to create the type_infos.
|
|
create_poly_info(ModuleInfo0, PredInfo0, ProcInfo2, PolyInfo0),
|
|
polymorphism_make_type_info_vars(Types, Context,
|
|
TypeInfoVars, TypeInfoGoals, PolyInfo0, PolyInfo),
|
|
poly_info_extract(PolyInfo, PredInfo0, PredInfo,
|
|
ProcInfo0, ProcInfo, ModuleInfo),
|
|
|
|
% Get the new varset and vartypes from the proc_info.
|
|
proc_info_get_vartypes(ProcInfo, !:VarTypes),
|
|
proc_info_get_varset(ProcInfo, !:VarSet),
|
|
|
|
% Put the new module_info, pred_info, and proc_info back in the
|
|
% table_info.
|
|
table_info_init(ModuleInfo, PredInfo, ProcInfo, !:TableInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred var_mode_pos_is_io_state(vartypes::in,
|
|
var_mode_pos_method::in) is semidet.
|
|
|
|
var_mode_pos_is_io_state(VarTypes, VarModePosMethod) :-
|
|
var_is_io_state(VarTypes, project_var(VarModePosMethod)).
|
|
|
|
:- pred var_mode_is_io_state(vartypes::in, pair(prog_var, mer_mode)::in)
|
|
is semidet.
|
|
|
|
var_mode_is_io_state(VarTypes, Var - _) :-
|
|
var_is_io_state(VarTypes, Var).
|
|
|
|
:- pred var_is_io_state(vartypes::in, prog_var::in) is semidet.
|
|
|
|
var_is_io_state(VarTypes, Var) :-
|
|
map.lookup(VarTypes, Var, VarType),
|
|
type_is_io_state(VarType).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- func tabling_c_attributes_dupl = pragma_foreign_proc_attributes.
|
|
|
|
tabling_c_attributes_dupl = Attrs :-
|
|
Attrs0 = default_attributes(lang_c),
|
|
set_may_call_mercury(proc_will_not_call_mercury, Attrs0, Attrs1),
|
|
set_affects_liveness(proc_does_not_affect_liveness, Attrs1, Attrs2),
|
|
set_may_duplicate(yes(proc_may_duplicate), Attrs2, Attrs).
|
|
|
|
:- func tabling_c_attributes_no_dupl = pragma_foreign_proc_attributes.
|
|
|
|
tabling_c_attributes_no_dupl = Attrs :-
|
|
Attrs0 = default_attributes(lang_c),
|
|
set_may_call_mercury(proc_will_not_call_mercury, Attrs0, Attrs1),
|
|
set_affects_liveness(proc_does_not_affect_liveness, Attrs1, Attrs).
|
|
|
|
:- func make_generator_c_attributes = pragma_foreign_proc_attributes.
|
|
|
|
make_generator_c_attributes = Attrs :-
|
|
Attrs0 = default_attributes(lang_c),
|
|
set_may_call_mercury(proc_may_call_mercury, Attrs0, Attrs).
|
|
|
|
:- func dummy_type_var = mer_type.
|
|
|
|
dummy_type_var = Type :-
|
|
varset.init(DummyTVarSet0),
|
|
varset.new_var(DummyTVarSet0, DummyTVar, _),
|
|
Type = type_variable(DummyTVar, kind_star).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- func proc_table_info_name = string.
|
|
:- func cur_table_node_name = string.
|
|
:- func next_table_node_name = string.
|
|
:- func table_tip_node_name = string.
|
|
:- func base_name = string.
|
|
:- func memo_non_record_name = string.
|
|
:- func subgoal_name = string.
|
|
:- func status_name = string.
|
|
:- func answer_block_name = string.
|
|
:- func success_indicator_name = string.
|
|
:- func arg_name(int) = string.
|
|
:- func num_input_args_name = string.
|
|
:- func pred_name_var_name = string.
|
|
:- func answer_table_name = string.
|
|
:- func consumer_name = string.
|
|
:- func generator_name = string.
|
|
:- func generator_pred_name = string.
|
|
:- func returning_generator_locn = string.
|
|
|
|
proc_table_info_name = "proc_table_info".
|
|
cur_table_node_name = "cur_node".
|
|
next_table_node_name = "next_node".
|
|
table_tip_node_name = "table_tip".
|
|
base_name = "base".
|
|
memo_non_record_name = "record".
|
|
subgoal_name = "subgoal".
|
|
status_name = "status".
|
|
answer_block_name = "answerblock".
|
|
success_indicator_name = "SUCCESS_INDICATOR".
|
|
arg_name(VarSeqNum) = "arg" ++ int_to_string(VarSeqNum).
|
|
num_input_args_name = "num_input_args".
|
|
pred_name_var_name = "pred_name".
|
|
answer_table_name = "answer_table".
|
|
consumer_name = "consumer".
|
|
generator_name = "generator".
|
|
generator_pred_name = "generator_pred".
|
|
returning_generator_locn = "MR_mmos_returning_generator".
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type table_info
|
|
---> table_info(
|
|
table_module_info :: module_info,
|
|
table_cur_pred_info :: pred_info,
|
|
table_cur_proc_info :: proc_info
|
|
).
|
|
|
|
:- pred table_info_init(module_info::in,
|
|
pred_info::in, proc_info::in, table_info::out) is det.
|
|
|
|
:- pred table_info_extract(table_info::in, module_info::out,
|
|
pred_info::out, proc_info::out) is det.
|
|
|
|
table_info_init(ModuleInfo, PredInfo, ProcInfo, TableInfo) :-
|
|
TableInfo = table_info(ModuleInfo, PredInfo, ProcInfo).
|
|
|
|
table_info_extract(TableInfo, ModuleInfo, PredInfo, ProcInfo) :-
|
|
TableInfo = table_info(ModuleInfo, PredInfo, ProcInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- func this_file = string.
|
|
|
|
this_file = "table_gen.m".
|
|
|
|
%-----------------------------------------------------------------------------%
|