mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-06 16:08:32 +00:00
*/*.m:
As above.
configure.ac:
Require the installed compiler to support this capability.
469 lines
14 KiB
Mathematica
469 lines
14 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2007-2011 The University of Melbourne.
|
|
% Copyright (C) 2014-2018 The Mercury team.
|
|
% This file is distributed under the terms specified in COPYING.LIB.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: backjump.m
|
|
% Author: Mark Brown <mark@csse.unimelb.edu.au>
|
|
% Stability: medium
|
|
%
|
|
% This module defines the Mercury interface for backjumping.
|
|
%
|
|
% An application can use this module to add backjumping to their search
|
|
% algorithms in the following way:
|
|
%
|
|
% - At points in the search tree where you wish to backjump *to*, add a
|
|
% call to get_choice_id/1.
|
|
%
|
|
% - When in the search tree you discover that there are no further solutions
|
|
% between the current execution point and the failure port of an earlier
|
|
% call to get_choice_id/1, call backjump/1 with the relevant choice_id.
|
|
% This takes execution immediately to that failure port.
|
|
%
|
|
% It is important to avoid backjumping to a choicepoint that has already
|
|
% been pruned away, either on failure or due to a commit. In the former case
|
|
% (which can occur if the choice_id is stored in a non-trailed mutable, for
|
|
% example) the implementation throws an exception. In the latter case,
|
|
% behaviour is undefined but most likely will be a seg fault. This can occur
|
|
% if multi/nondet code that returns a choice_id is called from a
|
|
% cc_multi/cc_nondet context, for example.
|
|
%
|
|
% Note that the definition of "solution" in the above means solution with
|
|
% respect to the search algorithm, and doesn't necessarily mean solution of
|
|
% the immediate parent or any particular ancestor.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module backjump.
|
|
:- interface.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Abstract type representing points in the search tree which can be
|
|
% backjumped to.
|
|
%
|
|
:- type choice_id.
|
|
|
|
% Returns a single unused choice_id, then fails. We make this nondet
|
|
% rather than det, however, so that:
|
|
% a) it leaves a nondet stack frame behind which can later be used
|
|
% by backjump/1, and
|
|
% b) the calling context is forced to deal with the case of no
|
|
% solutions, which may occur even if later conjuncts can never fail
|
|
% (since they may call backjump/1, which counts as an exceptional
|
|
% rather than a logical answer).
|
|
%
|
|
:- impure pred get_choice_id(choice_id::out) is nondet.
|
|
|
|
% Backjump to the point where the choice_id was created. That is,
|
|
% jump immediately to the FAIL event of the corresponding call to
|
|
% get_choice_id.
|
|
%
|
|
:- impure pred backjump(choice_id::in) is erroneous.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- interface.
|
|
|
|
% Debugging support.
|
|
%
|
|
:- func to_int(choice_id) = int.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module io.
|
|
:- import_module list.
|
|
:- import_module string.
|
|
|
|
:- type choice_id == int.
|
|
|
|
get_choice_id(Id) :-
|
|
impure builtin_choice_id(Id).
|
|
|
|
backjump(Id) :-
|
|
impure builtin_backjump(Id).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- impure pred builtin_choice_id(choice_id::out) is nondet.
|
|
:- pragma terminates(pred(builtin_choice_id/1)).
|
|
|
|
:- impure pred builtin_backjump(choice_id::in) is erroneous.
|
|
:- pragma terminates(pred(builtin_backjump/1)).
|
|
|
|
% builtin_choice_id and builtin_backjump are implemented below using
|
|
% hand-coded low-level C code.
|
|
%
|
|
% IMPORTANT: any changes or additions to external predicates should be
|
|
% reflected in the definition of pred_is_external in
|
|
% mdbcomp/program_representation.m. The debugger needs to know what predicates
|
|
% are defined externally, so that it knows not to expect events for those
|
|
% predicates.
|
|
:- pragma external_pred(builtin_choice_id/1).
|
|
:- pragma external_pred(builtin_backjump/1).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_decl("C", "#include \"mercury_backjump.h\"").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The --high-level-code implementation.
|
|
%
|
|
|
|
:- pragma foreign_decl("C",
|
|
"
|
|
// protect against multiple inclusion
|
|
#ifndef ML_BACKJUMP_GUARD
|
|
#define ML_BACKJUMP_GUARD
|
|
|
|
#ifdef MR_HIGHLEVEL_CODE
|
|
|
|
#include <setjmp.h>
|
|
|
|
#define MR_CONT_PARAMS MR_Cont cont, void *cont_env
|
|
#define MR_CONT_PARAM_TYPES MR_Cont, void *
|
|
#define MR_CONT_ARGS cont, cont_env
|
|
#define MR_CONT_CALL() cont(cont_env)
|
|
|
|
void MR_CALL
|
|
mercury__backjump__builtin_choice_id_1_p_0(
|
|
MR_BackJumpChoiceId *id, MR_CONT_PARAMS);
|
|
|
|
void MR_CALL
|
|
mercury__backjump__builtin_backjump_1_p_0(MR_BackJumpChoiceId id);
|
|
|
|
#endif // MR_HIGHLEVEL_CODE
|
|
#endif // ML_BACKJUMP_GUARD
|
|
").
|
|
|
|
:- pragma foreign_code("C",
|
|
"
|
|
#ifdef MR_HIGHLEVEL_CODE
|
|
|
|
#ifdef MR_NATIVE_GC
|
|
|
|
// XXX code is needed to trace the local variables
|
|
// in the builtin_choice_id predicate for accurate GC.
|
|
|
|
#endif // MR_NATIVE_GC
|
|
|
|
void MR_CALL
|
|
mercury__backjump__builtin_choice_id_1_p_0(MR_Integer *id, MR_CONT_PARAMS)
|
|
{
|
|
MR_BackJumpHandler this_handler;
|
|
|
|
this_handler.MR_bjh_prev = MR_GET_BACKJUMP_HANDLER();
|
|
this_handler.MR_bjh_id = MR_GET_NEXT_CHOICE_ID();
|
|
MR_SET_BACKJUMP_HANDLER(&this_handler);
|
|
|
|
if (setjmp(this_handler.MR_bjh_handler) == 0) {
|
|
#ifdef MR_DEBUG_JMPBUFS
|
|
fprintf(stderr, ""choice setjmp %p\\n"", this_handler.MR_bjh_handler);
|
|
#endif
|
|
|
|
*id = this_handler.MR_bjh_id;
|
|
MR_CONT_CALL();
|
|
} else {
|
|
#ifdef MR_DEBUG_JMPBUFS
|
|
fprintf(stderr, ""choice caught jmp %p\\n"",
|
|
this_handler.MR_bjh_handler);
|
|
#endif
|
|
}
|
|
|
|
MR_SET_BACKJUMP_HANDLER(this_handler.MR_bjh_prev);
|
|
}
|
|
|
|
void MR_CALL
|
|
mercury__backjump__builtin_backjump_1_p_0(MR_BackJumpChoiceId id)
|
|
{
|
|
MR_BackJumpHandler *backjump_handler;
|
|
|
|
backjump_handler = MR_GET_BACKJUMP_HANDLER();
|
|
|
|
// XXX when we commit and prune away nondet stack frames, we leave the
|
|
// backjump handlers on the stack. If the caller tries to backjump to
|
|
// a frame that has been pruned away, the handler may still be on the
|
|
// stack and we won't detect the problem.
|
|
//
|
|
// We could avoid this problem by adding a trailing function which
|
|
// prunes back the handler stack on a commit, which would mean that in
|
|
// this case we will reach the bottom of the stack and call
|
|
// ML_report_invalid_backjump rather than seg faulting. But that would
|
|
// require trailing to be always available. Instead, we just rely on
|
|
// the caller only backjumping to frames that actually do exist.
|
|
//
|
|
// (The same problem would occur if the caller tries to backjump to a
|
|
// frame that has already failed. In this case, though, the choice ID
|
|
// will also no longer be live since the call to get_choice_id would have
|
|
// been backtracked over, so this isn't as much of a problem as with
|
|
// commits.)
|
|
|
|
while (backjump_handler != NULL) {
|
|
if (backjump_handler->MR_bjh_id == id) {
|
|
break;
|
|
}
|
|
backjump_handler = backjump_handler->MR_bjh_prev;
|
|
}
|
|
|
|
if (backjump_handler == NULL) {
|
|
ML_report_invalid_backjump(id);
|
|
exit(EXIT_FAILURE);
|
|
} else {
|
|
|
|
#ifdef MR_DEBUG_JMPBUFS
|
|
fprintf(stderr, ""backjump longjmp %p\\n"",
|
|
backjump_handler->MR_bjh_handler);
|
|
#endif
|
|
longjmp(backjump_handler->MR_bjh_handler, 1);
|
|
}
|
|
}
|
|
|
|
#endif // MR_HIGHLEVEL_CODE
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The --no-high-level-code implementation.
|
|
%
|
|
|
|
:- pragma foreign_decl("C",
|
|
"
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
|
|
#include ""mercury_stacks.h""
|
|
#include ""mercury_stack_trace.h""
|
|
#include ""mercury_trace_base.h""
|
|
#include ""mercury_layout_util.h""
|
|
#include ""mercury_deep_profiling_hand.h""
|
|
|
|
#endif // !MR_HIGHLEVEL_CODE
|
|
").
|
|
|
|
:- pragma foreign_code("C",
|
|
"
|
|
void mercury_sys_init_backjumps_init(void);
|
|
void mercury_sys_init_backjumps_init_type_tables(void);
|
|
#ifdef MR_DEEP_PROFILING
|
|
void mercury_sys_init_backjumps_write_out_proc_statics(FILE *deep_fp,
|
|
FILE *procrep_fp);
|
|
#endif
|
|
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
|
|
#define ML_DUMMY_LINE 0
|
|
|
|
#define ML_BACKJUMP_STRUCT \
|
|
(((MR_BackJumpHandler *) (MR_curfr + 1 - MR_NONDET_FIXED_SIZE)) - 1)
|
|
|
|
#ifdef ML_DEBUG_BACKJUMPS
|
|
#define ML_BACKJUMP_CHECKPOINT(s, p) \
|
|
do { \
|
|
fflush(stdout); \
|
|
fprintf(stderr, ""backjumps (%s): "" \
|
|
""loc %p, prev %p, id %d, sp %p, fr %p\\n"", \
|
|
s, p, p->MR_bjh_prev, p->MR_bjh_id, p->MR_bjh_saved_sp, \
|
|
p->MR_bjh_saved_fr); \
|
|
} while (0)
|
|
#else
|
|
#define ML_BACKJUMP_CHECKPOINT(s, p)
|
|
#endif
|
|
|
|
MR_define_extern_entry(mercury__backjump__builtin_choice_id_1_0);
|
|
MR_define_extern_entry(mercury__backjump__builtin_backjump_1_0);
|
|
|
|
MR_declare_label(mercury__backjump__builtin_choice_id_1_0_i1);
|
|
|
|
MR_proc_static_user_no_site(backjump, builtin_choice_id, 1, 0,
|
|
""backjump.m"", ML_DUMMY_LINE, MR_TRUE);
|
|
MR_proc_static_user_no_site(backjump, builtin_backjump, 1, 0,
|
|
""backjump.m"", ML_DUMMY_LINE, MR_TRUE);
|
|
|
|
MR_EXTERN_USER_PROC_STATIC_PROC_LAYOUT(MR_DETISM_NON,
|
|
MR_PROC_NO_SLOT_COUNT, -1, MR_PREDICATE, backjump, builtin_choice_id,
|
|
1, 0);
|
|
MR_EXTERN_USER_PROC_STATIC_PROC_LAYOUT(MR_DETISM_DET, 1,
|
|
MR_LONG_LVAL_STACKVAR_INT(1), MR_PREDICATE, backjump,
|
|
builtin_backjump, 1, 0);
|
|
|
|
MR_MAKE_USER_INTERNAL_LAYOUT(backjump, builtin_choice_id, 1, 0, 1);
|
|
|
|
MR_BEGIN_MODULE(hand_written_backjump_module)
|
|
MR_init_entry_sl(mercury__backjump__builtin_choice_id_1_0);
|
|
MR_init_entry_sl(mercury__backjump__builtin_backjump_1_0);
|
|
|
|
MR_init_label_sl(mercury__backjump__builtin_choice_id_1_0_i1);
|
|
MR_BEGIN_CODE
|
|
|
|
MR_define_entry(mercury__backjump__builtin_choice_id_1_0);
|
|
{
|
|
MR_mkpragmaframe(""builtin_choice_id/1"", 0, MR_BackJumpHandler_Struct,
|
|
MR_LABEL(mercury__backjump__builtin_choice_id_1_0_i1));
|
|
|
|
#if defined(MR_DEEP_PROFILING)
|
|
MR_fatal_error(
|
|
""builtin_choice_id: NYI backjumping and deep profiling"");
|
|
#endif
|
|
|
|
ML_BACKJUMP_STRUCT->MR_bjh_prev = MR_GET_BACKJUMP_HANDLER();
|
|
ML_BACKJUMP_STRUCT->MR_bjh_id = MR_GET_NEXT_CHOICE_ID();
|
|
ML_BACKJUMP_STRUCT->MR_bjh_saved_sp = MR_sp;
|
|
ML_BACKJUMP_STRUCT->MR_bjh_saved_fr = MR_curfr;
|
|
MR_SET_BACKJUMP_HANDLER(ML_BACKJUMP_STRUCT);
|
|
|
|
ML_BACKJUMP_CHECKPOINT(""create"", ML_BACKJUMP_STRUCT);
|
|
|
|
MR_r1 = (MR_Word) ML_BACKJUMP_STRUCT->MR_bjh_id;
|
|
MR_succeed();
|
|
}
|
|
MR_define_label(mercury__backjump__builtin_choice_id_1_0_i1);
|
|
{
|
|
// Restore the previous handler.
|
|
MR_SET_BACKJUMP_HANDLER(ML_BACKJUMP_STRUCT->MR_bjh_prev);
|
|
MR_fail();
|
|
}
|
|
|
|
MR_define_entry(mercury__backjump__builtin_backjump_1_0);
|
|
{
|
|
MR_BackJumpChoiceId id = MR_r1;
|
|
MR_BackJumpHandler *backjump_handler;
|
|
|
|
backjump_handler = MR_GET_BACKJUMP_HANDLER();
|
|
|
|
#if defined(MR_DEEP_PROFILING)
|
|
MR_fatal_error(
|
|
""builtin_backjump: NYI backjumping and deep profiling"");
|
|
#endif
|
|
|
|
// XXX see comments in the high-level implementation.
|
|
while (backjump_handler != NULL) {
|
|
if (backjump_handler->MR_bjh_id == id) {
|
|
break;
|
|
}
|
|
|
|
ML_BACKJUMP_CHECKPOINT(""scan"", backjump_handler);
|
|
|
|
backjump_handler = backjump_handler->MR_bjh_prev;
|
|
}
|
|
|
|
if (backjump_handler == NULL) {
|
|
ML_report_invalid_backjump(id);
|
|
exit(EXIT_FAILURE);
|
|
} else {
|
|
ML_BACKJUMP_CHECKPOINT(""found"", backjump_handler);
|
|
|
|
// XXX we should produce some EXCP trace events here, to give
|
|
// the user an opportunity to retry a goal that calculated a
|
|
// (possibly incorrect) backjump.
|
|
|
|
MR_SET_BACKJUMP_HANDLER(backjump_handler->MR_bjh_prev);
|
|
MR_sp_word = (MR_Word) backjump_handler->MR_bjh_saved_sp;
|
|
MR_maxfr_word = (MR_Word) backjump_handler->MR_bjh_saved_fr;
|
|
MR_fail();
|
|
}
|
|
}
|
|
|
|
MR_END_MODULE
|
|
|
|
#endif // !MR_HIGHLEVEL_CODE
|
|
|
|
/*
|
|
INIT mercury_sys_init_backjumps
|
|
*/
|
|
|
|
void mercury_sys_init_backjumps_init(void)
|
|
{
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
hand_written_backjump_module();
|
|
#endif
|
|
}
|
|
|
|
void mercury_sys_init_backjumps_init_type_tables(void)
|
|
{
|
|
// no types to register
|
|
}
|
|
|
|
#ifdef MR_DEEP_PROFILING
|
|
void
|
|
mercury_sys_init_backjumps_write_out_proc_statics(FILE *deep_fp,
|
|
FILE *procrep_fp)
|
|
{
|
|
MR_write_out_user_proc_static(deep_fp, procrep_fp,
|
|
&MR_proc_layout_user_name(backjump, builtin_choice_id, 1, 0));
|
|
MR_write_out_user_proc_static(deep_fp, procrep_fp,
|
|
&MR_proc_layout_user_name(backjump, builtin_backjump, 1, 0));
|
|
}
|
|
#endif // MR_DEEP_PROFILING
|
|
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_code("C#", "
|
|
|
|
public static void
|
|
builtin_choice_id_1_p_0(object cont, /* env_ptr */ object cont_env_ptr)
|
|
{
|
|
throw new System.Exception(""builtin_choice_id/1 not implemented"");
|
|
}
|
|
|
|
public static void
|
|
builtin_backjump_1_p_0(int Id_2)
|
|
{
|
|
throw new System.Exception(""builtin_backjump/1 not implemented"");
|
|
}
|
|
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_code("Java", "
|
|
|
|
public static void
|
|
builtin_choice_id_1_p_0(jmercury.runtime.MethodPtr cont,
|
|
/* env_ptr */ java.lang.Object cont_env_ptr)
|
|
{
|
|
throw new java.lang.Error(""builtin_choice_id/1 not implemented"");
|
|
}
|
|
|
|
public static void
|
|
builtin_backjump_1_p_0(int Id_2)
|
|
{
|
|
throw new java.lang.Error(""builtin_backjump/1 not implemented"");
|
|
}
|
|
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_export("C", report_invalid_backjump(in, di, uo),
|
|
"ML_report_invalid_backjump").
|
|
|
|
:- pred report_invalid_backjump(int::in, io::di, io::uo) is det.
|
|
|
|
report_invalid_backjump(Id, !IO) :-
|
|
io.output_stream(CurOutStream, !IO),
|
|
io.flush_output(CurOutStream, !IO),
|
|
io.stderr_stream(StdErrStream, !IO),
|
|
io.format(StdErrStream, "Uncaught Mercury backjump (%d)\n", [i(Id)], !IO),
|
|
io.flush_output(StdErrStream, !IO).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
to_int(P) = P.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module backjump.
|
|
%---------------------------------------------------------------------------%
|