elk/src/cont.c

365 lines
10 KiB
C

/* cont.c: Continuations and dynamic-wind.
*
* $Id$
*
* Copyright 1990, 1991, 1992, 1993, 1994, 1995, Oliver Laumann, Berlin
* Copyright 2002, 2003 Sam Hocevar <sam@zoy.org>, Paris
*
* This software was derived from Elk 1.2, which was Copyright 1987, 1988,
* 1989, Nixdorf Computer AG and TELES GmbH, Berlin (Elk 1.2 has been written
* by Oliver Laumann for TELES Telematic Services, Berlin, in a joint project
* between TELES and Nixdorf Microprocessor Engineering, Berlin).
*
* Oliver Laumann, TELES GmbH, Nixdorf Computer AG and Sam Hocevar, as co-
* owners or individual owners of copyright in this software, grant to any
* person or company a worldwide, royalty free, license to
*
* i) copy this software,
* ii) prepare derivative works based on this software,
* iii) distribute copies of this software or derivative works,
* iv) perform this software, or
* v) display this software,
*
* provided that this notice is not removed and that neither Oliver Laumann
* nor Teles nor Nixdorf are deemed to have made any representations as to
* the suitability of this software for any purpose nor are held responsible
* for any defects of this software.
*
* THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
*/
#include "config.h"
#include <string.h>
#include "kernel.h"
extern void Switch_Environment (Object);
void Jump_Cont (struct S_Control *, Object);
void Do_Wind (Object);
/* The C library versions of longjmp on the VAX and the Convex unwind
* the stack. As Jump_Cont below installs a new stack before calling
* longjmp, the standard version cannot be used. The following simplistic
* version of setjmp/longjmp is used instead:
*/
#if defined(vax) || defined(__vax__)
__asm__(" .globl _setjmp");
__asm__("_setjmp:");
__asm__(" .word 0");
__asm__(" movl 4(ap),r0");
__asm__(" movq r2,(r0)+");
__asm__(" movq r4,(r0)+");
__asm__(" movq r6,(r0)+");
__asm__(" movq r8,(r0)+");
__asm__(" movq r10,(r0)+");
__asm__(" movl fp,(r0)+");
__asm__(" movq 4(fp),(r0)+");
__asm__(" movq 12(fp),(r0)+");
__asm__(" movq 20(fp),(r0)");
__asm__(" clrl r0");
__asm__(" ret");
__asm__(" .globl _longjmp");
__asm__("_longjmp:");
__asm__(" .word 0");
__asm__(" movl 4(ap),r0");
__asm__(" movq (r0)+,r2");
__asm__(" movq (r0)+,r4");
__asm__(" movq (r0)+,r6");
__asm__(" movq (r0)+,r8");
__asm__(" movq (r0)+,r10");
__asm__(" movl (r0)+,r1");
__asm__(" movq (r0)+,4(r1)");
__asm__(" movq (r0)+,12(r1)");
__asm__(" movq (r0),20(r1)");
__asm__(" movl 8(ap),r0");
__asm__(" movl r1,fp");
__asm__(" ret");
#endif
#if defined(convex) || defined(__convex__)
convex_longjmp (char *p, int i) {
__asm__("ld.w 4(ap),s0");
__asm__("ld.w 0(ap),a1");
__asm__("ld.w 12(a1),a7");
__asm__("ld.w 16(a1),a0");
__asm__("ld.w 8(a1),a3");
__asm__("mov a3,psw");
__asm__("ld.w 4(a1),a2");
__asm__("jmp 0(a2)");
}
#define longjmp convex_longjmp
#endif
WIND *First_Wind, *Last_Wind;
static Object Cont_Value;
#ifndef HAVE_ALLOCA
static Object Cont_GCsave;
#endif
int Check_Stack_Grows_Down () {
char foo;
return &foo < stkbase;
}
/* Stack_Size returns the current stack size relative to stkbase.
* It works independent of the direction into which the stack grows
* (the stack grows upwards on HP-PA based machines and Pyramids).
*/
unsigned int Stack_Size () {
char foo;
return Stack_Grows_Down ? stkbase-&foo : &foo-stkbase;
}
void Grow_Stack (struct S_Control *cp, Object val) {
char buf[100];
/* Prevent the optimizer from optimizing buf away:
*/
memset (buf, 0, 1);
Jump_Cont (cp, val);
}
void Jump_Cont (struct S_Control *cp, Object val) {
static struct S_Control *p;
static char *from, *to; /* Must not be allocated on stack */
static int i; /* Ditto */
char foo;
/* Reinstall the saved stack contents; take stack direction
* into account. cp must be put into a static variable, as
* variables living on the stack cannot be referenced any
* longer after the new stack has been installed.
*
* (The asm below must not be the first statement in the function
* to prevent buggy Sun ANSI SPARCompiler C 2.0.1 from emitting
* it at the wrong position.)
*/
p = cp;
Cont_Value = val;
if (Stack_Grows_Down) {
if (stkbase < &foo + p->size) Grow_Stack (cp, val);
to = stkbase - p->size;
} else {
if (stkbase + p->size > &foo) Grow_Stack (cp, val);
to = stkbase;
}
from = p->stack;
#if defined(sparc) || defined(__sparc__)
__asm__("t 0x3"); /* Flush register window */
#endif
for (i = p->size; i > 0; i--)
*to++ = *from++;
longjmp (p->j, 1);
}
#ifndef HAVE_ALLOCA
Object Terminate_Cont (Object cont) {
Free_Mem_Nodes (CONTROL(cont)->memlist);
return Void;
}
#endif
Object P_Control_Pointp (Object x) {
return TYPE(x) == T_Control_Point ? True : False;
}
Object P_Call_With_Current_Continuation (Object proc) {
register int t;
t = TYPE(proc);
if (t != T_Primitive && t != T_Compound && t != T_Control_Point)
Wrong_Type_Combination (proc, "procedure");
return Internal_Call_CC (0, proc);
}
Object Internal_Call_CC (int from_dump, Object proc) {
Object control, ret, gcsave;
register struct S_Control *cp;
register char *p, *to;
register int size;
GC_Node3;
control = gcsave = Null;
GC_Link3 (proc, control, gcsave);
#ifndef HAVE_ALLOCA
gcsave = Save_GC_Nodes ();
#endif
size = Stack_Size ();
size = (size + 7) & ~7;
control = Alloc_Object (size + sizeof (struct S_Control) - 1,
T_Control_Point, 0);
cp = CONTROL(control);
cp->env = The_Environment;
cp->gclist = GC_List;
cp->firstwind = First_Wind;
cp->lastwind = Last_Wind;
cp->tailcall = Tail_Call;
cp->intrlevel = Intr_Level;
cp->size = size;
cp->memsave = Null;
cp->gcsave = gcsave;
#if defined(sparc) || defined(__sparc__)
__asm__("t 0x3"); /* Flush register window */
#endif
/* Save the current stack contents; take stack direction
* into account. delta holds the number of bytes by which
* the stack contents has been moved in memory (it is required
* to access variables on the saved stack later):
*/
p = Stack_Grows_Down ? stkbase - cp->size : stkbase;
to = cp->stack;
memcpy (to, p, cp->size);
cp->delta = to - p;
#ifndef HAVE_ALLOCA
Register_Object (control, (GENERIC)0, Terminate_Cont, 0);
Save_Mem_Nodes (control);
#endif
if (setjmp (CONTROL(control)->j) != 0) {
#ifndef HAVE_ALLOCA
Restore_GC_Nodes (Cont_GCsave);
#endif
if (Intr_Level == 0) {
Force_Enable_Interrupts;
} else {
Force_Disable_Interrupts;
}
return Cont_Value;
}
if (from_dump) {
#ifdef CAN_DUMP
Dump_Control_Point = control;
#endif
ret = False;
} else {
control = Cons (control, Null);
ret = Funcall (proc, control, 0);
}
GC_Unlink;
return ret;
}
void Funcall_Control_Point (Object control, Object argl, int eval) {
Object val, len;
register struct S_Control *cp;
register WIND *w, *wp, *cwp, *p;
register int delta = 0;
GC_Node3;
if (GC_In_Progress)
Fatal_Error ("jumping out of GC");
val = Null;
GC_Link3 (argl, control, val);
len = P_Length (argl);
if (FIXNUM(len) != 1)
Primitive_Error ("control point expects one argument");
val = Car (argl);
if (eval)
val = Eval (val);
delta = CONTROL(control)->delta;
wp = First_Wind;
cwp = CONTROL(control)->firstwind;
while (wp && cwp) {
p = (WIND *)NORM(wp);
if (!EQ(wp->inout,p->inout)) break;
wp = wp->next;
cwp = p->next;
}
if (wp) {
for (w = Last_Wind; w != wp->prev; w = w->prev)
Do_Wind (Cdr (w->inout));
}
while (cwp) {
delta = CONTROL(control)->delta;
p = (WIND *)NORM(cwp);
cwp = p->next;
Do_Wind (Car (p->inout));
}
GC_Unlink;
Disable_Interrupts;
cp = CONTROL(control);
Switch_Environment (cp->env);
GC_List = cp->gclist;
#ifndef HAVE_ALLOCA
Restore_Mem_Nodes (control);
Cont_GCsave = CONTROL(control)->gcsave;
#endif
First_Wind = cp->firstwind;
Last_Wind = cp->lastwind;
Intr_Level = cp->intrlevel;
Jump_Cont (cp, val);
/*NOTREACHED*/
}
void Do_Wind (Object w) {
Object oldenv, b, tmp;
if (TYPE(w) == T_Vector) { /* fluid-let */
oldenv = The_Environment;
Switch_Environment (VECTOR(w)->data[1]);
b = Lookup_Symbol (VECTOR(w)->data[0], 0);
if (Nullp (b))
Panic ("fluid-let");
tmp = VECTOR(w)->data[2];
VECTOR(w)->data[2] = Cdr (b);
Cdr (b) = tmp;
SYMBOL(Car (b))->value = tmp;
VECTOR(w)->data[1] = oldenv;
Switch_Environment (oldenv);
} else { /* dynamic-wind */
(void)Funcall (w, Null, 0);
}
}
void Add_Wind (register WIND *w, Object in, Object out) {
Object inout;
GC_Node2;
GC_Link2 (in, out);
inout = Cons (in, out);
w->inout = inout;
w->next = 0;
if (First_Wind == 0)
First_Wind = w;
else
Last_Wind->next = w;
w->prev = Last_Wind;
Last_Wind = w;
GC_Unlink;
}
Object P_Dynamic_Wind (Object in, Object body, Object out) {
WIND w, *first = First_Wind;
Object ret;
GC_Node4;
Check_Procedure (in);
Check_Procedure (body);
Check_Procedure (out);
ret = Null;
GC_Link4 (in, body, out, ret);
Add_Wind (&w, in, out);
(void)Funcall (in, Null, 0);
ret = Funcall (body, Null, 0);
(void)Funcall (out, Null, 0);
if ((Last_Wind = w.prev))
Last_Wind->next = 0;
First_Wind = first;
GC_Unlink;
return ret;
}
Object P_Control_Point_Environment (Object c) {
Check_Type (c, T_Control_Point);
return CONTROL(c)->env;
}