3 /* This malloc-style heap code is reasonably generic. Maybe in the future, it
4 will be used for the data heap too, if we ever get incremental
5 mark/sweep/compact GC. */
6 void new_heap(F_HEAP
*heap
, CELL size
)
8 heap
->segment
= alloc_segment(align_page(size
));
10 fatal_error("Out of memory in new_heap",size
);
11 heap
->free_list
= NULL
;
14 /* Allocate a code heap during startup */
15 void init_code_heap(CELL size
)
17 new_heap(&code_heap
,size
);
20 bool in_code_heap_p(CELL ptr
)
22 return (ptr
>= code_heap
.segment
->start
23 && ptr
<= code_heap
.segment
->end
);
26 /* If there is no previous block, next_free becomes the head of the free list,
28 INLINE
void update_free_list(F_HEAP
*heap
, F_BLOCK
*prev
, F_BLOCK
*next_free
)
31 prev
->next_free
= next_free
;
33 heap
->free_list
= next_free
;
36 /* Called after reading the code heap from the image file, and after code GC.
38 In the former case, we must add a large free block from compiling.base + size to
40 void build_free_list(F_HEAP
*heap
, CELL size
)
43 F_BLOCK
*prev_free
= NULL
;
44 F_BLOCK
*scan
= first_block(heap
);
45 F_BLOCK
*end
= (F_BLOCK
*)(heap
->segment
->start
+ size
);
47 /* Add all free blocks to the free list */
48 while(scan
&& scan
< end
)
53 update_free_list(heap
,prev_free
,scan
);
59 critical_error("Invalid scan->status",(CELL
)scan
);
64 scan
= next_block(heap
,scan
);
67 /* If there is room at the end of the heap, add a free block. This
68 branch is only taken after loading a new image, not after code GC */
69 if((CELL
)(end
+ 1) <= heap
->segment
->end
)
72 end
->next_free
= NULL
;
73 end
->size
= heap
->segment
->end
- (CELL
)end
;
75 /* add final free block */
76 update_free_list(heap
,prev_free
,end
);
78 /* This branch is taken if the newly loaded image fits exactly, or
82 /* even if there's no room at the end of the heap for a new
83 free block, we might have to jigger it up by a few bytes in
84 case prev + prev->size */
86 prev
->size
= heap
->segment
->end
- (CELL
)prev
;
88 /* this is the last free block */
89 update_free_list(heap
,prev_free
,NULL
);
94 /* Allocate a block of memory from the mark and sweep GC heap */
95 CELL
heap_allot(F_HEAP
*heap
, CELL size
)
98 F_BLOCK
*scan
= heap
->free_list
;
100 size
= (size
+ 31) & ~31;
104 CELL this_size
= scan
->size
- sizeof(F_BLOCK
);
106 if(scan
->status
!= B_FREE
)
107 critical_error("Invalid block in free list",(CELL
)scan
);
112 scan
= scan
->next_free
;
116 /* we found a candidate block */
119 if(this_size
- size
<= sizeof(F_BLOCK
))
121 /* too small to be split */
122 next_free
= scan
->next_free
;
126 /* split the block in two */
127 CELL new_size
= size
+ sizeof(F_BLOCK
);
128 F_BLOCK
*split
= (F_BLOCK
*)((CELL
)scan
+ new_size
);
129 split
->status
= B_FREE
;
130 split
->size
= scan
->size
- new_size
;
131 split
->next_free
= scan
->next_free
;
132 scan
->size
= new_size
;
136 /* update the free list */
137 update_free_list(heap
,prev
,next_free
);
139 /* this is our new block */
140 scan
->status
= B_ALLOCATED
;
142 return (CELL
)(scan
+ 1);
148 /* If in the middle of code GC, we have to grow the heap, GC restarts from
149 scratch, so we have to unmark any marked blocks. */
150 void unmark_marked(F_HEAP
*heap
)
152 F_BLOCK
*scan
= first_block(heap
);
156 if(scan
->status
== B_MARKED
)
157 scan
->status
= B_ALLOCATED
;
159 scan
= next_block(heap
,scan
);
163 /* After code GC, all referenced code blocks have status set to B_MARKED, so any
164 which are allocated and not marked can be reclaimed. */
165 void free_unmarked(F_HEAP
*heap
)
167 F_BLOCK
*prev
= NULL
;
168 F_BLOCK
*scan
= first_block(heap
);
175 if(prev
&& prev
->status
== B_FREE
)
176 prev
->size
+= scan
->size
;
179 scan
->status
= B_FREE
;
184 if(prev
&& prev
->status
== B_FREE
)
185 prev
->size
+= scan
->size
;
188 scan
->status
= B_ALLOCATED
;
192 critical_error("Invalid scan->status",(CELL
)scan
);
195 scan
= next_block(heap
,scan
);
198 build_free_list(heap
,heap
->segment
->size
);
201 /* Compute total sum of sizes of free blocks, and size of largest free block */
202 void heap_usage(F_HEAP
*heap
, CELL
*used
, CELL
*total_free
, CELL
*max_free
)
208 F_BLOCK
*scan
= first_block(heap
);
218 *total_free
+= scan
->size
;
219 if(scan
->size
> *max_free
)
220 *max_free
= scan
->size
;
223 critical_error("Invalid scan->status",(CELL
)scan
);
226 scan
= next_block(heap
,scan
);
230 /* The size of the heap, not including the last block if it's free */
231 CELL
heap_size(F_HEAP
*heap
)
233 F_BLOCK
*scan
= first_block(heap
);
235 while(next_block(heap
,scan
) != NULL
)
236 scan
= next_block(heap
,scan
);
238 /* this is the last block in the heap, and it is free */
239 if(scan
->status
== B_FREE
)
240 return (CELL
)scan
- heap
->segment
->start
;
241 /* otherwise the last block is allocated */
243 return heap
->segment
->size
;
246 /* Apply a function to every code block */
247 void iterate_code_heap(CODE_HEAP_ITERATOR iter
)
249 F_BLOCK
*scan
= first_block(&code_heap
);
253 if(scan
->status
!= B_FREE
)
254 iterate_code_heap_step(block_to_compiled(scan
),iter
);
255 scan
= next_block(&code_heap
,scan
);
259 /* Copy all literals referenced from a code block to newspace */
260 void collect_literals_step(F_COMPILED
*compiled
, CELL code_start
, CELL literals_start
)
262 if(collecting_gen
>= compiled
->last_scan
)
265 CELL literal_end
= literals_start
+ compiled
->literals_length
;
267 if(collecting_accumulation_gen_p())
268 compiled
->last_scan
= collecting_gen
;
270 compiled
->last_scan
= collecting_gen
+ 1;
272 for(scan
= literals_start
; scan
< literal_end
; scan
+= CELLS
)
273 copy_handle((CELL
*)scan
);
275 if(compiled
->relocation
!= F
)
277 copy_handle(&compiled
->relocation
);
279 F_BYTE_ARRAY
*relocation
= untag_object(compiled
->relocation
);
281 F_REL
*rel
= (F_REL
*)(relocation
+ 1);
282 F_REL
*rel_end
= (F_REL
*)((char *)rel
+ byte_array_capacity(relocation
));
286 if(REL_TYPE(rel
) == RT_IMMEDIATE
)
288 CELL offset
= rel
->offset
+ code_start
;
289 F_FIXNUM absolute_value
= get(CREF(literals_start
,REL_ARGUMENT(rel
)));
290 apply_relocation(REL_CLASS(rel
),offset
,absolute_value
);
297 flush_icache(code_start
,literals_start
- code_start
);
301 /* Copy literals referenced from all code blocks to newspace */
302 void collect_literals(void)
304 iterate_code_heap(collect_literals_step
);
307 /* Mark all XTs and literals referenced from a word XT */
308 void recursive_mark(F_BLOCK
*block
)
310 /* If already marked, do nothing */
311 switch(block
->status
)
316 block
->status
= B_MARKED
;
319 critical_error("Marking the wrong block",(CELL
)block
);
323 F_COMPILED
*compiled
= block_to_compiled(block
);
324 iterate_code_heap_step(compiled
,collect_literals_step
);
327 /* Push the free space and total size of the code heap */
328 void primitive_code_room(void)
330 CELL used
, total_free
, max_free
;
331 heap_usage(&code_heap
,&used
,&total_free
,&max_free
);
332 dpush(tag_fixnum((code_heap
.segment
->size
) / 1024));
333 dpush(tag_fixnum(used
/ 1024));
334 dpush(tag_fixnum(total_free
/ 1024));
335 dpush(tag_fixnum(max_free
/ 1024));
338 /* Dump all code blocks for debugging */
339 void dump_heap(F_HEAP
*heap
)
343 F_BLOCK
*scan
= first_block(heap
);
354 size
+= object_size(block_to_compiled(scan
)->relocation
);
355 status
= "allocated";
358 size
+= object_size(block_to_compiled(scan
)->relocation
);
366 print_cell_hex((CELL
)scan
); print_string(" ");
367 print_cell_hex(scan
->size
); print_string(" ");
368 print_string(status
); print_string("\n");
370 scan
= next_block(heap
,scan
);
373 print_cell(size
); print_string(" bytes of relocation data\n");
376 /* Compute where each block is going to go, after compaction */
377 CELL
compute_heap_forwarding(F_HEAP
*heap
)
379 F_BLOCK
*scan
= first_block(heap
);
380 CELL address
= (CELL
)first_block(heap
);
384 if(scan
->status
== B_ALLOCATED
)
386 scan
->forwarding
= (F_BLOCK
*)address
;
387 address
+= scan
->size
;
389 else if(scan
->status
== B_MARKED
)
390 critical_error("Why is the block marked?",0);
392 scan
= next_block(heap
,scan
);
395 return address
- heap
->segment
->start
;
398 F_COMPILED
*forward_xt(F_COMPILED
*compiled
)
400 return block_to_compiled(compiled_to_block(compiled
)->forwarding
);
403 void forward_frame_xt(F_STACK_FRAME
*frame
)
405 CELL offset
= (CELL
)FRAME_RETURN_ADDRESS(frame
) - (CELL
)frame_code(frame
);
406 F_COMPILED
*forwarded
= forward_xt(frame_code(frame
));
407 frame
->xt
= (XT
)(forwarded
+ 1);
408 FRAME_RETURN_ADDRESS(frame
) = (XT
)((CELL
)forwarded
+ offset
);
411 void forward_object_xts(void)
417 while((obj
= next_object()) != F
)
419 if(type_of(obj
) == WORD_TYPE
)
421 F_WORD
*word
= untag_object(obj
);
423 word
->code
= forward_xt(word
->code
);
425 word
->profiling
= forward_xt(word
->profiling
);
427 else if(type_of(obj
) == QUOTATION_TYPE
)
429 F_QUOTATION
*quot
= untag_object(obj
);
431 if(quot
->compiledp
!= F
)
432 quot
->code
= forward_xt(quot
->code
);
434 else if(type_of(obj
) == CALLSTACK_TYPE
)
436 F_CALLSTACK
*stack
= untag_object(obj
);
437 iterate_callstack_object(stack
,forward_frame_xt
);
441 /* End the heap scan */
445 /* Set the XT fields now that the heap has been compacted */
446 void fixup_object_xts(void)
452 while((obj
= next_object()) != F
)
454 if(type_of(obj
) == WORD_TYPE
)
456 F_WORD
*word
= untag_object(obj
);
457 update_word_xt(word
);
459 else if(type_of(obj
) == QUOTATION_TYPE
)
461 F_QUOTATION
*quot
= untag_object(obj
);
463 if(quot
->compiledp
!= F
)
464 set_quot_xt(quot
,quot
->code
);
468 /* End the heap scan */
472 void compact_heap(F_HEAP
*heap
)
474 F_BLOCK
*scan
= first_block(heap
);
478 F_BLOCK
*next
= next_block(heap
,scan
);
480 if(scan
->status
== B_ALLOCATED
&& scan
!= scan
->forwarding
)
481 memcpy(scan
->forwarding
,scan
,scan
->size
);
486 /* Move all free space to the end of the code heap. This is not very efficient,
487 since it makes several passes over the code and data heaps, but we only ever
488 do this before saving a deployed image and exiting, so performaance is not
490 void compact_code_heap(void)
492 /* Free all unreachable code blocks */
495 /* Figure out where the code heap blocks are going to end up */
496 CELL size
= compute_heap_forwarding(&code_heap
);
498 /* Update word and quotation code pointers */
499 forward_object_xts();
501 /* Actually perform the compaction */
502 compact_heap(&code_heap
);
504 /* Update word and quotation XTs */
507 /* Now update the free list; there will be a single free block at
509 build_free_list(&code_heap
,size
);