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)
|