Workaround 'NULL==*flh is always true' cppcheck style warning in allocobj
[platform/upstream/libgc.git] / mach_dep.c
1 /*
2  * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
3  * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
4  *
5  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
6  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
7  *
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.
13  */
14
15 #include "private/gc_priv.h"
16
17 #if !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2)
18
19 #include <stdio.h>
20
21 #ifdef AMIGA
22 # ifndef __GNUC__
23 #   include <dos.h>
24 # else
25 #   include <machine/reg.h>
26 # endif
27 #endif
28
29 #if defined(MACOS) && defined(__MWERKS__)
30
31 #if defined(POWERPC)
32
33 # define NONVOLATILE_GPR_COUNT 19
34   struct ppc_registers {
35         unsigned long gprs[NONVOLATILE_GPR_COUNT];      /* R13-R31 */
36   };
37   typedef struct ppc_registers ppc_registers;
38
39 # if defined(CPPCHECK)
40     void getRegisters(ppc_registers* regs);
41 # else
42     asm static void getRegisters(register ppc_registers* regs)
43     {
44         stmw    r13,regs->gprs                          /* save R13-R31 */
45         blr
46     }
47 # endif
48
49   static void PushMacRegisters(void)
50   {
51         ppc_registers regs;
52         int i;
53         getRegisters(&regs);
54         for (i = 0; i < NONVOLATILE_GPR_COUNT; i++)
55                 GC_push_one(regs.gprs[i]);
56   }
57
58 #else /* M68K */
59
60   asm static void PushMacRegisters(void)
61   {
62     sub.w   #4,sp                   /* reserve space for one parameter */
63     move.l  a2,(sp)
64     jsr         GC_push_one
65     move.l  a3,(sp)
66     jsr         GC_push_one
67     move.l  a4,(sp)
68     jsr         GC_push_one
69 #   if !__option(a6frames)
70       /* <pcb> perhaps a6 should be pushed if stack frames are not being used */
71         move.l  a6,(sp)
72         jsr             GC_push_one
73 #   endif
74         /* skip a5 (globals), a6 (frame pointer), and a7 (stack pointer) */
75     move.l  d2,(sp)
76     jsr         GC_push_one
77     move.l  d3,(sp)
78     jsr         GC_push_one
79     move.l  d4,(sp)
80     jsr         GC_push_one
81     move.l  d5,(sp)
82     jsr         GC_push_one
83     move.l  d6,(sp)
84     jsr         GC_push_one
85     move.l  d7,(sp)
86     jsr         GC_push_one
87     add.w   #4,sp                   /* fix stack */
88     rts
89   }
90
91 #endif /* M68K */
92
93 #endif /* MACOS && __MWERKS__ */
94
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;
99 # endif
100
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.                                              */
105
106 #undef HAVE_PUSH_REGS
107
108 #if defined(USE_ASM_PUSH_REGS)
109 # define HAVE_PUSH_REGS
110 #else  /* No asm implementation */
111
112 # ifdef STACK_NOT_SCANNED
113     void GC_push_regs(void)
114     {
115       /* empty */
116     }
117 #   define HAVE_PUSH_REGS
118
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    */
122     /* by the linker.                                                   */
123     void GC_push_regs(void)
124     {
125          /*  AMIGA - could be replaced by generic code                  */
126          /* a0, a1, d0 and d1 are caller save */
127
128 #       ifdef __GNUC__
129           asm("subq.w &0x4,%sp");       /* allocate word on top of stack */
130
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");
143
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));
148 #         ifndef __SASC
149             /* Can probably be changed to #if 0 -Kjetil M. (a4=globals) */
150             GC_push_one(getreg(REG_A4));
151 #         endif
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__ */
162     }
163 #   define HAVE_PUSH_REGS
164
165 # elif defined(MACOS)
166
167 #   if defined(M68K) && defined(THINK_C) && !defined(CPPCHECK)
168 #     define PushMacReg(reg) \
169               move.l  reg,(sp) \
170               jsr             GC_push_one
171       void GC_push_regs(void)
172       {
173           asm {
174               sub.w   #4,sp          ; reserve space for one parameter.
175               PushMacReg(a2);
176               PushMacReg(a3);
177               PushMacReg(a4);
178               ; skip a5 (globals), a6 (frame pointer), and a7 (stack pointer)
179               PushMacReg(d2);
180               PushMacReg(d3);
181               PushMacReg(d4);
182               PushMacReg(d5);
183               PushMacReg(d6);
184               PushMacReg(d7);
185               add.w   #4,sp          ; fix stack.
186           }
187       }
188 #     define HAVE_PUSH_REGS
189 #     undef PushMacReg
190 #   elif defined(__MWERKS__)
191       void GC_push_regs(void)
192       {
193           PushMacRegisters();
194       }
195 #     define HAVE_PUSH_REGS
196 #   endif /* __MWERKS__ */
197 # endif /* MACOS */
198
199 #endif /* !USE_ASM_PUSH_REGS */
200
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     */
204  /* issues.                                                             */
205 # undef HAVE_PUSH_REGS
206 #endif
207
208 #if !defined(HAVE_PUSH_REGS) && defined(UNIX_LIKE)
209 # include <signal.h>
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>
214 #   else
215 #     include <ucontext.h>
216 #   endif /* !DARWIN */
217 #   ifdef GETCONTEXT_FPU_EXCMASK_BUG
218 #     include <fenv.h>
219 #   endif
220 # endif
221 #endif /* !HAVE_PUSH_REGS */
222
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 *),
228                                           volatile ptr_t arg)
229 {
230   volatile int dummy;
231   volatile ptr_t context = 0;
232
233 # if defined(HAVE_PUSH_REGS)
234     GC_push_regs();
235 # else
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        */
239       /* getcontext().                                          */
240       static signed char getcontext_works = 0; /* (-1) - broken, 1 - works */
241       ucontext_t ctxt;
242 #     ifdef GETCONTEXT_FPU_EXCMASK_BUG
243         /* Workaround a bug (clearing the FPU exception mask) in        */
244         /* getcontext on Linux/x86_64.                                  */
245 #       ifdef 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;
249
250 #         if defined(CPPCHECK)
251             GC_noop1((word)&old_fcw);
252 #         endif
253           __asm__ __volatile__ ("fstcw %0" : "=m" (*&old_fcw));
254 #       else
255           int except_mask = fegetexcept();
256 #       endif
257 #     endif
258
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.  */
265         } else {
266           context = (ptr_t)&ctxt;
267         }
268         if (EXPECT(0 == getcontext_works, FALSE))
269           getcontext_works = context != NULL ? 1 : -1;
270       }
271 #     ifdef GETCONTEXT_FPU_EXCMASK_BUG
272 #       ifdef X86_64
273           __asm__ __volatile__ ("fldcw %0" : : "m" (*&old_fcw));
274           {
275             unsigned mxcsr;
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));
281           }
282 #       else /* !X86_64 */
283           if (feenableexcept(except_mask) < 0)
284             ABORT("feenableexcept failed");
285 #       endif
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();
292 #     endif
293       if (NULL == context) /* getcontext failed */
294 #   endif /* !NO_GETCONTEXT */
295     {
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        */
299         /* the stack.                                                   */
300         __builtin_unwind_init();
301 #     elif defined(NO_CRT) && defined(MSWIN32)
302         CONTEXT ctx;
303         RtlCaptureContext(&ctx);
304 #     else
305         /* Generic code                          */
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.     */
309         jmp_buf regs;
310         word * i = (word *)&regs;
311         ptr_t lim = (ptr_t)(&regs) + sizeof(regs);
312
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++) {
316             *i = 0;
317         }
318 #       if defined(MSWIN32) || defined(MSWINCE) || defined(UTS4) \
319            || defined(OS2) || defined(CX_UX) || defined(__CC_ARM) \
320            || defined(LINUX) || defined(EWS4800) || defined(RTEMS)
321           (void) setjmp(regs);
322 #       else
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.               */
327 #       endif
328 #     endif /* !HAVE_BUILTIN_UNWIND_INIT */
329     }
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));
338 }
339
340 #endif /* !SN_TARGET_ORBIS && !SN_TARGET_PSP2 */