[m-dev.] for review: support catching exceptions in call_engine()
Fergus Henderson
fjh at cs.mu.OZ.AU
Thu Sep 16 02:04:21 AEST 1999
Hi,
If there's no objections, I'll commit this sometime soon.
----------
Estimated hours taken: 16
Add support for Mercury exception handling to call_engine().
Currently that support is not yet used, but the idea is to eventually
use this to e.g. allow Mercury exceptions to be automatically
converted into C++ exceptions when you export a Mercury procedure
to C++.
runtime/mercury_stacks.h:
Add stuff for exception handling, adapted from code in
library/exception.m, for use by mercury_engine.c.
runtime/mercury_engine.c:
Rename call_engine as MR_call_engine().
Add a new parameter `bool catch_exceptions', and change
the result type from `void' to `Word *'.
If the catch_exceptions is true, then it installs an exception handler
to catch Mercury exceptions and returns the Mercury exception thrown,
if any.
runtime/mercury_engine.h:
Add a new field e_exception to the MercuryEngine structure.
This thread-local variable is used to hold the Mercury exception
object thrown, if any, when call_engine_inner() returns via longjmp().
Update the prototype for call_engine() to match its new
MR_call_engine() interface.
runtime/mercury_bootstrap.h:
Add a #define of call_engine() that calls MR_call_engine(),
for bootstrapping.
runtime/mercury_wrapper.c:
runtime/mercury_thread.c:
compiler/export.m:
Call MR_call_engine() rather than call_engine().
runtime/mercury_wrapper.c:
Ensure that we do not clobber the value of MR_curfr in do_interpreter,
since it may be needed for the exception handler frame in
MR_call_engine() if do_interpreter were invoked via
MR_call_engine(ENTRY(do_interpreter), TRUE).
library/exception.m:
Simplify the code for the different builtin_catch procedures by
using the MR_create_exception_handler() macro defined in
mercury_stacks.h (this avoids quite a bit of code duplication
in the old code).
Modify the code for builtin_throw to allow the exception handler
to be C code; in that case, we save the exception object
in the e_exception field of the MercuryEngine and then jump to
the exception handler using longjmp().
Workspace: /home/mercury0/fjh/mercury
Index: compiler/export.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/export.m,v
retrieving revision 1.26
diff -u -r1.26 export.m
--- export.m 1998/11/20 04:07:35 1.26
+++ export.m 1999/09/15 15:28:38
@@ -138,15 +138,15 @@
% restore_registers();
% <copy input arguments from Mercury__Arguments into registers>
% /* save the registers which may be clobbered */
- % /* by the C function call call_engine(). */
+ % /* by the C function call MR_call_engine(). */
% save_transient_registers();
% {
% Declare_entry(<label of called proc>);
- % call_engine(ENTRY(<label of called proc>);
+ % (void) MR_call_engine(ENTRY(<label of called proc>), FALSE);
% }
% /* restore the registers which may have been */
% /* clobbered by the return from the C function */
- % /* call_engine() */
+ % /* MR_call_engine() */
% restore_transient_registers();
% #if SEMIDET
% if (!r1) {
@@ -199,9 +199,9 @@
"\t{\n\tDeclare_entry(",
ProcLabelString,
");\n",
- "\tcall_engine(ENTRY(",
+ "\t(void) MR_call_engine(ENTRY(",
ProcLabelString,
- "));\n\t}\n",
+ "), FALSE);\n\t}\n",
"\trestore_transient_registers();\n",
MaybeFail,
OutputArgs,
Index: library/exception.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/exception.m,v
retrieving revision 1.1
diff -u -r1.1 exception.m
--- exception.m 1999/08/31 12:55:43 1.1
+++ exception.m 1999/09/15 15:58:08
@@ -463,8 +463,6 @@
:- pragma c_code("
-Define_extern_entry(exception_handler_do_fail);
-
/*
** MR_trace_throw():
** Unwind the stack as far as possible, until we reach a frame
@@ -546,8 +544,6 @@
return NULL;
}
-enum CodeModel { MODEL_DET, MODEL_SEMI, MODEL_NON };
-
/* swap the heap with the solutions heap */
#define swap_heaps() \\
{ \\
@@ -564,29 +560,6 @@
MR_solutions_heap_zone = swap_heaps_temp_hp_zone; \\
}
-/*
-** Define a struct for the framevars that we use in an exception handler
-** nondet stack frame. This struct gets allocated on the nondet stack
-** using mkpragmaframe().
-*/
-typedef struct Exception_Handler_Frame_struct {
- Word code_model;
- Word handler;
- Word *stack_ptr;
-#ifdef MR_USE_TRAIL
- Word trail_ptr;
- Word ticket_counter;
-#endif
-#ifndef CONSERVATIVE_GC
- Word *heap_ptr;
- Word *solns_heap_ptr;
- MemoryZone *heap_zone;
-#endif
-} Exception_Handler_Frame;
-
-#define FRAMEVARS \\
- (((Exception_Handler_Frame *) (MR_curfr - MR_NONDET_FIXED_SIZE)) - 1)
-
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 */
@@ -619,7 +592,6 @@
** MR_MAKE_PROC_LAYOUT(entry, detism, slots, succip_locn, pred_or_func,
** module, name, arity, mode)
*/
-/* do we need one for exception_handler_do_fail? */
MR_MAKE_PROC_LAYOUT(mercury__exception__builtin_throw_1_0,
MR_DETISM_DET, BUILTIN_THROW_STACK_SIZE, MR_LONG_LVAL_STACKVAR(1),
@@ -670,14 +642,13 @@
#endif
init_entry(mercury__exception__builtin_throw_1_0);
init_label(mercury__exception__builtin_throw_1_0_i1);
- init_entry(exception_handler_do_fail);
BEGIN_CODE
/*
** builtin_catch(Goal, Handler, Result)
** call Goal(R).
** if succeeds, set Result = R.
-** if throws an exception, call Handler(Result).
+** 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,
@@ -693,45 +664,12 @@
#endif
Define_entry(mercury__exception__builtin_catch_3_2); /* cc_multi */
/*
- ** Create a handler on the stack with the special redoip
- ** of `exception_handler_do_fail' (we'll look for this redoip
- ** when unwinding the nondet stack in builtin_throw/1),
- ** and save the stuff we will need if an exception is thrown.
- */
- mkpragmaframe(""builtin_catch/3 [model_det]"", 0,
- Exception_Handler_Frame_struct,
- ENTRY(exception_handler_do_fail));
- FRAMEVARS->code_model = MODEL_DET;
- FRAMEVARS->handler = r3; /* save the Handler closure */
- FRAMEVARS->stack_ptr = MR_sp; /* save the det stack pointer */
-#ifndef CONSERVATIVE_GC
- /* save the heap and solutions heap pointers */
- FRAMEVARS->heap_ptr = MR_hp;
- FRAMEVARS->solns_heap_ptr = MR_sol_hp;
- FRAMEVARS->heap_zone = MR_heap_zone;
-#endif
-#ifdef MR_USE_TRAIL
- /* save the trail state */
- MR_mark_ticket_stack(FRAMEVARS->ticket_counter);
- MR_store_ticket(FRAMEVARS->trail_ptr);
-#endif
-
- /*
- ** Now we need to create another frame.
- ** This is so that we can be sure that no-one will hijack
- ** the redoip of the special frame we created above.
- ** (The compiler sometimes generates ``hijacking'' code that saves
- ** the topmost redoip on the stack, and temporarily replaces it
- ** with a new redoip that will do some processing on failure
- ** before restoring the original redoip. This would cause
- ** problems when doing stack unwinding in builtin_throw/1,
- ** because we wouldn't be able to find the special redoip.
- ** But code will only ever hijack the topmost frame, so we
- ** can avoid this by creating a second frame above the special
- ** frame.)
+ ** Create an exception handler entry on the nondet stack.
+ ** (Register r3 holds the Handler closure.)
*/
- mktempframe(ENTRY(do_fail));
-
+ MR_create_exception_handler(""builtin_catch/3 [model_det]"",
+ MR_MODEL_DET_HANDLER, r3, ENTRY(do_fail));
+
/*
** Now call `Goal(Result)'.
*/
@@ -746,6 +684,9 @@
update_prof_current_proc(LABEL(mercury__exception__builtin_catch_3_2));
/*
** On exit from do_call_det_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();
@@ -757,7 +698,7 @@
** call Goal(R).
** if succeeds, set Result = R.
** if fails, fail.
-** if throws an exception, call Handler(Result).
+** 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,
@@ -772,47 +713,13 @@
}
#endif
Define_entry(mercury__exception__builtin_catch_3_3); /* cc_nondet */
- /*
- ** Create a handler on the stack with the special redoip
- ** of `exception_handler_do_fail' (we'll look for this redoip
- ** when unwinding the nondet stack in builtin_throw/1),
- ** and save the stuff we will need if an exception is thrown.
- */
- mkpragmaframe(""builtin_catch/3 [model_semi]"", 0,
- Exception_Handler_Frame_struct,
- ENTRY(exception_handler_do_fail));
- FRAMEVARS->code_model = MODEL_SEMI;
- FRAMEVARS->handler = r3; /* save the Handler closure */
- FRAMEVARS->stack_ptr = MR_sp; /* save the det stack pointer */
-#ifndef CONSERVATIVE_GC
- /* save the heap and solutions heap pointers */
- FRAMEVARS->heap_ptr = MR_hp;
- FRAMEVARS->solns_heap_ptr = MR_sol_hp;
- FRAMEVARS->heap_zone = MR_heap_zone;
-#endif
-#ifdef MR_USE_TRAIL
- /* save the trail state */
- MR_mark_ticket_stack(FRAMEVARS->ticket_counter);
- MR_store_ticket(FRAMEVARS->trail_ptr);
-#endif
-
-
/*
- ** Now we need to create another frame.
- ** This is so that we can be sure that no-one will hijack
- ** the redoip of the special frame we created above.
- ** (The compiler sometimes generates ``hijacking'' code that saves
- ** the topmost redoip on the stack, and temporarily replaces it
- ** with a new redoip that will do some processing on failure
- ** before restoring the original redoip. This would cause
- ** problems when doing stack unwinding in builtin_throw/1,
- ** because we wouldn't be able to find the special redoip.
- ** But code will only ever hijacks the topmost frame, so we
- ** can avoid this by creating a second frame above the special
- ** frame.)
+ ** Create an exception handler entry on the nondet stack.
+ ** (Register r3 holds the Handler closure.)
*/
- mktempframe(ENTRY(do_fail));
-
+ MR_create_exception_handler(""builtin_catch/3 [model_semi]"",
+ MR_MODEL_SEMI_HANDLER, r3, ENTRY(do_fail));
+
/*
** Now call `Goal(Result)'.
*/
@@ -842,7 +749,7 @@
** call Goal(R).
** if succeeds, set Result = R.
** if fails, fail.
-** if throws an exception, call Handler(Result).
+** 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,
@@ -857,49 +764,19 @@
}
#endif
Define_entry(mercury__exception__builtin_catch_3_5); /* nondet */
- /*
- ** Create a handler on the stack with the special redoip
- ** of `exception_handler_do_fail' (we'll look for this redoip
- ** when unwinding the nondet stack in builtin_throw/1),
- ** and save the stuff we will need if an exception is thrown.
- */
- mkpragmaframe(""builtin_catch/3 [model_nondet]"", 0,
- Exception_Handler_Frame_struct,
- ENTRY(exception_handler_do_fail));
- FRAMEVARS->code_model = MODEL_NON;
- FRAMEVARS->handler = r3; /* save the Handler closure */
- FRAMEVARS->stack_ptr = MR_sp; /* save the det stack pointer */
-#ifndef CONSERVATIVE_GC
- /* save the heap and solutions heap pointers */
- FRAMEVARS->heap_ptr = MR_hp;
- FRAMEVARS->solns_heap_ptr = MR_sol_hp;
- FRAMEVARS->heap_zone = MR_heap_zone;
-#endif
-#ifdef MR_USE_TRAIL
- /* save the trail state */
- MR_mark_ticket_stack(FRAMEVARS->ticket_counter);
- MR_store_ticket(FRAMEVARS->trail_ptr);
-#endif
-
/*
- ** Now we need to create another frame.
- ** This is so that we can be sure that no-one will hijack
- ** the redoip of the special frame we created above.
- ** (The compiler sometimes generates ``hijacking'' code that saves
- ** the topmost redoip on the stack, and temporarily replaces it
- ** with a new redoip that will do some processing on failure
- ** before restoring the original redoip. This would cause
- ** problems when doing stack unwinding in builtin_throw/1,
- ** because we wouldn't be able to find the special redoip.
- ** But code will only ever hijacks the topmost frame, so we
- ** can avoid this by creating a second frame above the special
- ** frame.)
+ ** Create an exception handler entry on the nondet stack.
+ ** (Register r3 holds the Handler closure.)
*/
#ifdef MR_USE_TRAIL
- mktempframe(LABEL(mercury__exception__builtin_catch_3_5_i3));
+ MR_create_exception_handler(""builtin_catch/3 [model_nondet]"",
+ MR_MODEL_NON_HANDLER, r3,
+ LABEL(mercury__exception__builtin_catch_3_5_i3));
#else
- mktempframe(ENTRY(do_fail));
+ MR_create_exception_handler(""builtin_catch/3 [model_nondet]"",
+ MR_MODEL_NON_HANDLER, r3, ENTRY(do_fail));
#endif
+
/*
** Now call `Goal(Result)'.
@@ -932,8 +809,11 @@
/*
** builtin_throw(Exception):
** Throw the specified exception.
-** That means unwinding the nondet stack until we find a handler, and then
-** calling Handler(Result).
+** 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.
*/
@@ -941,7 +821,7 @@
{
Word exception = r1;
Word handler;
- enum CodeModel catch_code_model;
+ enum MR_HandlerCodeModel catch_code_model;
Word *orig_curfr;
Unsigned exception_event_number = MR_trace_event_number;
@@ -1016,19 +896,29 @@
}
/*
- ** Save the handler we found, and reset the det stack top.
+ ** 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);
- catch_code_model = FRAMEVARS->code_model;
- handler = FRAMEVARS->handler;
- MR_sp = FRAMEVARS->stack_ptr; /* reset the det stack pointer */
+
+ /*
+ ** Reset the det stack.
+ */
+ MR_sp = MR_EXCEPTION_FRAMEVARS->stack_ptr;
#ifdef MR_USE_TRAIL
/*
** Reset the trail.
*/
- MR_reset_ticket(FRAMEVARS->trail_ptr, MR_exception);
- MR_discard_tickets_to(FRAMEVARS->ticket_counter);
+ MR_reset_ticket(MR_EXCEPTION_FRAMEVARS->trail_ptr, MR_exception);
+ MR_discard_tickets_to(MR_EXCEPTION_FRAMEVARS->ticket_counter);
#endif
#ifndef CONSERVATIVE_GC
/*
@@ -1049,7 +939,7 @@
Word * saved_solns_heap_ptr;
/* switch to the solutions heap */
- if (MR_heap_zone == FRAMEVARS->heap_zone) {
+ if (MR_heap_zone == MR_EXCEPTION_FRAMEVARS->heap_zone) {
swap_heaps();
}
@@ -1060,22 +950,23 @@
** Note that we need to save/restore the hp register, if it
** is transient, before/after calling deep_copy().
*/
- assert(FRAMEVARS->heap_ptr <= FRAMEVARS->heap_zone->top);
+ assert(FRAMEVARS->heap_ptr <= MR_EXCEPTION_FRAMEVARS->heap_zone->top);
save_transient_registers();
exception = deep_copy((Word *) exception,
(Word *) &mercury_data_std_util__type_ctor_info_univ_0,
- FRAMEVARS->heap_ptr, FRAMEVARS->heap_zone->top);
+ FRAMEVARS->heap_ptr, MR_EXCEPTION_FRAMEVARS->heap_zone->top);
restore_transient_registers();
/* switch back to the ordinary heap */
swap_heaps();
/* reset the heap */
- assert(FRAMEVARS->heap_ptr <= MR_hp);
- MR_hp = FRAMEVARS->heap_ptr;
+ 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(FRAMEVARS->solns_heap_ptr <= MR_solutions_heap_zone->top);
+ assert(MR_EXCEPTION_FRAMEVARS->solns_heap_ptr <=
+ MR_solutions_heap_zone->top);
save_transient_registers();
exception = deep_copy((Word *) exception,
(Word *) &mercury_data_std_util__type_ctor_info_univ_0,
@@ -1083,10 +974,9 @@
restore_transient_registers();
/* reset the solutions heap */
- fflush(NULL);
- assert(FRAMEVARS->solns_heap_ptr <= saved_solns_heap_ptr);
+ assert(MR_EXCEPTION_FRAMEVARS->solns_heap_ptr <= saved_solns_heap_ptr);
assert(saved_solns_heap_ptr <= MR_sol_hp);
- if (catch_code_model == MODEL_NON) {
+ 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
@@ -1094,7 +984,7 @@
** 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
+ ** where it was before copying the exception object to it.
*/
MR_sol_hp = saved_solns_heap_ptr;
} else {
@@ -1103,7 +993,7 @@
** we can safely reset the solutions heap to where
** it was when it try (catch) was entered.
*/
- MR_sol_hp = FRAMEVARS->solns_heap_ptr;
+ MR_sol_hp = MR_EXCEPTION_FRAMEVARS->solns_heap_ptr;
}
}
#endif /* !defined(CONSERVATIVE_GC) */
@@ -1117,9 +1007,20 @@
MR_curfr = MR_maxfr;
/*
- ** Now invoke the handler that we found, as `Handler(Result)',
+ ** 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 */
@@ -1131,7 +1032,7 @@
** the result in r1, which is where do_call_det_closure puts it,
** so we can to a tailcall.
*/
- if (catch_code_model != MODEL_SEMI) {
+ if (catch_code_model != MR_MODEL_SEMI_HANDLER) {
tailcall(ENTRY(do_call_det_closure),
ENTRY(mercury__exception__builtin_throw_1_0));
}
@@ -1148,17 +1049,8 @@
r1 = TRUE;
MR_succip = (Code *) MR_stackvar(1);
decr_sp_pop_msg(1);
- proceed();
+ proceed(); /* return to the caller of `builtin_catch' */
-Define_entry(exception_handler_do_fail);
- /*
- ** `exception_handler_do_fail' is the same as `do_fail':
- ** it just invokes fail(). The reason we don't just use
- ** `do_fail' for this is that when unwinding the stack we
- ** check for a redoip of `exception_handler_do_fail' and
- ** handle it specially.
- */
- fail();
END_MODULE
Index: runtime/mercury_bootstrap.h
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_bootstrap.h,v
retrieving revision 1.9
diff -u -r1.9 mercury_bootstrap.h
--- mercury_bootstrap.h 1999/09/10 10:26:24 1.9
+++ mercury_bootstrap.h 1999/09/15 15:09:15
@@ -233,6 +233,9 @@
#define mercury_data___type_ctor_info_func_0_struct \
MR_TypeCtorInfo_struct
+/* stuff from mercury_engine.h */
+#define call_engine(e) ((void) MR_call_engine((e), FALSE))
+
#endif /* not MR_NO_BACKWARDS_COMPAT */
#endif /* MERCURY_BOOTSTRAP_H */
Index: runtime/mercury_engine.c
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_engine.c,v
retrieving revision 1.16
diff -u -r1.16 mercury_engine.c
--- mercury_engine.c 1999/04/30 04:25:39 1.16
+++ mercury_engine.c 1999/09/15 15:16:14
@@ -28,7 +28,7 @@
#endif
-static void call_engine_inner(Code *entry_point);
+static void call_engine_inner(Code *entry_point) NO_RETURN;
#ifndef USE_GCC_NONLOCAL_GOTOS
static Code *engine_done(void);
@@ -152,13 +152,27 @@
/*---------------------------------------------------------------------------*/
/*
-** call_engine(Code *entry_point)
+** Word *
+** MR_call_engine(Code *entry_point, bool catch_exceptions)
**
** This routine calls a Mercury routine from C.
**
** The called routine should be det/semidet/cc_multi/cc_nondet.
-** The virtual machine registers must be set up correctly
-** before the call. Specifically, the non-transient real registers
+**
+** If the called routine returns normally (this includes the case of a
+** semidet/cc_nondet routine failing, i.e. returning with r1 = FALSE),
+** then MR_call_engine() will return NULL.
+**
+** If the called routine exits by throwing an exception, then the
+** behaviour depends on the `catch_exceptions' flag.
+** if `catch_exceptions' is true, then MR_call_engine() will return the
+** Mercury exception object thrown. If `catch_exceptions' is false,
+** then MR_call_engine() will not return; instead, the code for `throw'
+** will unwind the stacks (including the C stack) back to the nearest
+** enclosing exception handler.
+**
+** The virtual machine registers must be set up correctly before the call
+** to MR_call_engine(). Specifically, the non-transient real registers
** must have valid values, and the fake_reg copies of the transient
** (register window) registers must have valid values; call_engine()
** will call restore_transient_registers() and will then assume that
@@ -189,8 +203,8 @@
** and another portable version that works on standard ANSI C compilers.
*/
-void
-call_engine(Code *entry_point)
+Word *
+MR_call_engine(Code *entry_point, bool catch_exceptions)
{
jmp_buf curr_jmp_buf;
@@ -208,17 +222,56 @@
MR_ENGINE(e_jmp_buf) = &curr_jmp_buf;
/*
+ ** Create an exception handler frame on the nondet stack
+ ** so that we can catch and return Mercury exceptions.
+ */
+ if (catch_exceptions) {
+ MR_create_exception_handler("call_engine",
+ MR_C_LONGJMP_HANDLER, 0, ENTRY(do_fail));
+ }
+
+ /*
** Mark this as the spot to return to.
- ** On return, restore the registers (since longjmp may clobber
- ** them), restore the saved value of MR_ENGINE(e_jmp_buf), and then
- ** exit.
*/
-
if (setjmp(curr_jmp_buf)) {
+ Word * this_frame;
+ Word * exception;
+
debugmsg0("...caught longjmp\n");
+ /*
+ ** On return,
+ ** restore the registers (since longjmp may clobber them),
+ ** and restore the saved value of MR_ENGINE(e_jmp_buf).
+ */
restore_registers();
MR_ENGINE(e_jmp_buf) = prev_jmp_buf;
- return;
+ if (catch_exceptions) {
+ /*
+ ** Figure out whether or not we got an exception.
+ ** If we got an exception, then all of the necessary
+ ** cleanup such as stack unwinding has already been
+ ** done, so all we have to do here is to return the
+ ** exception.
+ */
+ exception = MR_ENGINE(e_exception);
+ if (exception != NULL) {
+ return exception;
+ }
+ /*
+ ** If we added an exception hander, but we didn't
+ ** get an exception, then we need to remove the
+ ** exception handler frames from the nondet stack
+ ** and deallocate the trail ticket allocated by
+ ** MR_create_exception_handler().
+ */
+ this_frame = MR_curfr;
+ MR_maxfr = MR_prevfr_slot(this_frame);
+ MR_curfr = MR_succfr_slot(this_frame);
+#ifdef MR_USE_TRAIL
+ MR_discard_ticket();
+#endif
+ }
+ return NULL;
}
call_engine_inner(entry_point);
@@ -228,7 +281,7 @@
/* The gcc-specific version */
-void
+static void
call_engine_inner(Code *entry_point)
{
/*
@@ -287,7 +340,7 @@
#ifdef MR_LOWLEVEL_DEBUG
memset((void *)locals, MAGIC_MARKER, LOCALS_SIZE);
#endif
- debugmsg1("in `call_engine', locals at %p\n", (void *)locals);
+ debugmsg1("in `call_engine_inner', locals at %p\n", (void *)locals);
/*
** Increment the number of times we've entered this
@@ -346,10 +399,10 @@
/*
** We need to ensure that there is at least one
- ** real function call in call_engine(), because
+ ** real function call in call_engine_inner(), because
** otherwise gcc thinks that it doesn't need to
** restore the caller-save registers (such as
- ** the return address!) because it thinks call_engine() is
+ ** the return address!) because it thinks call_engine_inner() is
** a leaf routine which doesn't call anything else,
** and so it thinks that they won't have been clobbered.
**
@@ -395,6 +448,7 @@
** Since longjmp() may clobber the registers, we need to
** save them first.
*/
+ MR_ENGINE(e_exception) = NULL;
save_registers();
debugmsg0("longjmping out...\n");
longjmp(*(MR_ENGINE(e_jmp_buf)), 1);
@@ -424,6 +478,7 @@
static Code *
engine_done(void)
{
+ MR_ENGINE(e_exception) = NULL;
save_registers();
debugmsg0("longjmping out...\n");
longjmp(*(MR_ENGINE(e_jmp_buf)), 1);
@@ -529,6 +584,7 @@
Define_extern_entry(do_succeed);
Define_extern_entry(do_last_succeed);
Define_extern_entry(do_not_reached);
+Define_extern_entry(exception_handler_do_fail);
BEGIN_MODULE(special_labels_module)
init_entry_ai(do_redo);
@@ -536,6 +592,7 @@
init_entry_ai(do_succeed);
init_entry_ai(do_last_succeed);
init_entry_ai(do_not_reached);
+ init_entry_ai(exception_handler_do_fail);
BEGIN_CODE
Define_entry(do_redo);
@@ -551,11 +608,18 @@
MR_succeed_discard();
Define_entry(do_not_reached);
- printf("reached not_reached\n");
- exit(1);
-#ifndef USE_GCC_NONLOCAL_GOTOS
- return 0;
-#endif
+ fatal_error("reached not_reached\n");
+
+Define_entry(exception_handler_do_fail);
+ /*
+ ** `exception_handler_do_fail' is the same as `do_fail':
+ ** it just invokes fail(). The reason we don't just use
+ ** `do_fail' for this is that when unwinding the stack we
+ ** check for a redoip of `exception_handler_do_fail' and
+ ** handle it specially.
+ */
+ fail();
+
END_MODULE
void mercury_sys_init_engine(void); /* suppress gcc warning */
Index: runtime/mercury_engine.h
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_engine.h,v
retrieving revision 1.12
diff -u -r1.12 mercury_engine.h
--- mercury_engine.h 1999/04/20 11:48:13 1.12
+++ mercury_engine.h 1999/09/15 15:16:45
@@ -212,6 +212,7 @@
*/
#endif
jmp_buf *e_jmp_buf;
+ Word *e_exception;
#ifndef CONSERVATIVE_GC
MemoryZone *heap_zone;
MemoryZone *solutions_heap_zone;
@@ -313,8 +314,9 @@
/*
** Functions that act on the current Mercury engine.
+** See the comments in mercury_engine.c for documentation on MR_call_engine().
*/
-extern void call_engine(Code *entry_point);
+extern Word * MR_call_engine(Code *entry_point, bool catch_exceptions);
extern void terminate_engine(void);
extern void dump_prev_locations(void);
@@ -330,5 +332,6 @@
Declare_entry(do_reset_framevar0_fail);
Declare_entry(do_succeed);
Declare_entry(do_not_reached);
+Declare_entry(exception_handler_do_fail);
#endif /* not MERCURY_ENGINE_H */
Index: runtime/mercury_stacks.h
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_stacks.h,v
retrieving revision 1.17
diff -u -r1.17 mercury_stacks.h
--- mercury_stacks.h 1999/04/30 04:25:41 1.17
+++ mercury_stacks.h 1999/09/10 14:54:54
@@ -15,7 +15,10 @@
#include "mercury_debug.h"
#include "mercury_goto.h"
#include "mercury_tabling.h"
+#include "mercury_engine.h"
+/*---------------------------------------------------------------------------*/
+
/* DEFINITIONS FOR MANIPULATING THE DET STACK */
#define MR_based_stackvar(base_sp, n) ((base_sp)[-(n)])
@@ -53,6 +56,8 @@
(void)0 \
)
+/*---------------------------------------------------------------------------*/
+
/* DEFINITIONS FOR NONDET STACK FRAMES */
#define MR_PREVFR (-0) /* prev frame on stack, set up at call */
@@ -94,6 +99,8 @@
#define MR_framevar(n) MR_based_framevar(MR_curfr, n)
+/*---------------------------------------------------------------------------*/
+
/* DEFINITIONS FOR MANIPULATING THE NONDET STACK */
#define MR_mkframe(predname, numslots, redoip) \
@@ -202,6 +209,147 @@
MR_curfr = MR_redofr_slot(MR_maxfr); \
GOTO(MR_redoip_slot(MR_maxfr)); \
} while (0)
+
+/*---------------------------------------------------------------------------*/
+
+/* DEFINITIONS FOR EXCEPTION HANDLING */
+
+#ifdef CONSERVATIVE_GC
+ #define MR_IF_NOT_CONSERVATIVE_GC(x)
+#else
+ #define MR_IF_NOT_CONSERVATIVE_GC(x) x
+#endif
+
+#ifdef MR_USE_TRAIL
+ #define MR_IF_USE_TRAIL(x) x
+#else
+ #define MR_IF_USE_TRAIL(x)
+#endif
+
+/*
+** This enum specifies the kind of handler in an exception handler
+** nondet stack frame.
+*/
+enum MR_HandlerCodeModel {
+ /*
+ ** For these three values, the exception handler is a Mercury closure
+ ** with the specified determinism. If an exception occurs, then
+ ** after the Mercury stacks have been unwound, the closure will
+ ** be called.
+ */
+ MR_MODEL_DET_HANDLER,
+ MR_MODEL_SEMI_HANDLER,
+ MR_MODEL_NON_HANDLER,
+ /*
+ ** For this value, the exception will be handled by C code using
+ ** setjmp/longjmp. If an exception occurs, then after the Mercury
+ ** stacks have been unwound, `MR_longjmp(MR_ENGINE(e_jmp_buf))' will
+ ** be called.
+ */
+ MR_C_LONGJMP_HANDLER
+};
+
+
+/*
+** Define a struct for the framevars that we use in an exception handler
+** nondet stack frame. This struct gets allocated on the nondet stack
+** using mkpragmaframe(), with a special redoip of
+** `exception_handler_do_fail'.
+*/
+typedef struct MR_Exception_Handler_Frame_struct {
+ /*
+ ** The `code_model' field is used to identify what kind of
+ ** handler it is. It holds values of type MR_HandlerCodeModel
+ ** (see above), but it is declared to have type `Word' to ensure
+ ** that everything remains word-aligned.
+ */
+ Word code_model;
+
+ /*
+ ** If code_model is MR_MODEL_*_HANDLER, then
+ ** the `handler' field holds the Mercury closure for the handler,
+ ** which will be a closure of the specified determinism.
+ ** If code_model is MR_C_LONGJMP, then this field is unused.
+ */
+ Word handler;
+
+ /*
+ ** The remaining fields hold stuff that must be saved in order
+ ** to unwind the Mercury stacks.
+ */
+
+ /* the det stack pointer */
+ Word *stack_ptr;
+
+ /* the trail state */
+ MR_IF_USE_TRAIL(
+ Word trail_ptr;
+ Word ticket_counter;
+ )
+
+ /* the heap state */
+ MR_IF_NOT_CONSERVATIVE_GC(
+ Word *heap_ptr;
+ Word *solns_heap_ptr;
+ MemoryZone *heap_zone;
+ )
+} MR_Exception_Handler_Frame;
+
+#define MR_EXCEPTION_FRAMEVARS \
+ (((MR_Exception_Handler_Frame *) (MR_curfr - MR_NONDET_FIXED_SIZE)) - 1)
+
+#define MR_create_exception_handler(name, \
+ handler_code_model, handler_closure, redoip) \
+ do { \
+ /* \
+ ** Create a handler on the stack with the special redoip \
+ ** of `exception_handler_do_fail' (we'll look for this \
+ ** redoip when unwinding the nondet stack in \
+ ** builtin_throw/1), and save the stuff we will \
+ ** need if an exception is thrown. \
+ */ \
+ mkpragmaframe((name), 0, \
+ MR_Exception_Handler_Frame_struct, \
+ ENTRY(exception_handler_do_fail)); \
+ /* record the handler's code model */ \
+ MR_EXCEPTION_FRAMEVARS->code_model = (handler_code_model); \
+ /* save the handler's closure */ \
+ MR_EXCEPTION_FRAMEVARS->handler = (handler_closure); \
+ /* save the det stack pointer */ \
+ MR_EXCEPTION_FRAMEVARS->stack_ptr = MR_sp; \
+ MR_IF_NOT_CONSERVATIVE_GC( \
+ /* save the heap and solutions heap pointers */ \
+ MR_EXCEPTION_FRAMEVARS->heap_ptr = MR_hp; \
+ MR_EXCEPTION_FRAMEVARS->solns_heap_ptr = MR_sol_hp; \
+ MR_EXCEPTION_FRAMEVARS->heap_zone = MR_heap_zone; \
+ ) \
+ MR_IF_USE_TRAIL( \
+ /* save the trail state */ \
+ MR_mark_ticket_stack( \
+ MR_EXCEPTION_FRAMEVARS->ticket_counter); \
+ MR_store_ticket(MR_EXCEPTION_FRAMEVARS->trail_ptr); \
+ ) \
+ \
+ /* \
+ ** Now we need to create another frame. \
+ ** This is so that we can be sure that no-one will hijack \
+ ** the redoip of the special frame we created above. \
+ ** (The compiler sometimes generates ``hijacking'' code \
+ ** that saves the topmost redoip on the stack, and \
+ ** temporarily replaces it with a new redoip that will \
+ ** do some processing on failure before restoring the \
+ ** original redoip. This would cause problems when \
+ ** doing stack unwinding in builtin_throw/1, because \
+ ** we wouldn't be able to find the special redoip. \
+ ** But code will only ever hijack the topmost frame, \
+ ** so we can avoid this by creating a second frame \
+ ** above the special frame.) \
+ */ \
+ mktempframe(redoip); \
+ } while (0)
+
+
+/*---------------------------------------------------------------------------*/
#ifdef MR_USE_MINIMAL_MODEL
Index: runtime/mercury_thread.c
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_thread.c,v
retrieving revision 1.8
diff -u -r1.8 mercury_thread.c
--- mercury_thread.c 1998/12/15 00:22:25 1.8
+++ mercury_thread.c 1999/09/15 15:20:49
@@ -102,7 +103,7 @@
switch (when_to_use) {
case MR_use_later :
- call_engine(ENTRY(do_runnext));
+ (void) MR_call_engine(ENTRY(do_runnext), FALSE);
destroy_engine(eng);
return;
Index: runtime/mercury_wrapper.c
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_wrapper.c,v
retrieving revision 1.41
diff -u -r1.41 mercury_wrapper.c
--- mercury_wrapper.c 1999/05/30 03:54:55 1.41
+++ mercury_wrapper.c 1999/09/15 15:10:38
@@ -21,7 +21,7 @@
** processes options (which are specified via an environment variable).
**
** It also defines mercury_runtime_main(), which invokes
-** call_engine(do_interpreter), which invokes main/2.
+** MR_call_engine(do_interpreter), which invokes main/2.
**
** It also defines mercury_runtime_terminate(), which performs
** various cleanups that are needed to terminate cleanly.
@@ -342,7 +342,7 @@
*/
restore_regs_from_mem(c_regs);
-} /* end runtime_mercury_main() */
+} /* end runtime_mercury_init() */
void
do_init_modules(void)
@@ -859,8 +861,8 @@
for (repcounter = 0; repcounter < repeats; repcounter++) {
debugmsg0("About to call engine\n");
- call_engine(ENTRY(do_interpreter));
- debugmsg0("Returning from call_engine()\n");
+ (void) MR_call_engine(ENTRY(do_interpreter), FALSE);
+ debugmsg0("Returning from MR_call_engine()\n");
}
if (use_own_timer) {
@@ -999,10 +1001,11 @@
BEGIN_CODE
Define_entry(do_interpreter);
- MR_incr_sp(3);
+ MR_incr_sp(4);
MR_stackvar(1) = (Word) MR_hp;
MR_stackvar(2) = (Word) MR_succip;
MR_stackvar(3) = (Word) MR_maxfr;
+ MR_stackvar(4) = (Word) MR_curfr;
MR_mkframe("interpreter", 1, LABEL(global_fail));
@@ -1054,7 +1057,8 @@
MR_hp = (Word *) MR_stackvar(1);
MR_succip = (Code *) MR_stackvar(2);
MR_maxfr = (Word *) MR_stackvar(3);
- MR_decr_sp(3);
+ MR_curfr = (Word *) MR_stackvar(4);
+ MR_decr_sp(4);
#ifdef MR_LOWLEVEL_DEBUG
if (MR_finaldebug && MR_detaildebug) {
--
Fergus Henderson <fjh at cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3 | -- the last words of T. S. Garp.
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to: mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions: mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------
More information about the developers
mailing list