/* Copyright (C) 1989, Digital Equipment Corporation */ /* All rights reserved. */ /* See the file COPYRIGHT for a full description. */ /* Last Modified On Tue Jun 26 00:25:56 1990 By muller */ #include #include #include #include #include #define PRIVATE static #define EXPORT #define IMPORT extern #define FORWARD /* for setjmp/longjmp stack frame manipulations */ #define SP 0 /* stack pointer as determined by Z Stern by looking at implementation of setjmp.s. */ #define AP -1 /* argument pointer, none in IBMRT */ #define FP -1 /* frame pointer, none in IBMRT */ #define Frame_Pad 2 /* extra words to copy in Shell */ extern int errno; 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 expensive and (at least on MIPS) longjmp insists we jump backward on the stack. */ /* 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 + 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; _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); } }