summaryrefslogtreecommitdiff
path: root/libgfortran/runtime/memory.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgfortran/runtime/memory.c')
-rw-r--r--libgfortran/runtime/memory.c312
1 files changed, 312 insertions, 0 deletions
diff --git a/libgfortran/runtime/memory.c b/libgfortran/runtime/memory.c
new file mode 100644
index 00000000000..ca5eb15244b
--- /dev/null
+++ b/libgfortran/runtime/memory.c
@@ -0,0 +1,312 @@
+/* Memory mamagement routines.
+ Copyright 2002 Free Software Foundation, Inc.
+ Contributed by Paul Brook <paul@nowt.org>
+
+This file is part of the GNU Fortran 95 runtime library (libgfor).
+
+Libgfor is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+Libgfor 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with libgfor; see the file COPYING.LIB. If not,
+write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#include "config.h"
+#include <stdlib.h>
+#include "libgfortran.h"
+
+/* If GFC_CLEAR_MEMORY is defined, the memory allocation routines will
+ return memory that is guaranteed to be set to zero. This can have
+ a severe efficiency penalty, so it should never be set if good
+ performance is desired, but it can help when you're debugging code. */
+#define GFC_CLEAR_MEMORY
+
+/* If GFC_CHECK_MEMORY is defined, we do some sanity checks at runtime.
+ This causes small overhead, but again, it also helps debugging. */
+#define GFC_CHECK_MEMORY
+
+/* We use a double linked list of these structures to keep track of
+ the memory we allocate internally. We could also use this for user
+ allocated memory (ALLOCATE/DEALLOCATE). This should be stored in a
+ seperate list. */
+#define malloc_t prefix(malloc_t)
+typedef struct malloc_t
+{
+ int magic;
+ int marker;
+ struct malloc_t *prev, *next;
+
+ /* The start of the block. */
+ void *data;
+}
+malloc_t;
+
+/* We try to make sure we don't get memory corruption by checking for
+ a magic number. */
+#define GFC_MALLOC_MAGIC 0x4d353941 /* "G95M" */
+
+#define HEADER_SIZE offsetof (malloc_t, data)
+#define DATA_POINTER(pheader) (&((pheader)->data))
+#define DATA_HEADER(pdata) ((malloc_t *)((char *) (pdata) - HEADER_SIZE))
+
+/* The root of the circular double linked list for compiler generated
+ malloc calls. */
+static malloc_t mem_root;
+
+
+void
+memory_init (void)
+{
+
+ /* The root should never be used directly, so don't set the magic. */
+ mem_root.magic = 0;
+ mem_root.next = &mem_root;
+ mem_root.prev = &mem_root;
+ mem_root.marker = 0;
+}
+
+
+/* Doesn't actually do any cleaning up, just throws an error if something
+ has got out of sync somewhere. */
+
+void
+runtime_cleanup (void)
+{
+ /* Make sure all memory we've allocated is freed on exit. */
+ if (mem_root.next != &mem_root)
+ runtime_error ("Unfreed memory on program termination");
+}
+
+
+
+void *
+get_mem (size_t n)
+{
+ void *p;
+
+#ifdef GFC_CLEAR_MEMORY
+ p = (void *) calloc (n, 1);
+#else
+#define temp malloc
+#undef malloc
+ p = (void *) malloc (n);
+#define malloc temp
+#undef temp
+#endif
+ if (p == NULL)
+ os_error ("Memory allocation failed");
+
+ return p;
+}
+
+
+void
+free_mem (void *p)
+{
+
+ free (p);
+}
+
+
+/* Allocates a block of memory with a size of N bytes. N does not
+ include the size of the header. */
+
+static malloc_t *
+malloc_with_header (size_t n)
+{
+ malloc_t *newmem;
+
+ n = n + HEADER_SIZE;
+
+ newmem = (malloc_t *) get_mem (n);
+
+ if (newmem)
+ {
+ newmem->magic = GFC_MALLOC_MAGIC;
+ newmem->marker = 0;
+ }
+
+ return newmem;
+}
+
+
+/* Allocate memory for internal (compiler generated) use. */
+
+void *
+internal_malloc_size (size_t size)
+{
+ malloc_t *newmem;
+
+ newmem = malloc_with_header (size);
+
+ if (!newmem)
+ os_error ("Out of memory.");
+
+ /* Add to end of list. */
+ newmem->next = &mem_root;
+ newmem->prev = mem_root.prev;
+ mem_root.prev->next = newmem;
+ mem_root.prev = newmem;
+
+ return DATA_POINTER (newmem);
+}
+
+
+void *
+internal_malloc (GFC_INTEGER_4 size)
+{
+#ifdef GFC_CHECK_MEMORY
+ /* Under normal circumstances, this is _never_ going to happen! */
+ if (size <= 0)
+ runtime_error ("Attempt to allocate a non-positive amount of memory.");
+
+#endif
+ return internal_malloc_size ((size_t) size);
+}
+
+
+void *
+internal_malloc64 (GFC_INTEGER_8 size)
+{
+#ifdef GFC_CHECK_MEMORY
+ /* Under normal circumstances, this is _never_ going to happen! */
+ if (size <= 0)
+ runtime_error ("Attempt to allocate a non-positive amount of memory.");
+#endif
+ return internal_malloc_size ((size_t) size);
+}
+
+
+/* Free internally allocated memory. Pointer is NULLified. Also used to
+ free user allocated memory. */
+/* TODO: keep a list of previously allocated blocks and reuse them. */
+
+void
+internal_free (void *mem)
+{
+ malloc_t *m;
+
+ if (!mem)
+ runtime_error ("Internal: Possible double free of temporary.");
+
+ m = DATA_HEADER (mem);
+
+ if (m->magic != GFC_MALLOC_MAGIC)
+ runtime_error ("Internal: No magic memblock marker. "
+ "Possible memory corruption");
+
+ /* Move markers up the chain, so they don't get lost. */
+ m->prev->marker += m->marker;
+ /* Remove from list. */
+ m->prev->next = m->next;
+ m->next->prev = m->prev;
+
+ free (m);
+}
+
+
+/* User-allocate, one call for each member of the alloc-list of an
+ ALLOCATE statement. */
+
+static void
+allocate_size (void **mem, size_t size, GFC_INTEGER_4 * stat)
+{
+ malloc_t *newmem;
+
+ if (!mem)
+ runtime_error ("Internal: NULL mem pointer in ALLOCATE.");
+
+ newmem = malloc_with_header (size);
+ if (!newmem)
+ {
+ if (stat)
+ {
+ *stat = 1;
+ return;
+ }
+ else
+ runtime_error ("ALLOCATE: Out of memory.");
+ }
+
+ /* We don't keep a list of these at the moment, so just link to itself. */
+ newmem->next = newmem;
+ newmem->prev = newmem;
+
+ (*mem) = DATA_POINTER (newmem);
+
+ if (stat)
+ *stat = 0;
+}
+
+
+void
+allocate (void **mem, GFC_INTEGER_4 size, GFC_INTEGER_4 * stat)
+{
+
+ if (size < 0)
+ {
+ runtime_error ("Attempt to allocate negative amount of memory. "
+ "Possible integer overflow");
+ abort ();
+ }
+
+ allocate_size (mem, (size_t) size, stat);
+}
+
+
+void
+allocate64 (void **mem, GFC_INTEGER_8 size, GFC_INTEGER_4 * stat)
+{
+
+ if (size < 0)
+ {
+ runtime_error
+ ("ALLOCATE64: Attempt to allocate negative amount of memory. "
+ "Possible integer overflow");
+ abort ();
+ }
+
+ allocate_size (mem, (size_t) size, stat);
+}
+
+
+/* User-deallocate; pointer is NULLified. */
+
+void
+deallocate (void **mem, GFC_INTEGER_4 * stat)
+{
+
+ if (!mem)
+ runtime_error ("Internal: NULL mem pointer in ALLOCATE.");
+
+ if (!*mem)
+ {
+ if (stat)
+ {
+ *stat = 1;
+ return;
+ }
+ else
+ {
+ runtime_error
+ ("Internal: Attempt to DEALLOCATE unallocated memory.");
+ abort ();
+ }
+ }
+
+ /* Just use the internal routine. */
+ internal_free (*mem);
+ *mem = NULL;
+
+ if (stat)
+ *stat = 0;
+}
+