Post Scarcity
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 * set a register in a stack frame. Alwaye use this to do so,
31 * because that way we can be sure the inc_ref happens!
32 */
33void set_reg( struct stack_frame *frame, int reg, struct cons_pointer value ) {
34 debug_printf( DEBUG_STACK, L"Setting register %d to ", reg );
37 dec_ref( frame->arg[reg] ); /* if there was anything in that slot
38 * previously other than NIL, we need to decrement it;
39 * NIL won't be decremented as it is locked. */
40 frame->arg[reg] = value;
41 inc_ref( value );
42
43 if ( reg == frame->args ) {
44 frame->args++;
45 }
46}
47
48
49/**
50 * get the actual stackframe object from this `pointer`, or NULL if
51 * `pointer` is not a stackframe pointer.
52 */
53struct stack_frame *get_stack_frame( struct cons_pointer pointer ) {
54 struct stack_frame *result = NULL;
55 struct vector_space_object *vso =
56 pointer2cell( pointer ).payload.vectorp.address;
57
58 if ( vectorpointp( pointer ) && stackframep( vso ) ) {
59 result = ( struct stack_frame * ) &( vso->payload );
61 L"get_stack_frame: all good, returning %p\n", result );
62 } else {
63 debug_print( L"get_stack_frame: fail, returning NULL\n", DEBUG_STACK );
64 }
65
66 return result;
67}
68
69/**
70 * Make an empty stack frame, and return it.
71 * @param previous the current top-of-stack;
72 * @param env the environment in which evaluation happens.
73 * @return the new frame, or NULL if memory is exhausted.
74 */
75struct cons_pointer make_empty_frame( struct cons_pointer previous ) {
76 debug_print( L"Entering make_empty_frame\n", DEBUG_ALLOC );
77 struct cons_pointer result =
78 make_vso( STACKFRAMETV, sizeof( struct stack_frame ) );
79
81
82 if ( !nilp( result ) ) {
83 struct stack_frame *frame = get_stack_frame( result );
84 /*
85 * \todo later, pop a frame off a free-list of stack frames
86 */
87
88 frame->previous = previous;
89
90 /*
91 * clearing the frame with memset would probably be slightly quicker, but
92 * this is clear.
93 */
94 frame->more = NIL;
95 frame->function = NIL;
96 frame->args = 0;
97
98 for ( int i = 0; i < args_in_frame; i++ ) {
99 frame->arg[i] = NIL;
100 }
101 }
102 debug_print( L"Leaving make_empty_frame\n", DEBUG_ALLOC );
104
105 return result;
106}
107
108/**
109 * Allocate a new stack frame with its previous pointer set to this value,
110 * its arguments set up from these args, evaluated in this env.
111 * @param previous the current top-of-stack;
112 * @args the arguments to load into this frame;
113 * @param env the environment in which evaluation happens.
114 * @return the new frame, or an exception if one occurred while building it.
115 */
117 struct cons_pointer args,
118 struct cons_pointer env ) {
119 debug_print( L"Entering make_stack_frame\n", DEBUG_STACK );
120 struct cons_pointer result = make_empty_frame( previous );
121
122 if ( nilp( result ) ) {
123 /* i.e. out of memory */
124 result =
126 } else {
127 struct stack_frame *frame = get_stack_frame( result );
128
129 while ( frame->args < args_in_frame && consp( args ) ) {
130 /* iterate down the arg list filling in the arg slots in the
131 * frame. When there are no more slots, if there are still args,
132 * stash them on more */
133 struct cons_space_object cell = pointer2cell( args );
134
135 /*
136 * \todo if we were running on real massively parallel hardware,
137 * each arg except the first should be handed off to another
138 * processor to be evaled in parallel; but see notes here:
139 * https://github.com/simon-brooke/post-scarcity/wiki/parallelism
140 */
141 struct cons_pointer val =
142 eval_form( frame, result, cell.payload.cons.car, env );
143 if ( exceptionp( val ) ) {
144 result = val;
145 break;
146 } else {
147 debug_printf( DEBUG_STACK, L"Setting argument %d to ",
148 frame->args );
149 debug_print_object( cell.payload.cons.car, DEBUG_STACK );
150 set_reg( frame, frame->args, val );
151 }
152
153 args = cell.payload.cons.cdr;
154 }
155
156 if ( !exceptionp( result ) ) {
157 if ( consp( args ) ) {
158 /* if we still have args, eval them and stick the values on `more` */
159 struct cons_pointer more =
160 eval_forms( get_stack_frame( previous ), previous, args,
161 env );
162 frame->more = more;
163 inc_ref( more );
164 }
165 }
166 debug_print( L"make_stack_frame: returning\n", DEBUG_STACK );
168 }
169
170 return result;
171}
172
173/**
174 * A 'special' frame is exactly like a normal stack frame except that the
175 * arguments are unevaluated.
176 * @param previous the previous stack frame;
177 * @param args a list of the arguments to be stored in this stack frame;
178 * @param env the execution environment;
179 * @return a new special frame.
180 */
182 struct cons_pointer args,
183 struct cons_pointer env ) {
184 debug_print( L"Entering make_special_frame\n", DEBUG_STACK );
185
186 struct cons_pointer result = make_empty_frame( previous );
187
188 if ( nilp( result ) ) {
189 /* i.e. out of memory */
190 result =
191 make_exception( c_string_to_lisp_string( L"Memory exhausted." ),
192 previous );
193 } else {
194 struct stack_frame *frame = get_stack_frame( result );
195
196 while ( frame->args < args_in_frame && !nilp( args ) ) {
197 /* iterate down the arg list filling in the arg slots in the
198 * frame. When there are no more slots, if there are still args,
199 * stash them on more */
200 struct cons_space_object cell = pointer2cell( args );
201
202 set_reg( frame, frame->args, cell.payload.cons.car );
203
204 args = cell.payload.cons.cdr;
205 }
206 if ( !exceptionp( result ) ) {
207 if ( consp( args ) ) {
208 frame->more = args;
209 inc_ref( args );
210 }
211 }
212 }
213 debug_print( L"make_special_frame: returning\n", DEBUG_STACK );
215
216 return result;
217}
218
219/**
220 * Free this stack frame.
221 */
222void free_stack_frame( struct stack_frame *frame ) {
223 /*
224 * \todo later, push it back on the stack-frame freelist
225 */
226 debug_print( L"Entering free_stack_frame\n", DEBUG_ALLOC );
227 for ( int i = 0; i < args_in_frame; i++ ) {
228 dec_ref( frame->arg[i] );
229 }
230 if ( !nilp( frame->more ) ) {
231 dec_ref( frame->more );
232 }
233 debug_print( L"Leaving free_stack_frame\n", DEBUG_ALLOC );
234}
235
236struct cons_pointer frame_get_previous( struct cons_pointer frame_pointer ) {
237 struct stack_frame *frame = get_stack_frame( frame_pointer );
238 struct cons_pointer result = NIL;
239
240 if ( frame != NULL ) {
241 result = frame->previous;
242 }
243
244 return result;
245}
246
248 struct cons_pointer frame_pointer ) {
249 struct stack_frame *frame = get_stack_frame( frame_pointer );
250
251 if ( frame != NULL ) {
252 url_fwprintf( output, L" <= " );
253 print( output, frame->arg[0] );
254 }
255}
256
257void dump_frame_context( URL_FILE *output, struct cons_pointer frame_pointer,
258 int depth ) {
259 struct stack_frame *frame = get_stack_frame( frame_pointer );
260
261 if ( frame != NULL ) {
262 url_fwprintf( output, L"\tContext: " );
263
264 int i = 0;
265 for ( struct cons_pointer cursor = frame_pointer;
266 i++ < depth && !nilp( cursor );
267 cursor = frame_get_previous( cursor ) ) {
268 dump_frame_context_fragment( output, cursor );
269 }
270
271 url_fwprintf( output, L"\n" );
272 }
273}
274
275/**
276 * Dump a stackframe to this stream for debugging
277 * @param output the stream
278 * @param frame_pointer the pointer to the frame
279 */
280void dump_frame( URL_FILE *output, struct cons_pointer frame_pointer ) {
281 struct stack_frame *frame = get_stack_frame( frame_pointer );
282
283 if ( frame != NULL ) {
284 url_fwprintf( output, L"Stack frame with %d arguments:\n",
285 frame->args );
286 dump_frame_context( output, frame_pointer, 4 );
287
288 for ( int arg = 0; arg < frame->args; arg++ ) {
289 struct cons_space_object cell = pointer2cell( frame->arg[arg] );
290
291 url_fwprintf( output, L"Arg %d:\t%4.4s\tcount: %10u\tvalue: ",
292 arg, cell.tag.bytes, cell.count );
293
294 print( output, frame->arg[arg] );
295 url_fputws( L"\n", output );
296 }
297 if ( !nilp( frame->more ) ) {
298 url_fputws( L"More: \t", output );
299 print( output, frame->more );
300 url_fputws( L"\n", output );
301 }
302 }
303}
304
305void dump_stack_trace( URL_FILE *output, struct cons_pointer pointer ) {
306 if ( exceptionp( pointer ) ) {
307 print( output, pointer2cell( pointer ).payload.exception.payload );
308 url_fputws( L"\n", output );
309 dump_stack_trace( output,
310 pointer2cell( pointer ).payload.exception.frame );
311 } else {
312 while ( vectorpointp( pointer )
313 && stackframep( pointer_to_vso( pointer ) ) ) {
314 dump_frame( output, pointer );
315 pointer = get_stack_frame( pointer )->previous;
316 }
317 }
318}
319
320/**
321 * Fetch a pointer to the value of the local variable at this index.
322 */
323struct cons_pointer fetch_arg( struct stack_frame *frame, unsigned int index ) {
324 struct cons_pointer result = NIL;
325
326 if ( index < args_in_frame ) {
327 result = frame->arg[index];
328 } else {
329 struct cons_pointer p = frame->more;
330
331 for ( int i = args_in_frame; i < index; i++ ) {
332 p = pointer2cell( p ).payload.cons.cdr;
333 }
334
335 result = pointer2cell( p ).payload.cons.car;
336 }
337
338 return result;
339}
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.
#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.
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:85
void debug_dump_object(struct cons_pointer pointer, int level)
Like dump_object, q.v., but protected by the verbosity mechanism.
Definition debug.c:136
void debug_printf(int level, wchar_t *format,...)
wprintf adapted for the debug logging system.
Definition debug.c:101
void debug_print(wchar_t *message, int level)
print this debug message to stderr, if verbosity matches level.
Definition debug.c:41
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:119
#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:130
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:181
struct cons_pointer frame_get_previous(struct cons_pointer frame_pointer)
Definition stack.c:236
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:323
void set_reg(struct stack_frame *frame, int reg, struct cons_pointer value)
set a register in a stack frame.
Definition stack.c:33
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:53
void dump_frame_context(URL_FILE *output, struct cons_pointer frame_pointer, int depth)
Definition stack.c:257
void dump_frame(URL_FILE *output, struct cons_pointer frame_pointer)
Dump a stackframe to this stream for debugging.
Definition stack.c:280
void dump_stack_trace(URL_FILE *output, struct cons_pointer pointer)
Definition stack.c:305
void free_stack_frame(struct stack_frame *frame)
Free this stack frame.
Definition stack.c:222
void dump_frame_context_fragment(URL_FILE *output, struct cons_pointer frame_pointer)
Definition stack.c:247
struct cons_pointer make_empty_frame(struct cons_pointer previous)
Make an empty stack frame, and return it.
Definition stack.c:75
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:116
#define stackframep(vso)
is this vector-space object a stack frame?
Definition stack.h:36
#define STACKFRAMETV
Definition stack.h:31
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...