From: Tanaka Akira Date: 2005-06-09T15:59:27+09:00 Subject: Re: Rails on Altix ia64 In article , "Adam P. Jenkins" writes: > Thank you very much! I just tried applying this patch, and now the > crash does not occur. I see. I think I understand why ruby is so unstable on IA64, now. This is updated patch which use magic setjmp to avoid the problem. It makes SEGV much rare. I got only 2 SEGVs in 100 test-all with ruby built with -O2. Index: eval.c =================================================================== RCS file: /src/ruby/eval.c,v retrieving revision 1.616.2.99 diff -u -r1.616.2.99 eval.c --- eval.c 7 Jun 2005 23:33:50 -0000 1.616.2.99 +++ eval.c 9 Jun 2005 04:08:08 -0000 @@ -32,9 +32,8 @@ #if defined(HAVE_GETCONTEXT) && defined(HAVE_SETCONTEXT) #include #define USE_CONTEXT -#else -#include #endif +#include #include "st.h" #include "dln.h" @@ -98,8 +97,6 @@ volatile int status; } rb_jmpbuf_t[1]; -#undef longjmp -#undef setjmp NORETURN(static void rb_jump_context(rb_jmpbuf_t, int)); static inline void rb_jump_context(env, val) @@ -110,15 +107,45 @@ setcontext(&env->context); abort(); /* ensure noreturn */ } -#define longjmp(env, val) rb_jump_context(env, val) -#define setjmp(j) ((j)->status = 0, getcontext(&(j)->context), (j)->status) +/* + * DUMMY_SETJMP is a magic for getcontext, gcc and IA64 register stack + * combination problem. + * + * Assume following code sequence. + * + * 1. set a register in the register stack such as r32. + * 2. call getcontext. + * 3. use the register. + * 4. update the register for other use. + * 5. call setcontext directly or indirectly. + * + * This code should be run as 1->2->3->4->5->3->4. + * But after second getcontext return (second 3), + * the register is broken (updated). + * It's because getcontext/setcontext doesn't preserve the content of the + * register stack. + * + * setjmp also doesn't preserve the content of the register stack. + * But it has not the problem because gcc knows setjmp may return twice. + * gcc detects setjmp and generates setjmp safe code. + * + * So setjmp call before getcontext call fix the problem. + * It is not required that setjmp is called at run time, since the problem is + * register usage. + */ +jmp_buf dummy_jmp_buf; +int dummy_setjmp_false = 0; +#define DUMMY_SETJMP (dummy_setjmp_false ? setjmp(dummy_jmp_buf) : 0) +#define ruby_longjmp(env, val) rb_jump_context(env, val) +#define ruby_setjmp(j) ((j)->status = 0, DUMMY_SETJMP, getcontext(&(j)->context), (j)->status) #else typedef jmp_buf rb_jmpbuf_t; -#ifndef setjmp -#ifdef HAVE__SETJMP -#define setjmp(env) _setjmp(env) -#define longjmp(env,val) _longjmp(env,val) -#endif +#if !defined(setjmp) && defined(HAVE__SETJMP) +#define ruby_setjmp(env) _setjmp(env) +#define ruby_longjmp(env,val) _longjmp(env,val) +#else +#define ruby_setjmp(env) setjmp(env) +#define ruby_longjmp(env,val) longjmp(env,val) #endif #endif @@ -927,12 +954,12 @@ #define PROT_LAMBDA INT2FIX(2) /* 5 */ #define PROT_YIELD INT2FIX(3) /* 7 */ -#define EXEC_TAG() (FLUSH_REGISTER_WINDOWS, setjmp(prot_tag->buf)) +#define EXEC_TAG() (FLUSH_REGISTER_WINDOWS, ruby_setjmp(prot_tag->buf)) #define JUMP_TAG(st) do { \ ruby_frame = prot_tag->frame; \ ruby_iter = prot_tag->iter; \ - longjmp(prot_tag->buf,(st)); \ + ruby_longjmp(prot_tag->buf,(st)); \ } while (0) #define POP_TAG() \ @@ -10006,7 +10033,7 @@ #define THREAD_SAVE_CONTEXT(th) \ (rb_thread_save_context(th),\ - rb_thread_switch((FLUSH_REGISTER_WINDOWS, setjmp((th)->context)))) + rb_thread_switch((FLUSH_REGISTER_WINDOWS, ruby_setjmp((th)->context)))) NORETURN(static void rb_thread_restore_context _((rb_thread_t,int))); @@ -10087,7 +10114,7 @@ rb_backref_set(tmp->last_match); tmp->last_match = tval; - longjmp(tmp->context, ex); + ruby_longjmp(tmp->context, ex); } static void Index: gc.c =================================================================== RCS file: /src/ruby/gc.c,v retrieving revision 1.168.2.18 diff -u -r1.168.2.18 gc.c --- gc.c 20 Jan 2005 09:34:36 -0000 1.168.2.18 +++ gc.c 9 Jun 2005 04:08:08 -0000 @@ -1483,14 +1483,6 @@ STACK_LEVEL_MAX = (rlim.rlim_cur - space) / sizeof(VALUE); } } -#if defined(__ia64__) && (!defined(__GNUC__) || __GNUC__ < 2 || defined(__OPTIMIZE__)) - /* ruby crashes on IA64 if compiled with optimizer on */ - /* when if STACK_LEVEL_MAX is greater than this magic number */ - /* I know this is a kludge. I suspect optimizer bug */ -#define IA64_MAGIC_STACK_LIMIT 49152 - if (STACK_LEVEL_MAX > IA64_MAGIC_STACK_LIMIT) - STACK_LEVEL_MAX = IA64_MAGIC_STACK_LIMIT; -#endif #endif } -- Tanaka Akira