From 21d483d14e2aea67e811cf83539a623bf5b44cc2 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Tue, 13 May 2008 11:47:06 +0000 Subject: - update to 1.8.5 - fix continuations on ia64 - remove umask setting from scriptlet, rpm sets it now --- .cvsignore | 2 +- guile-1.8.5-conts.patch | 474 ++++++++++++++++++++++++++++++++++++++++++++++++ guile.spec | 15 +- sources | 2 +- 4 files changed, 487 insertions(+), 6 deletions(-) create mode 100644 guile-1.8.5-conts.patch diff --git a/.cvsignore b/.cvsignore index 14ac502..861fac3 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1 +1 @@ -guile-1.8.4.tar.gz +guile-1.8.5.tar.gz diff --git a/guile-1.8.5-conts.patch b/guile-1.8.5-conts.patch new file mode 100644 index 0000000..66e979c --- /dev/null +++ b/guile-1.8.5-conts.patch @@ -0,0 +1,474 @@ +From 78aa4a8850396801f2e5515565d051b45b4f15b5 Mon Sep 17 00:00:00 2001 +From: Neil Jerram +Date: Thu, 8 May 2008 00:29:53 +0100 +Subject: [PATCH] Fix continuation problems on IA64. + +* Specific problems in IA64 make check + +** test-unwind + +Representation of the relevant dynamic context: + + non-rewindable + catch frame make cont. + o----o-----a----------b-------------c + \ + \ call cont. + o-----o-----------d + +A continuation is captured at (c), with a non-rewindable frame in the +dynamic context at (b). If a rewind through that frame was attempted, +Guile would throw to the catch at (a). Then the context unwinds back +past (a), then winds forwards again, and the captured continuation is +called at (d). + +We should end up at the catch at (a). On ia64, we get an "illegal +instruction". + +The problem is that Guile does not restore the ia64 register backing +store (RBS) stack (which is saved off when the continuation is +captured) until all the unwinding and rewinding is done. Therefore, +when the rewind code (scm_i_dowinds) hits the non-rewindable frame at +(b), the RBS stack hasn't yet been restored. The throw finds the +jmp_buf (for the catch at (a)) correctly from the dynamic context, and +jumps back to (a), but the RBS stack is invalid, hence the illegal +instruction. + +This could be fixed by restoring the RBS stack earlier, at the same +point (copy_stack) where the normal stack is restored. But that +causes a problem in the next test... + +** continuations.test + +The dynamic context diagram for this case is similar: + + non-rewindable + catch frame make cont. + a----x-----o----------b-------------c + \ + \ call cont. + o-------d + +The only significant difference is that the catch point (a) is +upstream of where the dynamic context forks. This means that the RBS +stack at (d) already contains the correct RBS contents for throwing +back to (a), so it doesn't matter whether the RBS stack that was saved +off with the continuation gets restored. + +This test passes with the Guile 1.8.4 code, but fails (with an +"illegal instruction") when the code is changed to restore the RBS +stack earlier as described above. + +The problem now is that the RBS stack is being restored _too_ early; +specifically when there is still stuff to do that relies on the old +RBS contents. When a continuation is called, the sequence of relevant +events is: + + (1) Grow the (normal) stack until it is bigger than the (normal) + stack saved off in the continuation. (scm_dynthrow, grow_stack) + + (2) scm_i_dowinds calls itself recursively, such that + + (2.1) for each rewind (from (x) to (c)) that will be needed, + another frame is added to the stack (both normal and RBS), + with local variables specifying the required rewind; the + rewinds don't actually happen yet, they will happen when + the stack unwinds again through these frames + + (2.2) required unwinds - back from where the continuation was + called (d) to the fork point (x) - are done immediately. + + (3) The normal (i.e. non-RBS) stack that was stored in the + continuation is restored (i.e. copied on top of the actual + stack). + + Note that this doesn't overwrite the frames that were added in + (2.1), because the growth in (1) ensures that the added frames + are beyond the end of the restored stack. + + (4) ? Restore the RBS stack here too ? + + (5) Return (from copy_stack) through the (2.1) frames, which means + that the rewinds now happen. + + (6) setcontext (or longjmp) to the context (c) where the + continuation was captured. + +The trouble is that step (1) does not create space in the RBS stack in +the same kind of way that it does for the normal stack. Therefore, if +the saved (in the continuation) RBS stack is big enough, it can +overwrite the RBS of the (2.1) frames that still need to complete. +This causes an illegal instruction when we return through those frames +and try to perform the rewinds. + +* Fix + +The key to the fix is that the saved RBS stack only needs to be +restored at some point before the next setcontext call, and that doing +it as close to the setcontext call as possible will avoid bad +interactions with the pre-setcontext stack. Therefore we do the +restoration at the last possible point, immediately before the next +setcontext call. + +The situation is complicated by there being two ways that the next +setcontext call can happen. + + - If the unwinding and rewinding is all successful, the next + setcontext will be the one from step (6) above. This is the + "normal" continuation invocation case. + + - If one of the rewinds throws an error, the next setcontext will + come from the throw implementation code. (And the one in step (6) + will never happen.) This is the rewind error case. + +In the rewind error case, the code calling setcontext knows nothing +about the continuation. So to cover both cases, we: + + - copy (in step (4) above) the address and length of the + continuation's saved RBS stack to the current thread state + (SCM_I_CURRENT_THREAD) + + - modify all setcontext callers so that they check the current + thread state for a saved RBS stack, and restore it if so before + calling setcontext. + +* Notes + +** I think rewinders cannot rely on using any stack data + +Unless it can be guaranteed that the data won't go into a register. +I'm not 100% sure about this, but I think it follows from the fact +that the RBS stack is not restored until after the rewinds have +happened. + +Note that this isn't a regression caused by the current fix. In Guile +1.8.4, the RBS stack was restored _after_ the rewinds, and this is +still the case now. + +** Most setcontext calls for `throw' don't need to change the RBS stack + +In the absence of continuation invocation, the setcontext call in the +throw implementation code always sets context to a place higher up the +same stack (both normal and RBS), hence no stack restoration is +needed. + +* Other changes + +** Using setcontext for all non-local jumps (for __ia64__) + +Along the way, I read a claim somewhere that setcontext was more +reliable than longjmp, in cases where the stack has been manipulated. + +I don't now have any reason to believe this, but it seems reasonable +anyway to leave the __ia64__ code using getcontext/setcontext, instead +of setjmp/longjmp. + +(I think the only possible argument against this would be performance - +if getcontext was significantly slower than setjmp. It that proves to +be the case, we should revisit this.) + +** Capping RBS base for non-main threads + +Somewhere else along the way, I hit a problem in GC, involving the RBS +stack of a non-main thread. The problem was, in +SCM_MARK_BACKING_STORE, that scm_ia64_register_backing_store_base was +returning a value that was massively greater than the value of +scm_ia64_ar_bsp, leading to a seg fault. This is because the +implementation of scm_ia64_register_backing_store_base is only valid +for the main thread. I couldn't find a neat way of getting the true +RBS base of a non-main thread, but one idea is simply to call +scm_ia64_ar_bsp when guilifying a thread, and use the value returned +as an upper bound for that thread's RBS base. (Note that the RBS +stack grows upwards.) + +(Were it not for scm_init_guile, we could be much more definitive +about this. We could take the value of scm_ia64_ar_bsp as a +definitive base address for the part of the RBS stack that Guile cares +about. We could also then discard +scm_ia64_register_backing_store_base.) +--- + libguile/ChangeLog | 35 +++++++++++++++++++++++++++ + libguile/__scm.h | 18 +++++++++++++- + libguile/continuations.c | 58 +++++++++++++++++++++------------------------ + libguile/continuations.h | 2 - + libguile/threads.c | 20 ++++++++++++++- + libguile/threads.h | 5 ++++ + libguile/throw.c | 6 ++++ + 7 files changed, 108 insertions(+), 36 deletions(-) + +diff --git a/libguile/ChangeLog b/libguile/ChangeLog +index 6c25443..dec6dcd 100644 +--- a/libguile/ChangeLog ++++ b/libguile/ChangeLog +@@ -1,3 +1,38 @@ ++2008-05-08 Neil Jerram ++ ++ * throw.c (scm_ithrow): For IA64 add a return statement, to ++ appease GCC. ++ ++ * threads.h (scm_i_thread): New IA64 fields: ++ register_backing_store_base and pending_rbs_continuation. ++ ++ * threads.c (guilify_self_1): For IA64: cap RBS base address at ++ the current value of scm_ia64_ar_bsp, and store the capped value ++ in thread state. ++ (SCM_MARK_BACKING_STORE): Use thread->register_backing_store_base ++ instead of scm_ia64_register_backing_store_base(). ++ (scm_threads_mark_stacks): Add "&" in "&t->regs", so that the code ++ works both for jmp_buf defined as an array, and jmp_buf defined as ++ a struct. ++ ++ * continuations.h (scm_t_contregs): Remove `fresh' and `ctx' ++ fields; these are now inside the IA64 definition of `jmp_buf'. ++ ++ * continuations.c (scm_make_continuation): Simplify, by moving ++ some of the IA64 code inside the definition of "setjmp", and by ++ some obvious commonizations. For IA64 register backing store ++ (RBS) stack base, use thread->register_backing_store_base instead ++ of scm_ia64_register_backing_store_base(). ++ (copy_stack): For IA64, store pointer to continuation being ++ invoked in thread state, so we can restore the continuation's RBS ++ stack just before the next setcontext call. ++ (copy_stack_and_call): Don't restore RBS stack explicitly here. ++ It will be restored, if appropriate, inside the longjmp call. ++ (scm_ia64_longjmp): New function. ++ ++ * __scm.h (setjmp, longjmp, jmp_buf): For IA64, implement using ++ getcontext and setcontext. ++ + 2008-05-07 Ludovic Courtès + + * numbers.c (scm_from_complex_double): Mark as `SCM_UNUSED'. +diff --git a/libguile/__scm.h b/libguile/__scm.h +index 3d6d9a7..b198f9d 100644 +--- a/libguile/__scm.h ++++ b/libguile/__scm.h +@@ -402,7 +402,23 @@ + # define setjmp setjump + # define longjmp longjump + # else /* ndef _CRAY1 */ +-# include ++# if defined (__ia64__) ++/* For IA64, emulate the setjmp API using getcontext. */ ++# include ++# include ++ typedef struct { ++ ucontext_t ctx; ++ int fresh; ++ } jmp_buf; ++# define setjmp(JB) \ ++ ( (JB).fresh = 1, \ ++ getcontext (&((JB).ctx)), \ ++ ((JB).fresh ? ((JB).fresh = 0, 0) : 1) ) ++# define longjmp(JB,VAL) scm_ia64_longjmp (&(JB), VAL) ++ void scm_ia64_longjmp (jmp_buf *, int); ++# else /* ndef __ia64__ */ ++# include ++# endif /* ndef __ia64__ */ + # endif /* ndef _CRAY1 */ + #endif /* ndef vms */ + +diff --git a/libguile/continuations.c b/libguile/continuations.c +index 39785a5..80a2790 100644 +--- a/libguile/continuations.c ++++ b/libguile/continuations.c +@@ -124,47 +124,30 @@ scm_make_continuation (int *first) + continuation->offset = continuation->stack - src; + memcpy (continuation->stack, src, sizeof (SCM_STACKITEM) * stack_size); + +-#ifdef __ia64__ +- continuation->fresh = 1; +- getcontext (&continuation->ctx); +- if (continuation->fresh) ++ *first = !setjmp (continuation->jmpbuf); ++ if (*first) + { ++#ifdef __ia64__ + continuation->backing_store_size = +- (char *) scm_ia64_ar_bsp(&continuation->ctx) ++ (char *) scm_ia64_ar_bsp(&continuation->jmpbuf.ctx) + - +- (char *) scm_ia64_register_backing_store_base (); ++ (char *) thread->register_backing_store_base; + continuation->backing_store = NULL; + continuation->backing_store = + scm_gc_malloc (continuation->backing_store_size, + "continuation backing store"); + memcpy (continuation->backing_store, +- (void *) scm_ia64_register_backing_store_base (), ++ (void *) thread->register_backing_store_base, + continuation->backing_store_size); +- *first = 1; +- continuation->fresh = 0; ++#endif /* __ia64__ */ + return cont; + } + else + { + SCM ret = continuation->throw_value; +- *first = 0; + continuation->throw_value = SCM_BOOL_F; + return ret; + } +-#else /* !__ia64__ */ +- if (setjmp (continuation->jmpbuf)) +- { +- SCM ret = continuation->throw_value; +- *first = 0; +- continuation->throw_value = SCM_BOOL_F; +- return ret; +- } +- else +- { +- *first = 1; +- return cont; +- } +-#endif /* !__ia64__ */ + } + #undef FUNC_NAME + +@@ -218,6 +201,9 @@ copy_stack (void *data) + copy_stack_data *d = (copy_stack_data *)data; + memcpy (d->dst, d->continuation->stack, + sizeof (SCM_STACKITEM) * d->continuation->num_stack_items); ++#ifdef __ia64__ ++ SCM_I_CURRENT_THREAD->pending_rbs_continuation = d->continuation; ++#endif + } + + static void +@@ -235,16 +221,26 @@ copy_stack_and_call (scm_t_contregs *continuation, SCM val, + scm_i_set_last_debug_frame (continuation->dframe); + + continuation->throw_value = val; +-#ifdef __ia64__ +- memcpy (scm_ia64_register_backing_store_base (), +- continuation->backing_store, +- continuation->backing_store_size); +- setcontext (&continuation->ctx); +-#else + longjmp (continuation->jmpbuf, 1); +-#endif + } + ++#ifdef __ia64__ ++void ++scm_ia64_longjmp (jmp_buf *JB, int VAL) ++{ ++ scm_i_thread *t = SCM_I_CURRENT_THREAD; ++ ++ if (t->pending_rbs_continuation) ++ { ++ memcpy (t->register_backing_store_base, ++ t->pending_rbs_continuation->backing_store, ++ t->pending_rbs_continuation->backing_store_size); ++ t->pending_rbs_continuation = NULL; ++ } ++ setcontext (&JB->ctx); ++} ++#endif ++ + /* Call grow_stack until the stack space is large enough, then, as the current + * stack frame might get overwritten, let copy_stack_and_call perform the + * actual copying and continuation calling. +diff --git a/libguile/continuations.h b/libguile/continuations.h +index 0274c1b..f6fb96a 100644 +--- a/libguile/continuations.h ++++ b/libguile/continuations.h +@@ -46,8 +46,6 @@ typedef struct + jmp_buf jmpbuf; + SCM dynenv; + #ifdef __ia64__ +- ucontext_t ctx; +- int fresh; + void *backing_store; + unsigned long backing_store_size; + #endif /* __ia64__ */ +diff --git a/libguile/threads.c b/libguile/threads.c +index 858a1eb..609fc99 100644 +--- a/libguile/threads.c ++++ b/libguile/threads.c +@@ -423,6 +423,22 @@ guilify_self_1 (SCM_STACKITEM *base) + t->pending_asyncs = 1; + t->last_debug_frame = NULL; + t->base = base; ++#ifdef __ia64__ ++ /* Calculate and store off the base of this thread's register ++ backing store (RBS). Unfortunately our implementation(s) of ++ scm_ia64_register_backing_store_base are only reliable for the ++ main thread. For other threads, therefore, find out the current ++ top of the RBS, and use that as a maximum. */ ++ t->register_backing_store_base = scm_ia64_register_backing_store_base (); ++ { ++ ucontext_t ctx; ++ void *bsp; ++ getcontext (&ctx); ++ bsp = scm_ia64_ar_bsp (&ctx); ++ if (t->register_backing_store_base > bsp) ++ t->register_backing_store_base = bsp; ++ } ++#endif + t->continuation_root = SCM_EOL; + t->continuation_base = base; + scm_i_pthread_cond_init (&t->sleep_cond, NULL); +@@ -1350,7 +1366,7 @@ SCM_DEFINE (scm_broadcast_condition_variable, "broadcast-condition-variable", 1, + scm_mark_locations ((SCM_STACKITEM *) &ctx.uc_mcontext, \ + ((size_t) (sizeof (SCM_STACKITEM) - 1 + sizeof ctx.uc_mcontext) \ + / sizeof (SCM_STACKITEM))); \ +- bot = (SCM_STACKITEM *) scm_ia64_register_backing_store_base (); \ ++ bot = (SCM_STACKITEM *) SCM_I_CURRENT_THREAD->register_backing_store_base; \ + top = (SCM_STACKITEM *) scm_ia64_ar_bsp (&ctx); \ + scm_mark_locations (bot, top - bot); } while (0) + #else +@@ -1374,7 +1390,7 @@ scm_threads_mark_stacks (void) + #else + scm_mark_locations (t->top, t->base - t->top); + #endif +- scm_mark_locations ((SCM_STACKITEM *) t->regs, ++ scm_mark_locations ((SCM_STACKITEM *) &t->regs, + ((size_t) sizeof(t->regs) + / sizeof (SCM_STACKITEM))); + } +diff --git a/libguile/threads.h b/libguile/threads.h +index 09939b0..d58a0fb 100644 +--- a/libguile/threads.h ++++ b/libguile/threads.h +@@ -28,6 +28,7 @@ + #include "libguile/root.h" + #include "libguile/iselect.h" + #include "libguile/dynwind.h" ++#include "libguile/continuations.h" + + #if SCM_USE_PTHREAD_THREADS + #include "libguile/pthread-threads.h" +@@ -107,6 +108,10 @@ typedef struct scm_i_thread { + SCM_STACKITEM *base; + SCM_STACKITEM *top; + jmp_buf regs; ++#ifdef __ia64__ ++ void *register_backing_store_base; ++ scm_t_contregs *pending_rbs_continuation; ++#endif + + } scm_i_thread; + +diff --git a/libguile/throw.c b/libguile/throw.c +index 1c25463..119d0bd 100644 +--- a/libguile/throw.c ++++ b/libguile/throw.c +@@ -824,6 +824,12 @@ scm_ithrow (SCM key, SCM args, int noreturn SCM_UNUSED) + /* Otherwise, it's some random piece of junk. */ + else + abort (); ++ ++#ifdef __ia64__ ++ /* On IA64, we #define longjmp as setcontext, and GCC appears not to ++ know that that doesn't return. */ ++ return SCM_UNSPECIFIED; ++#endif + } + + +-- +1.5.4.1 + diff --git a/guile.spec b/guile.spec index fafd521..5601544 100644 --- a/guile.spec +++ b/guile.spec @@ -1,12 +1,13 @@ Summary: A GNU implementation of Scheme for application extensibility Name: guile %define mver 1.8 -Version: 1.8.4 +Version: 1.8.5 Release: 1%{?dist} Source: ftp://ftp.gnu.org/pub/gnu/guile/guile-%{version}.tar.gz URL: http://www.gnu.org/software/guile/ Patch1: guile-1.8.4-multilib.patch Patch2: guile-1.8.4-testsuite.patch +Patch3: guile-1.8.5-conts.patch Patch4: guile-1.8.1-deplibs.patch License: GPLv2+ and LGPLv2+ Group: Development/Languages @@ -30,6 +31,7 @@ that you are developing. Summary: Libraries and header files for the GUILE extensibility library Group: Development/Libraries Requires: guile = %{epoch}:%{version} gmp-devel +Requires: pkgconfig %description devel The guile-devel package includes the libraries, header files, etc., @@ -44,6 +46,7 @@ install the guile package. %setup -q %patch1 -p1 -b .multilib %patch2 -p1 -b .testsuite +%patch3 -p1 -b .conts %patch4 -p1 -b .deplibs %build @@ -108,7 +111,6 @@ rm -f %{_datadir}/guile/site/slib{,cat} ln -sfT ../../slib %{_datadir}/guile/%{mver}/slib rm -f %{_datadir}/guile/%{mver}/slibcat export SCHEME_LIBRARY_PATH=%{_datadir}/slib/ -umask 0022 # Build SLIB catalog for pre in \ @@ -129,8 +131,7 @@ fi %files %defattr(-,root,root,-) -%doc AUTHORS COPYING* ChangeLog HACKING NEWS.bz2 README -%doc SNAPSHOTS ANON-CVS THANKS +%doc AUTHORS COPYING* ChangeLog HACKING NEWS.bz2 README THANKS %{_bindir}/guile %{_bindir}/guile-tools %{_libdir}/libguile*.so.* @@ -155,11 +156,17 @@ fi %{_bindir}/guile-snarf %{_datadir}/aclocal/* %{_libdir}/libguile.so +%{_libdir}/pkgconfig/*.pc %{_includedir}/guile %{_includedir}/libguile %{_includedir}/libguile.h %changelog +* Tue May 13 2008 Miroslav Lichvar - 5:1.8.5-1 +- update to 1.8.5 +- fix continuations on ia64 +- remove umask setting from scriptlet, rpm sets it now + * Thu Feb 21 2008 Miroslav Lichvar - 5:1.8.4-1 - update to 1.8.4 - add %%check diff --git a/sources b/sources index 06536c8..3291d76 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -aacc6a384af88a85574717a9221f5c6e guile-1.8.4.tar.gz +a3f8216544509a74a4441f689a0410d2 guile-1.8.5.tar.gz -- cgit