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"
17 #if !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2)
25 # include <machine/reg.h>
29 #if defined(MACOS) && defined(__MWERKS__)
33 # define NONVOLATILE_GPR_COUNT 19
34 struct ppc_registers {
35 unsigned long gprs[NONVOLATILE_GPR_COUNT]; /* R13-R31 */
37 typedef struct ppc_registers ppc_registers;
39 # if defined(CPPCHECK)
40 void getRegisters(ppc_registers* regs);
42 asm static void getRegisters(register ppc_registers* regs)
44 stmw r13,regs->gprs /* save R13-R31 */
49 static void PushMacRegisters(void)
54 for (i = 0; i < NONVOLATILE_GPR_COUNT; i++)
55 GC_push_one(regs.gprs[i]);
60 asm static void PushMacRegisters(void)
62 sub.w #4,sp /* reserve space for one parameter */
69 # if !__option(a6frames)
70 /* <pcb> perhaps a6 should be pushed if stack frames are not being used */
74 /* skip a5 (globals), a6 (frame pointer), and a7 (stack pointer) */
87 add.w #4,sp /* fix stack */
93 #endif /* MACOS && __MWERKS__ */
95 # if defined(SPARC) || defined(IA64)
96 /* Value returned from register flushing routine; either sp (SPARC) */
97 /* or ar.bsp (IA64). */
98 GC_INNER ptr_t GC_save_regs_ret_val = NULL;
101 /* Routine to mark from registers that are preserved by the C compiler. */
102 /* This must be ported to every new architecture. It is not optional, */
103 /* and should not be used on platforms that are either UNIX-like, or */
104 /* require thread support. */
106 #undef HAVE_PUSH_REGS
108 #if defined(USE_ASM_PUSH_REGS)
109 # define HAVE_PUSH_REGS
110 #else /* No asm implementation */
112 # ifdef STACK_NOT_SCANNED
113 void GC_push_regs(void)
117 # define HAVE_PUSH_REGS
119 # elif defined(M68K) && defined(AMIGA)
120 /* This function is not static because it could also be */
121 /* erroneously defined in .S file, so this error would be caught */
123 void GC_push_regs(void)
125 /* AMIGA - could be replaced by generic code */
126 /* a0, a1, d0 and d1 are caller save */
129 asm("subq.w &0x4,%sp"); /* allocate word on top of stack */
131 asm("mov.l %a2,(%sp)"); asm("jsr _GC_push_one");
132 asm("mov.l %a3,(%sp)"); asm("jsr _GC_push_one");
133 asm("mov.l %a4,(%sp)"); asm("jsr _GC_push_one");
134 asm("mov.l %a5,(%sp)"); asm("jsr _GC_push_one");
135 asm("mov.l %a6,(%sp)"); asm("jsr _GC_push_one");
136 /* Skip frame pointer and stack pointer */
137 asm("mov.l %d2,(%sp)"); asm("jsr _GC_push_one");
138 asm("mov.l %d3,(%sp)"); asm("jsr _GC_push_one");
139 asm("mov.l %d4,(%sp)"); asm("jsr _GC_push_one");
140 asm("mov.l %d5,(%sp)"); asm("jsr _GC_push_one");
141 asm("mov.l %d6,(%sp)"); asm("jsr _GC_push_one");
142 asm("mov.l %d7,(%sp)"); asm("jsr _GC_push_one");
144 asm("addq.w &0x4,%sp"); /* put stack back where it was */
145 # else /* !__GNUC__ */
146 GC_push_one(getreg(REG_A2));
147 GC_push_one(getreg(REG_A3));
149 /* Can probably be changed to #if 0 -Kjetil M. (a4=globals) */
150 GC_push_one(getreg(REG_A4));
152 GC_push_one(getreg(REG_A5));
153 GC_push_one(getreg(REG_A6));
154 /* Skip stack pointer */
155 GC_push_one(getreg(REG_D2));
156 GC_push_one(getreg(REG_D3));
157 GC_push_one(getreg(REG_D4));
158 GC_push_one(getreg(REG_D5));
159 GC_push_one(getreg(REG_D6));
160 GC_push_one(getreg(REG_D7));
161 # endif /* !__GNUC__ */
163 # define HAVE_PUSH_REGS
165 # elif defined(MACOS)
167 # if defined(M68K) && defined(THINK_C) && !defined(CPPCHECK)
168 # define PushMacReg(reg) \
171 void GC_push_regs(void)
174 sub.w #4,sp ; reserve space for one parameter.
178 ; skip a5 (globals), a6 (frame pointer), and a7 (stack pointer)
185 add.w #4,sp ; fix stack.
188 # define HAVE_PUSH_REGS
190 # elif defined(__MWERKS__)
191 void GC_push_regs(void)
195 # define HAVE_PUSH_REGS
196 # endif /* __MWERKS__ */
199 #endif /* !USE_ASM_PUSH_REGS */
201 #if defined(HAVE_PUSH_REGS) && defined(THREADS)
202 # error GC_push_regs cannot be used with threads
203 /* Would fail for GC_do_blocking. There are probably other safety */
205 # undef HAVE_PUSH_REGS
208 #if !defined(HAVE_PUSH_REGS) && defined(UNIX_LIKE)
210 # ifndef NO_GETCONTEXT
211 # if defined(DARWIN) \
212 && (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 /*MAC_OS_X_VERSION_10_6*/)
213 # include <sys/ucontext.h>
215 # include <ucontext.h>
216 # endif /* !DARWIN */
217 # ifdef GETCONTEXT_FPU_EXCMASK_BUG
221 #endif /* !HAVE_PUSH_REGS */
223 /* Ensure that either registers are pushed, or callee-save registers */
224 /* are somewhere on the stack, and then call fn(arg, ctxt). */
225 /* ctxt is either a pointer to a ucontext_t we generated, or NULL. */
226 GC_ATTR_NO_SANITIZE_ADDR
227 GC_INNER void GC_with_callee_saves_pushed(void (*fn)(ptr_t, void *),
231 volatile ptr_t context = 0;
233 # if defined(HAVE_PUSH_REGS)
236 # if defined(UNIX_LIKE) && !defined(NO_GETCONTEXT)
237 /* Older versions of Darwin seem to lack getcontext(). */
238 /* ARM and MIPS Linux often doesn't support a real */
240 static signed char getcontext_works = 0; /* (-1) - broken, 1 - works */
242 # ifdef GETCONTEXT_FPU_EXCMASK_BUG
243 /* Workaround a bug (clearing the FPU exception mask) in */
244 /* getcontext on Linux/x86_64. */
246 /* We manipulate FPU control word here just not to force the */
247 /* client application to use -lm linker option. */
248 unsigned short old_fcw;
250 # if defined(CPPCHECK)
251 GC_noop1((word)&old_fcw);
253 __asm__ __volatile__ ("fstcw %0" : "=m" (*&old_fcw));
255 int except_mask = fegetexcept();
259 if (getcontext_works >= 0) {
260 if (getcontext(&ctxt) < 0) {
261 WARN("getcontext failed:"
262 " using another register retrieval method...\n", 0);
263 /* getcontext() is broken, do not try again. */
264 /* E.g., to workaround a bug in Docker ubuntu_32bit. */
266 context = (ptr_t)&ctxt;
268 if (EXPECT(0 == getcontext_works, FALSE))
269 getcontext_works = context != NULL ? 1 : -1;
271 # ifdef GETCONTEXT_FPU_EXCMASK_BUG
273 __asm__ __volatile__ ("fldcw %0" : : "m" (*&old_fcw));
276 /* And now correct the exception mask in SSE MXCSR. */
277 __asm__ __volatile__ ("stmxcsr %0" : "=m" (*&mxcsr));
278 mxcsr = (mxcsr & ~(FE_ALL_EXCEPT << 7)) |
279 ((old_fcw & FE_ALL_EXCEPT) << 7);
280 __asm__ __volatile__ ("ldmxcsr %0" : : "m" (*&mxcsr));
283 if (feenableexcept(except_mask) < 0)
284 ABORT("feenableexcept failed");
286 # endif /* GETCONTEXT_FPU_EXCMASK_BUG */
287 # if defined(SPARC) || defined(IA64)
288 /* On a register window machine, we need to save register */
289 /* contents on the stack for this to work. This may already be */
290 /* subsumed by the getcontext() call. */
291 GC_save_regs_ret_val = GC_save_regs_in_stack();
293 if (NULL == context) /* getcontext failed */
294 # endif /* !NO_GETCONTEXT */
296 # if defined(HAVE_BUILTIN_UNWIND_INIT)
297 /* This was suggested by Richard Henderson as the way to */
298 /* force callee-save registers and register windows onto */
300 __builtin_unwind_init();
301 # elif defined(NO_CRT) && defined(MSWIN32)
303 RtlCaptureContext(&ctx);
306 /* The idea is due to Parag Patel at HP. */
307 /* We're not sure whether he would like */
308 /* to be acknowledged for it or not. */
310 word * i = (word *)®s;
311 ptr_t lim = (ptr_t)(®s) + sizeof(regs);
313 /* Setjmp doesn't always clear all of the buffer. */
314 /* That tends to preserve garbage. Clear it. */
315 for (; (word)i < (word)lim; i++) {
318 # if defined(MSWIN32) || defined(MSWINCE) || defined(UTS4) \
319 || defined(OS2) || defined(CX_UX) || defined(__CC_ARM) \
320 || defined(LINUX) || defined(EWS4800) || defined(RTEMS)
323 (void) _setjmp(regs);
324 /* We don't want to mess with signals. According to */
325 /* SUSV3, setjmp() may or may not save signal mask. */
326 /* _setjmp won't, but is less portable. */
328 # endif /* !HAVE_BUILTIN_UNWIND_INIT */
330 # endif /* !HAVE_PUSH_REGS */
331 /* TODO: context here is sometimes just zero. At the moment, the */
332 /* callees don't really need it. */
333 fn(arg, (/* no volatile */ void *)context);
334 /* Strongly discourage the compiler from treating the above */
335 /* as a tail-call, since that would pop the register */
336 /* contents before we get a chance to look at them. */
337 GC_noop1(COVERT_DATAFLOW(&dummy));
340 #endif /* !SN_TARGET_ORBIS && !SN_TARGET_PSP2 */