Commits

Armin Rigo committed fd80fbd

Untested draft: a setjmp()-alike that can be used even if
the function containing the original setjmp() call returned.

  • Participants
  • Parent commits 615acc2

Comments (0)

Files changed (2)

hack/pypy-hack/rewind_setjmp/rewind_setjmp.c

+#include "rewind_setjmp.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <alloca.h>
+
+__thread struct _rewind_jmp_s _rewind_jmp;
+
+#define JMPBUF_TRAP_INDEX   1
+#define JMPBUF_TRAP         ((void *)-1)
+
+
+struct _rewind_jmp_moved_s {
+    struct _rewind_jmp_moved_s *next;
+    size_t size;
+};
+#define RJM_HEADER  sizeof(struct _rewind_jmp_moved_s)
+
+
+__attribute__((noinline, noreturn))
+void rewind_jmp_longjmp(void)
+{
+    assert(_rewind_jmp.moved_off_base != NULL);
+
+    while (_rewind_jmp.moved_off) {
+        struct _rewind_jmp_moved_s *p = _rewind_jmp.moved_off;
+        char *stack_free = (char *)_rewind_jmp.head;
+        char *target = (char *)_rewind_jmp.moved_off_base;
+        target -= p->size;
+        if (target < stack_free) {
+            /* need more stack space! */
+            _rewind_jmp.head = (rewind_jmp_buf *) alloca(stack_free - target);
+            rewind_jmp_longjmp();
+        }
+        memcpy(target, ((char *)p) + RJM_HEADER, p->size);
+        _rewind_jmp.moved_off_base = target;
+        _rewind_jmp.moved_off = p->next;
+        free(p);
+    }
+
+    assert(_rewind_jmp.jmpbuf[JMPBUF_TRAP_INDEX] != JMPBUF_TRAP);
+    __builtin_longjmp(_rewind_jmp.jmpbuf);
+}
+
+__attribute__((noinline))
+void _rewind_jmp_copy_stack_slice(void)
+{
+    if (_rewind_jmp.head == NULL) {
+        _rewind_jmp_free_stack_slices();
+        return;
+    }
+    char *base = (char *)_rewind_jmp.moved_off_base;
+    assert(_rewind_jmp.moved_off_base->prev == _rewind_jmp.head);
+    _rewind_jmp.moved_off_base = _rewind_jmp.head;
+    char *stop = (char *)_rewind_jmp.moved_off_base;
+    assert(stop > base);
+
+    struct _rewind_jmp_moved_s *next = (struct _rewind_jmp_moved_s *)
+        malloc(RJM_HEADER + (stop - base));
+    assert(next != NULL);    /* XXX out of memory */
+    next->next = _rewind_jmp.moved_off;
+    next->size = stop - base;
+    _rewind_jmp.moved_off = next;
+    memcpy(((char *)next) + RJM_HEADER, base, stop - base);
+}
+
+void _rewind_jmp_free_stack_slices(void)
+{
+    struct _rewind_jmp_moved_s *p = _rewind_jmp.moved_off;
+    struct _rewind_jmp_moved_s *pnext;
+    while (p) {
+        pnext = p->next;
+        free(p);
+        p = pnext;
+    }
+    _rewind_jmp.moved_off = NULL;
+    _rewind_jmp.jmpbuf[JMPBUF_TRAP_INDEX] = JMPBUF_TRAP;
+}

hack/pypy-hack/rewind_setjmp/rewind_setjmp.h

+#ifndef _REWIND_SETJMP_H_
+#define _REWIND_SETJMP_H_
+
+/************************************************************
+
+           :                   :       ^^^^^
+           |-------------------|    older frames in the stack
+           |   prev=0          |
+     ,---> | rewind_jmp_buf    |
+     |     |-------------------|
+     |     |                   |
+     |     :                   :
+     |     :                   :
+     |     |                   |
+     |     |-------------------|
+     `---------prev            |
+    ,----> | rewind_jmp_buf    |
+    |      +-------------------|
+    |      |                   |
+    |      :                   :
+    |      |                   |
+    |      |-------------------|
+    `----------prev            |
+     ,---> | rewind_jmp_buf    | <--------------- MOVED_OFF_BASE
+     |     |----------------  +-------------+
+     |     |                  | STACK COPY  |
+     |     |                  :             :
+     |     :                  |  size       |
+     |     |                  |  next       | <---- MOVED_OFF
+     |     |                  +---|------  +-------------+
+     |     |                   |  |        | STACK COPY  |
+     |     |-------------------|  |        : (SEQUEL)    :
+     `---------prev            |  |        :             :
+HEAD-----> | rewind_jmp_buf    |  |        |             |
+           |-------------------|  |        |  size       |
+                                  `------> |  next=0     |
+                                           +-------------+
+
+
+************************************************************/
+
+typedef struct _rewind_jmp_buf {
+    struct _rewind_jmp_buf *prev;
+} rewind_jmp_buf;
+
+struct _rewind_jmp_s {
+    rewind_jmp_buf *head;
+    rewind_jmp_buf *moved_off_base;
+    struct _rewind_jmp_moved_s *moved_off;
+    void *jmpbuf[5];
+};
+
+extern __thread struct _rewind_jmp_s _rewind_jmp;
+
+
+#define rewind_jmp_enterframe(rjbuf)   do {     \
+    rjbuf.prev = _rewind_jmp.head;              \
+    _rewind_jmp.head = &rjbuf;                  \
+} while (0)
+
+#define rewind_jmp_leaveframe(rjbuf)   do {     \
+    _rewind_jmp.head = rjbuf.prev;              \
+    if (&rjbuf == _rewind_jmp.moved_off_base)   \
+        _rewind_jmp_copy_stack_slice();         \
+} while (0)
+
+#define rewind_jmp_setjmp()   do {                              \
+    if (_rewind_jmp.moved_off) _rewind_jmp_free_stack_slices(); \
+    while (__builtin_setjmp(_rewind_jmp.jmpbuf) != 0) { }       \
+    _rewind_jmp.moved_off_base = _rewind_jmp.head;              \
+} while (0)
+
+#define rewind_jmp_forget()  do {                               \
+    if (_rewind_jmp.moved_off) _rewind_jmp_free_stack_slices(); \
+    _rewind_jmp.moved_off_base = 0;                             \
+} while (0)
+
+void rewind_jmp_longjmp(void);
+void _rewind_jmp_copy_stack_slice(void);
+void _rewind_jmp_free_stack_slices(void);
+
+#endif