Post Scarcity 0.0.6
A prototype for a post scarcity programming environment
Loading...
Searching...
No Matches
stack.c
Go to the documentation of this file.
1/*
2 * stack.c
3 *
4 * The Lisp evaluation stack.
5 *
6 * Stack frames could be implemented in cons space; indeed, the stack
7 * could simply be an assoc list consed onto the front of the environment.
8 * But such a stack would be costly to search. The design sketched here,
9 * with stack frames as special objects, SHOULD be substantially more
10 * efficient, but does imply we need to generalise the idea of cons pages
11 * with freelists to a more general 'equal sized object pages', so that
12 * allocating/freeing stack frames can be more efficient.
13 *
14 * (c) 2017 Simon Brooke <simon@journeyman.cc>
15 * Licensed under GPL version 2.0, or, at your option, any later version.
16 */
17
18#include <stdlib.h>
19
20#include "debug.h"
21#include "io/print.h"
22#include "memory/conspage.h"
24#include "memory/dump.h"
25#include "memory/stack.h"
26#include "memory/vectorspace.h"
27#include "ops/lispops.h"
28
29/**
30 * @brief If non-zero, maximum depth of stack.
31 *
32 */
33uint32_t stack_limit = 0;
34
35/**
36 * set a register in a stack frame. Alwaye use this to do so,
37 * because that way we can be sure the inc_ref happens!
38 */
39void set_reg( struct stack_frame *frame, int reg, struct cons_pointer value ) {
40 debug_printf( DEBUG_STACK, L"\tSetting register %d to ", reg );
43 dec_ref( frame->arg[reg] ); /* if there was anything in that slot
44 * previously other than NIL, we need to decrement it;
45 * NIL won't be decremented as it is locked. */
46 frame->arg[reg] = value;
47 inc_ref( value );
48
49 if ( reg == frame->args ) {
50 frame->args++;
51 }
52}
53
54
55/**
56 * get the actual stackframe object from this `pointer`, or NULL if
57 * `pointer` is not a stackframe pointer.
58 */
59struct stack_frame *get_stack_frame( struct cons_pointer pointer ) {
60 struct stack_frame *result = NULL;
61 struct vector_space_object *vso =
62 pointer2cell( pointer ).payload.vectorp.address;
63
64 if ( vectorpointp( pointer ) && stackframep( vso ) ) {
65 result = ( struct stack_frame * ) &( vso->payload );
66 // debug_printf( DEBUG_STACK,
67 // L"\nget_stack_frame: all good, returning %p\n", result );
68 } else {
69 debug_print( L"\nget_stack_frame: fail, returning NULL\n",
71 }
72
73 return result;
74}
75
76/**
77 * Make an empty stack frame, and return it.
78 *
79 * This function does the actual meat of making the frame.
80 *
81 * @param previous the current top-of-stack;
82 * @param depth the depth of the new frame.
83 * @return the new frame, or NULL if memory is exhausted.
84 */
86 uint32_t depth ) {
87 debug_print( L"Entering make_empty_frame\n", DEBUG_ALLOC );
88 struct cons_pointer result =
89 make_vso( STACKFRAMETV, sizeof( struct stack_frame ) );
90
91 if ( !nilp( result ) ) {
92 struct stack_frame *frame = get_stack_frame( result );
93 /*
94 * \todo later, pop a frame off a free-list of stack frames
95 */
96
97 frame->previous = previous;
98 frame->depth = depth;
99
100 /*
101 * The frame has already been cleared with memset in make_vso, but our
102 * NIL is not the same as C's NULL.
103 */
104 frame->more = NIL;
105 frame->function = NIL;
106 frame->args = 0;
107
108 for ( int i = 0; i < args_in_frame; i++ ) {
109 frame->arg[i] = NIL;
110 }
111
113 }
114 debug_print( L"Leaving make_empty_frame\n", DEBUG_ALLOC );
116
117 return result;
118}
119
120/**
121 * @brief Make an empty stack frame, and return it.
122 *
123 * This function does the error checking around actual construction.
124 *
125 * @param previous the current top-of-stack;
126 * @param env the environment in which evaluation happens.
127 * @return the new frame, or NULL if memory is exhausted.
128 */
129struct cons_pointer make_empty_frame( struct cons_pointer previous ) {
130 struct cons_pointer result = NIL;
131 uint32_t depth =
132 ( nilp( previous ) ) ? 0 : ( get_stack_frame( previous ) )->depth + 1;
133
134 if ( stack_limit == 0 || stack_limit > depth ) {
135 result = in_make_empty_frame( previous, depth );
136 } else {
138 L"WARNING: Exceeded stack limit of %d\n", stack_limit );
139 result =
141 ( L"Stack limit exceeded." ), previous );
142 }
143
144 if ( nilp( result ) ) {
145 /* i.e. out of memory */
146 result =
148 }
149
150 return result;
151}
152
153/**
154 * Allocate a new stack frame with its previous pointer set to this value,
155 * its arguments set up from these args, evaluated in this env.
156 * @param previous the current top-of-stack;
157 * @args the arguments to load into this frame;
158 * @param env the environment in which evaluation happens.
159 * @return the new frame, or an exception if one occurred while building it.
160 */
162 struct cons_pointer args,
163 struct cons_pointer env ) {
164 debug_print( L"Entering make_stack_frame\n", DEBUG_STACK );
165 struct cons_pointer result = make_empty_frame( previous );
166
167 if ( !exceptionp( result ) ) {
168 struct stack_frame *frame = get_stack_frame( result );
169
170 while ( frame->args < args_in_frame && consp( args ) ) {
171 /* iterate down the arg list filling in the arg slots in the
172 * frame. When there are no more slots, if there are still args,
173 * stash them on more */
174 struct cons_space_object cell = pointer2cell( args );
175
176 /*
177 * \todo if we were running on real massively parallel hardware,
178 * each arg except the first should be handed off to another
179 * processor to be evaled in parallel; but see notes here:
180 * https://github.com/simon-brooke/post-scarcity/wiki/parallelism
181 */
182 struct cons_pointer val =
183 eval_form( frame, result, cell.payload.cons.car, env );
184 if ( exceptionp( val ) ) {
185 result = val;
186 break;
187 } else {
188 debug_printf( DEBUG_STACK, L"\tSetting argument %d to ",
189 frame->args );
190 debug_print_object( cell.payload.cons.car, DEBUG_STACK );
191 debug_print( L"\n", DEBUG_STACK );
192 set_reg( frame, frame->args, val );
193 }
194
195 args = cell.payload.cons.cdr;
196 }
197
198 if ( !exceptionp( result ) ) {
199 if ( consp( args ) ) {
200 /* if we still have args, eval them and stick the values on `more` */
201 struct cons_pointer more =
202 eval_forms( get_stack_frame( previous ), previous, args,
203 env );
204 frame->more = more;
205 inc_ref( more );
206
207 for ( ; !nilp( args ); args = c_cdr( args ) ) {
208 frame->args++;
209 }
210 }
211 }
212 debug_print( L"make_stack_frame: returning\n", DEBUG_STACK );
214 }
215
216 return result;
217}
218
219/**
220 * A 'special' frame is exactly like a normal stack frame except that the
221 * arguments are unevaluated.
222 * @param previous the previous stack frame;
223 * @param args a list of the arguments to be stored in this stack frame;
224 * @param env the execution environment;
225 * @return a new special frame.
226 */
228 struct cons_pointer args,
229 struct cons_pointer env ) {
230 debug_print( L"Entering make_special_frame\n", DEBUG_STACK );
231
232 struct cons_pointer result = make_empty_frame( previous );
233
234 if ( !exceptionp( result ) ) {
235 struct stack_frame *frame = get_stack_frame( result );
236
237 while ( frame->args < args_in_frame && !nilp( args ) ) {
238 /* iterate down the arg list filling in the arg slots in the
239 * frame. When there are no more slots, if there are still args,
240 * stash them on more */
241 struct cons_space_object cell = pointer2cell( args );
242
243 set_reg( frame, frame->args, cell.payload.cons.car );
244
245 args = cell.payload.cons.cdr;
246 }
247 if ( !exceptionp( result ) ) {
248 if ( consp( args ) ) {
249 frame->more = args;
250 inc_ref( args );
251 }
252 }
253 }
254 debug_print( L"make_special_frame: returning\n", DEBUG_STACK );
256
257 return result;
258}
259
260/**
261 * Free this stack frame.
262 */
263void free_stack_frame( struct stack_frame *frame ) {
264 /*
265 * \todo later, push it back on the stack-frame freelist
266 */
267 debug_print( L"Entering free_stack_frame\n", DEBUG_ALLOC );
268 for ( int i = 0; i < args_in_frame; i++ ) {
269 dec_ref( frame->arg[i] );
270 }
271 if ( !nilp( frame->more ) ) {
272 dec_ref( frame->more );
273 }
274 debug_print( L"Leaving free_stack_frame\n", DEBUG_ALLOC );
275}
276
277struct cons_pointer frame_get_previous( struct cons_pointer frame_pointer ) {
278 struct stack_frame *frame = get_stack_frame( frame_pointer );
279 struct cons_pointer result = NIL;
280
281 if ( frame != NULL ) {
282 result = frame->previous;
283 }
284
285 return result;
286}
287
289 struct cons_pointer frame_pointer ) {
290 struct stack_frame *frame = get_stack_frame( frame_pointer );
291
292 if ( frame != NULL ) {
293 url_fwprintf( output, L" <= " );
294 print( output, frame->arg[0] );
295 }
296}
297
298void dump_frame_context( URL_FILE *output, struct cons_pointer frame_pointer,
299 int depth ) {
300 struct stack_frame *frame = get_stack_frame( frame_pointer );
301
302 if ( frame != NULL ) {
303 url_fwprintf( output, L"\tContext: " );
304
305 int i = 0;
306 for ( struct cons_pointer cursor = frame_pointer;
307 i++ < depth && !nilp( cursor );
308 cursor = frame_get_previous( cursor ) ) {
309 dump_frame_context_fragment( output, cursor );
310 }
311
312 url_fwprintf( output, L"\n" );
313 }
314}
315
316/**
317 * Dump a stackframe to this stream for debugging
318 * @param output the stream
319 * @param frame_pointer the pointer to the frame
320 */
321void dump_frame( URL_FILE *output, struct cons_pointer frame_pointer ) {
322 struct stack_frame *frame = get_stack_frame( frame_pointer );
323
324 if ( frame != NULL ) {
325 url_fwprintf( output, L"Stack frame %d with %d arguments:\n",
326 frame->depth, frame->args );
327 dump_frame_context( output, frame_pointer, 4 );
328
329 for ( int arg = 0; arg < frame->args; arg++ ) {
330 struct cons_space_object cell = pointer2cell( frame->arg[arg] );
331
332 url_fwprintf( output, L"\tArg %d:\t%4.4s\tcount: %10u\tvalue: ",
333 arg, cell.tag.bytes, cell.count );
334
335 print( output, frame->arg[arg] );
336 url_fputws( L"\n", output );
337 }
338 if ( !nilp( frame->more ) ) {
339 url_fputws( L"More: \t", output );
340 print( output, frame->more );
341 url_fputws( L"\n", output );
342 }
343 }
344}
345
346void dump_stack_trace( URL_FILE *output, struct cons_pointer pointer ) {
347 if ( exceptionp( pointer ) ) {
348 print( output, pointer2cell( pointer ).payload.exception.payload );
349 url_fputws( L"\n", output );
350 dump_stack_trace( output,
351 pointer2cell( pointer ).payload.exception.frame );
352 } else {
353 while ( vectorpointp( pointer )
354 && stackframep( pointer_to_vso( pointer ) ) ) {
355 dump_frame( output, pointer );
356 pointer = get_stack_frame( pointer )->previous;
357 }
358 }
359}
360
361/**
362 * Fetch a pointer to the value of the local variable at this index.
363 */
364struct cons_pointer fetch_arg( struct stack_frame *frame, unsigned int index ) {
365 struct cons_pointer result = NIL;
366
367 if ( index < args_in_frame ) {
368 result = frame->arg[index];
369 } else {
370 struct cons_pointer p = frame->more;
371
372 for ( int i = args_in_frame; i < index; i++ ) {
373 p = pointer2cell( p ).payload.cons.cdr;
374 }
375
376 result = pointer2cell( p ).payload.cons.car;
377 }
378
379 return result;
380}
struct cons_pointer privileged_string_memory_exhausted
The exception message printed when the world blows up, initialised in maybe_bind_init_symbols() in in...
Definition conspage.c:52
struct cons_pointer inc_ref(struct cons_pointer pointer)
increment the reference count of the object at this cons pointer.
struct cons_pointer dec_ref(struct cons_pointer pointer)
Decrement the reference count of the object at this cons pointer.
#define exceptionp(conspoint)
true if conspoint points to an exception, else false
struct cons_pointer more
list of any further argument bindings.
int args
the number of arguments provided.
#define args_in_frame
union cons_space_object::@2 tag
union cons_space_object::@3 payload
#define NIL
a cons pointer which points to the special NIL cell
struct cons_pointer make_exception(struct cons_pointer message, struct cons_pointer frame_pointer)
Construct an exception cell.
struct cons_pointer c_cdr(struct cons_pointer arg)
Implementation of cdr in C.
#define consp(conspoint)
true if conspoint points to a cons cell, else false
#define nilp(conspoint)
true if conspoint points to the special cell NIL, else false (there should only be one of these so it...
struct cons_pointer previous
the previous frame.
uint32_t count
the count of the number of references to this cell
struct cons_pointer c_string_to_lisp_string(wchar_t *string)
Return a lisp string representation of this wide character string.
struct cons_pointer inc_ref(struct cons_pointer pointer)
increment the reference count of the object at this cons pointer.
struct cons_pointer function
the function to be called.
int depth
the depth of the stack below this frame
struct cons_pointer arg[args_in_frame]
first 8 arument bindings.
#define vectorpointp(conspoint)
true if conspoint points to a vector pointer, else false.
#define pointer2cell(pointer)
given a cons_pointer as argument, return the cell.
An indirect pointer to a cons cell.
an object in cons space.
A stack frame.
void debug_println(int level)
print a line feed to stderr, if verbosity matches level.
Definition debug.c:104
void debug_dump_object(struct cons_pointer pointer, int level)
Like dump_object, q.v., but protected by the verbosity mechanism.
Definition debug.c:155
void debug_printf(int level, wchar_t *format,...)
wprintf adapted for the debug logging system.
Definition debug.c:120
void debug_print(wchar_t *message, int level)
print this debug message to stderr, if verbosity matches level.
Definition debug.c:60
void debug_print_object(struct cons_pointer pointer, int level)
print the object indicated by this pointer to stderr, if verbosity matches level.
Definition debug.c:138
#define DEBUG_STACK
Print messages debugging stack operations.
Definition debug.h:80
#define DEBUG_ALLOC
Print messages debugging memory allocation.
Definition debug.h:24
#define url_fputws(ws, f)
Definition fopen.h:51
#define url_fwprintf(f,...)
Definition fopen.h:50
struct cons_pointer eval_form(struct stack_frame *parent, struct cons_pointer parent_pointer, struct cons_pointer form, struct cons_pointer env)
Useful building block; evaluate this single form in the context of this parent stack frame and this e...
Definition lispops.c:65
struct cons_pointer eval_forms(struct stack_frame *frame, struct cons_pointer frame_pointer, struct cons_pointer list, struct cons_pointer env)
Evaluate all the forms in this list in the context of this stack frame and this env,...
Definition lispops.c:133
struct cons_pointer print(URL_FILE *output, struct cons_pointer pointer)
Print the cons-space object indicated by pointer to the stream indicated by output.
Definition print.c:156
struct cons_pointer make_special_frame(struct cons_pointer previous, struct cons_pointer args, struct cons_pointer env)
A 'special' frame is exactly like a normal stack frame except that the arguments are unevaluated.
Definition stack.c:227
struct cons_pointer frame_get_previous(struct cons_pointer frame_pointer)
Definition stack.c:277
struct cons_pointer fetch_arg(struct stack_frame *frame, unsigned int index)
Fetch a pointer to the value of the local variable at this index.
Definition stack.c:364
void set_reg(struct stack_frame *frame, int reg, struct cons_pointer value)
set a register in a stack frame.
Definition stack.c:39
struct stack_frame * get_stack_frame(struct cons_pointer pointer)
get the actual stackframe object from this pointer, or NULL if pointer is not a stackframe pointer.
Definition stack.c:59
void dump_frame_context(URL_FILE *output, struct cons_pointer frame_pointer, int depth)
Definition stack.c:298
void dump_frame(URL_FILE *output, struct cons_pointer frame_pointer)
Dump a stackframe to this stream for debugging.
Definition stack.c:321
void dump_stack_trace(URL_FILE *output, struct cons_pointer pointer)
Definition stack.c:346
void free_stack_frame(struct stack_frame *frame)
Free this stack frame.
Definition stack.c:263
void dump_frame_context_fragment(URL_FILE *output, struct cons_pointer frame_pointer)
Definition stack.c:288
struct cons_pointer make_empty_frame(struct cons_pointer previous)
Make an empty stack frame, and return it.
Definition stack.c:129
uint32_t stack_limit
If non-zero, maximum depth of stack.
Definition stack.c:33
struct cons_pointer in_make_empty_frame(struct cons_pointer previous, uint32_t depth)
Make an empty stack frame, and return it.
Definition stack.c:85
struct cons_pointer make_stack_frame(struct cons_pointer previous, struct cons_pointer args, struct cons_pointer env)
Allocate a new stack frame with its previous pointer set to this value, its arguments set up from the...
Definition stack.c:161
#define stackframep(vso)
is this vector-space object a stack frame?
Definition stack.h:38
#define STACKFRAMETV
Definition stack.h:33
struct cons_pointer make_vso(uint32_t tag, uint64_t payload_size)
Allocate a vector space object with this payload_size and tag, and return a cons_pointer which points...
Definition vectorspace.c:78
union vector_space_object::@5 payload
we'll malloc size bytes for payload, payload is just the first of these.
#define pointer_to_vso(pointer)
given a pointer to a vector space object, return the object.
Definition vectorspace.h:55
a vector_space_object is just a vector_space_header followed by a lump of bytes; what we deem to be i...