Files
mercury/compiler/table_gen.m
Zoltan Somogyi b560f66ab9 Move four modules from check_hlds.m to hlds.m.
After this, I think all modules in the check_hlds package belong there.

compiler/inst_match.m:
compiler/mode_test.m:
    Move these modules from the check_hlds package to the hlds package
    because most of their uses are outside the semantic analysis passes
    that the check_hlds package is intended to contain.

compiler/inst_merge.m:
    Move this module from the check_hlds package to the hlds package
    because it is imported by only two modules, instmap.m and inst_match.m,
    and after this diff, both are in the hlds package.

compiler/implementation_defined_literals.m:
    Move this module from the check_hlds package to the hlds package
    because it does a straightforward program transformation that
    does not have anything to do with semantic analysis (though its
    invocation does happen between semantic analysis passes).

compiler/notes/compiler_design.html:
    Update the documentation of the goal_path.m module. (I checked the
    documentation of the moved modules, which did not need updates,
    and found the need for this instead.)

compiler/*.m:
    Conform to the changes above. (For many modules, this deletes
    their import of the check_hlds package itself.)
2026-02-27 15:16:44 +11:00

4306 lines
181 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1997-2012 The University of Melbourne.
% Copyright (C) 2015-2026 The Mercury team.
% 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.
:- import_module hlds.hlds_module.
:- import_module parse_tree.
:- import_module parse_tree.error_spec.
:- 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.
:- import_module check_hlds.polymorphism_type_info.
:- import_module check_hlds.purity.
:- import_module check_hlds.recompute_instmap_deltas.
:- import_module hlds.code_model.
:- import_module hlds.goal_contains.
:- 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_markers.
:- import_module hlds.hlds_pred.
:- import_module hlds.hlds_proc_util.
:- import_module hlds.hlds_rtti.
:- import_module hlds.instmap.
:- import_module hlds.make_goal.
:- import_module hlds.mode_test.
:- import_module hlds.mode_util.
:- import_module hlds.pred_name.
:- import_module hlds.pred_table.
:- import_module hlds.status.
:- import_module hlds.type_util.
:- import_module libs.
:- import_module libs.globals.
:- import_module libs.options.
:- import_module ll_backend.
:- import_module ll_backend.continuation_info.
:- import_module mdbcomp.
:- import_module mdbcomp.builtin_modules.
:- import_module mdbcomp.prim_data.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.builtin_lib_types.
:- import_module parse_tree.parse_tree_out_misc.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_data_foreign.
:- import_module parse_tree.prog_data_pragma.
:- import_module parse_tree.prog_mode.
:- import_module parse_tree.prog_type.
:- import_module parse_tree.prog_type_construct.
:- import_module parse_tree.prog_type_scan.
:- import_module parse_tree.prog_type_test.
:- import_module parse_tree.prog_util.
:- import_module parse_tree.set_of_var.
:- import_module parse_tree.var_table.
:- import_module assoc_list.
:- import_module bool.
:- import_module int.
:- import_module map.
:- import_module maybe.
:- import_module one_or_more.
:- import_module pair.
:- import_module require.
:- import_module set.
:- import_module solutions.
:- import_module string.
:- import_module table_builtin.
:- 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_get_globals(!.ModuleInfo, Globals),
globals.lookup_bool_option(Globals, trace_table_io, TraceTableIO),
module_info_get_pred_id_table(!.ModuleInfo, PredIdTable0),
map.keys(PredIdTable0, PredIds),
map.init(GenMap0),
table_gen_process_preds(TraceTableIO, PredIds,
!ModuleInfo, GenMap0, _, !Specs).
:- pred table_gen_process_preds(bool::in, 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(TraceTableIO, [PredId | PredIds],
!ModuleInfo, !GenMap, !Specs) :-
table_gen_process_pred(TraceTableIO, PredId,
!ModuleInfo, !GenMap, !Specs),
table_gen_process_preds(TraceTableIO, PredIds,
!ModuleInfo, !GenMap, !Specs).
:- pred table_gen_process_pred(bool::in, 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(TraceTableIO, PredId, !ModuleInfo, !GenMap, !Specs) :-
module_info_pred_info(!.ModuleInfo, PredId, PredInfo),
ProcIds = pred_info_all_procids(PredInfo),
table_gen_process_procs(TraceTableIO, PredId, ProcIds,
!ModuleInfo, !GenMap, !Specs).
:- pred table_gen_process_procs(bool::in, 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(_, _, [], !ModuleInfo, !GenMap, !Specs).
table_gen_process_procs(TraceTableIO, PredId, [ProcId | ProcIds],
!ModuleInfo, !GenMap, !Specs) :-
module_info_pred_info(!.ModuleInfo, PredId, PredInfo),
pred_info_get_proc_table(PredInfo, ProcTable),
map.lookup(ProcTable, ProcId, ProcInfo0),
table_gen_process_proc(TraceTableIO, PredId, ProcId, ProcInfo0, PredInfo,
!ModuleInfo, !GenMap, !Specs),
table_gen_process_procs(TraceTableIO, PredId, ProcIds,
!ModuleInfo, !GenMap, !Specs).
:- pred table_gen_process_proc(bool::in,
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(TraceTableIO, PredId, ProcId, ProcInfo0, PredInfo0,
!ModuleInfo, !GenMap, !Specs) :-
proc_info_get_eval_method(ProcInfo0, EvalMethod),
(
EvalMethod = eval_tabled(TabledMethod),
table_gen_transform_proc_if_possible(TabledMethod, PredId,
ProcId, ProcInfo0, _, PredInfo0, _, !ModuleInfo, !GenMap, !Specs)
;
EvalMethod = eval_normal,
( if
TraceTableIO = yes,
proc_info_has_io_state_pair(!.ModuleInfo, ProcInfo0,
_InArgNum, _OutArgNum)
then
table_gen_process_io_proc(PredId, ProcId, ProcInfo0, PredInfo0,
!ModuleInfo, !GenMap, !Specs)
else
true
)
).
%---------------------------------------------------------------------------%
:- pred table_gen_process_io_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_io_proc(PredId, ProcId, ProcInfo0, PredInfo0,
!ModuleInfo, !GenMap, !Specs) :-
CodeModel = proc_info_interface_code_model(ProcInfo0),
(
CodeModel = model_det
;
( CodeModel = model_semi
; CodeModel = model_non
),
pred_id_to_int(PredId, PredIdInt),
Msg = string.format("I/O procedure pred id %d not model_det",
[i(PredIdInt)]),
unexpected($pred, Msg)
),
module_info_get_globals(!.ModuleInfo, Globals),
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(!.ModuleInfo, PredInfo0,
PredId, ProcId),
!:Specs = [Spec | !.Specs]
;
AnnotationIsMissing = no
),
(
TransformPrimitive = no
;
TransformPrimitive = yes(Unitize),
globals.lookup_bool_option(Globals, trace_table_io_only_retry,
TraceTableIoOnlyRetry),
(
TraceTableIoOnlyRetry = no,
pred_info_get_class_context(PredInfo0, TypeClassConstraints),
TypeClassConstraints =
univ_exist_constraints(UnivConstraints, ExistConstraints),
( if
UnivConstraints = [],
ExistConstraints = []
then
EntryKind = entry_stores_procid_inputs_outputs
else
EntryKind = entry_stores_procid_outputs
)
;
TraceTableIoOnlyRetry = yes,
EntryKind = entry_stores_outputs
),
TableIoMethod = tabled_io(EntryKind, Unitize),
proc_info_set_eval_method(eval_tabled(TableIoMethod),
ProcInfo0, ProcInfo1),
table_gen_transform_proc_if_possible(TableIoMethod,
PredId, ProcId, ProcInfo1, _, PredInfo0, _, !ModuleInfo,
!GenMap, !Specs)
).
:- 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,
( if
Require = yes,
not any_mercury_builtin_module(PredModuleName)
then
AnnotationIsMissing = yes,
TransformInfo = no
else
AnnotationIsMissing = no,
(
TransformAll = no,
TransformInfo = no
;
TransformAll = yes,
may_call_mercury_attributes(BodyGoal, MayCallMercuryAttrs),
( if MayCallMercuryAttrs = [proc_may_call_mercury] then
TransformInfo = no
else
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($pred,
"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),
not TabledForIoAttr = proc_not_tabled_for_io
).
:- func report_missing_tabled_for_io(module_info, pred_info, pred_id, proc_id)
= error_spec.
report_missing_tabled_for_io(ModuleInfo, PredInfo, PredId, ProcId) = Spec :-
pred_info_get_context(PredInfo, Context),
ProcPieces = describe_one_proc_name(ModuleInfo, yes(color_subject),
should_not_module_qualify, proc(PredId, ProcId)),
Pieces = [words("Error:")] ++ ProcPieces ++ [words("contains")] ++
color_as_incorrect([words("untabled I/O primitive.")]) ++
[nl],
Spec = spec($pred, severity_error, phase_code_gen, Context, Pieces).
%---------------------------------------------------------------------------%
:- pred table_gen_transform_proc_if_possible(tabled_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(TabledMethod, PredId, ProcId,
!ProcInfo, !PredInfo, !ModuleInfo, !GenMap, !Specs) :-
find_grade_problems_for_tabling(!.ModuleInfo, PredId, ProcId, TabledMethod,
GradeSpecs),
(
GradeSpecs = [],
table_gen_transform_proc(TabledMethod, PredId, ProcId,
!ProcInfo, !PredInfo, !ModuleInfo, !GenMap)
;
GradeSpecs = [_ | _],
( if TabledMethod = tabled_memo(table_attr_ignore_without_warning) then
true
else
!:Specs = GradeSpecs ++ !.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 find_grade_problems_for_tabling(module_info::in,
pred_id::in, proc_id::in, tabled_eval_method::in,
list(error_spec)::out) is det.
find_grade_problems_for_tabling(ModuleInfo, PredId, ProcId, TabledMethod,
!:Specs) :-
% We use severity_informational for any messages because severity_warning
% would combine with --halt-at-warn to prevent the clean compilation
% of the library and the compiler.
%
% Please keep this code in sync with current_grade_supports_tabling
% in globals.m.
!:Specs = [],
module_info_get_globals(ModuleInfo, Globals),
globals.get_target(Globals, Target),
(
Target = target_c
;
( Target = target_csharp
; Target = target_java
),
general_cannot_table_reason_spec(ModuleInfo, PredId, ProcId,
TabledMethod, gen_reason_non_c_backend, TargetSpec),
!:Specs = [TargetSpec | !.Specs]
),
globals.get_gc_method(Globals, GC),
(
GC = gc_accurate,
general_cannot_table_reason_spec(ModuleInfo, PredId, ProcId,
TabledMethod, gen_reason_gc_accurate, GcSpec),
!:Specs = [GcSpec | !.Specs]
;
GC = gc_hgc,
general_cannot_table_reason_spec(ModuleInfo, PredId, ProcId,
TabledMethod, gen_reason_gc_hgc, GcSpec),
!:Specs = [GcSpec | !.Specs]
;
( GC = gc_automatic
; GC = gc_none
; GC = gc_boehm
; GC = gc_boehm_debug
)
),
globals.lookup_bool_option(Globals, parallel, Parallel),
(
Parallel = no
;
Parallel = yes,
general_cannot_table_reason_spec(ModuleInfo, PredId, ProcId,
TabledMethod, gen_reason_parallel, ParSpec),
!:Specs = [ParSpec | !.Specs]
),
(
TabledMethod = tabled_minimal(_),
globals.lookup_bool_option(Globals, highlevel_code, HighLevelCode),
(
HighLevelCode = yes,
mm_cannot_table_reason_spec(mm_reason_hlc, HLCSpec),
!:Specs = [HLCSpec | !.Specs]
;
HighLevelCode = no
),
globals.lookup_bool_option(Globals, use_trail, UseTrail),
(
UseTrail = yes,
mm_cannot_table_reason_spec(mm_reason_trailing, TrailSpec),
!:Specs = [TrailSpec | !.Specs]
;
UseTrail = no
),
globals.lookup_bool_option(Globals, profile_calls, ProfileCalls),
globals.lookup_bool_option(Globals, profile_deep, ProfileDeep),
( if ( ProfileCalls = yes ; ProfileDeep = yes ) then
mm_cannot_table_reason_spec(mm_reason_profiling, ProfSpec),
!:Specs = [ProfSpec | !.Specs]
else
true
)
;
( TabledMethod = tabled_loop_check
; TabledMethod = tabled_memo(_)
; TabledMethod = tabled_io(_, _)
)
).
%---------------------%
:- pred general_cannot_table_reason_spec(module_info::in,
pred_id::in, proc_id::in, tabled_eval_method::in,
general_cannot_table_reason::in, error_spec::out) is det.
general_cannot_table_reason_spec(ModuleInfo, PredId, ProcId, TabledMethod,
Reason, Spec) :-
ReasonDesc = color_as_incorrect(gen_cannot_table_reason_desc(Reason)),
(
( TabledMethod = tabled_loop_check
; TabledMethod = tabled_memo(_)
),
TabledMethodStr = tabled_eval_method_to_string(TabledMethod),
module_info_pred_info(ModuleInfo, PredId, PredInfo),
ProcPieces = describe_qual_proc_name(ModuleInfo, proc(PredId, ProcId)),
pred_info_get_context(PredInfo, Context),
Pieces = [words("Ignoring the"), pragma_decl(TabledMethodStr),
words("declaration for")] ++ ProcPieces ++ [suffix(","),
words("because tabling is")] ++ ReasonDesc ++ [nl],
Spec = spec($pred, severity_warning(warn_cannot_table), phase_code_gen,
Context, Pieces)
;
TabledMethod = tabled_io(_, _),
module_info_pred_info(ModuleInfo, PredId, PredInfo),
pred_info_get_context(PredInfo, Context),
Pieces = [words("Warning: debugging implicitly tables"),
words("all predicates that perform I/O"),
words("(to make the mdb command `retry' safe across I/O),"),
words("but tabling is")] ++ ReasonDesc ++ [nl],
Spec = spec($pred, severity_warning(warn_cannot_table), phase_code_gen,
Context, Pieces)
;
TabledMethod = tabled_minimal(_),
Pieces = [words("Error: minimal model tabling is")] ++
ReasonDesc ++ [nl],
% We generate one no-context error_spec for each affected predicate,
% but we print only one copy of each duplicated error_spec.
Spec = no_ctxt_spec($pred, severity_error, phase_code_gen, Pieces)
).
:- type general_cannot_table_reason
---> gen_reason_non_c_backend
; gen_reason_gc_accurate
; gen_reason_gc_hgc
; gen_reason_parallel.
:- func gen_cannot_table_reason_desc(general_cannot_table_reason)
= list(format_piece).
gen_cannot_table_reason_desc(Reason) = Desc :-
(
Reason = gen_reason_non_c_backend,
Desc = [words("is implemented only on the C backend."), nl]
;
Reason = gen_reason_gc_accurate,
Desc = [words("is not compatible with --gc accurate."), nl]
;
Reason = gen_reason_gc_hgc,
Desc = [words("is not compatible with --gc hgc."), nl]
;
Reason = gen_reason_parallel,
Desc = [words("is not compatible with parallel execution."), nl]
).
%---------------------%
:- pred mm_cannot_table_reason_spec(mm_cannot_table_reason::in,
error_spec::out) is det.
mm_cannot_table_reason_spec(Reason, Spec) :-
Pieces = [words("Error: minimal model tabling is not compatible with")] ++
mm_cannot_table_reason_desc(Reason),
% We generate one no-context error_spec for each affected predicate,
% but we print only one copy of each duplicated error_spec.
Spec = no_ctxt_spec($pred, severity_error, phase_code_gen, Pieces).
:- type mm_cannot_table_reason
---> mm_reason_hlc
; mm_reason_trailing
; mm_reason_profiling.
:- func mm_cannot_table_reason_desc(mm_cannot_table_reason)
= list(format_piece).
mm_cannot_table_reason_desc(Reason) = Desc :-
(
Reason = mm_reason_hlc,
Desc = [words("generating high level code.")]
;
Reason = mm_reason_trailing,
Desc = [words("trailing.")]
;
Reason = mm_reason_profiling,
Desc = [words("profiling.")]
).
%---------------------------------------------------------------------------%
:- pred table_gen_transform_proc(tabled_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(TabledMethod, 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_var_table(!.ProcInfo, VarTable0),
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
),
(
TabledMethod = tabled_io(_, _),
expect(unify(MaybeAttributes, no), $pred, "tabled_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 = msm_all_same(arg_value),
Statistics = table_do_not_gather_statistics,
MaybeSizeLimit = no
;
( TabledMethod = tabled_loop_check
; TabledMethod = tabled_memo(_)
; TabledMethod = tabled_minimal(_)
),
CallStrictness = Attributes ^ table_attr_strictness,
Statistics = Attributes ^ table_attr_statistics,
MaybeSizeLimit = Attributes ^ table_attr_size_limit,
(
CallStrictness = cts_all_strict,
MaybeSpecMethod = msm_all_same(arg_value)
;
CallStrictness = cts_all_fast_loose,
MaybeSpecMethod = msm_all_same(arg_addr)
;
CallStrictness = cts_specified(ArgMethods, HiddenArgMethod),
MaybeSpecMethod = msm_specified(ArgMethods, HiddenArgMethod)
),
(
TabledMethod = tabled_loop_check
;
TabledMethod = tabled_memo(_)
;
TabledMethod = tabled_minimal(_),
expect(unify(MaybeSizeLimit, no), $pred,
"tabled_minimal with size limit"),
expect(unify(MaybeSpecMethod, msm_all_same(arg_value)), $pred,
"tabled_minimal without all_strict")
)
),
get_input_output_vars(!.ModuleInfo, VarTable0, HeadVars, ArgModes,
MaybeSpecMethod, _, InputVarModeMethods, OutputVarModeMethods),
allocate_slot_numbers(InputVarModeMethods, 0, NumberedInputVars),
allocate_slot_numbers(OutputVarModeMethods, 0, NumberedOutputVars),
(
TabledMethod = tabled_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, VarTable0, VarTable,
TableInfo0, TableInfo, Goal, MaybeProcTableIOInfo),
MaybeCallTableTip = no,
MaybeProcTableStructInfo = no
;
TabledMethod = tabled_loop_check,
create_new_loop_goal(OrigGoal, Statistics,
PredId, ProcId, HeadVars, NumberedInputVars, NumberedOutputVars,
VarTable0, VarTable, TableInfo0, TableInfo,
CallTableTip, Goal, InputSteps),
MaybeOutputSteps = no,
generate_gen_proc_table_info(TableInfo, PredId, ProcId,
VarTable, TabledMethod, InputSteps, MaybeOutputSteps,
InputVarModeMethods, OutputVarModeMethods, ProcTableStructInfo),
MaybeCallTableTip = yes(CallTableTip),
MaybeProcTableIOInfo = no,
MaybeProcTableStructInfo = yes(ProcTableStructInfo)
;
TabledMethod = tabled_memo(_),
(
CodeModel = model_non,
create_new_memo_non_goal(Detism, OrigGoal, Statistics,
MaybeSizeLimit, PredId, ProcId,
HeadVars, NumberedInputVars, NumberedOutputVars,
VarTable0, VarTable, TableInfo0, TableInfo,
CallTableTip, Goal, InputSteps, OutputSteps),
MaybeOutputSteps = yes(OutputSteps)
;
( CodeModel = model_det
; CodeModel = model_semi
),
create_new_memo_goal(Detism, OrigGoal, Statistics, MaybeSizeLimit,
PredId, ProcId,
HeadVars, NumberedInputVars, NumberedOutputVars,
VarTable0, VarTable, TableInfo0, TableInfo,
CallTableTip, Goal, InputSteps),
MaybeOutputSteps = no
),
generate_gen_proc_table_info(TableInfo, PredId, ProcId, VarTable,
TabledMethod, InputSteps, MaybeOutputSteps,
InputVarModeMethods, OutputVarModeMethods, ProcTableStructInfo),
MaybeCallTableTip = yes(CallTableTip),
MaybeProcTableIOInfo = no,
MaybeProcTableStructInfo = yes(ProcTableStructInfo)
;
TabledMethod = tabled_minimal(MinimalMethod),
expect(unify(CodeModel, model_non), $pred,
"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,
VarTable0, VarTable, TableInfo0, TableInfo,
CallTableTip, Goal, InputSteps, OutputSteps),
MaybeCallTableTip = yes(CallTableTip),
MaybeOutputSteps = yes(OutputSteps),
generate_gen_proc_table_info(TableInfo, PredId, ProcId, VarTable,
TabledMethod, InputSteps, MaybeOutputSteps,
InputVarModeMethods, OutputVarModeMethods,
ProcTableStructInfo),
MaybeProcTableStructInfo = yes(ProcTableStructInfo)
;
MinimalMethod = own_stacks_consumer,
do_own_stack_transform(Detism, OrigGoal, Statistics,
PredId, ProcId, !.PredInfo, !.ProcInfo,
HeadVars, NumberedInputVars, NumberedOutputVars,
VarTable0, VarTable, TableInfo0, TableInfo,
!GenMap, Goal, _InputSteps, _OutputSteps),
MaybeCallTableTip = no,
MaybeProcTableStructInfo = 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($pred, "own stacks generator")
),
MaybeProcTableIOInfo = no
),
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_var_table(VarTable, !ProcInfo),
proc_info_set_call_table_tip(MaybeCallTableTip, !ProcInfo),
(
MaybeProcTableIOInfo = no
;
MaybeProcTableIOInfo = yes(FinalProcTableIOInfo),
proc_info_set_maybe_proc_table_io_info(yes(FinalProcTableIOInfo),
!ProcInfo)
),
(
MaybeProcTableStructInfo = no
;
MaybeProcTableStructInfo = yes(FinalProcTableStructInfo),
PredProcId = proc(PredId, ProcId),
add_proc_table_struct(PredProcId, FinalProcTableStructInfo, !.ProcInfo,
!ModuleInfo)
),
% Some of the instmap_deltas generated in this module are pretty dodgy
% (especially those for if-then-elses), so recompute them here.
% XXX Fix this: generate correct-by-construction instmap_deltas.
recompute_instmap_delta_proc(no_recomp_atomics, !ProcInfo, !ModuleInfo),
pred_info_get_proc_table(!.PredInfo, ProcTable1),
map.det_update(ProcId, !.ProcInfo, ProcTable1, ProcTable),
pred_info_set_proc_table(ProcTable, !PredInfo),
% The transformation doesn't pay attention to the purity of compound goals,
% so recompute the purity here.
% XXX Fix this: generate correct-by-construction purity information.
repuritycheck_proc(!.ModuleInfo, proc(PredId, ProcId), !PredInfo),
module_info_set_pred_info(PredId, !.PredInfo, !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(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,
var_table::in, var_table::out, table_info::in, table_info::out,
prog_var::out, hlds_goal::out, list(table_step_desc)::out) is det.
create_new_loop_goal(OrigGoal, Statistics, PredId, ProcId,
HeadVars, NumberedInputVars, NumberedOutputVars, !VarTable,
!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_of_var.list_to_set(HeadVars, OrigNonLocals),
OrigGoal = hlds_goal(_, OrigGoalInfo),
OrigInstMapDelta = goal_info_get_instmap_delta(OrigGoalInfo),
Context = goal_info_get_context(OrigGoalInfo),
ModuleInfo = !.TableInfo ^ table_module_info,
generate_plain_call_table_lookup_goal(loop_status_type,
"table_loop_setup_shortcut", "MR_tbl_loop_setup",
NumberedInputVars, PredId, ProcId, Statistics, Context,
!VarTable, !TableInfo, TableTipVar, StatusVar, LookUpGoal, Steps),
generate_error_goal(!.TableInfo, Context, infinite_recursion_msg,
!VarTable, ActiveGoal),
TableTipArg = foreign_arg(TableTipVar,
yes(foreign_arg_name_mode(cur_table_node_name, in_mode)),
trie_node_type, bp_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_call_foreign_proc(ModuleInfo, MarkInactivePredName,
[TableTipArg], [], instmap_delta_bind_no_var, detism_det,
purity_impure, tabling_c_attributes_dupl, MarkInactiveCode,
Context, MarkInactiveGoal),
table_call_foreign_proc(ModuleInfo, MarkInactiveFailPredName,
[TableTipArg], [], instmap_delta_bind_no_var, detism_failure,
purity_impure, tabling_c_attributes_dupl, MarkInactiveFailCode,
Context, MarkInactiveFailGoal),
table_call_foreign_proc(ModuleInfo, MarkActiveFailPredName,
[TableTipArg], [], instmap_delta_bind_no_var, detism_failure,
purity_impure, tabling_c_attributes_dupl, MarkActiveFailCode,
Context, MarkActiveFailGoal),
% The actual determinism of a predicate can be tighter than its
% declared determinism. This can lead to problems, such as mantis bug 361.
%
% When we add code to the end of the procedure body to mark that the
% current subgoal is now inactive, we keep the original procedure body's
% determinism. This will cause a code generator abort (unexpected
% determinism) if the "mark inactive" goal has a looser determinism
% than the original body. This can happen by adding model_non "mark
% inactive" code to the end of a model_det or model_semi goal,
% or by adding model_semi "mark inactive" code to the end of a
% model_det goal.
%
% We could fix this in one of two ways.
%
% - We can set the determinism of the compound goal that includes
% both the original body and the "mark inactive" goal based on the
% code model of the "mark inactive goal, which should always be
% the looser code model.
%
% - We can choose which transformation to apply based on the actual
% determinism of the procedure body, not its declared determinism.
%
% We implement the second approach.
Detism = goal_info_get_determinism(OrigGoalInfo),
determinism_to_code_model(Detism, CodeModel),
set_of_var.list_to_set([TableTipVar | HeadVars], InactiveNonLocals),
OutputVars = list.map(project_var, NumberedOutputVars),
InactiveInstmapDelta = instmap_delta_bind_vars(OutputVars),
(
CodeModel = model_det,
InactiveGoalExpr = conj(plain_conj, [OrigGoal, MarkInactiveGoal])
;
CodeModel = model_semi,
InstMapDelta = goal_info_get_instmap_delta(OrigGoalInfo),
create_renaming(OutputVars, InstMapDelta, !VarTable,
Unifies, NewVars, Renaming),
rename_some_vars_in_goal(Renaming, OrigGoal, RenamedOrigGoal),
ThenGoalExpr = conj(plain_conj, Unifies ++ [MarkInactiveGoal]),
ThenVars = [TableTipVar | OutputVars] ++ NewVars,
set_of_var.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_of_var.make_singleton(TableTipVar),
AfterInstMapDelta, detism_multi, purity_impure, Context,
AfterGoalInfo),
AfterGoal = hlds_goal(AfterGoalExpr, AfterGoalInfo),
FirstGoalExpr = conj(plain_conj, [OrigGoal, AfterGoal]),
OrigGINonLocals = goal_info_get_nonlocals(OrigGoalInfo),
set_of_var.insert(TableTipVar, OrigGINonLocals, 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),
SwitchArms = [
case(loop_active_cons_id, [], ActiveGoal),
case(loop_inactive_cons_id, [], InactiveGoal)
],
SwitchExpr = switch(StatusVar, cannot_fail, SwitchArms),
set_of_var.insert_list([StatusVar, TableTipVar],
InactiveNonLocals, 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,
var_table::in, var_table::out, table_info::in, table_info::out,
prog_var::out, hlds_goal::out, list(table_step_desc)::out) is det.
create_new_memo_goal(Detism, OrigGoal, Statistics, _MaybeSizeLimit,
PredId, ProcId, HeadVars, NumberedInputVars, NumberedOutputVars,
!VarTable, !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_of_var.list_to_set(HeadVars, OrigNonLocals),
OrigGoal = hlds_goal(_, OrigGoalInfo),
OrigInstMapDelta = goal_info_get_instmap_delta(OrigGoalInfo),
Context = goal_info_get_context(OrigGoalInfo),
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($pred, "model_non")
),
generate_plain_call_table_lookup_goal(StatusType,
SetupPredName, SetupMacroName, NumberedInputVars,
PredId, ProcId, Statistics, Context, !VarTable,
!TableInfo, TableTipVar, StatusVar, LookUpGoal, Steps),
generate_error_goal(!.TableInfo, Context, infinite_recursion_msg,
!VarTable, ActiveGoal),
list.length(NumberedOutputVars, BlockSize),
generate_memo_save_goal(NumberedOutputVars, TableTipVar, BlockSize,
Context, !VarTable, !TableInfo, SaveAnswerGoals),
generate_memo_restore_goal(NumberedOutputVars, OrigInstMapDelta,
TableTipVar, Context, !VarTable, !.TableInfo, RestoreAnswerGoal),
SucceededGoal = RestoreAnswerGoal,
set_of_var.list_to_set([TableTipVar | HeadVars], InactiveNonLocals),
OutputVars = list.map(project_var, NumberedOutputVars),
InactiveInstmapDelta = instmap_delta_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),
SwitchArms = [
case(memo_det_active_cons_id, [], ActiveGoal),
case(memo_det_inactive_cons_id, [], InactiveGoal),
case(memo_det_succeeded_cons_id, [], SucceededGoal)
]
;
CodeModel = model_semi,
create_renaming(OutputVars, OrigInstMapDelta, !VarTable,
Unifies, NewVars, Renaming),
rename_some_vars_in_goal(Renaming, OrigGoal, RenamedOrigGoal),
ThenGoalExpr = conj(plain_conj, Unifies ++ SaveAnswerGoals),
ThenVars = [TableTipVar | OutputVars] ++ NewVars,
set_of_var.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(foreign_arg_name_mode(cur_table_node_name, in_mode)),
trie_node_type, bp_native_if_possible),
DebugArgStr = get_debug_arg_string(!.TableInfo),
MarkAsFailedCode = MarkAsFailedMacroName ++
"(" ++ DebugArgStr ++ ", " ++ cur_table_node_name ++ ");",
table_call_foreign_proc(ModuleInfo, MarkAsFailedPredName,
[TableTipArg], [], instmap_delta_bind_no_var, detism_failure,
purity_impure, tabling_c_attributes_dupl, MarkAsFailedCode,
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,
SwitchArms = [
case(memo_semi_active_cons_id, [], ActiveGoal),
case(memo_semi_inactive_cons_id, [], InactiveGoal),
case(memo_semi_succeeded_cons_id, [], SucceededGoal),
case(memo_semi_failed_cons_id, [], FailedGoal)
]
),
SwitchExpr = switch(StatusVar, cannot_fail, SwitchArms),
set_of_var.insert(StatusVar, InactiveNonLocals, 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,
var_table::in, var_table::out, table_info::in, table_info::out,
prog_var::out, hlds_goal::out,
list(table_step_desc)::out, list(table_step_desc)::out) is det.
create_new_memo_non_goal(Detism, OrigGoal, Statistics, _MaybeSizeLimit,
PredId, ProcId, HeadVars, NumberedInputVars, NumberedOutputVars,
!VarTable, !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_of_var.list_to_set(HeadVars, OrigNonLocals),
OrigGoal = hlds_goal(_, OrigGoalInfo),
OrigInstMapDelta = goal_info_get_instmap_delta(OrigGoalInfo),
Context = goal_info_get_context(OrigGoalInfo),
ModuleInfo = !.TableInfo ^ table_module_info,
list.length(NumberedOutputVars, BlockSize),
generate_error_goal(!.TableInfo, Context, infinite_recursion_msg,
!VarTable, InfiniteRecursionGoal),
generate_error_goal(!.TableInfo, Context, need_minimal_model_msg,
!VarTable, NeedMinModelGoal),
generate_memo_non_call_table_lookup_goal(NumberedInputVars,
PredId, ProcId, Statistics, Context, !VarTable,
!TableInfo, RecordVar, StatusVar, LookUpGoal, InputSteps),
generate_memo_non_save_goals(NumberedOutputVars, PredId, ProcId,
RecordVar, BlockSize, Statistics, Context, !VarTable,
!TableInfo, OutputSteps, SaveAnswerGoals),
generate_memo_non_restore_goal(Detism, NumberedOutputVars,
OrigInstMapDelta, RecordVar, Context,
!VarTable, !.TableInfo, RestoreAllAnswerGoal),
RecordVarName = memo_non_record_name,
RecordArg = foreign_arg(RecordVar,
yes(foreign_arg_name_mode(RecordVarName, in_mode)),
memo_non_record_type, bp_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_call_foreign_proc(ModuleInfo, MarkIncompletePredName,
[RecordArg], [], instmap_delta_bind_no_var, detism_det,
purity_impure, tabling_c_attributes_dupl, MarkIncompleteCode,
Context, MarkIncompleteGoal),
table_call_foreign_proc(ModuleInfo, MarkActivePredName,
[RecordArg], [], instmap_delta_bind_no_var, detism_failure,
purity_impure, tabling_c_attributes_dupl, MarkActiveCode,
Context, MarkActiveGoal),
table_call_foreign_proc(ModuleInfo, MarkCompletePredName,
[RecordArg], [], instmap_delta_bind_no_var, detism_failure,
purity_impure, tabling_c_attributes_dupl, MarkCompleteCode,
Context, MarkCompleteGoal),
OrigSaveExpr = conj(plain_conj, [OrigGoal | SaveAnswerGoals]),
set_of_var.insert(RecordVar, OrigNonLocals, 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_of_var.make_singleton(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_of_var.list_to_set([RecordVar | HeadVars], InactiveNonLocals),
OutputVars = list.map(project_var, NumberedOutputVars),
InactiveInstmapDelta = instmap_delta_bind_vars(OutputVars),
SwitchArms = [
case(memo_non_active_cons_id, [], InfiniteRecursionGoal),
case(memo_non_inactive_cons_id, [], InactiveGoal),
case(memo_non_incomplete_cons_id, [], NeedMinModelGoal),
case(memo_non_complete_cons_id, [], RestoreAllAnswerGoal)
],
SwitchExpr = switch(StatusVar, cannot_fail, SwitchArms),
set_of_var.insert(StatusVar, InactiveNonLocals, 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_entry_kind::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,
var_table::in, var_table::out, table_info::in, table_info::out,
hlds_goal::out, maybe(proc_table_io_info)::out) is det.
create_new_io_goal(OrigGoal, TableIoEntryKind, Unitize, TableIoStates,
PredId, ProcId, HeadVarModes, OrigInputVars, OrigOutputVars,
!VarTable, !TableInfo, Goal, MaybeProcTableIOInfo) :-
OrigGoal = hlds_goal(_, OrigGoalInfo),
ModuleInfo0 = !.TableInfo ^ table_module_info,
module_info_pred_info(ModuleInfo0, PredId, PredInfo),
pred_info_get_markers(PredInfo, Markers),
( if marker_is_present(Markers, marker_user_marked_no_inline) then
% 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(PredId, PredInfo, ProcId, CallExpr,
ModuleInfo0, ModuleInfo),
NewGoal = hlds_goal(CallExpr, OrigGoalInfo),
!TableInfo ^ table_module_info := ModuleInfo
else
NewGoal = OrigGoal,
ModuleInfo = ModuleInfo0
),
OrigNonLocals = goal_info_get_nonlocals(OrigGoalInfo),
Context = goal_info_get_context(OrigGoalInfo),
(
TableIoStates = yes,
IoStateAssignToVars = [],
IoStateAssignFromVars = [],
SavedOutputVars = OrigOutputVars,
SavedHeadVars = HeadVarModes
;
TableIoStates = no,
list.filter(var_mode_pos_is_io_state(!.VarTable),
OrigOutputVars, IoStateAssignToVars, MisNumberedSavedOutputVars),
reallocate_slot_numbers(MisNumberedSavedOutputVars, 0,
SavedOutputVars),
list.filter(var_mode_pos_is_io_state(!.VarTable),
OrigInputVars, IoStateAssignFromVars, _MisNumberedSavedInputVars),
list.filter(var_mode_is_io_state(!.VarTable),
HeadVarModes, _, SavedHeadVars)
),
generate_new_table_var("TableVar", trie_node_type, is_not_dummy_type,
!VarTable, TableVar),
generate_new_table_var("CounterVar", int_type, is_not_dummy_type,
!VarTable, CounterVar),
generate_new_table_var("StartVar", int_type, is_not_dummy_type,
!VarTable, StartVar),
table_plain_call(ModuleInfo, "table_io_in_range",
[TableVar, CounterVar, StartVar],
instmap_delta_bind_vars([TableVar, CounterVar, StartVar]),
detism_semi, purity_impure, Context, InRangeGoal),
generate_new_table_var("TipVar", trie_node_type, is_not_dummy_type,
!VarTable, TipVar),
table_plain_call(ModuleInfo, "table_lookup_insert_start_int",
[TableVar, StartVar, CounterVar, TipVar],
instmap_delta_bind_var(TipVar),
detism_det, purity_impure, Context, LookupGoal),
table_plain_call(ModuleInfo, "table_io_has_occurred",
[TipVar], instmap_delta_bind_no_var,
detism_semi, purity_impure, Context, OccurredGoal),
(
TableIoEntryKind = entry_stores_procid_inputs_outputs,
ShroudedPredProcId = shroud_pred_proc_id(proc(PredId, ProcId)),
TableIoEntryConsId = table_io_entry_desc(ShroudedPredProcId),
make_const_construction_alloc(TableIoEntryConsId, c_pointer_type,
is_not_dummy_type, "TableIoEntryDescPtr", TableIoEntryDescGoal,
TableIoEntryDescPtrVar, !VarTable),
allocate_plain_slot_numbers(SavedHeadVars, 1, NumberedSavedHeadVars),
NumberedSaveVars = [
var_mode_pos_method(TableIoEntryDescPtrVar, 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,
proc_info_get_rtti_varmaps(ProcInfo0, RttiVarMaps),
continuation_info.generate_table_arg_type_info(!.VarTable, RttiVarMaps,
list.map(project_var_pos, NumberedSavedHeadVars),
TableArgTypeInfo),
ProcTableIOInfo = proc_table_io_info(yes(TableArgTypeInfo)),
MaybeProcTableIOInfo = yes(ProcTableIOInfo)
;
TableIoEntryKind = entry_stores_procid_outputs,
ShroudedPredProcId = shroud_pred_proc_id(proc(PredId, ProcId)),
TableIoEntryConsId = table_io_entry_desc(ShroudedPredProcId),
make_const_construction_alloc(TableIoEntryConsId, c_pointer_type,
is_not_dummy_type, "TableIoEntryDescPtr", TableIoEntryDescGoal,
TableIoEntryDescPtrVar, !VarTable),
NumberedRestoreVars0 =
list.map(project_out_arg_method, SavedOutputVars),
reallocate_slot_numbers(NumberedRestoreVars0, 1,
NumberedRestoreVars),
NumberedSaveVars = [
var_mode_pos_method(TableIoEntryDescPtrVar, in_mode, 0, unit)
| NumberedRestoreVars],
ProcTableIOInfo = proc_table_io_info(no),
MaybeProcTableIOInfo = yes(ProcTableIOInfo)
;
TableIoEntryKind = entry_stores_outputs,
TableIoEntryDescGoal = true_goal,
NumberedRestoreVars =
list.map(project_out_arg_method, SavedOutputVars),
NumberedSaveVars = NumberedRestoreVars,
MaybeProcTableIOInfo = no
),
list.length(NumberedSaveVars, BlockSize),
OrigInstMapDelta = goal_info_get_instmap_delta(OrigGoalInfo),
generate_memo_restore_goal(NumberedRestoreVars, OrigInstMapDelta, TipVar,
Context, !VarTable, !.TableInfo, RestoreAnswerGoal0),
(
TableIoStates = yes,
RestoreAnswerGoal = RestoreAnswerGoal0
;
TableIoStates = no,
( if
IoStateAssignFromVars = [IoStateAssignFromVarPrime],
IoStateAssignToVars = [IoStateAssignToVarPrime]
then
IoStateAssignFromVar = project_var(IoStateAssignFromVarPrime),
IoStateAssignToVar = project_var(IoStateAssignToVarPrime)
else
% The call to proc_info_has_io_state_pair in
% table_gen_process_procs should ensure that we never get here.
unexpected($pred, "one in / one out violation")
),
table_plain_call(ModuleInfo, "table_io_copy_io_state",
[IoStateAssignFromVar, IoStateAssignToVar],
instmap_delta_from_assoc_list([
IoStateAssignFromVar - ground(clobbered, none_or_default_func),
IoStateAssignToVar - ground(unique, none_or_default_func)]),
detism_det, purity_pure, Context, IoStateAssignGoal),
RestoreAnswerGoalExpr = conj(plain_conj,
[RestoreAnswerGoal0, IoStateAssignGoal]),
create_instmap_delta([RestoreAnswerGoal0, IoStateAssignGoal],
RestoreAnswerInstMapDelta0),
RestoreAnswerGoal0 = hlds_goal(_, RestoreAnswerGoal0Info),
RestoreAnswer0NonLocals =
goal_info_get_nonlocals(RestoreAnswerGoal0Info),
set_of_var.insert_list([IoStateAssignFromVar, IoStateAssignToVar],
RestoreAnswer0NonLocals, 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, !VarTable, !TableInfo, SaveAnswerGoals),
(
Unitize = table_io_alone,
CallSaveAnswerGoalList =
[NewGoal, TableIoEntryDescGoal | SaveAnswerGoals]
;
Unitize = table_io_unitize,
generate_new_table_var("SavedTraceEnabled", int_type,
is_not_dummy_type, !VarTable, SavedTraceEnabledVar),
table_plain_call(ModuleInfo, "table_io_left_bracket_unitized_goal",
[SavedTraceEnabledVar],
instmap_delta_bind_var(SavedTraceEnabledVar),
detism_det, purity_impure, Context, LeftBracketGoal),
table_plain_call(ModuleInfo, "table_io_right_bracket_unitized_goal",
[SavedTraceEnabledVar], instmap_delta_bind_no_var,
detism_det, purity_impure, Context, RightBracketGoal),
CallSaveAnswerGoalList = [LeftBracketGoal, NewGoal,
RightBracketGoal, TableIoEntryDescGoal | SaveAnswerGoals]
),
CallSaveAnswerGoalExpr = conj(plain_conj, CallSaveAnswerGoalList),
create_instmap_delta(CallSaveAnswerGoalList, CallSaveAnswerInstMapDelta0),
set_of_var.insert(TipVar, OrigNonLocals, 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_of_var.insert(TipVar, OrigNonLocals, 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_of_var.insert_list([TableVar, CounterVar, StartVar],
OrigNonLocals, 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,
var_table::in, var_table::out, table_info::in, table_info::out,
prog_var::out, hlds_goal::out,
list(table_step_desc)::out, list(table_step_desc)::out) is det.
create_new_mm_goal(Detism, OrigGoal, Statistics, PredId, ProcId,
HeadVars, NumberedInputVars, NumberedOutputVars, !VarTable,
!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_of_var.list_to_set(HeadVars, OrigNonLocals),
OrigGoal = hlds_goal(_, OrigGoalInfo),
OrigInstMapDelta = goal_info_get_instmap_delta(OrigGoalInfo),
Context = goal_info_get_context(OrigGoalInfo),
ModuleInfo = !.TableInfo ^ table_module_info,
list.length(NumberedOutputVars, BlockSize),
generate_mm_call_table_lookup_goal(NumberedInputVars, PredId, ProcId,
Statistics, Context, !VarTable, !TableInfo,
SubgoalVar, StatusVar, LookUpGoal, InputSteps),
generate_mm_save_goals(NumberedOutputVars, SubgoalVar, PredId, ProcId,
BlockSize, Statistics, Context, !VarTable, !TableInfo,
OutputSteps, SaveAnswerGoals),
generate_mm_restore_goal(Detism, NumberedOutputVars, OrigInstMapDelta,
SubgoalVar, Context, !VarTable, !.TableInfo,
RestoreAllAnswerGoal),
generate_mm_suspend_goal(NumberedOutputVars, OrigInstMapDelta,
SubgoalVar, Context, !VarTable, !.TableInfo, SuspendGoal),
MainExpr = conj(plain_conj, [OrigGoal | SaveAnswerGoals]),
set_of_var.insert_list([SubgoalVar, StatusVar],
OrigNonLocals, 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_plain_call(ModuleInfo, "table_mm_completion",
[SubgoalVar], instmap_delta_bind_no_var,
detism_det, purity_impure, Context, ResumeGoal0),
append_fail(ResumeGoal0, ResumeGoal),
InactiveExpr = disj([MainGoal, ResumeGoal]),
InactiveGoal = hlds_goal(InactiveExpr, MainGoalInfo),
SwitchArms = [
case(mm_inactive_cons_id, [], InactiveGoal),
case(mm_active_cons_id, [], SuspendGoal),
case(mm_complete_cons_id, [], RestoreAllAnswerGoal)
],
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),
% ( if CT1 = NULL then
% table_mmos_save_inputs(0, A), % into global var MR_mmos_input_arg
% Generator = table_mmos_setup_generator(CT1, 1, p2_gen, "p"),
% then
% 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,
var_table::in, var_table::out, table_info::in, table_info::out,
generator_map::in, generator_map::out, hlds_goal::out,
list(table_step_desc)::out, list(table_step_desc)::out) is det.
do_own_stack_transform(Detism, OrigGoal, Statistics, PredId, ProcId,
PredInfo0, ProcInfo0, HeadVars, NumberedInputVars, NumberedOutputVars,
!VarTable, !TableInfo, !GenMap, Goal, InputSteps, OutputSteps) :-
PredName = pred_info_name(PredInfo0),
( if map.search(!.GenMap, PredId, GeneratorPredIdPrime) then
GeneratorPredId = GeneratorPredIdPrime
else
clone_pred_info(PredId, ProcId, PredInfo0, HeadVars,
NumberedOutputVars, GeneratorPredId, !TableInfo),
map.det_insert(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_of_var.list_to_set(HeadVars, OrigNonLocals),
OrigGoal = hlds_goal(_, OrigGoalInfo),
OrigInstMapDelta = goal_info_get_instmap_delta(OrigGoalInfo),
Context = goal_info_get_context(OrigGoalInfo),
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,
is_not_dummy_type, !VarTable, GeneratorPredVar),
generate_new_table_var("Consumer", consumer_type, is_not_dummy_type,
!VarTable, ConsumerVar),
ShroudedPredProcId = shroud_pred_proc_id(proc(GeneratorPredId, ProcId)),
GeneratorConsId = closure_cons(ShroudedPredProcId),
make_const_construction(Context, GeneratorPredVar, GeneratorConsId,
MakeGeneratorVarGoal),
generate_call_table_lookup_goals(NumberedInputVars,
GeneratorPredId, ProcId, Statistics, Context, !VarTable, !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(foreign_arg_name_mode(generator_pred_name, in_mode)),
GeneratorPredType, bp_native_if_possible),
ConsumerArg = foreign_arg(ConsumerVar,
yes(foreign_arg_name_mode(ConsumerVarName, out_mode)),
consumer_type, bp_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_call_foreign_proc(ModuleInfo, SetupPredName,
[InfoArg, GeneratorPredArg, ConsumerArg], LookupForeignArgs,
instmap_delta_bind_var(ConsumerVar),
detism_det, purity_impure, make_generator_c_attributes,
LookupDeclCodeStr ++ SetupCode, 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, is_not_dummy_type,
!VarTable, AnswerBlockVar),
( if Detism = detism_multi then
ConsumePredName = "table_mmos_consume_next_answer_multi"
else if Detism = detism_non then
ConsumePredName = "table_mmos_consume_next_answer_nondet"
else
unexpected($pred, "invalid determinism")
),
% XXX consider inlining the predicate being called
table_plain_call(ModuleInfo, ConsumePredName,
[ConsumerVar, AnswerBlockVar], instmap_delta_bind_var(AnswerBlockVar),
Detism, purity_impure, Context, GetNextAnswerGoal),
DebugArgStr = get_debug_arg_string(!.TableInfo),
generate_restore_goals(ModuleInfo, OrigInstMapDelta, DebugArgStr,
NumberedOutputVars, RestoreInstMapDeltaSrc,
RestoreArgs, RestoreCodeStr, !VarTable),
AnswerBlockArg = foreign_arg(AnswerBlockVar,
yes(foreign_arg_name_mode(answer_block_name, in_mode)),
answer_block_type, bp_native_if_possible),
RestoreAllPredName = "table_mmos_restore_answers",
table_call_foreign_proc(ModuleInfo, RestoreAllPredName,
[AnswerBlockArg], RestoreArgs,
instmap_delta_from_assoc_list(RestoreInstMapDeltaSrc),
detism_det, purity_semipure, tabling_c_attributes_no_dupl,
RestoreCodeStr, 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, !.VarTable,
GeneratorTableInfo0, GeneratorTableInfo, InputSteps, OutputSteps),
!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(foreign_arg_name_mode(InputVarName, _InMode))
;
MaybeArgNameMode = no,
unexpected($pred, "no InputVarName")
),
mode_get_insts(ModuleInfo, Mode, InitInst, _FinalInst),
PickupMode = from_to_mode(free, InitInst),
PickupArg = foreign_arg(InputVar,
yes(foreign_arg_name_mode(InputVarName, PickupMode)),
Type, bp_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_of_progvar::in, instmap_delta::in,
var_table::in, table_info::in, table_info::out,
list(table_step_desc)::in, list(table_step_desc)::out) is det.
do_own_stack_create_generator(PredId, ProcId, !.PredInfo, !.ProcInfo,
Statistics, Context, GeneratorVar, DebugArgStr, PickupVarCode,
PickupForeignArgs, NumberedInputVars, NumberedOutputVars,
OrigNonLocals, OrigInstMapDelta, !.VarTable, !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 = [GeneratorVar - ground_inst
| PickupInstMapDeltaSrc0],
PickupGeneratorCode = "\t\t" ++ generator_name ++
" = MR_mmos_new_generator;\n",
PickupGeneratorArg = foreign_arg(GeneratorVar,
yes(foreign_arg_name_mode(generator_name, out_mode)),
generator_type, bp_native_if_possible),
table_call_foreign_proc(ModuleInfo0, "table_mmos_pickup_inputs",
[PickupGeneratorArg], PickupForeignArgs,
instmap_delta_from_assoc_list(PickupInstMapDeltaSrc),
detism_det, purity_impure, tabling_c_attributes_no_dupl,
PickupGeneratorCode ++ PickupVarCode, Context, PickupGoal),
list.length(NumberedOutputVars, BlockSize),
generate_own_stack_save_return_goal(NumberedOutputVars, GeneratorVar,
PredId, ProcId, BlockSize, Statistics, Context, !VarTable,
!TableInfo, OutputSteps, SaveReturnAnswerGoals),
proc_info_get_goal(!.ProcInfo, OrigGoal),
OrigGoal = hlds_goal(_, OrigGoalInfo),
MainGoalExpr = conj(plain_conj, [OrigGoal | SaveReturnAnswerGoals]),
Detism = goal_info_get_determinism(OrigGoalInfo),
set_of_var.insert(GeneratorVar, OrigNonLocals, 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(foreign_arg_name_mode(generator_name, in_mode)),
generator_type, bp_native_if_possible),
table_call_foreign_proc(ModuleInfo0, "table_mmos_completion",
[CompletionArg], [], instmap_delta_bind_no_var,
detism_failure, purity_impure, tabling_c_attributes_no_dupl,
CompletionCode, 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_var_table(!.VarTable, !ProcInfo),
GenTabledMethod = tabled_minimal(own_stacks_generator),
InputVarModeMethods = list.map(project_out_pos, NumberedInputVars),
OutputVarModeMethods = list.map(project_out_pos, NumberedOutputVars),
generate_gen_proc_table_info(!.TableInfo, PredId, ProcId, !.VarTable,
GenTabledMethod, InputSteps, yes(OutputSteps),
InputVarModeMethods, OutputVarModeMethods, ProcTableStructInfo),
PredProcId = proc(PredId, ProcId),
add_proc_table_struct(PredProcId, ProcTableStructInfo, !.ProcInfo,
ModuleInfo0, ModuleInfo1),
SpecialReturn = generator_return(returning_generator_locn, DebugArgStr),
proc_info_set_maybe_special_return(yes(SpecialReturn), !ProcInfo),
proc_info_set_eval_method(eval_tabled(GenTabledMethod), !ProcInfo),
pred_info_get_proc_table(!.PredInfo, ProcTable0),
map.det_insert(ProcId, !.ProcInfo, ProcTable0, ProcTable),
pred_info_set_proc_table(ProcTable, !PredInfo),
module_info_set_pred_info(PredId, !.PredInfo, ModuleInfo1, ModuleInfo),
!TableInfo ^ table_module_info := ModuleInfo.
:- pred clone_pred_info(pred_id::in, proc_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, OrigProcId, 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),
PredOrFunc = pred_info_is_pred_or_func(PredInfo0),
PredName = pred_info_name(PredInfo0),
pred_info_get_context(PredInfo0, Context),
% The generator is local even if the original predicate is exported.
PredStatus = pred_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_proof_map(PredInfo0, ClassProofMap),
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),
Transform = tn_minimal_model_generator(PredOrFunc,
proc_id_to_int(OrigProcId)),
make_transformed_pred_name(PredName, Transform, GenPredName),
assoc_list.from_corresponding_lists(HeadVars, ArgTypes0, HeadVarTypes),
keep_only_output_arg_types(HeadVarTypes, NumberedOutputVars, ArgTypes),
PredFormArity = arg_list_arity(ArgTypes),
markers_to_marker_list(Markers0, MarkerList0),
list.filter(filter_marker, MarkerList0, MarkerList),
marker_list_to_markers(MarkerList, Markers),
Origin = origin_pred_transform(pred_transform_table_generator,
OrigOrigin, OrigPredId),
CurUserDecl = maybe.no,
pred_info_init(PredOrFunc, ModuleName, GenPredName, PredFormArity, Context,
Origin, PredStatus, CurUserDecl, GoalType, Markers,
ArgTypes, TypeVarSet, ExistQVars, ClassContext, ClassProofMap,
ClassConstraintMap, ClausesInfo, VarNameRemap, GenPredInfo),
ModuleInfo0 = !.TableInfo ^ table_module_info,
module_info_get_predicate_table(ModuleInfo0, PredTable0),
predicate_table_insert(GenPredInfo, GeneratorPredId,
PredTable0, PredTable),
module_info_set_predicate_table(PredTable, ModuleInfo0, ModuleInfo),
!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
% which calls the new procedure with its formal arguments as the
% actual arguments.
%
:- pred clone_proc_and_create_call(pred_id::in, pred_info::in, proc_id::in,
hlds_goal_expr::out, module_info::in, module_info::out) is det.
clone_proc_and_create_call(PredId, PredInfo, ProcId, CallExpr, !ModuleInfo) :-
pred_info_proc_info(PredInfo, ProcId, ProcInfo),
proc_info_get_context(ProcInfo, ProcContext),
proc_info_get_var_table(ProcInfo, ProcVarTable),
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_has_parallel_conj(ProcInfo, HasParallelConj),
proc_info_get_var_name_remap(ProcInfo, VarNameRemap),
SeqNum = item_no_seq_num,
proc_info_create(ProcContext, SeqNum, ProcVarTable, ProcHeadVars,
ProcInstVarSet, ProcHeadModes, detism_decl_none, ProcDetism, ProcGoal,
ProcRttiVarMaps, address_is_not_taken, HasParallelConj, VarNameRemap,
NewProcInfo),
ModuleName = pred_info_module(PredInfo),
OrigPredName = pred_info_name(PredInfo),
PredOrFunc = pred_info_is_pred_or_func(PredInfo),
Transform = tn_io_tabling(PredOrFunc),
make_transformed_pred_name(OrigPredName, Transform, NewPredName),
pred_info_get_origin(PredInfo, OrigOrigin),
Origin = origin_proc_transform(proc_transform_io_tabling, OrigOrigin,
PredId, ProcId),
pred_info_get_context(PredInfo, PredContext),
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_get_goal_type(PredInfo, GoalType),
pred_info_create(PredOrFunc, ModuleName, NewPredName,
PredContext, Origin, pred_status(status_local), Markers,
PredArgTypes, PredTypeVarSet, PredExistQVars, PredClassContext,
PredAssertions, VarNameRemap, GoalType,
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,
qualified(ModuleName, 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 | VarTable], [Out | Outs], OutTypes) :-
Out = var_mode_pos_method(OutVar, _, _, _),
( if Var = OutVar then
keep_only_output_arg_types(VarTable, Outs, OutTypesTail),
OutTypes = [Type | OutTypesTail]
else
keep_only_output_arg_types(VarTable, [Out | Outs], OutTypes)
).
:- pred filter_marker(pred_marker::in) is semidet.
filter_marker(Marker) :-
keep_marker(Marker) = yes.
:- func keep_marker(pred_marker) = bool.
keep_marker(marker_stub) = no.
keep_marker(marker_builtin_stub) = no.
keep_marker(marker_infer_type) = no.
keep_marker(marker_infer_modes) = no.
keep_marker(marker_no_pred_decl) = no.
keep_marker(marker_no_detism_warning) = no.
keep_marker(marker_user_marked_inline) = no.
keep_marker(marker_heuristic_inline) = no.
keep_marker(marker_user_marked_no_inline) = no.
keep_marker(marker_mmc_marked_no_inline) = yes.
keep_marker(marker_consider_used) = no.
keep_marker(marker_req_sw_arms_type_order) = 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.
keep_marker(marker_has_require_scope) = yes.
keep_marker(marker_has_incomplete_switch) = yes.
keep_marker(marker_has_format_call) = yes.
keep_marker(marker_has_rhs_lambda) = yes.
keep_marker(marker_fact_table_semantic_errors) = no.
%---------------------------------------------------------------------------%
:- pred generate_gen_proc_table_info(table_info::in, pred_id::in, proc_id::in,
var_table::in, tabled_eval_method::in,
list(table_step_desc)::in, maybe(list(table_step_desc))::in,
list(var_mode_method)::in, list(var_mode_method)::in,
proc_table_struct_info::out) is det.
generate_gen_proc_table_info(TableInfo, PredId, ProcId, VarTable, TabledMethod,
InputSteps, MaybeOutputSteps, InputVars, OutputVars,
ProcTableStructInfo) :-
ModuleInfo = TableInfo ^ table_module_info,
PredInfo = TableInfo ^ table_cur_pred_info,
ProcInfo = TableInfo ^ table_cur_proc_info,
RTTIProcLabel = make_rtti_proc_label(ModuleInfo, PredId, ProcId),
pred_info_get_typevarset(PredInfo, TVarSet),
proc_info_get_context(ProcInfo, Context),
InOutHeadVars = InputVars ++ OutputVars,
allocate_slot_numbers(InOutHeadVars, 1, NumberedInOutHeadVars),
ArgInfos = list.map(project_var_pos, NumberedInOutHeadVars),
proc_info_get_rtti_varmaps(ProcInfo, RttiVarMaps),
continuation_info.generate_table_arg_type_info(VarTable, RttiVarMaps,
ArgInfos, TableArgTypeInfo),
NumInputs = list.length(InputVars),
NumOutputs = list.length(OutputVars),
ProcTableStructInfo = proc_table_struct_info(RTTIProcLabel, TVarSet,
Context, NumInputs, NumOutputs, InputSteps, MaybeOutputSteps,
TableArgTypeInfo, TabledMethod).
%---------------------------------------------------------------------------%
% Generate a goal for doing lookups in call tables for
% loopcheck and memo predicates.
%
:- pred generate_plain_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,
var_table::in, var_table::out, table_info::in, table_info::out,
prog_var::out, prog_var::out,
hlds_goal::out, list(table_step_desc)::out) is det.
generate_plain_call_table_lookup_goal(StatusType, PredName,
SetupMacroName, NumberedVars, PredId, ProcId, Statistics, Context,
!VarTable, !TableInfo, TableTipVar, StatusVar, Goal, Steps) :-
generate_call_table_lookup_goals(NumberedVars, PredId, ProcId,
Statistics, Context, !VarTable, !TableInfo, Steps,
TableTipVar, TableTipArg, InfoArg, LookupForeignArgs,
LookupPrefixGoals, LookupCodeStr, CallTableTipAssignStr),
generate_new_table_var("Status", StatusType, is_not_dummy_type,
!VarTable, StatusVar),
ModuleInfo = !.TableInfo ^ table_module_info,
StatusVarName = status_name,
StatusArg = foreign_arg(StatusVar,
yes(foreign_arg_name_mode(StatusVarName, out_mode)),
StatusType, bp_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_call_foreign_proc(ModuleInfo, PredName,
Args, LookupForeignArgs, instmap_delta_bind_vars(BoundVars),
detism_det, purity_impure, tabling_c_attributes_no_dupl,
CodeStr, Context, SetupGoal0),
attach_call_table_tip(SetupGoal0, SetupGoal),
LookupSetupGoals = LookupPrefixGoals ++ [SetupGoal],
GoalExpr = conj(plain_conj, LookupSetupGoals),
Vars = list.map(project_var, NumberedVars),
set_of_var.list_to_set([StatusVar, TableTipVar | Vars], NonLocals),
goal_info_init_hide(NonLocals,
instmap_delta_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,
var_table::in, var_table::out, table_info::in, table_info::out,
prog_var::out, prog_var::out,
hlds_goal::out, list(table_step_desc)::out) is det.
generate_memo_non_call_table_lookup_goal(NumberedVars, PredId, ProcId,
Statistics, Context, !VarTable, !TableInfo,
RecordVar, StatusVar, Goal, Steps) :-
generate_call_table_lookup_goals(NumberedVars, PredId, ProcId,
Statistics, Context, !VarTable, !TableInfo, Steps,
_TableTipVar, _TableTipArg, InfoArg, LookupForeignArgs,
LookupPrefixGoals, LookupCodeStr, _CallTableTipAssignStr),
ModuleInfo = !.TableInfo ^ table_module_info,
generate_new_table_var("Record", memo_non_record_type, is_not_dummy_type,
!VarTable, RecordVar),
generate_new_table_var("Status", memo_non_status_type, is_not_dummy_type,
!VarTable, 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(foreign_arg_name_mode(RecordVarName, out_mode)),
memo_non_record_type, bp_native_if_possible),
StatusArg = foreign_arg(StatusVar,
yes(foreign_arg_name_mode(StatusVarName, out_mode)),
memo_non_status_type, bp_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_call_foreign_proc(ModuleInfo, SetupPredName,
Args, LookupForeignArgs,
instmap_delta_bind_vars(BoundVars),
detism_det, purity_impure, tabling_c_attributes_no_dupl,
LookupDeclCodeStr ++ PredCodeStr, Context, SetupGoal0),
attach_call_table_tip(SetupGoal0, SetupGoal),
LookupSetupGoals = LookupPrefixGoals ++ [SetupGoal],
GoalExpr = conj(plain_conj, LookupSetupGoals),
Vars = list.map(project_var, NumberedVars),
set_of_var.list_to_set([StatusVar, RecordVar | Vars], NonLocals),
goal_info_init_hide(NonLocals,
instmap_delta_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,
var_table::in, var_table::out, table_info::in, table_info::out,
prog_var::out, prog_var::out,
hlds_goal::out, list(table_step_desc)::out) is det.
generate_mm_call_table_lookup_goal(NumberedVars, PredId, ProcId,
Statistics, Context, !VarTable, !TableInfo,
SubgoalVar, StatusVar, Goal, Steps) :-
generate_call_table_lookup_goals(NumberedVars, PredId, ProcId,
Statistics, Context, !VarTable, !TableInfo, Steps,
_TableTipVar, _TableTipArg, InfoArg, LookupForeignArgs,
LookupPrefixGoals, LookupCodeStr, _CallTableTipAssignStr),
generate_new_table_var("Subgoal", subgoal_type, is_not_dummy_type,
!VarTable, SubgoalVar),
generate_new_table_var("Status", mm_status_type, is_not_dummy_type,
!VarTable, StatusVar),
SetupPredName = "table_mm_setup",
SetupMacroName = "MR_tbl_mm_setup",
BoundVars = [SubgoalVar, StatusVar],
SubgoalVarName = subgoal_name,
StatusVarName = status_name,
SubgoalArg = foreign_arg(SubgoalVar,
yes(foreign_arg_name_mode(SubgoalVarName, out_mode)),
subgoal_type, bp_native_if_possible),
StatusArg = foreign_arg(StatusVar,
yes(foreign_arg_name_mode(StatusVarName, out_mode)),
mm_status_type, bp_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_call_foreign_proc(ModuleInfo, SetupPredName,
Args, LookupForeignArgs,
instmap_delta_bind_vars(BoundVars),
detism_det, purity_impure, tabling_c_attributes_no_dupl,
CodeStr, Context, SetupGoal0),
attach_call_table_tip(SetupGoal0, SetupGoal),
LookupSetupGoals = LookupPrefixGoals ++ [SetupGoal],
GoalExpr = conj(plain_conj, LookupSetupGoals),
Vars = list.map(project_var, NumberedVars),
set_of_var.list_to_set([StatusVar, SubgoalVar | Vars], NonLocals),
goal_info_init_hide(NonLocals,
instmap_delta_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,
var_table::in, var_table::out, table_info::in, table_info::out,
list(table_step_desc)::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, !VarTable, !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, Context,
proc_table_info_name, InfoArg, GetTableInfoGoal, !VarTable),
MaybeStatsRef = stats_ref(Statistics, call_table),
DebugArgStr = get_debug_arg_string(!.TableInfo),
BackArgStr = get_back_arg_string(!.TableInfo),
generate_table_lookup_goals(NumberedVars, MaybeStatsRef,
DebugArgStr, BackArgStr, Context, !VarTable, !TableInfo,
InputSteps, LookupArgs, LookupPrefixGoals, LookupCodeStr),
PrefixGoals = [GetTableInfoGoal] ++ LookupPrefixGoals,
% We ignore _StatsPrefixGoals and _StatsExtraArgs because we always
% include ProcTableInfoVar in the arguments.
maybe_record_overall_stats(PredId, ProcId, Context,
proc_table_info_name, cur_table_node_name, MaybeStatsRef, !VarTable,
_StatsPrefixGoals, _StatsExtraArgs, StatsCodeStr),
MainCodeStr = InfoToPtrCodeStr ++ LookupCodeStr ++ StatsCodeStr,
CallTableTipVarName = "CallTableTipVar",
generate_new_table_var(CallTableTipVarName, trie_node_type,
is_not_dummy_type, !VarTable, CallTableTipVar),
CallTableTipArg = foreign_arg(CallTableTipVar,
yes(foreign_arg_name_mode(CallTableTipVarName, out_mode)),
trie_node_type, bp_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,
var_table::in, var_table::out, table_info::in, table_info::out,
list(table_step_desc)::out, list(foreign_arg)::out,
list(hlds_goal)::out, string::out) is det.
generate_answer_table_lookup_goals(NumberedVars, PredId, ProcId, Statistics,
Context, !VarTable, !TableInfo, OutputSteps, ForeignArgs,
PrefixGoals, CodeStr) :-
MaybeStatsRef = stats_ref(Statistics, answer_table),
DebugArgStr = get_debug_arg_string(!.TableInfo),
BackArgStr = "MR_FALSE",
generate_table_lookup_goals(NumberedVars, MaybeStatsRef,
DebugArgStr, BackArgStr, Context, !VarTable, !TableInfo,
OutputSteps, LookupArgs, LookupPrefixGoals, LookupCodeStr),
maybe_record_overall_stats(PredId, ProcId, Context,
proc_table_info_name, cur_table_node_name,
MaybeStatsRef, !VarTable,
StatsPrefixGoals, StatsExtraArgs, StatsCodeStr),
CodeStr = LookupCodeStr ++ StatsCodeStr,
ForeignArgs = StatsExtraArgs ++ LookupArgs,
PrefixGoals = StatsPrefixGoals ++ LookupPrefixGoals.
:- pred maybe_record_overall_stats(pred_id::in, proc_id::in, prog_context::in,
string::in, string::in, maybe(string)::in,
var_table::in, var_table::out, list(hlds_goal)::out,
list(foreign_arg)::out, string::out) is det.
maybe_record_overall_stats(PredId, ProcId, Context, InfoVarName, TipVarName,
MaybeStatsRef, !VarTable, PrefixGoals, Args, StatsCodeStr) :-
(
MaybeStatsRef = no,
PrefixGoals = [],
Args = [],
StatsCodeStr = ""
;
MaybeStatsRef = yes(StatsRef),
generate_get_table_info_goal(PredId, ProcId, Context, InfoVarName,
Arg, Goal, !VarTable),
PrefixGoals = [Goal],
Args = [Arg],
StatsCodeStr =
"\t" ++ StatsRef ++ ".MR_ts_num_lookups++;\n" ++
"\t" ++ "if (MR_trie_node_seen_before(" ++ TipVarName ++ ")) " ++
"{\n" ++
"\t\t" ++ StatsRef ++ ".MR_ts_num_lookups_is_dupl++;\n" ++
"\t" ++ "}\n"
).
:- pred generate_get_table_info_goal(pred_id::in, proc_id::in,
prog_context::in, string::in, foreign_arg::out, hlds_goal::out,
var_table::in, var_table::out) is det.
generate_get_table_info_goal(PredId, ProcId, Context, InfoVarName,
Arg, Goal, !VarTable) :-
generate_new_table_var("ProcTableInfo", proc_table_info_type,
is_not_dummy_type, !VarTable, ProcTableInfoVar),
Arg = foreign_arg(ProcTableInfoVar,
yes(foreign_arg_name_mode(InfoVarName, in_mode)),
proc_table_info_type, bp_native_if_possible),
ShroudedPredProcId = shroud_pred_proc_id(proc(PredId, ProcId)),
InfoConsId = tabling_info_const(ShroudedPredProcId),
make_const_construction(Context, 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(Goal0, Goal) :-
Goal0 = hlds_goal(GoalExpr, GoalInfo0),
Features0 = goal_info_get_features(GoalInfo0),
set.insert(feature_call_table_gen, Features0, Features),
goal_info_set_features(Features, GoalInfo0, GoalInfo),
Goal = hlds_goal(GoalExpr, 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,
maybe(string)::in, string::in, string::in, term.context::in,
var_table::in, var_table::out, table_info::in, table_info::out,
list(table_step_desc)::out, list(foreign_arg)::out,
list(hlds_goal)::out, string::out) is det.
generate_table_lookup_goals([], _, _, _, _, !VarTable, !TableInfo,
[], [], [], "").
generate_table_lookup_goals([VarModePos | NumberedVars], MaybeStatsRef,
DebugArgStr, BackArgStr, Context, !VarTable, !TableInfo,
[StepDesc | StepDescs], ForeignArgs ++ RestForeignArgs,
PrefixGoals ++ RestPrefixGoals, CodeStr ++ RestCodeStr) :-
VarModePos = var_mode_pos_method(Var, _, VarSeqNum, ArgMethod),
ModuleInfo = !.TableInfo ^ table_module_info,
lookup_var_entry(!.VarTable, Var, VarEntry),
VarName = var_entry_name(Var, VarEntry),
VarType = VarEntry ^ vte_type,
CtorCat = classify_type(ModuleInfo, VarType),
(
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, CtorCat, VarType, Var,
VarSeqNum, MaybeStatsRef, DebugArgStr, BackArgStr, Context,
!VarTable, !TableInfo, Step, ForeignArgs, PrefixGoals, CodeStr)
),
StepDesc = table_step_desc(VarName, Step),
generate_table_lookup_goals(NumberedVars, MaybeStatsRef,
DebugArgStr, BackArgStr, Context, !VarTable, !TableInfo,
StepDescs, RestForeignArgs, RestPrefixGoals, RestCodeStr).
:- pred gen_lookup_call_for_type(arg_tabling_method::in,
type_ctor_category::in, mer_type::in, prog_var::in, int::in,
maybe(string)::in, string::in, string::in, term.context::in,
var_table::in, var_table::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(ArgTablingMethod0, CtorCat, Type, ArgVar, VarSeqNum,
MaybeStatsRef, DebugArgStr, BackArgStr, Context, !VarTable,
!TableInfo, Step, ExtraArgs, PrefixGoals, CodeStr) :-
ModuleInfo = !.TableInfo ^ table_module_info,
ArgName = arg_name(VarSeqNum),
ForeignArg = foreign_arg(ArgVar,
yes(foreign_arg_name_mode(ArgName, in_mode)),
Type, bp_native_if_possible),
(
( CtorCat = ctor_cat_enum(_)
; CtorCat = ctor_cat_builtin(cat_builtin_int(_))
; CtorCat = ctor_cat_builtin(cat_builtin_char)
; CtorCat = ctor_cat_void
),
% Values in these type categories don't have an address.
( if ArgTablingMethod0 = arg_addr then
ArgTablingMethod = arg_value
else
ArgTablingMethod = ArgTablingMethod0
)
;
( CtorCat = ctor_cat_builtin(cat_builtin_string)
; CtorCat = ctor_cat_builtin(cat_builtin_float)
; CtorCat = ctor_cat_system(_)
; CtorCat = ctor_cat_higher_order
; CtorCat = ctor_cat_tuple
; CtorCat = ctor_cat_variable
; CtorCat = ctor_cat_user(_)
; CtorCat = ctor_cat_builtin_dummy
),
ArgTablingMethod = ArgTablingMethod0
),
MaybeStepStatsArgStr = maybe_step_stats_arg_addr(MaybeStatsRef, VarSeqNum),
(
ArgTablingMethod = arg_value,
(
CtorCat = ctor_cat_enum(cat_enum_mercury),
type_to_ctor_det(Type, TypeCtor),
module_info_get_type_table(ModuleInfo, TypeTable),
get_enum_max_int_tag(TypeTable, TypeCtor, MaxIntTag),
EnumRange = MaxIntTag + 1,
LookupMacroName = "MR_tbl_lookup_insert_enum",
Step = table_trie_step_enum(EnumRange),
PrefixGoals = [],
ExtraArgs = [ForeignArg],
LookupCodeStr = "\t" ++ LookupMacroName ++ "(" ++
MaybeStepStatsArgStr ++ ", " ++ DebugArgStr ++ ", " ++
BackArgStr ++ ", " ++ cur_table_node_name ++ ", " ++
int_to_string(EnumRange) ++ ", " ++ ArgName ++ ", " ++
next_table_node_name ++ ");\n"
;
% Mercury doesn't know the specific values of the foreign
% enums, so we cannot use an array as a trie (since we don't
% know how big the array would have to be). However, hashing
% the foreign enum will work.
%
% XXX The code of this case is the same as the code of the case
% shared by the builtin types below. The only reason why it is
% here is that switch detection cannot yet look three levels deep.
CtorCat = ctor_cat_enum(cat_enum_foreign),
CatString = "foreign_enum",
Step = table_trie_step_foreign_enum,
LookupMacroName = "MR_tbl_lookup_insert_" ++ CatString,
PrefixGoals = [],
ExtraArgs = [ForeignArg],
LookupCodeStr = "\t" ++ LookupMacroName ++ "(" ++
MaybeStepStatsArgStr ++ ", " ++ DebugArgStr ++ ", " ++
BackArgStr ++ ", " ++ cur_table_node_name ++ ", " ++
ArgName ++ ", " ++ next_table_node_name ++ ");\n"
;
% XXX The code of this case is the same as the code of the case
% shared by the builtin types below. The only reason why it is
% here is that switch detection cannot yet look three levels deep.
( CtorCat = ctor_cat_system(cat_system_type_info)
; CtorCat = ctor_cat_system(cat_system_type_ctor_info)
),
CatString = "typeinfo",
Step = table_trie_step_typeinfo,
LookupMacroName = "MR_tbl_lookup_insert_" ++ CatString,
PrefixGoals = [],
ExtraArgs = [ForeignArg],
LookupCodeStr = "\t" ++ LookupMacroName ++ "(" ++
MaybeStepStatsArgStr ++ ", " ++ DebugArgStr ++ ", " ++
BackArgStr ++ ", " ++ cur_table_node_name ++ ", " ++
ArgName ++ ", " ++ next_table_node_name ++ ");\n"
;
(
CtorCat = ctor_cat_builtin(cat_builtin_int(IntType)),
int_type_module_name(IntType, CatString),
Step = table_trie_step_int(IntType)
;
CtorCat = ctor_cat_builtin(cat_builtin_char),
CatString = "char",
Step = table_trie_step_char
;
CtorCat = ctor_cat_builtin(cat_builtin_string),
CatString = "string",
Step = table_trie_step_string
;
CtorCat = ctor_cat_builtin(cat_builtin_float),
CatString = "float",
Step = table_trie_step_float
),
LookupMacroName = "MR_tbl_lookup_insert_" ++ CatString,
PrefixGoals = [],
ExtraArgs = [ForeignArg],
LookupCodeStr = "\t" ++ LookupMacroName ++ "(" ++
MaybeStepStatsArgStr ++ ", " ++ DebugArgStr ++ ", " ++
BackArgStr ++ ", " ++ cur_table_node_name ++ ", " ++
ArgName ++ ", " ++ next_table_node_name ++ ");\n"
;
( CtorCat = ctor_cat_higher_order
; CtorCat = ctor_cat_tuple
; CtorCat = ctor_cat_variable
; CtorCat = ctor_cat_user(_)
),
MaybeAddrString = "",
IsAddr = table_value,
gen_general_lookup_call(IsAddr, MaybeAddrString, Type, ForeignArg,
ArgName, VarSeqNum, MaybeStatsRef, DebugArgStr, BackArgStr,
Context, !VarTable, !TableInfo, Step, ExtraArgs,
PrefixGoals, LookupCodeStr)
;
CtorCat = ctor_cat_builtin_dummy,
Step = table_trie_step_dummy,
PrefixGoals = [],
ExtraArgs = [],
LookupCodeStr = "\t" ++ next_table_node_name ++ " = " ++
cur_table_node_name ++ ";\n"
;
CtorCat = ctor_cat_void,
unexpected($pred, "void")
;
CtorCat = ctor_cat_system(cat_system_typeclass_info),
unexpected($pred, "typeclass_info_type")
;
CtorCat = ctor_cat_system(cat_system_base_typeclass_info),
unexpected($pred, "base_typeclass_info_type")
)
;
ArgTablingMethod = arg_addr,
(
CtorCat = ctor_cat_enum(_),
unexpected($pred, "tabling enums by addr")
;
CtorCat = ctor_cat_builtin(cat_builtin_int(_)),
unexpected($pred, "tabling integer type by addr")
;
CtorCat = ctor_cat_builtin(cat_builtin_char),
unexpected($pred, "tabling chars by addr")
;
( CtorCat = ctor_cat_builtin(cat_builtin_string)
; CtorCat = ctor_cat_builtin(cat_builtin_float)
; CtorCat = ctor_cat_system(_)
; CtorCat = ctor_cat_higher_order
; CtorCat = ctor_cat_tuple
; CtorCat = ctor_cat_variable
; CtorCat = ctor_cat_user(_)
),
MaybeAddrString = "_addr",
IsAddr = table_addr,
gen_general_lookup_call(IsAddr, MaybeAddrString, Type, ForeignArg,
ArgName, VarSeqNum, MaybeStatsRef, DebugArgStr, BackArgStr,
Context, !VarTable, !TableInfo, Step, ExtraArgs,
PrefixGoals, LookupCodeStr)
;
CtorCat = ctor_cat_builtin_dummy,
unexpected($pred, "tabling dummies by addr")
;
CtorCat = ctor_cat_void,
unexpected($pred, "void")
)
;
ArgTablingMethod = arg_promise_implied,
unexpected($pred, "arg_promise_implied")
),
UpdateCurNodeCodeStr = "\t" ++ cur_table_node_name ++ " = " ++
next_table_node_name ++ ";\n",
(
MaybeStatsRef = no,
CodeStr = LookupCodeStr ++ UpdateCurNodeCodeStr
;
MaybeStatsRef = yes(StatsRef),
StepStatsArgStr = step_stats_arg_addr(StatsRef, VarSeqNum),
NextVarName = next_table_node_name,
LookupStatsCodeStr =
"\t" ++ StepStatsArgStr ++ ".MR_tss_num_lookups++;\n" ++
"\t" ++ "if (MR_trie_node_seen_before(" ++ NextVarName ++ "))" ++
"{\n" ++
"\t\t" ++ StepStatsArgStr ++ ".MR_tss_num_lookups_is_dupl++;\n" ++
"\t" ++ "}\n",
CodeStr = LookupCodeStr ++ LookupStatsCodeStr ++ UpdateCurNodeCodeStr
).
:- pred gen_general_lookup_call(table_value_or_addr::in, string::in,
mer_type::in, foreign_arg::in, string::in, int::in, maybe(string)::in,
string::in, string::in, term.context::in,
var_table::in, var_table::out, table_info::in, table_info::out,
table_trie_step::out, list(foreign_arg)::out, list(hlds_goal)::out,
string::out) is det.
gen_general_lookup_call(IsAddr, MaybeAddrString, Type, ForeignArg, ArgName,
VarSeqNum, MaybeStatsRef, DebugArgStr, BackArgStr, Context,
!VarTable, !TableInfo, Step, ExtraArgs, PrefixGoals, LookupCodeStr) :-
type_vars_in_type(Type, TypeVars),
(
TypeVars = [],
MaybePolyString = "",
IsPoly = table_is_mono
;
TypeVars = [_ | _],
MaybePolyString = "_poly",
IsPoly = table_is_poly
),
Step = table_trie_step_general(Type, IsPoly, IsAddr),
LookupMacroName = "MR_tbl_lookup_insert_gen" ++
MaybePolyString ++ MaybeAddrString,
table_gen_make_type_info_var(Type, Context, !VarTable,
!TableInfo, TypeInfoVar, PrefixGoals),
TypeInfoArgName = "input_typeinfo" ++ int_to_string(VarSeqNum),
lookup_var_type(!.VarTable, TypeInfoVar, TypeInfoType),
ForeignTypeInfoArg = foreign_arg(TypeInfoVar,
yes(foreign_arg_name_mode(TypeInfoArgName, in_mode)),
TypeInfoType, bp_native_if_possible),
ExtraArgs = [ForeignTypeInfoArg, ForeignArg],
StepStatsArgStr = maybe_step_stats_arg_addr(MaybeStatsRef, VarSeqNum),
LookupCodeStr = "\t" ++ LookupMacroName ++ "(" ++
StepStatsArgStr ++ ", " ++ DebugArgStr ++ ", " ++ BackArgStr ++ ", " ++
cur_table_node_name ++ ", " ++ TypeInfoArgName ++ ", " ++
ArgName ++ ", " ++ 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,
var_table::in, var_table::out, table_info::in, table_info::out,
list(hlds_goal)::out) is det.
generate_memo_save_goal(NumberedSaveVars, TableTipVar, BlockSize,
Context, !VarTable, !TableInfo, Goals) :-
ModuleInfo = !.TableInfo ^ table_module_info,
TipVarName = cur_table_node_name,
TableArg = foreign_arg(TableTipVar,
yes(foreign_arg_name_mode(TipVarName, in_mode)),
trie_node_type, bp_native_if_possible),
( if BlockSize > 0 then
CreatePredName = "table_memo_fill_answer_block_shortcut",
CreateMacroName = "MR_tbl_memo_create_answer_block",
generate_all_save_goals(NumberedSaveVars, TipVarName,
BlockSize, CreateMacroName, Context, !VarTable,
!TableInfo, SaveArgs, SavePrefixGoals, SaveDeclCode, SaveCode),
table_call_foreign_proc(ModuleInfo, CreatePredName,
[TableArg], SaveArgs, instmap_delta_bind_no_var,
detism_det, purity_impure, tabling_c_attributes_dupl,
SaveDeclCode ++ SaveCode, Context, SaveGoal),
Goals = SavePrefixGoals ++ [SaveGoal]
else
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_call_foreign_proc(ModuleInfo, MarkAsSucceededPredName,
[TableArg], [], instmap_delta_bind_no_var,
detism_det, purity_impure, tabling_c_attributes_dupl,
MarkAsSucceededCode, 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,
var_table::in, var_table::out, table_info::in, table_info::out,
list(table_step_desc)::out, list(hlds_goal)::out) is det.
generate_memo_non_save_goals(NumberedSaveVars, PredId, ProcId,
RecordVar, BlockSize, Statistics, Context, !VarTable,
!TableInfo, OutputSteps, Goals) :-
ModuleInfo = !.TableInfo ^ table_module_info,
RecordName = memo_non_record_name,
RecordArg = foreign_arg(RecordVar,
yes(foreign_arg_name_mode(RecordName, in_mode)),
memo_non_record_type, bp_native_if_possible),
generate_answer_table_lookup_goals(NumberedSaveVars, PredId, ProcId,
Statistics, Context, !VarTable, !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, !VarTable, !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_call_foreign_proc(ModuleInfo, DuplCheckPredName,
[RecordArg], LookupForeignArgs, instmap_delta_bind_no_var,
detism_semi, purity_impure, tabling_c_attributes_dupl,
CodeStr, 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,
var_table::in, var_table::out, table_info::in, table_info::out,
list(table_step_desc)::out, list(hlds_goal)::out) is det.
generate_mm_save_goals(NumberedSaveVars, SubgoalVar, PredId, ProcId, BlockSize,
Statistics, Context, !VarTable, !TableInfo, OutputSteps, Goals) :-
ModuleInfo = !.TableInfo ^ table_module_info,
DebugArgStr = get_debug_arg_string(!.TableInfo),
generate_answer_table_lookup_goals(NumberedSaveVars, PredId, ProcId,
Statistics, Context, !VarTable, !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, !VarTable, !TableInfo,
_SaveArgs, _SavePrefixGoals, SaveDeclCode, CreateSaveCode),
SubgoalName = subgoal_name,
Args = [foreign_arg(SubgoalVar,
yes(foreign_arg_name_mode(SubgoalName, in_mode)),
subgoal_type, bp_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_call_foreign_proc(ModuleInfo, DuplCheckPredName,
Args, LookupForeignArgs, instmap_delta_bind_no_var,
detism_semi, purity_impure, tabling_c_attributes_dupl,
CodeStr, 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,
var_table::in, var_table::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, !VarTable, !TableInfo,
SaveArgs, SavePrefixGoals, SaveDeclCodeStr, CreateSaveCodeStr) :-
DebugArgStr = get_debug_arg_string(!.TableInfo),
generate_save_goals(NumberedSaveVars, DebugArgStr, Context,
!VarTable, !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,
var_table::in, var_table::out, table_info::in, table_info::out,
list(table_step_desc)::out, list(hlds_goal)::out) is det.
generate_own_stack_save_return_goal(NumberedOutputVars, GeneratorVar,
PredId, ProcId, BlockSize, Statistics, Context, !VarTable,
!TableInfo, OutputSteps, Goals) :-
GeneratorName = generator_name,
GeneratorArg = foreign_arg(GeneratorVar,
yes(foreign_arg_name_mode(GeneratorName, in_mode)),
generator_type, bp_native_if_possible),
DebugArgStr = get_debug_arg_string(!.TableInfo),
generate_answer_table_lookup_goals(NumberedOutputVars, PredId, ProcId,
Statistics, Context, !VarTable, !TableInfo, OutputSteps,
LookupForeignArgs, LookupPrefixGoals, LookupCodeStr),
generate_save_goals(NumberedOutputVars, DebugArgStr, Context,
!VarTable, !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_call_foreign_proc(ModuleInfo, DuplCheckPredName,
Args, LookupForeignArgs, instmap_delta_bind_no_var,
detism_semi, purity_impure, tabling_c_attributes_dupl,
CodeStr, Context, DuplicateCheckSaveGoal),
Goals = LookupPrefixGoals ++ SavePrefixGoals ++
[DuplicateCheckSaveGoal].
:- pred generate_save_goals(list(var_mode_pos_method(T))::in, string::in,
term.context::in, var_table::in, var_table::out,
table_info::in, table_info::out,
list(foreign_arg)::out, list(hlds_goal)::out, string::out) is det.
generate_save_goals([], _, _, !VarTable, !TableInfo, [], [], "").
generate_save_goals([NumberedVar | NumberedRest], DebugArgStr, Context,
!VarTable, !TableInfo, Args ++ RestArgs,
PrefixGoals ++ RestPrefixGoals, CodeStr ++ RestCodeStr) :-
NumberedVar = var_mode_pos_method(Var, _Mode, Offset, _),
ModuleInfo = !.TableInfo ^ table_module_info,
lookup_var_type(!.VarTable, Var, VarType),
CtorCat = classify_type(ModuleInfo, VarType),
gen_save_call_for_type(CtorCat, VarType, Var, Offset, DebugArgStr, Context,
!VarTable, !TableInfo, Args, PrefixGoals, CodeStr),
generate_save_goals(NumberedRest, DebugArgStr, Context, !VarTable,
!TableInfo, RestArgs, RestPrefixGoals, RestCodeStr).
:- pred gen_save_call_for_type(type_ctor_category::in, mer_type::in,
prog_var::in, int::in, string::in, term.context::in,
var_table::in, var_table::out, table_info::in, table_info::out,
list(foreign_arg)::out, list(hlds_goal)::out, string::out) is det.
gen_save_call_for_type(CtorCat, Type, Var, Offset, DebugArgStr, Context,
!VarTable, !TableInfo, Args, PrefixGoals, CodeStr) :-
Name = arg_name(Offset),
ForeignArg = foreign_arg(Var, yes(foreign_arg_name_mode(Name, in_mode)),
Type, bp_native_if_possible),
( if type_is_io_state(Type) then
SaveMacroName = "MR_tbl_save_io_state_answer",
Args = [ForeignArg],
PrefixGoals = [],
CodeStr = "\t" ++ SaveMacroName ++ "(" ++ DebugArgStr ++ ", " ++
answer_block_name ++ ", " ++ int_to_string(Offset) ++ ", "
++ Name ++ ");\n"
else if builtin_type(CtorCat) = no then
% 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(foreign_arg_name_mode(Name, in_mode)),
dummy_type_var, bp_native_if_possible),
table_gen_make_type_info_var(Type, Context, !VarTable,
!TableInfo, TypeInfoVar, PrefixGoals),
TypeInfoName = "save_arg_typeinfo" ++ int_to_string(Offset),
lookup_var_type(!.VarTable, TypeInfoVar, TypeInfoType),
TypeInfoForeignArg = foreign_arg(TypeInfoVar,
yes(foreign_arg_name_mode(TypeInfoName, in_mode)),
TypeInfoType, bp_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"
else
type_save_category(CtorCat, 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,
var_table::in, var_table::out, table_info::in, hlds_goal::out) is det.
generate_memo_restore_goal(NumberedOutputVars, OrigInstMapDelta, TipVar,
Context, !VarTable, TableInfo, Goal) :-
(
NumberedOutputVars = [_ | _],
DebugArgStr = get_debug_arg_string(TableInfo),
ModuleInfo = TableInfo ^ table_module_info,
generate_restore_goals(ModuleInfo, OrigInstMapDelta, DebugArgStr,
NumberedOutputVars, RestoreInstMapDeltaSrc, RestoreArgs,
RestoreCodeStr, !VarTable),
BaseVarName = base_name,
Arg = foreign_arg(TipVar,
yes(foreign_arg_name_mode(BaseVarName, in_mode)),
trie_node_type, bp_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_call_foreign_proc(ModuleInfo, GetPredName,
Args, RestoreArgs,
instmap_delta_from_assoc_list(RestoreInstMapDeltaSrc),
detism_det, purity_semipure, tabling_c_attributes_dupl,
DeclCodeStr ++ GetRestoreCodeStr, 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, var_table::in, var_table::out, table_info::in,
hlds_goal::out) is det.
generate_memo_non_restore_goal(Detism, NumberedOutputVars, OrigInstMapDelta,
RecordVar, Context, !VarTable, TableInfo, Goal) :-
( if Detism = detism_multi then
ReturnAllAns = "table_memo_return_all_answers_multi"
else if Detism = detism_non then
ReturnAllAns = "table_memo_return_all_answers_nondet"
else
unexpected($pred, "invalid determinism")
),
generate_new_table_var("AnswerBlock", answer_block_type, is_not_dummy_type,
!VarTable, AnswerBlockVar),
ModuleInfo = TableInfo ^ table_module_info,
table_plain_call(ModuleInfo, ReturnAllAns,
[RecordVar, AnswerBlockVar], instmap_delta_bind_var(AnswerBlockVar),
Detism, purity_semipure, Context, ReturnAnswerBlocksGoal),
DebugArgStr = get_debug_arg_string(TableInfo),
generate_restore_goals(ModuleInfo, OrigInstMapDelta, DebugArgStr,
NumberedOutputVars, RestoreInstMapDeltaSrc, RestoreArgs,
RestoreCodeStr, !VarTable),
OutputVars = list.map(project_var, NumberedOutputVars),
Arg = foreign_arg(AnswerBlockVar,
yes(foreign_arg_name_mode(answer_block_name, in_mode)),
answer_block_type, bp_native_if_possible),
Args = [Arg],
PredName = "table_memo_non_return_all_shortcut",
table_call_foreign_proc(ModuleInfo, PredName,
Args, RestoreArgs,
instmap_delta_from_assoc_list(RestoreInstMapDeltaSrc),
detism_det, purity_semipure, tabling_c_attributes_no_dupl,
RestoreCodeStr, Context, ShortcutGoal),
GoalExpr = conj(plain_conj, [ReturnAnswerBlocksGoal, ShortcutGoal]),
set_of_var.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, var_table::in, var_table::out, table_info::in,
hlds_goal::out) is det.
generate_mm_restore_goal(Detism, NumberedOutputVars, OrigInstMapDelta,
SubgoalVar, Context, !VarTable, TableInfo, Goal) :-
( if Detism = detism_multi then
ReturnAllAns = "table_mm_return_all_multi"
else if Detism = detism_non then
ReturnAllAns = "table_mm_return_all_nondet"
else
unexpected($pred, "invalid determinism")
),
generate_mm_restore_or_suspend_goal(ReturnAllAns, Detism, purity_semipure,
NumberedOutputVars, OrigInstMapDelta, SubgoalVar, Context,
!VarTable, 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,
var_table::in, var_table::out, table_info::in, hlds_goal::out) is det.
generate_mm_suspend_goal(NumberedOutputVars, OrigInstMapDelta, SubgoalVar,
Context, !VarTable, TableInfo, Goal) :-
generate_mm_restore_or_suspend_goal("table_mm_suspend_consumer",
detism_non, purity_impure, NumberedOutputVars, OrigInstMapDelta,
SubgoalVar, Context, !VarTable, 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, var_table::in, var_table::out,
table_info::in, hlds_goal::out) is det.
generate_mm_restore_or_suspend_goal(PredName, Detism, Purity,
NumberedOutputVars, OrigInstMapDelta, SubgoalVar, Context,
!VarTable, TableInfo, Goal) :-
generate_new_table_var("AnswerBlock", answer_block_type, is_not_dummy_type,
!VarTable, AnswerBlockVar),
ModuleInfo = TableInfo ^ table_module_info,
table_plain_call(ModuleInfo, PredName,
[SubgoalVar, AnswerBlockVar], instmap_delta_bind_var(AnswerBlockVar),
Detism, Purity, Context, ReturnAnswerBlocksGoal),
DebugArgStr = get_debug_arg_string(TableInfo),
generate_restore_goals(ModuleInfo, OrigInstMapDelta, DebugArgStr,
NumberedOutputVars, RestoreInstMapDeltaSrc, RestoreArgs,
RestoreCodeStr, !VarTable),
OutputVars = list.map(project_var, NumberedOutputVars),
Arg = foreign_arg(AnswerBlockVar,
yes(foreign_arg_name_mode(answer_block_name, in_mode)),
answer_block_type, bp_native_if_possible),
Args = [Arg],
ReturnAllPredName = "table_mm_return_all_shortcut",
table_call_foreign_proc(ModuleInfo, ReturnAllPredName,
Args, RestoreArgs,
instmap_delta_from_assoc_list(RestoreInstMapDeltaSrc),
detism_det, purity_semipure, tabling_c_attributes_no_dupl,
RestoreCodeStr, Context, ReturnAllGoal),
GoalExpr = conj(plain_conj, [ReturnAnswerBlocksGoal, ReturnAllGoal]),
set_of_var.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(module_info::in, instmap_delta::in, string::in,
list(var_mode_pos_method(T))::in,
assoc_list(prog_var, mer_inst)::out, list(foreign_arg)::out, string::out,
var_table::in, var_table::out) is det.
generate_restore_goals(_, _, _, [], [], [], "", !VarTable).
generate_restore_goals(ModuleInfo, OrigInstmapDelta, DebugArgStr,
[NumberedVar | NumberedVars], [VarInst | VarInsts], [Arg | Args],
CodeStr ++ RestCodeStr, !VarTable) :-
NumberedVar = var_mode_pos_method(Var, _Mode, Offset, _),
lookup_var_type(!.VarTable, Var, VarType),
CtorCat = classify_type(ModuleInfo, VarType),
gen_restore_call_for_type(DebugArgStr, CtorCat, VarType, OrigInstmapDelta,
Var, Offset, VarInst, Arg, CodeStr),
generate_restore_goals(ModuleInfo, OrigInstmapDelta, DebugArgStr,
NumberedVars, VarInsts, Args, RestCodeStr, !VarTable).
:- pred gen_restore_call_for_type(string::in, type_ctor_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, CtorCat, Type, OrigInstmapDelta, Var,
Offset, Var - Inst, Arg, CodeStr) :-
Name = "restore_arg" ++ int_to_string(Offset),
( if type_is_io_state(Type) then
RestoreMacroName = "MR_tbl_restore_io_state_answer",
ArgType = Type
else if builtin_type(CtorCat) = no then
RestoreMacroName = "MR_tbl_restore_any_answer",
ArgType = dummy_type_var
else
type_save_category(CtorCat, CatString),
RestoreMacroName = "MR_tbl_restore_" ++ CatString ++ "_answer",
ArgType = Type
),
( if instmap_delta_search_var(OrigInstmapDelta, Var, InstPrime) then
Inst = InstPrime
else
unexpected($pred, "no inst")
),
Arg = foreign_arg(Var,
yes(foreign_arg_name_mode(Name, from_to_mode(free, Inst))),
ArgType, bp_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,
var_table::in, var_table::out, hlds_goal::out) is det.
generate_error_goal(TableInfo, Context, Msg, !VarTable, Goal) :-
ModuleInfo = TableInfo ^ table_module_info,
PredInfo = TableInfo ^ table_cur_pred_info,
ModuleName = pred_info_module(PredInfo),
PredName = pred_info_name(PredInfo),
pred_info_get_orig_arity(PredInfo, PredFormArity),
PredOrFunc = pred_info_is_pred_or_func(PredInfo),
PredOrFuncStr = pred_or_func_to_str(PredOrFunc),
PredNameStr = sym_name_to_string(qualified(ModuleName, PredName)),
user_arity_pred_form_arity(PredOrFunc,
user_arity(UserArityInt), PredFormArity),
Message = string.format("%s in %s %s/%d",
[s(Msg), s(PredOrFuncStr), s(PredNameStr), i(UserArityInt)]),
make_string_const_construction_alloc(Message, "Message",
MessageStrGoal, MessageVar, !VarTable),
table_plain_call(ModuleInfo, "table_error",
[MessageVar], instmap_delta_bind_no_var,
detism_erroneous, purity_pure, Context, CallGoal),
GoalExpr = conj(plain_conj, [MessageStrGoal, CallGoal]),
goal_info_init_hide(set_of_var.init, instmap_delta_bind_no_var,
detism_erroneous, purity_impure, Context, GoalInfo),
Goal = hlds_goal(GoalExpr, GoalInfo).
%---------------------------------------------------------------------------%
:- pred generate_new_table_var(string::in, mer_type::in, is_dummy_type::in,
var_table::in, var_table::out, prog_var::out) is det.
generate_new_table_var(Name, Type, IsDummy, !VarTable, Var) :-
Entry = vte(Name, Type, IsDummy),
add_var_entry(Entry, Var, !VarTable).
:- pred table_plain_call(module_info::in, string::in, list(prog_var)::in,
instmap_delta::in, determinism::in, purity::in, term.context::in,
hlds_goal::out) is det.
table_plain_call(ModuleInfo, PredName, ArgVars, InstMapDelta,
Detism, Purity, Context, Goal) :-
BuiltinModule = mercury_table_builtin_module,
(
Purity = purity_pure,
Features0 = []
;
( Purity = purity_semipure
; Purity = purity_impure
),
Features0 = [feature_not_impure_for_determinism]
),
( if Detism = detism_failure then
Features = [feature_preserve_backtrack_into | Features0]
else
Features = Features0
),
generate_plain_call(ModuleInfo, pf_predicate, BuiltinModule, PredName,
[], ArgVars, InstMapDelta, only_mode, Detism, Purity,
Features, Context, Goal).
:- pred table_call_foreign_proc(module_info::in, string::in,
list(foreign_arg)::in, list(foreign_arg)::in, instmap_delta::in,
determinism::in, purity::in, foreign_proc_attributes::in,
string::in, term.context::in, hlds_goal::out) is det.
table_call_foreign_proc(ModuleInfo, PredName, Args, ExtraArgs, InstMapDelta,
Detism, Purity, Attributes, Code, Context, Goal) :-
(
Purity = purity_pure,
Features0 = []
;
( Purity = purity_semipure
; Purity = purity_impure
),
Features0 = [feature_not_impure_for_determinism]
),
( if Detism = detism_failure then
Features = [feature_preserve_backtrack_into | Features0]
else
Features = Features0
),
BuiltinModule = mercury_table_builtin_module,
MaybeTraceRuntimCond = no,
generate_call_foreign_proc(ModuleInfo, pf_predicate,
BuiltinModule, PredName, [], Args, ExtraArgs, InstMapDelta, only_mode,
Detism, Purity, Features, Attributes,
MaybeTraceRuntimCond, Code, Context, Goal).
:- pred append_fail(hlds_goal::in, hlds_goal::out) is det.
append_fail(Goal, GoalAndThenFail) :-
Goal = hlds_goal(_, GoalInfo),
NonLocals = goal_info_get_nonlocals(GoalInfo),
Context = goal_info_get_context(GoalInfo),
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).
%---------------------------------------------------------------------------%
:- 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
---> msm_all_same(arg_tabling_method)
; msm_specified(
list(maybe(arg_tabling_method)),
hidden_arg_tabling_method
).
:- pred get_input_output_vars(module_info::in, var_table::in,
list(prog_var)::in, list(mer_mode)::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($pred, "lists not same length").
get_input_output_vars(_, _, [], [_ | _], !MaybeSpecMethod, _, _) :-
unexpected($pred, "lists not same length").
get_input_output_vars(ModuleInfo, VarTable, [Var | Vars], [Mode | Modes],
!MaybeSpecMethod, InVarModes, OutVarModes) :-
lookup_var_type(VarTable, Var, Type),
( if mode_is_fully_input(ModuleInfo, Type, Mode) then
get_input_output_vars(ModuleInfo, VarTable, Vars, Modes,
!MaybeSpecMethod, InVarModes0, OutVarModes),
(
!.MaybeSpecMethod = msm_all_same(ArgMethod)
;
!.MaybeSpecMethod =
msm_specified(MaybeArgMethods0, HiddenArgMethod),
( if
list.split_last(MaybeArgMethods0, MaybeArgMethods,
LastMaybeArgMethod)
then
(
LastMaybeArgMethod = yes(ArgMethod)
;
LastMaybeArgMethod = no,
unexpected($pred, "bad method for input var")
),
!:MaybeSpecMethod = msm_specified(MaybeArgMethods,
HiddenArgMethod)
else
% 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.
(
HiddenArgMethod = table_hidden_arg_value,
ArgMethod = arg_value
;
HiddenArgMethod = table_hidden_arg_addr,
ArgMethod = arg_addr
),
!:MaybeSpecMethod = msm_all_same(ArgMethod)
)
),
InVarModes = [var_mode_method(Var, Mode, ArgMethod) | InVarModes0]
else if mode_is_fully_output(ModuleInfo, Type, Mode) then
get_input_output_vars(ModuleInfo, VarTable, Vars, Modes,
!MaybeSpecMethod, InVarModes, OutVarModes0),
(
!.MaybeSpecMethod = msm_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 =
msm_specified(MaybeArgMethods0, HiddenArgMethod),
( if
list.split_last(MaybeArgMethods0, MaybeArgMethods,
LastMaybeArgMethod)
then
expect(unify(LastMaybeArgMethod, no), $pred,
"bad method for output var"),
!:MaybeSpecMethod = msm_specified(MaybeArgMethods,
HiddenArgMethod)
else
% 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.
(
HiddenArgMethod = table_hidden_arg_value,
ArgMethod = arg_value
;
HiddenArgMethod = table_hidden_arg_addr,
ArgMethod = arg_addr
),
!:MaybeSpecMethod = msm_all_same(ArgMethod)
)
),
OutVarModes = [var_mode_method(Var, Mode, arg_value) | OutVarModes0]
else
% We should have caught this when we added the tabling pragma
% to the proc_info.
unexpected($pred, "bad var")
).
%---------------------------------------------------------------------------%
:- pred create_instmap_delta(list(hlds_goal)::in, instmap_delta::out) is det.
create_instmap_delta([], IMD) :-
IMD = instmap_delta_bind_no_var.
create_instmap_delta([Goal | Rest], IMD) :-
Goal = hlds_goal(_, GoalInfo),
IMD0 = goal_info_get_instmap_delta(GoalInfo),
create_instmap_delta(Rest, IMD1),
instmap_delta_apply_instmap_delta(IMD0, IMD1, test_size, IMD).
%---------------------------------------------------------------------------%
:- pred add_proc_table_struct(pred_proc_id::in, proc_table_struct_info::in,
proc_info::in, module_info::in, module_info::out) is det.
add_proc_table_struct(PredProcId, ProcTableStructInfo, ProcInfo,
!ModuleInfo) :-
module_info_get_table_struct_map(!.ModuleInfo, TableStructMap0),
proc_info_get_table_attributes(ProcInfo, MaybeTableAttributes),
(
MaybeTableAttributes = yes(TableAttributes)
;
MaybeTableAttributes = no,
TableAttributes = default_memo_table_attributes
),
TableStructInfo = table_struct_info(ProcTableStructInfo, TableAttributes),
map.det_insert(PredProcId, TableStructInfo,
TableStructMap0, TableStructMap),
module_info_set_table_struct_map(TableStructMap, !ModuleInfo).
%---------------------------------------------------------------------------%
:- type var_mode_method
---> var_mode_method(
% The head variable.
prog_var,
% The mode of the head variable.
mer_mode,
% 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).
arg_tabling_method
).
:- type var_mode_pos_method == var_mode_pos_method(arg_tabling_method).
:- type var_mode_pos_method(T)
---> var_mode_pos_method(
% The head variable.
prog_var,
% The mode of the head variable.
mer_mode,
% The offset of the head variable in the answer block;
% the first slot is at offset 0.
int,
% 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.
T
).
:- 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).
:- pred goal_info_init_hide(set_of_progvar::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_ctor_category) = bool.
builtin_type(CtorCat) = Builtin :-
(
( CtorCat = ctor_cat_builtin(_)
; CtorCat = ctor_cat_void
; CtorCat = ctor_cat_system(cat_system_type_ctor_info)
; CtorCat = ctor_cat_system(cat_system_typeclass_info)
; CtorCat = ctor_cat_system(cat_system_base_typeclass_info)
),
Builtin = yes
;
( CtorCat = ctor_cat_user(_)
; CtorCat = ctor_cat_enum(_)
; CtorCat = ctor_cat_system(cat_system_type_info)
; CtorCat = ctor_cat_variable
; CtorCat = ctor_cat_tuple
; CtorCat = ctor_cat_builtin_dummy
; CtorCat = ctor_cat_higher_order
),
Builtin = 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_ctor_category::in, string::out) is det.
type_save_category(CtorCat, Name) :-
(
CtorCat = ctor_cat_enum(cat_enum_mercury),
Name = "enum"
;
CtorCat = ctor_cat_enum(cat_enum_foreign),
sorry($pred, "tabling and foreign enumerations NYI.")
;
CtorCat = ctor_cat_builtin(cat_builtin_int(int_type_int)),
Name = "int"
;
CtorCat = ctor_cat_builtin(cat_builtin_int(int_type_uint)),
Name = "uint"
;
CtorCat = ctor_cat_builtin(cat_builtin_int(int_type_int8)),
Name = "int8"
;
CtorCat = ctor_cat_builtin(cat_builtin_int(int_type_uint8)),
Name = "uint8"
;
CtorCat = ctor_cat_builtin(cat_builtin_int(int_type_int16)),
Name = "int16"
;
CtorCat = ctor_cat_builtin(cat_builtin_int(int_type_uint16)),
Name = "uint16"
;
CtorCat = ctor_cat_builtin(cat_builtin_int(int_type_int32)),
Name = "int32"
;
CtorCat = ctor_cat_builtin(cat_builtin_int(int_type_uint32)),
Name = "uint32"
;
CtorCat = ctor_cat_builtin(cat_builtin_int(int_type_int64)),
Name = "int64"
;
CtorCat = ctor_cat_builtin(cat_builtin_int(int_type_uint64)),
Name = "uint64"
;
CtorCat = ctor_cat_builtin(cat_builtin_float),
Name = "float"
;
CtorCat = ctor_cat_builtin(cat_builtin_char),
Name = "char"
;
CtorCat = ctor_cat_builtin(cat_builtin_string),
Name = "string"
;
CtorCat = ctor_cat_higher_order,
Name = "pred"
;
% Could do better.
( CtorCat = ctor_cat_user(_)
; CtorCat = ctor_cat_variable
; CtorCat = ctor_cat_system(cat_system_type_info)
),
Name = "any"
;
CtorCat = ctor_cat_tuple,
Name = "any"
;
( CtorCat = ctor_cat_system(cat_system_type_ctor_info)
; CtorCat = ctor_cat_system(cat_system_typeclass_info)
; CtorCat = ctor_cat_system(cat_system_base_typeclass_info)
; CtorCat = ctor_cat_builtin_dummy
; CtorCat = ctor_cat_void
),
unexpected($pred, "unexpected category")
).
%---------------------------------------------------------------------------%
:- pred get_enum_max_int_tag(type_table::in, type_ctor::in, int::out) is det.
get_enum_max_int_tag(TypeTable, TypeCtor, MaxIntTag) :-
lookup_type_ctor_defn(TypeTable, TypeCtor, TypeDefn),
hlds_data.get_type_defn_body(TypeDefn, TypeBody),
( if
TypeBody = hlds_du_type(TypeBodyDu),
TypeBodyDu = type_body_du(_Ctors, _AlphaSortedCtors, MaybeSuperType,
MaybeCanonical, MaybeRepn, _MaybeForeign),
MaybeCanonical = canon,
MaybeRepn = yes(Repn),
Repn ^ dur_kind = du_type_kind_mercury_enum
then
CtorRepns = Repn ^ dur_ctor_repns,
(
MaybeSuperType = not_a_subtype,
list.det_last(CtorRepns, LastCtorRepn),
max_enum_int_tag(LastCtorRepn, 0, MaxIntTag)
;
MaybeSuperType = subtype_of(_),
% Subtype enums do not necessary use all values from 0 to
% MaxIntTag, so this will create a trie node that may be larger
% than necessary. We could subtract the lowest possible enum value
% to reduce the range, or switch to a different step type if the
% range of values is sparsely populated.
list.foldl(max_enum_int_tag, CtorRepns, 0, MaxIntTag)
)
else
unexpected($pred, "enum type is not du_type?")
).
:- pred max_enum_int_tag(constructor_repn::in, int::in, int::out) is det.
max_enum_int_tag(CtorRepn, !MaxIntTag) :-
ConsTag = CtorRepn ^ cr_tag,
( if ConsTag = int_tag(int_tag_int(IntTagVal)) then
!:MaxIntTag = int.max(!.MaxIntTag, IntTagVal)
else
unexpected($pred, "enum has non-int tag")
).
%---------------------------------------------------------------------------%
:- 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_ref(table_attr_statistics, call_or_answer_table) = maybe(string).
stats_ref(Statistics, Kind) = MaybeStatsRef :-
(
Statistics = table_do_not_gather_statistics,
MaybeStatsRef = no
;
Statistics = table_gather_statistics,
(
Kind = call_table,
KindStr = "MR_TABLE_CALL_TABLE"
;
Kind = answer_table,
KindStr = "MR_TABLE_ANSWER_TABLE"
),
StatsRef = proc_table_info_name ++ "->MR_pt_stats" ++
"[" ++ KindStr ++ "][MR_TABLE_STATS_CURR]",
MaybeStatsRef = yes(StatsRef)
).
:- func maybe_step_stats_arg_addr(maybe(string), int) = string.
maybe_step_stats_arg_addr(MaybeStatsRef, SeqNum) = ArgStr :-
(
MaybeStatsRef = no,
ArgStr = "NULL"
;
MaybeStatsRef = yes(StatsRef),
ArgStr = "&(" ++ step_stats_arg_addr(StatsRef, SeqNum) ++ ")"
).
:- func step_stats_arg_addr(string, int) = string.
step_stats_arg_addr(StatsRef, SeqNum) = ArgStr :-
ArgStr = StatsRef ++ ".MR_ts_steps" ++ "[" ++ int_to_string(SeqNum) ++ "]".
%---------------------------------------------------------------------------%
:- pred table_gen_make_type_info_var(mer_type::in, term.context::in,
var_table::in, var_table::out, table_info::in, table_info::out,
prog_var::out, list(hlds_goal)::out) is det.
table_gen_make_type_info_var(Type, Context, !VarTable, !TableInfo,
TypeInfoVar, TypeInfoGoals) :-
table_gen_make_type_info_vars([Type], Context, !VarTable, !TableInfo,
TypeInfoVars, TypeInfoGoals),
( if TypeInfoVars = [TypeInfoVar0] then
TypeInfoVar = TypeInfoVar0
else
unexpected($pred, "list length != 1")
).
:- pred table_gen_make_type_info_vars(list(mer_type)::in, term.context::in,
var_table::in, var_table::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, !VarTable, !TableInfo,
TypeInfoVars, TypeInfoGoals) :-
% Extract the information from table_info.
table_info_extract(!.TableInfo, ModuleInfo0, PredInfo0, ProcInfo0),
% Put the var_table back in the proc_info.
proc_info_set_var_table(!.VarTable, ProcInfo0, ProcInfo1),
% Generate the code that creates the type_infos.
polymorphism_make_type_info_vars_mi(Types, Context,
TypeInfoVars, TypeInfoGoals, ModuleInfo0, ModuleInfo,
PredInfo0, PredInfo, ProcInfo1, ProcInfo),
% Get the new var_table from the proc_info.
proc_info_get_var_table(ProcInfo, !:VarTable),
% 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(var_table::in, var_mode_pos_method::in)
is semidet.
var_mode_pos_is_io_state(VarTable, VarModePosMethod) :-
var_is_io_state(VarTable, project_var(VarModePosMethod)).
:- pred var_mode_is_io_state(var_table::in, pair(prog_var, mer_mode)::in)
is semidet.
var_mode_is_io_state(VarTable, Var - _) :-
var_is_io_state(VarTable, Var).
:- pred var_is_io_state(var_table::in, prog_var::in) is semidet.
var_is_io_state(VarTable, Var) :-
lookup_var_type(VarTable, Var, VarType),
type_is_io_state(VarType).
%---------------------------------------------------------------------------%
:- func tabling_c_attributes_dupl = 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 = 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 = 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 :-
% XXX This code looks like it will generate type variables that
% will collide with actual type variables of the current procedure,
% if it has any.
varset.init(DummyTVarSet0),
varset.new_var(DummyTVar, DummyTVarSet0, _),
Type = type_variable(DummyTVar, kind_star).
%---------------------------------------------------------------------------%
:- func loop_inactive_cons_id = cons_id.
:- func loop_active_cons_id = cons_id.
loop_inactive_cons_id = du_data_ctor(du_ctor(SymName, 0, TypeCtor)) :-
SymName = qualified(mercury_table_builtin_module, "loop_inactive"),
TypeCtor = loop_status_type_ctor.
loop_active_cons_id = du_data_ctor(du_ctor(SymName, 0, TypeCtor)) :-
SymName = qualified(mercury_table_builtin_module, "loop_active"),
TypeCtor = loop_status_type_ctor.
:- func memo_det_inactive_cons_id = cons_id.
:- func memo_det_active_cons_id = cons_id.
:- func memo_det_succeeded_cons_id = cons_id.
memo_det_inactive_cons_id = du_data_ctor(du_ctor(SymName, 0, TypeCtor)) :-
SymName = qualified(mercury_table_builtin_module, "memo_det_inactive"),
TypeCtor = memo_det_status_type_ctor.
memo_det_active_cons_id = du_data_ctor(du_ctor(SymName, 0, TypeCtor)) :-
SymName = qualified(mercury_table_builtin_module, "memo_det_active"),
TypeCtor = memo_det_status_type_ctor.
memo_det_succeeded_cons_id = du_data_ctor(du_ctor(SymName, 0, TypeCtor)) :-
SymName = qualified(mercury_table_builtin_module, "memo_det_succeeded"),
TypeCtor = memo_det_status_type_ctor.
:- func memo_semi_inactive_cons_id = cons_id.
:- func memo_semi_active_cons_id = cons_id.
:- func memo_semi_succeeded_cons_id = cons_id.
:- func memo_semi_failed_cons_id = cons_id.
memo_semi_inactive_cons_id = du_data_ctor(du_ctor(SymName, 0, TypeCtor)) :-
SymName = qualified(mercury_table_builtin_module, "memo_semi_inactive"),
TypeCtor = memo_semi_status_type_ctor.
memo_semi_active_cons_id = du_data_ctor(du_ctor(SymName, 0, TypeCtor)) :-
SymName = qualified(mercury_table_builtin_module, "memo_semi_active"),
TypeCtor = memo_semi_status_type_ctor.
memo_semi_succeeded_cons_id = du_data_ctor(du_ctor(SymName, 0, TypeCtor)) :-
SymName = qualified(mercury_table_builtin_module, "memo_semi_succeeded"),
TypeCtor = memo_semi_status_type_ctor.
memo_semi_failed_cons_id = du_data_ctor(du_ctor(SymName, 0, TypeCtor)) :-
SymName = qualified(mercury_table_builtin_module, "memo_semi_failed"),
TypeCtor = memo_semi_status_type_ctor.
:- func memo_non_inactive_cons_id = cons_id.
:- func memo_non_active_cons_id = cons_id.
:- func memo_non_incomplete_cons_id = cons_id.
:- func memo_non_complete_cons_id = cons_id.
memo_non_inactive_cons_id = du_data_ctor(du_ctor(SymName, 0, TypeCtor)) :-
SymName = qualified(mercury_table_builtin_module, "memo_non_inactive"),
TypeCtor = memo_non_status_type_ctor.
memo_non_active_cons_id = du_data_ctor(du_ctor(SymName, 0, TypeCtor)) :-
SymName = qualified(mercury_table_builtin_module, "memo_non_active"),
TypeCtor = memo_non_status_type_ctor.
memo_non_incomplete_cons_id = du_data_ctor(du_ctor(SymName, 0, TypeCtor)) :-
SymName = qualified(mercury_table_builtin_module, "memo_non_incomplete"),
TypeCtor = memo_non_status_type_ctor.
memo_non_complete_cons_id = du_data_ctor(du_ctor(SymName, 0, TypeCtor)) :-
SymName = qualified(mercury_table_builtin_module, "memo_non_complete"),
TypeCtor = memo_non_status_type_ctor.
:- func mm_inactive_cons_id = cons_id.
:- func mm_active_cons_id = cons_id.
:- func mm_complete_cons_id = cons_id.
mm_inactive_cons_id = du_data_ctor(du_ctor(SymName, 0, TypeCtor)) :-
SymName = qualified(mercury_table_builtin_module, "mm_inactive"),
TypeCtor = mm_status_type_ctor.
mm_active_cons_id = du_data_ctor(du_ctor(SymName, 0, TypeCtor)) :-
SymName = qualified(mercury_table_builtin_module, "mm_active"),
TypeCtor = mm_status_type_ctor.
mm_complete_cons_id = du_data_ctor(du_ctor(SymName, 0, TypeCtor)) :-
SymName = qualified(mercury_table_builtin_module, "mm_complete"),
TypeCtor = mm_status_type_ctor.
:- func loop_status_type_ctor = type_ctor.
loop_status_type_ctor = TypeCtor :-
TypeModule = mercury_table_builtin_module,
TypeSymName = qualified(TypeModule, "loop_status"),
TypeCtor = type_ctor(TypeSymName, 0).
:- func memo_det_status_type_ctor = type_ctor.
memo_det_status_type_ctor = TypeCtor :-
TypeModule = mercury_table_builtin_module,
TypeSymName = qualified(TypeModule, "memo_det_status"),
TypeCtor = type_ctor(TypeSymName, 0).
:- func memo_semi_status_type_ctor = type_ctor.
memo_semi_status_type_ctor = TypeCtor :-
TypeModule = mercury_table_builtin_module,
TypeSymName = qualified(TypeModule, "memo_semi_status"),
TypeCtor = type_ctor(TypeSymName, 0).
:- func memo_non_status_type_ctor = type_ctor.
memo_non_status_type_ctor = TypeCtor :-
TypeModule = mercury_table_builtin_module,
TypeSymName = qualified(TypeModule, "memo_non_status"),
TypeCtor = type_ctor(TypeSymName, 0).
:- func mm_status_type_ctor = type_ctor.
mm_status_type_ctor = TypeCtor :-
TypeModule = mercury_table_builtin_module,
TypeSymName = qualified(TypeModule, "mm_status"),
TypeCtor = type_ctor(TypeSymName, 0).
%---------------------------------------------------------------------------%
:- 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 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.
:- pragma consider_used(func(table_tip_node_name/0)).
:- pragma consider_used(func(num_input_args_name/0)).
:- pragma consider_used(func(pred_name_var_name/0)).
:- pragma consider_used(func(answer_table_name/0)).
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.
table_info_init(ModuleInfo, PredInfo, ProcInfo, TableInfo) :-
TableInfo = table_info(ModuleInfo, PredInfo, ProcInfo).
:- pred table_info_extract(table_info::in,
module_info::out, pred_info::out, proc_info::out) is det.
table_info_extract(TableInfo, ModuleInfo, PredInfo, ProcInfo) :-
TableInfo = table_info(ModuleInfo, PredInfo, ProcInfo).
%---------------------------------------------------------------------------%
:- end_module transform_hlds.table_gen.
%---------------------------------------------------------------------------%