1 /* JitterLisp: interpreter: naïve C version.
2
3 Copyright (C) 2017, 2018, 2020 Luca Saiu
4 Written by Luca Saiu
5
6 This file is part of the JitterLisp language implementation, distributed as
7 an example along with Jitter under the same license.
8
9 Jitter is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Jitter is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Jitter. If not, see <http://www.gnu.org/licenses/>. */
21
22
23 #include "jitterlisp-eval-interpreter.h"
24 #include "jitterlisp-eval-vm.h"
25
26 #include "jitterlisp.h"
27
28
29
30
31 /* Non-Jittery interpreter: AST evaluation helpers for primitives and closures.
32 * ************************************************************************** */
33
34 /* Return the evaluation of the given primitive on the given (of course still
35 unevaluated) operand ASTs. Assume that the rator argument is an encoded
36 primitive, and that rand_asts is a C array of operand_no elements.
37 The noinline attribute is important here: this function invokes a primitive C
38 function in what would syntactically look like a tail context, but passing it
39 a pointer to local storage as argument; that prevents GCC from compiling the
40 call as a sibling call optimization, which in itself is a very minor loss.
41 However having the body of this function inlined in
42 jitterlisp_eval_interpreter_ast , which in its turn also inlines
43 jitterlisp_eval_interpreter_ast_call , would prevent sibling call compilation
44 in the case of *closure* tail calls, thus leaking stack space for tail calls.
45 Tested with a GCC 8 snapshot from early October 2017. */
46 __attribute__ ((noinline))
47 static jitterlisp_object
jitterlisp_eval_interpreter_ast_primitive(jitterlisp_object rator,const jitterlisp_object * rand_asts,size_t rand_no,jitterlisp_object env)48 jitterlisp_eval_interpreter_ast_primitive (jitterlisp_object rator,
49 const jitterlisp_object *rand_asts,
50 size_t rand_no,
51 jitterlisp_object env)
52 {
53 /* FIXME: this, and likely this C function signature as well, will need to
54 change with an exact-pointer-finding GC.
55 Evaluate primitive actuals into a temporary array which is large enough for
56 the actuals of any primitive. Don't bother initializing the elements we
57 don't actually use. If the AST has been built correctly the primitive
58 in-arity is correct, so we don't need to check it now at run time. */
59 jitterlisp_object values [JITTERLISP_PRIMITIVE_MAX_IN_ARITY];
60 int i;
61 for (i = 0; i < rand_no; i ++)
62 values [i] = jitterlisp_eval_interpreter_ast (rand_asts [i], env);
63
64 /* FIXME: does GCC guarantee that this will not be compiled as a sibling
65 call? It is important that the current stack frame is not popped until
66 the callee returns. */
67 return JITTERLISP_PRIMITIVE_DECODE(rator)->function (values);
68 }
69
70 /* Return the result of the given call in the given environment. The operator
71 is an AST, still to evaluate, and the operands are tagged ASTs in the given
72 number; the operator comes first in the array. If the operator doesn't
73 evaluate to a closure this function errors out cleanly. */
74 static inline jitterlisp_object
jitterlisp_eval_interpreter_ast_call(const jitterlisp_object * rator_and_rand_asts,size_t rator_and_rand_no,jitterlisp_object env)75 jitterlisp_eval_interpreter_ast_call
76 (const jitterlisp_object *rator_and_rand_asts,
77 size_t rator_and_rand_no,
78 jitterlisp_object env)
79 {
80 /* First evaluate the operator. */
81 jitterlisp_object rator_value
82 = jitterlisp_eval_interpreter_ast (rator_and_rand_asts [0], env);
83 if (! JITTERLISP_IS_CLOSURE(rator_value))
84 {
85 jitterlisp_print_error_char_star ("About "); // FIXME: add to the error message
86 jitterlisp_print_error (rator_value);
87 jitterlisp_print_error_char_star (":\n");
88 jitterlisp_error_cloned ("call: non-closure operator");
89 }
90
91 /* If we arrived here the operator is a closure. Is it compiled or
92 interpreted? */
93 struct jitterlisp_closure *c = JITTERLISP_CLOSURE_DECODE(rator_value);
94
95 // FIXME: shall I check the arity *before* evaluating actuals or after, as
96 // the code does now?
97 // In either case compiled code must have the same semantics. Do whatever
98 // is faster on compiled code. There is another identical case above
99 // and one more below.
100
101 /* If the closure is compiled tail-call the helper function and ignore the
102 rest of this. */
103 if (__builtin_expect ((c->kind == jitterlisp_closure_type_compiled),
104 false))
105 return jitterlisp_call_compiled (rator_value,
106 rator_and_rand_asts + 1,
107 rator_and_rand_no - 1,
108 env);
109
110 /* The closure is interpreted. Evaluate actuals binding them to the closure
111 formals, in order, starting from the closure environment. Unfortunately we
112 have to check the arity at run time, differently from the primitive
113 case. */
114 struct jitterlisp_interpreted_closure *ic = & c->interpreted;
115 jitterlisp_object formals = ic->formals;
116 jitterlisp_object body_env = ic->environment;
117 int i;
118 // FIXME: shall I check the arity *before* evaluating actuals or after, as
119 // the code does now?
120 // In either case compiled code must have the same semantics. Do whatever
121 // is faster on compiled code. There are two other identical cases above.
122 for (i = 1; i < rator_and_rand_no; i ++)
123 {
124 if (JITTERLISP_IS_EMPTY_LIST(formals))
125 {
126 jitterlisp_print_error_char_star ("About a call to "); // FIXME: add to the error message
127 jitterlisp_print_error (rator_value);
128 jitterlisp_print_error_char_star (":\n");
129 jitterlisp_error_cloned ("call: too many actuals");
130 }
131
132 jitterlisp_object rand_value =
133 jitterlisp_eval_interpreter_ast (rator_and_rand_asts [i], env);
134 jitterlisp_object formal = JITTERLISP_EXP_C_A_CAR(formals);
135 body_env = jitterlisp_environment_bind (body_env, formal, rand_value);
136
137 formals = JITTERLISP_EXP_C_A_CDR(formals);
138 }
139 if (! JITTERLISP_IS_EMPTY_LIST(formals))
140 {
141 jitterlisp_print_error_char_star ("About a call to "); // FIXME: add to the error message
142 jitterlisp_print_error (rator_value);
143 jitterlisp_print_error_char_star (":\n");
144 jitterlisp_error_cloned ("call: not enough actuals");
145 }
146
147 /* Return the evaluation of the closure body in the extended closure
148 environment. */
149 jitterlisp_object body_ast = ic->body;
150 return jitterlisp_eval_interpreter_ast (body_ast, body_env);
151 }
152
153
154
155
156 /* Non-Jittery interpreter: AST evaluation.
157 * ************************************************************************** */
158
159 /* This is the main function for AST interpretation. */
160 jitterlisp_object
jitterlisp_eval_interpreter_ast(jitterlisp_object o,jitterlisp_object env)161 jitterlisp_eval_interpreter_ast (jitterlisp_object o,
162 jitterlisp_object env)
163 {
164 /* No need to validate o: if it comes from macroexpansion it's definitely an
165 encoded AST, and its subs are well-formed as well. No need to validate
166 env for the same reason. */
167 const struct jitterlisp_ast *ast = JITTERLISP_AST_DECODE(o);
168 const jitter_uint sub_no = ast->sub_no;
169 const jitterlisp_object * const subs = ast->subs;
170 switch (ast->case_)
171 {
172 case jitterlisp_ast_case_literal:
173 return subs [0];
174
175 case jitterlisp_ast_case_variable:
176 return jitterlisp_environment_lookup (env, subs [0]);
177
178 case jitterlisp_ast_case_define:
179 {
180 jitterlisp_object defined_value
181 = jitterlisp_eval_interpreter_ast (subs [1], env);
182 jitterlisp_define (subs [0], defined_value);
183 return JITTERLISP_NOTHING;
184 }
185
186 case jitterlisp_ast_case_if:
187 {
188 jitterlisp_object condition_result
189 = jitterlisp_eval_interpreter_ast (subs [0], env);
190 jitterlisp_object branch
191 = (JITTERLISP_IS_FALSE(condition_result)
192 ? subs [2]
193 : subs [1]);
194 return jitterlisp_eval_interpreter_ast (branch, env);
195 }
196
197 case jitterlisp_ast_case_setb:
198 {
199 jitterlisp_object bound_value
200 = jitterlisp_eval_interpreter_ast (subs [1], env);
201 jitterlisp_environment_setb (env, subs [0], bound_value);
202 return JITTERLISP_NOTHING;
203 }
204
205 case jitterlisp_ast_case_while:
206 {
207 const jitterlisp_object guard = subs [0];
208 const jitterlisp_object body = subs [1];
209 while (! JITTERLISP_IS_FALSE(jitterlisp_eval_interpreter_ast (guard,
210 env)))
211 jitterlisp_eval_interpreter_ast (body, env);
212 return JITTERLISP_NOTHING;
213 }
214
215 case jitterlisp_ast_case_primitive:
216 return jitterlisp_eval_interpreter_ast_primitive (subs [0],
217 subs + 1,
218 sub_no - 1,
219 env);
220
221 case jitterlisp_ast_case_call:
222 return jitterlisp_eval_interpreter_ast_call (subs, sub_no, env);
223
224 case jitterlisp_ast_case_lambda:
225 {
226 /* Notice that the lambda formals are already stored as a list of
227 symbols in the AST, differently from other AST cases; that is an
228 optimization to make this closure initialization faster. */
229 jitterlisp_object res;
230 JITTERLISP_CLOSURE_(res, env, subs [0], subs [1]);
231 return res;
232 }
233
234 case jitterlisp_ast_case_let:
235 {
236 /* Evaluate the bound form in env, then bind its result to the bound
237 variable in the current environment. */
238 jitterlisp_object bound_value
239 = jitterlisp_eval_interpreter_ast (subs [1], env);
240 env = jitterlisp_environment_bind (env, subs [0], bound_value);
241
242 /* Evaluate the body in the extended environment. */
243 return jitterlisp_eval_interpreter_ast (subs [2], env);
244 }
245
246 case jitterlisp_ast_case_sequence:
247 jitterlisp_eval_interpreter_ast (subs [0], env);
248 return jitterlisp_eval_interpreter_ast (subs [1], env);
249
250 default:
251 jitterlisp_print_error_char_star ("About "); // FIXME: add to the error message
252 jitterlisp_print_error (o);
253 jitterlisp_print_error_char_star (":\n");
254 jitterlisp_error_cloned ("eval: invalid or unimplemented AST case");
255 }
256 }
257
258
259
260
261 /* Non-Jittery interpreter: user API.
262 * ************************************************************************** */
263
264 jitterlisp_object
jitterlisp_eval_globally_interpreter(jitterlisp_object unexpanded_form)265 jitterlisp_eval_globally_interpreter (jitterlisp_object unexpanded_form)
266 {
267 return jitterlisp_eval_interpreter (unexpanded_form,
268 jitterlisp_empty_environment);
269 }
270
271 jitterlisp_object
jitterlisp_eval_interpreter(jitterlisp_object unexpanded_form,jitterlisp_object env)272 jitterlisp_eval_interpreter (jitterlisp_object unexpanded_form,
273 jitterlisp_object env)
274 {
275 if (jitterlisp_settings.verbose)
276 {
277 jitterlisp_log_char_star ("Macroexpanding ");
278 jitterlisp_log (unexpanded_form);
279 jitterlisp_log_char_star ("...\n");
280 }
281 jitterlisp_object ast = jitterlisp_macroexpand (unexpanded_form, env);
282 if (jitterlisp_settings.verbose)
283 {
284 jitterlisp_log_char_star ("...into ");
285 jitterlisp_log (ast);
286 jitterlisp_log_char_star ("\n");
287 }
288 return jitterlisp_eval_interpreter_ast (ast, env);
289 }
290
291
292
293
294 /* Non-Jittery interpreter: apply.
295 * ************************************************************************** */
296
297 /* Differently from what happens in simple meta-circual interpreters here eval
298 and apply are not mutually recursive: eval doesn't evaluate a procedure call
299 operands into a temporary list, for efficiency reasons. However this is
300 convenient to have, particularly to be called from Lisp (with additional type
301 checking done by the primimitive function), when the operands are already a
302 list. */
303
304 /* Unfortunately this is difficult to factor with
305 jitterlisp_eval_interpreter_ast_call without introducing unnecessary
306 allocation, and here performance is important. */
307 jitterlisp_object
jitterlisp_apply_interpreter(jitterlisp_object closure_value,jitterlisp_object operands_as_list)308 jitterlisp_apply_interpreter (jitterlisp_object closure_value,
309 jitterlisp_object operands_as_list)
310 {
311 /* Decode the closure. No need to check that it's actually a closure, but
312 here we don't know if it's compiled or interpreted. */
313 struct jitterlisp_closure *c = JITTERLISP_CLOSURE_DECODE(closure_value);
314
315 /* If the closure is compiled tail-call another function which does the
316 job of calling the VM, checking for in-arity mismatches. */
317 if (__builtin_expect ((c->kind == jitterlisp_closure_type_compiled),
318 false))
319 return jitterlisp_apply_compiled (closure_value, operands_as_list);
320
321 /* The closure is interpreted, so it's the interpreter's job to evaluate
322 the call. Keep fields in automatic C variables. */
323 struct jitterlisp_interpreted_closure *ic = & c->interpreted;
324 jitterlisp_object formals = ic->formals;
325 jitterlisp_object body_env = ic->environment;
326
327 /* Bind operands to formals in the closure environment. */
328 while (! JITTERLISP_IS_EMPTY_LIST (operands_as_list))
329 {
330 if (JITTERLISP_IS_EMPTY_LIST(formals))
331 {
332 jitterlisp_print_error_char_star ("About a call to "); // FIXME: add to the error message
333 jitterlisp_print_error (closure_value);
334 jitterlisp_print_error_char_star ("\n");
335 jitterlisp_error_cloned ("apply: too many actuals");
336 }
337 /* If this were a safe C function I would check whether operands_as_list
338 is a cons; but this has been already checked out of this function when
339 we get here thru a primitive call. */
340
341 /* Extend the environment with one formal/operand binding. */
342 jitterlisp_object formal = JITTERLISP_EXP_C_A_CAR(formals);
343 jitterlisp_object rand_value = JITTERLISP_EXP_C_A_CAR(operands_as_list);
344 body_env = jitterlisp_environment_bind (body_env, formal, rand_value);
345
346 /* Advance the two lists. */
347 formals = JITTERLISP_EXP_C_A_CDR(formals);
348 operands_as_list = JITTERLISP_EXP_C_A_CDR(operands_as_list);
349 }
350 if (! JITTERLISP_IS_EMPTY_LIST(formals))
351 {
352 jitterlisp_print_error_char_star ("About a call to "); // FIXME: add to the error message
353 jitterlisp_print_error (closure_value);
354 jitterlisp_print_error_char_star ("\n");
355 jitterlisp_error_cloned ("apply: not enough actuals");
356 }
357
358 /* Return the evaluation of the closure body in the extended closure
359 environment. */
360 jitterlisp_object body_ast = ic->body;
361 return jitterlisp_eval_interpreter_ast (body_ast, body_env);
362 }
363
364
365
366
367 /* Call into interpreted code.
368 * ************************************************************************** */
369
370 jitterlisp_object
jitterlisp_call_interpreted(const struct jitterlisp_interpreted_closure * ic,jitterlisp_object * actual_values,jitter_uint actual_value_no)371 jitterlisp_call_interpreted (const struct jitterlisp_interpreted_closure *ic,
372 jitterlisp_object *actual_values,
373 jitter_uint actual_value_no)
374 {
375 /* Keep fields in automatic C variables. */
376 jitterlisp_object formals = ic->formals;
377 jitterlisp_object body_env = ic->environment;
378
379 /* Bind operands to formals in the closure environment. No need for arity
380 checking. */
381 int i;
382 for (i = 0; i < actual_value_no; i ++)
383 {
384 /* Extend the environment with one formal/operand binding. */
385 jitterlisp_object formal = JITTERLISP_EXP_C_A_CAR(formals);
386 jitterlisp_object rand_value = actual_values [i];
387 body_env = jitterlisp_environment_bind (body_env, formal, rand_value);
388
389 /* Advance the formal list. */
390 formals = JITTERLISP_EXP_C_A_CDR(formals);
391 }
392
393 /* Return the evaluation of the closure body in the extended closure
394 environment. */
395 jitterlisp_object body_ast = ic->body;
396 return jitterlisp_eval_interpreter_ast (body_ast, body_env);
397 }
398