| /* |
| * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers |
| * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. |
| * |
| * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED |
| * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. |
| * |
| * Permission is hereby granted to use or copy this program |
| * for any purpose, provided the above notices are retained on all copies. |
| * Permission to modify the code and to distribute modified code is granted, |
| * provided the above notices are retained, and a notice that the code was |
| * modified is included with the above copyright notice. |
| */ |
| /* Boehm, November 17, 1995 12:13 pm PST */ |
| # include "private/gc_priv.h" |
| # include <stdio.h> |
| # include <setjmp.h> |
| # if defined(OS2) || defined(CX_UX) |
| # define _setjmp(b) setjmp(b) |
| # define _longjmp(b,v) longjmp(b,v) |
| # endif |
| # ifdef AMIGA |
| # ifndef __GNUC__ |
| # include <dos.h> |
| # else |
| # include <machine/reg.h> |
| # endif |
| # endif |
| |
| #if defined(__MWERKS__) && !defined(POWERPC) |
| |
| asm static void PushMacRegisters() |
| { |
| sub.w #4,sp // reserve space for one parameter. |
| move.l a2,(sp) |
| jsr GC_push_one |
| move.l a3,(sp) |
| jsr GC_push_one |
| move.l a4,(sp) |
| jsr GC_push_one |
| # if !__option(a6frames) |
| // <pcb> perhaps a6 should be pushed if stack frames are not being used. |
| move.l a6,(sp) |
| jsr GC_push_one |
| # endif |
| // skip a5 (globals), a6 (frame pointer), and a7 (stack pointer) |
| move.l d2,(sp) |
| jsr GC_push_one |
| move.l d3,(sp) |
| jsr GC_push_one |
| move.l d4,(sp) |
| jsr GC_push_one |
| move.l d5,(sp) |
| jsr GC_push_one |
| move.l d6,(sp) |
| jsr GC_push_one |
| move.l d7,(sp) |
| jsr GC_push_one |
| add.w #4,sp // fix stack. |
| rts |
| } |
| |
| #endif /* __MWERKS__ */ |
| |
| # if defined(SPARC) || defined(IA64) |
| /* Value returned from register flushing routine; either sp (SPARC) */ |
| /* or ar.bsp (IA64) */ |
| ptr_t GC_save_regs_ret_val; |
| # endif |
| |
| /* Routine to mark from registers that are preserved by the C compiler. */ |
| /* This must be ported to every new architecture. It is noe optional, */ |
| /* and should not be used on platforms that are either UNIX-like, or */ |
| /* require thread support. */ |
| |
| #undef HAVE_PUSH_REGS |
| |
| #if defined(USE_ASM_PUSH_REGS) |
| # define HAVE_PUSH_REGS |
| #else /* No asm implementation */ |
| void GC_push_regs() |
| { |
| # if defined(M68K) && defined(AMIGA) |
| /* AMIGA - could be replaced by generic code */ |
| /* a0, a1, d0 and d1 are caller save */ |
| |
| # ifdef __GNUC__ |
| asm("subq.w &0x4,%sp"); /* allocate word on top of stack */ |
| |
| asm("mov.l %a2,(%sp)"); asm("jsr _GC_push_one"); |
| asm("mov.l %a3,(%sp)"); asm("jsr _GC_push_one"); |
| asm("mov.l %a4,(%sp)"); asm("jsr _GC_push_one"); |
| asm("mov.l %a5,(%sp)"); asm("jsr _GC_push_one"); |
| asm("mov.l %a6,(%sp)"); asm("jsr _GC_push_one"); |
| /* Skip frame pointer and stack pointer */ |
| asm("mov.l %d2,(%sp)"); asm("jsr _GC_push_one"); |
| asm("mov.l %d3,(%sp)"); asm("jsr _GC_push_one"); |
| asm("mov.l %d4,(%sp)"); asm("jsr _GC_push_one"); |
| asm("mov.l %d5,(%sp)"); asm("jsr _GC_push_one"); |
| asm("mov.l %d6,(%sp)"); asm("jsr _GC_push_one"); |
| asm("mov.l %d7,(%sp)"); asm("jsr _GC_push_one"); |
| |
| asm("addq.w &0x4,%sp"); /* put stack back where it was */ |
| # define HAVE_PUSH_REGS |
| # else /* !__GNUC__ */ |
| GC_push_one(getreg(REG_A2)); |
| GC_push_one(getreg(REG_A3)); |
| # ifndef __SASC |
| /* Can probably be changed to #if 0 -Kjetil M. (a4=globals)*/ |
| GC_push_one(getreg(REG_A4)); |
| # endif |
| GC_push_one(getreg(REG_A5)); |
| GC_push_one(getreg(REG_A6)); |
| /* Skip stack pointer */ |
| GC_push_one(getreg(REG_D2)); |
| GC_push_one(getreg(REG_D3)); |
| GC_push_one(getreg(REG_D4)); |
| GC_push_one(getreg(REG_D5)); |
| GC_push_one(getreg(REG_D6)); |
| GC_push_one(getreg(REG_D7)); |
| # define HAVE_PUSH_REGS |
| # endif /* !__GNUC__ */ |
| # endif /* AMIGA */ |
| |
| # if defined(M68K) && defined(MACOS) |
| # if defined(THINK_C) |
| # define PushMacReg(reg) \ |
| move.l reg,(sp) \ |
| jsr GC_push_one |
| asm { |
| sub.w #4,sp ; reserve space for one parameter. |
| PushMacReg(a2); |
| PushMacReg(a3); |
| PushMacReg(a4); |
| ; skip a5 (globals), a6 (frame pointer), and a7 (stack pointer) |
| PushMacReg(d2); |
| PushMacReg(d3); |
| PushMacReg(d4); |
| PushMacReg(d5); |
| PushMacReg(d6); |
| PushMacReg(d7); |
| add.w #4,sp ; fix stack. |
| } |
| # define HAVE_PUSH_REGS |
| # undef PushMacReg |
| # endif /* THINK_C */ |
| # if defined(__MWERKS__) |
| PushMacRegisters(); |
| # define HAVE_PUSH_REGS |
| # endif /* __MWERKS__ */ |
| # endif /* MACOS */ |
| } |
| #endif /* !USE_ASM_PUSH_REGS */ |
| |
| #if defined(HAVE_PUSH_REGS) && defined(THREADS) |
| # error GC_push_regs cannot be used with threads |
| /* Would fail for GC_do_blocking. There are probably other safety */ |
| /* issues. */ |
| # undef HAVE_PUSH_REGS |
| #endif |
| |
| #if !defined(HAVE_PUSH_REGS) && defined(UNIX_LIKE) |
| # include <ucontext.h> |
| #endif |
| |
| /* Ensure that either registers are pushed, or callee-save registers */ |
| /* are somewhere on the stack, and then call fn(arg, ctxt). */ |
| /* ctxt is either a pointer to a ucontext_t we generated, or NULL. */ |
| void GC_with_callee_saves_pushed(void (*fn)(ptr_t, void *), |
| ptr_t arg) |
| { |
| word dummy; |
| void * context = 0; |
| |
| # if defined(HAVE_PUSH_REGS) |
| GC_push_regs(); |
| # elif defined(UNIX_LIKE) && !defined(DARWIN) && !defined(ARM32) |
| /* Older versions of Darwin seem to lack getcontext(). */ |
| /* ARM Linux often doesn't support a real getcontext(). */ |
| ucontext_t ctxt; |
| if (getcontext(&ctxt) < 0) |
| ABORT ("Getcontext failed: Use another register retrieval method?"); |
| context = &ctxt; |
| # if defined(SPARC) || defined(IA64) |
| /* On a register window machine, we need to save register */ |
| /* contents on the stack for this to work. This may already be */ |
| /* subsumed by the getcontext() call. */ |
| { |
| GC_save_regs_ret_val = GC_save_regs_in_stack(); |
| } |
| # endif /* register windows. */ |
| # elif defined(HAVE_BUILTIN_UNWIND_INIT) |
| /* This was suggested by Richard Henderson as the way to */ |
| /* force callee-save registers and register windows onto */ |
| /* the stack. */ |
| __builtin_unwind_init(); |
| # else /* !HAVE_BUILTIN_UNWIND_INIT && !UNIX_LIKE */ |
| /* && !HAVE_PUSH_REGS */ |
| /* Generic code */ |
| /* The idea is due to Parag Patel at HP. */ |
| /* We're not sure whether he would like */ |
| /* to be he acknowledged for it or not. */ |
| jmp_buf regs; |
| register word * i = (word *) regs; |
| register ptr_t lim = (ptr_t)(regs) + (sizeof regs); |
| |
| /* Setjmp doesn't always clear all of the buffer. */ |
| /* That tends to preserve garbage. Clear it. */ |
| for (; (char *)i < lim; i++) { |
| *i = 0; |
| } |
| # if defined(MSWIN32) || defined(MSWINCE) \ |
| || defined(UTS4) || defined(LINUX) || defined(EWS4800) |
| (void) setjmp(regs); |
| # else |
| (void) _setjmp(regs); |
| /* We don't want to mess with signals. According to */ |
| /* SUSV3, setjmp() may or may not save signal mask. */ |
| /* _setjmp won't, but is less portable. */ |
| # endif |
| # endif /* !HAVE_PUSH_REGS ... */ |
| fn(arg, context); |
| /* Strongly discourage the compiler from treating the above */ |
| /* as a tail-call, since that would pop the register */ |
| /* contents before we get a chance to look at them. */ |
| GC_noop1((word)(&dummy)); |
| } |
| |
| void GC_push_regs_and_stack(ptr_t cold_gc_frame) |
| { |
| GC_with_callee_saves_pushed(GC_push_current_stack, cold_gc_frame); |
| } |
| |
| #if defined(ASM_CLEAR_CODE) |
| # ifdef LINT |
| /*ARGSUSED*/ |
| ptr_t GC_clear_stack_inner(arg, limit) |
| ptr_t arg; word limit; |
| { return(arg); } |
| /* The real version is in a .S file */ |
| # endif |
| #endif /* ASM_CLEAR_CODE */ |