summaryrefslogtreecommitdiff
path: root/ocaml-binary-annot/byterun/minor_gc.c
diff options
context:
space:
mode:
Diffstat (limited to 'ocaml-binary-annot/byterun/minor_gc.c')
-rw-r--r--ocaml-binary-annot/byterun/minor_gc.c321
1 files changed, 321 insertions, 0 deletions
diff --git a/ocaml-binary-annot/byterun/minor_gc.c b/ocaml-binary-annot/byterun/minor_gc.c
new file mode 100644
index 0000000000..91aa29799b
--- /dev/null
+++ b/ocaml-binary-annot/byterun/minor_gc.c
@@ -0,0 +1,321 @@
+/***********************************************************************/
+/* */
+/* Objective Caml */
+/* */
+/* Damien Doligez, projet Para, INRIA Rocquencourt */
+/* */
+/* Copyright 1996 Institut National de Recherche en Informatique et */
+/* en Automatique. All rights reserved. This file is distributed */
+/* under the terms of the GNU Library General Public License, with */
+/* the special exception on linking described in file ../LICENSE. */
+/* */
+/***********************************************************************/
+
+/* $Id$ */
+
+#include <string.h>
+#include "config.h"
+#include "fail.h"
+#include "finalise.h"
+#include "gc.h"
+#include "gc_ctrl.h"
+#include "major_gc.h"
+#include "memory.h"
+#include "minor_gc.h"
+#include "misc.h"
+#include "mlvalues.h"
+#include "roots.h"
+#include "signals.h"
+#include "weak.h"
+
+asize_t caml_minor_heap_size;
+static void *caml_young_base = NULL;
+CAMLexport char *caml_young_start = NULL, *caml_young_end = NULL;
+CAMLexport char *caml_young_ptr = NULL, *caml_young_limit = NULL;
+
+CAMLexport struct caml_ref_table
+ caml_ref_table = { NULL, NULL, NULL, NULL, NULL, 0, 0},
+ caml_weak_ref_table = { NULL, NULL, NULL, NULL, NULL, 0, 0};
+
+int caml_in_minor_collection = 0;
+
+#ifdef DEBUG
+static unsigned long minor_gc_counter = 0;
+#endif
+
+void caml_alloc_table (struct caml_ref_table *tbl, asize_t sz, asize_t rsv)
+{
+ value **new_table;
+
+ tbl->size = sz;
+ tbl->reserve = rsv;
+ new_table = (value **) caml_stat_alloc ((tbl->size + tbl->reserve)
+ * sizeof (value *));
+ if (tbl->base != NULL) caml_stat_free (tbl->base);
+ tbl->base = new_table;
+ tbl->ptr = tbl->base;
+ tbl->threshold = tbl->base + tbl->size;
+ tbl->limit = tbl->threshold;
+ tbl->end = tbl->base + tbl->size + tbl->reserve;
+}
+
+static void reset_table (struct caml_ref_table *tbl)
+{
+ tbl->size = 0;
+ tbl->reserve = 0;
+ if (tbl->base != NULL) caml_stat_free (tbl->base);
+ tbl->base = tbl->ptr = tbl->threshold = tbl->limit = tbl->end = NULL;
+}
+
+static void clear_table (struct caml_ref_table *tbl)
+{
+ tbl->ptr = tbl->base;
+ tbl->limit = tbl->threshold;
+}
+
+void caml_set_minor_heap_size (asize_t size)
+{
+ char *new_heap;
+ void *new_heap_base;
+
+ Assert (size >= Minor_heap_min);
+ Assert (size <= Minor_heap_max);
+ Assert (size % sizeof (value) == 0);
+ if (caml_young_ptr != caml_young_end) caml_minor_collection ();
+ Assert (caml_young_ptr == caml_young_end);
+ new_heap = caml_aligned_malloc(size, 0, &new_heap_base);
+ if (new_heap == NULL) caml_raise_out_of_memory();
+ if (caml_page_table_add(In_young, new_heap, new_heap + size) != 0)
+ caml_raise_out_of_memory();
+
+ if (caml_young_start != NULL){
+ caml_page_table_remove(In_young, caml_young_start, caml_young_end);
+ free (caml_young_base);
+ }
+ caml_young_base = new_heap_base;
+ caml_young_start = new_heap;
+ caml_young_end = new_heap + size;
+ caml_young_limit = caml_young_start;
+ caml_young_ptr = caml_young_end;
+ caml_minor_heap_size = size;
+
+ reset_table (&caml_ref_table);
+ reset_table (&caml_weak_ref_table);
+}
+
+static value oldify_todo_list = 0;
+
+/* Note that the tests on the tag depend on the fact that Infix_tag,
+ Forward_tag, and No_scan_tag are contiguous. */
+
+void caml_oldify_one (value v, value *p)
+{
+ value result;
+ header_t hd;
+ mlsize_t sz, i;
+ tag_t tag;
+
+ tail_call:
+ if (Is_block (v) && Is_young (v)){
+ Assert (Hp_val (v) >= caml_young_ptr);
+ hd = Hd_val (v);
+ if (hd == 0){ /* If already forwarded */
+ *p = Field (v, 0); /* then forward pointer is first field. */
+ }else{
+ tag = Tag_hd (hd);
+ if (tag < Infix_tag){
+ value field0;
+
+ sz = Wosize_hd (hd);
+ result = caml_alloc_shr (sz, tag);
+ *p = result;
+ field0 = Field (v, 0);
+ Hd_val (v) = 0; /* Set forward flag */
+ Field (v, 0) = result; /* and forward pointer. */
+ if (sz > 1){
+ Field (result, 0) = field0;
+ Field (result, 1) = oldify_todo_list; /* Add this block */
+ oldify_todo_list = v; /* to the "to do" list. */
+ }else{
+ Assert (sz == 1);
+ p = &Field (result, 0);
+ v = field0;
+ goto tail_call;
+ }
+ }else if (tag >= No_scan_tag){
+ sz = Wosize_hd (hd);
+ result = caml_alloc_shr (sz, tag);
+ for (i = 0; i < sz; i++) Field (result, i) = Field (v, i);
+ Hd_val (v) = 0; /* Set forward flag */
+ Field (v, 0) = result; /* and forward pointer. */
+ *p = result;
+ }else if (tag == Infix_tag){
+ mlsize_t offset = Infix_offset_hd (hd);
+ caml_oldify_one (v - offset, p); /* Cannot recurse deeper than 1. */
+ *p += offset;
+ }else{
+ value f = Forward_val (v);
+ tag_t ft = 0;
+ int vv = 1;
+
+ Assert (tag == Forward_tag);
+ if (Is_block (f)){
+ vv = Is_in_value_area(f);
+ if (vv) {
+ ft = Tag_val (Hd_val (f) == 0 ? Field (f, 0) : f);
+ }
+ }
+ if (!vv || ft == Forward_tag || ft == Lazy_tag || ft == Double_tag){
+ /* Do not short-circuit the pointer. Copy as a normal block. */
+ Assert (Wosize_hd (hd) == 1);
+ result = caml_alloc_shr (1, Forward_tag);
+ *p = result;
+ Hd_val (v) = 0; /* Set (GC) forward flag */
+ Field (v, 0) = result; /* and forward pointer. */
+ p = &Field (result, 0);
+ v = f;
+ goto tail_call;
+ }else{
+ v = f; /* Follow the forwarding */
+ goto tail_call; /* then oldify. */
+ }
+ }
+ }
+ }else{
+ *p = v;
+ }
+}
+
+/* Finish the work that was put off by [caml_oldify_one].
+ Note that [caml_oldify_one] itself is called by oldify_mopup, so we
+ have to be careful to remove the first entry from the list before
+ oldifying its fields. */
+void caml_oldify_mopup (void)
+{
+ value v, new_v, f;
+ mlsize_t i;
+
+ while (oldify_todo_list != 0){
+ v = oldify_todo_list; /* Get the head. */
+ Assert (Hd_val (v) == 0); /* It must be forwarded. */
+ new_v = Field (v, 0); /* Follow forward pointer. */
+ oldify_todo_list = Field (new_v, 1); /* Remove from list. */
+
+ f = Field (new_v, 0);
+ if (Is_block (f) && Is_young (f)){
+ caml_oldify_one (f, &Field (new_v, 0));
+ }
+ for (i = 1; i < Wosize_val (new_v); i++){
+ f = Field (v, i);
+ if (Is_block (f) && Is_young (f)){
+ caml_oldify_one (f, &Field (new_v, i));
+ }else{
+ Field (new_v, i) = f;
+ }
+ }
+ }
+}
+
+/* Make sure the minor heap is empty by performing a minor collection
+ if needed.
+*/
+void caml_empty_minor_heap (void)
+{
+ value **r;
+
+ if (caml_young_ptr != caml_young_end){
+ caml_in_minor_collection = 1;
+ caml_gc_message (0x02, "<", 0);
+ caml_oldify_local_roots();
+ for (r = caml_ref_table.base; r < caml_ref_table.ptr; r++){
+ caml_oldify_one (**r, *r);
+ }
+ caml_oldify_mopup ();
+ for (r = caml_weak_ref_table.base; r < caml_weak_ref_table.ptr; r++){
+ if (Is_block (**r) && Is_young (**r)){
+ if (Hd_val (**r) == 0){
+ **r = Field (**r, 0);
+ }else{
+ **r = caml_weak_none;
+ }
+ }
+ }
+ if (caml_young_ptr < caml_young_start) caml_young_ptr = caml_young_start;
+ caml_stat_minor_words += Wsize_bsize (caml_young_end - caml_young_ptr);
+ caml_young_ptr = caml_young_end;
+ caml_young_limit = caml_young_start;
+ clear_table (&caml_ref_table);
+ clear_table (&caml_weak_ref_table);
+ caml_gc_message (0x02, ">", 0);
+ caml_in_minor_collection = 0;
+ }
+ caml_final_empty_young ();
+#ifdef DEBUG
+ {
+ value *p;
+ for (p = (value *) caml_young_start; p < (value *) caml_young_end; ++p){
+ *p = Debug_free_minor;
+ }
+ ++ minor_gc_counter;
+ }
+#endif
+}
+
+/* Do a minor collection and a slice of major collection, call finalisation
+ functions, etc.
+ Leave the minor heap empty.
+*/
+CAMLexport void caml_minor_collection (void)
+{
+ intnat prev_alloc_words = caml_allocated_words;
+
+ caml_empty_minor_heap ();
+
+ caml_stat_promoted_words += caml_allocated_words - prev_alloc_words;
+ ++ caml_stat_minor_collections;
+ caml_major_collection_slice (0);
+ caml_force_major_slice = 0;
+
+ caml_final_do_calls ();
+
+ caml_empty_minor_heap ();
+}
+
+CAMLexport value caml_check_urgent_gc (value extra_root)
+{
+ CAMLparam1 (extra_root);
+ if (caml_force_major_slice) caml_minor_collection();
+ CAMLreturn (extra_root);
+}
+
+void caml_realloc_ref_table (struct caml_ref_table *tbl)
+{ Assert (tbl->ptr == tbl->limit);
+ Assert (tbl->limit <= tbl->end);
+ Assert (tbl->limit >= tbl->threshold);
+
+ if (tbl->base == NULL){
+ caml_alloc_table (tbl, caml_minor_heap_size / sizeof (value) / 8, 256);
+ }else if (tbl->limit == tbl->threshold){
+ caml_gc_message (0x08, "ref_table threshold crossed\n", 0);
+ tbl->limit = tbl->end;
+ caml_urge_major_slice ();
+ }else{ /* This will almost never happen with the bytecode interpreter. */
+ asize_t sz;
+ asize_t cur_ptr = tbl->ptr - tbl->base;
+ Assert (caml_force_major_slice);
+
+ tbl->size *= 2;
+ sz = (tbl->size + tbl->reserve) * sizeof (value *);
+ caml_gc_message (0x08, "Growing ref_table to %"
+ ARCH_INTNAT_PRINTF_FORMAT "dk bytes\n",
+ (intnat) sz/1024);
+ tbl->base = (value **) realloc ((char *) tbl->base, sz);
+ if (tbl->base == NULL){
+ caml_fatal_error ("Fatal error: ref_table overflow\n");
+ }
+ tbl->end = tbl->base + tbl->size + tbl->reserve;
+ tbl->threshold = tbl->base + tbl->size;
+ tbl->ptr = tbl->base + cur_ptr;
+ tbl->limit = tbl->end;
+ }
+}