Files
mercury/compiler/table_gen.m
Zoltan Somogyi 672f77c4ec Add a new compiler option. --inform-ite-instead-of-switch.
Estimated hours taken: 20
Branches: main

Add a new compiler option. --inform-ite-instead-of-switch. If this is enabled,
the compiler will generate informational messages about if-then-elses that
it thinks should be converted to switches for the sake of program reliability.

Act on the output generated by this option.

compiler/simplify.m:
	Implement the new option.

	Fix an old bug that could cause us to generate warnings about code
	that was OK in one duplicated copy but not in another (where a switch
	arm's code is duplicated due to the case being selected for more than
	one cons_id).

compiler/options.m:
	Add the new option.

	Add a way to test for the bug fix in simplify.

doc/user_guide.texi:
	Document the new option.

NEWS:
	Mention the new option.

library/*.m:
mdbcomp/*.m:
browser/*.m:
compiler/*.m:
deep_profiler/*.m:
	Convert if-then-elses to switches at most of the sites suggested by the
	new option. At the remaining sites, switching to switches would have
	nontrivial downsides. This typically happens with the switched-on type
	has many functors, and we treat one or two specially (e.g. cons/2 in
	the cons_id type).

	Perform misc cleanups in the vicinity of the if-then-else to switch
	conversions.

	In a few cases, improve the error messages generated.

compiler/accumulator.m:
compiler/hlds_goal.m:
	(Rename and) move insts for particular kinds of goal from
	accumulator.m to hlds_goal.m, to allow them to be used in other
	modules. Using these insts allowed us to eliminate some if-then-elses
	entirely.

compiler/exprn_aux.m:
	Instead of fixing some if-then-elses, delete the predicates containing
	them, since they aren't used, and (as pointed out by the new option)
	would need considerable other fixing if they were ever needed again.

compiler/lp_rational.m:
	Add prefixes to the names of the function symbols on some types,
	since without those prefixes, it was hard to figure out what type
	the switch corresponding to an old if-then-else was switching on.

tests/invalid/reserve_tag.err_exp:
	Expect a new, improved error message.
2007-11-23 07:36:01 +00:00

3746 lines
160 KiB
Mathematica

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