[llvm] Fix the loadable llvm build.
[mono.git] / mono / metadata / sgen-pinning.c
index ee5c5ec55159a3e970590bbd74db2d8e1b4642a5..9ada778bc7460520a599b808cd156eaaa734d48e 100644 (file)
@@ -1,25 +1,22 @@
 /*
+ * sgen-pinning.c: The pin queue.
+ *
  * Copyright 2001-2003 Ximian, Inc
  * Copyright 2003-2010 Novell, Inc.
- * 
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- * 
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * Copyright (C) 2012 Xamarin Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License 2.0 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License 2.0 along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include "config.h"
@@ -27,6 +24,7 @@
 
 #include "metadata/sgen-gc.h"
 #include "metadata/sgen-pinning.h"
+#include "metadata/sgen-protocol.h"
 
 static void** pin_queue;
 static int pin_queue_size = 0;
@@ -37,13 +35,13 @@ static int last_num_pinned = 0;
 static void *pin_hash_filter [PIN_HASH_SIZE];
 
 void
-mono_sgen_init_pinning (void)
+sgen_init_pinning (void)
 {
        memset (pin_hash_filter, 0, sizeof (pin_hash_filter));
 }
 
 void
-mono_sgen_finish_pinning (void)
+sgen_finish_pinning (void)
 {
        last_num_pinned = next_pin_slot;
        next_pin_slot = 0;
@@ -53,16 +51,16 @@ static void
 realloc_pin_queue (void)
 {
        int new_size = pin_queue_size? pin_queue_size + pin_queue_size/2: 1024;
-       void **new_pin = mono_sgen_alloc_internal_dynamic (sizeof (void*) * new_size, INTERNAL_MEM_PIN_QUEUE);
+       void **new_pin = sgen_alloc_internal_dynamic (sizeof (void*) * new_size, INTERNAL_MEM_PIN_QUEUE, TRUE);
        memcpy (new_pin, pin_queue, sizeof (void*) * next_pin_slot);
-       mono_sgen_free_internal_dynamic (pin_queue, sizeof (void*) * pin_queue_size, INTERNAL_MEM_PIN_QUEUE);
+       sgen_free_internal_dynamic (pin_queue, sizeof (void*) * pin_queue_size, INTERNAL_MEM_PIN_QUEUE);
        pin_queue = new_pin;
        pin_queue_size = new_size;
-       DEBUG (4, fprintf (gc_debug_file, "Reallocated pin queue to size: %d\n", new_size));
+       SGEN_LOG (4, "Reallocated pin queue to size: %d", new_size);
 }
 
 void
-mono_sgen_pin_stage_ptr (void *ptr)
+sgen_pin_stage_ptr (void *ptr)
 {
        /*very simple multiplicative hash function, tons better than simple and'ng */ 
        int hash_idx = ((mword)ptr * 1737350767) & (PIN_HASH_SIZE - 1);
@@ -93,7 +91,7 @@ optimized_pin_queue_search (void *addr)
 }
 
 void**
-mono_sgen_find_optimized_pin_queue_area (void *start, void *end, int *num)
+sgen_find_optimized_pin_queue_area (void *start, void *end, int *num)
 {
        int first, last;
        first = optimized_pin_queue_search (start);
@@ -105,29 +103,37 @@ mono_sgen_find_optimized_pin_queue_area (void *start, void *end, int *num)
 }
 
 void
-mono_sgen_find_section_pin_queue_start_end (GCMemSection *section)
+sgen_find_section_pin_queue_start_end (GCMemSection *section)
 {
-       DEBUG (6, fprintf (gc_debug_file, "Pinning from section %p (%p-%p)\n", section, section->data, section->end_data));
-       section->pin_queue_start = mono_sgen_find_optimized_pin_queue_area (section->data, section->end_data, &section->pin_queue_num_entries);
-       DEBUG (6, fprintf (gc_debug_file, "Found %d pinning addresses in section %p\n", section->pin_queue_num_entries, section));
+       SGEN_LOG (6, "Pinning from section %p (%p-%p)", section, section->data, section->end_data);
+       section->pin_queue_start = sgen_find_optimized_pin_queue_area (section->data, section->end_data, &section->pin_queue_num_entries);
+       SGEN_LOG (6, "Found %d pinning addresses in section %p", section->pin_queue_num_entries, section);
 }
 
 /*This will setup the given section for the while pin queue. */
 void
-mono_sgen_pinning_setup_section (GCMemSection *section)
+sgen_pinning_setup_section (GCMemSection *section)
 {
        section->pin_queue_start = pin_queue;
        section->pin_queue_num_entries = next_pin_slot;
 }
 
+void
+sgen_pinning_trim_queue_to_section (GCMemSection *section)
+{
+       next_pin_slot = section->pin_queue_num_entries;
+}
 
 void
-mono_sgen_pin_queue_clear_discarded_entries (GCMemSection *section, int max_pin_slot)
+sgen_pin_queue_clear_discarded_entries (GCMemSection *section, int max_pin_slot)
 {
        void **start = section->pin_queue_start + section->pin_queue_num_entries;
        void **end = pin_queue + max_pin_slot;
        void *addr;
 
+       if (!start)
+               return;
+
        for (; start < end; ++start) {
                addr = *start;
                if ((char*)addr < section->data || (char*)addr > section->end_data)
@@ -136,31 +142,16 @@ mono_sgen_pin_queue_clear_discarded_entries (GCMemSection *section, int max_pin_
        }
 }
 
-static G_GNUC_UNUSED void
-print_nursery_gaps (void* start_nursery, void *end_nursery)
-{
-       int i;
-       gpointer first = start_nursery;
-       gpointer next;
-       for (i = 0; i < next_pin_slot; ++i) {
-               next = pin_queue [i];
-               fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
-               first = next;
-       }
-       next = end_nursery;
-       fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
-}
-
 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
 void
-mono_sgen_optimize_pin_queue (int start_slot)
+sgen_optimize_pin_queue (int start_slot)
 {
        void **start, **cur, **end;
        /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
        /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
-       DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
+       SGEN_LOG (5, "Sorting pin queue, size: %d", next_pin_slot);
        if ((next_pin_slot - start_slot) > 1)
-               mono_sgen_sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
+               sgen_sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
        start = cur = pin_queue + start_slot;
        end = pin_queue + next_pin_slot;
        while (cur < end) {
@@ -170,24 +161,188 @@ mono_sgen_optimize_pin_queue (int start_slot)
                start++;
        };
        next_pin_slot = start - pin_queue;
-       DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
-       //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));   
+       SGEN_LOG (5, "Pin queue reduced to size: %d", next_pin_slot);
 }
 
 int
-mono_sgen_get_pinned_count (void)
+sgen_get_pinned_count (void)
 {
        return next_pin_slot;
 }
 
 void
-mono_sgen_dump_pin_queue (void)
+sgen_dump_pin_queue (void)
 {
        int i;
 
        for (i = 0; i < last_num_pinned; ++i) {
-               DEBUG (3, fprintf (gc_debug_file, "Bastard pinning obj %p (%s), size: %d\n", pin_queue [i], mono_sgen_safe_name (pin_queue [i]), mono_sgen_safe_object_get_size (pin_queue [i])));
+               SGEN_LOG (3, "Bastard pinning obj %p (%s), size: %d", pin_queue [i], sgen_safe_name (pin_queue [i]), sgen_safe_object_get_size (pin_queue [i]));
        }       
 }
 
+typedef struct _CementHashEntry CementHashEntry;
+struct _CementHashEntry {
+       char *obj;
+       unsigned int count;
+};
+
+static CementHashEntry cement_hash [SGEN_CEMENT_HASH_SIZE];
+static CementHashEntry cement_hash_concurrent [SGEN_CEMENT_HASH_SIZE];
+
+static gboolean cement_enabled = TRUE;
+static gboolean cement_concurrent = FALSE;
+
+void
+sgen_cement_init (gboolean enabled)
+{
+       cement_enabled = enabled;
+}
+
+void
+sgen_cement_reset (void)
+{
+       SGEN_ASSERT (1, !cement_concurrent, "Concurrent cementing cannot simply be reset");
+
+       memset (cement_hash, 0, sizeof (cement_hash));
+       binary_protocol_cement_reset ();
+}
+
+/*
+ * The reason we cannot simply reset cementing at the start of a
+ * concurrent collection is that the nursery collections running
+ * concurrently must keep pinning the cemented objects, because we
+ * don't have the global remsets that point to them anymore - if the
+ * nursery collector moved the cemented objects, we'd have invalid
+ * pointers in the major heap.
+ *
+ * What we do instead is to reset cementing at the start of concurrent
+ * collections in such a way that nursery collections happening during
+ * the major collection still pin the formerly cemented objects.  We
+ * have a shadow cementing table for that purpose.  The nursery
+ * collections still work with the old cementing table, while the
+ * major collector builds up a new cementing table, adding global
+ * remsets whenever needed like usual.  When the major collector
+ * finishes, the old cementing table is replaced by the new one.
+ */
+
+void
+sgen_cement_concurrent_start (void)
+{
+       SGEN_ASSERT (1, !cement_concurrent, "Concurrent cementing has already been started");
+       cement_concurrent = TRUE;
+
+       memset (cement_hash_concurrent, 0, sizeof (cement_hash));
+}
+
+void
+sgen_cement_concurrent_finish (void)
+{
+       SGEN_ASSERT (1, cement_concurrent, "Concurrent cementing hasn't been started");
+       cement_concurrent = FALSE;
+
+       memcpy (cement_hash, cement_hash_concurrent, sizeof (cement_hash));
+}
+
+gboolean
+sgen_cement_lookup (char *obj)
+{
+       int i = mono_aligned_addr_hash (obj) % SGEN_CEMENT_HASH_SIZE;
+
+       SGEN_ASSERT (5, sgen_ptr_in_nursery (obj), "Looking up cementing for non-nursery objects makes no sense");
+
+       if (!cement_enabled)
+               return FALSE;
+
+       if (!cement_hash [i].obj)
+               return FALSE;
+       if (cement_hash [i].obj != obj)
+               return FALSE;
+
+       return cement_hash [i].count >= SGEN_CEMENT_THRESHOLD;
+}
+
+gboolean
+sgen_cement_lookup_or_register (char *obj)
+{
+       int i;
+       CementHashEntry *hash;
+       gboolean concurrent_cementing = sgen_concurrent_collection_in_progress ();
+
+       if (!cement_enabled)
+               return FALSE;
+
+       if (concurrent_cementing)
+               SGEN_ASSERT (5, cement_concurrent, "Cementing wasn't inited with concurrent flag");
+
+       if (concurrent_cementing)
+               hash = cement_hash_concurrent;
+       else
+               hash = cement_hash;
+
+       /*
+        * We use modulus hashing, which is fine with constants as gcc
+        * can optimize them to multiplication, but with variable
+        * values it would be a bad idea given armv7 has no hardware
+        * for division, making it 20x slower than a multiplication.
+        *
+        * This code path can be quite hot, depending on the workload,
+        * so if we make the hash size user-adjustable we should
+        * figure out something not involving division.
+        */
+       i = mono_aligned_addr_hash (obj) % SGEN_CEMENT_HASH_SIZE;
+
+       SGEN_ASSERT (5, sgen_ptr_in_nursery (obj), "Can only cement pointers to nursery objects");
+
+       if (!hash [i].obj) {
+               SGEN_ASSERT (5, !hash [i].count, "Cementing hash inconsistent");
+               hash [i].obj = obj;
+       } else if (hash [i].obj != obj) {
+               return FALSE;
+       }
+
+       if (hash [i].count >= SGEN_CEMENT_THRESHOLD)
+               return TRUE;
+
+       ++hash [i].count;
+       if (hash [i].count == SGEN_CEMENT_THRESHOLD) {
+               if (G_UNLIKELY (MONO_GC_OBJ_CEMENTED_ENABLED())) {
+                       MonoVTable *vt G_GNUC_UNUSED = (MonoVTable*)SGEN_LOAD_VTABLE (obj);
+                       MONO_GC_OBJ_CEMENTED ((mword)obj, sgen_safe_object_get_size ((MonoObject*)obj),
+                                       vt->klass->name_space, vt->klass->name);
+               }
+#ifdef SGEN_BINARY_PROTOCOL
+               binary_protocol_cement (obj, (gpointer)SGEN_LOAD_VTABLE (obj),
+                               sgen_safe_object_get_size ((MonoObject*)obj));
+#endif
+       }
+
+       return FALSE;
+}
+
+void
+sgen_cement_iterate (IterateObjectCallbackFunc callback, void *callback_data)
+{
+       int i;
+       for (i = 0; i < SGEN_CEMENT_HASH_SIZE; ++i) {
+               if (!cement_hash [i].count)
+                       continue;
+
+               SGEN_ASSERT (5, cement_hash [i].count >= SGEN_CEMENT_THRESHOLD, "Cementing hash inconsistent");
+
+               callback (cement_hash [i].obj, 0, callback_data);
+       }
+}
+
+void
+sgen_cement_clear_below_threshold (void)
+{
+       int i;
+       for (i = 0; i < SGEN_CEMENT_HASH_SIZE; ++i) {
+               if (cement_hash [i].count < SGEN_CEMENT_THRESHOLD) {
+                       cement_hash [i].obj = NULL;
+                       cement_hash [i].count = 0;
+               }
+       }
+}
+
 #endif /* HAVE_SGEN_GC */