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-2025 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  // A68G memory is organised following below schema.
  27  //
  28  // +------------+----------+-----------+---------+-------------+----------------+
  29  // | HEAP                                        | STACK                        |
  30  // | FIXED HEAP | A68 HEAP | TEMP HEAP | HANDLES | FRAME STACK | ARGUMENT STACK |
  31  // +------------+----------+-----------+---------+-------------+----------------+
  32  //                ^                                ^             ^
  33  //                A68G_HP                          A68G_FP       A68G_SP
  34  //
  35  // The HEAP starts at the lowest address, and the ARGUMENT STACK ends at the highest address. 
  36  //
  37  // The HEAP is for both C and A68 objects. For C objects, this simple scheme is more efficient 
  38  // than many calls to 'malloc' and 'free'.
  39  //
  40  // FIXED HEAP contains permanent C objects forming the syntax tree, symbol tables, etcetera.
  41  // A68 HEAP contains data stored by HEAP generators in the running A68 program.
  42  // TEMP HEAP stores temporary run-time data in the heap.
  43  // HANDLES store A68 REFs enabling garbage collecting of the A68 HEAP.
  44  // FRAME STACK stores local variables according the lexical levels.
  45  // ARGUMENT STACK is for PROC/OP arguments and results, the "evaluation stack".
  46  //
  47  // The 'heap pointer' counts up and the 'temp heap pointer' counts down.
  48  // Should they collide, then the heap is full.
  49  // This way there is no need to reserve space for the heap segments a priori.
  50  // The stack could follow the same mechanism to have a joined space for 
  51  // the FRAME STACK and ARGUMENT STACK, but is not implemented like that.
  52  //
  53  // Care must be taken to not sweep intermediate HEAP data pointed to from the STACK.
  54  // Therefore the garbage collector only acts in between units.
  55  //
  56  // Note that A68G stores all row elements in the heap, to save space in the stack. 
  57  // If a row is local, only the descriptor is in the FRAME STACK.
  58  // The garbage collector will sweep the elements of a stale local row.
  59  
  60  #include "a68g.h"
  61  #include "a68g-prelude.h"
  62  
  63  //! Reasonable limits for chunk sizes.
  64  
  65  void storage_limit (size_t n) 
  66  {
  67    ABEND ((n) > MAX_MEM_SIZE, ERROR_VALUE_TOO_LARGE, ERROR_MEMORY_FULL);
  68  }
  69  
  70  //! @brief Initialise C and A68 heap management.
  71  
  72  void init_heap (void) 
  73  {
  74  // Note that some overhead is allowed for every size that can be specified.
  75    size_t heap_a_size = A68G_ALIGN (A68G (heap_size) + A68G (storage_overhead));
  76    size_t handle_a_size = A68G_ALIGN (A68G (handle_pool_size) + A68G (storage_overhead));
  77    size_t frame_a_size = A68G_ALIGN (A68G (frame_stack_size) + A68G (storage_overhead));
  78    size_t expr_a_size = A68G_ALIGN (A68G (expr_stack_size) + A68G (storage_overhead));
  79    storage_limit (heap_a_size);
  80    storage_limit (handle_a_size);
  81    storage_limit (frame_a_size);
  82    storage_limit (expr_a_size);
  83    size_t total_size = A68G_ALIGN (heap_a_size + handle_a_size + frame_a_size + expr_a_size);
  84    storage_limit (total_size);
  85  // Reserve the space.
  86    errno = 0;
  87    BYTE_T *core = (BYTE_T *) (A68G_ALIGN_T *) a68g_alloc (total_size, __func__, __LINE__);
  88    if (core == NO_BYTE || errno != 0) {
  89      ABEND (A68G_TRUE, ERROR_MEMORY_FULL, NO_TEXT);
  90    }
  91  // Initialise.
  92    A68G_HEAP = NO_BYTE;
  93    A68G_HANDLES = NO_BYTE;
  94    A68G_STACK = NO_BYTE;
  95    A68G_SP = 0;
  96    A68G_FP = 0;
  97    A68G_HP = 0;
  98    A68G_GLOBALS = 0;
  99    A68G_HEAP = & (core[0]);
 100    A68G_HANDLES = & (A68G_HEAP[heap_a_size]);
 101    A68G_STACK = & (A68G_HANDLES[handle_a_size]);
 102    A68G (fixed_heap_pointer) = A68G_ALIGNMENT;
 103    A68G (temp_heap_pointer) = heap_a_size;
 104    A68G (frame_start) = 0;
 105    A68G (frame_end) = A68G (frame_start) + frame_a_size;
 106    A68G (stack_start) = A68G (frame_end);
 107    A68G (stack_end) = A68G (stack_start) + expr_a_size;
 108    ABEND (errno != 0, ERROR_MEMORY_FULL, NO_TEXT);
 109  }
 110  
 111  //! @brief aligned allocation.
 112  
 113  void *a68g_alloc (size_t len, const char *f, int line) 
 114  {
 115  // We need this since malloc aligns to "standard C types".
 116  // __float128 is not a standard type, apparently ...
 117  //
 118    (void) f;
 119    (void) line;
 120    storage_limit (len);
 121    void *p = NULL;
 122    size_t align = sizeof (A68G_ALIGN_T);
 123    int save = errno;
 124    errno = 0;
 125  // Allocation is platform dependent.
 126    #if defined (BUILD_WINDOWS) 
 127      p = _aligned_malloc (len, align);
 128      ABEND (p == NULL || errno != 0, "cannot allocate memory", NO_TEXT);
 129    #elif defined (HAVE_POSIX_MEMALIGN) 
 130      errno = posix_memalign (&p, align, len);
 131      if (errno != 0) {
 132        p = NULL;
 133      }
 134    #elif defined (HAVE_ALIGNED_ALLOC) // Glibc version of posix_memalign.
 135      if (align < sizeof (void *)) {
 136        errno = EINVAL;
 137      } else {
 138        p = aligned_alloc (align, len);
 139      }
 140    #else
 141      p = malloc (len); // Aude audenda.
 142    #endif
 143  //
 144    if (p == (void *) NULL || errno != 0) {
 145      static BUFFER msg;
 146      if (len > A68G_GIGA) {
 147        a68g_bufprt (msg, SNPRINTF_SIZE, "request for " A68G_LU " GB", len / A68G_GIGA);
 148      } else if (len > A68G_MEGA) {
 149        a68g_bufprt (msg, SNPRINTF_SIZE, "request for " A68G_LU " MB", len / A68G_MEGA);
 150      } else {
 151        a68g_bufprt (msg, SNPRINTF_SIZE, "request for " A68G_LU " bytes", len);
 152      }
 153      ABEND (A68G_TRUE, ERROR_MEMORY_FULL, msg);
 154    }
 155    errno = save;
 156    return p;
 157  }
 158  
 159  void a68g_free (void *z) 
 160  {
 161    if (z != NULL) {
 162    #if defined (BUILD_WINDOWS) 
 163      _aligned_free (z); // On WINDOWS, free cannot deallocate _aligned_malloc
 164    #else
 165      free (z);
 166    #endif
 167    }
 168  }
 169  
 170  //! @brief Give pointer to block of "s" bytes.
 171  
 172  BYTE_T *get_heap_space (size_t s) 
 173  {
 174    ABEND (s == 0, ERROR_INVALID_SIZE, NO_TEXT);
 175    BYTE_T *z = (BYTE_T *) (A68G_ALIGN_T *) a68g_alloc (A68G_ALIGN (s), __func__, __LINE__);
 176    ABEND (z == NO_BYTE, ERROR_MEMORY_FULL, NO_TEXT);
 177    return z;
 178  }
 179  
 180  //! @brief Make a new copy of concatenated strings.
 181  
 182  char *new_string (char *t, ...) 
 183  {
 184    va_list vl;
 185    va_start (vl, t);
 186    char *q = t;
 187    if (q == NO_TEXT) {
 188      va_end (vl);
 189      return NO_TEXT;
 190    }
 191    size_t len = 0;
 192    while (q != NO_TEXT) {
 193      len += strlen (q);
 194      q = va_arg (vl, char *);
 195    }
 196    va_end (vl);
 197    len++;
 198    char *z = (char *) get_heap_space (len);
 199    z[0] = NULL_CHAR;
 200    q = t;
 201    va_start (vl, t);
 202    while (q != NO_TEXT) {
 203      a68g_bufcat (z, q, len);
 204      q = va_arg (vl, char *);
 205    }
 206    va_end (vl);
 207    return z;
 208  }
 209  
 210  //! @brief Make a new copy of "t".
 211  
 212  char *new_fixed_string (char *t) 
 213  {
 214    size_t n = strlen (t) + 1;
 215    char *z = (char *) get_fixed_heap_space (n);
 216    a68g_bufcpy (z, t, n);
 217    return z;
 218  }
 219  
 220  //! @brief Make a new copy of "t".
 221  
 222  char *new_temp_string (char *t) 
 223  {
 224    size_t n = strlen (t) + 1;
 225    char *z = (char *) get_temp_heap_space (n);
 226    a68g_bufcpy (z, t, n);
 227    return z;
 228  }
 229  
 230  //! @brief Get (preferably fixed) heap space.
 231  
 232  BYTE_T *get_fixed_heap_space (size_t s) 
 233  {
 234    if (A68G (heap_is_fluid)) {
 235      BYTE_T *z = HEAP_ADDRESS (A68G (fixed_heap_pointer));
 236      A68G (fixed_heap_pointer) += A68G_ALIGN (s);
 237  // Allow for extra storage for diagnostics etcetera.
 238      ABEND (((A68G (fixed_heap_pointer) + A68G (storage_overhead)) >= A68G (temp_heap_pointer)), ERROR_MEMORY_FULL, NO_TEXT);
 239      return z;
 240    } else {
 241      return get_heap_space (s);
 242    }
 243  }
 244  
 245  //! @brief Get (preferably temporary) heap space.
 246  
 247  BYTE_T *get_temp_heap_space (size_t s) 
 248  {
 249    if (A68G (heap_is_fluid)) {
 250      A68G (temp_heap_pointer) -= A68G_ALIGN (s);
 251  // Allow for extra storage for diagnostics etcetera.
 252      ABEND (((A68G (fixed_heap_pointer) + A68G (storage_overhead)) >= A68G (temp_heap_pointer)), ERROR_MEMORY_FULL, NO_TEXT);
 253      return HEAP_ADDRESS (A68G (temp_heap_pointer));
 254    } else {
 255      return get_heap_space (s);
 256    }
 257  }
 258  
 259  //! @brief Get size of C stack.
 260  
 261  void get_C_stack_size (void) 
 262  {
 263    #if defined (BUILD_WINDOWS) 
 264      A68G (stack_size) = A68G_MEGA; // Guestimate
 265    #else
 266      errno = 0;
 267  // Some systems do not implement RLIMIT_STACK so if getrlimit fails, we do not abend.
 268      struct rlimit limits;
 269      if (! (getrlimit (RLIMIT_STACK, &limits) == 0 && errno == 0)) {
 270        A68G (stack_size) = A68G_MEGA;
 271      }
 272      A68G (stack_size) = (size_t) (RLIM_CUR (&limits) < RLIM_MAX (&limits) ? RLIM_CUR (&limits) : RLIM_MAX (&limits));
 273  // A heuristic in case getrlimit yields extreme numbers: the frame stack is
 274  // assumed to fill at a rate comparable to the C stack, so the C stack needs
 275  // not be larger than the frame stack. This may not be true.
 276      if (A68G (stack_size) < A68G_KILO || (A68G (stack_size) > 96 * A68G_MEGA && A68G (stack_size) > A68G (frame_stack_size))) {
 277        A68G (stack_size) = A68G (frame_stack_size);
 278      }
 279    #endif
 280    A68G (stack_limit) = (A68G (stack_size) > (4 * A68G (storage_overhead)) ? (A68G (stack_size) - A68G (storage_overhead)) : A68G (stack_size) / 2);
 281  }
 282  
 283  //! @brief Free heap allocated by genie.
 284  
 285  void genie_free (NODE_T *p) 
 286  {
 287    for (; p != NO_NODE; FORWARD (p)) {
 288      genie_free (SUB (p));
 289      if (GINFO (p) != NO_GINFO) {
 290        a68g_free (CONSTANT (GINFO (p)));
 291        CONSTANT (GINFO (p)) = NO_CONSTANT;
 292        a68g_free (COMPILE_NAME (GINFO (p)));
 293        COMPILE_NAME (GINFO (p)) = NO_TEXT;
 294      }
 295    }
 296  }
 297  
 298  //! @brief Free heap allocated by genie.
 299  
 300  void free_syntax_tree (NODE_T *p) 
 301  {
 302    for (; p != NO_NODE; FORWARD (p)) {
 303      free_syntax_tree (SUB (p));
 304      a68g_free (NPRAGMENT (p));
 305      NPRAGMENT (p) = NO_TEXT;
 306      DIAGNOSTIC_T *d = DIAGNOSTICS (LINE (INFO (p)));
 307      while (d != NO_DIAGNOSTIC) {
 308        a68g_free (TEXT (d));
 309        DIAGNOSTIC_T *stale = d;
 310        FORWARD (d);
 311        a68g_free (stale);
 312      }
 313      DIAGNOSTICS (LINE (INFO (p))) = NO_DIAGNOSTIC;
 314    }
 315  }
     


© 2002-2025 J.M. van der Veer (jmvdveer@xs4all.nl)