fixing bugs related to stack, class stack, exceptio stack handling

This commit is contained in:
2022-02-27 19:35:47 +00:00
parent 3f03140dcc
commit f4661d018a
5 changed files with 207 additions and 83 deletions

View File

@ -140,7 +140,7 @@ static void terminate_all_processes (hcl_t* hcl);
/* ------------------------------------------------------------------------- */
#define HCL_EXSTACK_PUSH(hcl, ctx_, ip_, clsp_) \
#define HCL_EXSTACK_PUSH(hcl, ctx_, ip_, clsp_, sp_) \
do { \
hcl_oop_process_t ap = (hcl)->processor->active; \
hcl_ooi_t exsp = HCL_OOP_TO_SMOOI(ap->exsp); \
@ -152,6 +152,7 @@ static void terminate_all_processes (hcl_t* hcl);
exsp++; ap->slot[exsp] = (ctx_); \
exsp++; ap->slot[exsp] = HCL_SMOOI_TO_OOP(ip_); \
exsp++; ap->slot[exsp] = HCL_SMOOI_TO_OOP(clsp_); \
exsp++; ap->slot[exsp] = HCL_SMOOI_TO_OOP(sp_); \
ap->exsp = HCL_SMOOI_TO_OOP(exsp); \
} while (0)
@ -159,14 +160,15 @@ static void terminate_all_processes (hcl_t* hcl);
do { \
hcl_oop_process_t ap = (hcl)->processor->active; \
hcl_ooi_t exsp = HCL_OOP_TO_SMOOI(ap->exsp); \
exsp -= 3; \
exsp -= 4; \
ap->exsp = HCL_SMOOI_TO_OOP(exsp); \
} while (0)
#define HCL_EXSTACK_POP_TO(hcl, ctx_, ip_, clsp_) \
#define HCL_EXSTACK_POP_TO(hcl, ctx_, ip_, clsp_, sp_) \
do { \
hcl_oop_process_t ap = (hcl)->processor->active; \
hcl_ooi_t exsp = HCL_OOP_TO_SMOOI(ap->exsp); \
sp_ = HCL_OOP_TO_SMOOI(ap->slot[exsp]); exsp--; \
clsp_ = HCL_OOP_TO_SMOOI(ap->slot[exsp]); exsp--; \
ip_ = HCL_OOP_TO_SMOOI(ap->slot[exsp]); exsp--; \
ctx_ = ap->slot[exsp]; exsp--; \
@ -602,7 +604,7 @@ static hcl_oop_process_t make_process (hcl_t* hcl, hcl_oop_context_t c)
static HCL_INLINE void sleep_active_process (hcl_t* hcl, int state)
{
STORE_ACTIVE_SP(hcl);
STORE_ACTIVE_SP (hcl);
/* store the current active context to the current process.
* it is the suspended context of the process to be suspended */
@ -644,7 +646,7 @@ static void switch_to_process (hcl_t* hcl, hcl_oop_process_t proc, int new_state
/* the new process must be in the runnable state */
HCL_ASSERT (hcl, proc->state == HCL_SMOOI_TO_OOP(PROC_STATE_RUNNABLE) ||
proc->state == HCL_SMOOI_TO_OOP(PROC_STATE_WAITING));
proc->state == HCL_SMOOI_TO_OOP(PROC_STATE_WAITING));
sleep_active_process (hcl, new_state_for_old_active);
wake_process (hcl, proc);
@ -903,10 +905,12 @@ static void dump_process_info (hcl_t* hcl, hcl_bitmask_t log_mask)
static HCL_INLINE void reset_process_stack_pointers (hcl_t* hcl, hcl_oop_process_t proc)
{
#if defined(HCL_DEBUG_VM_PROCESSOR)
HCL_LOG9 (hcl, HCL_LOG_IC | HCL_LOG_DEBUG,
"Processor - process[%zd] SP: %zd ST: %zd EXSP: %zd(%zd) EXST: %zd CLSP: %zd(%zd) CLST: %zd\n",
HCL_LOG4 (hcl, HCL_LOG_IC | HCL_LOG_DEBUG,
"Processor - process[%zd] SP: %zd(%zd) ST: %zd",
HCL_OOP_TO_SMOOI(proc->id),
HCL_OOP_TO_SMOOI(proc->sp), HCL_OOP_TO_SMOOI(proc->st),
HCL_OOP_TO_SMOOI(proc->sp), HCL_OOP_TO_SMOOI(proc->sp) - (-1), HCL_OOP_TO_SMOOI(proc->st));
HCL_LOG6 (hcl, HCL_LOG_IC | HCL_LOG_DEBUG,
" EXSP: %zd(%zd) EXST: %zd CLSP: %zd(%zd) CLST: %zd\n",
HCL_OOP_TO_SMOOI(proc->exsp), HCL_OOP_TO_SMOOI(proc->exsp) - HCL_OOP_TO_SMOOI(proc->st), HCL_OOP_TO_SMOOI(proc->exst),
HCL_OOP_TO_SMOOI(proc->clsp), HCL_OOP_TO_SMOOI(proc->clsp) - HCL_OOP_TO_SMOOI(proc->exst), HCL_OOP_TO_SMOOI(proc->clst));
#endif
@ -930,12 +934,16 @@ static void terminate_process (hcl_t* hcl, hcl_oop_process_t proc)
{
hcl_oop_process_t nrp;
/* terminating the active process */
HCL_ASSERT (hcl, proc->state == HCL_SMOOI_TO_OOP(PROC_STATE_RUNNING));
nrp = find_next_runnable_process(hcl);
STORE_ACTIVE_SP (hcl); /* commit the stack pointer before termination */
unchain_from_processor (hcl, proc, PROC_STATE_TERMINATED);
reset_process_stack_pointers (hcl, proc); /* invalidate the process stack */
proc->current_context = proc->initial_context; /* not needed but just in case */
/* a runnable or running process must not be chanined to the
* process list of a semaphore */
HCL_ASSERT (hcl, (hcl_oop_t)proc->sem == hcl->_nil);
@ -960,11 +968,14 @@ static void terminate_process (hcl_t* hcl, hcl_oop_process_t proc)
}
else
{
/* there are other processes to schedule */
switch_to_process (hcl, nrp, PROC_STATE_TERMINATED);
}
}
else
{
/* termiante a runnable process which is not an actively running process */
HCL_ASSERT (hcl, proc->state == HCL_SMOOI_TO_OOP(PROC_STATE_RUNNABLE));
unchain_from_processor (hcl, proc, PROC_STATE_TERMINATED);
reset_process_stack_pointers (hcl, proc); /* invalidate the process stack */
}
@ -2137,7 +2148,8 @@ static hcl_oop_block_t find_cmethod_noseterr (hcl_t* hcl, hcl_oop_class_t class_
/* TODO: implement method cache */
HCL_ASSERT (hcl, HCL_IS_CLASS(hcl, class_));
HCL_ASSERT (hcl, HCL_IS_SYMBOL(hcl, op_name));
/*HCL_ASSERT (hcl, HCL_IS_SYMBOL(hcl, op_name));*/
HCL_ASSERT (hcl, HCL_OBJ_IS_CHAR_POINTER(op_name));
name.ptr = HCL_OBJ_GET_CHAR_SLOT(op_name);
name.len = HCL_OBJ_GET_SIZE(op_name);
@ -2179,15 +2191,16 @@ static hcl_oop_block_t find_cmethod_noseterr (hcl_t* hcl, hcl_oop_class_t class_
return HCL_NULL;
}
static hcl_oop_block_t find_imethod_noseterr (hcl_t* hcl, hcl_oop_class_t class_, hcl_oop_t op, int to_super, hcl_ooi_t* ivaroff, hcl_oop_class_t* owner)
static hcl_oop_block_t find_imethod_noseterr (hcl_t* hcl, hcl_oop_class_t class_, hcl_oop_t op_name, int to_super, hcl_ooi_t* ivaroff, hcl_oop_class_t* owner)
{
hcl_oocs_t name;
HCL_ASSERT (hcl, HCL_IS_CLASS(hcl, class_));
HCL_ASSERT (hcl, HCL_IS_SYMBOL(hcl, op));
/*HCL_ASSERT (hcl, HCL_IS_SYMBOL(hcl, op_name));*/
HCL_ASSERT (hcl, HCL_OBJ_IS_CHAR_POINTER(op_name));
name.ptr = HCL_OBJ_GET_CHAR_SLOT(op);
name.len = HCL_OBJ_GET_SIZE(op);
name.ptr = HCL_OBJ_GET_CHAR_SLOT(op_name);
name.len = HCL_OBJ_GET_SIZE(op_name);
if (to_super)
{
@ -2234,7 +2247,8 @@ static HCL_INLINE int send_message (hcl_t* hcl, hcl_oop_t rcv, hcl_oop_t msg, in
hcl_ooi_t ivaroff;
int x;
HCL_ASSERT (hcl, HCL_IS_SYMBOL(hcl, msg));
HCL_ASSERT (hcl, HCL_OBJ_IS_CHAR_POINTER(msg));
/*HCL_ASSERT (hcl, HCL_IS_SYMBOL(hcl, msg));*/
/* ============================= */
/* TODO: implement methods cache */
@ -2272,7 +2286,7 @@ static HCL_INLINE int send_message (hcl_t* hcl, hcl_oop_t rcv, hcl_oop_t msg, in
static HCL_INLINE int do_throw (hcl_t* hcl, hcl_oop_t val, hcl_ooi_t ip)
{
hcl_oop_context_t catch_ctx;
hcl_ooi_t catch_ip, clsp;
hcl_ooi_t catch_ip, clsp, sp;
if (HCL_EXSTACK_IS_EMPTY(hcl))
{
@ -2300,7 +2314,7 @@ static HCL_INLINE int do_throw (hcl_t* hcl, hcl_oop_t val, hcl_ooi_t ip)
}
/* pop the exception stack to get information to rewind context */
HCL_EXSTACK_POP_TO (hcl, catch_ctx, catch_ip, clsp);
HCL_EXSTACK_POP_TO (hcl, catch_ctx, catch_ip, clsp, sp);
/* discard unfinished class definitions for the exception thrown.
*
@ -2319,6 +2333,8 @@ static HCL_INLINE int do_throw (hcl_t* hcl, hcl_oop_t val, hcl_ooi_t ip)
SWITCH_ACTIVE_CONTEXT (hcl, catch_ctx);
hcl->ip = catch_ip; /* override the instruction pointer */
hcl->sp = sp; /* restore the stack pointer of the active process context */
/* push the exception value to the stack */
HCL_STACK_PUSH (hcl, val);
return 0;
@ -2893,7 +2909,7 @@ switch_to_next:
/* ------------------------------------------------------------------------- */
static HCL_INLINE int do_return (hcl_t* hcl, hcl_oop_t return_value)
static HCL_INLINE int do_return_from_home (hcl_t* hcl, hcl_oop_t return_value)
{
/* if (hcl->active_context == hcl->processor->active->initial_context) // read the interactive mode note below... */
if ((hcl_oop_t)hcl->active_context->home == hcl->_nil)
@ -2967,13 +2983,13 @@ static HCL_INLINE int do_return (hcl_t* hcl, hcl_oop_t return_value)
}
hcl->active_context->home->ip = HCL_SMOOI_TO_OOP(-1); /* mark that this context has returned */
hcl->ip = -1; /* mark that the active context has returned. saved into hcl->active_context->ip in SWITCH_ACTIVE_CONTEXT() */
hcl->ip = -1; /* mark that the active context has returned. committed to hcl->active_context->ip in SWITCH_ACTIVE_CONTEXT() */
SWITCH_ACTIVE_CONTEXT (hcl, hcl->active_context->home->sender);
/* push the return value to the stack of the new active context */
HCL_STACK_PUSH (hcl, return_value);
#if 0
/* stack dump */
HCL_DEBUG1 (hcl, "****** non local returning %O\n", return_value);
@ -3005,18 +3021,69 @@ static HCL_INLINE void do_return_from_block (hcl_t* hcl)
}
else
{
/* The compiler produces the class_exit instruction and the try_exit instruction
* for return, break, continue in a class defintion scope and in a try-catch scope
* respectively.
/*
TODO: should i restore the class stack pointer too???
let context remeber the it and use it to restore
[CASE 1]
(defclass X
; ....
(return 20) ; the class defintion isn't over, but return is executed?? or simply disallow return in the class context outside a method?
(return 20) ; the class defintion isn't over, but return is executed,
; ....
)
*/
[CASE 2]
(try
(defclass C
(return 200)
(printf "============================\n"))
catch (e)
(printf "EXCEPTION => %O\n" e)
)
[CASE 3]
(defclass C
(try
(return 99)
catch (e)
(printf "EXCEPTOIN => %O\n" e)
)
(printf "============================\n")
)
[CASE 4]
(try
(defclass C
(try
(return 99)
catch (e)
(printf "EXCEPTOIN => %O\n" e)
)
(printf "============================\n")
)
catch (e)
(printf "EXCEPTOIN => %O\n" e)
)
[CASE 5]
(try
(defclass D
(defclass C
(try
(return 99)
catch (e)
(printf "EXCEPTOIN => %O\n" e)
)
(printf "============================\n")
)
}
catch (e)
(printf "EXCEPTOIN => %O\n" e)
)
* the actual return instruction handler doesn't need to care about the
* class stack and exception stack.
*/
/* it is a normal block return as the active block context
* is not the initial context of a process */
@ -3063,9 +3130,34 @@ static int execute (hcl_t* hcl)
if (hcl->abort_req < 0) goto oops;
if (hcl->abort_req > 0 || (!hcl->no_proc_switch && switch_process_if_needed(hcl) == 0)) break;
if (HCL_UNLIKELY(hcl->ip >= HCL_FUNCTION_GET_CODE_SIZE(hcl->active_function)))
if (HCL_UNLIKELY(hcl->ip < 0 || hcl->ip >= HCL_FUNCTION_GET_CODE_SIZE(hcl->active_function)))
{
HCL_DEBUG2 (hcl, "Stopping execution as IP reached the end of bytecode(%zu) - SP %zd\n", hcl->code.bc.len, hcl->sp);
if (hcl->ip < 0)
{
/* do_return_from_home() implements a simple check against a dead context.
* but the check is far from perfect. there are many ways to return from an
* active context and enter a dead context thereafter.
(defun t(f)
(set q (lambda()
(printf "hello word\n")
(return-from-home 200)
))
(f)
)
(defun x()
(t (lambda() (return-from-home 100)))
(printf ">>>>>>>>>>>>>>>>>>>>>>>>\n");
)
(x) ; x is exited by (return-from-home 100) triggered by (f)
(printf "------------------------\n")
(q) ; (return-from-home 200) exits t and since t is called from x, it flows back to the dead x.
*/
HCL_DEBUG1 (hcl, "Stopping execution as a dead context gets active - IP %zd\n", hcl->ip);
}
else
{
HCL_DEBUG2 (hcl, "Stopping execution as IP reached the end of bytecode(%zu) - IP %zd\n", hcl->code.bc.len, hcl->ip);
}
return_value = hcl->_nil;
goto handle_return;
}
@ -3531,20 +3623,23 @@ if (do_throw(hcl, hcl->_nil, fetched_instruction_pointer) <= -1)
catch_ip = hcl->ip + b1;
/* TODO: ip overflow check? */
clsp = HCL_CLSTACK_GET_SP(hcl);
HCL_EXSTACK_PUSH (hcl, hcl->active_context, catch_ip, clsp);
HCL_EXSTACK_PUSH (hcl, hcl->active_context, catch_ip, clsp, hcl->sp);
break;
}
case HCL_CODE_TRY_ENTER2:
{
hcl_ooi_t catch_ip, clsp;
FETCH_PARAM_CODE_TO (hcl, b1);
LOG_INST_1 (hcl, "try_enter2 %zu", b1);
catch_ip = hcl->ip + MAX_CODE_JUMP + b1;
/* TODO: ip overflow check? */
clsp = HCL_CLSTACK_GET_SP(hcl);
HCL_EXSTACK_PUSH (hcl, hcl->active_context, catch_ip, clsp);
HCL_EXSTACK_PUSH (hcl, hcl->active_context, catch_ip, clsp, hcl->sp);
break;
}
@ -3860,7 +3955,8 @@ if (do_throw(hcl, hcl->_nil, fetched_instruction_pointer) <= -1)
handle_send_2:
rcv = HCL_STACK_GETRCV(hcl, b1);
op = HCL_STACK_GETOP(hcl, b1);
if (!HCL_IS_SYMBOL(hcl, op))
/*if (!HCL_IS_SYMBOL(hcl, op))*/
if (!HCL_OBJ_IS_CHAR_POINTER(op))
{
hcl_seterrbfmt (hcl, HCL_ECALL, "unable to send %O to %O - invalid message", op, rcv); /* TODO: change to HCL_ESEND?? */
goto cannot_send;
@ -3879,6 +3975,7 @@ if (do_throw(hcl, hcl->_nil, fetched_instruction_pointer) <= -1)
{
hcl_seterrbfmt (hcl, HCL_ECALL, "unable to send %O to %O - invalid receiver", op, rcv); /* TODO: change to HCL_ESEND?? */
cannot_send:
//HCL_STACK_POPS (hcl, b1 + 2); /* pop the receiver, message, and arguments as the call fails. TODO: check if this clearing is correct */
if (do_throw_with_internal_errmsg(hcl, fetched_instruction_pointer) >= 0) break;
goto oops_with_errmsg_supplement;
}
@ -4315,7 +4412,8 @@ if (do_throw(hcl, hcl->_nil, fetched_instruction_pointer) <= -1)
break;
case HCL_CODE_RETURN_STACKTOP:
/* this implements the non-local return. the non-local return is not compatible with stack based try-catch implementation. */
/* [NOTE] this implements the non-local return. the non-local return is not compatible with stack based try-catch implementation.
* [TODO] can make it compatiable? */
LOG_INST_0 (hcl, "return_stacktop");
return_value = HCL_STACK_GETTOP(hcl);
HCL_STACK_POP (hcl);
@ -4327,7 +4425,7 @@ if (do_throw(hcl, hcl->_nil, fetched_instruction_pointer) <= -1)
handle_return:
hcl->last_retv = return_value;
if (do_return(hcl, return_value) <= -1) goto oops;
if (do_return_from_home(hcl, return_value) <= -1) goto oops;
break;
case HCL_CODE_RETURN_FROM_BLOCK: