1 /* This file is part of NIT ( http://www.nitlanguage.org ).
3 * Copyright 2009 Julien Chevalier <chevjulien@gmail.com>
5 * This file is free software, which comes along with NIT. This software is
6 * distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
7 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
8 * PARTICULAR PURPOSE. You can modify it is you want, provided this header
9 * is kept unaltered, and a notification of the changes is added.
10 * You are allowed to redistribute it and sell it, alone or is a part of
18 # define assert(x) while(0)
21 #include "gc_static_objects_list.h"
23 /** The current active heap.
24 * During GC collect, it is the destination (i.e. the next active heap). */
25 char *gc_heap_pointer
;
27 /** The inactive heap.
28 * During GC collect, is is the source (i.e. the previous active heap). */
29 char *gc_old_heap_pointer
;
31 /** The size (total capacity) of the active heap. */
34 /** The total size of currently allocated objects. */
37 /** Typedef used by boxing val. FIXME: Do someting better! */
38 typedef struct TBOX_struct
{
39 const classtable_elt_t
*vft
;
44 /** First free position in the active heap. */
45 char *gc_allocation_pointer
;
47 /** Position of objects copied but not yet visited (during GC collect). */
48 char *gc_scavenging_pointer
;
50 /** List of global objects. */
51 GC_List staticObjects
;
53 /** Size of a memory page. Used to grow or shrink the GC heap. */
54 unsigned long page_size
;
56 /* Markbit manipulations */
57 #define GET_MARKBIT(x) (((val_t)((x)[0].vft)) & 1)
58 #define SET_MARKBIT(x) ((x)->vft = (void*)(((bigint)((x)->vft)) | 1))
59 #define REMOVE_MARKBIT(x) ((x) ^ 1)
61 void Nit_gc_init(void) {
62 page_size
= sysconf(_SC_PAGESIZE
);
64 gc_heap_pointer
= calloc(1, page_size
);
65 if (gc_heap_pointer
==NULL
) exit(1);
66 gc_heap_size
= page_size
;
69 gc_allocation_pointer
= gc_heap_pointer
;
71 gc_old_heap_pointer
= NULL
;
72 gc_scavenging_pointer
= NULL
;
74 GC_List_Init(&staticObjects
);
77 /** Copy object to the new heap if needed then return the address of the copy. */
78 static val_t
GC_evacuation(obj_t object
) {
82 Nit_NativeArray array
;
84 assert(ISOBJ(object
) && !ISNULL(object
));
85 if (GET_MARKBIT(object
) != (bigint
)0) {
86 /* Object already evacuated. So forward it. */
87 newAdress
= REMOVE_MARKBIT((bigint
)((object
)->vft
));
89 newAdress
= (val_t
)gc_allocation_pointer
;
90 if (OBJ_IS_ARRAY(object
)) {
91 array
= (Nit_NativeArray
)object
;
92 size
= sizeof(struct Nit_NativeArray
) + ((array
->size
- 1) * sizeof(val_t
));
93 } else if (OBJ_IS_BOX(object
)) {
94 size
= sizeof(struct TBOX_struct
);
96 objectSize
= (bigint
)((object
)[0].vft
[1].i
);
97 size
= (objectSize
) * sizeof(val_t
);
99 memcpy(gc_allocation_pointer
, object
, size
);
100 (object
)[0].vft
= (classtable_elt_t
*)gc_allocation_pointer
;
102 gc_allocation_pointer
+= size
;
108 /** Process the next evacuated object to copy/update its fields. */
109 static void GC_scavenging(void) {
113 val_t referencedObject
;
115 object
= (obj_t
)gc_scavenging_pointer
;
116 if (OBJ_IS_BOX(object
)) {
117 size
= sizeof(struct TBOX_struct
);
118 } else if (OBJ_IS_ARRAY(object
)) {
119 Nit_NativeArray array
= (Nit_NativeArray
)gc_scavenging_pointer
;
120 size
= sizeof(struct Nit_NativeArray
) + ((array
->size
- 1) * sizeof(val_t
));
121 for (i
= 0; i
< array
->size
; i
++) {
122 referencedObject
= array
->val
[i
];
123 if (!ISNULL(referencedObject
) && ISOBJ(referencedObject
)) {
124 array
->val
[i
] = GC_evacuation((obj_t
)referencedObject
);
128 bigint objectSize
= (bigint
)((object
)->vft
[1].i
);
129 size
= (objectSize
) * sizeof(val_t
);
130 for (i
= 2; i
< objectSize
; i
++) {
131 referencedObject
= object
[i
].objectSize
;
132 if (!ISNULL(referencedObject
) && ISOBJ(referencedObject
)) {
133 object
[i
].objectSize
= GC_evacuation((obj_t
)referencedObject
);
138 gc_scavenging_pointer
+= size
;
141 /** Collect live objects in the old head and copy them to the active heap. */
142 static void GC_collect(void) {
146 struct stack_frame_t
*frame
= stack_frame_head
;
147 GC_static_object
*staticObject
= staticObjects
.top
;
149 struct nitni_ref
*global_ref
;
150 struct nitni_ref_array_link
*local_ref_array_link
; /* for native interface */
152 gc_allocation_pointer
= gc_heap_pointer
;
153 gc_scavenging_pointer
= gc_heap_pointer
;
155 /* Process global objects (registered by GC_add_static_object) */
156 for (i
= 0; i
< staticObjects
.size
; i
++) {
157 object
= *(val_t
*)(staticObject
->pointer
);
158 if (!ISNULL(object
) && ISOBJ(object
)) {
159 *(staticObject
->pointer
) = GC_evacuation((obj_t
)object
);
161 staticObject
= staticObject
->next
;
164 /* Process global reference to Nit objects from C code */
165 global_ref
= nitni_global_ref_list
->head
;
166 while (global_ref
!= NULL
) {
167 object
= global_ref
->val
;
168 if (!ISNULL(object
) && ISOBJ(object
)) {
169 global_ref
->val
= GC_evacuation((obj_t
)object
);
171 global_ref
= global_ref
->next
;
174 /* Process function frames (local variables) */
175 while (frame
!= NULL
) {
176 for (j
= 0; j
< frame
->REG_size
; j
++) {
177 object
= frame
->REG
[j
];
178 if (!ISNULL(object
) && ISOBJ(object
)) {
179 frame
->REG
[j
] = GC_evacuation((obj_t
)object
);
183 /* Process C references to Nit objects */
184 local_ref_array_link
= frame
->nitni_local_ref_head
;
185 while ( local_ref_array_link
!= NULL
)
187 for (j
= 0; j
< local_ref_array_link
->count
; j
++) {
188 object
= local_ref_array_link
->reg
[j
]->val
;
189 if (!ISNULL(object
) && ISOBJ(object
)) {
190 local_ref_array_link
->reg
[j
]->val
= GC_evacuation((obj_t
)object
);
193 local_ref_array_link
= local_ref_array_link
->next
;
196 if (frame
== frame
->prev
) break;
199 while (gc_allocation_pointer
!= gc_scavenging_pointer
) {
203 gc_scavenging_pointer
= NULL
;
205 gc_used_size
= gc_allocation_pointer
- gc_heap_pointer
;
208 /** Allocate a new active heap. */
209 static void GC_prepare_heap_size(size_t size
) {
210 assert(gc_old_heap_pointer
== NULL
);
211 gc_old_heap_pointer
= gc_heap_pointer
;
213 gc_heap_pointer
= calloc(1, size
);
214 if (gc_heap_pointer
== NULL
) {
220 void Nit_gc_print_usage(void) {
221 #if __STDC_VERSION >= 199901L
222 /* If we are compiling with standard C99 or more recent, we can use %zu. */
223 printf("GC: Size %zu usage %zu (%.2f%%)\n", gc_heap_size
, gc_used_size
, 100.0*gc_used_size
/gc_heap_size
);
225 /* We are not compiling with a standard that allows us to use %zu, let's cast the two unsigned integers into the biggest we can !*/
226 printf("GC: Size %lu usage %lu (%.2f%%)\n", (unsigned long)gc_heap_size
, (unsigned long)gc_used_size
, 100.0*gc_used_size
/gc_heap_size
);
230 /** Enlarge the heap and collect dead objects. Can also shrink the heap.
231 * Size is the aditionnal number of bytes required. */
232 static void GC_enlarge_and_collect(size_t size
) {
235 /* Heap grows exponentially. */
236 try_size
= gc_heap_size
;
237 while (size
> (try_size
- gc_used_size
)) {
238 try_size
= try_size
* 1.5;
241 /* Build a new heap. */
242 GC_prepare_heap_size(try_size
);
247 /* Free the old heap. */
248 free(gc_old_heap_pointer
);
249 gc_old_heap_pointer
= NULL
;
252 while (gc_heap_size
> 2*page_size
&& 3*(gc_used_size
+size
) < gc_heap_size
)
253 gc_heap_size
= gc_heap_size
/ 1.5;
254 /*Nit_gc_print_usage();*/
257 void *Nit_gc_malloc(size_t size
) {
260 if (size
> (gc_heap_size
- gc_used_size
))
261 GC_enlarge_and_collect(size
);
263 result
= gc_allocation_pointer
;
264 gc_allocation_pointer
+= size
;
265 gc_used_size
+= size
;
270 void GC_add_static_object(val_t
*pointer
) {
271 GC_List_Push(&staticObjects
, pointer
);
274 /* Is invoked by intern method Sys:force_garbage_collection */
275 void Nit_gc_force_garbage_collection( void ) {
276 GC_enlarge_and_collect( 0 );