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
21#include "memory/conspage.h"
22#include "debug.h"
23#include "memory/dump.h"
24#include "ops/lispops.h"
25#include "io/print.h"
26#include "memory/stack.h"
27#include "memory/vectorspace.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 =
125 make_exception( c_string_to_lisp_string( L"Memory exhausted." ),
126 previous );
127 } else {
128 struct stack_frame *frame = get_stack_frame( result );
129
130 while ( frame->args < args_in_frame && consp( args ) ) {
131 /* iterate down the arg list filling in the arg slots in the
132 * frame. When there are no more slots, if there are still args,
133 * stash them on more */
134 struct cons_space_object cell = pointer2cell( args );
135
136 /*
137 * \todo if we were running on real massively parallel hardware,
138 * each arg except the first should be handed off to another
139 * processor to be evaled in parallel; but see notes here:
140 * https://github.com/simon-brooke/post-scarcity/wiki/parallelism
141 */
142 struct cons_pointer val =
143 eval_form( frame, result, cell.payload.cons.car, env );
144 if ( exceptionp( val ) ) {
145 result = val;
146 break;
147 } else {
148 debug_printf( DEBUG_STACK, L"Setting argument %d to ",
149 frame->args );
150 debug_print_object( cell.payload.cons.car, DEBUG_STACK );
151 set_reg( frame, frame->args, val );
152 }
153
154 args = cell.payload.cons.cdr;
155 }
156
157 if ( !exceptionp( result ) ) {
158 if ( consp( args ) ) {
159 /* if we still have args, eval them and stick the values on `more` */
160 struct cons_pointer more =
161 eval_forms( get_stack_frame( previous ), previous, args,
162 env );
163 frame->more = more;
164 inc_ref( more );
165 }
166
167 }
168 }
169 debug_print( L"make_stack_frame: returning\n", DEBUG_STACK );
171
172 return result;
173}
174
175/**
176 * A 'special' frame is exactly like a normal stack frame except that the
177 * arguments are unevaluated.
178 * @param previous the previous stack frame;
179 * @param args a list of the arguments to be stored in this stack frame;
180 * @param env the execution environment;
181 * @return a new special frame.
182 */
184 struct cons_pointer args,
185 struct cons_pointer env ) {
186 debug_print( L"Entering make_special_frame\n", DEBUG_STACK );
187
188 struct cons_pointer result = make_empty_frame( previous );
189
190 if ( nilp( result ) ) {
191 /* i.e. out of memory */
192 result =
193 make_exception( c_string_to_lisp_string( L"Memory exhausted." ),
194 previous );
195 } else {
196 struct stack_frame *frame = get_stack_frame( result );
197
198 while ( frame->args < args_in_frame && !nilp( args ) ) {
199 /* iterate down the arg list filling in the arg slots in the
200 * frame. When there are no more slots, if there are still args,
201 * stash them on more */
202 struct cons_space_object cell = pointer2cell( args );
203
204 set_reg( frame, frame->args, cell.payload.cons.car );
205
206 args = cell.payload.cons.cdr;
207 }
208 if ( !exceptionp( result ) ) {
209 if ( consp( args ) ) {
210 frame->more = args;
211 inc_ref( args );
212 }
213 }
214 }
215 debug_print( L"make_special_frame: returning\n", DEBUG_STACK );
217
218 return result;
219}
220
221/**
222 * Free this stack frame.
223 */
224void free_stack_frame( struct stack_frame *frame ) {
225 /*
226 * \todo later, push it back on the stack-frame freelist
227 */
228 debug_print( L"Entering free_stack_frame\n", DEBUG_ALLOC );
229 for ( int i = 0; i < args_in_frame; i++ ) {
230 dec_ref( frame->arg[i] );
231 }
232 if ( !nilp( frame->more ) ) {
233 dec_ref( frame->more );
234 }
235 debug_print( L"Leaving free_stack_frame\n", DEBUG_ALLOC );
236}
237
238
239/**
240 * Dump a stackframe to this stream for debugging
241 * @param output the stream
242 * @param frame_pointer the pointer to the frame
243 */
244void dump_frame( URL_FILE *output, struct cons_pointer frame_pointer ) {
245 struct stack_frame *frame = get_stack_frame( frame_pointer );
246
247 if ( frame != NULL ) {
248 url_fwprintf( output, L"Stack frame with %d arguments:\n",
249 frame->args );
250 for ( int arg = 0; arg < frame->args; arg++ ) {
251 struct cons_space_object cell = pointer2cell( frame->arg[arg] );
252
253 url_fwprintf( output, L"Arg %d:\t%c%c%c%c\tcount: %10u\tvalue: ",
254 arg, cell.tag.bytes[0], cell.tag.bytes[1],
255 cell.tag.bytes[2], cell.tag.bytes[3], cell.count );
256
257 print( output, frame->arg[arg] );
258 url_fputws( L"\n", output );
259 }
260 if ( !nilp( frame->more ) ) {
261 url_fputws( L"More: \t", output );
262 print( output, frame->more );
263 url_fputws( L"\n", output );
264 }
265 }
266}
267
268void dump_stack_trace( URL_FILE *output, struct cons_pointer pointer ) {
269 if ( exceptionp( pointer ) ) {
270 print( output, pointer2cell( pointer ).payload.exception.payload );
271 url_fputws( L"\n", output );
272 dump_stack_trace( output,
273 pointer2cell( pointer ).payload.exception.frame );
274 } else {
275 while ( vectorpointp( pointer )
276 && stackframep( pointer_to_vso( pointer ) ) ) {
277 dump_frame( output, pointer );
278 pointer = get_stack_frame( pointer )->previous;
279 }
280 }
281}
282
283/**
284 * Fetch a pointer to the value of the local variable at this index.
285 */
286struct cons_pointer fetch_arg( struct stack_frame *frame, unsigned int index ) {
287 struct cons_pointer result = NIL;
288
289 if ( index < args_in_frame ) {
290 result = frame->arg[index];
291 } else {
292 struct cons_pointer p = frame->more;
293
294 for ( int i = args_in_frame; i < index; i++ ) {
295 p = pointer2cell( p ).payload.cons.cdr;
296 }
297
298 result = pointer2cell( p ).payload.cons.car;
299 }
300
301 return result;
302}
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:77
#define DEBUG_ALLOC
Print messages debugging memory allocation.
Definition debug.h:21
#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:64
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:129
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:151
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:183
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:286
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(URL_FILE *output, struct cons_pointer frame_pointer)
Dump a stackframe to this stream for debugging.
Definition stack.c:244
void dump_stack_trace(URL_FILE *output, struct cons_pointer pointer)
Definition stack.c:268
void free_stack_frame(struct stack_frame *frame)
Free this stack frame.
Definition stack.c:224
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:75
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...