/* Copyright (C) 1989, Digital Equipment Corporation */ /* All rights reserved. */ /* See the file COPYRIGHT for a full description. */ /* Last Modified On Tue Jun 26 00:24:53 1990 By muller!mntgfx!lisch */ /* Modified On Fri Mar 9 14:16:52 PST 1990 By uunet!mntgfx!lisch */ #include #include #include #include #include #define PRIVATE static #define EXPORT #define IMPORT extern #define FORWARD /* for setjmp/longjmp stack frame manipulations */ #define SP 2 /* stack pointer */ #define AP -1 /* argument pointer, none on Apollo */ #define FP 3 /* frame pointer */ #define Frame_Pad 2 /* extra words to copy in Shell */ FORWARD PRIVATE void oops (); FORWARD PRIVATE void determine_context (); IMPORT _ThreadF__Shell (); typedef struct { char *self; int *stack; int *stack_top; int *stack_bottom; char *handlers; int errno; jmp_buf jmpbuf; } context; EXPORT int contextSize = sizeof (context); EXPORT char *_ThreadSup__self; EXPORT char *_ThreadSup__handlers; EXPORT int *_ThreadSup__stackLimit; /* We use _setjmp and _longjmp because longjmp is more expensive We also need to fake out Domain/OS and convince it that we are jumping backwards in the stack, even when we are not. To do this, we set the a6 and a7 registers (frame and stack pointers) to the destination. */ IMPORT apollo_set_regs(int a6, int a7); /* Assumptions: After the longjmp, only the SP and FP registers contain live values which point to the stack. In particular, this routine's return PC is not used. The compiler keeps its live variables (namely stack, proc, and closure) in registers or in the copied stack frame. Pros and cons of this approach: + no assembler needed (except on Apollo) + reasonably portable + avoids depending on the compiler to keep variables in registers + allows the check for return from proc - does not place proc at the root of the stack */ PRIVATE int intermediate_stack[100]; PRIVATE jmp_buf base_jmpbuf; PRIVATE int growth; PRIVATE context *current_context; PRIVATE int seal = 123456; EXPORT void InitTopContext (self, c) char *self; context *c; { jmp_buf env; c->self = self; c->stack = NULL; c->stack_top = 0; c->stack_bottom = env; current_context = c; /* determine what should go in the stack of future threads */ if (_setjmp (env) == 0) { determine_context (env); } else { oops (NULL, "returning from top level setjmp", 1); } } PRIVATE void determine_context (before_call) jmp_buf before_call; { int *oldSP = (int *) before_call [SP]; int *mySP, *newSP; if (_setjmp (base_jmpbuf) == 0) { /* first time through */ mySP = (int *) base_jmpbuf [SP]; /* Compare oldSP and mySP to learn stack my frame size and growth direction. Copy the frame (plus pad) to intermediate_stack and remember where that should go in the stacks */ if (mySP == oldSP) { /* stack did not grow ! */ oops (NULL, "stack did not grow", 2); } else if (mySP < oldSP) { /* stack grows downward */ newSP = intermediate_stack; oldSP += Frame_Pad; for (growth = 0; mySP <= oldSP; *newSP++ = *oldSP--, growth--); } else { /* stack grows upward */ newSP = intermediate_stack; oldSP -= Frame_Pad; for (growth = 0; mySP >= oldSP; *newSP++ = *oldSP++, growth++); } return; } else { /* running on the other stack */ _ThreadSup__self = current_context->self; _ThreadSup__handlers = current_context->handlers; _ThreadSup__stackLimit = current_context->stack_top; errno = current_context->errno; _ThreadF__Shell (); fprintf(stderr, "Fatal: Return to ThreadSup.Shell\n"); exit(1); } } EXPORT void InitContext (self, c, stack, size) char *self; context *c; int *stack; int size; { int dsp; c->self = self; c->stack = stack; c->stack_top = stack; c->stack_bottom = stack + size - 1; c->handlers = NULL; c->errno = errno; bcopy (base_jmpbuf, c->jmpbuf, sizeof (jmp_buf)); /* fill the stack with some well-know value; that should make programs a bit more deterministic (hence easier to debug) */ bzero (stack, sizeof (stack)); if (growth < 0) { { int *src = intermediate_stack; int *dst = stack + size - 2; int i; for (i = 0; i >= growth; i--) { *dst-- = *src++; } c->stack_top = stack + 1; dsp = (int) dst - (int) base_jmpbuf [SP]; }} else { { int *src = intermediate_stack; int *dst = stack + 1; int i; for (i =0; i <= growth; i++) { *dst++ = *src++; } c->stack_top = stack + size - 2; dsp = (int) dst - (int) base_jmpbuf [SP]; }} *(c->stack_top) = seal; *(c->stack_bottom) = seal; c->jmpbuf [SP] += dsp; if (AP >= 0) { /* needs vax testing */ c->jmpbuf [AP] += dsp; } if (FP >= 0) { /* needs vax testing */ c->jmpbuf [FP] += dsp; } } EXPORT void Transfer (to) context *to; { if ((current_context->stack != NULL) && ((*(current_context->stack_top) != seal) || (*(current_context->stack_bottom) != seal))) { oops (current_context, "from stack corrupted", 3); } if ((to->stack != NULL) && ((*(to->stack_top) != seal) || (*(to->stack_bottom) != seal))) { oops (to, "to stack corrupted", 4); } if (_setjmp (current_context->jmpbuf) == 0) { current_context->handlers = _ThreadSup__handlers; current_context->errno = errno; current_context = to; apollo_set_regs(to->jmpbuf[FP], to->jmpbuf[SP]); _longjmp (to->jmpbuf, 1); } else { _ThreadSup__self = current_context->self; _ThreadSup__handlers = current_context->handlers; _ThreadSup__stackLimit = current_context->stack_top; errno = current_context->errno; return; } } PRIVATE void oops (c, msg, num) context *c; char *msg; int num; { fprintf (stderr, "ThreadSup internal error %d: %s\n", num, msg); if (c != NULL) { fprintf (stderr, " &stack=%#x\n", c->stack); fprintf (stderr, " self = %#x\n", c->self); if (c->stack != NULL) { fprintf (stderr, " seal0 = %d\n", *(c->stack_top)); fprintf (stderr, " seal1 = %d\n", *(c->stack_bottom)); }} exit (1); } EXPORT void Tos (c, start, stop) context *c; int *start, *stop; { if (growth > 0) { *start = (int) (c->stack_top + 1); *stop = c->jmpbuf [SP]; } else { *start = c->jmpbuf [SP]; *stop = (int) (c->stack_bottom - 1); } } EXPORT void SelfTos (start, stop) int *start, *stop; { int tos; if (growth > 0) { *start = (int) (current_context->stack_top + 1); *stop = (int) &tos; } else { *start = (int) &tos; *stop = (int) (current_context->stack_bottom - 1); } }