this repo has no description
1// vim: set tabstop=2 shiftwidth=2 textwidth=79 expandtab:
2// gcc -O2 -g -Wall -Wextra -pedantic -fno-strict-aliasing
3// assets/code/lisp/compiling-closures.c
4
5// In general: https://course.ccs.neu.edu/cs4410sp20/#%28part._lectures%29
6// https://course.ccs.neu.edu/cs4410sp20/lec_let-and-stack_notes.html#%28part._let._.Growing_the_language__adding_let%29
7
8// _GNU_SOURCE is used for MAP_ANONYMOUS in sys/mman.h and strdup in string.h
9#define _GNU_SOURCE
10#include <assert.h> // for assert
11#include <stdarg.h>
12#include <stdbool.h> // for bool
13#include <stddef.h> // for NULL
14#include <stdint.h> // for int32_t, etc
15#include <stdio.h> // for getline, fprintf
16#include <string.h> // for memcpy
17#include <sys/mman.h> // for mmap
18#undef _GNU_SOURCE
19
20#include "greatest.h"
21
22#define WARN_UNUSED __attribute__((warn_unused_result))
23
24// Objects
25
26typedef intptr_t word;
27typedef uintptr_t uword;
28
29// These constants are defined in a enum because the right hand side of a
30// statement like
31// static const int kFoo = ...;
32// must be a so-called "Integer Constant Expression". Compilers are required to
33// support a certain set of these expressions, but are not required to support
34// arbitrary arithmetic with other integer constants. Compilers such as gcc
35// before gcc-8 just decided not to play this game, while gcc-8+ and Clang play
36// just fine.
37// Since this arithmetic with constant values works just fine for enums, make
38// all these constants enum values instead.
39// See https://twitter.com/tekknolagi/status/1328449329472835586 for more info.
40enum {
41 kBitsPerByte = 8, // bits
42 kWordSize = sizeof(word), // bytes
43 kBitsPerWord = kWordSize * kBitsPerByte, // bits
44
45 kIntegerTag = 0x0, // 0b00
46 kIntegerTagMask = 0x3, // 0b11
47 kIntegerShift = 2,
48 kIntegerBits = kBitsPerWord - kIntegerShift,
49
50 kImmediateTagMask = 0x3f,
51
52 kCharTag = 0x0f, // 0b00001111
53 kCharMask = 0xff, // 0b11111111
54 kCharShift = 8,
55
56 kBoolTag = 0x1f, // 0b0011111
57 kBoolMask = 0x80, // 0b10000000
58 kBoolShift = 7,
59
60 kNilTag = 0x2f, // 0b101111
61
62 kErrorTag = 0x3f, // 0b111111
63
64 kPairTag = 0x1, // 0b001
65 kSymbolTag = 0x5, // 0b101
66 kClosureTag = 0x6, // 0b110
67 kHeapTagMask = ((uword)0x7), // 0b000...111
68 kHeapPtrMask = ~kHeapTagMask, // 0b1111...1000
69
70 kCarIndex = 0,
71 kCarOffset = kCarIndex * kWordSize,
72 kCdrIndex = kCarIndex + 1,
73 kCdrOffset = kCdrIndex * kWordSize,
74 kPairSize = kCdrOffset + kWordSize,
75
76 kClosureLabelIndex = 0,
77 kClosureLabelOffset = kClosureLabelIndex * kWordSize,
78};
79
80// These are defined as macros because they will not work as static const int
81// constants (per above explanation), and enum constants are only required to
82// be an int wide (per ISO C).
83#define INTEGER_MAX ((1LL << (kIntegerBits - 1)) - 1)
84#define INTEGER_MIN (-(1LL << (kIntegerBits - 1)))
85
86uword Object_encode_integer(word value) {
87 assert(value < INTEGER_MAX && "too big");
88 assert(value > INTEGER_MIN && "too small");
89 return value << kIntegerShift;
90}
91
92word Object_decode_integer(uword value) { return (word)value >> kIntegerShift; }
93
94bool Object_is_integer(uword value) {
95 return (value & kIntegerTagMask) == kIntegerTag;
96}
97
98uword Object_encode_char(char value) {
99 return ((uword)value << kCharShift) | kCharTag;
100}
101
102char Object_decode_char(uword value) {
103 return (value >> kCharShift) & kCharMask;
104}
105
106bool Object_is_char(uword value) {
107 return (value & kImmediateTagMask) == kCharTag;
108}
109
110uword Object_encode_bool(bool value) {
111 return ((uword)value << kBoolShift) | kBoolTag;
112}
113
114bool Object_decode_bool(uword value) { return value & kBoolMask; }
115
116uword Object_true() { return Object_encode_bool(true); }
117
118uword Object_false() { return Object_encode_bool(false); }
119
120uword Object_nil() { return kNilTag; }
121
122uword Object_error() { return kErrorTag; }
123
124uword Object_address(const void *obj) { return (uword)obj & kHeapPtrMask; }
125
126bool Object_is_pair(uword value) { return (value & kHeapTagMask) == kPairTag; }
127
128uword Object_pair_car(uword value) {
129 assert(Object_is_pair(value));
130 return ((uword *)Object_address((void *)value))[kCarIndex];
131}
132
133uword Object_pair_cdr(uword value) {
134 assert(Object_is_pair(value));
135 return ((uword *)Object_address((void *)value))[kCdrIndex];
136}
137
138bool Object_is_closure(uword value) {
139 return (value & kHeapTagMask) == kClosureTag;
140}
141
142uword Object_closure_label(uword value) {
143 assert(Object_is_closure(value));
144 return ((uword *)Object_address((void *)value))[kClosureLabelIndex];
145}
146
147uword Object_closure_freevar(uword value, word n) {
148 assert(Object_is_closure(value));
149 // +1 for label
150 return ((uword *)Object_address((void *)value))[n + 1];
151}
152
153// End Objects
154
155// Buffer
156
157typedef unsigned char byte;
158
159typedef enum {
160 kWritable,
161 kExecutable,
162} BufferState;
163
164typedef struct {
165 byte *address;
166 BufferState state;
167 word len;
168 word capacity;
169 word entrypoint;
170} Buffer;
171
172byte *Buffer_alloc_writable(word capacity) {
173 byte *result = mmap(/*addr=*/NULL, capacity, PROT_READ | PROT_WRITE,
174 MAP_ANONYMOUS | MAP_PRIVATE,
175 /*filedes=*/-1, /*off=*/0);
176 assert(result != MAP_FAILED);
177 return result;
178}
179
180void Buffer_init(Buffer *result, word capacity) {
181 result->address = Buffer_alloc_writable(capacity);
182 assert(result->address != MAP_FAILED);
183 result->state = kWritable;
184 result->len = 0;
185 result->capacity = capacity;
186 result->entrypoint = 0;
187}
188
189word Buffer_len(Buffer *buf) { return buf->len; }
190
191void Buffer_deinit(Buffer *buf) {
192 munmap(buf->address, buf->capacity);
193 buf->address = NULL;
194 buf->len = 0;
195 buf->capacity = 0;
196 buf->entrypoint = 0;
197}
198
199int Buffer_make_executable(Buffer *buf) {
200 int result = mprotect(buf->address, buf->len, PROT_EXEC);
201 buf->state = kExecutable;
202 return result;
203}
204
205byte Buffer_at8(Buffer *buf, word pos) { return buf->address[pos]; }
206
207void Buffer_at_put8(Buffer *buf, word pos, byte b) { buf->address[pos] = b; }
208
209word max(word left, word right) { return left > right ? left : right; }
210
211void Buffer_ensure_capacity(Buffer *buf, word additional_capacity) {
212 if (buf->len + additional_capacity <= buf->capacity) {
213 return;
214 }
215 word new_capacity =
216 max(buf->capacity * 2, buf->capacity + additional_capacity);
217 byte *address = Buffer_alloc_writable(new_capacity);
218 memcpy(address, buf->address, buf->len);
219 int result = munmap(buf->address, buf->capacity);
220 assert(result == 0 && "munmap failed");
221 buf->address = address;
222 buf->capacity = new_capacity;
223}
224
225void Buffer_write8(Buffer *buf, byte b) {
226 Buffer_ensure_capacity(buf, sizeof b);
227 Buffer_at_put8(buf, buf->len++, b);
228}
229
230void Buffer_write32(Buffer *buf, int32_t value) {
231 for (uword i = 0; i < sizeof(value); i++) {
232 Buffer_write8(buf, (value >> (i * kBitsPerByte)) & 0xff);
233 }
234}
235
236void Buffer_at_put32(Buffer *buf, word offset, int32_t value) {
237 for (uword i = 0; i < sizeof(value); i++) {
238 Buffer_at_put8(buf, offset + i, (value >> (i * kBitsPerByte)) & 0xff);
239 }
240}
241
242void Buffer_write_arr(Buffer *buf, const byte *arr, word arr_size) {
243 Buffer_ensure_capacity(buf, arr_size);
244 for (word i = 0; i < arr_size; i++) {
245 Buffer_write8(buf, arr[i]);
246 }
247}
248
249void Buffer_dump(Buffer *buf, FILE *fp) {
250 for (word i = 0; i < Buffer_len(buf); i++) {
251 fprintf(fp, "%.2x ", buf->address[i]);
252 }
253 fprintf(fp, "\n");
254}
255
256// End Buffer
257
258// Emit
259
260typedef enum {
261 kRax = 0,
262 kRcx,
263 kRdx,
264 kRbx,
265 kRsp,
266 kRbp,
267 kRsi,
268 kRdi,
269} Register;
270
271typedef enum {
272 kAl = 0,
273 kCl,
274 kDl,
275 kBl,
276 kAh,
277 kCh,
278 kDh,
279 kBh,
280} PartialRegister;
281
282typedef enum {
283 kOverflow = 0,
284 kNotOverflow,
285 kBelow,
286 kCarry = kBelow,
287 kNotAboveOrEqual = kBelow,
288 kAboveOrEqual,
289 kNotBelow = kAboveOrEqual,
290 kNotCarry = kAboveOrEqual,
291 kEqual,
292 kZero = kEqual,
293 kLess = 0xc,
294 kNotGreaterOrEqual = kLess,
295 // TODO(max): Add more
296} Condition;
297
298typedef enum {
299 kModDeref = 0,
300 kModDisp8,
301 kModDisp32,
302 kModDirect,
303} Mod;
304
305typedef enum {
306 kNear = 2,
307 kFar = 3,
308} CallDistance;
309
310typedef struct Indirect {
311 Register reg;
312 word disp;
313} Indirect;
314
315Indirect Ind(Register reg, word disp) {
316 return (Indirect){.reg = reg, .disp = disp};
317}
318
319// [ Instruction Prefixes (1 byte, optional) ]
320// [ Opcode (1, 2, or 3 bytes) ]
321// [ ModR/M (1 byte, if required) ]
322// [ Scale-Index-Base (1 byte, if required) ]
323// [ Displacement (1, 2, or 4 bytes, if required) ]
324// [ Immediate data (1, 2, or 4 bytes, if required) ]
325
326// http://www.c-jump.com/CIS77/CPU/x86/lecture.html
327// https://wiki.osdev.org/X86-64_Instruction_Encoding
328
329enum {
330 kRexPrefix = 0x48,
331};
332
333typedef enum {
334 Scale1 = 0,
335 Scale2,
336 Scale4,
337 Scale8,
338} Scale;
339
340typedef enum {
341 kIndexRax = 0,
342 kIndexRcx,
343 kIndexRdx,
344 kIndexRbx,
345 kIndexNone,
346 kIndexRbp,
347 kIndexRsi,
348 kIndexRdi
349} Index;
350
351byte modrm(byte mod, byte rm, byte reg) {
352 return ((mod & 0x3) << 6) | ((reg & 0x7) << 3) | (rm & 0x7);
353}
354
355byte sib(Register base, Index index, Scale scale) {
356 return ((scale & 0x3) << 6) | ((index & 0x7) << 3) | (base & 0x7);
357}
358
359void Emit_mov_reg_imm32(Buffer *buf, Register dst, int32_t src) {
360 Buffer_write8(buf, kRexPrefix);
361 Buffer_write8(buf, 0xc7);
362 Buffer_write8(buf, modrm(kModDirect, dst, 0));
363 Buffer_write32(buf, src);
364}
365
366void Emit_ret(Buffer *buf) { Buffer_write8(buf, 0xc3); }
367
368void Emit_add_reg_imm32(Buffer *buf, Register dst, int32_t src) {
369 Buffer_write8(buf, kRexPrefix);
370 if (dst == kRax) {
371 // Optimization: add eax, {imm32} can either be encoded as 05 {imm32} or 81
372 // c0 {imm32}.
373 Buffer_write8(buf, 0x05);
374 } else {
375 Buffer_write8(buf, 0x81);
376 Buffer_write8(buf, modrm(kModDirect, dst, 0));
377 }
378 Buffer_write32(buf, src);
379}
380
381void Emit_sub_reg_imm32(Buffer *buf, Register dst, int32_t src) {
382 Buffer_write8(buf, kRexPrefix);
383 if (dst == kRax) {
384 // Optimization: sub eax, {imm32} can either be encoded as 2d {imm32} or 81
385 // e8 {imm32}.
386 Buffer_write8(buf, 0x2d);
387 } else {
388 Buffer_write8(buf, 0x81);
389 Buffer_write8(buf, modrm(kModDirect, dst, 5));
390 }
391 Buffer_write32(buf, src);
392}
393
394void Emit_shl_reg_imm8(Buffer *buf, Register dst, int8_t bits) {
395 Buffer_write8(buf, kRexPrefix);
396 Buffer_write8(buf, 0xc1);
397 Buffer_write8(buf, modrm(kModDirect, dst, 4));
398 Buffer_write8(buf, bits);
399}
400
401void Emit_shr_reg_imm8(Buffer *buf, Register dst, int8_t bits) {
402 Buffer_write8(buf, kRexPrefix);
403 Buffer_write8(buf, 0xc1);
404 Buffer_write8(buf, modrm(kModDirect, dst, 5));
405 Buffer_write8(buf, bits);
406}
407
408void Emit_or_reg_imm8(Buffer *buf, Register dst, uint8_t tag) {
409 Buffer_write8(buf, kRexPrefix);
410 Buffer_write8(buf, 0x83);
411 Buffer_write8(buf, modrm(kModDirect, dst, 1));
412 Buffer_write8(buf, tag);
413}
414
415void Emit_and_reg_imm8(Buffer *buf, Register dst, uint8_t tag) {
416 Buffer_write8(buf, kRexPrefix);
417 Buffer_write8(buf, 0x83);
418 Buffer_write8(buf, modrm(kModDirect, dst, 4));
419 Buffer_write8(buf, tag);
420}
421
422void Emit_cmp_reg_imm32(Buffer *buf, Register left, int32_t right) {
423 Buffer_write8(buf, kRexPrefix);
424 if (left == kRax) {
425 // Optimization: cmp rax, {imm32} can either be encoded as 3d {imm32} or 81
426 // f8 {imm32}.
427 Buffer_write8(buf, 0x3d);
428 } else {
429 Buffer_write8(buf, 0x81);
430 Buffer_write8(buf, modrm(kModDirect, left, 7));
431 }
432 Buffer_write32(buf, right);
433}
434
435void Emit_setcc_imm8(Buffer *buf, Condition cond, PartialRegister dst) {
436 // TODO(max): Emit a REX prefix if we need anything above RDI.
437 Buffer_write8(buf, 0x0f);
438 Buffer_write8(buf, 0x90 + cond);
439 Buffer_write8(buf, 0xc0 + (dst & 0x7));
440}
441
442uint8_t disp8(int8_t disp) { return disp >= 0 ? disp : 0x100 + disp; }
443
444void Emit_address_disp8(Buffer *buf, Register direct, Indirect indirect) {
445 if (indirect.reg == kRsp) {
446 Buffer_write8(buf, modrm(kModDisp8, kIndexNone, direct));
447 Buffer_write8(buf, sib(kRsp, kIndexNone, Scale1));
448 } else {
449 Buffer_write8(buf, modrm(kModDisp8, indirect.reg, direct));
450 }
451 Buffer_write8(buf, disp8(indirect.disp));
452}
453
454// mov [dst+disp], src
455// or
456// mov %src, disp(%dst)
457void Emit_store_reg_indirect(Buffer *buf, Indirect dst, Register src) {
458 Buffer_write8(buf, kRexPrefix);
459 Buffer_write8(buf, 0x89);
460 Emit_address_disp8(buf, src, dst);
461}
462
463// add dst, [src+disp]
464// or
465// add disp(%src), %dst
466void Emit_add_reg_indirect(Buffer *buf, Register dst, Indirect src) {
467 Buffer_write8(buf, kRexPrefix);
468 Buffer_write8(buf, 0x03);
469 Emit_address_disp8(buf, dst, src);
470}
471
472// sub dst, [src+disp]
473// or
474// sub disp(%src), %dst
475void Emit_sub_reg_indirect(Buffer *buf, Register dst, Indirect src) {
476 Buffer_write8(buf, kRexPrefix);
477 Buffer_write8(buf, 0x2b);
478 Emit_address_disp8(buf, dst, src);
479}
480
481// mul rax, [src+disp]
482// or
483// mul disp(%src), %rax
484void Emit_mul_reg_indirect(Buffer *buf, Indirect src) {
485 Buffer_write8(buf, kRexPrefix);
486 Buffer_write8(buf, 0xf7);
487 Emit_address_disp8(buf, /*subop*/ 4, src);
488}
489
490// cmp left, [right+disp]
491// or
492// cmp disp(%right), %left
493void Emit_cmp_reg_indirect(Buffer *buf, Register left, Indirect right) {
494 Buffer_write8(buf, kRexPrefix);
495 Buffer_write8(buf, 0x3b);
496 Emit_address_disp8(buf, left, right);
497}
498
499// mov dst, [src+disp]
500// or
501// mov disp(%src), %dst
502void Emit_load_reg_indirect(Buffer *buf, Register dst, Indirect src) {
503 Buffer_write8(buf, kRexPrefix);
504 Buffer_write8(buf, 0x8b);
505 Emit_address_disp8(buf, dst, src);
506}
507
508uint32_t disp32(int32_t disp) { return disp >= 0 ? disp : 0x100000000 + disp; }
509
510word Emit_jcc(Buffer *buf, Condition cond, int32_t offset) {
511 Buffer_write8(buf, 0x0f);
512 Buffer_write8(buf, 0x80 + cond);
513 word pos = Buffer_len(buf);
514 Buffer_write32(buf, disp32(offset));
515 return pos;
516}
517
518word Emit_jmp(Buffer *buf, int32_t offset) {
519 Buffer_write8(buf, 0xe9);
520 word pos = Buffer_len(buf);
521 Buffer_write32(buf, disp32(offset));
522 return pos;
523}
524
525void Emit_backpatch_imm32(Buffer *buf, int32_t target_pos) {
526 word current_pos = Buffer_len(buf);
527 word relative_pos = current_pos - target_pos - sizeof(int32_t);
528 Buffer_at_put32(buf, target_pos, disp32(relative_pos));
529}
530
531void Emit_mov_reg_reg(Buffer *buf, Register dst, Register src) {
532 Buffer_write8(buf, kRexPrefix);
533 Buffer_write8(buf, 0x89);
534 Buffer_write8(buf, modrm(kModDirect, dst, src));
535}
536
537// mov [dst+disp], imm32
538// or
539// mov imm32, disp(%dst)
540void Emit_store_indirect_imm32(Buffer *buf, Indirect dst, int32_t src) {
541 Buffer_write8(buf, kRexPrefix);
542 Buffer_write8(buf, 0xc7);
543 Emit_address_disp8(buf, /*/0*/ 0, dst);
544 Buffer_write32(buf, src);
545}
546
547void Emit_rsp_adjust(Buffer *buf, word adjust) {
548 if (adjust < 0) {
549 Emit_sub_reg_imm32(buf, kRsp, -adjust);
550 } else if (adjust > 0) {
551 Emit_add_reg_imm32(buf, kRsp, adjust);
552 }
553}
554
555void Emit_call_imm32(Buffer *buf, word absolute_address) {
556 // 5 is length of call instruction
557 word relative_address = absolute_address - (Buffer_len(buf) + 5);
558 Buffer_write8(buf, 0xe8);
559 Buffer_write32(buf, relative_address);
560}
561
562void Emit_call_indirect(Buffer *buf, Indirect target) {
563 assert(target.reg != kRsp);
564 Buffer_write8(buf, 0xff);
565 Buffer_write8(buf, modrm(kModDisp32, target.reg, kNear));
566 Buffer_write32(buf, target.disp);
567}
568
569void Emit_call_reg(Buffer *buf, Register reg) {
570 assert(reg != kRsp);
571 Buffer_write8(buf, 0xff);
572 Buffer_write8(buf, modrm(kModDirect, reg, kNear));
573}
574
575// End Emit
576
577// AST
578
579typedef struct ASTNode ASTNode;
580
581typedef struct Pair {
582 ASTNode *car;
583 ASTNode *cdr;
584} Pair;
585
586typedef struct Symbol {
587 word length;
588 char cstr[];
589} Symbol;
590
591bool AST_is_integer(ASTNode *node) {
592 return ((uword)node & kIntegerTagMask) == kIntegerTag;
593}
594
595word AST_get_integer(ASTNode *node) {
596 return Object_decode_integer((uword)node);
597}
598
599ASTNode *AST_new_integer(word value) {
600 return (ASTNode *)Object_encode_integer(value);
601}
602
603bool AST_is_char(ASTNode *node) {
604 return ((uword)node & kImmediateTagMask) == kCharTag;
605}
606
607char AST_get_char(ASTNode *node) { return Object_decode_char((uword)node); }
608
609ASTNode *AST_new_char(char value) {
610 return (ASTNode *)Object_encode_char(value);
611}
612
613bool AST_is_bool(ASTNode *node) {
614 return ((uword)node & kImmediateTagMask) == kBoolTag;
615}
616
617bool AST_get_bool(ASTNode *node) { return Object_decode_bool((uword)node); }
618
619ASTNode *AST_new_bool(bool value) {
620 return (ASTNode *)Object_encode_bool(value);
621}
622
623bool AST_is_nil(ASTNode *node) { return (uword)node == Object_nil(); }
624
625ASTNode *AST_nil() { return (ASTNode *)Object_nil(); }
626
627bool AST_is_error(ASTNode *node) { return (uword)node == Object_error(); }
628
629ASTNode *AST_error() { return (ASTNode *)Object_error(); }
630
631ASTNode *AST_heap_alloc(unsigned char tag, uword size) {
632 // Initialize to 0
633 void *address = calloc(size, 1);
634 assert(address != NULL && "allocation failed");
635 return (ASTNode *)((uword)address | tag);
636}
637
638bool AST_is_heap_object(ASTNode *node) {
639 // For some reason masking out the tag first and then doing the comparison
640 // makes this branchless
641 unsigned char tag = (uword)node & kHeapTagMask;
642 // Heap object tags are between 0b001 and 0b110 except for 0b100 (which is an
643 // integer)
644 return (tag & kIntegerTagMask) > 0 && (tag & kImmediateTagMask) != 0x7;
645}
646
647void AST_pair_set_car(ASTNode *node, ASTNode *car);
648void AST_pair_set_cdr(ASTNode *node, ASTNode *cdr);
649
650ASTNode *AST_new_pair(ASTNode *car, ASTNode *cdr) {
651 ASTNode *node = AST_heap_alloc(kPairTag, sizeof(Pair));
652 AST_pair_set_car(node, car);
653 AST_pair_set_cdr(node, cdr);
654 return node;
655}
656
657bool AST_is_pair(ASTNode *node) {
658 return ((uword)node & kHeapTagMask) == kPairTag;
659}
660
661Pair *AST_as_pair(ASTNode *node) {
662 assert(AST_is_pair(node));
663 return (Pair *)Object_address(node);
664}
665
666ASTNode *AST_pair_car(ASTNode *node) { return AST_as_pair(node)->car; }
667
668void AST_pair_set_car(ASTNode *node, ASTNode *car) {
669 AST_as_pair(node)->car = car;
670}
671
672ASTNode *AST_pair_cdr(ASTNode *node) { return AST_as_pair(node)->cdr; }
673
674void AST_pair_set_cdr(ASTNode *node, ASTNode *cdr) {
675 AST_as_pair(node)->cdr = cdr;
676}
677
678void AST_heap_free(ASTNode *node) {
679 if (!AST_is_heap_object(node)) {
680 return;
681 }
682 if (AST_is_pair(node)) {
683 AST_heap_free(AST_pair_car(node));
684 AST_heap_free(AST_pair_cdr(node));
685 }
686 free((void *)Object_address(node));
687}
688
689Symbol *AST_as_symbol(ASTNode *node);
690
691ASTNode *AST_new_symbol(const char *str) {
692 word data_length = strlen(str) + 1; // for NUL
693 ASTNode *node = AST_heap_alloc(kSymbolTag, sizeof(Symbol) + data_length);
694 Symbol *s = AST_as_symbol(node);
695 s->length = data_length;
696 memcpy(s->cstr, str, data_length);
697 return node;
698}
699
700bool AST_is_symbol(ASTNode *node) {
701 return ((uword)node & kHeapTagMask) == kSymbolTag;
702}
703
704Symbol *AST_as_symbol(ASTNode *node) {
705 assert(AST_is_symbol(node));
706 return (Symbol *)Object_address(node);
707}
708
709const char *AST_symbol_cstr(ASTNode *node) {
710 return (const char *)AST_as_symbol(node)->cstr;
711}
712
713bool AST_symbol_matches(ASTNode *node, const char *cstr) {
714 return strcmp(AST_symbol_cstr(node), cstr) == 0;
715}
716
717int node_to_str(ASTNode *node, char *buf, word size);
718
719int list_to_str(ASTNode *node, char *buf, word size) {
720 if (AST_is_pair(node)) {
721 word result = 0;
722 result += snprintf(buf + result, size, " ");
723 result += node_to_str(AST_pair_car(node), buf + result, size);
724 result += list_to_str(AST_pair_cdr(node), buf + result, size);
725 return result;
726 }
727 if (AST_is_nil(node)) {
728 return snprintf(buf, size, ")");
729 }
730 word result = 0;
731 result += snprintf(buf + result, size, " . ");
732 result += node_to_str(node, buf + result, size);
733 result += snprintf(buf + result, size, ")");
734 return result;
735}
736
737int node_to_str(ASTNode *node, char *buf, word size) {
738 if (AST_is_integer(node)) {
739 return snprintf(buf, size, "%ld", AST_get_integer(node));
740 }
741 if (AST_is_char(node)) {
742 return snprintf(buf, size, "'%c'", AST_get_char(node));
743 }
744 if (AST_is_bool(node)) {
745 return snprintf(buf, size, "%s", AST_get_bool(node) ? "true" : "false");
746 }
747 if (AST_is_nil(node)) {
748 return snprintf(buf, size, "nil");
749 }
750 if (AST_is_pair(node)) {
751 word result = 0;
752 result += snprintf(buf + result, size, "(");
753 result += node_to_str(AST_pair_car(node), buf + result, size);
754 result += list_to_str(AST_pair_cdr(node), buf + result, size);
755 return result;
756 }
757 if (AST_is_symbol(node)) {
758 return snprintf(buf, size, "%s", AST_symbol_cstr(node));
759 }
760 assert(0 && "unknown ast");
761}
762
763char *AST_to_cstr(ASTNode *node) {
764 int size = node_to_str(node, NULL, 0);
765 char *buf = malloc(size + 1);
766 assert(buf != NULL);
767 node_to_str(node, buf, size + 1);
768 buf[size] = '\0';
769 return buf;
770}
771
772// End AST
773
774// Reader
775
776void advance(word *pos) { ++*pos; }
777
778char next(char *input, word *pos) {
779 advance(pos);
780 return input[*pos];
781}
782
783ASTNode *read_integer(char *input, word *pos, int sign) {
784 word result = 0;
785 for (char c = input[*pos]; isdigit(c); c = next(input, pos)) {
786 result *= 10;
787 result += c - '0';
788 }
789 return AST_new_integer(sign * result);
790}
791
792bool starts_symbol(char c) {
793 switch (c) {
794 case '+':
795 case '-':
796 case '*':
797 case '<':
798 case '>':
799 case '=':
800 case '?':
801 return true;
802 default:
803 return isalpha(c);
804 }
805}
806
807bool is_symbol_char(char c) { return starts_symbol(c) || isdigit(c); }
808
809const word ATOM_MAX = 32;
810
811ASTNode *read_symbol(char *input, word *pos) {
812 char buf[ATOM_MAX + 1]; // +1 for NUL
813 word length = 0;
814 for (length = 0; length < ATOM_MAX && is_symbol_char(input[*pos]); length++) {
815 buf[length] = input[*pos];
816 advance(pos);
817 }
818 buf[length] = '\0';
819 return AST_new_symbol(buf);
820}
821
822ASTNode *read_char(char *input, word *pos) {
823 char c = input[*pos];
824 if (c == '\'') {
825 return AST_error();
826 }
827 advance(pos);
828 if (input[*pos] != '\'') {
829 return AST_error();
830 }
831 advance(pos);
832 return AST_new_char(c);
833}
834
835char skip_whitespace(char *input, word *pos) {
836 char c = '\0';
837 for (c = input[*pos]; isspace(c); c = next(input, pos)) {
838 ;
839 }
840 return c;
841}
842
843ASTNode *read_rec(char *input, word *pos);
844
845ASTNode *read_list(char *input, word *pos) {
846 char c = skip_whitespace(input, pos);
847 if (c == ')') {
848 advance(pos);
849 return AST_nil();
850 }
851 ASTNode *car = read_rec(input, pos);
852 assert(car != AST_error());
853 ASTNode *cdr = read_list(input, pos);
854 assert(cdr != AST_error());
855 return AST_new_pair(car, cdr);
856}
857
858ASTNode *read_rec(char *input, word *pos) {
859 char c = skip_whitespace(input, pos);
860 if (isdigit(c)) {
861 return read_integer(input, pos, /*sign=*/1);
862 }
863 if (c == '-' && isdigit(input[*pos + 1])) {
864 advance(pos);
865 return read_integer(input, pos, /*sign=*/-1);
866 }
867 if (c == '+' && isdigit(input[*pos + 1])) {
868 advance(pos);
869 return read_integer(input, pos, /*sign=*/1);
870 }
871 if (starts_symbol(c)) {
872 return read_symbol(input, pos);
873 }
874 if (c == '\'') {
875 advance(pos); // skip '\''
876 return read_char(input, pos);
877 }
878 if (c == '#' && input[*pos + 1] == 't') {
879 advance(pos); // skip '#'
880 advance(pos); // skip 't'
881 return AST_new_bool(true);
882 }
883 if (c == '#' && input[*pos + 1] == 'f') {
884 advance(pos); // skip '#'
885 advance(pos); // skip 'f'
886 return AST_new_bool(false);
887 }
888 if (c == '(') {
889 advance(pos); // skip '('
890 return read_list(input, pos);
891 }
892 return AST_error();
893}
894
895ASTNode *Reader_read(char *input) {
896 word pos = 0;
897 return read_rec(input, &pos);
898}
899
900// End Reader
901
902// Transformer
903
904ASTNode *operand1(ASTNode *args) { return AST_pair_car(args); }
905
906ASTNode *operand2(ASTNode *args) { return AST_pair_car(AST_pair_cdr(args)); }
907
908ASTNode *operand3(ASTNode *args) {
909 return AST_pair_car(AST_pair_cdr(AST_pair_cdr(args)));
910}
911
912static word gensym_idx = 0;
913
914word gensym_next() { return gensym_idx++; }
915
916void gensym_reset() { gensym_idx = 0; }
917
918const char *gensym() {
919 char buf[128];
920 snprintf(buf, sizeof buf, "f%ld", gensym_next());
921 return strdup(buf);
922}
923
924bool set_contains(ASTNode *set, const char *name) {
925 if (AST_is_nil(set)) {
926 return false;
927 }
928 assert(AST_is_pair(set));
929 ASTNode *elt = AST_pair_car(set);
930 if (AST_symbol_matches(elt, name)) {
931 return true;
932 }
933 return set_contains(AST_pair_cdr(set), name);
934}
935
936ASTNode *set_merge(ASTNode *left, ASTNode *right) {
937 if (AST_is_nil(left)) {
938 return right;
939 }
940 ASTNode *elt = AST_pair_car(left);
941 ASTNode *rest = AST_pair_cdr(left);
942 const char *name = AST_symbol_cstr(elt);
943 if (set_contains(rest, name) || set_contains(right, name)) {
944 return set_merge(rest, right);
945 }
946 return AST_new_pair(elt, set_merge(AST_pair_cdr(left), right));
947}
948
949typedef ASTNode *MapFunction(ASTNode *node);
950
951ASTNode *map(MapFunction fn, ASTNode *node) {
952 if (AST_is_nil(node)) {
953 return node;
954 }
955 ASTNode *elt = AST_pair_car(node);
956 return AST_new_pair((*fn)(elt), map(fn, AST_pair_cdr(node)));
957}
958
959ASTNode *free_in_rec(ASTNode *node, ASTNode *bound) {
960 if (AST_is_integer(node) || AST_is_char(node) || AST_is_bool(node) ||
961 AST_is_nil(node)) {
962 // Nothing free; nothing referenced
963 return AST_nil();
964 }
965 if (AST_is_symbol(node)) {
966 if (AST_symbol_matches(node, "if") || AST_symbol_matches(node, "let") ||
967 AST_symbol_matches(node, "lambda") ||
968 AST_symbol_matches(node, "closure") ||
969 AST_symbol_matches(node, "quote") || AST_symbol_matches(node, "+") ||
970 AST_symbol_matches(node, "apply")) {
971 // Nothing free; special names are not variable references
972 return AST_nil();
973 }
974 if (set_contains(bound, AST_symbol_cstr(node))) {
975 // Nothing free; name is bound
976 return AST_nil();
977 }
978 return AST_new_pair(node, AST_nil());
979 }
980 assert(AST_is_pair(node));
981 ASTNode *callable = AST_pair_car(node);
982 ASTNode *args = AST_pair_cdr(node);
983 if (AST_is_symbol(callable)) {
984 // Handle special forms that bind variables
985 if (AST_symbol_matches(callable, "let")) {
986 ASTNode *bindings = operand1(args);
987 ASTNode *freevars_bindings = AST_nil();
988 ASTNode *bindings_names = map(AST_pair_car, bindings);
989 while (!AST_is_nil(bindings)) {
990 ASTNode *binding_value = AST_pair_cdr(AST_pair_car(bindings));
991 freevars_bindings =
992 set_merge(freevars_bindings, free_in_rec(binding_value, bound));
993 bindings = AST_pair_cdr(bindings);
994 }
995 ASTNode *body = operand2(args);
996 ASTNode *new_bound = set_merge(bindings_names, bound);
997 ASTNode *freevars_body = free_in_rec(body, new_bound);
998 return set_merge(freevars_bindings, freevars_body);
999 }
1000 if (AST_symbol_matches(callable, "lambda")) {
1001 ASTNode *params = operand1(args);
1002 ASTNode *body = operand2(args);
1003 return free_in_rec(body, set_merge(bound, params));
1004 }
1005 }
1006 // Handle some call (fn arg0 arg1 ...)
1007 ASTNode *freevars = free_in_rec(callable, bound);
1008 while (!AST_is_nil(args)) {
1009 assert(AST_is_pair(args));
1010 ASTNode *arg = AST_pair_car(args);
1011 freevars = set_merge(freevars, free_in_rec(arg, bound));
1012 args = AST_pair_cdr(args);
1013 }
1014 return freevars;
1015}
1016
1017ASTNode *free_in(ASTNode *node) { return free_in_rec(node, AST_nil()); }
1018
1019ASTNode *list1(ASTNode *item0) { return AST_new_pair(item0, AST_nil()); }
1020
1021ASTNode *list2(ASTNode *item0, ASTNode *item1) {
1022 return AST_new_pair(item0, list1(item1));
1023}
1024
1025ASTNode *list3(ASTNode *item0, ASTNode *item1, ASTNode *item2) {
1026 return AST_new_pair(item0, list2(item1, item2));
1027}
1028
1029ASTNode *list4(ASTNode *item0, ASTNode *item1, ASTNode *item2, ASTNode *item3) {
1030 return AST_new_pair(item0, list3(item1, item2, item3));
1031}
1032
1033bool is_tagged_with(ASTNode *node, const char *expected) {
1034 if (!AST_is_pair(node)) {
1035 return false;
1036 }
1037 ASTNode *tag = AST_pair_car(node);
1038 if (!AST_is_symbol(tag)) {
1039 return false;
1040 }
1041 return AST_symbol_matches(tag, expected);
1042}
1043
1044ASTNode *Transform(ASTNode *node);
1045
1046ASTNode *Transform_lambda(ASTNode *node) {
1047 assert(is_tagged_with(node, "lambda"));
1048 ASTNode *args = AST_pair_cdr(node);
1049 ASTNode *params = operand1(args);
1050 ASTNode *body = operand2(args);
1051 ASTNode *freevars = free_in_rec(body, params);
1052 return list4(AST_new_symbol("lambda"), params, freevars, Transform(body));
1053}
1054
1055ASTNode *Transform_binding(ASTNode *binding) {
1056 ASTNode *name = AST_pair_car(binding);
1057 ASTNode *value = AST_pair_car(AST_pair_cdr(binding));
1058 return list2(name, Transform(value));
1059}
1060
1061ASTNode *Transform(ASTNode *node) {
1062 if (AST_is_integer(node) || AST_is_char(node) || AST_is_bool(node) ||
1063 AST_is_nil(node) || AST_is_symbol(node)) {
1064 // Nothing to traverse and transform
1065 return node;
1066 }
1067 if (is_tagged_with(node, "lambda")) {
1068 return Transform_lambda(node);
1069 }
1070 if (is_tagged_with(node, "let")) {
1071 ASTNode *args = AST_pair_cdr(node);
1072 ASTNode *bindings = operand1(args);
1073 ASTNode *body = operand2(args);
1074 return list3(AST_pair_car(node), map(Transform_binding, (bindings)),
1075 Transform(body));
1076 }
1077 return map(Transform, node);
1078}
1079
1080// End Transformer
1081
1082// Env
1083
1084typedef struct Env {
1085 const char *name;
1086 word value;
1087 struct Env *prev;
1088} Env;
1089
1090Env Env_bind(const char *name, word value, Env *prev) {
1091 return (Env){.name = name, .value = value, .prev = prev};
1092}
1093
1094bool Env_find(Env *env, const char *key, word *result) {
1095 if (env == NULL)
1096 return false;
1097 if (strcmp(env->name, key) == 0) {
1098 *result = env->value;
1099 return true;
1100 }
1101 return Env_find(env->prev, key, result);
1102}
1103
1104// End Env
1105
1106// Compile
1107
1108WARN_UNUSED int Compile_expr(Buffer *buf, ASTNode *node, word stack_index,
1109 Env *varenv, Env *labels);
1110
1111#define _(exp) \
1112 do { \
1113 int result = exp; \
1114 if (result != 0) \
1115 return result; \
1116 } while (0)
1117
1118void Compile_compare_imm32(Buffer *buf, int32_t value) {
1119 Emit_cmp_reg_imm32(buf, kRax, value);
1120 Emit_mov_reg_imm32(buf, kRax, 0);
1121 Emit_setcc_imm8(buf, kEqual, kAl);
1122 Emit_shl_reg_imm8(buf, kRax, kBoolShift);
1123 Emit_or_reg_imm8(buf, kRax, kBoolTag);
1124}
1125
1126// This is let, not let*. Therefore we keep track of two environments -- the
1127// parent environment, for evaluating the bindings, and the body environment,
1128// which will have all of the bindings in addition to the parent. This makes
1129// programs like (let ((a 1) (b a)) b) fail.
1130WARN_UNUSED int Compile_let(Buffer *buf, ASTNode *bindings, ASTNode *body,
1131 word stack_index, Env *binding_env, Env *body_env,
1132 Env *labels) {
1133 if (AST_is_nil(bindings)) {
1134 // Base case: no bindings. Compile the body
1135 _(Compile_expr(buf, body, stack_index, body_env, labels));
1136 return 0;
1137 }
1138 assert(AST_is_pair(bindings));
1139 // Get the next binding
1140 ASTNode *binding = AST_pair_car(bindings);
1141 ASTNode *name = AST_pair_car(binding);
1142 assert(AST_is_symbol(name));
1143 ASTNode *binding_expr = AST_pair_car(AST_pair_cdr(binding));
1144 // Compile the binding expression
1145 _(Compile_expr(buf, binding_expr, stack_index, binding_env, labels));
1146 Emit_store_reg_indirect(buf, /*dst=*/Ind(kRsp, stack_index),
1147 /*src=*/kRax);
1148 // Bind the name
1149 Env entry = Env_bind(AST_symbol_cstr(name), stack_index, body_env);
1150 _(Compile_let(buf, AST_pair_cdr(bindings), body, stack_index - kWordSize,
1151 /*binding_env=*/binding_env, /*body_env=*/&entry, labels));
1152 return 0;
1153}
1154
1155const int32_t kLabelPlaceholder = 0xdeadbeef;
1156
1157WARN_UNUSED int Compile_if(Buffer *buf, ASTNode *cond, ASTNode *consequent,
1158 ASTNode *alternate, word stack_index, Env *varenv,
1159 Env *labels) {
1160 _(Compile_expr(buf, cond, stack_index, varenv, labels));
1161 Emit_cmp_reg_imm32(buf, kRax, Object_false());
1162 word alternate_pos = Emit_jcc(buf, kEqual, kLabelPlaceholder); // je alternate
1163 _(Compile_expr(buf, consequent, stack_index, varenv, labels));
1164 word end_pos = Emit_jmp(buf, kLabelPlaceholder); // jmp end
1165 Emit_backpatch_imm32(buf, alternate_pos); // alternate:
1166 _(Compile_expr(buf, alternate, stack_index, varenv, labels));
1167 Emit_backpatch_imm32(buf, end_pos); // end:
1168 return 0;
1169}
1170
1171const Register kHeapPointer = kRsi;
1172const Register kClosurePointer = kRdi;
1173const Register kCodePointer = kRcx;
1174
1175WARN_UNUSED int Compile_cons(Buffer *buf, ASTNode *car, ASTNode *cdr,
1176 word stack_index, Env *varenv, Env *labels) {
1177 // Compile and store car on the stack
1178 _(Compile_expr(buf, car, stack_index, varenv, labels));
1179 Emit_store_reg_indirect(buf,
1180 /*dst=*/Ind(kRsp, stack_index),
1181 /*src=*/kRax);
1182 // Compile and store cdr
1183 _(Compile_expr(buf, cdr, stack_index - kWordSize, varenv, labels));
1184 Emit_store_reg_indirect(buf, /*dst=*/Ind(kHeapPointer, kCdrOffset),
1185 /*src=*/kRax);
1186 // Fetch car and store in the heap
1187 Emit_load_reg_indirect(buf, /*dst=*/kRax, /*src=*/Ind(kRsp, stack_index));
1188 Emit_store_reg_indirect(buf, /*dst=*/Ind(kHeapPointer, kCarOffset),
1189 /*src=*/kRax);
1190 // Store tagged pointer in rax
1191 // TODO(max): Rewrite as lea rax, [Heap+kPairTag]
1192 Emit_mov_reg_reg(buf, /*dst=*/kRax, /*src=*/kHeapPointer);
1193 Emit_or_reg_imm8(buf, /*dst=*/kRax, kPairTag);
1194 // Bump the heap pointer
1195 Emit_add_reg_imm32(buf, /*dst=*/kHeapPointer, kPairSize);
1196 return 0;
1197}
1198
1199word list_length(ASTNode *node) {
1200 if (AST_is_nil(node)) {
1201 return 0;
1202 }
1203 assert(AST_is_pair(node));
1204 return 1 + list_length(AST_pair_cdr(node));
1205}
1206
1207WARN_UNUSED int Compile_funcall(Buffer *buf, ASTNode *callable, ASTNode *args,
1208 word stack_index, Env *varenv, Env *labels,
1209 word closure_index) {
1210 if (AST_is_nil(args)) {
1211 // This should result in a closure
1212 return Compile_expr(buf, callable, stack_index, varenv, labels);
1213 }
1214 assert(AST_is_pair(args));
1215 ASTNode *arg = AST_pair_car(args);
1216 _(Compile_expr(buf, arg, stack_index, varenv, labels));
1217 Emit_store_reg_indirect(buf, Ind(kRsp, stack_index), kRax);
1218 return Compile_funcall(buf, callable, AST_pair_cdr(args),
1219 stack_index - kWordSize, varenv, labels,
1220 closure_index);
1221}
1222
1223WARN_UNUSED int Compile_closure(Buffer *buf, ASTNode *freevars,
1224 word stack_index, word closure_index,
1225 Env *varenv, Env *labels) {
1226 if (AST_is_nil(freevars)) {
1227 return 0;
1228 }
1229 assert(AST_is_pair(freevars));
1230 ASTNode *freevar = AST_pair_car(freevars);
1231 _(Compile_expr(buf, freevar, stack_index, varenv, labels));
1232 Emit_store_reg_indirect(buf, /*dst=*/Ind(kRsp, stack_index), /*src=*/kRax);
1233 _(Compile_closure(buf, AST_pair_cdr(freevars), stack_index - kWordSize,
1234 closure_index + kWordSize, varenv, labels));
1235 Emit_load_reg_indirect(buf, /*dst=*/kRax, /*src=*/Ind(kRsp, stack_index));
1236 Emit_store_reg_indirect(buf, /*dst=*/Ind(kHeapPointer, closure_index),
1237 /*src=*/kRax);
1238 return 0;
1239}
1240
1241WARN_UNUSED int Compile_call(Buffer *buf, ASTNode *callable, ASTNode *args,
1242 word stack_index, Env *varenv, Env *labels) {
1243 if (AST_is_symbol(callable)) {
1244 if (AST_symbol_matches(callable, "add1")) {
1245 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1246 Emit_add_reg_imm32(buf, kRax, Object_encode_integer(1));
1247 return 0;
1248 }
1249 if (AST_symbol_matches(callable, "sub1")) {
1250 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1251 Emit_sub_reg_imm32(buf, kRax, Object_encode_integer(1));
1252 return 0;
1253 }
1254 if (AST_symbol_matches(callable, "integer->char")) {
1255 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1256 Emit_shl_reg_imm8(buf, kRax, kCharShift - kIntegerShift);
1257 Emit_or_reg_imm8(buf, kRax, kCharTag);
1258 return 0;
1259 }
1260 if (AST_symbol_matches(callable, "char->integer")) {
1261 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1262 Emit_shr_reg_imm8(buf, kRax, kCharShift - kIntegerShift);
1263 return 0;
1264 }
1265 if (AST_symbol_matches(callable, "nil?")) {
1266 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1267 Compile_compare_imm32(buf, Object_nil());
1268 return 0;
1269 }
1270 if (AST_symbol_matches(callable, "zero?")) {
1271 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1272 Compile_compare_imm32(buf, Object_encode_integer(0));
1273 return 0;
1274 }
1275 if (AST_symbol_matches(callable, "not")) {
1276 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1277 // All non #f values are truthy
1278 // ...this might be a problem if we want to make nil falsey
1279 Compile_compare_imm32(buf, Object_false());
1280 return 0;
1281 }
1282 if (AST_symbol_matches(callable, "integer?")) {
1283 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1284 Emit_and_reg_imm8(buf, kRax, kIntegerTagMask);
1285 Compile_compare_imm32(buf, kIntegerTag);
1286 return 0;
1287 }
1288 if (AST_symbol_matches(callable, "boolean?")) {
1289 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1290 Emit_and_reg_imm8(buf, kRax, kImmediateTagMask);
1291 Compile_compare_imm32(buf, kBoolTag);
1292 return 0;
1293 }
1294 if (AST_symbol_matches(callable, "+")) {
1295 _(Compile_expr(buf, operand2(args), stack_index, varenv, labels));
1296 Emit_store_reg_indirect(buf, /*dst=*/Ind(kRsp, stack_index),
1297 /*src=*/kRax);
1298 _(Compile_expr(buf, operand1(args), stack_index - kWordSize, varenv,
1299 labels));
1300 Emit_add_reg_indirect(buf, /*dst=*/kRax, /*src=*/Ind(kRsp, stack_index));
1301 return 0;
1302 }
1303 if (AST_symbol_matches(callable, "-")) {
1304 _(Compile_expr(buf, operand2(args), stack_index, varenv, labels));
1305 Emit_store_reg_indirect(buf, /*dst=*/Ind(kRsp, stack_index),
1306 /*src=*/kRax);
1307 _(Compile_expr(buf, operand1(args), stack_index - kWordSize, varenv,
1308 labels));
1309 Emit_sub_reg_indirect(buf, /*dst=*/kRax, /*src=*/Ind(kRsp, stack_index));
1310 return 0;
1311 }
1312 if (AST_symbol_matches(callable, "*")) {
1313 _(Compile_expr(buf, operand2(args), stack_index, varenv, labels));
1314 // Remove the tag so that the result is still only tagged with 0b00
1315 // instead of 0b0000
1316 Emit_shr_reg_imm8(buf, kRax, kIntegerShift);
1317 Emit_store_reg_indirect(buf, /*dst=*/Ind(kRsp, stack_index),
1318 /*src=*/kRax);
1319 _(Compile_expr(buf, operand1(args), stack_index - kWordSize, varenv,
1320 labels));
1321 Emit_mul_reg_indirect(buf, /*src=*/Ind(kRsp, stack_index));
1322 return 0;
1323 }
1324 if (AST_symbol_matches(callable, "=")) {
1325 _(Compile_expr(buf, operand2(args), stack_index, varenv, labels));
1326 Emit_store_reg_indirect(buf, /*dst=*/Ind(kRsp, stack_index),
1327 /*src=*/kRax);
1328 _(Compile_expr(buf, operand1(args), stack_index - kWordSize, varenv,
1329 labels));
1330 Emit_cmp_reg_indirect(buf, kRax, Ind(kRsp, stack_index));
1331 Emit_mov_reg_imm32(buf, kRax, 0);
1332 Emit_setcc_imm8(buf, kEqual, kAl);
1333 Emit_shl_reg_imm8(buf, kRax, kBoolShift);
1334 Emit_or_reg_imm8(buf, kRax, kBoolTag);
1335 return 0;
1336 }
1337 if (AST_symbol_matches(callable, "<")) {
1338 _(Compile_expr(buf, operand2(args), stack_index, varenv, labels));
1339 Emit_store_reg_indirect(buf, /*dst=*/Ind(kRsp, stack_index),
1340 /*src=*/kRax);
1341 _(Compile_expr(buf, operand1(args), stack_index - kWordSize, varenv,
1342 labels));
1343 Emit_cmp_reg_indirect(buf, kRax, Ind(kRsp, stack_index));
1344 Emit_mov_reg_imm32(buf, kRax, 0);
1345 Emit_setcc_imm8(buf, kLess, kAl);
1346 Emit_shl_reg_imm8(buf, kRax, kBoolShift);
1347 Emit_or_reg_imm8(buf, kRax, kBoolTag);
1348 return 0;
1349 }
1350 if (AST_symbol_matches(callable, "let")) {
1351 return Compile_let(buf, /*bindings=*/operand1(args),
1352 /*body=*/operand2(args), stack_index,
1353 /*binding_env=*/varenv,
1354 /*body_env=*/varenv, labels);
1355 }
1356 if (AST_symbol_matches(callable, "if")) {
1357 return Compile_if(buf, /*condition=*/operand1(args),
1358 /*consequent=*/operand2(args),
1359 /*alternate=*/operand3(args), stack_index, varenv,
1360 labels);
1361 }
1362 if (AST_symbol_matches(callable, "cons")) {
1363 return Compile_cons(buf, /*car=*/operand1(args), /*cdr=*/operand2(args),
1364 stack_index, varenv, labels);
1365 }
1366 if (AST_symbol_matches(callable, "car")) {
1367 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1368 Emit_load_reg_indirect(buf, /*dst=*/kRax,
1369 /*src=*/Ind(kRax, kCarOffset - kPairTag));
1370 return 0;
1371 }
1372 if (AST_symbol_matches(callable, "cdr")) {
1373 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1374 Emit_load_reg_indirect(buf, /*dst=*/kRax,
1375 /*src=*/Ind(kRax, kCdrOffset - kPairTag));
1376 return 0;
1377 }
1378 if (AST_symbol_matches(callable, "closure")) {
1379 // Closures look like (closure label freevar0 freevar1 freevar2 ...)
1380 ASTNode *label = operand1(args);
1381 assert(AST_is_symbol(label));
1382 word code_address;
1383 if (!Env_find(labels, AST_symbol_cstr(label), &code_address)) {
1384 return -1;
1385 }
1386 ASTNode *freevars = AST_pair_cdr(args);
1387 word num_freevars = list_length(freevars);
1388 // TODO(max): Decide if the entire heap should be parseable, and
1389 // code_address should be encoded as an integer, or if it should just
1390 // be output as-is.
1391 _(Compile_closure(buf, freevars, stack_index, /*closure_index=*/kWordSize,
1392 varenv, labels));
1393 // Add the code section address to the section offset of the code
1394 Emit_mov_reg_reg(buf, /*dst=*/kRax, /*src=*/kCodePointer);
1395 Emit_add_reg_imm32(buf, /*dst=*/kRax, code_address);
1396 // Closure is now in kHeapPointer; store the code
1397 Emit_store_reg_indirect(buf,
1398 /*dst=*/Ind(kHeapPointer, kClosureLabelOffset),
1399 /*src=*/kRax);
1400 // Tag the pointer in rax
1401 Emit_mov_reg_reg(buf, /*dst=*/kRax, /*src=*/kHeapPointer);
1402 Emit_or_reg_imm8(buf, /*dst=*/kRax, kClosureTag);
1403 Emit_add_reg_imm32(buf, /*dst=*/kHeapPointer,
1404 (num_freevars + 1) * kWordSize);
1405 return 0;
1406 }
1407 }
1408
1409 // * Evaluate all the arguments and save to the stack
1410 // * Save two stack locations: closure pointer, return point
1411 // * Evaluate the callable/operator
1412 // * Save the old value to the stack in current frame (?)
1413 // * Move the callable's closure pointer to rdi
1414 // * Adjust rsp; make sure return point is at [rsp-0]
1415 // * Call closure[label]
1416 // * Adjust rsp
1417 // * Restore calling frame's rdi from stack
1418
1419 // Skip two spaces on the stack to put the return address and closure
1420 // pointer
1421 word closure_stack_index = stack_index;
1422 word return_point_index = closure_stack_index - kWordSize;
1423 word arg_stack_index = return_point_index - kWordSize;
1424 // Compile arguments and callable
1425 _(Compile_funcall(buf, callable, args, arg_stack_index, varenv, labels,
1426 closure_stack_index));
1427 // Save the current closure pointer to the stack so that it can be restored
1428 // after the call
1429 Emit_store_reg_indirect(buf, /*dst=*/Ind(kRsp, closure_stack_index),
1430 /*src=*/kClosurePointer);
1431 // Set the closure pointer for the new call frame with tag removed
1432 Emit_mov_reg_reg(buf, /*dst=*/kClosurePointer, /*src=*/kRax);
1433 Emit_sub_reg_imm32(buf, kClosurePointer, kClosureTag);
1434 // Put the code pointer in rax so that it can be called
1435 Emit_load_reg_indirect(buf, /*dst=*/kRax,
1436 /*src=*/Ind(kClosurePointer, kClosureLabelOffset));
1437 // We enter Compile_call with a stack_index pointing to the next
1438 // available spot on the stack, where we will put the closure pointer. We
1439 // need to save the locals on the stack as well as the closure pointer. So
1440 // save everything up to *and* including stack_index.
1441 // [ 24: next available ] <-- closure pointer going here
1442 // [ 16: local ]
1443 // [ 8: local ]
1444 // [ 0: return point ]
1445 // stack_index is at 24 and rsp is at 0. Save all three slots.
1446 Emit_rsp_adjust(buf, stack_index);
1447 Emit_call_reg(buf, kRax);
1448 Emit_rsp_adjust(buf, -stack_index);
1449 // Load the saved closure pointer
1450 Emit_load_reg_indirect(buf, /*dst=*/kClosurePointer,
1451 /*src=*/Ind(kRsp, closure_stack_index));
1452 return 0;
1453}
1454
1455WARN_UNUSED int Compile_expr(Buffer *buf, ASTNode *node, word stack_index,
1456 Env *varenv, Env *labels) {
1457 if (AST_is_integer(node)) {
1458 word value = AST_get_integer(node);
1459 Emit_mov_reg_imm32(buf, kRax, Object_encode_integer(value));
1460 return 0;
1461 }
1462 if (AST_is_char(node)) {
1463 char value = AST_get_char(node);
1464 Emit_mov_reg_imm32(buf, kRax, Object_encode_char(value));
1465 return 0;
1466 }
1467 if (AST_is_bool(node)) {
1468 bool value = AST_get_bool(node);
1469 Emit_mov_reg_imm32(buf, kRax, Object_encode_bool(value));
1470 return 0;
1471 }
1472 if (AST_is_nil(node)) {
1473 Emit_mov_reg_imm32(buf, kRax, Object_nil());
1474 return 0;
1475 }
1476 if (AST_is_pair(node)) {
1477 return Compile_call(buf, AST_pair_car(node), AST_pair_cdr(node),
1478 stack_index, varenv, labels);
1479 }
1480 if (AST_is_symbol(node)) {
1481 const char *symbol = AST_symbol_cstr(node);
1482 word offset;
1483 if (Env_find(varenv, symbol, &offset)) {
1484 assert(offset != 0 && "neither stack variables nor closure variables "
1485 "should be at offset 0");
1486 // If the offset is negative, this is a stack-allocated variable.
1487 // Otherwise, it is a closure-allocated variable.
1488 Register reg = offset < 0 ? kRsp : kClosurePointer;
1489 Emit_load_reg_indirect(buf, /*dst=*/kRax, /*src=*/Ind(reg, offset));
1490 return 0;
1491 }
1492 return -1;
1493 }
1494 assert(0 && "unexpected node type");
1495}
1496
1497const byte kEntryPrologue[] = {
1498 // Save the code pointer in rcx, our global code pointer
1499 // mov kCodePointer, rsi
1500 kRexPrefix,
1501 0x8b,
1502 0xce,
1503 // Save the heap in rsi, our global heap pointer
1504 // mov kHeapPointer, rdi
1505 kRexPrefix,
1506 0x89,
1507 0xfe,
1508};
1509
1510const byte kFunctionEpilogue[] = {
1511 // ret
1512 0xc3,
1513};
1514
1515WARN_UNUSED int Compile_code_freevars(Buffer *buf, ASTNode *freevars,
1516 ASTNode *body, word stack_index,
1517 word freevar_index, Env *varenv,
1518 Env *labels) {
1519 if (AST_is_nil(freevars)) {
1520 _(Compile_expr(buf, body, stack_index, varenv, labels));
1521 Buffer_write_arr(buf, kFunctionEpilogue, sizeof kFunctionEpilogue);
1522 return 0;
1523 }
1524 assert(AST_is_pair(freevars));
1525 ASTNode *name = AST_pair_car(freevars);
1526 assert(AST_is_symbol(name));
1527 Env entry = Env_bind(AST_symbol_cstr(name), freevar_index, varenv);
1528 return Compile_code_freevars(buf, AST_pair_cdr(freevars), body, stack_index,
1529 freevar_index + kWordSize, &entry, labels);
1530}
1531
1532WARN_UNUSED int Compile_code_formals(Buffer *buf, ASTNode *formals,
1533 ASTNode *freevars, ASTNode *body,
1534 word stack_index, Env *varenv,
1535 Env *labels) {
1536 if (AST_is_nil(formals)) {
1537 word freevar_index = kWordSize;
1538 return Compile_code_freevars(buf, freevars, body, stack_index,
1539 freevar_index, varenv, labels);
1540 }
1541 assert(AST_is_pair(formals));
1542 ASTNode *name = AST_pair_car(formals);
1543 assert(AST_is_symbol(name));
1544 Env entry = Env_bind(AST_symbol_cstr(name), stack_index, varenv);
1545 return Compile_code_formals(buf, AST_pair_cdr(formals), freevars, body,
1546 stack_index - kWordSize, &entry, labels);
1547}
1548
1549WARN_UNUSED int Compile_code(Buffer *buf, ASTNode *code, Env *labels) {
1550 assert(AST_is_pair(code));
1551 ASTNode *code_sym = AST_pair_car(code);
1552 assert(AST_is_symbol(code_sym));
1553 assert(AST_symbol_matches(code_sym, "code"));
1554 ASTNode *args = AST_pair_cdr(code);
1555 ASTNode *formals = operand1(args);
1556 ASTNode *freevars = operand2(args);
1557 ASTNode *code_body = operand3(args);
1558 return Compile_code_formals(buf, formals, freevars, code_body,
1559 /*stack_index=*/-kWordSize,
1560 /*varenv=*/NULL, labels);
1561}
1562
1563WARN_UNUSED int Compile_labels(Buffer *buf, ASTNode *bindings, ASTNode *body,
1564 Env *labels) {
1565 if (AST_is_nil(bindings)) {
1566 buf->entrypoint = Buffer_len(buf);
1567 // Base case: no bindings. Compile the body
1568 Buffer_write_arr(buf, kEntryPrologue, sizeof kEntryPrologue);
1569 _(Compile_expr(buf, body, /*stack_index=*/-kWordSize, /*varenv=*/NULL,
1570 labels));
1571 Buffer_write_arr(buf, kFunctionEpilogue, sizeof kFunctionEpilogue);
1572 return 0;
1573 }
1574 assert(AST_is_pair(bindings));
1575 // Get the next binding
1576 ASTNode *binding = AST_pair_car(bindings);
1577 ASTNode *name = AST_pair_car(binding);
1578 assert(AST_is_symbol(name));
1579 ASTNode *binding_code = AST_pair_car(AST_pair_cdr(binding));
1580 word function_location = Buffer_len(buf);
1581 // Bind the name to the location in the instruction stream
1582 Env entry = Env_bind(AST_symbol_cstr(name), function_location, labels);
1583 // Compile the binding function
1584 _(Compile_code(buf, binding_code, &entry));
1585 return Compile_labels(buf, AST_pair_cdr(bindings), body, &entry);
1586}
1587
1588WARN_UNUSED int Compile_entry(Buffer *buf, ASTNode *node) {
1589 assert(AST_is_pair(node) && "program must have labels");
1590 // Assume it's (labels ...)
1591 ASTNode *labels_sym = AST_pair_car(node);
1592 assert(AST_is_symbol(labels_sym) && "program must have labels");
1593 assert(AST_symbol_matches(labels_sym, "labels") &&
1594 "program must have labels");
1595 ASTNode *args = AST_pair_cdr(node);
1596 ASTNode *bindings = operand1(args);
1597 assert(AST_is_pair(bindings) || AST_is_nil(bindings));
1598 ASTNode *body = operand2(args);
1599 return Compile_labels(buf, bindings, body, /*labels=*/NULL);
1600}
1601
1602// End Compile
1603
1604typedef uword (*JitFunction)(uword *heap, byte *code);
1605
1606// Testing
1607
1608uword Testing_execute_entry(Buffer *buf, uword *heap) {
1609 assert(buf != NULL);
1610 assert(buf->address != NULL);
1611 assert(buf->state == kExecutable);
1612 // The pointer-pointer cast is allowed but the underlying
1613 // data-to-function-pointer back-and-forth is only guaranteed to work on
1614 // POSIX systems (because of eg dlsym).
1615 byte *start_address = buf->address + buf->entrypoint;
1616 JitFunction function = *(JitFunction *)(&start_address);
1617 return function(heap, buf->address);
1618}
1619
1620uword Testing_execute_expr(Buffer *buf) {
1621 return Testing_execute_entry(buf, /*heap=*/NULL);
1622}
1623
1624TEST Testing_expect_entry_has_contents(Buffer *buf, byte *arr,
1625 size_t arr_size) {
1626 word total_size = sizeof kEntryPrologue + arr_size + sizeof kFunctionEpilogue;
1627 ASSERT_EQ_FMT(total_size, Buffer_len(buf), "%ld");
1628
1629 byte *ptr = buf->address;
1630 ASSERT_MEM_EQ(kEntryPrologue, ptr, sizeof kEntryPrologue);
1631 ptr += sizeof kEntryPrologue;
1632 ASSERT_MEM_EQ(arr, ptr, arr_size);
1633 ptr += arr_size;
1634 ASSERT_MEM_EQ(kFunctionEpilogue, ptr, sizeof kFunctionEpilogue);
1635 ptr += sizeof kFunctionEpilogue;
1636 PASS();
1637}
1638
1639WARN_UNUSED int Testing_compile_expr_entry(Buffer *buf, ASTNode *node) {
1640 Buffer_write_arr(buf, kEntryPrologue, sizeof kEntryPrologue);
1641 _(Compile_expr(buf, node, /*stack_index=*/-kWordSize, /*varenv=*/NULL,
1642 /*labels=*/NULL));
1643 Buffer_write_arr(buf, kFunctionEpilogue, sizeof kFunctionEpilogue);
1644 return 0;
1645}
1646
1647#define EXPECT_EQUALS_BYTES(buf, arr) \
1648 ASSERT_EQ_FMT(sizeof arr, Buffer_len(buf), "%ld"); \
1649 ASSERT_MEM_EQ(arr, (buf)->address, sizeof arr)
1650
1651#define EXPECT_ENTRY_CONTAINS_CODE(buf, arr) \
1652 CHECK_CALL(Testing_expect_entry_has_contents(buf, arr, sizeof arr))
1653
1654#define RUN_BUFFER_TEST(test_name) \
1655 do { \
1656 Buffer buf; \
1657 Buffer_init(&buf, 1); \
1658 GREATEST_RUN_TEST1(test_name, &buf); \
1659 Buffer_deinit(&buf); \
1660 } while (0)
1661
1662#define RUN_HEAP_TEST(test_name) \
1663 do { \
1664 Buffer buf; \
1665 Buffer_init(&buf, 1); \
1666 uword *heap = malloc(1000 * kWordSize); \
1667 GREATEST_RUN_TESTp(test_name, &buf, heap); \
1668 free(heap); \
1669 Buffer_deinit(&buf); \
1670 } while (0)
1671
1672ASTNode *new_unary_call(const char *name, ASTNode *arg) {
1673 return list2(AST_new_symbol(name), arg);
1674}
1675
1676ASTNode *new_binary_call(const char *name, ASTNode *arg0, ASTNode *arg1) {
1677 return list3(AST_new_symbol(name), arg0, arg1);
1678}
1679
1680// End Testing
1681
1682// Tests
1683
1684TEST encode_positive_integer(void) {
1685 ASSERT_EQ(Object_encode_integer(0), 0x0);
1686 ASSERT_EQ(Object_encode_integer(1), 0x4);
1687 ASSERT_EQ(Object_encode_integer(10), 0x28);
1688 PASS();
1689}
1690
1691TEST encode_negative_integer(void) {
1692 ASSERT_EQ(Object_encode_integer(0), 0x0);
1693 ASSERT_EQ(Object_encode_integer(-1), 0xfffffffffffffffc);
1694 ASSERT_EQ(Object_encode_integer(-10), 0xffffffffffffffd8);
1695 PASS();
1696}
1697
1698TEST encode_char(void) {
1699 ASSERT_EQ(Object_encode_char('\0'), 0xf);
1700 ASSERT_EQ(Object_encode_char('a'), 0x610f);
1701 PASS();
1702}
1703
1704TEST decode_char(void) {
1705 ASSERT_EQ(Object_decode_char(0xf), '\0');
1706 ASSERT_EQ(Object_decode_char(0x610f), 'a');
1707 PASS();
1708}
1709
1710TEST encode_bool(void) {
1711 ASSERT_EQ(Object_encode_bool(true), 0x9f);
1712 ASSERT_EQ(Object_encode_bool(false), 0x1f);
1713 ASSERT_EQ(Object_true(), 0x9f);
1714 ASSERT_EQ(Object_false(), 0x1f);
1715 PASS();
1716}
1717
1718TEST decode_bool(void) {
1719 ASSERT_EQ(Object_decode_bool(0x9f), true);
1720 ASSERT_EQ(Object_decode_bool(0x1f), false);
1721 PASS();
1722}
1723
1724TEST address(void) {
1725 ASSERT_EQ(Object_address((void *)0xFF01), 0xFF00);
1726 PASS();
1727}
1728
1729TEST emit_mov_reg_imm32_emits_modrm(Buffer *buf) {
1730 Emit_mov_reg_imm32(buf, kRax, 100);
1731 byte expected[] = {0x48, 0xc7, 0xc0, 0x64, 0x00, 0x00, 0x00};
1732 EXPECT_EQUALS_BYTES(buf, expected);
1733 ASSERT_EQ_FMT(modrm(kModDirect, kRax, 0), 0xc0, "0x%.2x");
1734 PASS();
1735}
1736
1737TEST emit_store_reg_indirect_emits_modrm_sib(Buffer *buf) {
1738 Emit_store_reg_indirect(buf, Ind(kRsp, -8), kRax);
1739 byte expected[] = {0x48, 0x89, 0x44, 0x24, 0xf8};
1740 EXPECT_EQUALS_BYTES(buf, expected);
1741 ASSERT_EQ_FMT(modrm(kModDisp8, kIndexNone, kRax), 0x44, "0x%.2x");
1742 ASSERT_EQ_FMT(sib(kRsp, kIndexNone, Scale1), 0x24, "0x%.2x");
1743 PASS();
1744}
1745
1746TEST ast_new_pair(void) {
1747 ASTNode *node = AST_new_pair(NULL, NULL);
1748 ASSERT(AST_is_pair(node));
1749 AST_heap_free(node);
1750 PASS();
1751}
1752
1753TEST ast_pair_car_returns_car(void) {
1754 ASTNode *node = AST_new_pair(AST_new_integer(123), NULL);
1755 ASTNode *car = AST_pair_car(node);
1756 ASSERT(AST_is_integer(car));
1757 ASSERT_EQ(Object_decode_integer((uword)car), 123);
1758 AST_heap_free(node);
1759 PASS();
1760}
1761
1762TEST ast_pair_cdr_returns_cdr(void) {
1763 ASTNode *node = AST_new_pair(NULL, AST_new_integer(123));
1764 ASTNode *cdr = AST_pair_cdr(node);
1765 ASSERT(AST_is_integer(cdr));
1766 ASSERT_EQ(Object_decode_integer((uword)cdr), 123);
1767 AST_heap_free(node);
1768 PASS();
1769}
1770
1771TEST ast_new_symbol(void) {
1772 const char *value = "my symbol";
1773 ASTNode *node = AST_new_symbol(value);
1774 ASSERT(AST_is_symbol(node));
1775 ASSERT_STR_EQ(AST_symbol_cstr(node), value);
1776 AST_heap_free(node);
1777 PASS();
1778}
1779
1780#define ASSERT_IS_CHAR_EQ(node, c) \
1781 do { \
1782 ASTNode *__tmp = node; \
1783 if (AST_is_error(__tmp)) { \
1784 fprintf(stderr, "Expected a char but got an error.\n"); \
1785 } \
1786 ASSERT(AST_is_char(__tmp)); \
1787 ASSERT_EQ(AST_get_char(__tmp), c); \
1788 } while (0);
1789
1790#define ASSERT_IS_INT_EQ(node, val) \
1791 do { \
1792 ASTNode *__tmp = node; \
1793 if (AST_is_error(__tmp)) { \
1794 fprintf(stderr, "Expected an int but got an error.\n"); \
1795 } \
1796 ASSERT(AST_is_integer(__tmp)); \
1797 ASSERT_EQ(AST_get_integer(__tmp), val); \
1798 } while (0);
1799
1800#define ASSERT_IS_SYM_EQ(node, cstr) \
1801 do { \
1802 ASTNode *__tmp = node; \
1803 if (AST_is_error(__tmp)) { \
1804 fprintf(stderr, "Expected a symbol but got an error.\n"); \
1805 } \
1806 ASSERT(AST_is_symbol(__tmp)); \
1807 ASSERT_STR_EQ(AST_symbol_cstr(__tmp), cstr); \
1808 } while (0);
1809
1810TEST read_with_integer_returns_integer(void) {
1811 char *input = "1234";
1812 ASTNode *node = Reader_read(input);
1813 ASSERT_IS_INT_EQ(node, 1234);
1814 AST_heap_free(node);
1815 PASS();
1816}
1817
1818TEST read_with_negative_integer_returns_integer(void) {
1819 char *input = "-1234";
1820 ASTNode *node = Reader_read(input);
1821 ASSERT_IS_INT_EQ(node, -1234);
1822 AST_heap_free(node);
1823 PASS();
1824}
1825
1826TEST read_with_positive_integer_returns_integer(void) {
1827 char *input = "+1234";
1828 ASTNode *node = Reader_read(input);
1829 ASSERT_IS_INT_EQ(node, 1234);
1830 AST_heap_free(node);
1831 PASS();
1832}
1833
1834TEST read_with_leading_whitespace_ignores_whitespace(void) {
1835 char *input = " \t \n 1234";
1836 ASTNode *node = Reader_read(input);
1837 ASSERT_IS_INT_EQ(node, 1234);
1838 AST_heap_free(node);
1839 PASS();
1840}
1841
1842TEST read_with_symbol_returns_symbol(void) {
1843 char *input = "hello?+-*=>";
1844 ASTNode *node = Reader_read(input);
1845 ASSERT_IS_SYM_EQ(node, "hello?+-*=>");
1846 AST_heap_free(node);
1847 PASS();
1848}
1849
1850TEST read_with_symbol_with_trailing_digits(void) {
1851 char *input = "add1 1";
1852 ASTNode *node = Reader_read(input);
1853 ASSERT_IS_SYM_EQ(node, "add1");
1854 AST_heap_free(node);
1855 PASS();
1856}
1857
1858TEST read_with_char_returns_char(void) {
1859 char *input = "'a'";
1860 ASTNode *node = Reader_read(input);
1861 ASSERT_IS_CHAR_EQ(node, 'a');
1862 ASSERT(AST_is_error(Reader_read("''")));
1863 ASSERT(AST_is_error(Reader_read("'aa'")));
1864 ASSERT(AST_is_error(Reader_read("'aa")));
1865 AST_heap_free(node);
1866 PASS();
1867}
1868
1869TEST read_with_bool_returns_bool(void) {
1870 ASSERT_EQ(Reader_read("#t"), AST_new_bool(true));
1871 ASSERT_EQ(Reader_read("#f"), AST_new_bool(false));
1872 ASSERT(AST_is_error(Reader_read("#")));
1873 ASSERT(AST_is_error(Reader_read("#x")));
1874 ASSERT(AST_is_error(Reader_read("##")));
1875 PASS();
1876}
1877
1878TEST read_with_nil_returns_nil(void) {
1879 char *input = "()";
1880 ASTNode *node = Reader_read(input);
1881 ASSERT(AST_is_nil(node));
1882 AST_heap_free(node);
1883 PASS();
1884}
1885
1886TEST read_with_list_returns_list(void) {
1887 char *input = "( 1 2 0 )";
1888 ASTNode *node = Reader_read(input);
1889 ASSERT(AST_is_pair(node));
1890 ASSERT_IS_INT_EQ(AST_pair_car(node), 1);
1891 ASSERT_IS_INT_EQ(AST_pair_car(AST_pair_cdr(node)), 2);
1892 ASSERT_IS_INT_EQ(AST_pair_car(AST_pair_cdr(AST_pair_cdr(node))), 0);
1893 ASSERT(AST_is_nil(AST_pair_cdr(AST_pair_cdr(AST_pair_cdr(node)))));
1894 AST_heap_free(node);
1895 PASS();
1896}
1897
1898TEST read_with_nested_list_returns_list(void) {
1899 char *input = "((hello world) (foo bar))";
1900 ASTNode *node = Reader_read(input);
1901 ASSERT(AST_is_pair(node));
1902 ASTNode *first = AST_pair_car(node);
1903 ASSERT(AST_is_pair(first));
1904 ASSERT_IS_SYM_EQ(AST_pair_car(first), "hello");
1905 ASSERT_IS_SYM_EQ(AST_pair_car(AST_pair_cdr(first)), "world");
1906 ASSERT(AST_is_nil(AST_pair_cdr(AST_pair_cdr(first))));
1907 ASTNode *second = AST_pair_car(AST_pair_cdr(node));
1908 ASSERT(AST_is_pair(second));
1909 ASSERT_IS_SYM_EQ(AST_pair_car(second), "foo");
1910 ASSERT_IS_SYM_EQ(AST_pair_car(AST_pair_cdr(second)), "bar");
1911 ASSERT(AST_is_nil(AST_pair_cdr(AST_pair_cdr(second))));
1912 AST_heap_free(node);
1913 PASS();
1914}
1915
1916TEST buffer_write8_increases_length(Buffer *buf) {
1917 ASSERT_EQ(Buffer_len(buf), 0);
1918 Buffer_write8(buf, 0xdb);
1919 ASSERT_EQ(Buffer_at8(buf, 0), 0xdb);
1920 ASSERT_EQ(Buffer_len(buf), 1);
1921 PASS();
1922}
1923
1924TEST buffer_write8_expands_buffer(void) {
1925 Buffer buf;
1926 Buffer_init(&buf, 1);
1927 ASSERT_EQ(buf.capacity, 1);
1928 ASSERT_EQ(buf.len, 0);
1929 Buffer_write8(&buf, 0xdb);
1930 Buffer_write8(&buf, 0xef);
1931 ASSERT(buf.capacity > 1);
1932 ASSERT_EQ(buf.len, 2);
1933 Buffer_deinit(&buf);
1934 PASS();
1935}
1936
1937TEST buffer_write32_expands_buffer(void) {
1938 Buffer buf;
1939 Buffer_init(&buf, 1);
1940 ASSERT_EQ(buf.capacity, 1);
1941 ASSERT_EQ(buf.len, 0);
1942 Buffer_write32(&buf, 0xdeadbeef);
1943 ASSERT(buf.capacity > 1);
1944 ASSERT_EQ(buf.len, 4);
1945 Buffer_deinit(&buf);
1946 PASS();
1947}
1948
1949TEST buffer_write32_writes_little_endian(Buffer *buf) {
1950 Buffer_write32(buf, 0xdeadbeef);
1951 ASSERT_EQ(Buffer_at8(buf, 0), 0xef);
1952 ASSERT_EQ(Buffer_at8(buf, 1), 0xbe);
1953 ASSERT_EQ(Buffer_at8(buf, 2), 0xad);
1954 ASSERT_EQ(Buffer_at8(buf, 3), 0xde);
1955 PASS();
1956}
1957
1958TEST compile_positive_integer(Buffer *buf) {
1959 word value = 123;
1960 ASTNode *node = AST_new_integer(value);
1961 int compile_result = Testing_compile_expr_entry(buf, node);
1962 ASSERT_EQ(compile_result, 0);
1963 // mov eax, imm(123)
1964 byte expected[] = {0x48, 0xc7, 0xc0, 0xec, 0x01, 0x00, 0x00};
1965 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1966 Buffer_make_executable(buf);
1967 uword result = Testing_execute_expr(buf);
1968 ASSERT_EQ(result, Object_encode_integer(value));
1969 PASS();
1970}
1971
1972TEST compile_negative_integer(Buffer *buf) {
1973 word value = -123;
1974 ASTNode *node = AST_new_integer(value);
1975 int compile_result = Testing_compile_expr_entry(buf, node);
1976 ASSERT_EQ(compile_result, 0);
1977 // mov eax, imm(-123)
1978 byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0xfe, 0xff, 0xff};
1979 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1980 Buffer_make_executable(buf);
1981 uword result = Testing_execute_expr(buf);
1982 ASSERT_EQ(result, Object_encode_integer(value));
1983 PASS();
1984}
1985
1986TEST compile_char(Buffer *buf) {
1987 char value = 'a';
1988 ASTNode *node = AST_new_char(value);
1989 int compile_result = Testing_compile_expr_entry(buf, node);
1990 ASSERT_EQ(compile_result, 0);
1991 // mov eax, imm('a')
1992 byte expected[] = {0x48, 0xc7, 0xc0, 0x0f, 0x61, 0x00, 0x00};
1993 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1994 Buffer_make_executable(buf);
1995 uword result = Testing_execute_expr(buf);
1996 ASSERT_EQ(result, Object_encode_char(value));
1997 PASS();
1998}
1999
2000TEST compile_true(Buffer *buf) {
2001 ASTNode *node = AST_new_bool(true);
2002 int compile_result = Testing_compile_expr_entry(buf, node);
2003 ASSERT_EQ(compile_result, 0);
2004 // mov eax, imm(true)
2005 byte expected[] = {0x48, 0xc7, 0xc0, 0x9f, 0x0, 0x0, 0x0};
2006 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2007 Buffer_make_executable(buf);
2008 uword result = Testing_execute_expr(buf);
2009 ASSERT_EQ(result, Object_true());
2010 PASS();
2011}
2012
2013TEST compile_false(Buffer *buf) {
2014 ASTNode *node = AST_new_bool(false);
2015 int compile_result = Testing_compile_expr_entry(buf, node);
2016 ASSERT_EQ(compile_result, 0);
2017 // mov eax, imm(false)
2018 byte expected[] = {0x48, 0xc7, 0xc0, 0x1f, 0x00, 0x00, 0x00};
2019 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2020 Buffer_make_executable(buf);
2021 uword result = Testing_execute_expr(buf);
2022 ASSERT_EQ(result, Object_false());
2023 PASS();
2024}
2025
2026TEST compile_nil(Buffer *buf) {
2027 ASTNode *node = AST_nil();
2028 int compile_result = Testing_compile_expr_entry(buf, node);
2029 ASSERT_EQ(compile_result, 0);
2030 // mov eax, imm(nil)
2031 byte expected[] = {0x48, 0xc7, 0xc0, 0x2f, 0x00, 0x00, 0x00};
2032 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2033 Buffer_make_executable(buf);
2034 uword result = Testing_execute_expr(buf);
2035 ASSERT_EQ(result, Object_nil());
2036 PASS();
2037}
2038
2039TEST compile_unary_add1(Buffer *buf) {
2040 ASTNode *node = new_unary_call("add1", AST_new_integer(123));
2041 int compile_result = Testing_compile_expr_entry(buf, node);
2042 ASSERT_EQ(compile_result, 0);
2043 // mov rax, imm(123); add rax, imm(1)
2044 byte expected[] = {0x48, 0xc7, 0xc0, 0xec, 0x01, 0x00, 0x00,
2045 0x48, 0x05, 0x04, 0x00, 0x00, 0x00};
2046 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2047 Buffer_make_executable(buf);
2048 uword result = Testing_execute_expr(buf);
2049 ASSERT_EQ(result, Object_encode_integer(124));
2050 AST_heap_free(node);
2051 PASS();
2052}
2053
2054TEST compile_unary_add1_nested(Buffer *buf) {
2055 ASTNode *node =
2056 new_unary_call("add1", new_unary_call("add1", AST_new_integer(123)));
2057 int compile_result = Testing_compile_expr_entry(buf, node);
2058 ASSERT_EQ(compile_result, 0);
2059 // mov rax, imm(123); add rax, imm(1); add rax, imm(1)
2060 byte expected[] = {0x48, 0xc7, 0xc0, 0xec, 0x01, 0x00, 0x00, 0x48, 0x05, 0x04,
2061 0x00, 0x00, 0x00, 0x48, 0x05, 0x04, 0x00, 0x00, 0x00};
2062 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2063 Buffer_make_executable(buf);
2064 uword result = Testing_execute_expr(buf);
2065 ASSERT_EQ(result, Object_encode_integer(125));
2066 AST_heap_free(node);
2067 PASS();
2068}
2069
2070TEST compile_unary_sub1(Buffer *buf) {
2071 ASTNode *node = new_unary_call("sub1", AST_new_integer(123));
2072 int compile_result = Testing_compile_expr_entry(buf, node);
2073 ASSERT_EQ(compile_result, 0);
2074 // mov rax, imm(123); sub rax, imm(1)
2075 byte expected[] = {0x48, 0xc7, 0xc0, 0xec, 0x01, 0x00, 0x00,
2076 0x48, 0x2d, 0x04, 0x00, 0x00, 0x00};
2077 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2078 Buffer_make_executable(buf);
2079 uword result = Testing_execute_expr(buf);
2080 ASSERT_EQ(result, Object_encode_integer(122));
2081 AST_heap_free(node);
2082 PASS();
2083}
2084
2085TEST compile_unary_integer_to_char(Buffer *buf) {
2086 ASTNode *node = new_unary_call("integer->char", AST_new_integer(97));
2087 int compile_result = Testing_compile_expr_entry(buf, node);
2088 ASSERT_EQ(compile_result, 0);
2089 // mov rax, imm(97); shl rax, 6; or rax, 0xf
2090 byte expected[] = {0x48, 0xc7, 0xc0, 0x84, 0x01, 0x00, 0x00, 0x48,
2091 0xc1, 0xe0, 0x06, 0x48, 0x83, 0xc8, 0x0f};
2092 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2093 Buffer_make_executable(buf);
2094 uword result = Testing_execute_expr(buf);
2095 ASSERT_EQ(result, Object_encode_char('a'));
2096 AST_heap_free(node);
2097 PASS();
2098}
2099
2100TEST compile_unary_char_to_integer(Buffer *buf) {
2101 ASTNode *node = new_unary_call("char->integer", AST_new_char('a'));
2102 int compile_result = Testing_compile_expr_entry(buf, node);
2103 ASSERT_EQ(compile_result, 0);
2104 // mov rax, imm('a'); shr rax, 6
2105 byte expected[] = {0x48, 0xc7, 0xc0, 0x0f, 0x61, 0x00,
2106 0x00, 0x48, 0xc1, 0xe8, 0x06};
2107 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2108 Buffer_make_executable(buf);
2109 uword result = Testing_execute_expr(buf);
2110 ASSERT_EQ(result, Object_encode_integer(97));
2111 AST_heap_free(node);
2112 PASS();
2113}
2114
2115TEST compile_unary_nilp_with_nil_returns_true(Buffer *buf) {
2116 ASTNode *node = new_unary_call("nil?", AST_nil());
2117 int compile_result = Testing_compile_expr_entry(buf, node);
2118 ASSERT_EQ(compile_result, 0);
2119 // 0: 48 c7 c0 2f 00 00 00 mov rax,0x2f
2120 // 7: 48 3d 2f 00 00 00 cmp rax,0x0000002f
2121 // d: 48 c7 c0 00 00 00 00 mov rax,0x0
2122 // 14: 0f 94 c0 sete al
2123 // 17: 48 c1 e0 07 shl rax,0x7
2124 // 1b: 48 83 c8 1f or rax,0x1f
2125 byte expected[] = {0x48, 0xc7, 0xc0, 0x2f, 0x00, 0x00, 0x00, 0x48,
2126 0x3d, 0x2f, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0,
2127 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48,
2128 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
2129 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2130 Buffer_make_executable(buf);
2131 uword result = Testing_execute_expr(buf);
2132 ASSERT_EQ(result, Object_true());
2133 AST_heap_free(node);
2134 PASS();
2135}
2136
2137TEST compile_unary_nilp_with_non_nil_returns_false(Buffer *buf) {
2138 ASTNode *node = new_unary_call("nil?", AST_new_integer(5));
2139 int compile_result = Testing_compile_expr_entry(buf, node);
2140 ASSERT_EQ(compile_result, 0);
2141 // 0: 48 c7 c0 14 00 00 00 mov rax,0x14
2142 // 7: 48 3d 2f 00 00 00 cmp rax,0x0000002f
2143 // d: 48 c7 c0 00 00 00 00 mov rax,0x0
2144 // 14: 0f 94 c0 sete al
2145 // 17: 48 c1 e0 07 shl rax,0x7
2146 // 1b: 48 83 c8 1f or rax,0x1f
2147 byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48,
2148 0x3d, 0x2f, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0,
2149 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48,
2150 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
2151 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2152 Buffer_make_executable(buf);
2153 uword result = Testing_execute_expr(buf);
2154 ASSERT_EQ(result, Object_false());
2155 AST_heap_free(node);
2156 PASS();
2157}
2158
2159TEST compile_unary_zerop_with_zero_returns_true(Buffer *buf) {
2160 ASTNode *node = new_unary_call("zero?", AST_new_integer(0));
2161 int compile_result = Testing_compile_expr_entry(buf, node);
2162 ASSERT_EQ(compile_result, 0);
2163 // 0: 48 c7 c0 00 00 00 00 mov rax,0x0
2164 // 7: 48 3d 00 00 00 00 cmp rax,0x00000000
2165 // d: 48 c7 c0 00 00 00 00 mov rax,0x0
2166 // 14: 0f 94 c0 sete al
2167 // 17: 48 c1 e0 07 shl rax,0x7
2168 // 1b: 48 83 c8 1f or rax,0x1f
2169 byte expected[] = {0x48, 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x48,
2170 0x3d, 0x00, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0,
2171 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48,
2172 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
2173 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2174 Buffer_make_executable(buf);
2175 uword result = Testing_execute_expr(buf);
2176 ASSERT_EQ(result, Object_true());
2177 AST_heap_free(node);
2178 PASS();
2179}
2180
2181TEST compile_unary_zerop_with_non_zero_returns_false(Buffer *buf) {
2182 ASTNode *node = new_unary_call("zero?", AST_new_integer(5));
2183 int compile_result = Testing_compile_expr_entry(buf, node);
2184 ASSERT_EQ(compile_result, 0);
2185 // 0: 48 c7 c0 14 00 00 00 mov rax,0x14
2186 // 7: 48 3d 00 00 00 00 cmp rax,0x00000000
2187 // d: 48 c7 c0 00 00 00 00 mov rax,0x0
2188 // 14: 0f 94 c0 sete al
2189 // 17: 48 c1 e0 07 shl rax,0x7
2190 // 1b: 48 83 c8 1f or rax,0x1f
2191 byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48,
2192 0x3d, 0x00, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0,
2193 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48,
2194 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
2195 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2196 Buffer_make_executable(buf);
2197 uword result = Testing_execute_expr(buf);
2198 ASSERT_EQ(result, Object_false());
2199 AST_heap_free(node);
2200 PASS();
2201}
2202
2203TEST compile_unary_not_with_false_returns_true(Buffer *buf) {
2204 ASTNode *node = new_unary_call("not", AST_new_bool(false));
2205 int compile_result = Testing_compile_expr_entry(buf, node);
2206 ASSERT_EQ(compile_result, 0);
2207 // 0: 48 c7 c0 1f 00 00 00 mov rax,0x1f
2208 // 7: 48 3d 1f 00 00 00 cmp rax,0x0000001f
2209 // d: 48 c7 c0 00 00 00 00 mov rax,0x0
2210 // 14: 0f 94 c0 sete al
2211 // 17: 48 c1 e0 07 shl rax,0x7
2212 // 1b: 48 83 c8 1f or rax,0x1f
2213 byte expected[] = {0x48, 0xc7, 0xc0, 0x1f, 0x00, 0x00, 0x00, 0x48,
2214 0x3d, 0x1f, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0,
2215 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48,
2216 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
2217 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2218 Buffer_make_executable(buf);
2219 uword result = Testing_execute_expr(buf);
2220 ASSERT_EQ(result, Object_true());
2221 AST_heap_free(node);
2222 PASS();
2223}
2224
2225TEST compile_unary_not_with_non_false_returns_false(Buffer *buf) {
2226 ASTNode *node = new_unary_call("not", AST_new_integer(5));
2227 int compile_result = Testing_compile_expr_entry(buf, node);
2228 ASSERT_EQ(compile_result, 0);
2229 // 0: 48 c7 c0 14 00 00 00 mov rax,0x14
2230 // 7: 48 3d 1f 00 00 00 cmp rax,0x0000001f
2231 // d: 48 c7 c0 00 00 00 00 mov rax,0x0
2232 // 14: 0f 94 c0 sete al
2233 // 17: 48 c1 e0 07 shl rax,0x7
2234 // 1b: 48 83 c8 1f or rax,0x1f
2235 byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48,
2236 0x3d, 0x1f, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0,
2237 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48,
2238 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
2239 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2240 Buffer_make_executable(buf);
2241 uword result = Testing_execute_expr(buf);
2242 ASSERT_EQ(result, Object_false());
2243 AST_heap_free(node);
2244 PASS();
2245}
2246
2247TEST compile_unary_integerp_with_integer_returns_true(Buffer *buf) {
2248 ASTNode *node = new_unary_call("integer?", AST_new_integer(5));
2249 int compile_result = Testing_compile_expr_entry(buf, node);
2250 ASSERT_EQ(compile_result, 0);
2251 // 0: 48 c7 c0 14 00 00 00 mov rax,0x14
2252 // 7: 48 83 e0 03 and rax,0x3
2253 // b: 48 3d 00 00 00 00 cmp rax,0x00000000
2254 // 11: 48 c7 c0 00 00 00 00 mov rax,0x0
2255 // 18: 0f 94 c0 sete al
2256 // 1b: 48 c1 e0 07 shl rax,0x7
2257 // 1f: 48 83 c8 1f or rax,0x1f
2258 byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48, 0x83,
2259 0xe0, 0x03, 0x48, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x48,
2260 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0,
2261 0x48, 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
2262 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2263 Buffer_make_executable(buf);
2264 uword result = Testing_execute_expr(buf);
2265 ASSERT_EQ(result, Object_true());
2266 AST_heap_free(node);
2267 PASS();
2268}
2269
2270TEST compile_unary_integerp_with_non_integer_returns_false(Buffer *buf) {
2271 ASTNode *node = new_unary_call("integer?", AST_nil());
2272 int compile_result = Testing_compile_expr_entry(buf, node);
2273 ASSERT_EQ(compile_result, 0);
2274 // 0: 48 c7 c0 2f 00 00 00 mov rax,0x2f
2275 // 7: 48 83 e0 03 and rax,0x3
2276 // b: 48 3d 00 00 00 00 cmp rax,0x00000000
2277 // 11: 48 c7 c0 00 00 00 00 mov rax,0x0
2278 // 18: 0f 94 c0 sete al
2279 // 1b: 48 c1 e0 07 shl rax,0x7
2280 // 1f: 48 83 c8 1f or rax,0x1f
2281 byte expected[] = {0x48, 0xc7, 0xc0, 0x2f, 0x00, 0x00, 0x00, 0x48, 0x83,
2282 0xe0, 0x03, 0x48, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x48,
2283 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0,
2284 0x48, 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
2285 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2286 Buffer_make_executable(buf);
2287 uword result = Testing_execute_expr(buf);
2288 ASSERT_EQ(result, Object_false());
2289 AST_heap_free(node);
2290 PASS();
2291}
2292
2293TEST compile_unary_booleanp_with_boolean_returns_true(Buffer *buf) {
2294 ASTNode *node = new_unary_call("boolean?", AST_new_bool(true));
2295 int compile_result = Testing_compile_expr_entry(buf, node);
2296 ASSERT_EQ(compile_result, 0);
2297 // 0: 48 c7 c0 9f 00 00 00 mov rax,0x9f
2298 // 7: 48 83 e0 3f and rax,0x3f
2299 // b: 48 3d 1f 00 00 00 cmp rax,0x0000001f
2300 // 11: 48 c7 c0 00 00 00 00 mov rax,0x0
2301 // 18: 0f 94 c0 sete al
2302 // 1b: 48 c1 e0 07 shl rax,0x7
2303 // 1f: 48 83 c8 1f or rax,0x1f
2304 byte expected[] = {0x48, 0xc7, 0xc0, 0x9f, 0x00, 0x00, 0x00, 0x48, 0x83,
2305 0xe0, 0x3f, 0x48, 0x3d, 0x1f, 0x00, 0x00, 0x00, 0x48,
2306 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0,
2307 0x48, 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
2308 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2309 Buffer_make_executable(buf);
2310 uword result = Testing_execute_expr(buf);
2311 ASSERT_EQ(result, Object_true());
2312 AST_heap_free(node);
2313 PASS();
2314}
2315
2316TEST compile_unary_booleanp_with_non_boolean_returns_false(Buffer *buf) {
2317 ASTNode *node = new_unary_call("boolean?", AST_new_integer(5));
2318 int compile_result = Testing_compile_expr_entry(buf, node);
2319 ASSERT_EQ(compile_result, 0);
2320 // 0: 48 c7 c0 14 00 00 00 mov rax,0x14
2321 // 7: 48 83 e0 3f and rax,0x3f
2322 // b: 48 3d 1f 00 00 00 cmp rax,0x0000001f
2323 // 11: 48 c7 c0 00 00 00 00 mov rax,0x0
2324 // 18: 0f 94 c0 sete al
2325 // 1b: 48 c1 e0 07 shl rax,0x7
2326 // 1f: 48 83 c8 1f or rax,0x1f
2327 byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48, 0x83,
2328 0xe0, 0x3f, 0x48, 0x3d, 0x1f, 0x00, 0x00, 0x00, 0x48,
2329 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0,
2330 0x48, 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
2331 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2332 Buffer_make_executable(buf);
2333 uword result = Testing_execute_expr(buf);
2334 ASSERT_EQ(result, Object_false());
2335 AST_heap_free(node);
2336 PASS();
2337}
2338
2339TEST compile_binary_plus(Buffer *buf) {
2340 ASTNode *node = new_binary_call("+", AST_new_integer(5), AST_new_integer(8));
2341 int compile_result = Testing_compile_expr_entry(buf, node);
2342 ASSERT_EQ(compile_result, 0);
2343 byte expected[] = {
2344 // 0: 48 c7 c0 20 00 00 00 mov rax,0x20
2345 0x48, 0xc7, 0xc0, 0x20, 0x00, 0x00, 0x00,
2346 // 7: 48 89 45 f8 mov QWORD PTR [rsp-0x8],rax
2347 0x48, 0x89, 0x44, 0x24, 0xf8,
2348 // b: 48 c7 c0 14 00 00 00 mov rax,0x14
2349 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00,
2350 // 12: 48 03 45 f8 add rax,QWORD PTR [rsp-0x8]
2351 0x48, 0x03, 0x44, 0x24, 0xf8};
2352 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2353 Buffer_make_executable(buf);
2354 uword result = Testing_execute_expr(buf);
2355 ASSERT_EQ(result, Object_encode_integer(13));
2356 AST_heap_free(node);
2357 PASS();
2358}
2359
2360TEST compile_binary_plus_nested(Buffer *buf) {
2361 ASTNode *node = new_binary_call(
2362 "+", new_binary_call("+", AST_new_integer(1), AST_new_integer(2)),
2363 new_binary_call("+", AST_new_integer(3), AST_new_integer(4)));
2364 int compile_result = Testing_compile_expr_entry(buf, node);
2365 ASSERT_EQ(compile_result, 0);
2366 byte expected[] = {
2367 // 4: 48 c7 c0 10 00 00 00 mov rax,0x10
2368 0x48, 0xc7, 0xc0, 0x10, 0x00, 0x00, 0x00,
2369 // b: 48 89 45 f8 mov QWORD PTR [rsp-0x8],rax
2370 0x48, 0x89, 0x44, 0x24, 0xf8,
2371 // f: 48 c7 c0 0c 00 00 00 mov rax,0xc
2372 0x48, 0xc7, 0xc0, 0x0c, 0x00, 0x00, 0x00,
2373 // 16: 48 03 45 f8 add rax,QWORD PTR [rsp-0x8]
2374 0x48, 0x03, 0x44, 0x24, 0xf8,
2375 // 1a: 48 89 45 f8 mov QWORD PTR [rsp-0x8],rax
2376 0x48, 0x89, 0x44, 0x24, 0xf8,
2377 // 1e: 48 c7 c0 08 00 00 00 mov rax,0x8
2378 0x48, 0xc7, 0xc0, 0x08, 0x00, 0x00, 0x00,
2379 // 25: 48 89 45 f0 mov QWORD PTR [rsp-0x10],rax
2380 0x48, 0x89, 0x44, 0x24, 0xf0,
2381 // 29: 48 c7 c0 04 00 00 00 mov rax,0x4
2382 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2383 // 30: 48 03 45 f0 add rax,QWORD PTR [rsp-0x10]
2384 0x48, 0x03, 0x44, 0x24, 0xf0,
2385 // 34: 48 03 45 f8 add rax,QWORD PTR [rsp-0x8]
2386 0x48, 0x03, 0x44, 0x24, 0xf8};
2387 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2388 Buffer_make_executable(buf);
2389 uword result = Testing_execute_expr(buf);
2390 ASSERT_EQ(result, Object_encode_integer(10));
2391 AST_heap_free(node);
2392 PASS();
2393}
2394
2395TEST compile_binary_minus(Buffer *buf) {
2396 ASTNode *node = new_binary_call("-", AST_new_integer(5), AST_new_integer(8));
2397 int compile_result = Testing_compile_expr_entry(buf, node);
2398 ASSERT_EQ(compile_result, 0);
2399 byte expected[] = {
2400 // 0: 48 c7 c0 20 00 00 00 mov rax,0x20
2401 0x48, 0xc7, 0xc0, 0x20, 0x00, 0x00, 0x00,
2402 // 7: 48 89 45 f8 mov QWORD PTR [rsp-0x8],rax
2403 0x48, 0x89, 0x44, 0x24, 0xf8,
2404 // b: 48 c7 c0 14 00 00 00 mov rax,0x14
2405 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00,
2406 // 12: 48 2b 45 f8 add rax,QWORD PTR [rsp-0x8]
2407 0x48, 0x2b, 0x44, 0x24, 0xf8};
2408 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2409 Buffer_make_executable(buf);
2410 uword result = Testing_execute_expr(buf);
2411 ASSERT_EQ(result, Object_encode_integer(-3));
2412 AST_heap_free(node);
2413 PASS();
2414}
2415
2416TEST compile_binary_minus_nested(Buffer *buf) {
2417 ASTNode *node = new_binary_call(
2418 "-", new_binary_call("-", AST_new_integer(5), AST_new_integer(1)),
2419 new_binary_call("-", AST_new_integer(4), AST_new_integer(3)));
2420 int compile_result = Testing_compile_expr_entry(buf, node);
2421 ASSERT_EQ(compile_result, 0);
2422 byte expected[] = {
2423 // 4: 48 c7 c0 0c 00 00 00 mov rax,0xc
2424 0x48, 0xc7, 0xc0, 0x0c, 0x00, 0x00, 0x00,
2425 // b: 48 89 45 f8 mov QWORD PTR [rsp-0x8],rax
2426 0x48, 0x89, 0x44, 0x24, 0xf8,
2427 // f: 48 c7 c0 10 00 00 00 mov rax,0x10
2428 0x48, 0xc7, 0xc0, 0x10, 0x00, 0x00, 0x00,
2429 // 16: 48 2b 45 f8 add rax,QWORD PTR [rsp-0x8]
2430 0x48, 0x2b, 0x44, 0x24, 0xf8,
2431 // 1a: 48 89 45 f8 mov QWORD PTR [rsp-0x8],rax
2432 0x48, 0x89, 0x44, 0x24, 0xf8,
2433 // 1e: 48 c7 c0 04 00 00 00 mov rax,0x4
2434 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2435 // 25: 48 89 45 f0 mov QWORD PTR [rsp-0x10],rax
2436 0x48, 0x89, 0x44, 0x24, 0xf0,
2437 // 29: 48 c7 c0 14 00 00 00 mov rax,0x14
2438 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00,
2439 // 30: 48 2b 45 f0 add rax,QWORD PTR [rsp-0x10]
2440 0x48, 0x2b, 0x44, 0x24, 0xf0,
2441 // 34: 48 2b 45 f8 add rax,QWORD PTR [rsp-0x8]
2442 0x48, 0x2b, 0x44, 0x24, 0xf8};
2443 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2444 Buffer_make_executable(buf);
2445 uword result = Testing_execute_expr(buf);
2446 ASSERT_EQ(result, Object_encode_integer(3));
2447 AST_heap_free(node);
2448 PASS();
2449}
2450
2451TEST compile_binary_mul(Buffer *buf) {
2452 ASTNode *node = new_binary_call("*", AST_new_integer(5), AST_new_integer(8));
2453 int compile_result = Testing_compile_expr_entry(buf, node);
2454 ASSERT_EQ(compile_result, 0);
2455 Buffer_make_executable(buf);
2456 uword result = Testing_execute_expr(buf);
2457 ASSERT_EQ_FMT(Object_encode_integer(40), result, "0x%lx");
2458 AST_heap_free(node);
2459 PASS();
2460}
2461
2462TEST compile_binary_mul_nested(Buffer *buf) {
2463 ASTNode *node = new_binary_call(
2464 "*", new_binary_call("*", AST_new_integer(1), AST_new_integer(2)),
2465 new_binary_call("*", AST_new_integer(3), AST_new_integer(4)));
2466 int compile_result = Testing_compile_expr_entry(buf, node);
2467 ASSERT_EQ(compile_result, 0);
2468 Buffer_make_executable(buf);
2469 uword result = Testing_execute_expr(buf);
2470 ASSERT_EQ_FMT(Object_encode_integer(24), result, "0x%lx");
2471 AST_heap_free(node);
2472 PASS();
2473}
2474
2475TEST compile_binary_eq_with_same_address_returns_true(Buffer *buf) {
2476 ASTNode *node = new_binary_call("=", AST_new_integer(5), AST_new_integer(5));
2477 int compile_result = Testing_compile_expr_entry(buf, node);
2478 ASSERT_EQ(compile_result, 0);
2479 Buffer_make_executable(buf);
2480 uword result = Testing_execute_expr(buf);
2481 ASSERT_EQ_FMT(Object_true(), result, "0x%lx");
2482 AST_heap_free(node);
2483 PASS();
2484}
2485
2486TEST compile_binary_eq_with_different_address_returns_false(Buffer *buf) {
2487 ASTNode *node = new_binary_call("=", AST_new_integer(5), AST_new_integer(4));
2488 int compile_result = Testing_compile_expr_entry(buf, node);
2489 ASSERT_EQ(compile_result, 0);
2490 Buffer_make_executable(buf);
2491 uword result = Testing_execute_expr(buf);
2492 ASSERT_EQ_FMT(Object_false(), result, "0x%lx");
2493 AST_heap_free(node);
2494 PASS();
2495}
2496
2497TEST compile_binary_lt_with_left_less_than_right_returns_true(Buffer *buf) {
2498 ASTNode *node = new_binary_call("<", AST_new_integer(-5), AST_new_integer(5));
2499 int compile_result = Testing_compile_expr_entry(buf, node);
2500 ASSERT_EQ(compile_result, 0);
2501 Buffer_make_executable(buf);
2502 uword result = Testing_execute_expr(buf);
2503 ASSERT_EQ_FMT(Object_true(), result, "0x%lx");
2504 AST_heap_free(node);
2505 PASS();
2506}
2507
2508TEST compile_binary_lt_with_left_equal_to_right_returns_false(Buffer *buf) {
2509 ASTNode *node = new_binary_call("<", AST_new_integer(5), AST_new_integer(5));
2510 int compile_result = Testing_compile_expr_entry(buf, node);
2511 ASSERT_EQ(compile_result, 0);
2512 Buffer_make_executable(buf);
2513 uword result = Testing_execute_expr(buf);
2514 ASSERT_EQ_FMT(Object_false(), result, "0x%lx");
2515 AST_heap_free(node);
2516 PASS();
2517}
2518
2519TEST compile_binary_lt_with_left_greater_than_right_returns_false(Buffer *buf) {
2520 ASTNode *node = new_binary_call("<", AST_new_integer(6), AST_new_integer(5));
2521 int compile_result = Testing_compile_expr_entry(buf, node);
2522 ASSERT_EQ(compile_result, 0);
2523 Buffer_make_executable(buf);
2524 uword result = Testing_execute_expr(buf);
2525 ASSERT_EQ_FMT(Object_false(), result, "0x%lx");
2526 AST_heap_free(node);
2527 PASS();
2528}
2529
2530TEST compile_symbol_in_env_returns_value(Buffer *buf) {
2531 ASTNode *node = AST_new_symbol("hello");
2532 Env env0 = Env_bind("hello", -33, /*prev=*/NULL);
2533 Env env1 = Env_bind("world", -66, &env0);
2534 int compile_result =
2535 Compile_expr(buf, node, -kWordSize, &env1, /*labels=*/NULL);
2536 ASSERT_EQ(compile_result, 0);
2537 byte expected[] = {// mov rax, [rsp-33]
2538 0x48, 0x8b, 0x44, 0x24, 0x100 - 33};
2539 EXPECT_EQUALS_BYTES(buf, expected);
2540 AST_heap_free(node);
2541 PASS();
2542}
2543
2544TEST compile_symbol_in_closure_returns_value(Buffer *buf) {
2545 ASTNode *node = AST_new_symbol("hello");
2546 Env env0 = Env_bind("hello", 33, /*prev=*/NULL);
2547 Env env1 = Env_bind("world", 66, &env0);
2548 int compile_result =
2549 Compile_expr(buf, node, -kWordSize, &env1, /*labels=*/NULL);
2550 ASSERT_EQ(compile_result, 0);
2551 byte expected[] = {// mov rax, [Closure+33]
2552 0x48, 0x8b, 0x47, 33};
2553 EXPECT_EQUALS_BYTES(buf, expected);
2554 AST_heap_free(node);
2555 PASS();
2556}
2557
2558TEST compile_symbol_in_env_returns_first_value(Buffer *buf) {
2559 ASTNode *node = AST_new_symbol("hello");
2560 Env env0 = Env_bind("hello", -55, /*prev=*/NULL);
2561 Env env1 = Env_bind("hello", -66, &env0);
2562 int compile_result =
2563 Compile_expr(buf, node, -kWordSize, &env1, /*labels=*/NULL);
2564 ASSERT_EQ(compile_result, 0);
2565 byte expected[] = {// mov rax, [rsp-66]
2566 0x48, 0x8b, 0x44, 0x24, 0x100 - 66};
2567 EXPECT_EQUALS_BYTES(buf, expected);
2568 AST_heap_free(node);
2569 PASS();
2570}
2571
2572TEST compile_symbol_not_in_env_raises_compile_error(Buffer *buf) {
2573 ASTNode *node = AST_new_symbol("hello");
2574 int compile_result =
2575 Compile_expr(buf, node, -kWordSize, /*varenv=*/NULL, /*labels=*/NULL);
2576 ASSERT_EQ(compile_result, -1);
2577 AST_heap_free(node);
2578 PASS();
2579}
2580
2581TEST compile_let_with_no_bindings(Buffer *buf) {
2582 ASTNode *node = Reader_read("(let () (+ 1 2))");
2583 int compile_result = Testing_compile_expr_entry(buf, node);
2584 ASSERT_EQ(compile_result, 0);
2585 Buffer_make_executable(buf);
2586 uword result = Testing_execute_expr(buf);
2587 ASSERT_EQ_FMT(Object_encode_integer(3), result, "0x%lx");
2588 AST_heap_free(node);
2589 PASS();
2590}
2591
2592TEST compile_let_with_one_binding(Buffer *buf) {
2593 ASTNode *node = Reader_read("(let ((a 1)) (+ a 2))");
2594 int compile_result = Testing_compile_expr_entry(buf, node);
2595 ASSERT_EQ(compile_result, 0);
2596 Buffer_make_executable(buf);
2597 uword result = Testing_execute_expr(buf);
2598 ASSERT_EQ_FMT(Object_encode_integer(3), result, "0x%lx");
2599 AST_heap_free(node);
2600 PASS();
2601}
2602
2603TEST compile_let_with_multiple_bindings(Buffer *buf) {
2604 ASTNode *node = Reader_read("(let ((a 1) (b 2)) (+ a b))");
2605 int compile_result = Testing_compile_expr_entry(buf, node);
2606 ASSERT_EQ(compile_result, 0);
2607 Buffer_make_executable(buf);
2608 uword result = Testing_execute_expr(buf);
2609 ASSERT_EQ_FMT(Object_encode_integer(3), result, "0x%lx");
2610 AST_heap_free(node);
2611 PASS();
2612}
2613
2614TEST compile_nested_let(Buffer *buf) {
2615 ASTNode *node = Reader_read("(let ((a 1)) (let ((b 2)) (+ a b)))");
2616 int compile_result = Testing_compile_expr_entry(buf, node);
2617 ASSERT_EQ(compile_result, 0);
2618 Buffer_make_executable(buf);
2619 uword result = Testing_execute_expr(buf);
2620 ASSERT_EQ_FMT(Object_encode_integer(3), result, "0x%lx");
2621 AST_heap_free(node);
2622 PASS();
2623}
2624
2625TEST compile_let_is_not_let_star(Buffer *buf) {
2626 ASTNode *node = Reader_read("(let ((a 1) (b a)) a)");
2627 int compile_result = Testing_compile_expr_entry(buf, node);
2628 ASSERT_EQ(compile_result, -1);
2629 AST_heap_free(node);
2630 PASS();
2631}
2632
2633TEST compile_if_with_true_cond(Buffer *buf) {
2634 ASTNode *node = Reader_read("(if #t 1 2)");
2635 int compile_result = Testing_compile_expr_entry(buf, node);
2636 ASSERT_EQ(compile_result, 0);
2637 byte expected[] = {
2638 // mov rax, 0x9f
2639 0x48, 0xc7, 0xc0, 0x9f, 0x00, 0x00, 0x00,
2640 // cmp rax, 0x1f
2641 0x48, 0x3d, 0x1f, 0x00, 0x00, 0x00,
2642 // je alternate
2643 0x0f, 0x84, 0x0c, 0x00, 0x00, 0x00,
2644 // mov rax, compile(1)
2645 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2646 // jmp end
2647 0xe9, 0x07, 0x00, 0x00, 0x00,
2648 // alternate:
2649 // mov rax, compile(2)
2650 0x48, 0xc7, 0xc0, 0x08, 0x00, 0x00, 0x00
2651 // end:
2652 };
2653 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2654 Buffer_make_executable(buf);
2655 uword result = Testing_execute_expr(buf);
2656 ASSERT_EQ_FMT(Object_encode_integer(1), result, "0x%lx");
2657 AST_heap_free(node);
2658 PASS();
2659}
2660
2661TEST compile_if_with_false_cond(Buffer *buf) {
2662 ASTNode *node = Reader_read("(if #f 1 2)");
2663 int compile_result = Testing_compile_expr_entry(buf, node);
2664 ASSERT_EQ(compile_result, 0);
2665 byte expected[] = {
2666 // mov rax, 0x1f
2667 0x48, 0xc7, 0xc0, 0x1f, 0x00, 0x00, 0x00,
2668 // cmp rax, 0x1f
2669 0x48, 0x3d, 0x1f, 0x00, 0x00, 0x00,
2670 // je alternate
2671 0x0f, 0x84, 0x0c, 0x00, 0x00, 0x00,
2672 // mov rax, compile(1)
2673 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2674 // jmp end
2675 0xe9, 0x07, 0x00, 0x00, 0x00,
2676 // alternate:
2677 // mov rax, compile(2)
2678 0x48, 0xc7, 0xc0, 0x08, 0x00, 0x00, 0x00
2679 // end:
2680 };
2681 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2682 Buffer_make_executable(buf);
2683 uword result = Testing_execute_expr(buf);
2684 ASSERT_EQ_FMT(Object_encode_integer(2), result, "0x%lx");
2685 AST_heap_free(node);
2686 PASS();
2687}
2688
2689TEST compile_nested_if(Buffer *buf) {
2690 ASTNode *node = Reader_read("(if (< 1 2) (if #f 3 4) 5)");
2691 int compile_result = Testing_compile_expr_entry(buf, node);
2692 ASSERT_EQ(compile_result, 0);
2693 Buffer_make_executable(buf);
2694 uword result = Testing_execute_expr(buf);
2695 ASSERT_EQ_FMT(Object_encode_integer(4), result, "0x%lx");
2696 AST_heap_free(node);
2697 PASS();
2698}
2699
2700TEST compile_cons(Buffer *buf, uword *heap) {
2701 ASTNode *node = Reader_read("(cons 1 2)");
2702 int compile_result = Testing_compile_expr_entry(buf, node);
2703 ASSERT_EQ(compile_result, 0);
2704 // clang-format off
2705 byte expected[] = {
2706 // mov rax, 0x2
2707 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2708 // mov [rsp-8], rax
2709 0x48, 0x89, 0x44, 0x24, 0xf8,
2710 // mov rax, 0x4
2711 0x48, 0xc7, 0xc0, 0x08, 0x00, 0x00, 0x00,
2712 // mov [Heap+Cdr], rax
2713 0x48, 0x89, 0x46, 0x08,
2714 // mov rax, [rsp-8]
2715 0x48, 0x8b, 0x44, 0x24, 0xf8,
2716 // mov [Heap+Car], rax
2717 0x48, 0x89, 0x46, 0x00,
2718 // mov rax, Heap
2719 0x48, 0x89, 0xf0,
2720 // or rax, kPairTag
2721 0x48, 0x83, 0xc8, 0x01,
2722 // add Heap, 2*kWordSize
2723 0x48, 0x81, 0xc6, 0x10, 0x00, 0x00, 0x00,
2724 };
2725 // clang-format on
2726 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2727 Buffer_make_executable(buf);
2728 uword result = Testing_execute_entry(buf, heap);
2729 ASSERT(Object_is_pair(result));
2730 ASSERT_EQ_FMT(Object_encode_integer(1), Object_pair_car(result), "0x%lx");
2731 ASSERT_EQ_FMT(Object_encode_integer(2), Object_pair_cdr(result), "0x%lx");
2732 AST_heap_free(node);
2733 PASS();
2734}
2735
2736TEST compile_two_cons(Buffer *buf, uword *heap) {
2737 ASTNode *node = Reader_read(
2738 "(let ((a (cons 1 2)) (b (cons 3 4))) (cons (cdr a) (cdr b)))");
2739 int compile_result = Testing_compile_expr_entry(buf, node);
2740 ASSERT_EQ(compile_result, 0);
2741 Buffer_make_executable(buf);
2742 uword result = Testing_execute_entry(buf, heap);
2743 ASSERT(Object_is_pair(result));
2744 ASSERT_EQ_FMT(Object_encode_integer(2), Object_pair_car(result), "0x%lx");
2745 ASSERT_EQ_FMT(Object_encode_integer(4), Object_pair_cdr(result), "0x%lx");
2746 AST_heap_free(node);
2747 PASS();
2748}
2749
2750TEST compile_nested_cons(Buffer *buf, uword *heap) {
2751 ASTNode *node = Reader_read("(cons (cons 1 2) (cons 3 4))");
2752 int compile_result = Testing_compile_expr_entry(buf, node);
2753 ASSERT_EQ(compile_result, 0);
2754 Buffer_make_executable(buf);
2755 uword result = Testing_execute_entry(buf, heap);
2756 ASSERT(Object_is_pair(result));
2757 ASSERT(Object_is_pair(Object_pair_car(result)));
2758 ASSERT_EQ_FMT(Object_encode_integer(1),
2759 Object_pair_car(Object_pair_car(result)), "0x%lx");
2760 ASSERT_EQ_FMT(Object_encode_integer(2),
2761 Object_pair_cdr(Object_pair_car(result)), "0x%lx");
2762 ASSERT(Object_is_pair(Object_pair_cdr(result)));
2763 ASSERT_EQ_FMT(Object_encode_integer(3),
2764 Object_pair_car(Object_pair_cdr(result)), "0x%lx");
2765 ASSERT_EQ_FMT(Object_encode_integer(4),
2766 Object_pair_cdr(Object_pair_cdr(result)), "0x%lx");
2767 AST_heap_free(node);
2768 PASS();
2769}
2770
2771TEST compile_car(Buffer *buf, uword *heap) {
2772 ASTNode *node = Reader_read("(car (cons 1 2))");
2773 int compile_result = Testing_compile_expr_entry(buf, node);
2774 ASSERT_EQ(compile_result, 0);
2775 // clang-format off
2776 byte expected[] = {
2777 // mov rax, 0x2
2778 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2779 // mov [rsp-8], rax
2780 0x48, 0x89, 0x44, 0x24, 0xf8,
2781 // mov rax, 0x4
2782 0x48, 0xc7, 0xc0, 0x08, 0x00, 0x00, 0x00,
2783 // mov [Heap+Cdr], rax
2784 0x48, 0x89, 0x46, 0x08,
2785 // mov rax, [rsp-8]
2786 0x48, 0x8b, 0x44, 0x24, 0xf8,
2787 // mov [Heap+Car], rax
2788 0x48, 0x89, 0x46, 0x00,
2789 // mov rax, Heap
2790 0x48, 0x89, 0xf0,
2791 // or rax, kPairTag
2792 0x48, 0x83, 0xc8, 0x01,
2793 // add Heap, 2*kWordSize
2794 0x48, 0x81, 0xc6, 0x10, 0x00, 0x00, 0x00,
2795 // mov rax, [rax-1]
2796 0x48, 0x8b, 0x40, 0xff,
2797 };
2798 // clang-format on
2799 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2800 Buffer_make_executable(buf);
2801 uword result = Testing_execute_entry(buf, heap);
2802 ASSERT_EQ_FMT(Object_encode_integer(1), result, "0x%lx");
2803 AST_heap_free(node);
2804 PASS();
2805}
2806
2807TEST compile_cdr(Buffer *buf, uword *heap) {
2808 ASTNode *node = Reader_read("(cdr (cons 1 2))");
2809 int compile_result = Testing_compile_expr_entry(buf, node);
2810 ASSERT_EQ(compile_result, 0);
2811 // clang-format off
2812 byte expected[] = {
2813 // mov rax, 0x2
2814 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2815 // mov [rsp-8], rax
2816 0x48, 0x89, 0x44, 0x24, 0xf8,
2817 // mov rax, 0x4
2818 0x48, 0xc7, 0xc0, 0x08, 0x00, 0x00, 0x00,
2819 // mov [Heap+Cdr], rax
2820 0x48, 0x89, 0x46, 0x08,
2821 // mov rax, [rsp-8]
2822 0x48, 0x8b, 0x44, 0x24, 0xf8,
2823 // mov [Heap+Car], rax
2824 0x48, 0x89, 0x46, 0x00,
2825 // mov rax, Heap
2826 0x48, 0x89, 0xf0,
2827 // or rax, kPairTag
2828 0x48, 0x83, 0xc8, 0x01,
2829 // add Heap, 2*kWordSize
2830 0x48, 0x81, 0xc6, 0x10, 0x00, 0x00, 0x00,
2831 // mov rax, [rax+7]
2832 0x48, 0x8b, 0x40, 0x07,
2833 };
2834 // clang-format on
2835 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2836 Buffer_make_executable(buf);
2837 uword result = Testing_execute_entry(buf, heap);
2838 ASSERT_EQ_FMT(Object_encode_integer(2), result, "0x%lx");
2839 AST_heap_free(node);
2840 PASS();
2841}
2842
2843TEST compile_code_with_no_params(Buffer *buf) {
2844 ASTNode *node = Reader_read("(code () () 1)");
2845 int compile_result = Compile_code(buf, node, /*labels=*/NULL);
2846 ASSERT_EQ(compile_result, 0);
2847 // clang-format off
2848 byte expected[] = {
2849 // mov rax, 0x2
2850 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2851 // ret
2852 0xc3,
2853 };
2854 // clang-format on
2855 EXPECT_EQUALS_BYTES(buf, expected);
2856 Buffer_make_executable(buf);
2857 uword result = Testing_execute_expr(buf);
2858 ASSERT_EQ_FMT(Object_encode_integer(1), result, "0x%lx");
2859 AST_heap_free(node);
2860 PASS();
2861}
2862
2863TEST compile_code_with_one_param(Buffer *buf) {
2864 ASTNode *node = Reader_read("(code (x) () x)");
2865 int compile_result = Compile_code(buf, node, /*labels=*/NULL);
2866 ASSERT_EQ(compile_result, 0);
2867 // clang-format off
2868 byte expected[] = {
2869 // mov rax, [rsp-8]
2870 0x48, 0x8b, 0x44, 0x24, 0xf8,
2871 // ret
2872 0xc3,
2873 };
2874 // clang-format on
2875 EXPECT_EQUALS_BYTES(buf, expected);
2876 AST_heap_free(node);
2877 PASS();
2878}
2879
2880TEST compile_code_with_one_freevar(Buffer *buf) {
2881 ASTNode *node = Reader_read("(code () (x) x)");
2882 int compile_result = Compile_code(buf, node, /*labels=*/NULL);
2883 ASSERT_EQ(compile_result, 0);
2884 // clang-format off
2885 byte expected[] = {
2886 // mov rax, [Closure+8]
2887 0x48, 0x8b, 0x47, 0x08,
2888 // ret
2889 0xc3,
2890 };
2891 // clang-format on
2892 EXPECT_EQUALS_BYTES(buf, expected);
2893 AST_heap_free(node);
2894 PASS();
2895}
2896
2897TEST compile_code_with_two_params(Buffer *buf) {
2898 ASTNode *node = Reader_read("(code (x y) () (+ x y))");
2899 int compile_result = Compile_code(buf, node, /*labels=*/NULL);
2900 ASSERT_EQ(compile_result, 0);
2901 // clang-format off
2902 byte expected[] = {
2903 // mov rax, [rsp-16]
2904 0x48, 0x8b, 0x44, 0x24, 0xf0,
2905 // mov [rsp-24], rax
2906 0x48, 0x89, 0x44, 0x24, 0xe8,
2907 // mov rax, [rsp-8]
2908 0x48, 0x8b, 0x44, 0x24, 0xf8,
2909 // add rax, [rsp-24]
2910 0x48, 0x03, 0x44, 0x24, 0xe8,
2911 // ret
2912 0xc3,
2913 };
2914 // clang-format on
2915 EXPECT_EQUALS_BYTES(buf, expected);
2916 AST_heap_free(node);
2917 PASS();
2918}
2919
2920TEST compile_code_with_two_freevars(Buffer *buf) {
2921 ASTNode *node = Reader_read("(code () (x y) (+ x y))");
2922 int compile_result = Compile_code(buf, node, /*labels=*/NULL);
2923 ASSERT_EQ(compile_result, 0);
2924 // clang-format off
2925 byte expected[] = {
2926 // mov rax, [Closure+16]
2927 0x48, 0x8b, 0x47, 0x10,
2928 // mov [rsp-8], rax
2929 0x48, 0x89, 0x44, 0x24, 0xf8,
2930 // mov rax, [Closure+8]
2931 0x48, 0x8b, 0x47, 0x08,
2932 // add rax, [rsp-8]
2933 0x48, 0x03, 0x44, 0x24, 0xf8,
2934 // ret
2935 0xc3,
2936 };
2937 // clang-format on
2938 EXPECT_EQUALS_BYTES(buf, expected);
2939 AST_heap_free(node);
2940 PASS();
2941}
2942
2943TEST compile_code_with_params_and_freevars(Buffer *buf) {
2944 ASTNode *node = Reader_read("(code (x) (y) (+ x y))");
2945 int compile_result = Compile_code(buf, node, /*labels=*/NULL);
2946 ASSERT_EQ(compile_result, 0);
2947 // clang-format off
2948 byte expected[] = {
2949 // mov rax, [Closure+8]
2950 0x48, 0x8b, 0x47, 0x08,
2951 // mov [rsp-16], rax
2952 0x48, 0x89, 0x44, 0x24, 0xf0,
2953 // mov rax, [rsp-8]
2954 0x48, 0x8b, 0x44, 0x24, 0xf8,
2955 // add rax, [rsp-16]
2956 0x48, 0x03, 0x44, 0x24, 0xf0,
2957 // ret
2958 0xc3,
2959 };
2960 // clang-format on
2961 EXPECT_EQUALS_BYTES(buf, expected);
2962 AST_heap_free(node);
2963 PASS();
2964}
2965
2966TEST compile_labels_with_no_labels(Buffer *buf) {
2967 ASTNode *node = Reader_read("(labels () 1)");
2968 int compile_result = Compile_entry(buf, node);
2969 ASSERT_EQ(compile_result, 0);
2970 // clang-format off
2971 byte expected[] = {
2972 // mov Code, rsi
2973 0x48, 0x8b, 0xce,
2974 // mov Heap, rdi
2975 0x48, 0x89, 0xfe,
2976 // mov rax, 0x2
2977 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2978 // ret
2979 0xc3,
2980 };
2981 // clang-format on
2982 EXPECT_EQUALS_BYTES(buf, expected);
2983 Buffer_make_executable(buf);
2984 uword result = Testing_execute_entry(buf, /*heap=*/NULL);
2985 ASSERT_EQ_FMT(Object_encode_integer(1), result, "0x%lx");
2986 AST_heap_free(node);
2987 PASS();
2988}
2989
2990TEST compile_labels_with_one_label(Buffer *buf) {
2991 ASTNode *node = Reader_read("(labels ((const (code () () 5))) 1)");
2992 int compile_result = Compile_entry(buf, node);
2993 ASSERT_EQ(compile_result, 0);
2994 // clang-format off
2995 byte expected[] = {
2996 // mov rax, compile(5)
2997 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00,
2998 // ret
2999 0xc3,
3000 // mov Code, rsi
3001 0x48, 0x8b, 0xce,
3002 // mov Heap, rdi
3003 0x48, 0x89, 0xfe,
3004 // mov rax, 0x2
3005 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
3006 // ret
3007 0xc3,
3008 };
3009 // clang-format on
3010 EXPECT_EQUALS_BYTES(buf, expected);
3011 Buffer_make_executable(buf);
3012 uword result = Testing_execute_entry(buf, /*heap=*/NULL);
3013 ASSERT_EQ_FMT(Object_encode_integer(1), result, "0x%lx");
3014 AST_heap_free(node);
3015 PASS();
3016}
3017
3018TEST compile_funcall_with_no_params(Buffer *buf, uword *heap) {
3019 ASTNode *node =
3020 Reader_read("(labels ((const (code () () 5))) ((closure const)))");
3021 int compile_result = Compile_entry(buf, node);
3022 ASSERT_EQ(compile_result, 0);
3023 // clang-format off
3024 byte expected[] = {
3025 // mov rax, compile(5)
3026 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00,
3027 // ret
3028 0xc3,
3029 // mov Code, rsi
3030 0x48, 0x8b, 0xce,
3031 // mov Heap, rdi
3032 0x48, 0x89, 0xfe,
3033 // mov rax, Code
3034 0x48, 0x89, 0xc8,
3035 // add rax, 0 (code offset)
3036 0x48, 0x05, 0x00, 0x00, 0x00, 0x00,
3037 // mov qword [Heap], rax
3038 0x48, 0x89, 0x46, 0x00,
3039 // mov rax, Heap
3040 0x48, 0x89, 0xf0,
3041 // or rax, 6
3042 0x48, 0x83, 0xc8, 0x06,
3043 // add Heap, 0x8 (label + 0 freevars)
3044 0x48, 0x81, 0xc6, 0x08, 0x00, 0x00, 0x00,
3045 // mov [rsp-8], rdi save closure
3046 0x48, 0x89, 0x7c, 0x24, 0xf8,
3047 // mov rdi, rax
3048 0x48, 0x89, 0xc7,
3049 // sub rdi, 0x6
3050 0x48, 0x81, 0xef, 0x06, 0x00, 0x00, 0x00,
3051 // mov rax, [Closure] closure[label]
3052 0x48, 0x8b, 0x47, 0x00,
3053 // sub rsp, 8
3054 0x48, 0x81, 0xec, 0x08, 0x00, 0x00, 0x00,
3055 // call near rax (const)
3056 0xff, 0xd0,
3057 // add rsp, 8
3058 0x48, 0x81, 0xc4, 0x08, 0x00, 0x00, 0x00,
3059 // mov rdi, [rsp-8] reload closure
3060 0x48, 0x8b, 0x7c, 0x24, 0xf8,
3061 // ret
3062 0xc3,
3063 };
3064 // clang-format on
3065 EXPECT_EQUALS_BYTES(buf, expected);
3066 Buffer_make_executable(buf);
3067 uword result = Testing_execute_entry(buf, heap);
3068 ASSERT_EQ_FMT(Object_encode_integer(5), result, "0x%lx");
3069 AST_heap_free(node);
3070 PASS();
3071}
3072
3073TEST compile_funcall_with_no_params_and_locals(Buffer *buf, uword *heap) {
3074 ASTNode *node = Reader_read(
3075 "(labels ((const (code () () 5))) (let ((a 1)) ((closure const))))");
3076 int compile_result = Compile_entry(buf, node);
3077 ASSERT_EQ(compile_result, 0);
3078 // clang-format off
3079 byte expected[] = {
3080 // mov rax, compile(5)
3081 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00,
3082 // ret
3083 0xc3,
3084 // mov Code, rsi
3085 0x48, 0x8b, 0xce,
3086 // mov Heap, rdi
3087 0x48, 0x89, 0xfe,
3088 // mov rax, compile(1)
3089 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
3090 // mov [rsp-8], rax
3091 0x48, 0x89, 0x44, 0x24, 0xf8,
3092 // mov rax, Code
3093 0x48, 0x89, 0xc8,
3094 // add rax, 0 (code offset)
3095 0x48, 0x05, 0x00, 0x00, 0x00, 0x00,
3096 // mov qword [Heap], rax
3097 0x48, 0x89, 0x46, 0x00,
3098 // mov rax, Heap
3099 0x48, 0x89, 0xf0,
3100 // or rax, 6
3101 0x48, 0x83, 0xc8, 0x06,
3102 // add Heap, 0x8 (label + 0 freevars)
3103 0x48, 0x81, 0xc6, 0x08, 0x00, 0x00, 0x00,
3104 // mov [rsp-16], rdi save closure
3105 0x48, 0x89, 0x7c, 0x24, 0xf0,
3106 // mov rdi, rax
3107 0x48, 0x89, 0xc7,
3108 // sub rdi, 0x6
3109 0x48, 0x81, 0xef, 0x06, 0x00, 0x00, 0x00,
3110 // mov rax, [Closure] closure[label]
3111 0x48, 0x8b, 0x47, 0x00,
3112 // sub rsp, 16
3113 0x48, 0x81, 0xec, 0x10, 0x00, 0x00, 0x00,
3114 // call near rax (const)
3115 0xff, 0xd0,
3116 // add rsp, 16
3117 0x48, 0x81, 0xc4, 0x10, 0x00, 0x00, 0x00,
3118 // mov rdi, [rsp-16] reload closure
3119 0x48, 0x8b, 0x7c, 0x24, 0xf0,
3120 // ret
3121 0xc3,
3122 };
3123 // clang-format on
3124 EXPECT_EQUALS_BYTES(buf, expected);
3125 Buffer_make_executable(buf);
3126 uword result = Testing_execute_entry(buf, heap);
3127 ASSERT_EQ_FMT(Object_encode_integer(5), result, "0x%lx");
3128 AST_heap_free(node);
3129 PASS();
3130}
3131
3132TEST compile_funcall_with_one_param(Buffer *buf, uword *heap) {
3133 ASTNode *node =
3134 Reader_read("(labels ((id (code (x) () x))) ((closure id) 5))");
3135 int compile_result = Compile_entry(buf, node);
3136 ASSERT_EQ(compile_result, 0);
3137 // clang-format off
3138 byte expected[] = {
3139 // mov rax, [rsp-8]
3140 0x48, 0x8b, 0x44, 0x24, 0xf8,
3141 // ret
3142 0xc3,
3143 // mov Code, rsi
3144 0x48, 0x8b, 0xce,
3145 // mov Heap, rdi
3146 0x48, 0x89, 0xfe,
3147 // mov rax, compile(5)
3148 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00,
3149 // mov [rsp-24], rax
3150 0x48, 0x89, 0x44, 0x24, 0xe8,
3151 // mov rax, Code
3152 0x48, 0x89, 0xc8,
3153 // add rax, 0 (code offset)
3154 0x48, 0x05, 0x00, 0x00, 0x00, 0x00,
3155 // mov qword [Heap], rax
3156 0x48, 0x89, 0x46, 0x00,
3157 // mov rax, Heap
3158 0x48, 0x89, 0xf0,
3159 // or rax, 6
3160 0x48, 0x83, 0xc8, 0x06,
3161 // add Heap, 0x8 (label + 0 freevars)
3162 0x48, 0x81, 0xc6, 0x08, 0x00, 0x00, 0x00,
3163 // mov [rsp-8], rdi save closure
3164 0x48, 0x89, 0x7c, 0x24, 0xf8,
3165 // mov rdi, rax
3166 0x48, 0x89, 0xc7,
3167 // sub rdi, 0x6
3168 0x48, 0x81, 0xef, 0x06, 0x00, 0x00, 0x00,
3169 // mov rax, [Closure] closure[label]
3170 0x48, 0x8b, 0x47, 0x00,
3171 // sub rsp, 8
3172 0x48, 0x81, 0xec, 0x08, 0x00, 0x00, 0x00,
3173 // call near rax (const)
3174 0xff, 0xd0,
3175 // add rsp, 8
3176 0x48, 0x81, 0xc4, 0x08, 0x00, 0x00, 0x00,
3177 // mov rdi, [rsp-8] reload closure
3178 0x48, 0x8b, 0x7c, 0x24, 0xf8,
3179 // ret
3180 0xc3,
3181 };
3182 // clang-format on
3183 EXPECT_EQUALS_BYTES(buf, expected);
3184 Buffer_make_executable(buf);
3185 uword result = Testing_execute_entry(buf, heap);
3186 ASSERT_EQ_FMT(Object_encode_integer(5), result, "0x%lx");
3187 AST_heap_free(node);
3188 PASS();
3189}
3190
3191SUITE(object_tests) {
3192 RUN_TEST(encode_positive_integer);
3193 RUN_TEST(encode_negative_integer);
3194 RUN_TEST(encode_char);
3195 RUN_TEST(decode_char);
3196 RUN_TEST(encode_bool);
3197 RUN_TEST(decode_bool);
3198 RUN_TEST(address);
3199}
3200
3201TEST compile_funcall_with_one_param_and_locals(Buffer *buf, uword *heap) {
3202 ASTNode *node = Reader_read(
3203 "(labels ((id (code (x) () x))) (let ((a 1)) ((closure id) 5)))");
3204 int compile_result = Compile_entry(buf, node);
3205 ASSERT_EQ(compile_result, 0);
3206 // clang-format off
3207 byte expected[] = {
3208 // mov rax, [rsp-8]
3209 0x48, 0x8b, 0x44, 0x24, 0xf8,
3210 // ret
3211 0xc3,
3212 // mov Code, rsi
3213 0x48, 0x8b, 0xce,
3214 // mov Heap, rdi
3215 0x48, 0x89, 0xfe,
3216 // mov rax, compile(1)
3217 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
3218 // mov [rsp-8], rax
3219 0x48, 0x89, 0x44, 0x24, 0xf8,
3220 // mov rax, compile(5)
3221 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00,
3222 // mov [rsp-32], rax
3223 0x48, 0x89, 0x44, 0x24, 0xe0,
3224 // mov rax, Code
3225 0x48, 0x89, 0xc8,
3226 // add rax, 0 (code offset)
3227 0x48, 0x05, 0x00, 0x00, 0x00, 0x00,
3228 // mov qword [Heap], rax
3229 0x48, 0x89, 0x46, 0x00,
3230 // mov rax, Heap
3231 0x48, 0x89, 0xf0,
3232 // or rax, 6
3233 0x48, 0x83, 0xc8, 0x06,
3234 // add Heap, 0x8 (label + 0 freevars)
3235 0x48, 0x81, 0xc6, 0x08, 0x00, 0x00, 0x00,
3236 // mov [rsp-16], rdi save closure
3237 0x48, 0x89, 0x7c, 0x24, 0xf0,
3238 // mov rdi, rax
3239 0x48, 0x89, 0xc7,
3240 // sub rdi, 0x6
3241 0x48, 0x81, 0xef, 0x06, 0x00, 0x00, 0x00,
3242 // mov rax, [Closure] closure[label]
3243 0x48, 0x8b, 0x47, 0x00,
3244 // sub rsp, 16
3245 0x48, 0x81, 0xec, 0x10, 0x00, 0x00, 0x00,
3246 // call near rax (const)
3247 0xff, 0xd0,
3248 // add rsp, 16
3249 0x48, 0x81, 0xc4, 0x10, 0x00, 0x00, 0x00,
3250 // mov rdi, [rsp-16] reload closure
3251 0x48, 0x8b, 0x7c, 0x24, 0xf0,
3252 // ret
3253 0xc3,
3254 };
3255 // clang-format on
3256 EXPECT_EQUALS_BYTES(buf, expected);
3257 Buffer_make_executable(buf);
3258 uword result = Testing_execute_entry(buf, heap);
3259 ASSERT_EQ_FMT(Object_encode_integer(5), result, "0x%lx");
3260 AST_heap_free(node);
3261 PASS();
3262}
3263
3264TEST compile_funcall_with_two_params_and_locals(Buffer *buf, uword *heap) {
3265 ASTNode *node =
3266 Reader_read("(labels ((add (code (x y) () (+ x y)))) (let ((a "
3267 "1)) ((closure add) 5 a)))");
3268 int compile_result = Compile_entry(buf, node);
3269 ASSERT_EQ(compile_result, 0);
3270 // clang-format off
3271 byte expected[] = {
3272 // mov rax, [rsp-8]
3273 0x48, 0x8b, 0x44, 0x24, 0xf0,
3274 // mov [rsp-24], rax
3275 0x48, 0x89, 0x44, 0x24, 0xe8,
3276 // mov rax, [rsp-8]
3277 0x48, 0x8b, 0x44, 0x24, 0xf8,
3278 // add rax, [rsp-24]
3279 0x48, 0x03, 0x44, 0x24, 0xe8,
3280 // ret
3281 0xc3,
3282
3283 // mov Code, rsi
3284 0x48, 0x8b, 0xce,
3285 // mov Heap, rdi
3286 0x48, 0x89, 0xfe,
3287 // mov rax, compile(1)
3288 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
3289 // mov [rsp-8], rax
3290 0x48, 0x89, 0x44, 0x24, 0xf8,
3291 // mov rax, compile(5)
3292 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00,
3293 // mov [rsp-32], rax
3294 0x48, 0x89, 0x44, 0x24, 0xe0,
3295 // mov rax, [rsp-8]
3296 0x48, 0x8b, 0x44, 0x24, 0xf8,
3297 // mov [rsp-40], rax
3298 0x48, 0x89, 0x44, 0x24, 0xd8,
3299 // mov rax, Code
3300 0x48, 0x89, 0xc8,
3301 // add rax, 0 (code offset)
3302 0x48, 0x05, 0x00, 0x00, 0x00, 0x00,
3303 // mov qword [Heap], rax
3304 0x48, 0x89, 0x46, 0x00,
3305 // mov rax, Heap
3306 0x48, 0x89, 0xf0,
3307 // or rax, 6
3308 0x48, 0x83, 0xc8, 0x06,
3309 // add Heap, 0x8 (label + 0 freevars)
3310 0x48, 0x81, 0xc6, 0x08, 0x00, 0x00, 0x00,
3311 // mov [rsp-16], rdi save closure
3312 0x48, 0x89, 0x7c, 0x24, 0xf0,
3313 // mov rdi, rax
3314 0x48, 0x89, 0xc7,
3315 // sub rdi, 0x6
3316 0x48, 0x81, 0xef, 0x06, 0x00, 0x00, 0x00,
3317 // mov rax, [Closure] closure[label]
3318 0x48, 0x8b, 0x47, 0x00,
3319 // sub rsp, 16
3320 0x48, 0x81, 0xec, 0x10, 0x00, 0x00, 0x00,
3321 // call near rax (const)
3322 0xff, 0xd0,
3323 // add rsp, 16
3324 0x48, 0x81, 0xc4, 0x10, 0x00, 0x00, 0x00,
3325 // mov rdi, [rsp-16] reload closure
3326 0x48, 0x8b, 0x7c, 0x24, 0xf0,
3327 // ret
3328 0xc3,
3329 };
3330 // clang-format on
3331 EXPECT_EQUALS_BYTES(buf, expected);
3332 Buffer_make_executable(buf);
3333 uword result = Testing_execute_entry(buf, heap);
3334 ASSERT_EQ_FMT(Object_encode_integer(6), result, "0x%lx");
3335 AST_heap_free(node);
3336 PASS();
3337}
3338
3339TEST compile_nested_funcall(Buffer *buf, uword *heap) {
3340 ASTNode *node = Reader_read("(labels ((add (code (x y) () (+ x y)))"
3341 " (sub (code (x y) () (- x y))))"
3342 " ((closure sub) 4 ((closure add) 1 2)))");
3343 int compile_result = Compile_entry(buf, node);
3344 ASSERT_EQ(compile_result, 0);
3345 Buffer_make_executable(buf);
3346 uword result = Testing_execute_entry(buf, heap);
3347 ASSERT_EQ_FMT(Object_encode_integer(1), result, "0x%lx");
3348 AST_heap_free(node);
3349 PASS();
3350}
3351
3352TEST compile_multilevel_funcall(Buffer *buf, uword *heap) {
3353 ASTNode *node =
3354 Reader_read("(labels ((add (code (x y) () (+ x y)))"
3355 " (add2 (code (x y) () ((closure add) x y))))"
3356 " ((closure add2) 1 2))");
3357 int compile_result = Compile_entry(buf, node);
3358 ASSERT_EQ(compile_result, 0);
3359 Buffer_make_executable(buf);
3360 uword result = Testing_execute_entry(buf, heap);
3361 ASSERT_EQ_FMT(Object_encode_integer(3), result, "0x%lx");
3362 AST_heap_free(node);
3363 PASS();
3364}
3365
3366TEST compile_factorial_funcall(Buffer *buf, uword *heap) {
3367 ASTNode *node = Reader_read(
3368 "(labels ((factorial (code (x) ()"
3369 " (if (< x 2) 1 (* x ((closure factorial) (- x 1)))))))"
3370 " ((closure factorial) 5))");
3371 int compile_result = Compile_entry(buf, node);
3372 ASSERT_EQ(compile_result, 0);
3373 Buffer_make_executable(buf);
3374 uword result = Testing_execute_entry(buf, heap);
3375 ASSERT_EQ_FMT(Object_encode_integer(120), result, "0x%lx");
3376 AST_heap_free(node);
3377 PASS();
3378}
3379
3380TEST compile_closure_undefined_label(Buffer *buf) {
3381 ASTNode *node = Reader_read("(closure nonexistent)");
3382 int compile_result =
3383 Compile_expr(buf, node, -kWordSize, /*varenv=*/NULL, /*labels=*/NULL);
3384 ASSERT_EQ(compile_result, -1);
3385 AST_heap_free(node);
3386 PASS();
3387}
3388
3389TEST compile_closure_no_freevars(Buffer *buf, uword *heap) {
3390 ASTNode *node = Reader_read("(closure foo)");
3391 uword closure_ptr = 0xe3;
3392 Env labels = Env_bind("foo", closure_ptr, NULL);
3393 Buffer_write_arr(buf, kEntryPrologue, sizeof kEntryPrologue);
3394 int compile_result =
3395 Compile_expr(buf, node, -kWordSize, /*varenv=*/NULL, &labels);
3396 ASSERT_EQ(compile_result, 0);
3397 Emit_ret(buf);
3398 // clang-format off
3399 byte expected[] = {
3400 // mov Code, rsi
3401 0x48, 0x8b, 0xce,
3402 // mov Heap, rdi
3403 0x48, 0x89, 0xfe,
3404 // mov rax, Code
3405 0x48, 0x89, 0xc8,
3406 // add rax, 0xe3 (code offset)
3407 0x48, 0x05, 0xe3, 0x00, 0x00, 0x00,
3408 // mov qword [Heap], rax
3409 0x48, 0x89, 0x46, 0x00,
3410 // mov rax, Heap
3411 0x48, 0x89, 0xf0,
3412 // or rax, 6
3413 0x48, 0x83, 0xc8, 0x06,
3414 // add Heap, 0x8 (label + 0 freevars)
3415 0x48, 0x81, 0xc6, 0x08, 0x00, 0x00, 0x00,
3416 // ret
3417 0xc3,
3418 };
3419 // clang-format on
3420 EXPECT_EQUALS_BYTES(buf, expected);
3421 Buffer_make_executable(buf);
3422 uword result = Testing_execute_entry(buf, heap);
3423 ASSERT(Object_is_closure(result));
3424 ASSERT_EQ_FMT((uword)(buf->address + closure_ptr),
3425 Object_closure_label(result), "%ld");
3426 AST_heap_free(node);
3427 PASS();
3428}
3429
3430TEST compile_closure_one_freevar(Buffer *buf, uword *heap) {
3431 ASTNode *node = Reader_read("(closure foo 1)");
3432 uword closure_ptr = 0xe3;
3433 Env labels = Env_bind("foo", closure_ptr, NULL);
3434 Buffer_write_arr(buf, kEntryPrologue, sizeof kEntryPrologue);
3435 int compile_result =
3436 Compile_expr(buf, node, -kWordSize, /*varenv=*/NULL, &labels);
3437 ASSERT_EQ(compile_result, 0);
3438 Emit_ret(buf);
3439 // clang-format off
3440 byte expected[] = {
3441 // mov rax, compile(1)
3442 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
3443 // mov [rsp-8], rax
3444 0x48, 0x89, 0x44, 0x24, 0xf8,
3445 // mov rax, [rsp - 8]
3446 0x48, 0x8b, 0x44, 0x24, 0xf8,
3447 // mov [Heap + 8], rax
3448 0x48, 0x89, 0x46, 0x08,
3449 // mov rax, Code
3450 0x48, 0x89, 0xc8,
3451 // add rax, 0xe3 (code offset)
3452 0x48, 0x05, 0xe3, 0x00, 0x00, 0x00,
3453 // mov qword [Heap], rax
3454 0x48, 0x89, 0x46, 0x00,
3455 // mov rax, Heap
3456 0x48, 0x89, 0xf0,
3457 // or rax, 6
3458 0x48, 0x83, 0xc8, 0x06,
3459 // add Heap, 0x10 (label + 1 freevar)
3460 0x48, 0x81, 0xc6, 0x10, 0x00, 0x00, 0x00,
3461 };
3462 // clang-format on
3463 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
3464 Buffer_make_executable(buf);
3465 uword result = Testing_execute_entry(buf, heap);
3466 ASSERT(Object_is_closure(result));
3467 ASSERT_EQ_FMT((uword)(buf->address + closure_ptr),
3468 Object_closure_label(result), "%ld");
3469 ASSERT_EQ_FMT(Object_encode_integer(1), Object_closure_freevar(result, 0),
3470 "%ld");
3471 AST_heap_free(node);
3472 PASS();
3473}
3474
3475TEST compile_closure_two_freevars(Buffer *buf, uword *heap) {
3476 ASTNode *node = Reader_read("(closure foo 1 2)");
3477 uword closure_ptr = 0xe3;
3478 Env labels = Env_bind("foo", closure_ptr, NULL);
3479 Buffer_write_arr(buf, kEntryPrologue, sizeof kEntryPrologue);
3480 int compile_result =
3481 Compile_expr(buf, node, -kWordSize, /*varenv=*/NULL, &labels);
3482 ASSERT_EQ(compile_result, 0);
3483 Emit_ret(buf);
3484 // clang-format off
3485 byte expected[] = {
3486 // mov rax, compile(1)
3487 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
3488 // mov [rsp-8], rax
3489 0x48, 0x89, 0x44, 0x24, 0xf8,
3490 // mov rax, compile(2)
3491 0x48, 0xc7, 0xc0, 0x08, 0x00, 0x00, 0x00,
3492 // mov [rsp-16], rax
3493 0x48, 0x89, 0x44, 0x24, 0xf0,
3494 // mov rax, [rsp - 16]
3495 0x48, 0x8b, 0x44, 0x24, 0xf0,
3496 // mov [Heap + 16], rax
3497 0x48, 0x89, 0x46, 0x10,
3498 // mov rax, [rsp - 8]
3499 0x48, 0x8b, 0x44, 0x24, 0xf8,
3500 // mov [Heap + 8], rax
3501 0x48, 0x89, 0x46, 0x08,
3502 // mov rax, Code
3503 0x48, 0x89, 0xc8,
3504 // add rax, 0xe3 (code offset)
3505 0x48, 0x05, 0xe3, 0x00, 0x00, 0x00,
3506 // mov qword [Heap], rax
3507 0x48, 0x89, 0x46, 0x00,
3508 // mov rax, Heap
3509 0x48, 0x89, 0xf0,
3510 // or rax, 6
3511 0x48, 0x83, 0xc8, 0x06,
3512 // add Heap, 0x18 (label + 2 freevars)
3513 0x48, 0x81, 0xc6, 0x18, 0x00, 0x00, 0x00,
3514 };
3515 // clang-format on
3516 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
3517 Buffer_make_executable(buf);
3518 uword result = Testing_execute_entry(buf, heap);
3519 ASSERT(Object_is_closure(result));
3520 ASSERT_EQ_FMT((uword)(buf->address + closure_ptr),
3521 Object_closure_label(result), "%ld");
3522 ASSERT_EQ_FMT(Object_encode_integer(1), Object_closure_freevar(result, 0),
3523 "%ld");
3524 ASSERT_EQ_FMT(Object_encode_integer(2), Object_closure_freevar(result, 1),
3525 "%ld");
3526 AST_heap_free(node);
3527 PASS();
3528}
3529
3530SUITE(ast_tests) {
3531 RUN_TEST(ast_new_pair);
3532 RUN_TEST(ast_pair_car_returns_car);
3533 RUN_TEST(ast_pair_cdr_returns_cdr);
3534 RUN_TEST(ast_new_symbol);
3535}
3536
3537SUITE(reader_tests) {
3538 RUN_TEST(read_with_integer_returns_integer);
3539 RUN_TEST(read_with_negative_integer_returns_integer);
3540 RUN_TEST(read_with_positive_integer_returns_integer);
3541 RUN_TEST(read_with_leading_whitespace_ignores_whitespace);
3542 RUN_TEST(read_with_symbol_returns_symbol);
3543 RUN_TEST(read_with_symbol_with_trailing_digits);
3544 RUN_TEST(read_with_nil_returns_nil);
3545 RUN_TEST(read_with_list_returns_list);
3546 RUN_TEST(read_with_nested_list_returns_list);
3547 RUN_TEST(read_with_char_returns_char);
3548 RUN_TEST(read_with_bool_returns_bool);
3549}
3550
3551SUITE(buffer_tests) {
3552 RUN_BUFFER_TEST(buffer_write8_increases_length);
3553 RUN_TEST(buffer_write8_expands_buffer);
3554 RUN_TEST(buffer_write32_expands_buffer);
3555 RUN_BUFFER_TEST(buffer_write32_writes_little_endian);
3556 RUN_BUFFER_TEST(emit_mov_reg_imm32_emits_modrm);
3557 RUN_BUFFER_TEST(emit_store_reg_indirect_emits_modrm_sib);
3558}
3559
3560TEST free_in_with_immediate_returns_nil() {
3561 ASSERT(AST_is_nil(free_in(AST_new_integer(5))));
3562 ASSERT(AST_is_nil(free_in(AST_new_char('a'))));
3563 ASSERT(AST_is_nil(free_in(AST_new_bool(true))));
3564 ASSERT(AST_is_nil(free_in(AST_nil())));
3565 PASS();
3566}
3567
3568TEST is_list_with_names(ASTNode *list, word n, ...) {
3569 if (!AST_is_pair(list)) {
3570 FAILm("Not a list");
3571 }
3572 va_list vl;
3573 va_start(vl, n);
3574 for (word i = 0; i < n; i++) {
3575 if (AST_is_nil(list)) {
3576 FAILm("List smaller than expected");
3577 }
3578 ASTNode *elt = AST_pair_car(list);
3579 if (!AST_is_symbol(elt)) {
3580 FAILm("List had non-symbol element");
3581 }
3582 const char *expected = va_arg(vl, const char *);
3583 if (!AST_symbol_matches(elt, expected)) {
3584 FAILm("List element does not match expected");
3585 }
3586 list = AST_pair_cdr(list);
3587 }
3588 if (!AST_is_nil(list)) {
3589 abort();
3590 FAILm("List larger than expected");
3591 }
3592 PASS();
3593}
3594
3595TEST free_in_with_symbol_returns_list() {
3596 {
3597 ASTNode *result = free_in(AST_new_symbol("foo"));
3598 CHECK_CALL(is_list_with_names(result, 1, "foo"));
3599 }
3600 PASS();
3601}
3602
3603TEST free_in_with_if() {
3604 {
3605 ASTNode *result = free_in(Reader_read("(if 1 2 3)"));
3606 ASSERT(AST_is_nil(result));
3607 }
3608 {
3609 ASTNode *result = free_in(Reader_read("(if foo 2 3)"));
3610 CHECK_CALL(is_list_with_names(result, 1, "foo"));
3611 }
3612 {
3613 ASTNode *result = free_in(Reader_read("(if 1 foo 3)"));
3614 CHECK_CALL(is_list_with_names(result, 1, "foo"));
3615 }
3616 {
3617 ASTNode *result = free_in(Reader_read("(if 1 2 foo)"));
3618 CHECK_CALL(is_list_with_names(result, 1, "foo"));
3619 }
3620 {
3621 ASTNode *result = free_in(Reader_read("(if foo 2 bar)"));
3622 CHECK_CALL(is_list_with_names(result, 2, "foo", "bar"));
3623 }
3624 {
3625 ASTNode *result = free_in(Reader_read("(if foo 2 foo)"));
3626 CHECK_CALL(is_list_with_names(result, 1, "foo"));
3627 }
3628 PASS();
3629}
3630
3631TEST free_in_with_let() {
3632 {
3633 ASTNode *result = free_in(Reader_read("(let () 1)"));
3634 ASSERT(AST_is_nil(result));
3635 }
3636 {
3637 ASTNode *result = free_in(Reader_read("(let () foo)"));
3638 CHECK_CALL(is_list_with_names(result, 1, "foo"));
3639 }
3640 {
3641 ASTNode *result = free_in(Reader_read("(let ((foo 1)) 1)"));
3642 ASSERT(AST_is_nil(result));
3643 }
3644 {
3645 ASTNode *result = free_in(Reader_read("(let ((foo 1) (bar 2)) foo)"));
3646 ASSERT(AST_is_nil(result));
3647 }
3648 {
3649 ASTNode *result = free_in(Reader_read("(let ((foo foo)) foo)"));
3650 CHECK_CALL(is_list_with_names(result, 1, "foo"));
3651 }
3652 {
3653 ASTNode *result = free_in(Reader_read("(let ((foo bar) (bar 3)) foo)"));
3654 CHECK_CALL(is_list_with_names(result, 1, "bar"));
3655 }
3656 {
3657 ASTNode *result =
3658 free_in(Reader_read("(let ((foo 1)) (let ((bar 2)) (+ foo bar)))"));
3659 ASSERT(AST_is_nil(result));
3660 }
3661 {
3662 ASTNode *result =
3663 free_in(Reader_read("(let ((foo 1)) (let ((baz 2)) (+ foo bar)))"));
3664 CHECK_CALL(is_list_with_names(result, 1, "bar"));
3665 }
3666 PASS();
3667}
3668
3669TEST free_in_with_lambda() {
3670 {
3671 ASTNode *result = free_in(Reader_read("(lambda () 1)"));
3672 ASSERT(AST_is_nil(result));
3673 }
3674 {
3675 ASTNode *result = free_in(Reader_read("(lambda () (+ foo 1))"));
3676 CHECK_CALL(is_list_with_names(result, 1, "foo"));
3677 }
3678 {
3679 ASTNode *result = free_in(Reader_read("(lambda (foo) 1)"));
3680 ASSERT(AST_is_nil(result));
3681 }
3682 {
3683 ASTNode *result = free_in(Reader_read("(lambda (foo bar) foo)"));
3684 ASSERT(AST_is_nil(result));
3685 }
3686 {
3687 ASTNode *result = free_in(Reader_read("(lambda (foo) foo)"));
3688 ASSERT(AST_is_nil(result));
3689 }
3690 {
3691 ASTNode *result = free_in(Reader_read("(lambda (foo) (+ foo bar))"));
3692 CHECK_CALL(is_list_with_names(result, 1, "bar"));
3693 }
3694 {
3695 ASTNode *result =
3696 free_in(Reader_read("(lambda (foo) (lambda (bar) (+ foo bar)))"));
3697 ASSERT(AST_is_nil(result));
3698 }
3699 {
3700 ASTNode *result =
3701 free_in(Reader_read("(lambda (foo) (lambda (baz) (+ foo bar)))"));
3702 CHECK_CALL(is_list_with_names(result, 1, "bar"));
3703 }
3704 PASS();
3705}
3706
3707TEST is_lambda_with_freevars(ASTNode *node, word n, ...) {
3708 if (!is_tagged_with(node, "lambda")) {
3709 FAILm("Not a lambda");
3710 }
3711 ASTNode *args = AST_pair_cdr(node);
3712 ASTNode *freevars = operand2(args);
3713 if (AST_is_nil(freevars) != (n == 0)) {
3714 FAILm("Expected freevars to be nil only when n == 0");
3715 }
3716 if (!AST_is_pair(freevars)) {
3717 FAILm("Freevars is not a list");
3718 }
3719 va_list vl;
3720 va_start(vl, n);
3721 for (word i = 0; i < n; i++) {
3722 if (AST_is_nil(freevars)) {
3723 FAILm("Freevars smaller than expected");
3724 }
3725 ASTNode *elt = AST_pair_car(freevars);
3726 if (!AST_is_symbol(elt)) {
3727 FAILm("Freevars had non-symbol element");
3728 }
3729 const char *expected = va_arg(vl, const char *);
3730 if (!AST_symbol_matches(elt, expected)) {
3731 FAILm("Freevars element does not match expected");
3732 }
3733 freevars = AST_pair_cdr(freevars);
3734 }
3735 if (!AST_is_nil(freevars)) {
3736 abort();
3737 FAILm("Freevars larger than expected");
3738 }
3739 PASS();
3740}
3741
3742TEST transform_lambda_adds_freevars() {
3743 {
3744 ASTNode *result = Transform_lambda(Reader_read("(lambda () a)"));
3745 CHECK_CALL(is_lambda_with_freevars(result, 1, "a"));
3746 }
3747 {
3748 ASTNode *result = Transform_lambda(Reader_read("(lambda () (+ a b))"));
3749 CHECK_CALL(is_lambda_with_freevars(result, 2, "a", "b"));
3750 }
3751 {
3752 ASTNode *result =
3753 Transform_lambda(Reader_read("(lambda (y) (lambda () (+ x y)))"));
3754 CHECK_CALL(is_lambda_with_freevars(result, 1, "x"));
3755 }
3756 PASS();
3757}
3758
3759TEST transform_let() {
3760 {
3761 ASTNode *result = Transform(Reader_read("(let () (lambda () a))"));
3762 ASSERT(is_tagged_with(result, "let"));
3763 ASTNode *bindings = AST_pair_car(AST_pair_cdr(result));
3764 ASSERT(AST_is_nil(bindings));
3765 ASTNode *body = AST_pair_car(AST_pair_cdr(AST_pair_cdr(result)));
3766 CHECK_CALL(is_lambda_with_freevars(body, 1, "a"));
3767 }
3768 {
3769 ASTNode *result = Transform(Reader_read("(let ((a (lambda () a))) 1)"));
3770 ASSERT(is_tagged_with(result, "let"));
3771 ASTNode *bindings = AST_pair_car(AST_pair_cdr(result));
3772 ASSERT(AST_is_pair(bindings));
3773 ASTNode *binding = AST_pair_car(bindings);
3774 ASTNode *value = AST_pair_car(AST_pair_cdr(binding));
3775 CHECK_CALL(is_lambda_with_freevars(value, 1, "a"));
3776 }
3777 PASS();
3778}
3779
3780TEST transform_arbitrary_call() {
3781 {
3782 ASTNode *result = Transform(Reader_read("((lambda () x) 1)"));
3783 ASTNode *lambda = AST_pair_car(result);
3784 CHECK_CALL(is_lambda_with_freevars(lambda, 1, "x"));
3785 }
3786 {
3787 ASTNode *result = Transform(Reader_read("(foo (lambda () x) 1)"));
3788 ASTNode *lambda = AST_pair_car(AST_pair_cdr(result));
3789 CHECK_CALL(is_lambda_with_freevars(lambda, 1, "x"));
3790 }
3791 PASS();
3792}
3793
3794SUITE(transform_tests) {
3795 RUN_TEST(free_in_with_immediate_returns_nil);
3796 RUN_TEST(free_in_with_symbol_returns_list);
3797 RUN_TEST(free_in_with_if);
3798 RUN_TEST(free_in_with_let);
3799 RUN_TEST(free_in_with_lambda);
3800 RUN_TEST(transform_lambda_adds_freevars);
3801 RUN_TEST(transform_let);
3802 RUN_TEST(transform_arbitrary_call);
3803}
3804
3805SUITE(compiler_tests) {
3806 RUN_BUFFER_TEST(compile_positive_integer);
3807 RUN_BUFFER_TEST(compile_negative_integer);
3808 RUN_BUFFER_TEST(compile_char);
3809 RUN_BUFFER_TEST(compile_true);
3810 RUN_BUFFER_TEST(compile_false);
3811 RUN_BUFFER_TEST(compile_nil);
3812 RUN_BUFFER_TEST(compile_unary_add1);
3813 RUN_BUFFER_TEST(compile_unary_add1_nested);
3814 RUN_BUFFER_TEST(compile_unary_sub1);
3815 RUN_BUFFER_TEST(compile_unary_integer_to_char);
3816 RUN_BUFFER_TEST(compile_unary_char_to_integer);
3817 RUN_BUFFER_TEST(compile_unary_nilp_with_nil_returns_true);
3818 RUN_BUFFER_TEST(compile_unary_nilp_with_non_nil_returns_false);
3819 RUN_BUFFER_TEST(compile_unary_zerop_with_zero_returns_true);
3820 RUN_BUFFER_TEST(compile_unary_zerop_with_non_zero_returns_false);
3821 RUN_BUFFER_TEST(compile_unary_not_with_false_returns_true);
3822 RUN_BUFFER_TEST(compile_unary_not_with_non_false_returns_false);
3823 RUN_BUFFER_TEST(compile_unary_integerp_with_integer_returns_true);
3824 RUN_BUFFER_TEST(compile_unary_integerp_with_non_integer_returns_false);
3825 RUN_BUFFER_TEST(compile_unary_booleanp_with_boolean_returns_true);
3826 RUN_BUFFER_TEST(compile_unary_booleanp_with_non_boolean_returns_false);
3827 RUN_BUFFER_TEST(compile_binary_plus);
3828 RUN_BUFFER_TEST(compile_binary_plus_nested);
3829 RUN_BUFFER_TEST(compile_binary_minus);
3830 RUN_BUFFER_TEST(compile_binary_minus_nested);
3831 RUN_BUFFER_TEST(compile_binary_mul);
3832 RUN_BUFFER_TEST(compile_binary_mul_nested);
3833 RUN_BUFFER_TEST(compile_binary_eq_with_same_address_returns_true);
3834 RUN_BUFFER_TEST(compile_binary_eq_with_different_address_returns_false);
3835 RUN_BUFFER_TEST(compile_binary_lt_with_left_less_than_right_returns_true);
3836 RUN_BUFFER_TEST(compile_binary_lt_with_left_equal_to_right_returns_false);
3837 RUN_BUFFER_TEST(compile_binary_lt_with_left_greater_than_right_returns_false);
3838 RUN_BUFFER_TEST(compile_symbol_in_env_returns_value);
3839 RUN_BUFFER_TEST(compile_symbol_in_closure_returns_value);
3840 RUN_BUFFER_TEST(compile_symbol_in_env_returns_first_value);
3841 RUN_BUFFER_TEST(compile_symbol_not_in_env_raises_compile_error);
3842 RUN_BUFFER_TEST(compile_let_with_no_bindings);
3843 RUN_BUFFER_TEST(compile_let_with_one_binding);
3844 RUN_BUFFER_TEST(compile_let_with_multiple_bindings);
3845 RUN_BUFFER_TEST(compile_nested_let);
3846 RUN_BUFFER_TEST(compile_let_is_not_let_star);
3847 RUN_BUFFER_TEST(compile_if_with_true_cond);
3848 RUN_BUFFER_TEST(compile_if_with_false_cond);
3849 RUN_BUFFER_TEST(compile_nested_if);
3850 RUN_HEAP_TEST(compile_cons);
3851 RUN_HEAP_TEST(compile_two_cons);
3852 RUN_HEAP_TEST(compile_nested_cons);
3853 RUN_HEAP_TEST(compile_car);
3854 RUN_HEAP_TEST(compile_cdr);
3855 RUN_BUFFER_TEST(compile_code_with_no_params);
3856 RUN_BUFFER_TEST(compile_code_with_one_param);
3857 RUN_BUFFER_TEST(compile_code_with_one_freevar);
3858 RUN_BUFFER_TEST(compile_code_with_two_params);
3859 RUN_BUFFER_TEST(compile_code_with_two_freevars);
3860 RUN_BUFFER_TEST(compile_code_with_params_and_freevars);
3861 RUN_BUFFER_TEST(compile_labels_with_no_labels);
3862 RUN_BUFFER_TEST(compile_labels_with_one_label);
3863 RUN_HEAP_TEST(compile_funcall_with_no_params);
3864 RUN_HEAP_TEST(compile_funcall_with_no_params_and_locals);
3865 RUN_HEAP_TEST(compile_funcall_with_one_param);
3866 RUN_HEAP_TEST(compile_funcall_with_one_param_and_locals);
3867 RUN_HEAP_TEST(compile_funcall_with_two_params_and_locals);
3868 RUN_HEAP_TEST(compile_nested_funcall);
3869 RUN_HEAP_TEST(compile_multilevel_funcall);
3870 RUN_HEAP_TEST(compile_factorial_funcall);
3871 RUN_BUFFER_TEST(compile_closure_undefined_label);
3872 RUN_HEAP_TEST(compile_closure_no_freevars);
3873 RUN_HEAP_TEST(compile_closure_one_freevar);
3874 RUN_HEAP_TEST(compile_closure_two_freevars);
3875}
3876
3877// End Tests
3878
3879GREATEST_MAIN_DEFS();
3880
3881int run_tests(int argc, char **argv) {
3882 GREATEST_MAIN_BEGIN();
3883 RUN_SUITE(object_tests);
3884 RUN_SUITE(ast_tests);
3885 RUN_SUITE(reader_tests);
3886 RUN_SUITE(buffer_tests);
3887 RUN_SUITE(transform_tests);
3888 RUN_SUITE(compiler_tests);
3889 GREATEST_MAIN_END();
3890}
3891
3892int main(int argc, char **argv) {
3893 assert(sizeof(word) == 8);
3894 return run_tests(argc, argv);
3895}