a68g-mem.c

     
   1  //! @file a68g-mem.c
   2  //! @author J. Marcel van der Veer
   3  
   4  //! @section Copyright
   5  //!
   6  //! This file is part of Algol68G - an Algol 68 compiler-interpreter.
   7  //! Copyright 2001-2024 J. Marcel van der Veer [algol68g@xs4all.nl].
   8  
   9  //! @section License
  10  //!
  11  //! This program is free software; you can redistribute it and/or modify it 
  12  //! under the terms of the GNU General Public License as published by the 
  13  //! Free Software Foundation; either version 3 of the License, or 
  14  //! (at your option) any later version.
  15  //!
  16  //! This program is distributed in the hope that it will be useful, but 
  17  //! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  18  //! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
  19  //! more details. You should have received a copy of the GNU General Public 
  20  //! License along with this program. If not, see [http://www.gnu.org/licenses/].
  21  
  22  //! @section Synopsis
  23  //!
  24  //! Low-level memory management.
  25  
  26  #include "a68g.h"
  27  #include "a68g-prelude.h"
  28  
  29  //! @brief Initialise C and A68 heap management.
  30  
  31  void init_heap (void)
  32  {
  33    unt heap_a_size = A68_ALIGN (A68 (heap_size));
  34    unt handle_a_size = A68_ALIGN (A68 (handle_pool_size));
  35    unt frame_a_size = A68_ALIGN (A68 (frame_stack_size));
  36    unt expr_a_size = A68_ALIGN (A68 (expr_stack_size));
  37    REAL_T /* sic */ total_size = A68_ALIGN (heap_a_size + handle_a_size + frame_a_size + 2 * expr_a_size);
  38    ABEND (OVER_2G (total_size), ERROR_OVER_2G, __func__);
  39    errno = 0;
  40    BYTE_T *core = (BYTE_T *) (A68_ALIGN_T *) a68_alloc ((size_t) total_size, __func__, __LINE__);
  41    ABEND (core == NO_BYTE, ERROR_OUT_OF_CORE, __func__);
  42    A68_HEAP = NO_BYTE;
  43    A68_HANDLES = NO_BYTE;
  44    A68_STACK = NO_BYTE;
  45    A68_SP = 0;
  46    A68_FP = 0;
  47    A68_HP = 0;
  48    A68_GLOBALS = 0;
  49    A68_HEAP = &(core[0]);
  50    A68_HANDLES = &(A68_HEAP[heap_a_size]);
  51    A68_STACK = &(A68_HANDLES[handle_a_size]);
  52    A68 (fixed_heap_pointer) = A68_ALIGNMENT;
  53    A68 (temp_heap_pointer) = total_size;
  54    A68 (frame_start) = 0;
  55    A68 (frame_end) = A68 (stack_start) = A68 (frame_start) + frame_a_size;
  56    A68 (stack_end) = A68 (stack_start) + expr_a_size;
  57    ABEND (errno != 0, ERROR_ALLOCATION, __func__);
  58  }
  59  
  60  //! @brief aligned allocation.
  61  
  62  void *a68_alloc (size_t len, const char *f, int line)
  63  {
  64  // We need this since malloc aligns to "standard C types".
  65  // __float128 is not a standard type, apparently ...
  66  // Huge chunks cause trouble!
  67    ABEND (len >= 2 * GIGABYTE, ERROR_OUT_OF_CORE, __func__);   
  68    if (len > 0) {
  69      void *p = NULL;
  70      int save = errno;
  71      size_t align = sizeof (A68_ALIGN_T);
  72      errno = 0;
  73  #if defined (BUILD_WIN32)
  74      p = _aligned_malloc (len, align);
  75  #elif defined (HAVE_POSIX_MEMALIGN)
  76      errno = posix_memalign (&p, align, len);
  77      if (errno != 0) {
  78        p = NULL;
  79      }
  80  #elif defined (HAVE_ALIGNED_ALLOC)
  81  // Glibc version of posix_memalign.
  82      if (align < sizeof (void *)) {
  83        errno = EINVAL;
  84      } else {
  85        p = aligned_alloc (align, len);
  86      }
  87  #else
  88  // Aude audenda.
  89      p = malloc (len);
  90  #endif
  91      if (p == (void *) NULL || errno != 0) {
  92        static BUFFER msg;
  93        a68_bufprt (msg, SNPRINTF_SIZE, "cannot allocate %lu bytes; called from function %s, line %d", (long unt) len, f, line);
  94        ABEND (A68_TRUE, ERROR_ALLOCATION, msg);
  95      }
  96      errno = save;
  97      return p;
  98    } else {
  99      return (void *) NULL;
 100    }
 101  }
 102  
 103  void a68_free (void *z)
 104  {
 105    if (z != NULL) {
 106  #if defined (BUILD_WIN32)
 107  // On WIN32, free cannot deallocate _aligned_malloc
 108      _aligned_free (z);
 109  #else
 110      free (z);
 111  #endif
 112    }
 113  }
 114  
 115  //! @brief Give pointer to block of "s" bytes.
 116  
 117  BYTE_T *get_heap_space (size_t s)
 118  {
 119    ABEND (s == 0, ERROR_INVALID_SIZE, __func__);
 120    BYTE_T *z = (BYTE_T *) (A68_ALIGN_T *) a68_alloc (A68_ALIGN (s), __func__, __LINE__);
 121    ABEND (z == NO_BYTE, ERROR_OUT_OF_CORE, __func__);
 122    return z;
 123  }
 124  
 125  //! @brief Make a new copy of concatenated strings.
 126  
 127  char *new_string (char *t, ...)
 128  {
 129    va_list vl;
 130    va_start (vl, t);
 131    char *q = t;
 132    if (q == NO_TEXT) {
 133      va_end (vl);
 134      return NO_TEXT;
 135    }
 136    int len = 0;
 137    while (q != NO_TEXT) {
 138      len += (int) strlen (q);
 139      q = va_arg (vl, char *);
 140    }
 141    va_end (vl);
 142    len++;
 143    char *z = (char *) get_heap_space ((size_t) len);
 144    z[0] = NULL_CHAR;
 145    q = t;
 146    va_start (vl, t);
 147    while (q != NO_TEXT) {
 148      a68_bufcat (z, q, len);
 149      q = va_arg (vl, char *);
 150    }
 151    va_end (vl);
 152    return z;
 153  }
 154  
 155  //! @brief Make a new copy of "t".
 156  
 157  char *new_fixed_string (char *t)
 158  {
 159    int n = (int) (strlen (t) + 1);
 160    char *z = (char *) get_fixed_heap_space ((size_t) n);
 161    a68_bufcpy (z, t, n);
 162    return z;
 163  }
 164  
 165  //! @brief Make a new copy of "t".
 166  
 167  char *new_temp_string (char *t)
 168  {
 169    int n = (int) (strlen (t) + 1);
 170    char *z = (char *) get_temp_heap_space ((size_t) n);
 171    a68_bufcpy (z, t, n);
 172    return z;
 173  }
 174  
 175  //! @brief Get (preferably fixed) heap space.
 176  
 177  BYTE_T *get_fixed_heap_space (size_t s)
 178  {
 179    if (A68 (heap_is_fluid)) {
 180      BYTE_T *z = HEAP_ADDRESS (A68 (fixed_heap_pointer));
 181      A68 (fixed_heap_pointer) += A68_ALIGN ((int) s);
 182  // Allow for extra storage for diagnostics etcetera 
 183      ABEND (A68 (fixed_heap_pointer) >= (A68 (heap_size) - MIN_MEM_SIZE), ERROR_OUT_OF_CORE, __func__);
 184      ABEND (((unt) A68 (temp_heap_pointer) - (unt) A68 (fixed_heap_pointer)) <= MIN_MEM_SIZE, ERROR_OUT_OF_CORE, __func__);
 185      return z;
 186    } else {
 187      return get_heap_space (s);
 188    }
 189  }
 190  
 191  //! @brief Get (preferably temporary) heap space.
 192  
 193  BYTE_T *get_temp_heap_space (size_t s)
 194  {
 195    if (A68 (heap_is_fluid)) {
 196      A68 (temp_heap_pointer) -= A68_ALIGN ((int) s);
 197  // Allow for extra storage for diagnostics etcetera.
 198      ABEND (((unt) A68 (temp_heap_pointer) - (unt) A68 (fixed_heap_pointer)) <= MIN_MEM_SIZE, ERROR_OUT_OF_CORE, __func__);
 199      return HEAP_ADDRESS (A68 (temp_heap_pointer));
 200    } else {
 201      return get_heap_space (s);
 202    }
 203  }
 204  
 205  //! @brief Get size of stack segment.
 206  
 207  void get_stack_size (void)
 208  {
 209  #if defined (BUILD_WIN32)
 210    A68 (stack_size) = MEGABYTE;  // Guestimate
 211  #else
 212    errno = 0;
 213  // Some systems do not implement RLIMIT_STACK so if getrlimit fails, we do not abend.
 214    struct rlimit limits;
 215    if (!(getrlimit (RLIMIT_STACK, &limits) == 0 && errno == 0)) {
 216      A68 (stack_size) = MEGABYTE;
 217    }
 218    A68 (stack_size) = (unt) (RLIM_CUR (&limits) < RLIM_MAX (&limits) ? RLIM_CUR (&limits) : RLIM_MAX (&limits));
 219  // A heuristic in case getrlimit yields extreme numbers: the frame stack is
 220  // assumed to fill at a rate comparable to the C stack, so the C stack needs
 221  // not be larger than the frame stack. This may not be true.
 222    if (A68 (stack_size) < KILOBYTE || (A68 (stack_size) > 96 * MEGABYTE && A68 (stack_size) > A68 (frame_stack_size))) {
 223      A68 (stack_size) = A68 (frame_stack_size);
 224    }
 225  #endif
 226    A68 (stack_limit) = (A68 (stack_size) > (4 * A68 (storage_overhead)) ? (A68 (stack_size) - A68 (storage_overhead)) : A68 (stack_size) / 2);
 227  }
 228  
 229  //! @brief Free heap allocated by genie.
 230  
 231  void genie_free (NODE_T * p)
 232  {
 233    for (; p != NO_NODE; FORWARD (p)) {
 234      genie_free (SUB (p));
 235      if (GINFO (p) != NO_GINFO) {
 236        a68_free (CONSTANT (GINFO (p)));
 237        CONSTANT (GINFO (p)) = NO_CONSTANT;
 238        a68_free (COMPILE_NAME (GINFO (p)));
 239        COMPILE_NAME (GINFO (p)) = NO_TEXT;
 240      }
 241    }
 242  }
 243  
 244  //! @brief Free heap allocated by genie.
 245  
 246  void free_syntax_tree (NODE_T * p)
 247  {
 248    for (; p != NO_NODE; FORWARD (p)) {
 249      free_syntax_tree (SUB (p));
 250      a68_free (NPRAGMENT (p));
 251      NPRAGMENT (p) = NO_TEXT;
 252      DIAGNOSTIC_T *d = DIAGNOSTICS (LINE (INFO (p)));
 253      while (d != NO_DIAGNOSTIC) {
 254        a68_free (TEXT (d));
 255        DIAGNOSTIC_T *stale = d;
 256        FORWARD (d);
 257        a68_free (stale);
 258      }
 259      DIAGNOSTICS (LINE (INFO (p))) = NO_DIAGNOSTIC;
 260    }
 261  }