1 
2 
3 /******************************************************************
4 
5   AmigaOS-specific routines for GC.
6   This file is normally included from os_dep.c
7 
8 ******************************************************************/
9 
10 
11 #if !defined(GC_AMIGA_DEF) && !defined(GC_AMIGA_SB) && !defined(GC_AMIGA_DS) && !defined(GC_AMIGA_AM)
12 # include "private/gc_priv.h"
13 # include <stdio.h>
14 # include <signal.h>
15 # define GC_AMIGA_DEF
16 # define GC_AMIGA_SB
17 # define GC_AMIGA_DS
18 # define GC_AMIGA_AM
19 #endif
20 
21 
22 #ifdef GC_AMIGA_DEF
23 
24 # ifndef __GNUC__
25 #   include <exec/exec.h>
26 # endif
27 # include <proto/exec.h>
28 # include <proto/dos.h>
29 # include <dos/dosextens.h>
30 # include <workbench/startup.h>
31 
32 #endif
33 
34 
35 
36 
37 #ifdef GC_AMIGA_SB
38 
39 /******************************************************************
40    Find the base of the stack.
41 ******************************************************************/
42 
GC_get_main_stack_base(void)43 ptr_t GC_get_main_stack_base(void)
44 {
45     struct Process *proc = (struct Process*)SysBase->ThisTask;
46 
47     /* Reference: Amiga Guru Book Pages: 42,567,574 */
48     if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS
49         && proc->pr_CLI != NULL) {
50         /* first ULONG is StackSize */
51         /*longPtr = proc->pr_ReturnAddr;
52         size = longPtr[0];*/
53 
54         return (char *)proc->pr_ReturnAddr + sizeof(ULONG);
55     } else {
56         return (char *)proc->pr_Task.tc_SPUpper;
57     }
58 }
59 
60 #endif
61 
62 
63 #ifdef GC_AMIGA_DS
64 /******************************************************************
65    Register data segments.
66 ******************************************************************/
67 
GC_register_data_segments(void)68    void GC_register_data_segments(void)
69    {
70      struct Process     *proc;
71      struct CommandLineInterface *cli;
72      BPTR myseglist;
73      ULONG *data;
74 
75 #    ifdef __GNUC__
76         ULONG dataSegSize;
77         GC_bool found_segment = FALSE;
78         extern char __data_size[];
79 
80         dataSegSize=__data_size+8;
81         /* Can`t find the Location of __data_size, because
82            it`s possible that is it, inside the segment. */
83 
84 #     endif
85 
86         proc= (struct Process*)SysBase->ThisTask;
87 
88         /* Reference: Amiga Guru Book Pages: 538ff,565,573
89                      and XOper.asm */
90         myseglist = proc->pr_SegList;
91         if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS) {
92           if (proc->pr_CLI != NULL) {
93             /* ProcLoaded       'Loaded as a command: '*/
94             cli = BADDR(proc->pr_CLI);
95             myseglist = cli->cli_Module;
96           }
97         } else {
98           ABORT("Not a Process.");
99         }
100 
101         if (myseglist == NULL) {
102             ABORT("Arrrgh.. can't find segments, aborting");
103         }
104 
105         /* xoper hunks Shell Process */
106 
107         for (data = (ULONG *)BADDR(myseglist); data != NULL;
108              data = (ULONG *)BADDR(data[0])) {
109           if ((ULONG)GC_register_data_segments < (ULONG)(&data[1])
110               || (ULONG)GC_register_data_segments > (ULONG)(&data[1])
111                                                     + data[-1]) {
112 #             ifdef __GNUC__
113                 if (dataSegSize == data[-1]) {
114                   found_segment = TRUE;
115                 }
116 #             endif
117               GC_add_roots_inner((char *)&data[1],
118                                  ((char *)&data[1]) + data[-1], FALSE);
119           }
120         } /* for */
121 #       ifdef __GNUC__
122            if (!found_segment) {
123              ABORT("Can`t find correct Segments.\nSolution: Use an newer version of ixemul.library");
124            }
125 #       endif
126    }
127 
128 #endif
129 
130 
131 
132 #ifdef GC_AMIGA_AM
133 
134 #ifndef GC_AMIGA_FASTALLOC
135 
GC_amiga_allocwrapper(size_t size,void * (* AllocFunction)(size_t size2))136 void *GC_amiga_allocwrapper(size_t size,void *(*AllocFunction)(size_t size2)){
137         return (*AllocFunction)(size);
138 }
139 
140 void *(*GC_amiga_allocwrapper_do)(size_t size,void *(*AllocFunction)(size_t size2))
141         =GC_amiga_allocwrapper;
142 
143 #else
144 
145 
146 
147 
148 void *GC_amiga_allocwrapper_firsttime(size_t size,void *(*AllocFunction)(size_t size2));
149 
150 void *(*GC_amiga_allocwrapper_do)(size_t size,void *(*AllocFunction)(size_t size2))
151         =GC_amiga_allocwrapper_firsttime;
152 
153 
154 /******************************************************************
155    Amiga-specific routines to obtain memory, and force GC to give
156    back fast-mem whenever possible.
157         These hacks makes gc-programs go many times faster when
158    the Amiga is low on memory, and are therefore strictly necessary.
159 
160    -Kjetil S. Matheussen, 2000.
161 ******************************************************************/
162 
163 
164 
165 /* List-header for all allocated memory. */
166 
167 struct GC_Amiga_AllocedMemoryHeader{
168         ULONG size;
169         struct GC_Amiga_AllocedMemoryHeader *next;
170 };
171 struct GC_Amiga_AllocedMemoryHeader *GC_AMIGAMEM=(struct GC_Amiga_AllocedMemoryHeader *)(int)~(NULL);
172 
173 
174 
175 /* Type of memory. Once in the execution of a program, this might change to MEMF_ANY|MEMF_CLEAR */
176 
177 ULONG GC_AMIGA_MEMF = MEMF_FAST | MEMF_CLEAR;
178 
179 
180 /* Prevents GC_amiga_get_mem from allocating memory if this one is TRUE. */
181 #ifndef GC_AMIGA_ONLYFAST
182 BOOL GC_amiga_dontalloc=FALSE;
183 #endif
184 
185 #ifdef GC_AMIGA_PRINTSTATS
186 int succ=0,succ2=0;
187 int nsucc=0,nsucc2=0;
188 int nullretries=0;
189 int numcollects=0;
190 int chipa=0;
191 int allochip=0;
192 int allocfast=0;
193 int cur0=0;
194 int cur1=0;
195 int cur10=0;
196 int cur50=0;
197 int cur150=0;
198 int cur151=0;
199 int ncur0=0;
200 int ncur1=0;
201 int ncur10=0;
202 int ncur50=0;
203 int ncur150=0;
204 int ncur151=0;
205 #endif
206 
207 /* Free everything at program-end. */
208 
GC_amiga_free_all_mem(void)209 void GC_amiga_free_all_mem(void){
210         struct GC_Amiga_AllocedMemoryHeader *gc_am=(struct GC_Amiga_AllocedMemoryHeader *)(~(int)(GC_AMIGAMEM));
211 
212 #ifdef GC_AMIGA_PRINTSTATS
213         printf("\n\n"
214                 "%d bytes of chip-mem, and %d bytes of fast-mem where allocated from the OS.\n",
215                 allochip,allocfast
216         );
217         printf(
218                 "%d bytes of chip-mem were returned from the GC_AMIGA_FASTALLOC supported allocating functions.\n",
219                 chipa
220         );
221         printf("\n");
222         printf("GC_gcollect was called %d times to avoid returning NULL or start allocating with the MEMF_ANY flag.\n",numcollects);
223         printf("%d of them was a success. (the others had to use allocation from the OS.)\n",nullretries);
224         printf("\n");
225         printf("Succeeded forcing %d gc-allocations (%d bytes) of chip-mem to be fast-mem.\n",succ,succ2);
226         printf("Failed forcing %d gc-allocations (%d bytes) of chip-mem to be fast-mem.\n",nsucc,nsucc2);
227         printf("\n");
228         printf(
229                 "Number of retries before succeeding a chip->fast force:\n"
230                 "0: %d, 1: %d, 2-9: %d, 10-49: %d, 50-149: %d, >150: %d\n",
231                 cur0,cur1,cur10,cur50,cur150,cur151
232         );
233         printf(
234                 "Number of retries before giving up a chip->fast force:\n"
235                 "0: %d, 1: %d, 2-9: %d, 10-49: %d, 50-149: %d, >150: %d\n",
236                 ncur0,ncur1,ncur10,ncur50,ncur150,ncur151
237         );
238 #endif
239 
240         while(gc_am!=NULL){
241                 struct GC_Amiga_AllocedMemoryHeader *temp = gc_am->next;
242                 FreeMem(gc_am,gc_am->size);
243                 gc_am=(struct GC_Amiga_AllocedMemoryHeader *)(~(int)(temp));
244         }
245 }
246 
247 #ifndef GC_AMIGA_ONLYFAST
248 
249 /* All memory with address lower than this one is chip-mem. */
250 
251 char *chipmax;
252 
253 
254 /*
255  * Always set to the last size of memory tried to be allocated.
256  * Needed to ensure allocation when the size is bigger than 100000.
257  *
258  */
259 size_t latestsize;
260 
261 #endif
262 
263 
264 #ifdef GC_AMIGA_FASTALLOC
265 
266 /*
267  * The actual function that is called with the GET_MEM macro.
268  *
269  */
270 
GC_amiga_get_mem(size_t size)271 void *GC_amiga_get_mem(size_t size){
272         struct GC_Amiga_AllocedMemoryHeader *gc_am;
273 
274 #ifndef GC_AMIGA_ONLYFAST
275         if(GC_amiga_dontalloc==TRUE){
276                 return NULL;
277         }
278 
279         /* We really don't want to use chip-mem, but if we must, then as little as possible. */
280         if(GC_AMIGA_MEMF==(MEMF_ANY|MEMF_CLEAR) && size>100000 && latestsize<50000) return NULL;
281 #endif
282 
283         gc_am=AllocMem((ULONG)(size + sizeof(struct GC_Amiga_AllocedMemoryHeader)),GC_AMIGA_MEMF);
284         if(gc_am==NULL) return NULL;
285 
286         gc_am->next=GC_AMIGAMEM;
287         gc_am->size=size + sizeof(struct GC_Amiga_AllocedMemoryHeader);
288         GC_AMIGAMEM=(struct GC_Amiga_AllocedMemoryHeader *)(~(int)(gc_am));
289 
290 #ifdef GC_AMIGA_PRINTSTATS
291         if((char *)gc_am<chipmax){
292                 allochip+=size;
293         }else{
294                 allocfast+=size;
295         }
296 #endif
297 
298         return gc_am+1;
299 
300 }
301 
302 #endif
303 
304 
305 #ifndef GC_AMIGA_ONLYFAST
306 
307 /* Tries very hard to force GC to find fast-mem to return. Done recursively
308  * to hold the rejected memory-pointers reachable from the collector in an
309  * easy way.
310  *
311  */
312 #ifdef GC_AMIGA_RETRY
GC_amiga_rec_alloc(size_t size,void * (* AllocFunction)(size_t size2),const int rec)313 void *GC_amiga_rec_alloc(size_t size,void *(*AllocFunction)(size_t size2),const int rec){
314         void *ret;
315 
316         ret=(*AllocFunction)(size);
317 
318 #ifdef GC_AMIGA_PRINTSTATS
319         if((char *)ret>chipmax || ret==NULL){
320                 if(ret==NULL){
321                         nsucc++;
322                         nsucc2+=size;
323                         if(rec==0) ncur0++;
324                         if(rec==1) ncur1++;
325                         if(rec>1 && rec<10) ncur10++;
326                         if(rec>=10 && rec<50) ncur50++;
327                         if(rec>=50 && rec<150) ncur150++;
328                         if(rec>=150) ncur151++;
329                 }else{
330                         succ++;
331                         succ2+=size;
332                         if(rec==0) cur0++;
333                         if(rec==1) cur1++;
334                         if(rec>1 && rec<10) cur10++;
335                         if(rec>=10 && rec<50) cur50++;
336                         if(rec>=50 && rec<150) cur150++;
337                         if(rec>=150) cur151++;
338                 }
339         }
340 #endif
341 
342         if (((char *)ret)<=chipmax && ret!=NULL && (rec<(size>500000?9:size/5000))){
343                 ret=GC_amiga_rec_alloc(size,AllocFunction,rec+1);
344         }
345 
346         return ret;
347 }
348 #endif
349 
350 
351 /* The allocating-functions defined inside the Amiga-blocks in gc.h is called
352  * via these functions.
353  */
354 
355 
GC_amiga_allocwrapper_any(size_t size,void * (* AllocFunction)(size_t size2))356 void *GC_amiga_allocwrapper_any(size_t size,void *(*AllocFunction)(size_t size2)){
357         void *ret;
358 
359         GC_amiga_dontalloc=TRUE; /* Pretty tough thing to do, but its indeed necessary. */
360         latestsize=size;
361 
362         ret=(*AllocFunction)(size);
363 
364         if(((char *)ret) <= chipmax){
365                 if(ret==NULL){
366                         /* Give GC access to allocate memory. */
367 #ifdef GC_AMIGA_GC
368                         if(!GC_dont_gc){
369                                 GC_gcollect();
370 #ifdef GC_AMIGA_PRINTSTATS
371                                 numcollects++;
372 #endif
373                                 ret=(*AllocFunction)(size);
374                         }
375                         if(ret==NULL)
376 #endif
377                         {
378                                 GC_amiga_dontalloc=FALSE;
379                                 ret=(*AllocFunction)(size);
380                                 if(ret==NULL){
381                                         WARN("Out of Memory!  Returning NIL!\n", 0);
382                                 }
383                         }
384 #ifdef GC_AMIGA_PRINTSTATS
385                         else{
386                                 nullretries++;
387                         }
388                         if(ret!=NULL && (char *)ret<=chipmax) chipa+=size;
389 #endif
390                 }
391 #ifdef GC_AMIGA_RETRY
392                 else{
393                         void *ret2;
394                         /* We got chip-mem. Better try again and again and again etc., we might get fast-mem sooner or later... */
395                         /* Using gctest to check the effectiveness of doing this, does seldom give a very good result. */
396                         /* However, real programs doesn't normally rapidly allocate and deallocate. */
397                         if(
398                                 AllocFunction!=GC_malloc_uncollectable
399 #ifdef GC_ATOMIC_UNCOLLECTABLE
400                                 && AllocFunction!=GC_malloc_atomic_uncollectable
401 #endif
402                         ){
403                                 ret2=GC_amiga_rec_alloc(size,AllocFunction,0);
404                         }else{
405                                 ret2=(*AllocFunction)(size);
406 #ifdef GC_AMIGA_PRINTSTATS
407                                 if((char *)ret2<chipmax || ret2==NULL){
408                                         nsucc++;
409                                         nsucc2+=size;
410                                         ncur0++;
411                                 }else{
412                                         succ++;
413                                         succ2+=size;
414                                         cur0++;
415                                 }
416 #endif
417                         }
418                         if(((char *)ret2)>chipmax){
419                                 GC_free(ret);
420                                 ret=ret2;
421                         }else{
422                                 GC_free(ret2);
423                         }
424                 }
425 #endif
426         }
427 
428         GC_amiga_dontalloc=FALSE;
429 
430         return ret;
431 }
432 
433 
434 
435 void (*GC_amiga_toany)(void)=NULL;
436 
GC_amiga_set_toany(void (* func)(void))437 void GC_amiga_set_toany(void (*func)(void)){
438         GC_amiga_toany=func;
439 }
440 
441 #endif /* !GC_AMIGA_ONLYFAST */
442 
443 
GC_amiga_allocwrapper_fast(size_t size,void * (* AllocFunction)(size_t size2))444 void *GC_amiga_allocwrapper_fast(size_t size,void *(*AllocFunction)(size_t size2)){
445         void *ret;
446 
447         ret=(*AllocFunction)(size);
448 
449         if(ret==NULL){
450                 /* Enable chip-mem allocation. */
451 #ifdef GC_AMIGA_GC
452                 if(!GC_dont_gc){
453                         GC_gcollect();
454 #ifdef GC_AMIGA_PRINTSTATS
455                         numcollects++;
456 #endif
457                         ret=(*AllocFunction)(size);
458                 }
459                 if(ret==NULL)
460 #endif
461                 {
462 #ifndef GC_AMIGA_ONLYFAST
463                         GC_AMIGA_MEMF=MEMF_ANY | MEMF_CLEAR;
464                         if(GC_amiga_toany!=NULL) (*GC_amiga_toany)();
465                         GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_any;
466                         return GC_amiga_allocwrapper_any(size,AllocFunction);
467 #endif
468                 }
469 #ifdef GC_AMIGA_PRINTSTATS
470                 else{
471                         nullretries++;
472                 }
473 #endif
474         }
475 
476         return ret;
477 }
478 
GC_amiga_allocwrapper_firsttime(size_t size,void * (* AllocFunction)(size_t size2))479 void *GC_amiga_allocwrapper_firsttime(size_t size,void *(*AllocFunction)(size_t size2)){
480         atexit(&GC_amiga_free_all_mem);
481         chipmax=(char *)SysBase->MaxLocMem; /* For people still having SysBase in chip-mem, this might speed up a bit. */
482         GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_fast;
483         return GC_amiga_allocwrapper_fast(size,AllocFunction);
484 }
485 
486 
487 #endif /* GC_AMIGA_FASTALLOC */
488 
489 
490 
491 /*
492  * The wrapped realloc function.
493  *
494  */
GC_amiga_realloc(void * old_object,size_t new_size_in_bytes)495 void *GC_amiga_realloc(void *old_object,size_t new_size_in_bytes){
496 #ifndef GC_AMIGA_FASTALLOC
497         return GC_realloc(old_object,new_size_in_bytes);
498 #else
499         void *ret;
500         latestsize=new_size_in_bytes;
501         ret=GC_realloc(old_object,new_size_in_bytes);
502         if(ret==NULL && new_size_in_bytes != 0
503            && GC_AMIGA_MEMF==(MEMF_FAST | MEMF_CLEAR)){
504                 /* Out of fast-mem. */
505 #ifdef GC_AMIGA_GC
506                 if(!GC_dont_gc){
507                         GC_gcollect();
508 #ifdef GC_AMIGA_PRINTSTATS
509                         numcollects++;
510 #endif
511                         ret=GC_realloc(old_object,new_size_in_bytes);
512                 }
513                 if(ret==NULL)
514 #endif
515                 {
516 #ifndef GC_AMIGA_ONLYFAST
517                         GC_AMIGA_MEMF=MEMF_ANY | MEMF_CLEAR;
518                         if(GC_amiga_toany!=NULL) (*GC_amiga_toany)();
519                         GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_any;
520                         ret=GC_realloc(old_object,new_size_in_bytes);
521 #endif
522                 }
523 #ifdef GC_AMIGA_PRINTSTATS
524                 else{
525                         nullretries++;
526                 }
527 #endif
528         }
529         if(ret==NULL && new_size_in_bytes != 0){
530                 WARN("Out of Memory!  Returning NIL!\n", 0);
531         }
532 #ifdef GC_AMIGA_PRINTSTATS
533         if(((char *)ret)<chipmax && ret!=NULL){
534                 chipa+=new_size_in_bytes;
535         }
536 #endif
537         return ret;
538 #endif
539 }
540 
541 #endif /* GC_AMIGA_AM */
542