2 * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
3 * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved.
5 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
6 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
8 * Permission is hereby granted to use or copy this program
9 * for any purpose, provided the above notices are retained on all copies.
10 * Permission to modify the code and to distribute modified code is granted,
11 * provided the above notices are retained, and a notice that the code was
12 * modified is included with the above copyright notice.
15 #include "private/gc_priv.h"
20 #if defined(OS2) || defined(CX_UX) || defined(__CC_ARM)
21 # define _setjmp(b) setjmp(b)
22 # define _longjmp(b,v) longjmp(b,v)
29 # include <machine/reg.h>
33 #if defined(__MWERKS__) && !defined(POWERPC)
35 asm static void PushMacRegisters()
37 sub.w #4,sp // reserve space for one parameter.
44 # if !__option(a6frames)
45 // <pcb> perhaps a6 should be pushed if stack frames are not being used.
49 // skip a5 (globals), a6 (frame pointer), and a7 (stack pointer)
62 add.w #4,sp // fix stack.
66 #endif /* __MWERKS__ */
68 # if defined(SPARC) || defined(IA64)
69 /* Value returned from register flushing routine; either sp (SPARC) */
70 /* or ar.bsp (IA64). */
71 GC_INNER ptr_t GC_save_regs_ret_val = NULL;
74 /* Routine to mark from registers that are preserved by the C compiler. */
75 /* This must be ported to every new architecture. It is not optional, */
76 /* and should not be used on platforms that are either UNIX-like, or */
77 /* require thread support. */
81 #if defined(USE_ASM_PUSH_REGS)
82 # define HAVE_PUSH_REGS
83 #else /* No asm implementation */
85 # if defined(M68K) && defined(AMIGA)
86 /* This function is not static because it could also be */
87 /* errorneously defined in .S file, so this error would be caught */
89 void GC_push_regs(void)
91 /* AMIGA - could be replaced by generic code */
92 /* a0, a1, d0 and d1 are caller save */
95 asm("subq.w &0x4,%sp"); /* allocate word on top of stack */
97 asm("mov.l %a2,(%sp)"); asm("jsr _GC_push_one");
98 asm("mov.l %a3,(%sp)"); asm("jsr _GC_push_one");
99 asm("mov.l %a4,(%sp)"); asm("jsr _GC_push_one");
100 asm("mov.l %a5,(%sp)"); asm("jsr _GC_push_one");
101 asm("mov.l %a6,(%sp)"); asm("jsr _GC_push_one");
102 /* Skip frame pointer and stack pointer */
103 asm("mov.l %d2,(%sp)"); asm("jsr _GC_push_one");
104 asm("mov.l %d3,(%sp)"); asm("jsr _GC_push_one");
105 asm("mov.l %d4,(%sp)"); asm("jsr _GC_push_one");
106 asm("mov.l %d5,(%sp)"); asm("jsr _GC_push_one");
107 asm("mov.l %d6,(%sp)"); asm("jsr _GC_push_one");
108 asm("mov.l %d7,(%sp)"); asm("jsr _GC_push_one");
110 asm("addq.w &0x4,%sp"); /* put stack back where it was */
111 # else /* !__GNUC__ */
112 GC_push_one(getreg(REG_A2));
113 GC_push_one(getreg(REG_A3));
115 /* Can probably be changed to #if 0 -Kjetil M. (a4=globals) */
116 GC_push_one(getreg(REG_A4));
118 GC_push_one(getreg(REG_A5));
119 GC_push_one(getreg(REG_A6));
120 /* Skip stack pointer */
121 GC_push_one(getreg(REG_D2));
122 GC_push_one(getreg(REG_D3));
123 GC_push_one(getreg(REG_D4));
124 GC_push_one(getreg(REG_D5));
125 GC_push_one(getreg(REG_D6));
126 GC_push_one(getreg(REG_D7));
127 # endif /* !__GNUC__ */
129 # define HAVE_PUSH_REGS
131 # elif defined(M68K) && defined(MACOS)
133 # if defined(THINK_C)
134 # define PushMacReg(reg) \
137 void GC_push_regs(void)
140 sub.w #4,sp ; reserve space for one parameter.
144 ; skip a5 (globals), a6 (frame pointer), and a7 (stack pointer)
151 add.w #4,sp ; fix stack.
154 # define HAVE_PUSH_REGS
156 # elif defined(__MWERKS__)
157 void GC_push_regs(void)
161 # define HAVE_PUSH_REGS
162 # endif /* __MWERKS__ */
165 #endif /* !USE_ASM_PUSH_REGS */
167 #if defined(HAVE_PUSH_REGS) && defined(THREADS)
168 # error GC_push_regs cannot be used with threads
169 /* Would fail for GC_do_blocking. There are probably other safety */
171 # undef HAVE_PUSH_REGS
174 #if !defined(HAVE_PUSH_REGS) && defined(UNIX_LIKE)
176 # ifndef NO_GETCONTEXT
177 # include <ucontext.h>
178 # ifdef GETCONTEXT_FPU_EXCMASK_BUG
182 #endif /* !HAVE_PUSH_REGS */
184 /* Ensure that either registers are pushed, or callee-save registers */
185 /* are somewhere on the stack, and then call fn(arg, ctxt). */
186 /* ctxt is either a pointer to a ucontext_t we generated, or NULL. */
187 GC_INNER void GC_with_callee_saves_pushed(void (*fn)(ptr_t, void *),
193 # if defined(HAVE_PUSH_REGS)
195 # elif defined(UNIX_LIKE) && !defined(NO_GETCONTEXT)
196 /* Older versions of Darwin seem to lack getcontext(). */
197 /* ARM and MIPS Linux often doesn't support a real */
200 # ifdef GETCONTEXT_FPU_EXCMASK_BUG
201 /* Workaround a bug (clearing the FPU exception mask) in */
202 /* getcontext on Linux/x86_64. */
204 /* We manipulate FPU control word here just not to force the */
205 /* client application to use -lm linker option. */
206 unsigned short old_fcw;
207 __asm__ __volatile__ ("fstcw %0" : "=m" (*&old_fcw));
209 int except_mask = fegetexcept();
212 if (getcontext(&ctxt) < 0)
213 ABORT ("getcontext failed: Use another register retrieval method?");
214 # ifdef GETCONTEXT_FPU_EXCMASK_BUG
216 __asm__ __volatile__ ("fldcw %0" : : "m" (*&old_fcw));
219 /* And now correct the exception mask in SSE MXCSR. */
220 __asm__ __volatile__ ("stmxcsr %0" : "=m" (*&mxcsr));
221 mxcsr = (mxcsr & ~(FE_ALL_EXCEPT << 7)) |
222 ((old_fcw & FE_ALL_EXCEPT) << 7);
223 __asm__ __volatile__ ("ldmxcsr %0" : : "m" (*&mxcsr));
226 if (feenableexcept(except_mask) < 0)
227 ABORT("feenableexcept failed");
231 # if defined(SPARC) || defined(IA64)
232 /* On a register window machine, we need to save register */
233 /* contents on the stack for this to work. This may already be */
234 /* subsumed by the getcontext() call. */
235 GC_save_regs_ret_val = GC_save_regs_in_stack();
236 # endif /* register windows. */
237 # elif defined(HAVE_BUILTIN_UNWIND_INIT) \
238 && !(defined(POWERPC) && defined(DARWIN)) \
239 && !(defined(I386) && defined(RTEMS))
240 /* This was suggested by Richard Henderson as the way to */
241 /* force callee-save registers and register windows onto */
243 /* Mark Sibly points out that this doesn't seem to work */
244 /* on MacOS 10.3.9/PowerPC. */
245 __builtin_unwind_init();
246 # else /* !HAVE_BUILTIN_UNWIND_INIT && !UNIX_LIKE */
247 /* && !HAVE_PUSH_REGS */
249 /* The idea is due to Parag Patel at HP. */
250 /* We're not sure whether he would like */
251 /* to be he acknowledged for it or not. */
253 register word * i = (word *) regs;
254 register ptr_t lim = (ptr_t)(regs) + (sizeof regs);
256 /* Setjmp doesn't always clear all of the buffer. */
257 /* That tends to preserve garbage. Clear it. */
258 for (; (char *)i < lim; i++) {
261 # if defined(MSWIN32) || defined(MSWINCE) || defined(UTS4) \
262 || defined(LINUX) || defined(EWS4800) || defined(RTEMS)
265 (void) _setjmp(regs);
266 /* We don't want to mess with signals. According to */
267 /* SUSV3, setjmp() may or may not save signal mask. */
268 /* _setjmp won't, but is less portable. */
270 # endif /* !HAVE_PUSH_REGS ... */
271 /* FIXME: context here is sometimes just zero. At the moment the */
272 /* callees don't really need it. */
274 /* Strongly discourage the compiler from treating the above */
275 /* as a tail-call, since that would pop the register */
276 /* contents before we get a chance to look at them. */
277 GC_noop1((word)(&dummy));
280 #if defined(ASM_CLEAR_CODE)
283 ptr_t GC_clear_stack_inner(ptr_t arg, word limit)
287 /* The real version is in a .S file */
289 #endif /* ASM_CLEAR_CODE */