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