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