aboutsummaryrefslogtreecommitdiffstats
path: root/src/x86asm_compiler.h
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2024-06-21 18:20:35 +0200
committerBad Diode <bd@badd10de.dev>2024-06-21 18:20:35 +0200
commit835f4d9f23f55a973d76ae9384b7b9d75da5472b (patch)
tree8e817452f8437db07688cb6e63a1a73bcce543eb /src/x86asm_compiler.h
parent5a25eeefd13b0e1988ecaf7e497ebde81e71bb2e (diff)
downloadbdl-835f4d9f23f55a973d76ae9384b7b9d75da5472b.tar.gz
bdl-835f4d9f23f55a973d76ae9384b7b9d75da5472b.zip
Remove old files no longer needed as reference
Diffstat (limited to 'src/x86asm_compiler.h')
-rw-r--r--src/x86asm_compiler.h818
1 files changed, 818 insertions, 0 deletions
diff --git a/src/x86asm_compiler.h b/src/x86asm_compiler.h
new file mode 100644
index 0000000..6ca4467
--- /dev/null
+++ b/src/x86asm_compiler.h
@@ -0,0 +1,818 @@
1#ifndef BDL_COMPILER_H
2#define BDL_COMPILER_H
3
4#define PRELUDE_FILE "src/x86_64/prelude.asm"
5#define POSTLUDE_FILE "src/x86_64/postlude.asm"
6
7#define HEAP_SIZE MB(32)
8
9typedef struct Constant {
10 Object *obj;
11 char *label;
12} Constant;
13
14static Constant *constants = NULL;
15static char **labels = NULL;
16static char **procedures = NULL;
17
18static char* current_context = NULL;
19#define context_printf(fmt, ...) \
20do { \
21 char buf[KB(4)]; \
22 int n_chars = sprintf(buf, fmt, ##__VA_ARGS__); \
23 array_insert(current_context, buf, n_chars); \
24} while(false);
25
26static Environment *current_env = NULL;
27
28// TODO: Separate c/h files
29// TODO: Create a "driver.c" file with the (display) function for external
30// linkage or assembly inlining.
31// TODO: Ensure we don't compile a function multiple times (for example with
32// a function that contains internal functions).
33
34// Immediate constants.
35#define NIL_VAL 47LU
36#define BOOL_MASK 127LU
37#define BOOL_TAG 31LU
38#define BOOL_SHIFT 7LU
39#define TRUE_VAL ((1 << BOOL_SHIFT) | BOOL_TAG)
40#define FALSE_VAL ((0 << BOOL_SHIFT) | BOOL_TAG)
41#define FIXNUM_MASK 3LU
42#define FIXNUM_TAG 0LU
43#define FIXNUM_SHIFT 2LU
44
45// Heap allocated objects.
46#define PTR_MASK ~7LU
47#define STRING_MASK 7LU
48#define STRING_TAG 3LU
49#define PAIR_MASK 7LU
50#define PAIR_TAG 1LU
51#define LAMBDA_MASK 7LU
52#define LAMBDA_TAG 6LU
53
54void compile_object(Object *obj);
55void compile_fixnum(Object *obj);
56void compile_proc_call(Object *obj);
57void compile(Program program);
58
59char *
60generate_label(char *prefix) {
61 static size_t label_counter = 0;
62 char buf[32];
63 sprintf(buf, "%s%zu", prefix, label_counter++);
64 size_t len = strlen(buf);
65 char * ret = malloc(len + 1);
66 memcpy(ret, buf, len);
67 ret[len] = 0;
68 array_push(labels, ret);
69 return ret;
70}
71
72void
73emit_file(char *file_name) {
74 FILE *file = fopen(file_name, "r");
75 if (!file) {
76 fprintf(stderr, "error: couldn't open input file: %s\n", file_name);
77 exit(EXIT_FAILURE);
78 }
79 char buf[1024];
80 size_t n = 0;
81 while ((n = fread(&buf, 1, 1024, file)) > 0) {
82 fwrite(buf, 1, n, stdout);
83 }
84}
85
86void
87compile_fixnum(Object *obj) {
88 context_printf(" ;; --> compile_fixnum\n");
89 context_printf(" mov rax, %zu\n", (obj->fixnum << FIXNUM_SHIFT) | FIXNUM_TAG);
90 context_printf(" push rax\n");
91 context_printf(" ;; <-- compile_fixnum\n");
92}
93
94void
95compile_boolean(Object *obj) {
96 context_printf(" ;; --> compile_boolean\n");
97 int is_true = obj->type == OBJ_TYPE_TRUE;
98 context_printf(" mov rax, %zu\n", (is_true << BOOL_SHIFT) | BOOL_TAG);
99 context_printf(" push rax\n");
100 context_printf(" ;; <-- compile_boolean\n");
101}
102
103void
104compile_nil(void) {
105 context_printf(" ;; --> compile_nil\n");
106 context_printf(" mov rax, NIL_VAL\n");
107 context_printf(" push rax\n");
108 context_printf(" ;; <-- compile_nil\n");
109}
110
111typedef enum OpType {
112 // Arithmetic.
113 OP_ADD,
114 OP_SUB,
115 OP_MUL,
116 OP_DIV,
117 OP_MOD,
118 // Type predicates.
119 OP_IS_NIL,
120 OP_IS_ZERO,
121 OP_IS_BOOL,
122 OP_IS_FIXNUM,
123 // Logic operations.
124 OP_EQUAL,
125 OP_GREATER,
126 OP_LESS,
127 OP_GREATER_EQ,
128 OP_LESS_EQ,
129} OpType;
130
131void
132compile_type_predicate(OpType op, Object* args) {
133 context_printf(" ;; --> compile_type_predicate\n");
134 compile_object(args->head);
135 context_printf(" pop rax\n");
136 switch (op) {
137 case OP_IS_NIL: {
138 context_printf(" cmp rax, NIL_VAL\n");
139 } break;
140 case OP_IS_ZERO: {
141 context_printf(" cmp rax, 0\n");
142 } break;
143 case OP_IS_BOOL: {
144 context_printf(" and rax, BOOL_MASK\n");
145 context_printf(" cmp rax, BOOL_TAG\n");
146 } break;
147 case OP_IS_FIXNUM: {
148 context_printf(" and rax, FIXNUM_MASK\n");
149 context_printf(" cmp rax, FIXNUM_TAG\n");
150 } break;
151 default: break;
152 }
153 context_printf(" mov rax, 0\n");
154 context_printf(" sete al\n");
155 context_printf(" shl rax, BOOL_SHIFT\n");
156 context_printf(" or rax, BOOL_TAG\n");
157 context_printf(" push rax\n");
158 context_printf(" ;; <-- compile_type_predicate\n");
159}
160
161void
162compile_not(Object* args) {
163 context_printf(" ;; --> compile_not\n");
164 compile_object(args->head);
165 context_printf(" pop rax\n");
166 context_printf(" cmp rax, FALSE_VAL\n");
167 context_printf(" mov rax, 0\n");
168 context_printf(" sete al\n");
169 context_printf(" shl rax, BOOL_SHIFT\n");
170 context_printf(" or rax, BOOL_TAG\n");
171 context_printf(" push rax\n");
172 context_printf(" ;; <-- compile_not\n");
173}
174
175void
176compile_and(Object *args) {
177 context_printf(" ;; --> compile_and\n");
178 char *lab_false = generate_label("BDLL");
179 char *lab_exit = generate_label("BDLL");
180 while (args != NULL) {
181 compile_object(args->head);
182 args = args->tail;
183 context_printf(" pop rax\n");
184 context_printf(" cmp rax, FALSE_VAL\n");
185 context_printf(" je %s\n", lab_false);
186 }
187 context_printf(" mov rax, TRUE_VAL\n");
188 context_printf(" push rax\n");
189 context_printf(" jmp %s\n", lab_exit);
190 context_printf("%s:\n", lab_false);
191 context_printf(" mov rax, FALSE_VAL\n");
192 context_printf(" push rax\n");
193 context_printf("%s:\n", lab_exit);
194 context_printf(" ;; <-- compile_and\n");
195}
196
197void
198compile_or(Object *args) {
199 context_printf(" ;; --> compile_or\n");
200 char *lab_true = generate_label("BDLL");
201 char *lab_exit = generate_label("BDLL");
202 while (args != NULL) {
203 compile_object(args->head);
204 args = args->tail;
205 context_printf(" pop rax\n");
206 context_printf(" cmp rax, FALSE_VAL\n");
207 context_printf(" jne %s\n", lab_true);
208 }
209 context_printf(" mov rax, FALSE_VAL\n");
210 context_printf(" push rax\n");
211 context_printf(" jmp %s\n", lab_exit);
212 context_printf("%s:\n", lab_true);
213 context_printf(" mov rax, TRUE_VAL\n");
214 context_printf(" push rax\n");
215 context_printf("%s:\n", lab_exit);
216 context_printf(" ;; <-- compile_or\n");
217}
218
219void
220compile_cmp_list(OpType op, Object* args) {
221 context_printf(" ;; --> compile_cmp_list\n");
222 compile_object(args->head);
223 char *lab_false = generate_label("BDLL");
224 char *lab_exit = generate_label("BDLL");
225 args = args->tail;
226 while (args != NULL) {
227 compile_object(args->head);
228 args = args->tail;
229
230 // Current value.
231 context_printf(" pop rcx\n");
232
233 // Previous value.
234 context_printf(" pop rax\n");
235
236 // Comparison.
237 context_printf(" cmp rax, rcx\n");
238 switch (op) {
239 case OP_EQUAL: { context_printf(" jne %s\n", lab_false); } break;
240 case OP_GREATER: { context_printf(" jle %s\n", lab_false); } break;
241 case OP_LESS: { context_printf(" jge %s\n", lab_false); } break;
242 case OP_GREATER_EQ: { context_printf(" jl %s\n", lab_false); } break;
243 case OP_LESS_EQ: { context_printf(" jg %s\n", lab_false); } break;
244 default: break;
245 }
246 context_printf(" push rcx\n");
247 }
248 context_printf(" pop rcx\n");
249 context_printf(" mov rax, TRUE_VAL\n");
250 context_printf(" push rax\n");
251 context_printf(" jmp %s\n", lab_exit);
252 context_printf("%s:\n", lab_false);
253 context_printf(" mov rax, FALSE_VAL\n");
254 context_printf(" push rax\n");
255 context_printf("%s:\n", lab_exit);
256 context_printf(" ;; <-- compile_cmp_list\n");
257}
258
259void
260compile_arithmetic_list(OpType op, Object* args) {
261 context_printf(" ;; --> compile_arithmetic\n");
262 compile_object(args->head);
263 args = args->tail;
264 while (args != NULL) {
265 compile_object(args->head);
266 args = args->tail;
267 context_printf(" pop rcx\n");
268 context_printf(" pop rax\n");
269 switch (op) {
270 case OP_ADD: { context_printf(" add rax, rcx\n"); } break;
271 case OP_SUB: { context_printf(" sub rax, rcx\n"); } break;
272 case OP_MUL: {
273 context_printf(" sar rax, FIXNUM_SHIFT\n");
274 context_printf(" sar rcx, FIXNUM_SHIFT\n");
275 context_printf(" mul rcx\n");
276 context_printf(" shl rax, FIXNUM_SHIFT\n");
277 } break;
278 case OP_DIV: {
279 context_printf(" sar rax, FIXNUM_SHIFT\n");
280 context_printf(" sar rcx, FIXNUM_SHIFT\n");
281 context_printf(" mov rdx, 0\n");
282 context_printf(" div rcx\n");
283 context_printf(" shl rax, FIXNUM_SHIFT\n");
284 } break;
285 case OP_MOD: {
286 context_printf(" sar rax, FIXNUM_SHIFT\n");
287 context_printf(" sar rcx, FIXNUM_SHIFT\n");
288 context_printf(" mov rdx, 0\n");
289 context_printf(" div rcx\n");
290 context_printf(" mov rax, rdx\n");
291 context_printf(" shl rax, FIXNUM_SHIFT\n");
292 } break;
293 default: break;
294 }
295 context_printf(" push rax\n");
296 }
297 context_printf(" ;; <-- compile_arithmetic\n");
298}
299
300void
301compile_cons(Object *obj) {
302 context_printf(" ;; --> compile_cons\n");
303 // Store objects into the car and cdr.
304 compile_object(obj->head);
305 compile_object(obj->tail->head);
306 context_printf(" pop rdx\n");
307 context_printf(" pop rax\n");
308 context_printf(" mov [r15], rax\n");
309 context_printf(" mov [r15 + 8], rdx\n");
310
311 // Push memory address of cons cell.
312 context_printf(" mov rax, r15\n");
313 context_printf(" or rax, %zu\n", PAIR_TAG);
314 context_printf(" push rax\n");
315
316 // Bump allocation register.
317 context_printf(" add r15, 16\n");
318 context_printf(" ;; <-- compile_cons\n");
319}
320
321void
322compile_car(Object *obj) {
323 context_printf(" ;; --> compile_car\n");
324 compile_object(obj->head);
325 context_printf(" pop rax\n");
326 context_printf(" and rax, %zu\n", ~PAIR_MASK);
327 context_printf(" mov rdx, [rax]\n");
328 context_printf(" push rdx\n");
329 context_printf(" ;; <-- compile_car\n");
330}
331
332void
333compile_cdr(Object *obj) {
334 context_printf(" ;; --> compile_cdr\n");
335 compile_object(obj->head);
336 context_printf(" pop rax\n");
337 context_printf(" and rax, %zu\n", ~PAIR_MASK);
338 context_printf(" mov rdx, [rax + 8]\n");
339 context_printf(" push rdx\n");
340 context_printf(" ;; <-- compile_cdr\n");
341}
342
343size_t
344compile_call_body(Object *obj) {
345 // Compile operator.
346 compile_object(obj->head);
347 context_printf(" pop rax\n");
348 context_printf(" mov rcx, PTR_MASK\n");
349 context_printf(" and rcx, rax\n");
350 context_printf(" mov rax, [rcx]\n");
351 context_printf(" push rax\n");
352
353 // Get the number of parameters/captured variables for this function.
354 Object *fun = obj->head;
355 // FIXME: this is horrible and WILL BREAK.
356 while (!IS_LAMBDA(fun)) {
357 if (IS_SYMBOL(fun)) {
358 fun = symbol_in_env(current_env, fun);
359 continue;
360 }
361 if (IS_PAIR(fun)) {
362 fun = fun->head;
363 }
364 }
365
366 size_t n_args = array_size(fun->env->params);
367 size_t n_cap = array_size(fun->env->captured);
368 size_t offset = n_args + n_cap;
369
370 // Push captured variables.
371 for (size_t i = 0; i < n_cap; i++) {
372 context_printf(" mov rax, [rcx + 8 * %zu]\n", i + 1);
373 context_printf(" push rax\n");
374 }
375
376 // Compile arguments.
377 while (obj->tail != NULL) {
378 obj = obj->tail;
379 compile_object(obj->head);
380 }
381 return offset;
382}
383
384void
385compile_call(Object *obj) {
386 context_printf(" ;; --> compile_call\n");
387 context_printf(" push rbp\n");
388
389 // Prepare return pointer.
390 char *lab_end = generate_label("BDLL");
391 context_printf(" lea rcx, [%s]\n", lab_end);
392 context_printf(" push rcx\n");
393
394 // Function call compilation without start/end.
395 size_t offset = compile_call_body(obj);
396
397 // Call function.
398 context_printf(" mov rdi, [rsp + %zu]\n", 8 * offset);
399 context_printf(" jmp rdi\n");
400
401 // Restore stack to previous location and store the result on top.
402 context_printf("%s:\n", lab_end);
403 context_printf(" add rsp, %zu\n", 8 * (offset + 2));
404 context_printf(" pop rbp\n");
405 context_printf(" push rax\n");
406 context_printf(" ;; <-- compile_call\n");
407}
408
409void
410compile_proc_call(Object *obj) {
411 // TODO: Probably we want to use a hash table for these lookups that is
412 // initialized at the start of the compilation procedure.
413 if (sv_equal(&obj->head->text, &STRING("+"))) {
414 compile_arithmetic_list(OP_ADD, obj->tail);
415 } else if (sv_equal(&obj->head->text, &STRING("-"))) {
416 compile_arithmetic_list(OP_SUB, obj->tail);
417 } else if (sv_equal(&obj->head->text, &STRING("*"))) {
418 compile_arithmetic_list(OP_MUL, obj->tail);
419 } else if (sv_equal(&obj->head->text, &STRING("/"))) {
420 compile_arithmetic_list(OP_DIV, obj->tail);
421 } else if (sv_equal(&obj->head->text, &STRING("%"))) {
422 compile_arithmetic_list(OP_MOD, obj->tail);
423 } else if (sv_equal(&obj->head->text, &STRING("nil?"))) {
424 compile_type_predicate(OP_IS_NIL, obj->tail);
425 } else if (sv_equal(&obj->head->text, &STRING("zero?"))) {
426 compile_type_predicate(OP_IS_ZERO, obj->tail);
427 } else if (sv_equal(&obj->head->text, &STRING("fixnum?"))) {
428 compile_type_predicate(OP_IS_FIXNUM, obj->tail);
429 } else if (sv_equal(&obj->head->text, &STRING("bool?"))) {
430 compile_type_predicate(OP_IS_BOOL, obj->tail);
431 } else if (sv_equal(&obj->head->text, &STRING("display"))) {
432 compile_object(obj->tail->head);
433 context_printf(" pop rdi\n");
434 context_printf(" call display\n");
435 } else if (sv_equal(&obj->head->text, &STRING("not"))) {
436 compile_not(obj->tail);
437 } else if (sv_equal(&obj->head->text, &STRING("and"))) {
438 compile_and(obj->tail);
439 } else if (sv_equal(&obj->head->text, &STRING("or"))) {
440 compile_or(obj->tail);
441 } else if (sv_equal(&obj->head->text, &STRING("="))) {
442 compile_cmp_list(OP_EQUAL, obj->tail);
443 } else if (sv_equal(&obj->head->text, &STRING(">"))) {
444 compile_cmp_list(OP_GREATER, obj->tail);
445 } else if (sv_equal(&obj->head->text, &STRING("<"))) {
446 compile_cmp_list(OP_LESS, obj->tail);
447 } else if (sv_equal(&obj->head->text, &STRING(">="))) {
448 compile_cmp_list(OP_GREATER_EQ, obj->tail);
449 } else if (sv_equal(&obj->head->text, &STRING("<="))) {
450 compile_cmp_list(OP_LESS_EQ, obj->tail);
451 } else if (sv_equal(&obj->head->text, &STRING("cons"))) {
452 compile_cons(obj->tail);
453 } else if (sv_equal(&obj->head->text, &STRING("car"))) {
454 compile_car(obj->tail);
455 } else if (sv_equal(&obj->head->text, &STRING("cdr"))) {
456 compile_cdr(obj->tail);
457 } else {
458 compile_call(obj);
459 }
460}
461
462void
463compile_if(Object *obj) {
464 context_printf(" ;; --> compile_if\n");
465 char *lab_false = generate_label("BDLL");
466 compile_object(obj->condition);
467 context_printf(" pop rax\n");
468 context_printf(" cmp rax, FALSE_VAL\n");
469 context_printf(" je %s\n", lab_false);
470 compile_object(obj->expr_true);
471 if (obj->expr_false != NULL) {
472 char *lab_exit = generate_label("BDLL");
473 context_printf(" jmp %s\n", lab_exit);
474 context_printf("%s:\n", lab_false);
475 compile_object(obj->expr_false);
476 context_printf("%s:\n", lab_exit);
477 } else {
478 context_printf("%s:\n", lab_false);
479 }
480 context_printf(" ;; <-- compile_if\n");
481}
482
483void
484compile_string(Object *obj) {
485 context_printf(" ;; --> compile_string\n");
486 Constant c;
487
488 // Check if the string is already stored as a constant.
489 ssize_t idx = -1;
490 for (size_t i = 0; i < array_size(constants); i++) {
491 c = constants[i];
492 if (object_equal(c.obj, obj)) {
493 idx = i;
494 break;
495 }
496 }
497 if (idx < 0) {
498 idx = array_size(constants);
499 c = (Constant){
500 .obj = obj,
501 .label = generate_label("BDLC"),
502 };
503 array_push(constants, c);
504 }
505
506 // Create a tagged pointer to the label.
507 context_printf(" mov rax, %s\n", c.label);
508 context_printf(" or rax, STRING_TAG\n");
509 context_printf(" push rax\n");
510 context_printf(" ;; <-- compile_string\n");
511}
512
513void
514compile_lambda(Object *obj) {
515 context_printf(" ;; --> compile_lambda\n");
516
517 // Create a new compilation context.
518 char *prev_context = current_context;
519 Environment *prev_env = current_env;
520 current_env = obj->env;
521 current_context = NULL;
522 array_init(current_context, 0);
523
524 char *name = generate_label("BDLP");
525 context_printf("alignb 8\n");
526 context_printf("%s:\n", name);
527
528 // Prepare size vars.
529 size_t n_locals = array_size(current_env->locals);
530 size_t n_params = array_size(current_env->params);
531 size_t n_captured = array_size(current_env->captured);
532
533 // Initialize function call frame.
534 context_printf(" sub rsp, %zu\n", 8 * n_locals);
535 context_printf(" mov rbp, rsp\n");
536
537 // Procedure body.
538 // In case the last expression of a function doesn't return anything (e.g.
539 // a `def` or `display` primitive), we store a sentinel `nil` value at the
540 // end of the stack.
541 //
542 // NOTE: This is probably better handled by a type system that
543 // allows functions to return void, but right now the caller and function
544 // creation expect that all functions return values. Failure to comply with
545 // this convention will result in a corrupted stack.
546 for (size_t i = 0; i < array_size(obj->body) - 1; i++) {
547 compile_object(obj->body[i]);
548 }
549 Object *last_expr = obj->body[array_size(obj->body) - 1];
550
551 // Tail Call Optimization.
552 // TODO: also for if statements
553 // FIXME: only pairs that are not primitives.
554 if (IS_PAIR(last_expr)) {
555 // Discard the previous stack frame.
556 context_printf(" mov rsp, rbp\n");
557
558 size_t old_offset = n_locals + n_captured + n_params;
559 size_t new_offset = compile_call_body(last_expr);
560 context_printf(" mov rdi, [rbp - 8]\n");
561 for (size_t i = 0; i < new_offset + 1; i++) {
562 context_printf(" mov rax, [rbp - 8 * %zu]\n", i + 1);
563 context_printf(" mov [rbp + 8 * %zu], rax\n", old_offset - i);
564 }
565
566 // Set the stack pointer at the end of given parameters.
567 context_printf(" mov rsp, rbp\n");
568 ssize_t offset_diff = old_offset - new_offset;
569 if (offset_diff > 0) {
570 context_printf(" add rsp, 8 * %zu\n", offset_diff);
571 } else {
572 context_printf(" sub rsp, 8 * %zu\n", offset_diff);
573 }
574
575 context_printf(" jmp rdi\n");
576 } else {
577 compile_nil();
578 compile_object(last_expr);
579
580 // Return is stored in the `rax`.
581 context_printf(" pop rax\n");
582
583 // Restore the previous call frame.
584 size_t rp_offset = (n_locals + n_params + n_captured + 1);
585 context_printf(" mov rdi, [rbp + %zu]\n", 8 * rp_offset);
586 context_printf(" mov rsp, rbp\n");
587 context_printf(" add rsp, %zu\n", 8 * n_locals);
588 context_printf(" jmp rdi\n");
589 }
590
591 context_printf("\n");
592
593 // Restore previous compilation context.
594 array_push(procedures, current_context);
595 current_context = prev_context;
596 current_env = prev_env;
597
598 // Add function address.
599 context_printf(" mov rax, %s\n", name);
600 context_printf(" mov [r15], rax\n");
601
602 // Add captured variables to the heap.
603 for (size_t i = 0; i < n_captured; i++) {
604 ssize_t idx = find_var_index(current_env->locals, obj->env->captured[i]);
605 context_printf(" mov rax, rbp\n");
606 context_printf(" add rax, %ld\n", 8 * idx);
607 context_printf(" mov [r15 + %ld], rax\n", 8 * (i + 1));
608 // TODO: What about capturing captured variables or parameters?
609 assert(idx != -1 && "unexpected index");
610 }
611
612 // Create tagged pointer with this lambda procedure.
613 context_printf(" mov rax, r15\n");
614 context_printf(" or rax, %zu\n", LAMBDA_TAG);
615
616 // Push compiled object to the stack.
617 context_printf(" push rax\n");
618
619 // Adjust the heap pointer depending on the number of variables captured.
620 context_printf(" add r15, %ld\n", 8 * (n_captured + 1));
621
622 context_printf(" ;; <-- compile_lambda\n");
623}
624
625void
626compile_def(Object *obj) {
627 context_printf(" ;; --> compile_def\n");
628 compile_object(obj->var_expr);
629 ssize_t idx = find_var_index(current_env->locals, obj->var_name);
630 context_printf(" pop rax\n");
631 context_printf(" mov [rbp + %ld], rax\n", 8 * idx);
632 context_printf(" ;; <-- compile_def\n");
633}
634
635void
636compile_symbol(Object *obj) {
637 context_printf(" ;; --> compile_symbol\n");
638 ssize_t idx = -1;
639
640 // TODO: Is a captured variable?
641 // FIXME: Order might be an issue, for example if the variable was initially
642 // captured but then declared as a local?
643 // (def a 40)
644 // (fun ext ()
645 // (display a)
646 // (def a 10)
647 // (display a))
648 idx = find_var_index(current_env->captured, obj);
649 if (idx != -1) {
650 size_t n_locals = array_size(current_env->locals);
651 size_t n_params = array_size(current_env->params);
652 size_t n_cap = array_size(current_env->captured);
653 size_t offset = 8 * (n_locals + n_params + n_cap - idx - 1);
654 context_printf(" mov rcx, [rbp + %ld]\n", offset);
655 context_printf(" mov rax, [rcx]\n");
656 context_printf(" push rax\n");
657 context_printf(" ;; <-- compile_symbol\n");
658 return;
659 }
660
661 // Is a local variable?
662 idx = find_var_index(current_env->locals, obj);
663 if (idx != -1) {
664 context_printf(" mov rax, [rbp + %ld]\n", 8 * idx);
665 context_printf(" push rax\n");
666 context_printf(" ;; <-- compile_symbol\n");
667 return;
668 }
669
670 // Is a function parameter?
671 idx = find_var_index(current_env->params, obj);
672 if (idx != -1) {
673 size_t n_locals = array_size(current_env->locals);
674 size_t n_params = array_size(current_env->params);
675 size_t offset = 8 * (n_locals + n_params - idx - 1);
676 context_printf(" mov rax, [rbp + %ld]\n", offset);
677 context_printf(" push rax\n");
678 context_printf(" ;; <-- compile_symbol\n");
679 return;
680 }
681
682 assert(idx != -1 && "unexpected index");
683}
684
685void
686compile_object(Object *obj) {
687 switch (obj->type) {
688 case OBJ_TYPE_NIL: { compile_nil(); } break;
689 case OBJ_TYPE_TRUE:
690 case OBJ_TYPE_FALSE: { compile_boolean(obj); } break;
691 case OBJ_TYPE_FIXNUM: { compile_fixnum(obj); } break;
692 case OBJ_TYPE_PAIR: { compile_proc_call(obj); } break;
693 case OBJ_TYPE_STRING: { compile_string(obj); } break;
694 case OBJ_TYPE_IF: { compile_if(obj); } break;
695 case OBJ_TYPE_LAMBDA: { compile_lambda(obj); } break;
696 case OBJ_TYPE_DEF: { compile_def(obj); } break;
697 case OBJ_TYPE_SYMBOL: { compile_symbol(obj); } break;
698 default: break;
699 }
700}
701
702void
703emit_bss_section(void) {
704 printf("section .bss\n");
705 printf("bdl_heap:\n");
706 printf(" resb HEAP_SIZE\n");
707 printf("\n");
708}
709
710void
711emit_data_section(void) {
712 printf("section .data\n");
713 printf("true_str:\n db \"true\", 10\n");
714 printf(" alignb 8\n");
715 printf("false_str:\n db \"false\", 10\n");
716 printf(" alignb 8\n");
717 printf("lambda_str:\n");
718 char lambda_str[] = "#{lambda}";
719 printf(" dq %ld\n", sizeof(lambda_str));
720 printf(" db \"%s\", 10\n", lambda_str);
721 printf(" alignb 8\n");
722 for (size_t i = 0; i < array_size(constants); i++) {
723 // NOTE: Only supporting string constants for now.
724 Constant c = constants[i];
725 int n = c.obj->text.n;
726 // TODO: escape characters maybe?
727 // TODO: quote all strings maybe?
728 printf("%s:\n", c.label);
729 printf(" dq %d\n", n + 1);
730 printf(" db \"%.*s\", 10\n", n, c.obj->text.start);
731 printf(" alignb 8\n");
732 }
733 printf("\n");
734}
735
736void
737compile(Program program) {
738 // Prepare compilation variables.
739 array_init(constants, 0);
740 array_init(labels, 0);
741 array_init(procedures, 0);
742 array_init(current_context, 0);
743 current_env = program.env;
744
745 // Compile program.
746 for (size_t i = 0; i < array_size(program.roots); i++) {
747 Object *root = program.roots[i];
748 compile_object(root);
749 }
750
751 // Base defines.
752 printf("%%define NIL_VAL %zu\n", NIL_VAL);
753 printf("%%define TRUE_VAL %zu\n", TRUE_VAL);
754 printf("%%define FALSE_VAL %zu\n", FALSE_VAL);
755 printf("%%define BOOL_MASK %zu\n", BOOL_MASK);
756 printf("%%define BOOL_TAG %zu\n", BOOL_TAG);
757 printf("%%define BOOL_SHIFT %zu\n", BOOL_SHIFT);
758 printf("%%define FIXNUM_MASK %zu\n", FIXNUM_MASK);
759 printf("%%define FIXNUM_TAG %zu\n", FIXNUM_TAG);
760 printf("%%define FIXNUM_SHIFT %zu\n", FIXNUM_SHIFT);
761 printf("%%define PAIR_MASK %zu\n", PAIR_MASK);
762 printf("%%define PAIR_TAG %zu\n", PAIR_TAG);
763 printf("%%define PTR_MASK %zu\n", PTR_MASK);
764 printf("%%define STRING_MASK %zu\n", STRING_MASK);
765 printf("%%define STRING_TAG %zu\n", STRING_TAG);
766 printf("%%define LAMBDA_MASK %zu\n", LAMBDA_MASK);
767 printf("%%define LAMBDA_TAG %zu\n", LAMBDA_TAG);
768 printf("%%define HEAP_SIZE %zu\n", HEAP_SIZE);
769 printf("\n");
770
771 // Prelude.
772 emit_file(PRELUDE_FILE);
773 printf("\n");
774
775 // Function definitions.
776 for (size_t i = 0; i < array_size(procedures); i++) {
777 char *ctx = procedures[i];
778 for (size_t i = 0; i < array_size(ctx); i++) {
779 putchar(ctx[i]);
780 }
781 }
782
783 // Main context.
784 printf("alignb 8\n");
785 printf("global _start\n");
786 printf("_start:\n");
787
788 // Initialize heap pointer.
789 printf(" mov r15, bdl_heap\n");
790
791 // Initialize main locals.
792 printf(" sub rsp, %zu\n", 8 * array_size(current_env->locals));
793 printf(" mov rbp, rsp\n");
794
795 // Keep the bottom stack value set as NIL_VAL for a default return value.
796 printf(" push NIL_VAL\n");
797 for (size_t i = 0; i < array_size(current_context); i++) {
798 putchar(current_context[i]);
799 }
800
801 // Postlude.
802 emit_file(POSTLUDE_FILE);
803 emit_data_section();
804 emit_bss_section();
805
806 // Clean resources.
807 array_free(constants);
808 for (size_t i = 0; i < array_size(labels); i++) {
809 free(labels[i]);
810 }
811 array_free(labels);
812 for (size_t i = 0; i < array_size(procedures); i++) {
813 array_free(procedures[i]);
814 }
815 array_free(procedures);
816}
817
818#endif // BDL_COMPILER_H