mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-12 20:34:19 +00:00
Estimated hours taken: 10 Add support for the MLDS back-end (i.e. the `--high-level-code' option) to various parts of the standard library. library/benchmarking.m: Add `#ifndef MR_HIGHLEVEL_CODE' to ifdef out the parts of `report_stats' which depend on the details of the low-level execution model. Rewrite benchmark_det and benchmark_nondet using impure Mercury with `pragma c_code' fragments, rather than using low-level C code. The low-level C code was a maintenance problem (e.g. I don't think it was restoring the MR_ticket_counter properly in trailing grades) and this way avoids the need to duplicate the hand-written code for the MLDS back-end. library/exception.m: Implement exception handling for the MLDS back-end, using setjmp() and longjmp(). library/math.m: Add `#ifndef MR_HIGHLEVEL_CODE' around the call to MR_dump_stack(), since that code requires the low-level execution model. library/gc.m: Add `#ifndef MR_HIGHLEVEL_CODE' around the calls to MR_clear_zone_for_GC(), since they depend on the details of the low-level execution model and are not required for --high-level-code.
1507 lines
47 KiB
Mathematica
1507 lines
47 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1997-1999 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU Library General
|
|
% Public License - see the file COPYING.LIB in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% File: exception.m.
|
|
% Main author: fjh.
|
|
% Stability: medium
|
|
|
|
% This file defines the Mercury interface for exception handling.
|
|
|
|
% Note that throwing an exception across the C interface won't work.
|
|
% That is, if a Mercury procedure that is exported to C using `pragma export'
|
|
% throws an exception which is not caught within that procedure, then
|
|
% you will get undefined behaviour.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
:- module exception.
|
|
:- interface.
|
|
:- import_module std_util, list, io.
|
|
|
|
%
|
|
% throw(Exception):
|
|
% Throw the specified exception.
|
|
%
|
|
:- pred throw(T).
|
|
:- mode throw(in) is erroneous.
|
|
|
|
:- func throw(T) = _.
|
|
:- mode throw(in) = out is erroneous.
|
|
|
|
% The following types are used by try/3 and try/5.
|
|
|
|
:- type exception_result(T)
|
|
---> succeeded(T)
|
|
; failed
|
|
; exception(univ).
|
|
|
|
:- inst cannot_fail
|
|
---> succeeded(ground)
|
|
; exception(ground).
|
|
|
|
%
|
|
% try(Goal, Result):
|
|
% Operational semantics:
|
|
% Call Goal(R).
|
|
% If Goal(R) fails, succeed with Result = failed.
|
|
% If Goal(R) succeeds, succeed with Result = succeeded(R).
|
|
% If Goal(R) throws an exception E, succeed with Result = exception(E).
|
|
% Declarative semantics:
|
|
% try(Goal, Result) <=>
|
|
% ( Goal(R), Result = succeeded(R)
|
|
% ; not Goal(_), Result = failed
|
|
% ; Result = exception(_)
|
|
% ).
|
|
%
|
|
:- pred try(pred(T), exception_result(T)).
|
|
:- mode try(pred(out) is det, out(cannot_fail)) is cc_multi.
|
|
:- mode try(pred(out) is semidet, out) is cc_multi.
|
|
:- mode try(pred(out) is cc_multi, out(cannot_fail)) is cc_multi.
|
|
:- mode try(pred(out) is cc_nondet, out) is cc_multi.
|
|
|
|
%
|
|
% try_io(Goal, Result, IO_0, IO):
|
|
% Operational semantics:
|
|
% Call Goal(R, IO_0, IO_1).
|
|
% If it succeeds, succeed with Result = succeeded(R) and IO = IO_1.
|
|
% If it throws an exception E, succeed with Result = exception(E)
|
|
% and with the final IO state being whatever state resulted
|
|
% from the partial computation from IO_0.
|
|
% Declarative semantics:
|
|
% try_io(Goal, Result, IO_0, IO) <=>
|
|
% ( Goal(R, IO_0, IO), Result = succeeded(R)
|
|
% ; Result = exception(_)
|
|
% ).
|
|
%
|
|
:- pred try_io(pred(T, io__state, io__state),
|
|
exception_result(T), io__state, io__state).
|
|
:- mode try_io(pred(out, di, uo) is det,
|
|
out(cannot_fail), di, uo) is cc_multi.
|
|
:- mode try_io(pred(out, di, uo) is cc_multi,
|
|
out(cannot_fail), di, uo) is cc_multi.
|
|
|
|
%
|
|
% try_all(Goal, ResultList):
|
|
% Operational semantics:
|
|
% Try to find all solutions to Goal(R), using backtracking.
|
|
% Collect the solutions found in the ResultList, until
|
|
% the goal either throws an exception or fails.
|
|
% If it throws an exception, put that exception at the end of
|
|
% the ResultList.
|
|
% Declaratively:
|
|
% try_all(Goal, ResultList) <=>
|
|
% (if
|
|
% list__reverse(ResultList, [Last | AllButLast]),
|
|
% Last = exception(_)
|
|
% then
|
|
% all [M] (list__member(M, AllButLast) =>
|
|
% (M = succeeded(R), Goal(R))),
|
|
% else
|
|
% all [M] (list__member(M, ResultList) =>
|
|
% (M = succeeded(R), Goal(R))),
|
|
% all [R] (Goal(R) =>
|
|
% list__member(succeeded(R), ResultList)),
|
|
% ).
|
|
|
|
:- pred try_all(pred(T), list(exception_result(T))).
|
|
:- mode try_all(pred(out) is det, out(try_all_det)) is cc_multi.
|
|
:- mode try_all(pred(out) is semidet, out(try_all_semidet)) is cc_multi.
|
|
:- mode try_all(pred(out) is multi, out(try_all_multi)) is cc_multi.
|
|
:- mode try_all(pred(out) is nondet, out(try_all_nondet)) is cc_multi.
|
|
|
|
:- inst [] ---> [].
|
|
:- inst try_all_det ---> [cannot_fail].
|
|
:- inst try_all_semidet ---> [] ; [cannot_fail].
|
|
:- inst try_all_multi ---> [cannot_fail | try_all_nondet].
|
|
:- inst try_all_nondet == list_skel(cannot_fail).
|
|
|
|
%
|
|
% incremental_try_all(Goal, AccumulatorPred, Acc0, Acc):
|
|
% Same as
|
|
% try_all(Goal, Results),
|
|
% std_util__unsorted_aggregate(Results, AccumulatorPred, Acc0, Acc)
|
|
% except that operationally, the execution of try_all
|
|
% and std_util__unsorted_aggregate is interleaved.
|
|
|
|
:- pred incremental_try_all(pred(T), pred(exception_result(T), A, A), A, A).
|
|
:- mode incremental_try_all(pred(out) is nondet,
|
|
pred(in, di, uo) is det, di, uo) is cc_multi.
|
|
:- mode incremental_try_all(pred(out) is nondet,
|
|
pred(in, in, out) is det, in, out) is cc_multi.
|
|
|
|
%
|
|
% rethrow(ExceptionResult):
|
|
% Rethrows the specified exception result
|
|
% (which should be of the form `exception(_)',
|
|
% not `succeeded(_)' or `failed'.).
|
|
%
|
|
:- pred rethrow(exception_result(T)).
|
|
:- mode rethrow(in(bound(exception(ground)))) is erroneous.
|
|
|
|
:- func rethrow(exception_result(T)) = _.
|
|
:- mode rethrow(in(bound(exception(ground)))) = out is erroneous.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
:- import_module string, require.
|
|
|
|
:- pred try(determinism, pred(T), exception_result(T)).
|
|
:- mode try(in(bound(det)), pred(out) is det, out(cannot_fail))
|
|
is cc_multi.
|
|
:- mode try(in(bound(semidet)), pred(out) is semidet, out) is cc_multi.
|
|
:- mode try(in(bound(cc_multi)), pred(out) is cc_multi, out(cannot_fail))
|
|
is cc_multi.
|
|
:- mode try(in(bound(cc_nondet)), pred(out) is cc_nondet, out) is cc_multi.
|
|
|
|
:- pred try_io(determinism, pred(T, io__state, io__state),
|
|
exception_result(T), io__state, io__state).
|
|
:- mode try_io(in(bound(det)), pred(out, di, uo) is det,
|
|
out(cannot_fail), di, uo) is cc_multi.
|
|
:- mode try_io(in(bound(cc_multi)), pred(out, di, uo) is cc_multi,
|
|
out(cannot_fail), di, uo) is cc_multi.
|
|
|
|
:- pred try_all(determinism, pred(T), list(exception_result(T))).
|
|
:- mode try_all(in(bound(det)), pred(out) is det,
|
|
out(try_all_det)) is cc_multi.
|
|
:- mode try_all(in(bound(semidet)), pred(out) is semidet,
|
|
out(try_all_semidet)) is cc_multi.
|
|
:- mode try_all(in(bound(multi)), pred(out) is multi,
|
|
out(try_all_multi)) is cc_multi.
|
|
:- mode try_all(in(bound(nondet)), pred(out) is nondet,
|
|
out(try_all_nondet)) is cc_multi.
|
|
|
|
% The functors in this type must be in the same order as the
|
|
% enumeration constants in the C enum `ML_Determinism' defined below.
|
|
:- type determinism
|
|
---> det
|
|
; semidet
|
|
; cc_multi
|
|
; cc_nondet
|
|
; multi
|
|
; nondet
|
|
; erroneous
|
|
; failure.
|
|
|
|
:- pred get_determinism(pred(T), determinism).
|
|
:- mode get_determinism(pred(out) is det, out(bound(det))) is cc_multi.
|
|
:- mode get_determinism(pred(out) is semidet, out(bound(semidet))) is cc_multi.
|
|
:- mode get_determinism(pred(out) is multi, out(bound(multi))) is cc_multi.
|
|
:- mode get_determinism(pred(out) is nondet, out(bound(nondet))) is cc_multi.
|
|
:- mode get_determinism(pred(out) is cc_multi, out(bound(cc_multi)))
|
|
is cc_multi.
|
|
:- mode get_determinism(pred(out) is cc_nondet, out(bound(cc_nondet)))
|
|
is cc_multi.
|
|
|
|
:- pred get_determinism_2(pred(T, io__state, io__state), determinism).
|
|
:- mode get_determinism_2(pred(out, di, uo) is det, out(bound(det)))
|
|
is cc_multi.
|
|
:- mode get_determinism_2(pred(out, di, uo) is cc_multi, out(bound(cc_multi)))
|
|
is cc_multi.
|
|
|
|
% Unfortunately the only way to implement get_determinism/2 is to use
|
|
% the C interface, since Mercury doesn't allow different code for different
|
|
% modes.
|
|
|
|
% The enumeration constants in this enum must be in the same order as the
|
|
% functors in the Mercury type `determinism' defined above.
|
|
:- pragma c_header_code("
|
|
typedef enum {
|
|
ML_DET,
|
|
ML_SEMIDET,
|
|
ML_CC_MULTI,
|
|
ML_CC_NONDET,
|
|
ML_MULTI,
|
|
ML_NONDET,
|
|
ML_ERRONEOUS,
|
|
ML_FAILURE
|
|
} ML_Determinism;
|
|
").
|
|
|
|
:- pragma c_code(
|
|
get_determinism(_Pred::pred(out) is det,
|
|
Det::out(bound(det))),
|
|
will_not_call_mercury,
|
|
"Det = ML_DET"
|
|
).
|
|
:- pragma c_code(
|
|
get_determinism(_Pred::pred(out) is semidet,
|
|
Det::out(bound(semidet))),
|
|
will_not_call_mercury,
|
|
"Det = ML_SEMIDET"
|
|
).
|
|
:- pragma c_code(
|
|
get_determinism(_Pred::pred(out) is cc_multi,
|
|
Det::out(bound(cc_multi))),
|
|
will_not_call_mercury,
|
|
"Det = ML_CC_MULTI"
|
|
).
|
|
:- pragma c_code(
|
|
get_determinism(_Pred::pred(out) is cc_nondet,
|
|
Det::out(bound(cc_nondet))),
|
|
will_not_call_mercury,
|
|
"Det = ML_CC_NONDET"
|
|
).
|
|
:- pragma c_code(
|
|
get_determinism(_Pred::pred(out) is multi,
|
|
Det::out(bound(multi))),
|
|
will_not_call_mercury,
|
|
"Det = ML_MULTI"
|
|
).
|
|
:- pragma c_code(
|
|
get_determinism(_Pred::pred(out) is nondet,
|
|
Det::out(bound(nondet))),
|
|
will_not_call_mercury,
|
|
"Det = ML_NONDET"
|
|
).
|
|
|
|
:- pragma c_code(
|
|
get_determinism_2(_Pred::pred(out, di, uo) is det,
|
|
Det::out(bound(det))),
|
|
will_not_call_mercury,
|
|
"Det = ML_DET"
|
|
).
|
|
|
|
:- pragma c_code(
|
|
get_determinism_2(_Pred::pred(out, di, uo) is cc_multi,
|
|
Det::out(bound(cc_multi))),
|
|
will_not_call_mercury,
|
|
"Det = ML_CC_MULTI"
|
|
).
|
|
|
|
throw(Exception) :-
|
|
type_to_univ(Exception, Univ),
|
|
builtin_throw(Univ).
|
|
|
|
throw(Exception) = _ :-
|
|
throw(Exception).
|
|
|
|
rethrow(exception(Univ)) :-
|
|
builtin_throw(Univ).
|
|
|
|
rethrow(ExceptionResult) = _ :-
|
|
rethrow(ExceptionResult).
|
|
|
|
:- pred wrap_success(pred(T), exception_result(T)) is det.
|
|
:- mode wrap_success(pred(out) is det, out) is det.
|
|
:- mode wrap_success(pred(out) is semidet, out) is semidet.
|
|
:- mode wrap_success(pred(out) is multi, out) is multi.
|
|
:- mode wrap_success(pred(out) is nondet, out) is nondet.
|
|
:- mode wrap_success(pred(out) is cc_multi, out) is cc_multi.
|
|
:- mode wrap_success(pred(out) is cc_nondet, out) is cc_nondet.
|
|
wrap_success(Goal, succeeded(R)) :- Goal(R).
|
|
|
|
:- pred wrap_success_or_failure(pred(T), exception_result(T)) is det.
|
|
:- mode wrap_success_or_failure(pred(out) is det, out) is det.
|
|
:- mode wrap_success_or_failure(pred(out) is semidet, out) is det.
|
|
:- mode wrap_success_or_failure(pred(out) is multi, out) is multi.
|
|
:- mode wrap_success_or_failure(pred(out) is nondet, out) is multi.
|
|
:- mode wrap_success_or_failure(pred(out) is cc_multi, out) is cc_multi.
|
|
:- mode wrap_success_or_failure(pred(out) is cc_nondet, out) is cc_multi.
|
|
wrap_success_or_failure(Goal, Result) :-
|
|
(if Goal(R) then Result = succeeded(R) else Result = failed).
|
|
|
|
/*********************
|
|
% This doesn't work, due to
|
|
% bash$ mmc exception.m
|
|
% Software error: sorry, not implemented: taking address of pred
|
|
% `wrap_success_or_failure/2' with multiple modes.
|
|
% Instead, we need to switch on the Detism argument.
|
|
|
|
try(_Detism, Goal, Result) :-
|
|
builtin_catch(wrap_success_or_failure(Goal), wrap_exception, Result).
|
|
*********************/
|
|
|
|
try(Goal, Result) :-
|
|
get_determinism(Goal, Detism),
|
|
try(Detism, Goal, Result).
|
|
|
|
try(det, Goal, Result) :-
|
|
builtin_catch((pred(R::out) is det :-
|
|
wrap_success_or_failure(Goal, R)),
|
|
wrap_exception, Result0),
|
|
cc_multi_equal(Result0, Result).
|
|
try(semidet, Goal, Result) :-
|
|
builtin_catch((pred(R::out) is det :-
|
|
wrap_success_or_failure(Goal, R)),
|
|
wrap_exception, Result0),
|
|
cc_multi_equal(Result0, Result).
|
|
try(cc_multi, Goal, Result) :-
|
|
|
|
builtin_catch(
|
|
(pred(R::out) is cc_multi :-
|
|
wrap_success_or_failure(Goal, R)
|
|
),
|
|
wrap_exception, Result).
|
|
try(cc_nondet, Goal, Result) :-
|
|
builtin_catch((pred(R::out) is cc_multi :-
|
|
wrap_success_or_failure(Goal, R)),
|
|
wrap_exception, Result).
|
|
|
|
|
|
/**********
|
|
% This doesn't work, due to
|
|
% bash$ mmc exception.m
|
|
% Software error: sorry, not implemented: taking address of pred
|
|
% `wrap_success_or_failure/2' with multiple modes.
|
|
% Instead, we need to switch on the Detism argument.
|
|
|
|
try_all(Goal, ResultList) :-
|
|
unsorted_solutions(builtin_catch(wrap_success(Goal), wrap_exception),
|
|
ResultList).
|
|
**********/
|
|
|
|
try_all(Goal, ResultList) :-
|
|
get_determinism(Goal, Detism),
|
|
try_all(Detism, Goal, ResultList).
|
|
|
|
try_all(det, Goal, [Result]) :-
|
|
try(det, Goal, Result).
|
|
try_all(semidet, Goal, ResultList) :-
|
|
try(semidet, Goal, Result),
|
|
( Result = failed, ResultList = []
|
|
; Result = succeeded(_), ResultList = [Result]
|
|
; Result = exception(_), ResultList = [Result]
|
|
).
|
|
try_all(multi, Goal, ResultList) :-
|
|
unsorted_solutions((pred(Result::out) is multi :-
|
|
builtin_catch((pred(R::out) is multi :-
|
|
wrap_success(Goal, R)),
|
|
wrap_exception, Result)),
|
|
ResultList).
|
|
try_all(nondet, Goal, ResultList) :-
|
|
unsorted_solutions((pred(Result::out) is nondet :-
|
|
builtin_catch((pred(R::out) is nondet :-
|
|
wrap_success(Goal, R)),
|
|
wrap_exception, Result)),
|
|
ResultList).
|
|
|
|
incremental_try_all(Goal, AccPred, Acc0, Acc) :-
|
|
unsorted_aggregate((pred(Result::out) is nondet :-
|
|
builtin_catch((pred(R::out) is nondet :-
|
|
wrap_success(Goal, R)),
|
|
wrap_exception, Result)),
|
|
AccPred, Acc0, Acc).
|
|
|
|
try_io(IO_Goal, Result) -->
|
|
{ get_determinism_2(IO_Goal, Detism) },
|
|
try_io(Detism, IO_Goal, Result).
|
|
|
|
% We'd better not inline try_io/5, since it uses a horrible hack
|
|
% with unsafe_perform_io (see below) that might confuse the compiler.
|
|
:- pragma no_inline(try_io/5).
|
|
try_io(det, IO_Goal, Result) -->
|
|
{ Goal = (pred(R::out) is det :-
|
|
very_unsafe_perform_io(IO_Goal, R)) },
|
|
{ try(det, Goal, Result) }.
|
|
try_io(cc_multi, IO_Goal, Result) -->
|
|
{ Goal = (pred(R::out) is cc_multi :-
|
|
very_unsafe_perform_io(IO_Goal, R)) },
|
|
{ try(cc_multi, Goal, Result) }.
|
|
|
|
:- pred very_unsafe_perform_io(pred(T, io__state, io__state), T).
|
|
:- mode very_unsafe_perform_io(pred(out, di, uo) is det, out) is det.
|
|
:- mode very_unsafe_perform_io(pred(out, di, uo) is cc_multi, out)
|
|
is det.
|
|
% Mercury doesn't support impure higher-order pred terms, so if we want
|
|
% to form a closure from unsafe_perform_io, as we need to do above,
|
|
% then we must (falsely!) promise that it is pure.
|
|
:- pragma promise_pure(very_unsafe_perform_io/2). % XXX this is a lie
|
|
|
|
very_unsafe_perform_io(Goal, Result) :-
|
|
impure unsafe_perform_io(Goal, Result).
|
|
|
|
:- pred wrap_exception(univ::in, exception_result(T)::out) is det.
|
|
wrap_exception(Exception, exception(Exception)).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred builtin_throw(univ).
|
|
:- mode builtin_throw(in) is erroneous.
|
|
|
|
:- type handler(T) == pred(univ, T).
|
|
:- inst handler == (pred(in, out) is det).
|
|
|
|
%
|
|
% builtin_catch/3 is actually impure. But we don't declare it as impure,
|
|
% because the code for try_all/3 takes its address (to pass to
|
|
% unsorted_solutions/2), and Mercury does not (yet?) support
|
|
% impure higher-order pred terms.
|
|
%
|
|
:- /* impure */
|
|
pred builtin_catch(pred(T), handler(T), T).
|
|
:- mode builtin_catch(pred(out) is det, in(handler), out) is det.
|
|
:- mode builtin_catch(pred(out) is semidet, in(handler), out) is semidet.
|
|
:- mode builtin_catch(pred(out) is cc_multi, in(handler), out) is cc_multi.
|
|
:- mode builtin_catch(pred(out) is cc_nondet, in(handler), out) is cc_nondet.
|
|
:- mode builtin_catch(pred(out) is multi, in(handler), out) is multi.
|
|
:- mode builtin_catch(pred(out) is nondet, in(handler), out) is nondet.
|
|
|
|
% builtin_throw and builtin_catch are implemented below using
|
|
% hand-coded low-level C code.
|
|
|
|
:- external(builtin_throw/1).
|
|
:- external(builtin_catch/3).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% The --high-level-code implementation
|
|
%
|
|
|
|
:- pragma c_header_code("
|
|
#ifdef MR_HIGHLEVEL_CODE
|
|
|
|
/* det ==> model_det */
|
|
#define mercury__exception__builtin_catch_3_p_0 \
|
|
mercury__exception__builtin_catch_model_det
|
|
|
|
/* semidet ==> model_semi */
|
|
#define mercury__exception__builtin_catch_3_p_1 \
|
|
mercury__exception__builtin_catch_model_semi
|
|
|
|
/* cc_multi ==> model_det */
|
|
#define mercury__exception__builtin_catch_3_p_2 \
|
|
mercury__exception__builtin_catch_model_det
|
|
|
|
/* cc_nondet ==> model_semi */
|
|
#define mercury__exception__builtin_catch_3_p_3 \
|
|
mercury__exception__builtin_catch_model_semi
|
|
|
|
/* multi ==> model_non */
|
|
#define mercury__exception__builtin_catch_3_p_4 \
|
|
mercury__exception__builtin_catch_model_non
|
|
|
|
/* nondet ==> model_non */
|
|
#define mercury__exception__builtin_catch_3_p_5 \
|
|
mercury__exception__builtin_catch_model_non
|
|
|
|
void mercury__exception__builtin_throw_1_p_0(MR_Word);
|
|
|
|
void mercury__exception__builtin_throw_1_p_0(MR_Word exception);
|
|
void mercury__exception__builtin_catch_model_det(MR_Word type_info,
|
|
MR_Word pred, MR_Word handler_pred, MR_Box *output);
|
|
bool mercury__exception__builtin_catch_model_semi(MR_Word type_info,
|
|
MR_Word pred, MR_Word handler_pred, MR_Box *output);
|
|
void mercury__exception__builtin_catch_model_non(MR_Word type_info,
|
|
MR_Word pred, MR_Word handler_pred, MR_Box *output,
|
|
#ifdef MR_USE_GCC_NESTED_FUNCTIONS
|
|
MR_NestedCont cont
|
|
#else
|
|
MR_Cont cont, void *cont_env
|
|
#endif
|
|
);
|
|
#endif /* MR_HIGHLEVEL_CODE */
|
|
").
|
|
|
|
:- pragma c_code("
|
|
#ifdef MR_HIGHLEVEL_CODE
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static void
|
|
ML_call_goal_det(MR_Word type_info, MR_Word closure, MR_Box *result)
|
|
{
|
|
typedef void FuncType(void *, MR_Box *);
|
|
FuncType *code = (FuncType *)
|
|
MR_field(MR_mktag(0), closure, (Integer) 1);
|
|
(*code)((void *) closure, result);
|
|
}
|
|
|
|
static bool
|
|
ML_call_goal_semi(MR_Word type_info, MR_Word closure, MR_Box *result)
|
|
{
|
|
typedef bool FuncType(void *, MR_Box *);
|
|
FuncType *code = (FuncType *)
|
|
MR_field(MR_mktag(0), closure, (Integer) 1);
|
|
return (*code)((void *) closure, result);
|
|
}
|
|
|
|
#ifdef MR_USE_GCC_NESTED_FUNCTIONS
|
|
|
|
static void
|
|
ML_call_goal_non(MR_Word type_info, MR_Word closure, MR_Box *result,
|
|
MR_NestedCont cont)
|
|
{
|
|
typedef void FuncType(void *, MR_Box *, MR_NestedCont);
|
|
FuncType *code = (FuncType *)
|
|
MR_field(MR_mktag(0), closure, (Integer) 1);
|
|
(*code)((void *) closure, result, cont);
|
|
}
|
|
|
|
#else
|
|
|
|
static void
|
|
ML_call_goal_non(MR_Word type_info, MR_Word closure, MR_Box *result,
|
|
MR_Cont cont, void *cont_env)
|
|
{
|
|
typedef void FuncType(void *, MR_Box *, MR_Cont, void *);
|
|
FuncType *code = (FuncType *)
|
|
MR_field(MR_mktag(0), closure, (Integer) 1);
|
|
(*code)((void *) closure, result, cont, cont_env);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static void
|
|
ML_call_handler_det(MR_Word type_info, MR_Word closure, MR_Word exception,
|
|
MR_Box *result)
|
|
{
|
|
typedef void FuncType(void *, MR_Box, MR_Box *);
|
|
FuncType *code = (FuncType *)
|
|
MR_field(MR_mktag(0), closure, (Integer) 1);
|
|
(*code)((void *) closure, exception, result);
|
|
}
|
|
|
|
static bool
|
|
ML_call_handler_semi(MR_Word type_info, MR_Word closure, MR_Word exception,
|
|
MR_Box *result)
|
|
{
|
|
typedef bool FuncType(void *, MR_Box, MR_Box *);
|
|
FuncType *code = (FuncType *)
|
|
MR_field(MR_mktag(0), closure, (Integer) 1);
|
|
return (*code)((void *) closure, exception, result);
|
|
}
|
|
|
|
#ifdef MR_USE_GCC_NESTED_FUNCTIONS
|
|
|
|
static void
|
|
ML_call_handler_non(MR_Word type_info, MR_Word closure, MR_Word exception,
|
|
MR_Box *result, MR_NestedCont cont)
|
|
{
|
|
typedef void FuncType(void *, MR_Box, MR_Box *, MR_NestedCont);
|
|
FuncType *code = (FuncType *)
|
|
MR_field(MR_mktag(0), closure, (Integer) 1);
|
|
(*code)((void *) closure, exception, result, cont);
|
|
}
|
|
|
|
#else
|
|
|
|
static void
|
|
ML_call_handler_non(MR_Word type_info, MR_Word closure, MR_Word exception,
|
|
MR_Box *result, MR_Cont cont, void *cont_env)
|
|
{
|
|
typedef void FuncType(void *, MR_Box, MR_Box *, MR_Cont, void *);
|
|
FuncType *code = (FuncType *)
|
|
MR_field(MR_mktag(0), closure, (Integer) 1);
|
|
(*code)((void *) closure, exception, result, cont, cont_env);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#include <stdlib.h>
|
|
#include <setjmp.h>
|
|
|
|
typedef MR_Word MR_Univ;
|
|
|
|
typedef struct ML_ExceptionHandler_struct {
|
|
struct ML_ExceptionHandler_struct *prev;
|
|
jmp_buf handler;
|
|
MR_Univ exception;
|
|
} ML_ExceptionHandler;
|
|
|
|
ML_ExceptionHandler *ML_exception_handler;
|
|
|
|
void
|
|
mercury__exception__builtin_throw_1_p_0(MR_Univ exception)
|
|
{
|
|
if (ML_exception_handler->handler == NULL) {
|
|
ML_report_uncaught_exception(exception);
|
|
abort();
|
|
} else {
|
|
ML_exception_handler->exception = exception;
|
|
longjmp(ML_exception_handler->handler, 1);
|
|
}
|
|
}
|
|
|
|
void
|
|
mercury__exception__builtin_catch_model_det(MR_Word type_info,
|
|
MR_Word pred, MR_Word handler_pred, MR_Box *output)
|
|
{
|
|
ML_ExceptionHandler this_handler;
|
|
|
|
this_handler.prev = ML_exception_handler;
|
|
ML_exception_handler = &this_handler;
|
|
if (setjmp(this_handler.handler) == 0) {
|
|
ML_call_goal_det(type_info, pred, output);
|
|
ML_exception_handler = this_handler.prev;
|
|
} else {
|
|
ML_exception_handler = this_handler.prev;
|
|
ML_call_handler_det(type_info, handler_pred,
|
|
this_handler.exception, output);
|
|
}
|
|
}
|
|
|
|
bool
|
|
mercury__exception__builtin_catch_model_semi(MR_Word type_info,
|
|
MR_Word pred, MR_Word handler_pred, MR_Box *output)
|
|
{
|
|
ML_ExceptionHandler this_handler;
|
|
|
|
this_handler.prev = ML_exception_handler;
|
|
ML_exception_handler = &this_handler;
|
|
if (setjmp(this_handler.handler) == 0) {
|
|
bool result = ML_call_goal_semi(type_info, pred, output);
|
|
ML_exception_handler = this_handler.prev;
|
|
return result;
|
|
} else {
|
|
ML_exception_handler = this_handler.prev;
|
|
return ML_call_handler_semi(type_info, handler_pred,
|
|
this_handler.exception, output);
|
|
}
|
|
}
|
|
|
|
#ifdef MR_USE_GCC_NESTED_FUNCTIONS
|
|
|
|
void
|
|
mercury__exception__builtin_catch_model_non(MR_Word type_info,
|
|
MR_Word pred, MR_Word handler_pred, MR_Box *output,
|
|
MR_NestedCont cont)
|
|
{
|
|
ML_ExceptionHandler this_handler;
|
|
|
|
auto void success_cont(void);
|
|
void success_cont(void) {
|
|
/*
|
|
** If we reach here, it means that
|
|
** the nondet goal has succeeded, so we
|
|
** need to restore the previous exception
|
|
** handler before calling its continuation
|
|
*/
|
|
ML_exception_handler = this_handler.prev;
|
|
(*cont)();
|
|
|
|
/*
|
|
** If we get here, it means that the continuation
|
|
** has failed, and so we are about to redo the
|
|
** nondet goal. Thus we need to re-establish
|
|
** its exception handler.
|
|
*/
|
|
ML_exception_handler = &this_handler;
|
|
}
|
|
|
|
this_handler.prev = ML_exception_handler;
|
|
ML_exception_handler = &this_handler;
|
|
if (setjmp(this_handler.handler) == 0) {
|
|
ML_call_goal_non(type_info, pred, output, success_cont);
|
|
ML_exception_handler = this_handler.prev;
|
|
} else {
|
|
ML_exception_handler = this_handler.prev;
|
|
ML_call_handler_non(type_info, handler_pred,
|
|
this_handler.exception, output, cont);
|
|
}
|
|
}
|
|
|
|
#else /* ! MR_USE_GCC_NESTED_FUNCTIONS */
|
|
|
|
struct ML_catch_env {
|
|
ML_ExceptionHandler this_handler;
|
|
MR_Cont cont;
|
|
void *cont_env;
|
|
};
|
|
|
|
static void
|
|
ML_catch_success_cont(void *env_ptr) {
|
|
struct ML_catch_env *env = (struct ML_catch_env *) env_ptr;
|
|
|
|
/*
|
|
** If we reach here, it means that
|
|
** the nondet goal has succeeded, so we
|
|
** need to restore the previous exception
|
|
** handler before calling its continuation
|
|
*/
|
|
ML_exception_handler = env->this_handler.prev;
|
|
(*env->cont)(env->cont_env);
|
|
|
|
/*
|
|
** If we get here, it means that the continuation
|
|
** has failed, and so we are about to redo the
|
|
** nondet goal. Thus we need to re-establish
|
|
** its exception handler.
|
|
*/
|
|
ML_exception_handler = &env->this_handler;
|
|
}
|
|
|
|
void
|
|
mercury__exception__builtin_catch_model_non(MR_Word type_info,
|
|
MR_Word pred, MR_Word handler_pred, MR_Box *output,
|
|
MR_Cont cont, void *cont_env)
|
|
{
|
|
struct ML_catch_env locals;
|
|
locals.cont = cont;
|
|
locals.cont_env = cont_env;
|
|
|
|
locals.this_handler.prev = ML_exception_handler;
|
|
ML_exception_handler = &locals.this_handler;
|
|
if (setjmp(locals.this_handler.handler) == 0) {
|
|
ML_call_goal_non(type_info, pred, output,
|
|
ML_catch_success_cont, &locals);
|
|
/*
|
|
** If we reach here, it means that
|
|
** the nondet goal has failed, so we
|
|
** need to restore the previous exception
|
|
** handler
|
|
*/
|
|
ML_exception_handler = locals.this_handler.prev;
|
|
return;
|
|
} else {
|
|
/*
|
|
** We caught an exception.
|
|
** Restore the previous exception handler,
|
|
** and then invoke the handler predicate
|
|
** for this handler.
|
|
*/
|
|
ML_exception_handler = locals.this_handler.prev;
|
|
ML_call_handler_non(type_info, handler_pred,
|
|
locals.this_handler.exception, output,
|
|
cont, cont_env);
|
|
}
|
|
}
|
|
|
|
#endif /* ! MR_USE_GCC_NESTED_FUNCTIONS */
|
|
|
|
#endif /* MR_HIGHLEVEL_CODE */
|
|
").
|
|
|
|
/*********
|
|
This causes problems because the LLDS back-end
|
|
does not let you export code with determinism `nondet'.
|
|
Instead we handle-code it... see below.
|
|
|
|
:- pred call_goal(pred(T), T).
|
|
:- mode call_goal(pred(out) is det, out) is det.
|
|
:- mode call_goal(pred(out) is semidet, out) is semidet.
|
|
:- mode call_goal(pred(out) is nondet, out) is nondet.
|
|
|
|
call_goal(Goal, Result) :- Goal(Result).
|
|
|
|
:- pred call_handler(pred(univ, T), univ, T).
|
|
:- mode call_handler(pred(in, out) is det, in, out) is det.
|
|
:- mode call_handler(pred(in, out) is semidet, in, out) is semidet.
|
|
:- mode call_handler(pred(in, out) is nondet, in, out) is nondet.
|
|
|
|
call_handler(Handler, Exception, Result) :- Handler(Exception, Result).
|
|
|
|
:- pragma export(call_goal(pred(out) is det, out), "ML_call_goal_det").
|
|
:- pragma export(call_goal(pred(out) is semidet, out), "ML_call_goal_semidet").
|
|
% :- pragma export(call_goal(pred(out) is nondet, out), "ML_call_goal_nondet").
|
|
|
|
:- pragma export(call_handler(pred(in, out) is det, in, out),
|
|
"ML_call_handler_det").
|
|
:- pragma export(call_handler(pred(in, out) is semidet, in, out),
|
|
"ML_call_handler_semidet").
|
|
% :- pragma export(call_handler(pred(in, out) is nondet, in, out),
|
|
% "ML_call_handler_nondet").
|
|
|
|
*******/
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% The --no-high-level-code implementation
|
|
%
|
|
|
|
:- pragma c_header_code("
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include ""mercury_deep_copy.h""
|
|
#include ""mercury_trace_base.h""
|
|
#include ""mercury_stack_trace.h""
|
|
#include ""mercury_layout_util.h""
|
|
|
|
MR_DECLARE_TYPE_CTOR_INFO_STRUCT( \
|
|
mercury_data_std_util__type_ctor_info_univ_0);
|
|
#endif
|
|
").
|
|
|
|
:- pragma c_code("
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
|
|
/*
|
|
** MR_trace_throw():
|
|
** Unwind the stack as far as possible, until we reach a frame
|
|
** with an exception handler. As we go, invoke
|
|
** `MR_trace(..., MR_PORT_EXCEPTION, ...)' for each stack frame,
|
|
** to signal to the debugger that that procedure has exited via
|
|
** an exception. This allows to user to use the `retry' command
|
|
** to restart a goal which exited via an exception.
|
|
**
|
|
** Note that if MR_STACK_TRACE is not defined, then we may not be
|
|
** able to traverse the stack all the way; in that case, we just
|
|
** print a warning and then continue. It might be better to just
|
|
** `#ifdef' out all this code (and the code in builtin_throw which
|
|
** calls it) if MR_STACK_TRACE is not defined.
|
|
*/
|
|
|
|
#define WARNING(msg) \\
|
|
fprintf(stderr, ""mdb: warning: %s\\n"" \\
|
|
""This may result in some exception events\\n"" \\
|
|
""being omitted from the trace.\\n"", (msg))
|
|
|
|
static Code *
|
|
MR_trace_throw(Code *success_pointer, Word *det_stack_pointer,
|
|
Word *current_frame)
|
|
{
|
|
const MR_Internal *label;
|
|
const MR_Stack_Layout_Label *return_label_layout;
|
|
|
|
/*
|
|
** Find the layout info for the stack frame pointed to by MR_succip
|
|
*/
|
|
label = MR_lookup_internal_by_addr(success_pointer);
|
|
if (label == NULL) {
|
|
WARNING(""internal label not found\\n"");
|
|
return NULL;
|
|
}
|
|
return_label_layout = label->i_layout;
|
|
|
|
while (return_label_layout != NULL) {
|
|
const MR_Stack_Layout_Entry *entry_layout;
|
|
Code *MR_jumpaddr;
|
|
MR_Stack_Walk_Step_Result result;
|
|
const char *problem;
|
|
|
|
/*
|
|
** check if we've reached a frame with an exception handler
|
|
*/
|
|
entry_layout = return_label_layout->MR_sll_entry;
|
|
if (!MR_DETISM_DET_STACK(entry_layout->MR_sle_detism)
|
|
&& MR_redoip_slot(current_frame) ==
|
|
ENTRY(exception_handler_do_fail))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
** invoke MR_trace() to trace the exception
|
|
*/
|
|
if (return_label_layout->MR_sll_port != MR_PORT_EXCEPTION) {
|
|
fatal_error(""return layout port is not exception"");
|
|
}
|
|
|
|
MR_jumpaddr = MR_trace(return_label_layout);
|
|
if (MR_jumpaddr != NULL) {
|
|
return MR_jumpaddr;
|
|
}
|
|
|
|
/*
|
|
** unwind the stacks back to the previous stack frame
|
|
*/
|
|
result = MR_stack_walk_step(entry_layout, &return_label_layout,
|
|
&det_stack_pointer, ¤t_frame, &problem);
|
|
if (result != STEP_OK) {
|
|
WARNING(problem);
|
|
return NULL;
|
|
}
|
|
restore_transient_registers();
|
|
MR_sp = det_stack_pointer;
|
|
MR_curfr = current_frame;
|
|
save_transient_registers();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* swap the heap with the solutions heap */
|
|
#define swap_heaps() \\
|
|
{ \\
|
|
/* save the current heap */ \\
|
|
Word *swap_heaps_temp_hp = MR_hp; \\
|
|
MemoryZone *swap_heaps_temp_hp_zone = MR_heap_zone; \\
|
|
\\
|
|
/* set heap to solutions heap */ \\
|
|
MR_hp = MR_sol_hp; \\
|
|
MR_heap_zone = MR_solutions_heap_zone; \\
|
|
\\
|
|
/* set the solutions heap to be the old heap */ \\
|
|
MR_sol_hp = swap_heaps_temp_hp; \\
|
|
MR_solutions_heap_zone = swap_heaps_temp_hp_zone; \\
|
|
}
|
|
|
|
Define_extern_entry(mercury__exception__builtin_catch_3_0); /* det */
|
|
Define_extern_entry(mercury__exception__builtin_catch_3_1); /* semidet */
|
|
Define_extern_entry(mercury__exception__builtin_catch_3_2); /* cc_multi */
|
|
Define_extern_entry(mercury__exception__builtin_catch_3_3); /* cc_nondet */
|
|
Define_extern_entry(mercury__exception__builtin_catch_3_4); /* multi */
|
|
Define_extern_entry(mercury__exception__builtin_catch_3_5); /* nondet */
|
|
|
|
Define_extern_entry(mercury__exception__builtin_throw_1_0);
|
|
|
|
/* the following is defined in runtime/mercury_ho_call.c */
|
|
Declare_entry(mercury__do_call_closure);
|
|
|
|
/* the following is defined in runtime/mercury_trace_base.c */
|
|
Declare_entry(MR_do_trace_redo_fail);
|
|
|
|
Declare_label(mercury__exception__builtin_catch_3_2_i2);
|
|
Declare_label(mercury__exception__builtin_catch_3_3_i2);
|
|
Declare_label(mercury__exception__builtin_catch_3_5_i2);
|
|
#ifdef MR_USE_TRAIL
|
|
Declare_label(mercury__exception__builtin_catch_3_5_i3);
|
|
#endif
|
|
Declare_label(mercury__exception__builtin_throw_1_0_i1);
|
|
|
|
#define BUILTIN_THROW_STACK_SIZE 1
|
|
|
|
|
|
/*
|
|
** MR_MAKE_PROC_LAYOUT(entry, detism, slots, succip_locn, pred_or_func,
|
|
** module, name, arity, mode)
|
|
*/
|
|
|
|
MR_MAKE_PROC_LAYOUT(mercury__exception__builtin_throw_1_0,
|
|
MR_DETISM_DET, BUILTIN_THROW_STACK_SIZE, MR_LONG_LVAL_STACKVAR(1),
|
|
MR_PREDICATE, ""exception"", ""builtin_throw"", 1, 0);
|
|
MR_MAKE_INTERNAL_LAYOUT(mercury__exception__builtin_throw_1_0, 1);
|
|
|
|
/*
|
|
** The following procedures all allocate their stack frames on
|
|
** the nondet stack, so for the purposes of doing stack traces
|
|
** we say they have MR_DETISM_NON, even though they are not
|
|
** actually nondet.
|
|
*/
|
|
MR_MAKE_PROC_LAYOUT(mercury__exception__builtin_catch_3_2,
|
|
MR_DETISM_NON, /* really cc_multi; also used for det */
|
|
MR_ENTRY_NO_SLOT_COUNT, MR_LONG_LVAL_TYPE_UNKNOWN,
|
|
MR_PREDICATE, ""exception"", ""builtin_catch"", 3, 2);
|
|
MR_MAKE_PROC_LAYOUT(mercury__exception__builtin_catch_3_3,
|
|
MR_DETISM_NON, /* really cc_nondet; also used for semidet */
|
|
MR_ENTRY_NO_SLOT_COUNT, MR_LONG_LVAL_TYPE_UNKNOWN,
|
|
MR_PREDICATE, ""exception"", ""builtin_catch"", 3, 3);
|
|
MR_MAKE_PROC_LAYOUT(mercury__exception__builtin_catch_3_5,
|
|
MR_DETISM_NON, /* ; also used for multi */
|
|
MR_ENTRY_NO_SLOT_COUNT, MR_LONG_LVAL_TYPE_UNKNOWN,
|
|
MR_PREDICATE, ""exception"", ""builtin_catch"", 3, 5);
|
|
|
|
MR_MAKE_INTERNAL_LAYOUT(mercury__exception__builtin_catch_3_2, 1);
|
|
MR_MAKE_INTERNAL_LAYOUT(mercury__exception__builtin_catch_3_2, 2);
|
|
MR_MAKE_INTERNAL_LAYOUT(mercury__exception__builtin_catch_3_3, 1);
|
|
MR_MAKE_INTERNAL_LAYOUT(mercury__exception__builtin_catch_3_3, 2);
|
|
MR_MAKE_INTERNAL_LAYOUT(mercury__exception__builtin_catch_3_5, 1);
|
|
MR_MAKE_INTERNAL_LAYOUT(mercury__exception__builtin_catch_3_5, 2);
|
|
#ifdef MR_USE_TRAIL
|
|
MR_MAKE_INTERNAL_LAYOUT(mercury__exception__builtin_catch_3_5, 3);
|
|
#endif
|
|
|
|
BEGIN_MODULE(exceptions_module)
|
|
init_entry(mercury__exception__builtin_catch_3_0);
|
|
init_entry(mercury__exception__builtin_catch_3_1);
|
|
init_entry_sl(mercury__exception__builtin_catch_3_2);
|
|
init_entry_sl(mercury__exception__builtin_catch_3_3);
|
|
init_entry(mercury__exception__builtin_catch_3_4);
|
|
init_entry_sl(mercury__exception__builtin_catch_3_5);
|
|
init_label_sl(mercury__exception__builtin_catch_3_2_i2);
|
|
init_label_sl(mercury__exception__builtin_catch_3_3_i2);
|
|
init_label_sl(mercury__exception__builtin_catch_3_5_i2);
|
|
#ifdef MR_USE_TRAIL
|
|
init_label(mercury__exception__builtin_catch_3_5_i3);
|
|
#endif
|
|
init_entry(mercury__exception__builtin_throw_1_0);
|
|
init_label(mercury__exception__builtin_throw_1_0_i1);
|
|
BEGIN_CODE
|
|
|
|
/*
|
|
** builtin_catch(Goal, Handler, Result)
|
|
** call Goal(R).
|
|
** if succeeds, set Result = R.
|
|
** if throws an exception, call Handler(Exception, Result).
|
|
**
|
|
** This is the model_det version.
|
|
** On entry, we have a type_info (which we don't use) in r1,
|
|
** the Goal to execute in r2 and the Handler in r3.
|
|
** On exit, we should put Result in r1.
|
|
*/
|
|
Define_entry(mercury__exception__builtin_catch_3_0); /* det */
|
|
#ifdef PROFILE_CALLS
|
|
{
|
|
tailcall(ENTRY(mercury__exception__builtin_catch_3_2),
|
|
ENTRY(mercury__exception__builtin_catch_3_0));
|
|
}
|
|
#endif
|
|
Define_entry(mercury__exception__builtin_catch_3_2); /* cc_multi */
|
|
/*
|
|
** Create an exception handler entry on the nondet stack.
|
|
** (Register r3 holds the Handler closure.)
|
|
*/
|
|
MR_create_exception_handler(""builtin_catch/3 [model_det]"",
|
|
MR_MODEL_DET_HANDLER, r3, ENTRY(do_fail));
|
|
|
|
/*
|
|
** Now call `Goal(Result)'.
|
|
*/
|
|
r1 = r2; /* The Goal to call */
|
|
r2 = 0; /* Zero additional input arguments */
|
|
r3 = 1; /* One output argument */
|
|
call(ENTRY(mercury__do_call_closure),
|
|
LABEL(mercury__exception__builtin_catch_3_2_i2),
|
|
ENTRY(mercury__exception__builtin_catch_3_2));
|
|
|
|
Define_label(mercury__exception__builtin_catch_3_2_i2);
|
|
update_prof_current_proc(LABEL(mercury__exception__builtin_catch_3_2));
|
|
/*
|
|
** On exit from mercury__do_call_closure, Result is in r1
|
|
**
|
|
** We must now deallocate the ticket and nondet stack frame that
|
|
** were allocated by MR_create_exception_handler().
|
|
*/
|
|
#ifdef MR_USE_TRAIL
|
|
MR_discard_ticket();
|
|
#endif
|
|
MR_succeed_discard();
|
|
|
|
/*
|
|
** builtin_catch(Goal, Handler, Result)
|
|
** call Goal(R).
|
|
** if succeeds, set Result = R.
|
|
** if fails, fail.
|
|
** if throws an exception, call Handler(Exception, Result).
|
|
**
|
|
** This is the model_semi version.
|
|
** On entry, we have a type_info (which we don't use) in r1,
|
|
** the Goal to execute in r2 and the Handler in r3,
|
|
** and on exit, we should put Result in r2.
|
|
*/
|
|
Define_entry(mercury__exception__builtin_catch_3_1); /* semidet */
|
|
#ifdef PROFILE_CALLS
|
|
{
|
|
tailcall(ENTRY(mercury__exception__builtin_catch_3_3),
|
|
ENTRY(mercury__exception__builtin_catch_3_1));
|
|
}
|
|
#endif
|
|
Define_entry(mercury__exception__builtin_catch_3_3); /* cc_nondet */
|
|
/*
|
|
** Create an exception handler entry on the nondet stack.
|
|
** (Register r3 holds the Handler closure.)
|
|
*/
|
|
MR_create_exception_handler(""builtin_catch/3 [model_semi]"",
|
|
MR_MODEL_SEMI_HANDLER, r3, ENTRY(do_fail));
|
|
|
|
/*
|
|
** Now call `Goal(Result)'.
|
|
*/
|
|
r1 = r2; /* The Goal to call */
|
|
r2 = 0; /* Zero additional input arguments */
|
|
r3 = 1; /* One output argument */
|
|
call(ENTRY(mercury__do_call_closure),
|
|
LABEL(mercury__exception__builtin_catch_3_3_i2),
|
|
ENTRY(mercury__exception__builtin_catch_3_3));
|
|
|
|
Define_label(mercury__exception__builtin_catch_3_3_i2);
|
|
update_prof_current_proc(LABEL(mercury__exception__builtin_catch_3_3));
|
|
/*
|
|
** On exit from do_call_semidet_closure, the success/failure
|
|
** indicator is in r1, and Result is in r2.
|
|
** Note that we call succeed_discard() to exit regardless
|
|
** of whether r1 is true or false. We just return the r1 value
|
|
** back to our caller.
|
|
*/
|
|
#ifdef MR_USE_TRAIL
|
|
MR_discard_ticket();
|
|
#endif
|
|
MR_succeed_discard();
|
|
|
|
/*
|
|
** builtin_catch(Goal, Handler, Result)
|
|
** call Goal(R).
|
|
** if succeeds, set Result = R.
|
|
** if fails, fail.
|
|
** if throws an exception, call Handler(Exception, Result).
|
|
**
|
|
** This is the model_non version.
|
|
** On entry, we have a type_info (which we don't use) in r1,
|
|
** the Goal to execute in r2 and the Handler in r3.
|
|
** On exit, we should put Result in r1.
|
|
*/
|
|
Define_entry(mercury__exception__builtin_catch_3_4); /* multi */
|
|
#ifdef PROFILE_CALLS
|
|
{
|
|
tailcall(ENTRY(mercury__exception__builtin_catch_3_5),
|
|
ENTRY(mercury__exception__builtin_catch_3_4));
|
|
}
|
|
#endif
|
|
Define_entry(mercury__exception__builtin_catch_3_5); /* nondet */
|
|
/*
|
|
** Create an exception handler entry on the nondet stack.
|
|
** (Register r3 holds the Handler closure.)
|
|
*/
|
|
#ifdef MR_USE_TRAIL
|
|
MR_create_exception_handler(""builtin_catch/3 [model_nondet]"",
|
|
MR_MODEL_NON_HANDLER, r3,
|
|
LABEL(mercury__exception__builtin_catch_3_5_i3));
|
|
#else
|
|
MR_create_exception_handler(""builtin_catch/3 [model_nondet]"",
|
|
MR_MODEL_NON_HANDLER, r3, ENTRY(do_fail));
|
|
#endif
|
|
|
|
|
|
/*
|
|
** Now call `Goal(Result)'.
|
|
*/
|
|
r1 = r2; /* the Goal to call */
|
|
r2 = 0; /* Zero additional input arguments */
|
|
r3 = 1; /* One output argument */
|
|
call(ENTRY(mercury__do_call_closure),
|
|
LABEL(mercury__exception__builtin_catch_3_5_i2),
|
|
ENTRY(mercury__exception__builtin_catch_3_5));
|
|
|
|
Define_label(mercury__exception__builtin_catch_3_5_i2);
|
|
update_prof_current_proc(LABEL(mercury__exception__builtin_catch_3_5));
|
|
/*
|
|
** On exit from do_call_nondet_closure, Result is in r1
|
|
**
|
|
** Note that we need to keep the trail ticket still,
|
|
** in case it is needed again on backtracking.
|
|
** We can only discard it when we MR_fail() out, or
|
|
** (if an exception is thrown) in the throw.
|
|
*/
|
|
MR_succeed();
|
|
|
|
#ifdef MR_USE_TRAIL
|
|
Define_label(mercury__exception__builtin_catch_3_5_i3);
|
|
MR_discard_ticket();
|
|
MR_fail();
|
|
#endif
|
|
|
|
/*
|
|
** builtin_throw(Exception):
|
|
** Throw the specified exception.
|
|
** That means unwinding the nondet stack until we find a handler,
|
|
** unwinding all the other Mercury stacks, and then
|
|
** calling longjmp() to unwind the C stack.
|
|
** The longjmp() will branch to builtin_catch which will then
|
|
** call Handler(Exception, Result).
|
|
**
|
|
** On entry, we have Exception in r1.
|
|
*/
|
|
Define_entry(mercury__exception__builtin_throw_1_0);
|
|
{
|
|
Word exception = r1;
|
|
Word handler;
|
|
enum MR_HandlerCodeModel catch_code_model;
|
|
Word *orig_curfr;
|
|
Unsigned exception_event_number = MR_trace_event_number;
|
|
|
|
/*
|
|
** let the debugger trace exception throwing
|
|
*/
|
|
if (MR_trace_enabled) {
|
|
Code *MR_jumpaddr;
|
|
save_transient_registers();
|
|
MR_jumpaddr = MR_trace_throw(MR_succip, MR_sp, MR_curfr);
|
|
restore_transient_registers();
|
|
if (MR_jumpaddr != NULL) GOTO(MR_jumpaddr);
|
|
}
|
|
|
|
/*
|
|
** Search the nondet stack for an exception handler,
|
|
** i.e. a frame whose redoip is `exception_handler_do_fail'
|
|
** (one created by `builtin_catch').
|
|
** N.B. We search down the `succfr' chain, not the `prevfr' chain;
|
|
** this ensures that we only find handlers installed by our callers,
|
|
** not handlers installed by procedures that we called but which
|
|
** are still on the nondet stack because they left choice points
|
|
** behind.
|
|
*/
|
|
orig_curfr = MR_curfr;
|
|
while (MR_redoip_slot(MR_curfr) != ENTRY(exception_handler_do_fail)) {
|
|
MR_curfr = MR_succfr_slot(MR_curfr);
|
|
if (MR_curfr < MR_CONTEXT(nondetstack_zone)->min) {
|
|
Word *save_succip;
|
|
/*
|
|
** There was no exception handler.
|
|
**
|
|
** We restore the original value of MR_curfr,
|
|
** print out some diagnostics,
|
|
** and then terminate execution.
|
|
**
|
|
** We need to save the registers to the fake_reg
|
|
** array using save_registers() before calling
|
|
** ML_report_uncaught_exception, since that is
|
|
** Mercury code and the C->Mercury interface expects
|
|
** the registers to be saved.
|
|
** We also need to save & restore the MR_succip
|
|
** across that call, since any call to Mercury code
|
|
** may clobber MR_succip (and also the Mercury
|
|
** registers r1, r2, r3, etc., but for those we don't
|
|
** care, since we don't use them).
|
|
** Note that the save_registers() alone is not
|
|
** sufficient since the Mercury code may clobber the
|
|
** copy of MR_succip in the fake_reg.
|
|
*/
|
|
MR_curfr = orig_curfr;
|
|
fflush(stdout);
|
|
save_succip = MR_succip;
|
|
save_registers();
|
|
ML_report_uncaught_exception(exception);
|
|
MR_succip = save_succip;
|
|
MR_trace_report(stderr);
|
|
if (exception_event_number > 0) {
|
|
fprintf(stderr, ""Last trace event before ""
|
|
""the unhandled exception was ""
|
|
""event #%ld.\\n"",
|
|
(long) exception_event_number);
|
|
}
|
|
if (MR_trace_enabled) {
|
|
/*
|
|
** The stack has already been unwound
|
|
** by MR_trace_throw(), so we can't dump it.
|
|
** (In fact, if we tried to dump the now-empty
|
|
** stack, we'd get incorrect results, since
|
|
** MR_trace_throw() does not restore MR_succip
|
|
** to the appropriate value.)
|
|
*/
|
|
} else {
|
|
MR_dump_stack(MR_succip, MR_sp, MR_curfr,
|
|
FALSE);
|
|
}
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Save the handler we found
|
|
*/
|
|
catch_code_model = MR_EXCEPTION_FRAMEVARS->code_model;
|
|
handler = MR_EXCEPTION_FRAMEVARS->handler;
|
|
|
|
/*
|
|
** Reset the success ip (i.e. return address).
|
|
** This ensures that when we return from this procedure,
|
|
** we will return to the caller of `builtin_catch'.
|
|
*/
|
|
MR_succip = MR_succip_slot(MR_curfr);
|
|
|
|
/*
|
|
** Reset the det stack.
|
|
*/
|
|
MR_sp = MR_EXCEPTION_FRAMEVARS->stack_ptr;
|
|
|
|
#ifdef MR_USE_TRAIL
|
|
/*
|
|
** Reset the trail.
|
|
*/
|
|
MR_reset_ticket(MR_EXCEPTION_FRAMEVARS->trail_ptr, MR_exception);
|
|
MR_discard_tickets_to(MR_EXCEPTION_FRAMEVARS->ticket_counter);
|
|
#endif
|
|
#ifndef CONSERVATIVE_GC
|
|
/*
|
|
** Reset the heap. But we need to be careful to preserve the
|
|
** thrown exception object.
|
|
**
|
|
** The following algorithm uses the `solutions heap', and will work
|
|
** with non-conservative gc. We copy the exception object to the
|
|
** solutions_heap, reset the heap pointer, and then copy it back.
|
|
**
|
|
** An improvement to this would be to copy the exception object to the
|
|
** solutions heap, but have deep_copy add an offset to the pointers
|
|
** (at least, those that would otherwise point to the solutions heap),
|
|
** so that, when finished, a block move of the solutions heap back to
|
|
** the real heap will leave all the pointers in the correct place.
|
|
*/
|
|
{
|
|
Word * saved_solns_heap_ptr;
|
|
|
|
/* switch to the solutions heap */
|
|
if (MR_heap_zone == MR_EXCEPTION_FRAMEVARS->heap_zone) {
|
|
swap_heaps();
|
|
}
|
|
|
|
saved_solns_heap_ptr = MR_hp;
|
|
|
|
/*
|
|
** deep_copy() the exception to the solutions heap.
|
|
** Note that we need to save/restore the hp register, if it
|
|
** is transient, before/after calling deep_copy().
|
|
*/
|
|
assert(MR_EXCEPTION_FRAMEVARS->heap_ptr <=
|
|
MR_EXCEPTION_FRAMEVARS->heap_zone->top);
|
|
save_transient_registers();
|
|
exception = deep_copy(&exception,
|
|
(Word *) &mercury_data_std_util__type_ctor_info_univ_0,
|
|
MR_EXCEPTION_FRAMEVARS->heap_ptr,
|
|
MR_EXCEPTION_FRAMEVARS->heap_zone->top);
|
|
restore_transient_registers();
|
|
|
|
/* switch back to the ordinary heap */
|
|
swap_heaps();
|
|
|
|
/* reset the heap */
|
|
assert(MR_EXCEPTION_FRAMEVARS->heap_ptr <= MR_hp);
|
|
MR_hp = MR_EXCEPTION_FRAMEVARS->heap_ptr;
|
|
|
|
/* deep_copy the exception back to the ordinary heap */
|
|
assert(MR_EXCEPTION_FRAMEVARS->solns_heap_ptr <=
|
|
MR_solutions_heap_zone->top);
|
|
save_transient_registers();
|
|
exception = deep_copy(&exception,
|
|
(Word *) &mercury_data_std_util__type_ctor_info_univ_0,
|
|
saved_solns_heap_ptr, MR_solutions_heap_zone->top);
|
|
restore_transient_registers();
|
|
|
|
/* reset the solutions heap */
|
|
assert(MR_EXCEPTION_FRAMEVARS->solns_heap_ptr <= saved_solns_heap_ptr);
|
|
assert(saved_solns_heap_ptr <= MR_sol_hp);
|
|
if (catch_code_model == MR_MODEL_NON_HANDLER) {
|
|
/*
|
|
** If the code inside the try (catch) was nondet,
|
|
** then its caller (which may be solutions/2) may
|
|
** have put some more stuff on the solutions-heap
|
|
** after the goal succeeded; the goal may have
|
|
** only thrown after being re-entered on backtracking.
|
|
** Thus we can only reset the solutions heap to
|
|
** where it was before copying the exception object to it.
|
|
*/
|
|
MR_sol_hp = saved_solns_heap_ptr;
|
|
} else {
|
|
/*
|
|
** If the code inside the try (catch) was det or semidet,
|
|
** we can safely reset the solutions heap to where
|
|
** it was when it try (catch) was entered.
|
|
*/
|
|
MR_sol_hp = MR_EXCEPTION_FRAMEVARS->solns_heap_ptr;
|
|
}
|
|
}
|
|
#endif /* !defined(CONSERVATIVE_GC) */
|
|
|
|
/*
|
|
** Pop the final exception handler frame off the nondet stack,
|
|
** and reset the nondet stack top. (This must be done last,
|
|
** since it invalidates all the framevars.)
|
|
*/
|
|
MR_maxfr = MR_prevfr_slot(MR_curfr);
|
|
MR_curfr = MR_maxfr;
|
|
|
|
/*
|
|
** Now longjmp to the catch, which will invoke the handler
|
|
** that we found.
|
|
*/
|
|
|
|
if (catch_code_model == MR_C_LONGJMP_HANDLER) {
|
|
MR_ENGINE(e_exception) = (Word *) exception;
|
|
save_registers();
|
|
longjmp(*(MR_ENGINE(e_jmp_buf)), 1);
|
|
}
|
|
|
|
/*
|
|
** Otherwise, the handler is a Mercury closure.
|
|
** Invoke the handler as `Handler(Exception, Result)'.
|
|
*/
|
|
r1 = handler; /* get the Handler closure */
|
|
r2 = 1; /* One additional input argument */
|
|
r3 = 1; /* One output argument */
|
|
r4 = exception; /* This is our one input argument */
|
|
|
|
/*
|
|
** If the catch was semidet, we need to set the success indicator
|
|
** r1 to TRUE and return the result in r2; otherwise, we return
|
|
** the result in r1, which is where mercury__do_call_closure puts it,
|
|
** so we can to a tailcall.
|
|
*/
|
|
if (catch_code_model != MR_MODEL_SEMI_HANDLER) {
|
|
tailcall(ENTRY(mercury__do_call_closure),
|
|
ENTRY(mercury__exception__builtin_throw_1_0));
|
|
}
|
|
MR_incr_sp_push_msg(1, ""builtin_throw/1"");
|
|
MR_stackvar(1) = (Word) MR_succip;
|
|
call(ENTRY(mercury__do_call_closure),
|
|
LABEL(mercury__exception__builtin_throw_1_0_i1),
|
|
ENTRY(mercury__exception__builtin_throw_1_0));
|
|
}
|
|
Define_label(mercury__exception__builtin_throw_1_0_i1);
|
|
update_prof_current_proc(LABEL(mercury__exception__builtin_throw_1_0));
|
|
/* we've just returned from mercury__do_call_closure */
|
|
r2 = r1;
|
|
r1 = TRUE;
|
|
MR_succip = (Code *) MR_stackvar(1);
|
|
MR_decr_sp_pop_msg(1);
|
|
proceed(); /* return to the caller of `builtin_catch' */
|
|
|
|
END_MODULE
|
|
|
|
|
|
/* Ensure that the initialization code for the above module gets run. */
|
|
/*
|
|
INIT mercury_sys_init_exceptions
|
|
*/
|
|
|
|
/* suppress gcc -Wmissing-decls warning */
|
|
void mercury_sys_init_exceptions(void);
|
|
|
|
void mercury_sys_init_exceptions(void) {
|
|
exceptions_module();
|
|
}
|
|
|
|
#endif /* ! MR_HIGHLEVEL_CODE */
|
|
|
|
").
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pragma export(report_uncaught_exception(in, di, uo),
|
|
"ML_report_uncaught_exception").
|
|
|
|
:- pred report_uncaught_exception(univ, io__state, io__state).
|
|
:- mode report_uncaught_exception(in, di, uo) is cc_multi.
|
|
|
|
report_uncaught_exception(Exception) -->
|
|
try_io(report_uncaught_exception_2(Exception), Result),
|
|
( { Result = succeeded(_) }
|
|
; { Result = exception(_) }
|
|
% if we got a further exception while trying to report
|
|
% the uncaught exception, just ignore it
|
|
).
|
|
|
|
:- pred report_uncaught_exception_2(univ, unit, io__state, io__state).
|
|
:- mode report_uncaught_exception_2(in, out, di, uo) is det.
|
|
|
|
report_uncaught_exception_2(Exception, unit) -->
|
|
io__stderr_stream(StdErr),
|
|
io__write_string(StdErr, "Uncaught exception:\n"),
|
|
( { univ_to_type(Exception, software_error(Message)) } ->
|
|
io__format(StdErr, "Software Error: %s\n", [s(Message)])
|
|
;
|
|
io__write(StdErr, univ_value(Exception)),
|
|
io__nl(StdErr)
|
|
).
|
|
|
|
/*
|
|
** unsafe_perform_io/2 is the same as unsafe_perform_io/1
|
|
** (see extras/trailed_update/unsafe.m)
|
|
** except that it also allows the predicate to return an output argument.
|
|
*/
|
|
:- impure pred unsafe_perform_io(pred(T, io__state, io__state), T).
|
|
:- mode unsafe_perform_io(pred(out, di, uo) is det, out) is det.
|
|
:- mode unsafe_perform_io(pred(out, di, uo) is cc_multi, out) is det.
|
|
|
|
:- pragma c_code(
|
|
unsafe_perform_io(P::(pred(out, di, uo) is det), X::out),
|
|
may_call_mercury,
|
|
"{
|
|
ML_exception_call_io_pred_det(TypeInfo_for_T, P, &X);
|
|
}").
|
|
:- pragma c_code(
|
|
unsafe_perform_io(P::(pred(out, di, uo) is cc_multi), X::out),
|
|
may_call_mercury,
|
|
"{
|
|
ML_exception_call_io_pred_cc_multi(TypeInfo_for_T, P, &X);
|
|
}").
|
|
|
|
:- pred call_io_pred(pred(T, io__state, io__state), T, io__state, io__state).
|
|
:- mode call_io_pred(pred(out, di, uo) is det, out, di, uo) is det.
|
|
:- mode call_io_pred(pred(out, di, uo) is cc_multi, out, di, uo) is cc_multi.
|
|
|
|
:- pragma export(call_io_pred(pred(out, di, uo) is det, out, di, uo),
|
|
"ML_exception_call_io_pred_det").
|
|
:- pragma export(call_io_pred(pred(out, di, uo) is cc_multi, out, di, uo),
|
|
"ML_exception_call_io_pred_cc_multi").
|
|
|
|
call_io_pred(P, X) --> P(X).
|
|
|
|
%-----------------------------------------------------------------------------%
|