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-procedures.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#define _GNU_SOURCE
9#include <assert.h> // for assert
10#include <stdbool.h> // for bool
11#include <stddef.h> // for NULL
12#include <stdint.h> // for int32_t, etc
13#include <stdio.h> // for getline, fprintf
14#include <string.h> // for memcpy
15#include <sys/mman.h> // for mmap
16#undef _GNU_SOURCE
17
18#include "greatest.h"
19
20#define WARN_UNUSED __attribute__((warn_unused_result))
21
22// Objects
23
24typedef int64_t word;
25typedef uint64_t uword;
26
27// These constants are defined in a enum because the right hand side of a
28// statement like
29// static const int kFoo = ...;
30// must be a so-called "Integer Constant Expression". Compilers are required to
31// support a certain set of these expressions, but are not required to support
32// arbitrary arithmetic with other integer constants. Compilers such as gcc
33// before gcc-8 just decided not to play this game, while gcc-8+ and Clang play
34// just fine.
35// Since this arithmetic with constant values works just fine for enums, make
36// all these constants enum values instead.
37// See https://twitter.com/tekknolagi/status/1328449329472835586 for more info.
38enum {
39 kBitsPerByte = 8, // bits
40 kWordSize = sizeof(word), // bytes
41 kBitsPerWord = kWordSize * kBitsPerByte, // bits
42
43 kIntegerTag = 0x0, // 0b00
44 kIntegerTagMask = 0x3, // 0b11
45 kIntegerShift = 2,
46 kIntegerBits = kBitsPerWord - kIntegerShift,
47
48 kImmediateTagMask = 0x3f,
49
50 kCharTag = 0x0f, // 0b00001111
51 kCharMask = 0xff, // 0b11111111
52 kCharShift = 8,
53
54 kBoolTag = 0x1f, // 0b0011111
55 kBoolMask = 0x80, // 0b10000000
56 kBoolShift = 7,
57
58 kNilTag = 0x2f, // 0b101111
59
60 kErrorTag = 0x3f, // 0b111111
61
62 kPairTag = 0x1, // 0b001
63 kSymbolTag = 0x5, // 0b101
64 kClosureTag = 0x6, // 0b110
65 kHeapTagMask = ((uword)0x7), // 0b000...111
66 kHeapPtrMask = ~kHeapTagMask, // 0b1111...1000
67
68 kCarIndex = 0,
69 kCarOffset = kCarIndex * kWordSize,
70 kCdrIndex = kCarIndex + 1,
71 kCdrOffset = kCdrIndex * kWordSize,
72 kPairSize = kCdrOffset + kWordSize,
73};
74
75// These are defined as macros because they will not work as static const int
76// constants (per above explanation), and enum constants are only required to
77// be an int wide (per ISO C).
78#define INTEGER_MAX ((1LL << (kIntegerBits - 1)) - 1)
79#define INTEGER_MIN (-(1LL << (kIntegerBits - 1)))
80
81uword Object_encode_integer(word value) {
82 assert(value < INTEGER_MAX && "too big");
83 assert(value > INTEGER_MIN && "too small");
84 return value << kIntegerShift;
85}
86
87word Object_decode_integer(uword value) { return (word)value >> kIntegerShift; }
88
89bool Object_is_integer(uword value) {
90 return (value & kIntegerTagMask) == kIntegerTag;
91}
92
93uword Object_encode_char(char value) {
94 return ((uword)value << kCharShift) | kCharTag;
95}
96
97char Object_decode_char(uword value) {
98 return (value >> kCharShift) & kCharMask;
99}
100
101bool Object_is_char(uword value) {
102 return (value & kImmediateTagMask) == kCharTag;
103}
104
105uword Object_encode_bool(bool value) {
106 return ((uword)value << kBoolShift) | kBoolTag;
107}
108
109bool Object_decode_bool(uword value) { return value & kBoolMask; }
110
111uword Object_true() { return Object_encode_bool(true); }
112
113uword Object_false() { return Object_encode_bool(false); }
114
115uword Object_nil() { return kNilTag; }
116
117uword Object_error() { return kErrorTag; }
118
119uword Object_address(void *obj) { return (uword)obj & kHeapPtrMask; }
120
121bool Object_is_pair(uword value) { return (value & kHeapTagMask) == kPairTag; }
122
123uword Object_pair_car(uword value) {
124 assert(Object_is_pair(value));
125 return ((uword *)Object_address((void *)value))[kCarIndex];
126}
127
128uword Object_pair_cdr(uword value) {
129 assert(Object_is_pair(value));
130 return ((uword *)Object_address((void *)value))[kCdrIndex];
131}
132
133// End Objects
134
135// Buffer
136
137typedef unsigned char byte;
138
139typedef enum {
140 kWritable,
141 kExecutable,
142} BufferState;
143
144typedef struct {
145 byte *address;
146 BufferState state;
147 word len;
148 word capacity;
149 word entrypoint;
150} Buffer;
151
152byte *Buffer_alloc_writable(word capacity) {
153 byte *result = mmap(/*addr=*/NULL, capacity, PROT_READ | PROT_WRITE,
154 MAP_ANONYMOUS | MAP_PRIVATE,
155 /*filedes=*/-1, /*off=*/0);
156 assert(result != MAP_FAILED);
157 return result;
158}
159
160void Buffer_init(Buffer *result, word capacity) {
161 result->address = Buffer_alloc_writable(capacity);
162 assert(result->address != MAP_FAILED);
163 result->state = kWritable;
164 result->len = 0;
165 result->capacity = capacity;
166 result->entrypoint = 0;
167}
168
169word Buffer_len(Buffer *buf) { return buf->len; }
170
171void Buffer_deinit(Buffer *buf) {
172 munmap(buf->address, buf->capacity);
173 buf->address = NULL;
174 buf->len = 0;
175 buf->capacity = 0;
176 buf->entrypoint = 0;
177}
178
179int Buffer_make_executable(Buffer *buf) {
180 int result = mprotect(buf->address, buf->len, PROT_EXEC);
181 buf->state = kExecutable;
182 return result;
183}
184
185byte Buffer_at8(Buffer *buf, word pos) { return buf->address[pos]; }
186
187void Buffer_at_put8(Buffer *buf, word pos, byte b) { buf->address[pos] = b; }
188
189word max(word left, word right) { return left > right ? left : right; }
190
191void Buffer_ensure_capacity(Buffer *buf, word additional_capacity) {
192 if (buf->len + additional_capacity <= buf->capacity) {
193 return;
194 }
195 word new_capacity =
196 max(buf->capacity * 2, buf->capacity + additional_capacity);
197 byte *address = Buffer_alloc_writable(new_capacity);
198 memcpy(address, buf->address, buf->len);
199 int result = munmap(buf->address, buf->capacity);
200 assert(result == 0 && "munmap failed");
201 buf->address = address;
202 buf->capacity = new_capacity;
203}
204
205void Buffer_write8(Buffer *buf, byte b) {
206 Buffer_ensure_capacity(buf, sizeof b);
207 Buffer_at_put8(buf, buf->len++, b);
208}
209
210void Buffer_write32(Buffer *buf, int32_t value) {
211 for (uword i = 0; i < sizeof(value); i++) {
212 Buffer_write8(buf, (value >> (i * kBitsPerByte)) & 0xff);
213 }
214}
215
216void Buffer_at_put32(Buffer *buf, word offset, int32_t value) {
217 for (uword i = 0; i < sizeof(value); i++) {
218 Buffer_at_put8(buf, offset + i, (value >> (i * kBitsPerByte)) & 0xff);
219 }
220}
221
222void Buffer_write_arr(Buffer *buf, const byte *arr, word arr_size) {
223 Buffer_ensure_capacity(buf, arr_size);
224 for (word i = 0; i < arr_size; i++) {
225 Buffer_write8(buf, arr[i]);
226 }
227}
228
229void Buffer_dump(Buffer *buf, FILE *fp) {
230 for (word i = 0; i < Buffer_len(buf); i++) {
231 fprintf(fp, "%.2x ", buf->address[i]);
232 }
233 fprintf(fp, "\n");
234}
235
236// End Buffer
237
238// Emit
239
240typedef enum {
241 kRax = 0,
242 kRcx,
243 kRdx,
244 kRbx,
245 kRsp,
246 kRbp,
247 kRsi,
248 kRdi,
249} Register;
250
251typedef enum {
252 kAl = 0,
253 kCl,
254 kDl,
255 kBl,
256 kAh,
257 kCh,
258 kDh,
259 kBh,
260} PartialRegister;
261
262typedef enum {
263 kOverflow = 0,
264 kNotOverflow,
265 kBelow,
266 kCarry = kBelow,
267 kNotAboveOrEqual = kBelow,
268 kAboveOrEqual,
269 kNotBelow = kAboveOrEqual,
270 kNotCarry = kAboveOrEqual,
271 kEqual,
272 kZero = kEqual,
273 kLess = 0xc,
274 kNotGreaterOrEqual = kLess,
275 // TODO(max): Add more
276} Condition;
277
278typedef struct Indirect {
279 Register reg;
280 word disp;
281} Indirect;
282
283Indirect Ind(Register reg, word disp) {
284 return (Indirect){.reg = reg, .disp = disp};
285}
286
287// [ Instruction Prefixes (1 byte, optional) ]
288// [ Opcode (1, 2, or 3 bytes) ]
289// [ ModR/M (1 byte, if required) ]
290// [ Scale-Index-Base (1 byte, if required) ]
291// [ Displacement (1, 2, or 4 bytes, if required) ]
292// [ Immediate data (1, 2, or 4 bytes, if required) ]
293
294// http://www.c-jump.com/CIS77/CPU/x86/lecture.html
295// https://wiki.osdev.org/X86-64_Instruction_Encoding
296
297enum {
298 kRexPrefix = 0x48,
299};
300
301typedef enum {
302 Scale1 = 0,
303 Scale2,
304 Scale4,
305 Scale8,
306} Scale;
307
308typedef enum {
309 kIndexRax = 0,
310 kIndexRcx,
311 kIndexRdx,
312 kIndexRbx,
313 kIndexNone,
314 kIndexRbp,
315 kIndexRsi,
316 kIndexRdi
317} Index;
318
319byte modrm(byte mod, byte rm, byte reg) {
320 return ((mod & 0x3) << 6) | ((reg & 0x7) << 3) | (rm & 0x7);
321}
322
323byte sib(Register base, Index index, Scale scale) {
324 return ((scale & 0x3) << 6) | ((index & 0x7) << 3) | (base & 0x7);
325}
326
327void Emit_mov_reg_imm32(Buffer *buf, Register dst, int32_t src) {
328 Buffer_write8(buf, kRexPrefix);
329 Buffer_write8(buf, 0xc7);
330 Buffer_write8(buf, modrm(/*direct*/ 3, dst, 0));
331 Buffer_write32(buf, src);
332}
333
334void Emit_ret(Buffer *buf) { Buffer_write8(buf, 0xc3); }
335
336void Emit_add_reg_imm32(Buffer *buf, Register dst, int32_t src) {
337 Buffer_write8(buf, kRexPrefix);
338 if (dst == kRax) {
339 // Optimization: add eax, {imm32} can either be encoded as 05 {imm32} or 81
340 // c0 {imm32}.
341 Buffer_write8(buf, 0x05);
342 } else {
343 Buffer_write8(buf, 0x81);
344 Buffer_write8(buf, modrm(/*direct*/ 3, dst, 0));
345 }
346 Buffer_write32(buf, src);
347}
348
349void Emit_sub_reg_imm32(Buffer *buf, Register dst, int32_t src) {
350 Buffer_write8(buf, kRexPrefix);
351 if (dst == kRax) {
352 // Optimization: sub eax, {imm32} can either be encoded as 2d {imm32} or 81
353 // e8 {imm32}.
354 Buffer_write8(buf, 0x2d);
355 } else {
356 Buffer_write8(buf, 0x81);
357 Buffer_write8(buf, modrm(/*direct*/ 3, dst, 5));
358 }
359 Buffer_write32(buf, src);
360}
361
362void Emit_shl_reg_imm8(Buffer *buf, Register dst, int8_t bits) {
363 Buffer_write8(buf, kRexPrefix);
364 Buffer_write8(buf, 0xc1);
365 Buffer_write8(buf, modrm(/*direct*/ 3, dst, 4));
366 Buffer_write8(buf, bits);
367}
368
369void Emit_shr_reg_imm8(Buffer *buf, Register dst, int8_t bits) {
370 Buffer_write8(buf, kRexPrefix);
371 Buffer_write8(buf, 0xc1);
372 Buffer_write8(buf, modrm(/*direct*/ 3, dst, 5));
373 Buffer_write8(buf, bits);
374}
375
376void Emit_or_reg_imm8(Buffer *buf, Register dst, uint8_t tag) {
377 Buffer_write8(buf, kRexPrefix);
378 Buffer_write8(buf, 0x83);
379 Buffer_write8(buf, modrm(/*direct*/ 3, dst, 1));
380 Buffer_write8(buf, tag);
381}
382
383void Emit_and_reg_imm8(Buffer *buf, Register dst, uint8_t tag) {
384 Buffer_write8(buf, kRexPrefix);
385 Buffer_write8(buf, 0x83);
386 Buffer_write8(buf, modrm(/*direct*/ 3, dst, 4));
387 Buffer_write8(buf, tag);
388}
389
390void Emit_cmp_reg_imm32(Buffer *buf, Register left, int32_t right) {
391 Buffer_write8(buf, kRexPrefix);
392 if (left == kRax) {
393 // Optimization: cmp rax, {imm32} can either be encoded as 3d {imm32} or 81
394 // f8 {imm32}.
395 Buffer_write8(buf, 0x3d);
396 } else {
397 Buffer_write8(buf, 0x81);
398 Buffer_write8(buf, modrm(/*direct*/ 3, left, 7));
399 }
400 Buffer_write32(buf, right);
401}
402
403void Emit_setcc_imm8(Buffer *buf, Condition cond, PartialRegister dst) {
404 // TODO(max): Emit a REX prefix if we need anything above RDI.
405 Buffer_write8(buf, 0x0f);
406 Buffer_write8(buf, 0x90 + cond);
407 Buffer_write8(buf, 0xc0 + (dst & 0x7));
408}
409
410uint8_t disp8(int8_t disp) { return disp >= 0 ? disp : 0x100 + disp; }
411
412void Emit_address_disp8(Buffer *buf, Register direct, Indirect indirect) {
413 if (indirect.reg == kRsp) {
414 Buffer_write8(buf, modrm(/*disp8*/ 1, kIndexNone, direct));
415 Buffer_write8(buf, sib(kRsp, kIndexNone, Scale1));
416 } else {
417 Buffer_write8(buf, modrm(/*disp8*/ 1, indirect.reg, direct));
418 }
419 Buffer_write8(buf, disp8(indirect.disp));
420}
421
422// mov [dst+disp], src
423// or
424// mov %src, disp(%dst)
425void Emit_store_reg_indirect(Buffer *buf, Indirect dst, Register src) {
426 Buffer_write8(buf, kRexPrefix);
427 Buffer_write8(buf, 0x89);
428 Emit_address_disp8(buf, src, dst);
429}
430
431// add dst, [src+disp]
432// or
433// add disp(%src), %dst
434void Emit_add_reg_indirect(Buffer *buf, Register dst, Indirect src) {
435 Buffer_write8(buf, kRexPrefix);
436 Buffer_write8(buf, 0x03);
437 Emit_address_disp8(buf, dst, src);
438}
439
440// sub dst, [src+disp]
441// or
442// sub disp(%src), %dst
443void Emit_sub_reg_indirect(Buffer *buf, Register dst, Indirect src) {
444 Buffer_write8(buf, kRexPrefix);
445 Buffer_write8(buf, 0x2b);
446 Emit_address_disp8(buf, dst, src);
447}
448
449// mul rax, [src+disp]
450// or
451// mul disp(%src), %rax
452void Emit_mul_reg_indirect(Buffer *buf, Indirect src) {
453 Buffer_write8(buf, kRexPrefix);
454 Buffer_write8(buf, 0xf7);
455 Emit_address_disp8(buf, /*subop*/ 4, src);
456}
457
458// cmp left, [right+disp]
459// or
460// cmp disp(%right), %left
461void Emit_cmp_reg_indirect(Buffer *buf, Register left, Indirect right) {
462 Buffer_write8(buf, kRexPrefix);
463 Buffer_write8(buf, 0x3b);
464 Emit_address_disp8(buf, left, right);
465}
466
467// mov dst, [src+disp]
468// or
469// mov disp(%src), %dst
470void Emit_load_reg_indirect(Buffer *buf, Register dst, Indirect src) {
471 Buffer_write8(buf, kRexPrefix);
472 Buffer_write8(buf, 0x8b);
473 Emit_address_disp8(buf, dst, src);
474}
475
476uint32_t disp32(int32_t disp) { return disp >= 0 ? disp : 0x100000000 + disp; }
477
478word Emit_jcc(Buffer *buf, Condition cond, int32_t offset) {
479 Buffer_write8(buf, 0x0f);
480 Buffer_write8(buf, 0x80 + cond);
481 word pos = Buffer_len(buf);
482 Buffer_write32(buf, disp32(offset));
483 return pos;
484}
485
486word Emit_jmp(Buffer *buf, int32_t offset) {
487 Buffer_write8(buf, 0xe9);
488 word pos = Buffer_len(buf);
489 Buffer_write32(buf, disp32(offset));
490 return pos;
491}
492
493void Emit_backpatch_imm32(Buffer *buf, int32_t target_pos) {
494 word current_pos = Buffer_len(buf);
495 word relative_pos = current_pos - target_pos - sizeof(int32_t);
496 Buffer_at_put32(buf, target_pos, disp32(relative_pos));
497}
498
499void Emit_mov_reg_reg(Buffer *buf, Register dst, Register src) {
500 Buffer_write8(buf, kRexPrefix);
501 Buffer_write8(buf, 0x89);
502 Buffer_write8(buf, modrm(/*direct*/ 3, dst, src));
503}
504
505// mov [dst+disp], imm32
506// or
507// mov imm32, disp(%dst)
508void Emit_store_indirect_imm32(Buffer *buf, Indirect dst, int32_t src) {
509 Buffer_write8(buf, kRexPrefix);
510 Buffer_write8(buf, 0xc7);
511 Emit_address_disp8(buf, /*/0*/ 0, dst);
512 Buffer_write32(buf, src);
513}
514
515void Emit_rsp_adjust(Buffer *buf, word adjust) {
516 if (adjust < 0) {
517 Emit_sub_reg_imm32(buf, kRsp, -adjust);
518 } else if (adjust > 0) {
519 Emit_add_reg_imm32(buf, kRsp, adjust);
520 }
521}
522
523void Emit_call_imm32(Buffer *buf, word absolute_address) {
524 // 5 is length of call instruction
525 word relative_address = absolute_address - (Buffer_len(buf) + 5);
526 Buffer_write8(buf, 0xe8);
527 Buffer_write32(buf, relative_address);
528}
529
530// End Emit
531
532// AST
533
534typedef struct ASTNode ASTNode;
535
536typedef struct Pair {
537 ASTNode *car;
538 ASTNode *cdr;
539} Pair;
540
541typedef struct Symbol {
542 word length;
543 char cstr[];
544} Symbol;
545
546bool AST_is_integer(ASTNode *node) {
547 return ((uword)node & kIntegerTagMask) == kIntegerTag;
548}
549
550word AST_get_integer(ASTNode *node) {
551 return Object_decode_integer((uword)node);
552}
553
554ASTNode *AST_new_integer(word value) {
555 return (ASTNode *)Object_encode_integer(value);
556}
557
558bool AST_is_char(ASTNode *node) {
559 return ((uword)node & kImmediateTagMask) == kCharTag;
560}
561
562char AST_get_char(ASTNode *node) { return Object_decode_char((uword)node); }
563
564ASTNode *AST_new_char(char value) {
565 return (ASTNode *)Object_encode_char(value);
566}
567
568bool AST_is_bool(ASTNode *node) {
569 return ((uword)node & kImmediateTagMask) == kBoolTag;
570}
571
572bool AST_get_bool(ASTNode *node) { return Object_decode_bool((uword)node); }
573
574ASTNode *AST_new_bool(bool value) {
575 return (ASTNode *)Object_encode_bool(value);
576}
577
578bool AST_is_nil(ASTNode *node) { return (uword)node == Object_nil(); }
579
580ASTNode *AST_nil() { return (ASTNode *)Object_nil(); }
581
582bool AST_is_error(ASTNode *node) { return (uword)node == Object_error(); }
583
584ASTNode *AST_error() { return (ASTNode *)Object_error(); }
585
586ASTNode *AST_heap_alloc(unsigned char tag, uword size) {
587 // Initialize to 0
588 uword address = (uword)calloc(size, 1);
589 return (ASTNode *)(address | tag);
590}
591
592bool AST_is_heap_object(ASTNode *node) {
593 // For some reason masking out the tag first and then doing the comparison
594 // makes this branchless
595 unsigned char tag = (uword)node & kHeapTagMask;
596 // Heap object tags are between 0b001 and 0b110 except for 0b100 (which is an
597 // integer)
598 return (tag & kIntegerTagMask) > 0 && (tag & kImmediateTagMask) != 0x7;
599}
600
601void AST_pair_set_car(ASTNode *node, ASTNode *car);
602void AST_pair_set_cdr(ASTNode *node, ASTNode *cdr);
603
604ASTNode *AST_new_pair(ASTNode *car, ASTNode *cdr) {
605 ASTNode *node = AST_heap_alloc(kPairTag, sizeof(Pair));
606 AST_pair_set_car(node, car);
607 AST_pair_set_cdr(node, cdr);
608 return node;
609}
610
611bool AST_is_pair(ASTNode *node) {
612 return ((uword)node & kHeapTagMask) == kPairTag;
613}
614
615Pair *AST_as_pair(ASTNode *node) {
616 assert(AST_is_pair(node));
617 return (Pair *)Object_address(node);
618}
619
620ASTNode *AST_pair_car(ASTNode *node) { return AST_as_pair(node)->car; }
621
622void AST_pair_set_car(ASTNode *node, ASTNode *car) {
623 AST_as_pair(node)->car = car;
624}
625
626ASTNode *AST_pair_cdr(ASTNode *node) { return AST_as_pair(node)->cdr; }
627
628void AST_pair_set_cdr(ASTNode *node, ASTNode *cdr) {
629 AST_as_pair(node)->cdr = cdr;
630}
631
632void AST_heap_free(ASTNode *node) {
633 if (!AST_is_heap_object(node)) {
634 return;
635 }
636 if (AST_is_pair(node)) {
637 AST_heap_free(AST_pair_car(node));
638 AST_heap_free(AST_pair_cdr(node));
639 }
640 free((void *)Object_address(node));
641}
642
643Symbol *AST_as_symbol(ASTNode *node);
644
645ASTNode *AST_new_symbol(const char *str) {
646 word data_length = strlen(str) + 1; // for NUL
647 ASTNode *node = AST_heap_alloc(kSymbolTag, sizeof(Symbol) + data_length);
648 Symbol *s = AST_as_symbol(node);
649 s->length = data_length;
650 memcpy(s->cstr, str, data_length);
651 return node;
652}
653
654bool AST_is_symbol(ASTNode *node) {
655 return ((uword)node & kHeapTagMask) == kSymbolTag;
656}
657
658Symbol *AST_as_symbol(ASTNode *node) {
659 assert(AST_is_symbol(node));
660 return (Symbol *)Object_address(node);
661}
662
663const char *AST_symbol_cstr(ASTNode *node) {
664 return (const char *)AST_as_symbol(node)->cstr;
665}
666
667bool AST_symbol_matches(ASTNode *node, const char *cstr) {
668 return strcmp(AST_symbol_cstr(node), cstr) == 0;
669}
670
671int node_to_str(ASTNode *node, char *buf, word size);
672
673int list_to_str(ASTNode *node, char *buf, word size) {
674 if (AST_is_pair(node)) {
675 word result = 0;
676 result += snprintf(buf + result, size, " ");
677 result += node_to_str(AST_pair_car(node), buf + result, size);
678 result += list_to_str(AST_pair_cdr(node), buf + result, size);
679 return result;
680 }
681 if (AST_is_nil(node)) {
682 return snprintf(buf, size, ")");
683 }
684 word result = 0;
685 result += snprintf(buf + result, size, " . ");
686 result += node_to_str(node, buf + result, size);
687 result += snprintf(buf + result, size, ")");
688 return result;
689}
690
691int node_to_str(ASTNode *node, char *buf, word size) {
692 if (AST_is_integer(node)) {
693 return snprintf(buf, size, "%ld", AST_get_integer(node));
694 }
695 if (AST_is_char(node)) {
696 return snprintf(buf, size, "'%c'", AST_get_char(node));
697 }
698 if (AST_is_bool(node)) {
699 return snprintf(buf, size, "%s", AST_get_bool(node) ? "true" : "false");
700 }
701 if (AST_is_nil(node)) {
702 return snprintf(buf, size, "nil");
703 }
704 if (AST_is_pair(node)) {
705 word result = 0;
706 result += snprintf(buf + result, size, "(");
707 result += node_to_str(AST_pair_car(node), buf + result, size);
708 result += list_to_str(AST_pair_cdr(node), buf + result, size);
709 return result;
710 }
711 if (AST_is_symbol(node)) {
712 return snprintf(buf, size, "%s", AST_symbol_cstr(node));
713 }
714 assert(0 && "unknown ast");
715}
716
717char *AST_to_cstr(ASTNode *node) {
718 int size = node_to_str(node, NULL, 0);
719 char *buf = malloc(size + 1);
720 assert(buf != NULL);
721 node_to_str(node, buf, size + 1);
722 buf[size] = '\0';
723 return buf;
724}
725
726// End AST
727
728// Reader
729
730void advance(word *pos) { ++*pos; }
731
732char next(char *input, word *pos) {
733 advance(pos);
734 return input[*pos];
735}
736
737ASTNode *read_integer(char *input, word *pos, int sign) {
738 word result = 0;
739 for (char c = input[*pos]; isdigit(c); c = next(input, pos)) {
740 result *= 10;
741 result += c - '0';
742 }
743 return AST_new_integer(sign * result);
744}
745
746bool starts_symbol(char c) {
747 switch (c) {
748 case '+':
749 case '-':
750 case '*':
751 case '<':
752 case '>':
753 case '=':
754 case '?':
755 return true;
756 default:
757 return isalpha(c);
758 }
759}
760
761bool is_symbol_char(char c) { return starts_symbol(c) || isdigit(c); }
762
763const word ATOM_MAX = 32;
764
765ASTNode *read_symbol(char *input, word *pos) {
766 char buf[ATOM_MAX + 1]; // +1 for NUL
767 word length = 0;
768 for (length = 0; length < ATOM_MAX && is_symbol_char(input[*pos]); length++) {
769 buf[length] = input[*pos];
770 advance(pos);
771 }
772 buf[length] = '\0';
773 return AST_new_symbol(buf);
774}
775
776ASTNode *read_char(char *input, word *pos) {
777 char c = input[*pos];
778 if (c == '\'') {
779 return AST_error();
780 }
781 advance(pos);
782 if (input[*pos] != '\'') {
783 return AST_error();
784 }
785 advance(pos);
786 return AST_new_char(c);
787}
788
789char skip_whitespace(char *input, word *pos) {
790 char c = '\0';
791 for (c = input[*pos]; isspace(c); c = next(input, pos)) {
792 ;
793 }
794 return c;
795}
796
797ASTNode *read_rec(char *input, word *pos);
798
799ASTNode *read_list(char *input, word *pos) {
800 char c = skip_whitespace(input, pos);
801 if (c == ')') {
802 advance(pos);
803 return AST_nil();
804 }
805 ASTNode *car = read_rec(input, pos);
806 assert(car != AST_error());
807 ASTNode *cdr = read_list(input, pos);
808 assert(cdr != AST_error());
809 return AST_new_pair(car, cdr);
810}
811
812ASTNode *read_rec(char *input, word *pos) {
813 char c = skip_whitespace(input, pos);
814 if (isdigit(c)) {
815 return read_integer(input, pos, /*sign=*/1);
816 }
817 if (c == '-' && isdigit(input[*pos + 1])) {
818 advance(pos);
819 return read_integer(input, pos, /*sign=*/-1);
820 }
821 if (c == '+' && isdigit(input[*pos + 1])) {
822 advance(pos);
823 return read_integer(input, pos, /*sign=*/1);
824 }
825 if (starts_symbol(c)) {
826 return read_symbol(input, pos);
827 }
828 if (c == '\'') {
829 advance(pos); // skip '\''
830 return read_char(input, pos);
831 }
832 if (c == '#' && input[*pos + 1] == 't') {
833 advance(pos); // skip '#'
834 advance(pos); // skip 't'
835 return AST_new_bool(true);
836 }
837 if (c == '#' && input[*pos + 1] == 'f') {
838 advance(pos); // skip '#'
839 advance(pos); // skip 'f'
840 return AST_new_bool(false);
841 }
842 if (c == '(') {
843 advance(pos); // skip '('
844 return read_list(input, pos);
845 }
846 return AST_error();
847}
848
849ASTNode *Reader_read(char *input) {
850 word pos = 0;
851 return read_rec(input, &pos);
852}
853
854// End Reader
855
856// Env
857
858typedef struct Env {
859 const char *name;
860 word value;
861 struct Env *prev;
862} Env;
863
864Env Env_bind(const char *name, word value, Env *prev) {
865 return (Env){.name = name, .value = value, .prev = prev};
866}
867
868bool Env_find(Env *env, const char *key, word *result) {
869 if (env == NULL)
870 return false;
871 if (strcmp(env->name, key) == 0) {
872 *result = env->value;
873 return true;
874 }
875 return Env_find(env->prev, key, result);
876}
877
878// End Env
879
880// Compile
881
882WARN_UNUSED int Compile_expr(Buffer *buf, ASTNode *node, word stack_index,
883 Env *varenv, Env *labels);
884
885ASTNode *operand1(ASTNode *args) { return AST_pair_car(args); }
886
887ASTNode *operand2(ASTNode *args) { return AST_pair_car(AST_pair_cdr(args)); }
888
889ASTNode *operand3(ASTNode *args) {
890 return AST_pair_car(AST_pair_cdr(AST_pair_cdr(args)));
891}
892
893#define _(exp) \
894 do { \
895 int result = exp; \
896 if (result != 0) \
897 return result; \
898 } while (0)
899
900void Compile_compare_imm32(Buffer *buf, int32_t value) {
901 Emit_cmp_reg_imm32(buf, kRax, value);
902 Emit_mov_reg_imm32(buf, kRax, 0);
903 Emit_setcc_imm8(buf, kEqual, kAl);
904 Emit_shl_reg_imm8(buf, kRax, kBoolShift);
905 Emit_or_reg_imm8(buf, kRax, kBoolTag);
906}
907
908// This is let, not let*. Therefore we keep track of two environments -- the
909// parent environment, for evaluating the bindings, and the body environment,
910// which will have all of the bindings in addition to the parent. This makes
911// programs like (let ((a 1) (b a)) b) fail.
912WARN_UNUSED int Compile_let(Buffer *buf, ASTNode *bindings, ASTNode *body,
913 word stack_index, Env *binding_env, Env *body_env,
914 Env *labels) {
915 if (AST_is_nil(bindings)) {
916 // Base case: no bindings. Compile the body
917 _(Compile_expr(buf, body, stack_index, body_env, labels));
918 return 0;
919 }
920 assert(AST_is_pair(bindings));
921 // Get the next binding
922 ASTNode *binding = AST_pair_car(bindings);
923 ASTNode *name = AST_pair_car(binding);
924 assert(AST_is_symbol(name));
925 ASTNode *binding_expr = AST_pair_car(AST_pair_cdr(binding));
926 // Compile the binding expression
927 _(Compile_expr(buf, binding_expr, stack_index, binding_env, labels));
928 Emit_store_reg_indirect(buf, /*dst=*/Ind(kRsp, stack_index),
929 /*src=*/kRax);
930 // Bind the name
931 Env entry = Env_bind(AST_symbol_cstr(name), stack_index, body_env);
932 _(Compile_let(buf, AST_pair_cdr(bindings), body, stack_index - kWordSize,
933 /*binding_env=*/binding_env, /*body_env=*/&entry, labels));
934 return 0;
935}
936
937const int32_t kLabelPlaceholder = 0xdeadbeef;
938
939WARN_UNUSED int Compile_if(Buffer *buf, ASTNode *cond, ASTNode *consequent,
940 ASTNode *alternate, word stack_index, Env *varenv,
941 Env *labels) {
942 _(Compile_expr(buf, cond, stack_index, varenv, labels));
943 Emit_cmp_reg_imm32(buf, kRax, Object_false());
944 word alternate_pos = Emit_jcc(buf, kEqual, kLabelPlaceholder); // je alternate
945 _(Compile_expr(buf, consequent, stack_index, varenv, labels));
946 word end_pos = Emit_jmp(buf, kLabelPlaceholder); // jmp end
947 Emit_backpatch_imm32(buf, alternate_pos); // alternate:
948 _(Compile_expr(buf, alternate, stack_index, varenv, labels));
949 Emit_backpatch_imm32(buf, end_pos); // end:
950 return 0;
951}
952
953const Register kHeapPointer = kRsi;
954
955WARN_UNUSED int Compile_cons(Buffer *buf, ASTNode *car, ASTNode *cdr,
956 word stack_index, Env *varenv, Env *labels) {
957 // Compile and store car on the stack
958 _(Compile_expr(buf, car, stack_index, varenv, labels));
959 Emit_store_reg_indirect(buf,
960 /*dst=*/Ind(kRsp, stack_index),
961 /*src=*/kRax);
962 // Compile and store cdr
963 _(Compile_expr(buf, cdr, stack_index - kWordSize, varenv, labels));
964 Emit_store_reg_indirect(buf, /*dst=*/Ind(kHeapPointer, kCdrOffset),
965 /*src=*/kRax);
966 // Fetch car and store in the heap
967 Emit_load_reg_indirect(buf, /*dst=*/kRax, /*src=*/Ind(kRsp, stack_index));
968 Emit_store_reg_indirect(buf, /*dst=*/Ind(kHeapPointer, kCarOffset),
969 /*src=*/kRax);
970 // Store tagged pointer in rax
971 // TODO(max): Rewrite as lea rax, [rsi+kPairTag]
972 Emit_mov_reg_reg(buf, /*dst=*/kRax, /*src=*/kHeapPointer);
973 Emit_or_reg_imm8(buf, /*dst=*/kRax, kPairTag);
974 // Bump the heap pointer
975 Emit_add_reg_imm32(buf, /*dst=*/kHeapPointer, kPairSize);
976 return 0;
977}
978
979word list_length(ASTNode *node) {
980 if (AST_is_nil(node)) {
981 return 0;
982 }
983 assert(AST_is_pair(node));
984 return 1 + list_length(AST_pair_cdr(node));
985}
986
987WARN_UNUSED int Compile_labelcall(Buffer *buf, ASTNode *callable, ASTNode *args,
988 word stack_index, Env *varenv, Env *labels,
989 word rsp_adjust) {
990 if (AST_is_nil(args)) {
991 word code_address;
992 if (!Env_find(labels, AST_symbol_cstr(callable), &code_address)) {
993 return -1;
994 }
995 // TODO(max): Determine if we need to align the stack to 16 bytes
996 // Save the locals
997 Emit_rsp_adjust(buf, rsp_adjust);
998 Emit_call_imm32(buf, code_address);
999 // Unsave the locals
1000 Emit_rsp_adjust(buf, -rsp_adjust);
1001 return 0;
1002 }
1003 assert(AST_is_pair(args));
1004 ASTNode *arg = AST_pair_car(args);
1005 _(Compile_expr(buf, arg, stack_index, varenv, labels));
1006 Emit_store_reg_indirect(buf, Ind(kRsp, stack_index), kRax);
1007 return Compile_labelcall(buf, callable, AST_pair_cdr(args),
1008 stack_index - kWordSize, varenv, labels, rsp_adjust);
1009}
1010
1011WARN_UNUSED int Compile_call(Buffer *buf, ASTNode *callable, ASTNode *args,
1012 word stack_index, Env *varenv, Env *labels) {
1013 if (AST_is_symbol(callable)) {
1014 if (AST_symbol_matches(callable, "add1")) {
1015 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1016 Emit_add_reg_imm32(buf, kRax, Object_encode_integer(1));
1017 return 0;
1018 }
1019 if (AST_symbol_matches(callable, "sub1")) {
1020 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1021 Emit_sub_reg_imm32(buf, kRax, Object_encode_integer(1));
1022 return 0;
1023 }
1024 if (AST_symbol_matches(callable, "integer->char")) {
1025 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1026 Emit_shl_reg_imm8(buf, kRax, kCharShift - kIntegerShift);
1027 Emit_or_reg_imm8(buf, kRax, kCharTag);
1028 return 0;
1029 }
1030 if (AST_symbol_matches(callable, "char->integer")) {
1031 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1032 Emit_shr_reg_imm8(buf, kRax, kCharShift - kIntegerShift);
1033 return 0;
1034 }
1035 if (AST_symbol_matches(callable, "nil?")) {
1036 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1037 Compile_compare_imm32(buf, Object_nil());
1038 return 0;
1039 }
1040 if (AST_symbol_matches(callable, "zero?")) {
1041 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1042 Compile_compare_imm32(buf, Object_encode_integer(0));
1043 return 0;
1044 }
1045 if (AST_symbol_matches(callable, "not")) {
1046 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1047 // All non #f values are truthy
1048 // ...this might be a problem if we want to make nil falsey
1049 Compile_compare_imm32(buf, Object_false());
1050 return 0;
1051 }
1052 if (AST_symbol_matches(callable, "integer?")) {
1053 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1054 Emit_and_reg_imm8(buf, kRax, kIntegerTagMask);
1055 Compile_compare_imm32(buf, kIntegerTag);
1056 return 0;
1057 }
1058 if (AST_symbol_matches(callable, "boolean?")) {
1059 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1060 Emit_and_reg_imm8(buf, kRax, kImmediateTagMask);
1061 Compile_compare_imm32(buf, kBoolTag);
1062 return 0;
1063 }
1064 if (AST_symbol_matches(callable, "+")) {
1065 _(Compile_expr(buf, operand2(args), stack_index, varenv, labels));
1066 Emit_store_reg_indirect(buf, /*dst=*/Ind(kRsp, stack_index),
1067 /*src=*/kRax);
1068 _(Compile_expr(buf, operand1(args), stack_index - kWordSize, varenv,
1069 labels));
1070 Emit_add_reg_indirect(buf, /*dst=*/kRax, /*src=*/Ind(kRsp, stack_index));
1071 return 0;
1072 }
1073 if (AST_symbol_matches(callable, "-")) {
1074 _(Compile_expr(buf, operand2(args), stack_index, varenv, labels));
1075 Emit_store_reg_indirect(buf, /*dst=*/Ind(kRsp, stack_index),
1076 /*src=*/kRax);
1077 _(Compile_expr(buf, operand1(args), stack_index - kWordSize, varenv,
1078 labels));
1079 Emit_sub_reg_indirect(buf, /*dst=*/kRax, /*src=*/Ind(kRsp, stack_index));
1080 return 0;
1081 }
1082 if (AST_symbol_matches(callable, "*")) {
1083 _(Compile_expr(buf, operand2(args), stack_index, varenv, labels));
1084 // Remove the tag so that the result is still only tagged with 0b00
1085 // instead of 0b0000
1086 Emit_shr_reg_imm8(buf, kRax, kIntegerShift);
1087 Emit_store_reg_indirect(buf, /*dst=*/Ind(kRsp, stack_index),
1088 /*src=*/kRax);
1089 _(Compile_expr(buf, operand1(args), stack_index - kWordSize, varenv,
1090 labels));
1091 Emit_mul_reg_indirect(buf, /*src=*/Ind(kRsp, stack_index));
1092 return 0;
1093 }
1094 if (AST_symbol_matches(callable, "=")) {
1095 _(Compile_expr(buf, operand2(args), stack_index, varenv, labels));
1096 Emit_store_reg_indirect(buf, /*dst=*/Ind(kRsp, stack_index),
1097 /*src=*/kRax);
1098 _(Compile_expr(buf, operand1(args), stack_index - kWordSize, varenv,
1099 labels));
1100 Emit_cmp_reg_indirect(buf, kRax, Ind(kRsp, stack_index));
1101 Emit_mov_reg_imm32(buf, kRax, 0);
1102 Emit_setcc_imm8(buf, kEqual, kAl);
1103 Emit_shl_reg_imm8(buf, kRax, kBoolShift);
1104 Emit_or_reg_imm8(buf, kRax, kBoolTag);
1105 return 0;
1106 }
1107 if (AST_symbol_matches(callable, "<")) {
1108 _(Compile_expr(buf, operand2(args), stack_index, varenv, labels));
1109 Emit_store_reg_indirect(buf, /*dst=*/Ind(kRsp, stack_index),
1110 /*src=*/kRax);
1111 _(Compile_expr(buf, operand1(args), stack_index - kWordSize, varenv,
1112 labels));
1113 Emit_cmp_reg_indirect(buf, kRax, Ind(kRsp, stack_index));
1114 Emit_mov_reg_imm32(buf, kRax, 0);
1115 Emit_setcc_imm8(buf, kLess, kAl);
1116 Emit_shl_reg_imm8(buf, kRax, kBoolShift);
1117 Emit_or_reg_imm8(buf, kRax, kBoolTag);
1118 return 0;
1119 }
1120 if (AST_symbol_matches(callable, "let")) {
1121 return Compile_let(buf, /*bindings=*/operand1(args),
1122 /*body=*/operand2(args), stack_index,
1123 /*binding_env=*/varenv,
1124 /*body_env=*/varenv, labels);
1125 }
1126 if (AST_symbol_matches(callable, "if")) {
1127 return Compile_if(buf, /*condition=*/operand1(args),
1128 /*consequent=*/operand2(args),
1129 /*alternate=*/operand3(args), stack_index, varenv,
1130 labels);
1131 }
1132 if (AST_symbol_matches(callable, "cons")) {
1133 return Compile_cons(buf, /*car=*/operand1(args), /*cdr=*/operand2(args),
1134 stack_index, varenv, labels);
1135 }
1136 if (AST_symbol_matches(callable, "car")) {
1137 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1138 Emit_load_reg_indirect(buf, /*dst=*/kRax,
1139 /*src=*/Ind(kRax, kCarOffset - kPairTag));
1140 return 0;
1141 }
1142 if (AST_symbol_matches(callable, "cdr")) {
1143 _(Compile_expr(buf, operand1(args), stack_index, varenv, labels));
1144 Emit_load_reg_indirect(buf, /*dst=*/kRax,
1145 /*src=*/Ind(kRax, kCdrOffset - kPairTag));
1146 return 0;
1147 }
1148 if (AST_symbol_matches(callable, "labelcall")) {
1149 ASTNode *label = operand1(args);
1150 assert(AST_is_symbol(label));
1151 ASTNode *call_args = AST_pair_cdr(args);
1152 // Skip a space on the stack to put the return address
1153 word arg_stack_index = stack_index - kWordSize;
1154 // We enter Compile_call with a stack_index pointing to the next
1155 // available spot on the stack. Add kWordSize (stack_index is negative)
1156 // so that it is only a multiple of the number of locals N, not N+1.
1157 word rsp_adjust = stack_index + kWordSize;
1158 return Compile_labelcall(buf, label, call_args, arg_stack_index, varenv,
1159 labels, rsp_adjust);
1160 }
1161 }
1162 assert(0 && "unexpected call type");
1163}
1164
1165WARN_UNUSED int Compile_expr(Buffer *buf, ASTNode *node, word stack_index,
1166 Env *varenv, Env *labels) {
1167 if (AST_is_integer(node)) {
1168 word value = AST_get_integer(node);
1169 Emit_mov_reg_imm32(buf, kRax, Object_encode_integer(value));
1170 return 0;
1171 }
1172 if (AST_is_char(node)) {
1173 char value = AST_get_char(node);
1174 Emit_mov_reg_imm32(buf, kRax, Object_encode_char(value));
1175 return 0;
1176 }
1177 if (AST_is_bool(node)) {
1178 bool value = AST_get_bool(node);
1179 Emit_mov_reg_imm32(buf, kRax, Object_encode_bool(value));
1180 return 0;
1181 }
1182 if (AST_is_nil(node)) {
1183 Emit_mov_reg_imm32(buf, kRax, Object_nil());
1184 return 0;
1185 }
1186 if (AST_is_pair(node)) {
1187 return Compile_call(buf, AST_pair_car(node), AST_pair_cdr(node),
1188 stack_index, varenv, labels);
1189 }
1190 if (AST_is_symbol(node)) {
1191 const char *symbol = AST_symbol_cstr(node);
1192 word value;
1193 if (Env_find(varenv, symbol, &value)) {
1194 Emit_load_reg_indirect(buf, /*dst=*/kRax, /*src=*/Ind(kRsp, value));
1195 return 0;
1196 }
1197 return -1;
1198 }
1199 assert(0 && "unexpected node type");
1200}
1201
1202const byte kEntryPrologue[] = {
1203 // Save the heap in rsi, our global heap pointer
1204 // mov rsi, rdi
1205 kRexPrefix,
1206 0x89,
1207 0xfe,
1208};
1209
1210const byte kFunctionEpilogue[] = {
1211 // ret
1212 0xc3,
1213};
1214
1215WARN_UNUSED int Compile_code_impl(Buffer *buf, ASTNode *formals, ASTNode *body,
1216 word stack_index, Env *varenv, Env *labels) {
1217 if (AST_is_nil(formals)) {
1218 _(Compile_expr(buf, body, stack_index, varenv, labels));
1219 Buffer_write_arr(buf, kFunctionEpilogue, sizeof kFunctionEpilogue);
1220 return 0;
1221 }
1222 assert(AST_is_pair(formals));
1223 ASTNode *name = AST_pair_car(formals);
1224 assert(AST_is_symbol(name));
1225 Env entry = Env_bind(AST_symbol_cstr(name), stack_index, varenv);
1226 return Compile_code_impl(buf, AST_pair_cdr(formals), body,
1227 stack_index - kWordSize, &entry, labels);
1228}
1229
1230WARN_UNUSED int Compile_code(Buffer *buf, ASTNode *code, Env *labels) {
1231 assert(AST_is_pair(code));
1232 ASTNode *code_sym = AST_pair_car(code);
1233 assert(AST_is_symbol(code_sym));
1234 assert(AST_symbol_matches(code_sym, "code"));
1235 ASTNode *args = AST_pair_cdr(code);
1236 ASTNode *formals = operand1(args);
1237 ASTNode *code_body = operand2(args);
1238 return Compile_code_impl(buf, formals, code_body, /*stack_index=*/-kWordSize,
1239 /*varenv=*/NULL, labels);
1240}
1241
1242WARN_UNUSED int Compile_labels(Buffer *buf, ASTNode *bindings, ASTNode *body,
1243 Env *labels) {
1244 if (AST_is_nil(bindings)) {
1245 buf->entrypoint = Buffer_len(buf);
1246 // Base case: no bindings. Compile the body
1247 Buffer_write_arr(buf, kEntryPrologue, sizeof kEntryPrologue);
1248 _(Compile_expr(buf, body, /*stack_index=*/-kWordSize, /*varenv=*/NULL,
1249 labels));
1250 Buffer_write_arr(buf, kFunctionEpilogue, sizeof kFunctionEpilogue);
1251 return 0;
1252 }
1253 assert(AST_is_pair(bindings));
1254 // Get the next binding
1255 ASTNode *binding = AST_pair_car(bindings);
1256 ASTNode *name = AST_pair_car(binding);
1257 assert(AST_is_symbol(name));
1258 ASTNode *binding_code = AST_pair_car(AST_pair_cdr(binding));
1259 word function_location = Buffer_len(buf);
1260 // Bind the name to the location in the instruction stream
1261 Env entry = Env_bind(AST_symbol_cstr(name), function_location, labels);
1262 // Compile the binding function
1263 _(Compile_code(buf, binding_code, &entry));
1264 return Compile_labels(buf, AST_pair_cdr(bindings), body, &entry);
1265}
1266
1267WARN_UNUSED int Compile_entry(Buffer *buf, ASTNode *node) {
1268 assert(AST_is_pair(node) && "program must have labels");
1269 // Assume it's (labels ...)
1270 ASTNode *labels_sym = AST_pair_car(node);
1271 assert(AST_is_symbol(labels_sym) && "program must have labels");
1272 assert(AST_symbol_matches(labels_sym, "labels") &&
1273 "program must have labels");
1274 ASTNode *args = AST_pair_cdr(node);
1275 ASTNode *bindings = operand1(args);
1276 assert(AST_is_pair(bindings) || AST_is_nil(bindings));
1277 ASTNode *body = operand2(args);
1278 return Compile_labels(buf, bindings, body, /*labels=*/NULL);
1279}
1280
1281// End Compile
1282
1283typedef uword (*JitFunction)(uword *heap);
1284
1285// Testing
1286
1287uword Testing_execute_entry(Buffer *buf, uword *heap) {
1288 assert(buf != NULL);
1289 assert(buf->address != NULL);
1290 assert(buf->state == kExecutable);
1291 // The pointer-pointer cast is allowed but the underlying
1292 // data-to-function-pointer back-and-forth is only guaranteed to work on
1293 // POSIX systems (because of eg dlsym).
1294 byte *start_address = buf->address + buf->entrypoint;
1295 JitFunction function = *(JitFunction *)(&start_address);
1296 return function(heap);
1297}
1298
1299uword Testing_execute_expr(Buffer *buf) {
1300 return Testing_execute_entry(buf, /*heap=*/NULL);
1301}
1302
1303TEST Testing_expect_entry_has_contents(Buffer *buf, byte *arr,
1304 size_t arr_size) {
1305 word total_size = sizeof kEntryPrologue + arr_size + sizeof kFunctionEpilogue;
1306 ASSERT_EQ_FMT(total_size, Buffer_len(buf), "%ld");
1307
1308 byte *ptr = buf->address;
1309 ASSERT_MEM_EQ(kEntryPrologue, ptr, sizeof kEntryPrologue);
1310 ptr += sizeof kEntryPrologue;
1311 ASSERT_MEM_EQ(arr, ptr, arr_size);
1312 ptr += arr_size;
1313 ASSERT_MEM_EQ(kFunctionEpilogue, ptr, sizeof kFunctionEpilogue);
1314 ptr += sizeof kFunctionEpilogue;
1315 PASS();
1316}
1317
1318WARN_UNUSED int Testing_compile_expr_entry(Buffer *buf, ASTNode *node) {
1319 Buffer_write_arr(buf, kEntryPrologue, sizeof kEntryPrologue);
1320 _(Compile_expr(buf, node, /*stack_index=*/-kWordSize, /*varenv=*/NULL,
1321 /*labels=*/NULL));
1322 Buffer_write_arr(buf, kFunctionEpilogue, sizeof kFunctionEpilogue);
1323 return 0;
1324}
1325
1326#define EXPECT_EQUALS_BYTES(buf, arr) \
1327 ASSERT_EQ_FMT(sizeof arr, Buffer_len(buf), "%ld"); \
1328 ASSERT_MEM_EQ(arr, (buf)->address, sizeof arr)
1329
1330#define EXPECT_ENTRY_CONTAINS_CODE(buf, arr) \
1331 CHECK_CALL(Testing_expect_entry_has_contents(buf, arr, sizeof arr))
1332
1333#define RUN_BUFFER_TEST(test_name) \
1334 do { \
1335 Buffer buf; \
1336 Buffer_init(&buf, 1); \
1337 GREATEST_RUN_TEST1(test_name, &buf); \
1338 Buffer_deinit(&buf); \
1339 } while (0)
1340
1341#define RUN_HEAP_TEST(test_name) \
1342 do { \
1343 Buffer buf; \
1344 Buffer_init(&buf, 1); \
1345 uword *heap = malloc(1000 * kWordSize); \
1346 GREATEST_RUN_TESTp(test_name, &buf, heap); \
1347 free(heap); \
1348 Buffer_deinit(&buf); \
1349 } while (0)
1350
1351ASTNode *list1(ASTNode *item0) { return AST_new_pair(item0, AST_nil()); }
1352
1353ASTNode *list2(ASTNode *item0, ASTNode *item1) {
1354 return AST_new_pair(item0, list1(item1));
1355}
1356
1357ASTNode *list3(ASTNode *item0, ASTNode *item1, ASTNode *item2) {
1358 return AST_new_pair(item0, list2(item1, item2));
1359}
1360
1361ASTNode *new_unary_call(const char *name, ASTNode *arg) {
1362 return list2(AST_new_symbol(name), arg);
1363}
1364
1365ASTNode *new_binary_call(const char *name, ASTNode *arg0, ASTNode *arg1) {
1366 return list3(AST_new_symbol(name), arg0, arg1);
1367}
1368
1369// End Testing
1370
1371// Tests
1372
1373TEST encode_positive_integer(void) {
1374 ASSERT_EQ(Object_encode_integer(0), 0x0);
1375 ASSERT_EQ(Object_encode_integer(1), 0x4);
1376 ASSERT_EQ(Object_encode_integer(10), 0x28);
1377 PASS();
1378}
1379
1380TEST encode_negative_integer(void) {
1381 ASSERT_EQ(Object_encode_integer(0), 0x0);
1382 ASSERT_EQ(Object_encode_integer(-1), 0xfffffffffffffffc);
1383 ASSERT_EQ(Object_encode_integer(-10), 0xffffffffffffffd8);
1384 PASS();
1385}
1386
1387TEST encode_char(void) {
1388 ASSERT_EQ(Object_encode_char('\0'), 0xf);
1389 ASSERT_EQ(Object_encode_char('a'), 0x610f);
1390 PASS();
1391}
1392
1393TEST decode_char(void) {
1394 ASSERT_EQ(Object_decode_char(0xf), '\0');
1395 ASSERT_EQ(Object_decode_char(0x610f), 'a');
1396 PASS();
1397}
1398
1399TEST encode_bool(void) {
1400 ASSERT_EQ(Object_encode_bool(true), 0x9f);
1401 ASSERT_EQ(Object_encode_bool(false), 0x1f);
1402 ASSERT_EQ(Object_true(), 0x9f);
1403 ASSERT_EQ(Object_false(), 0x1f);
1404 PASS();
1405}
1406
1407TEST decode_bool(void) {
1408 ASSERT_EQ(Object_decode_bool(0x9f), true);
1409 ASSERT_EQ(Object_decode_bool(0x1f), false);
1410 PASS();
1411}
1412
1413TEST address(void) {
1414 ASSERT_EQ(Object_address((void *)0xFF01), 0xFF00);
1415 PASS();
1416}
1417
1418TEST emit_mov_reg_imm32_emits_modrm(Buffer *buf) {
1419 Emit_mov_reg_imm32(buf, kRax, 100);
1420 byte expected[] = {0x48, 0xc7, 0xc0, 0x64, 0x00, 0x00, 0x00};
1421 EXPECT_EQUALS_BYTES(buf, expected);
1422 ASSERT_EQ_FMT(modrm(/*direct*/ 3, kRax, 0), 0xc0, "0x%.2x");
1423 PASS();
1424}
1425
1426TEST emit_store_reg_indirect_emits_modrm_sib(Buffer *buf) {
1427 Emit_store_reg_indirect(buf, Ind(kRsp, -8), kRax);
1428 byte expected[] = {0x48, 0x89, 0x44, 0x24, 0xf8};
1429 EXPECT_EQUALS_BYTES(buf, expected);
1430 ASSERT_EQ_FMT(modrm(/*disp8*/ 1, kIndexNone, kRax), 0x44, "0x%.2x");
1431 ASSERT_EQ_FMT(sib(kRsp, kIndexNone, Scale1), 0x24, "0x%.2x");
1432 PASS();
1433}
1434
1435TEST ast_new_pair(void) {
1436 ASTNode *node = AST_new_pair(NULL, NULL);
1437 ASSERT(AST_is_pair(node));
1438 AST_heap_free(node);
1439 PASS();
1440}
1441
1442TEST ast_pair_car_returns_car(void) {
1443 ASTNode *node = AST_new_pair(AST_new_integer(123), NULL);
1444 ASTNode *car = AST_pair_car(node);
1445 ASSERT(AST_is_integer(car));
1446 ASSERT_EQ(Object_decode_integer((uword)car), 123);
1447 AST_heap_free(node);
1448 PASS();
1449}
1450
1451TEST ast_pair_cdr_returns_cdr(void) {
1452 ASTNode *node = AST_new_pair(NULL, AST_new_integer(123));
1453 ASTNode *cdr = AST_pair_cdr(node);
1454 ASSERT(AST_is_integer(cdr));
1455 ASSERT_EQ(Object_decode_integer((uword)cdr), 123);
1456 AST_heap_free(node);
1457 PASS();
1458}
1459
1460TEST ast_new_symbol(void) {
1461 const char *value = "my symbol";
1462 ASTNode *node = AST_new_symbol(value);
1463 ASSERT(AST_is_symbol(node));
1464 ASSERT_STR_EQ(AST_symbol_cstr(node), value);
1465 AST_heap_free(node);
1466 PASS();
1467}
1468
1469#define ASSERT_IS_CHAR_EQ(node, c) \
1470 do { \
1471 ASTNode *__tmp = node; \
1472 if (AST_is_error(__tmp)) { \
1473 fprintf(stderr, "Expected a char but got an error.\n"); \
1474 } \
1475 ASSERT(AST_is_char(__tmp)); \
1476 ASSERT_EQ(AST_get_char(__tmp), c); \
1477 } while (0);
1478
1479#define ASSERT_IS_INT_EQ(node, val) \
1480 do { \
1481 ASTNode *__tmp = node; \
1482 if (AST_is_error(__tmp)) { \
1483 fprintf(stderr, "Expected an int but got an error.\n"); \
1484 } \
1485 ASSERT(AST_is_integer(__tmp)); \
1486 ASSERT_EQ(AST_get_integer(__tmp), val); \
1487 } while (0);
1488
1489#define ASSERT_IS_SYM_EQ(node, cstr) \
1490 do { \
1491 ASTNode *__tmp = node; \
1492 if (AST_is_error(__tmp)) { \
1493 fprintf(stderr, "Expected a symbol but got an error.\n"); \
1494 } \
1495 ASSERT(AST_is_symbol(__tmp)); \
1496 ASSERT_STR_EQ(AST_symbol_cstr(__tmp), cstr); \
1497 } while (0);
1498
1499TEST read_with_integer_returns_integer(void) {
1500 char *input = "1234";
1501 ASTNode *node = Reader_read(input);
1502 ASSERT_IS_INT_EQ(node, 1234);
1503 AST_heap_free(node);
1504 PASS();
1505}
1506
1507TEST read_with_negative_integer_returns_integer(void) {
1508 char *input = "-1234";
1509 ASTNode *node = Reader_read(input);
1510 ASSERT_IS_INT_EQ(node, -1234);
1511 AST_heap_free(node);
1512 PASS();
1513}
1514
1515TEST read_with_positive_integer_returns_integer(void) {
1516 char *input = "+1234";
1517 ASTNode *node = Reader_read(input);
1518 ASSERT_IS_INT_EQ(node, 1234);
1519 AST_heap_free(node);
1520 PASS();
1521}
1522
1523TEST read_with_leading_whitespace_ignores_whitespace(void) {
1524 char *input = " \t \n 1234";
1525 ASTNode *node = Reader_read(input);
1526 ASSERT_IS_INT_EQ(node, 1234);
1527 AST_heap_free(node);
1528 PASS();
1529}
1530
1531TEST read_with_symbol_returns_symbol(void) {
1532 char *input = "hello?+-*=>";
1533 ASTNode *node = Reader_read(input);
1534 ASSERT_IS_SYM_EQ(node, "hello?+-*=>");
1535 AST_heap_free(node);
1536 PASS();
1537}
1538
1539TEST read_with_symbol_with_trailing_digits(void) {
1540 char *input = "add1 1";
1541 ASTNode *node = Reader_read(input);
1542 ASSERT_IS_SYM_EQ(node, "add1");
1543 AST_heap_free(node);
1544 PASS();
1545}
1546
1547TEST read_with_char_returns_char(void) {
1548 char *input = "'a'";
1549 ASTNode *node = Reader_read(input);
1550 ASSERT_IS_CHAR_EQ(node, 'a');
1551 ASSERT(AST_is_error(Reader_read("''")));
1552 ASSERT(AST_is_error(Reader_read("'aa'")));
1553 ASSERT(AST_is_error(Reader_read("'aa")));
1554 AST_heap_free(node);
1555 PASS();
1556}
1557
1558TEST read_with_bool_returns_bool(void) {
1559 ASSERT_EQ(Reader_read("#t"), AST_new_bool(true));
1560 ASSERT_EQ(Reader_read("#f"), AST_new_bool(false));
1561 ASSERT(AST_is_error(Reader_read("#")));
1562 ASSERT(AST_is_error(Reader_read("#x")));
1563 ASSERT(AST_is_error(Reader_read("##")));
1564 PASS();
1565}
1566
1567TEST read_with_nil_returns_nil(void) {
1568 char *input = "()";
1569 ASTNode *node = Reader_read(input);
1570 ASSERT(AST_is_nil(node));
1571 AST_heap_free(node);
1572 PASS();
1573}
1574
1575TEST read_with_list_returns_list(void) {
1576 char *input = "( 1 2 0 )";
1577 ASTNode *node = Reader_read(input);
1578 ASSERT(AST_is_pair(node));
1579 ASSERT_IS_INT_EQ(AST_pair_car(node), 1);
1580 ASSERT_IS_INT_EQ(AST_pair_car(AST_pair_cdr(node)), 2);
1581 ASSERT_IS_INT_EQ(AST_pair_car(AST_pair_cdr(AST_pair_cdr(node))), 0);
1582 ASSERT(AST_is_nil(AST_pair_cdr(AST_pair_cdr(AST_pair_cdr(node)))));
1583 AST_heap_free(node);
1584 PASS();
1585}
1586
1587TEST read_with_nested_list_returns_list(void) {
1588 char *input = "((hello world) (foo bar))";
1589 ASTNode *node = Reader_read(input);
1590 ASSERT(AST_is_pair(node));
1591 ASTNode *first = AST_pair_car(node);
1592 ASSERT(AST_is_pair(first));
1593 ASSERT_IS_SYM_EQ(AST_pair_car(first), "hello");
1594 ASSERT_IS_SYM_EQ(AST_pair_car(AST_pair_cdr(first)), "world");
1595 ASSERT(AST_is_nil(AST_pair_cdr(AST_pair_cdr(first))));
1596 ASTNode *second = AST_pair_car(AST_pair_cdr(node));
1597 ASSERT(AST_is_pair(second));
1598 ASSERT_IS_SYM_EQ(AST_pair_car(second), "foo");
1599 ASSERT_IS_SYM_EQ(AST_pair_car(AST_pair_cdr(second)), "bar");
1600 ASSERT(AST_is_nil(AST_pair_cdr(AST_pair_cdr(second))));
1601 AST_heap_free(node);
1602 PASS();
1603}
1604
1605TEST buffer_write8_increases_length(Buffer *buf) {
1606 ASSERT_EQ(Buffer_len(buf), 0);
1607 Buffer_write8(buf, 0xdb);
1608 ASSERT_EQ(Buffer_at8(buf, 0), 0xdb);
1609 ASSERT_EQ(Buffer_len(buf), 1);
1610 PASS();
1611}
1612
1613TEST buffer_write8_expands_buffer(void) {
1614 Buffer buf;
1615 Buffer_init(&buf, 1);
1616 ASSERT_EQ(buf.capacity, 1);
1617 ASSERT_EQ(buf.len, 0);
1618 Buffer_write8(&buf, 0xdb);
1619 Buffer_write8(&buf, 0xef);
1620 ASSERT(buf.capacity > 1);
1621 ASSERT_EQ(buf.len, 2);
1622 Buffer_deinit(&buf);
1623 PASS();
1624}
1625
1626TEST buffer_write32_expands_buffer(void) {
1627 Buffer buf;
1628 Buffer_init(&buf, 1);
1629 ASSERT_EQ(buf.capacity, 1);
1630 ASSERT_EQ(buf.len, 0);
1631 Buffer_write32(&buf, 0xdeadbeef);
1632 ASSERT(buf.capacity > 1);
1633 ASSERT_EQ(buf.len, 4);
1634 Buffer_deinit(&buf);
1635 PASS();
1636}
1637
1638TEST buffer_write32_writes_little_endian(Buffer *buf) {
1639 Buffer_write32(buf, 0xdeadbeef);
1640 ASSERT_EQ(Buffer_at8(buf, 0), 0xef);
1641 ASSERT_EQ(Buffer_at8(buf, 1), 0xbe);
1642 ASSERT_EQ(Buffer_at8(buf, 2), 0xad);
1643 ASSERT_EQ(Buffer_at8(buf, 3), 0xde);
1644 PASS();
1645}
1646
1647TEST compile_positive_integer(Buffer *buf) {
1648 word value = 123;
1649 ASTNode *node = AST_new_integer(value);
1650 int compile_result = Testing_compile_expr_entry(buf, node);
1651 ASSERT_EQ(compile_result, 0);
1652 // mov eax, imm(123)
1653 byte expected[] = {0x48, 0xc7, 0xc0, 0xec, 0x01, 0x00, 0x00};
1654 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1655 Buffer_make_executable(buf);
1656 uword result = Testing_execute_expr(buf);
1657 ASSERT_EQ(result, Object_encode_integer(value));
1658 PASS();
1659}
1660
1661TEST compile_negative_integer(Buffer *buf) {
1662 word value = -123;
1663 ASTNode *node = AST_new_integer(value);
1664 int compile_result = Testing_compile_expr_entry(buf, node);
1665 ASSERT_EQ(compile_result, 0);
1666 // mov eax, imm(-123)
1667 byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0xfe, 0xff, 0xff};
1668 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1669 Buffer_make_executable(buf);
1670 uword result = Testing_execute_expr(buf);
1671 ASSERT_EQ(result, Object_encode_integer(value));
1672 PASS();
1673}
1674
1675TEST compile_char(Buffer *buf) {
1676 char value = 'a';
1677 ASTNode *node = AST_new_char(value);
1678 int compile_result = Testing_compile_expr_entry(buf, node);
1679 ASSERT_EQ(compile_result, 0);
1680 // mov eax, imm('a')
1681 byte expected[] = {0x48, 0xc7, 0xc0, 0x0f, 0x61, 0x00, 0x00};
1682 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1683 Buffer_make_executable(buf);
1684 uword result = Testing_execute_expr(buf);
1685 ASSERT_EQ(result, Object_encode_char(value));
1686 PASS();
1687}
1688
1689TEST compile_true(Buffer *buf) {
1690 ASTNode *node = AST_new_bool(true);
1691 int compile_result = Testing_compile_expr_entry(buf, node);
1692 ASSERT_EQ(compile_result, 0);
1693 // mov eax, imm(true)
1694 byte expected[] = {0x48, 0xc7, 0xc0, 0x9f, 0x0, 0x0, 0x0};
1695 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1696 Buffer_make_executable(buf);
1697 uword result = Testing_execute_expr(buf);
1698 ASSERT_EQ(result, Object_true());
1699 PASS();
1700}
1701
1702TEST compile_false(Buffer *buf) {
1703 ASTNode *node = AST_new_bool(false);
1704 int compile_result = Testing_compile_expr_entry(buf, node);
1705 ASSERT_EQ(compile_result, 0);
1706 // mov eax, imm(false)
1707 byte expected[] = {0x48, 0xc7, 0xc0, 0x1f, 0x00, 0x00, 0x00};
1708 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1709 Buffer_make_executable(buf);
1710 uword result = Testing_execute_expr(buf);
1711 ASSERT_EQ(result, Object_false());
1712 PASS();
1713}
1714
1715TEST compile_nil(Buffer *buf) {
1716 ASTNode *node = AST_nil();
1717 int compile_result = Testing_compile_expr_entry(buf, node);
1718 ASSERT_EQ(compile_result, 0);
1719 // mov eax, imm(nil)
1720 byte expected[] = {0x48, 0xc7, 0xc0, 0x2f, 0x00, 0x00, 0x00};
1721 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1722 Buffer_make_executable(buf);
1723 uword result = Testing_execute_expr(buf);
1724 ASSERT_EQ(result, Object_nil());
1725 PASS();
1726}
1727
1728TEST compile_unary_add1(Buffer *buf) {
1729 ASTNode *node = new_unary_call("add1", AST_new_integer(123));
1730 int compile_result = Testing_compile_expr_entry(buf, node);
1731 ASSERT_EQ(compile_result, 0);
1732 // mov rax, imm(123); add rax, imm(1)
1733 byte expected[] = {0x48, 0xc7, 0xc0, 0xec, 0x01, 0x00, 0x00,
1734 0x48, 0x05, 0x04, 0x00, 0x00, 0x00};
1735 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1736 Buffer_make_executable(buf);
1737 uword result = Testing_execute_expr(buf);
1738 ASSERT_EQ(result, Object_encode_integer(124));
1739 AST_heap_free(node);
1740 PASS();
1741}
1742
1743TEST compile_unary_add1_nested(Buffer *buf) {
1744 ASTNode *node =
1745 new_unary_call("add1", new_unary_call("add1", AST_new_integer(123)));
1746 int compile_result = Testing_compile_expr_entry(buf, node);
1747 ASSERT_EQ(compile_result, 0);
1748 // mov rax, imm(123); add rax, imm(1); add rax, imm(1)
1749 byte expected[] = {0x48, 0xc7, 0xc0, 0xec, 0x01, 0x00, 0x00, 0x48, 0x05, 0x04,
1750 0x00, 0x00, 0x00, 0x48, 0x05, 0x04, 0x00, 0x00, 0x00};
1751 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1752 Buffer_make_executable(buf);
1753 uword result = Testing_execute_expr(buf);
1754 ASSERT_EQ(result, Object_encode_integer(125));
1755 AST_heap_free(node);
1756 PASS();
1757}
1758
1759TEST compile_unary_sub1(Buffer *buf) {
1760 ASTNode *node = new_unary_call("sub1", AST_new_integer(123));
1761 int compile_result = Testing_compile_expr_entry(buf, node);
1762 ASSERT_EQ(compile_result, 0);
1763 // mov rax, imm(123); sub rax, imm(1)
1764 byte expected[] = {0x48, 0xc7, 0xc0, 0xec, 0x01, 0x00, 0x00,
1765 0x48, 0x2d, 0x04, 0x00, 0x00, 0x00};
1766 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1767 Buffer_make_executable(buf);
1768 uword result = Testing_execute_expr(buf);
1769 ASSERT_EQ(result, Object_encode_integer(122));
1770 AST_heap_free(node);
1771 PASS();
1772}
1773
1774TEST compile_unary_integer_to_char(Buffer *buf) {
1775 ASTNode *node = new_unary_call("integer->char", AST_new_integer(97));
1776 int compile_result = Testing_compile_expr_entry(buf, node);
1777 ASSERT_EQ(compile_result, 0);
1778 // mov rax, imm(97); shl rax, 6; or rax, 0xf
1779 byte expected[] = {0x48, 0xc7, 0xc0, 0x84, 0x01, 0x00, 0x00, 0x48,
1780 0xc1, 0xe0, 0x06, 0x48, 0x83, 0xc8, 0x0f};
1781 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1782 Buffer_make_executable(buf);
1783 uword result = Testing_execute_expr(buf);
1784 ASSERT_EQ(result, Object_encode_char('a'));
1785 AST_heap_free(node);
1786 PASS();
1787}
1788
1789TEST compile_unary_char_to_integer(Buffer *buf) {
1790 ASTNode *node = new_unary_call("char->integer", AST_new_char('a'));
1791 int compile_result = Testing_compile_expr_entry(buf, node);
1792 ASSERT_EQ(compile_result, 0);
1793 // mov rax, imm('a'); shr rax, 6
1794 byte expected[] = {0x48, 0xc7, 0xc0, 0x0f, 0x61, 0x00,
1795 0x00, 0x48, 0xc1, 0xe8, 0x06};
1796 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1797 Buffer_make_executable(buf);
1798 uword result = Testing_execute_expr(buf);
1799 ASSERT_EQ(result, Object_encode_integer(97));
1800 AST_heap_free(node);
1801 PASS();
1802}
1803
1804TEST compile_unary_nilp_with_nil_returns_true(Buffer *buf) {
1805 ASTNode *node = new_unary_call("nil?", AST_nil());
1806 int compile_result = Testing_compile_expr_entry(buf, node);
1807 ASSERT_EQ(compile_result, 0);
1808 // 0: 48 c7 c0 2f 00 00 00 mov rax,0x2f
1809 // 7: 48 3d 2f 00 00 00 cmp rax,0x0000002f
1810 // d: 48 c7 c0 00 00 00 00 mov rax,0x0
1811 // 14: 0f 94 c0 sete al
1812 // 17: 48 c1 e0 07 shl rax,0x7
1813 // 1b: 48 83 c8 1f or rax,0x1f
1814 byte expected[] = {0x48, 0xc7, 0xc0, 0x2f, 0x00, 0x00, 0x00, 0x48,
1815 0x3d, 0x2f, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0,
1816 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48,
1817 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
1818 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1819 Buffer_make_executable(buf);
1820 uword result = Testing_execute_expr(buf);
1821 ASSERT_EQ(result, Object_true());
1822 AST_heap_free(node);
1823 PASS();
1824}
1825
1826TEST compile_unary_nilp_with_non_nil_returns_false(Buffer *buf) {
1827 ASTNode *node = new_unary_call("nil?", AST_new_integer(5));
1828 int compile_result = Testing_compile_expr_entry(buf, node);
1829 ASSERT_EQ(compile_result, 0);
1830 // 0: 48 c7 c0 14 00 00 00 mov rax,0x14
1831 // 7: 48 3d 2f 00 00 00 cmp rax,0x0000002f
1832 // d: 48 c7 c0 00 00 00 00 mov rax,0x0
1833 // 14: 0f 94 c0 sete al
1834 // 17: 48 c1 e0 07 shl rax,0x7
1835 // 1b: 48 83 c8 1f or rax,0x1f
1836 byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48,
1837 0x3d, 0x2f, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0,
1838 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48,
1839 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
1840 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1841 Buffer_make_executable(buf);
1842 uword result = Testing_execute_expr(buf);
1843 ASSERT_EQ(result, Object_false());
1844 AST_heap_free(node);
1845 PASS();
1846}
1847
1848TEST compile_unary_zerop_with_zero_returns_true(Buffer *buf) {
1849 ASTNode *node = new_unary_call("zero?", AST_new_integer(0));
1850 int compile_result = Testing_compile_expr_entry(buf, node);
1851 ASSERT_EQ(compile_result, 0);
1852 // 0: 48 c7 c0 00 00 00 00 mov rax,0x0
1853 // 7: 48 3d 00 00 00 00 cmp rax,0x00000000
1854 // d: 48 c7 c0 00 00 00 00 mov rax,0x0
1855 // 14: 0f 94 c0 sete al
1856 // 17: 48 c1 e0 07 shl rax,0x7
1857 // 1b: 48 83 c8 1f or rax,0x1f
1858 byte expected[] = {0x48, 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x48,
1859 0x3d, 0x00, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0,
1860 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48,
1861 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
1862 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1863 Buffer_make_executable(buf);
1864 uword result = Testing_execute_expr(buf);
1865 ASSERT_EQ(result, Object_true());
1866 AST_heap_free(node);
1867 PASS();
1868}
1869
1870TEST compile_unary_zerop_with_non_zero_returns_false(Buffer *buf) {
1871 ASTNode *node = new_unary_call("zero?", AST_new_integer(5));
1872 int compile_result = Testing_compile_expr_entry(buf, node);
1873 ASSERT_EQ(compile_result, 0);
1874 // 0: 48 c7 c0 14 00 00 00 mov rax,0x14
1875 // 7: 48 3d 00 00 00 00 cmp rax,0x00000000
1876 // d: 48 c7 c0 00 00 00 00 mov rax,0x0
1877 // 14: 0f 94 c0 sete al
1878 // 17: 48 c1 e0 07 shl rax,0x7
1879 // 1b: 48 83 c8 1f or rax,0x1f
1880 byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48,
1881 0x3d, 0x00, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0,
1882 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48,
1883 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
1884 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1885 Buffer_make_executable(buf);
1886 uword result = Testing_execute_expr(buf);
1887 ASSERT_EQ(result, Object_false());
1888 AST_heap_free(node);
1889 PASS();
1890}
1891
1892TEST compile_unary_not_with_false_returns_true(Buffer *buf) {
1893 ASTNode *node = new_unary_call("not", AST_new_bool(false));
1894 int compile_result = Testing_compile_expr_entry(buf, node);
1895 ASSERT_EQ(compile_result, 0);
1896 // 0: 48 c7 c0 1f 00 00 00 mov rax,0x1f
1897 // 7: 48 3d 1f 00 00 00 cmp rax,0x0000001f
1898 // d: 48 c7 c0 00 00 00 00 mov rax,0x0
1899 // 14: 0f 94 c0 sete al
1900 // 17: 48 c1 e0 07 shl rax,0x7
1901 // 1b: 48 83 c8 1f or rax,0x1f
1902 byte expected[] = {0x48, 0xc7, 0xc0, 0x1f, 0x00, 0x00, 0x00, 0x48,
1903 0x3d, 0x1f, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0,
1904 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48,
1905 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
1906 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1907 Buffer_make_executable(buf);
1908 uword result = Testing_execute_expr(buf);
1909 ASSERT_EQ(result, Object_true());
1910 AST_heap_free(node);
1911 PASS();
1912}
1913
1914TEST compile_unary_not_with_non_false_returns_false(Buffer *buf) {
1915 ASTNode *node = new_unary_call("not", AST_new_integer(5));
1916 int compile_result = Testing_compile_expr_entry(buf, node);
1917 ASSERT_EQ(compile_result, 0);
1918 // 0: 48 c7 c0 14 00 00 00 mov rax,0x14
1919 // 7: 48 3d 1f 00 00 00 cmp rax,0x0000001f
1920 // d: 48 c7 c0 00 00 00 00 mov rax,0x0
1921 // 14: 0f 94 c0 sete al
1922 // 17: 48 c1 e0 07 shl rax,0x7
1923 // 1b: 48 83 c8 1f or rax,0x1f
1924 byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48,
1925 0x3d, 0x1f, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0,
1926 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48,
1927 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
1928 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1929 Buffer_make_executable(buf);
1930 uword result = Testing_execute_expr(buf);
1931 ASSERT_EQ(result, Object_false());
1932 AST_heap_free(node);
1933 PASS();
1934}
1935
1936TEST compile_unary_integerp_with_integer_returns_true(Buffer *buf) {
1937 ASTNode *node = new_unary_call("integer?", AST_new_integer(5));
1938 int compile_result = Testing_compile_expr_entry(buf, node);
1939 ASSERT_EQ(compile_result, 0);
1940 // 0: 48 c7 c0 14 00 00 00 mov rax,0x14
1941 // 7: 48 83 e0 03 and rax,0x3
1942 // b: 48 3d 00 00 00 00 cmp rax,0x00000000
1943 // 11: 48 c7 c0 00 00 00 00 mov rax,0x0
1944 // 18: 0f 94 c0 sete al
1945 // 1b: 48 c1 e0 07 shl rax,0x7
1946 // 1f: 48 83 c8 1f or rax,0x1f
1947 byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48, 0x83,
1948 0xe0, 0x03, 0x48, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x48,
1949 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0,
1950 0x48, 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
1951 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1952 Buffer_make_executable(buf);
1953 uword result = Testing_execute_expr(buf);
1954 ASSERT_EQ(result, Object_true());
1955 AST_heap_free(node);
1956 PASS();
1957}
1958
1959TEST compile_unary_integerp_with_non_integer_returns_false(Buffer *buf) {
1960 ASTNode *node = new_unary_call("integer?", AST_nil());
1961 int compile_result = Testing_compile_expr_entry(buf, node);
1962 ASSERT_EQ(compile_result, 0);
1963 // 0: 48 c7 c0 2f 00 00 00 mov rax,0x2f
1964 // 7: 48 83 e0 03 and rax,0x3
1965 // b: 48 3d 00 00 00 00 cmp rax,0x00000000
1966 // 11: 48 c7 c0 00 00 00 00 mov rax,0x0
1967 // 18: 0f 94 c0 sete al
1968 // 1b: 48 c1 e0 07 shl rax,0x7
1969 // 1f: 48 83 c8 1f or rax,0x1f
1970 byte expected[] = {0x48, 0xc7, 0xc0, 0x2f, 0x00, 0x00, 0x00, 0x48, 0x83,
1971 0xe0, 0x03, 0x48, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x48,
1972 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0,
1973 0x48, 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
1974 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1975 Buffer_make_executable(buf);
1976 uword result = Testing_execute_expr(buf);
1977 ASSERT_EQ(result, Object_false());
1978 AST_heap_free(node);
1979 PASS();
1980}
1981
1982TEST compile_unary_booleanp_with_boolean_returns_true(Buffer *buf) {
1983 ASTNode *node = new_unary_call("boolean?", AST_new_bool(true));
1984 int compile_result = Testing_compile_expr_entry(buf, node);
1985 ASSERT_EQ(compile_result, 0);
1986 // 0: 48 c7 c0 9f 00 00 00 mov rax,0x9f
1987 // 7: 48 83 e0 3f and rax,0x3f
1988 // b: 48 3d 1f 00 00 00 cmp rax,0x0000001f
1989 // 11: 48 c7 c0 00 00 00 00 mov rax,0x0
1990 // 18: 0f 94 c0 sete al
1991 // 1b: 48 c1 e0 07 shl rax,0x7
1992 // 1f: 48 83 c8 1f or rax,0x1f
1993 byte expected[] = {0x48, 0xc7, 0xc0, 0x9f, 0x00, 0x00, 0x00, 0x48, 0x83,
1994 0xe0, 0x3f, 0x48, 0x3d, 0x1f, 0x00, 0x00, 0x00, 0x48,
1995 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0,
1996 0x48, 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
1997 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
1998 Buffer_make_executable(buf);
1999 uword result = Testing_execute_expr(buf);
2000 ASSERT_EQ(result, Object_true());
2001 AST_heap_free(node);
2002 PASS();
2003}
2004
2005TEST compile_unary_booleanp_with_non_boolean_returns_false(Buffer *buf) {
2006 ASTNode *node = new_unary_call("boolean?", AST_new_integer(5));
2007 int compile_result = Testing_compile_expr_entry(buf, node);
2008 ASSERT_EQ(compile_result, 0);
2009 // 0: 48 c7 c0 14 00 00 00 mov rax,0x14
2010 // 7: 48 83 e0 3f and rax,0x3f
2011 // b: 48 3d 1f 00 00 00 cmp rax,0x0000001f
2012 // 11: 48 c7 c0 00 00 00 00 mov rax,0x0
2013 // 18: 0f 94 c0 sete al
2014 // 1b: 48 c1 e0 07 shl rax,0x7
2015 // 1f: 48 83 c8 1f or rax,0x1f
2016 byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48, 0x83,
2017 0xe0, 0x3f, 0x48, 0x3d, 0x1f, 0x00, 0x00, 0x00, 0x48,
2018 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0,
2019 0x48, 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f};
2020 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2021 Buffer_make_executable(buf);
2022 uword result = Testing_execute_expr(buf);
2023 ASSERT_EQ(result, Object_false());
2024 AST_heap_free(node);
2025 PASS();
2026}
2027
2028TEST compile_binary_plus(Buffer *buf) {
2029 ASTNode *node = new_binary_call("+", AST_new_integer(5), AST_new_integer(8));
2030 int compile_result = Testing_compile_expr_entry(buf, node);
2031 ASSERT_EQ(compile_result, 0);
2032 byte expected[] = {
2033 // 0: 48 c7 c0 20 00 00 00 mov rax,0x20
2034 0x48, 0xc7, 0xc0, 0x20, 0x00, 0x00, 0x00,
2035 // 7: 48 89 45 f8 mov QWORD PTR [rsp-0x8],rax
2036 0x48, 0x89, 0x44, 0x24, 0xf8,
2037 // b: 48 c7 c0 14 00 00 00 mov rax,0x14
2038 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00,
2039 // 12: 48 03 45 f8 add rax,QWORD PTR [rsp-0x8]
2040 0x48, 0x03, 0x44, 0x24, 0xf8};
2041 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2042 Buffer_make_executable(buf);
2043 uword result = Testing_execute_expr(buf);
2044 ASSERT_EQ(result, Object_encode_integer(13));
2045 AST_heap_free(node);
2046 PASS();
2047}
2048
2049TEST compile_binary_plus_nested(Buffer *buf) {
2050 ASTNode *node = new_binary_call(
2051 "+", new_binary_call("+", AST_new_integer(1), AST_new_integer(2)),
2052 new_binary_call("+", AST_new_integer(3), AST_new_integer(4)));
2053 int compile_result = Testing_compile_expr_entry(buf, node);
2054 ASSERT_EQ(compile_result, 0);
2055 byte expected[] = {
2056 // 4: 48 c7 c0 10 00 00 00 mov rax,0x10
2057 0x48, 0xc7, 0xc0, 0x10, 0x00, 0x00, 0x00,
2058 // b: 48 89 45 f8 mov QWORD PTR [rsp-0x8],rax
2059 0x48, 0x89, 0x44, 0x24, 0xf8,
2060 // f: 48 c7 c0 0c 00 00 00 mov rax,0xc
2061 0x48, 0xc7, 0xc0, 0x0c, 0x00, 0x00, 0x00,
2062 // 16: 48 03 45 f8 add rax,QWORD PTR [rsp-0x8]
2063 0x48, 0x03, 0x44, 0x24, 0xf8,
2064 // 1a: 48 89 45 f8 mov QWORD PTR [rsp-0x8],rax
2065 0x48, 0x89, 0x44, 0x24, 0xf8,
2066 // 1e: 48 c7 c0 08 00 00 00 mov rax,0x8
2067 0x48, 0xc7, 0xc0, 0x08, 0x00, 0x00, 0x00,
2068 // 25: 48 89 45 f0 mov QWORD PTR [rsp-0x10],rax
2069 0x48, 0x89, 0x44, 0x24, 0xf0,
2070 // 29: 48 c7 c0 04 00 00 00 mov rax,0x4
2071 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2072 // 30: 48 03 45 f0 add rax,QWORD PTR [rsp-0x10]
2073 0x48, 0x03, 0x44, 0x24, 0xf0,
2074 // 34: 48 03 45 f8 add rax,QWORD PTR [rsp-0x8]
2075 0x48, 0x03, 0x44, 0x24, 0xf8};
2076 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2077 Buffer_make_executable(buf);
2078 uword result = Testing_execute_expr(buf);
2079 ASSERT_EQ(result, Object_encode_integer(10));
2080 AST_heap_free(node);
2081 PASS();
2082}
2083
2084TEST compile_binary_minus(Buffer *buf) {
2085 ASTNode *node = new_binary_call("-", AST_new_integer(5), AST_new_integer(8));
2086 int compile_result = Testing_compile_expr_entry(buf, node);
2087 ASSERT_EQ(compile_result, 0);
2088 byte expected[] = {
2089 // 0: 48 c7 c0 20 00 00 00 mov rax,0x20
2090 0x48, 0xc7, 0xc0, 0x20, 0x00, 0x00, 0x00,
2091 // 7: 48 89 45 f8 mov QWORD PTR [rsp-0x8],rax
2092 0x48, 0x89, 0x44, 0x24, 0xf8,
2093 // b: 48 c7 c0 14 00 00 00 mov rax,0x14
2094 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00,
2095 // 12: 48 2b 45 f8 add rax,QWORD PTR [rsp-0x8]
2096 0x48, 0x2b, 0x44, 0x24, 0xf8};
2097 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2098 Buffer_make_executable(buf);
2099 uword result = Testing_execute_expr(buf);
2100 ASSERT_EQ(result, Object_encode_integer(-3));
2101 AST_heap_free(node);
2102 PASS();
2103}
2104
2105TEST compile_binary_minus_nested(Buffer *buf) {
2106 ASTNode *node = new_binary_call(
2107 "-", new_binary_call("-", AST_new_integer(5), AST_new_integer(1)),
2108 new_binary_call("-", AST_new_integer(4), AST_new_integer(3)));
2109 int compile_result = Testing_compile_expr_entry(buf, node);
2110 ASSERT_EQ(compile_result, 0);
2111 byte expected[] = {
2112 // 4: 48 c7 c0 0c 00 00 00 mov rax,0xc
2113 0x48, 0xc7, 0xc0, 0x0c, 0x00, 0x00, 0x00,
2114 // b: 48 89 45 f8 mov QWORD PTR [rsp-0x8],rax
2115 0x48, 0x89, 0x44, 0x24, 0xf8,
2116 // f: 48 c7 c0 10 00 00 00 mov rax,0x10
2117 0x48, 0xc7, 0xc0, 0x10, 0x00, 0x00, 0x00,
2118 // 16: 48 2b 45 f8 add rax,QWORD PTR [rsp-0x8]
2119 0x48, 0x2b, 0x44, 0x24, 0xf8,
2120 // 1a: 48 89 45 f8 mov QWORD PTR [rsp-0x8],rax
2121 0x48, 0x89, 0x44, 0x24, 0xf8,
2122 // 1e: 48 c7 c0 04 00 00 00 mov rax,0x4
2123 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2124 // 25: 48 89 45 f0 mov QWORD PTR [rsp-0x10],rax
2125 0x48, 0x89, 0x44, 0x24, 0xf0,
2126 // 29: 48 c7 c0 14 00 00 00 mov rax,0x14
2127 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00,
2128 // 30: 48 2b 45 f0 add rax,QWORD PTR [rsp-0x10]
2129 0x48, 0x2b, 0x44, 0x24, 0xf0,
2130 // 34: 48 2b 45 f8 add rax,QWORD PTR [rsp-0x8]
2131 0x48, 0x2b, 0x44, 0x24, 0xf8};
2132 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2133 Buffer_make_executable(buf);
2134 uword result = Testing_execute_expr(buf);
2135 ASSERT_EQ(result, Object_encode_integer(3));
2136 AST_heap_free(node);
2137 PASS();
2138}
2139
2140TEST compile_binary_mul(Buffer *buf) {
2141 ASTNode *node = new_binary_call("*", AST_new_integer(5), AST_new_integer(8));
2142 int compile_result = Testing_compile_expr_entry(buf, node);
2143 ASSERT_EQ(compile_result, 0);
2144 Buffer_make_executable(buf);
2145 uword result = Testing_execute_expr(buf);
2146 ASSERT_EQ_FMT(Object_encode_integer(40), result, "0x%lx");
2147 AST_heap_free(node);
2148 PASS();
2149}
2150
2151TEST compile_binary_mul_nested(Buffer *buf) {
2152 ASTNode *node = new_binary_call(
2153 "*", new_binary_call("*", AST_new_integer(1), AST_new_integer(2)),
2154 new_binary_call("*", AST_new_integer(3), AST_new_integer(4)));
2155 int compile_result = Testing_compile_expr_entry(buf, node);
2156 ASSERT_EQ(compile_result, 0);
2157 Buffer_make_executable(buf);
2158 uword result = Testing_execute_expr(buf);
2159 ASSERT_EQ_FMT(Object_encode_integer(24), result, "0x%lx");
2160 AST_heap_free(node);
2161 PASS();
2162}
2163
2164TEST compile_binary_eq_with_same_address_returns_true(Buffer *buf) {
2165 ASTNode *node = new_binary_call("=", AST_new_integer(5), AST_new_integer(5));
2166 int compile_result = Testing_compile_expr_entry(buf, node);
2167 ASSERT_EQ(compile_result, 0);
2168 Buffer_make_executable(buf);
2169 uword result = Testing_execute_expr(buf);
2170 ASSERT_EQ_FMT(Object_true(), result, "0x%lx");
2171 AST_heap_free(node);
2172 PASS();
2173}
2174
2175TEST compile_binary_eq_with_different_address_returns_false(Buffer *buf) {
2176 ASTNode *node = new_binary_call("=", AST_new_integer(5), AST_new_integer(4));
2177 int compile_result = Testing_compile_expr_entry(buf, node);
2178 ASSERT_EQ(compile_result, 0);
2179 Buffer_make_executable(buf);
2180 uword result = Testing_execute_expr(buf);
2181 ASSERT_EQ_FMT(Object_false(), result, "0x%lx");
2182 AST_heap_free(node);
2183 PASS();
2184}
2185
2186TEST compile_binary_lt_with_left_less_than_right_returns_true(Buffer *buf) {
2187 ASTNode *node = new_binary_call("<", AST_new_integer(-5), AST_new_integer(5));
2188 int compile_result = Testing_compile_expr_entry(buf, node);
2189 ASSERT_EQ(compile_result, 0);
2190 Buffer_make_executable(buf);
2191 uword result = Testing_execute_expr(buf);
2192 ASSERT_EQ_FMT(Object_true(), result, "0x%lx");
2193 AST_heap_free(node);
2194 PASS();
2195}
2196
2197TEST compile_binary_lt_with_left_equal_to_right_returns_false(Buffer *buf) {
2198 ASTNode *node = new_binary_call("<", AST_new_integer(5), AST_new_integer(5));
2199 int compile_result = Testing_compile_expr_entry(buf, node);
2200 ASSERT_EQ(compile_result, 0);
2201 Buffer_make_executable(buf);
2202 uword result = Testing_execute_expr(buf);
2203 ASSERT_EQ_FMT(Object_false(), result, "0x%lx");
2204 AST_heap_free(node);
2205 PASS();
2206}
2207
2208TEST compile_binary_lt_with_left_greater_than_right_returns_false(Buffer *buf) {
2209 ASTNode *node = new_binary_call("<", AST_new_integer(6), AST_new_integer(5));
2210 int compile_result = Testing_compile_expr_entry(buf, node);
2211 ASSERT_EQ(compile_result, 0);
2212 Buffer_make_executable(buf);
2213 uword result = Testing_execute_expr(buf);
2214 ASSERT_EQ_FMT(Object_false(), result, "0x%lx");
2215 AST_heap_free(node);
2216 PASS();
2217}
2218
2219TEST compile_symbol_in_env_returns_value(Buffer *buf) {
2220 ASTNode *node = AST_new_symbol("hello");
2221 Env env0 = Env_bind("hello", 33, /*prev=*/NULL);
2222 Env env1 = Env_bind("world", 66, &env0);
2223 int compile_result =
2224 Compile_expr(buf, node, -kWordSize, &env1, /*labels=*/NULL);
2225 ASSERT_EQ(compile_result, 0);
2226 byte expected[] = {// mov rax, [rsp+33]
2227 0x48, 0x8b, 0x44, 0x24, 33};
2228 EXPECT_EQUALS_BYTES(buf, expected);
2229 AST_heap_free(node);
2230 PASS();
2231}
2232
2233TEST compile_symbol_in_env_returns_first_value(Buffer *buf) {
2234 ASTNode *node = AST_new_symbol("hello");
2235 Env env0 = Env_bind("hello", 55, /*prev=*/NULL);
2236 Env env1 = Env_bind("hello", 66, &env0);
2237 int compile_result =
2238 Compile_expr(buf, node, -kWordSize, &env1, /*labels=*/NULL);
2239 ASSERT_EQ(compile_result, 0);
2240 byte expected[] = {// mov rax, [rsp+66]
2241 0x48, 0x8b, 0x44, 0x24, 66};
2242 EXPECT_EQUALS_BYTES(buf, expected);
2243 AST_heap_free(node);
2244 PASS();
2245}
2246
2247TEST compile_symbol_not_in_env_raises_compile_error(Buffer *buf) {
2248 ASTNode *node = AST_new_symbol("hello");
2249 int compile_result =
2250 Compile_expr(buf, node, -kWordSize, /*varenv=*/NULL, /*labels=*/NULL);
2251 ASSERT_EQ(compile_result, -1);
2252 AST_heap_free(node);
2253 PASS();
2254}
2255
2256TEST compile_let_with_no_bindings(Buffer *buf) {
2257 ASTNode *node = Reader_read("(let () (+ 1 2))");
2258 int compile_result = Testing_compile_expr_entry(buf, node);
2259 ASSERT_EQ(compile_result, 0);
2260 Buffer_make_executable(buf);
2261 uword result = Testing_execute_expr(buf);
2262 ASSERT_EQ_FMT(Object_encode_integer(3), result, "0x%lx");
2263 AST_heap_free(node);
2264 PASS();
2265}
2266
2267TEST compile_let_with_one_binding(Buffer *buf) {
2268 ASTNode *node = Reader_read("(let ((a 1)) (+ a 2))");
2269 int compile_result = Testing_compile_expr_entry(buf, node);
2270 ASSERT_EQ(compile_result, 0);
2271 Buffer_make_executable(buf);
2272 uword result = Testing_execute_expr(buf);
2273 ASSERT_EQ_FMT(Object_encode_integer(3), result, "0x%lx");
2274 AST_heap_free(node);
2275 PASS();
2276}
2277
2278TEST compile_let_with_multiple_bindings(Buffer *buf) {
2279 ASTNode *node = Reader_read("(let ((a 1) (b 2)) (+ a b))");
2280 int compile_result = Testing_compile_expr_entry(buf, node);
2281 ASSERT_EQ(compile_result, 0);
2282 Buffer_make_executable(buf);
2283 uword result = Testing_execute_expr(buf);
2284 ASSERT_EQ_FMT(Object_encode_integer(3), result, "0x%lx");
2285 AST_heap_free(node);
2286 PASS();
2287}
2288
2289TEST compile_nested_let(Buffer *buf) {
2290 ASTNode *node = Reader_read("(let ((a 1)) (let ((b 2)) (+ a b)))");
2291 int compile_result = Testing_compile_expr_entry(buf, node);
2292 ASSERT_EQ(compile_result, 0);
2293 Buffer_make_executable(buf);
2294 uword result = Testing_execute_expr(buf);
2295 ASSERT_EQ_FMT(Object_encode_integer(3), result, "0x%lx");
2296 AST_heap_free(node);
2297 PASS();
2298}
2299
2300TEST compile_let_is_not_let_star(Buffer *buf) {
2301 ASTNode *node = Reader_read("(let ((a 1) (b a)) a)");
2302 int compile_result = Testing_compile_expr_entry(buf, node);
2303 ASSERT_EQ(compile_result, -1);
2304 AST_heap_free(node);
2305 PASS();
2306}
2307
2308TEST compile_if_with_true_cond(Buffer *buf) {
2309 ASTNode *node = Reader_read("(if #t 1 2)");
2310 int compile_result = Testing_compile_expr_entry(buf, node);
2311 ASSERT_EQ(compile_result, 0);
2312 byte expected[] = {
2313 // mov rax, 0x9f
2314 0x48, 0xc7, 0xc0, 0x9f, 0x00, 0x00, 0x00,
2315 // cmp rax, 0x1f
2316 0x48, 0x3d, 0x1f, 0x00, 0x00, 0x00,
2317 // je alternate
2318 0x0f, 0x84, 0x0c, 0x00, 0x00, 0x00,
2319 // mov rax, compile(1)
2320 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2321 // jmp end
2322 0xe9, 0x07, 0x00, 0x00, 0x00,
2323 // alternate:
2324 // mov rax, compile(2)
2325 0x48, 0xc7, 0xc0, 0x08, 0x00, 0x00, 0x00
2326 // end:
2327 };
2328 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2329 Buffer_make_executable(buf);
2330 uword result = Testing_execute_expr(buf);
2331 ASSERT_EQ_FMT(Object_encode_integer(1), result, "0x%lx");
2332 AST_heap_free(node);
2333 PASS();
2334}
2335
2336TEST compile_if_with_false_cond(Buffer *buf) {
2337 ASTNode *node = Reader_read("(if #f 1 2)");
2338 int compile_result = Testing_compile_expr_entry(buf, node);
2339 ASSERT_EQ(compile_result, 0);
2340 byte expected[] = {
2341 // mov rax, 0x1f
2342 0x48, 0xc7, 0xc0, 0x1f, 0x00, 0x00, 0x00,
2343 // cmp rax, 0x1f
2344 0x48, 0x3d, 0x1f, 0x00, 0x00, 0x00,
2345 // je alternate
2346 0x0f, 0x84, 0x0c, 0x00, 0x00, 0x00,
2347 // mov rax, compile(1)
2348 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2349 // jmp end
2350 0xe9, 0x07, 0x00, 0x00, 0x00,
2351 // alternate:
2352 // mov rax, compile(2)
2353 0x48, 0xc7, 0xc0, 0x08, 0x00, 0x00, 0x00
2354 // end:
2355 };
2356 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2357 Buffer_make_executable(buf);
2358 uword result = Testing_execute_expr(buf);
2359 ASSERT_EQ_FMT(Object_encode_integer(2), result, "0x%lx");
2360 AST_heap_free(node);
2361 PASS();
2362}
2363
2364TEST compile_nested_if(Buffer *buf) {
2365 ASTNode *node = Reader_read("(if (< 1 2) (if #f 3 4) 5)");
2366 int compile_result = Testing_compile_expr_entry(buf, node);
2367 ASSERT_EQ(compile_result, 0);
2368 Buffer_make_executable(buf);
2369 uword result = Testing_execute_expr(buf);
2370 ASSERT_EQ_FMT(Object_encode_integer(4), result, "0x%lx");
2371 AST_heap_free(node);
2372 PASS();
2373}
2374
2375TEST compile_cons(Buffer *buf, uword *heap) {
2376 ASTNode *node = Reader_read("(cons 1 2)");
2377 int compile_result = Testing_compile_expr_entry(buf, node);
2378 ASSERT_EQ(compile_result, 0);
2379 // clang-format off
2380 byte expected[] = {
2381 // mov rax, 0x2
2382 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2383 // mov [rsp-8], rax
2384 0x48, 0x89, 0x44, 0x24, 0xf8,
2385 // mov rax, 0x4
2386 0x48, 0xc7, 0xc0, 0x08, 0x00, 0x00, 0x00,
2387 // mov [rsi+Cdr], rax
2388 0x48, 0x89, 0x46, 0x08,
2389 // mov rax, [rsp-8]
2390 0x48, 0x8b, 0x44, 0x24, 0xf8,
2391 // mov [rsi+Car], rax
2392 0x48, 0x89, 0x46, 0x00,
2393 // mov rax, rsi
2394 0x48, 0x89, 0xf0,
2395 // or rax, kPairTag
2396 0x48, 0x83, 0xc8, 0x01,
2397 // add rsi, 2*kWordSize
2398 0x48, 0x81, 0xc6, 0x10, 0x00, 0x00, 0x00,
2399 };
2400 // clang-format on
2401 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2402 Buffer_make_executable(buf);
2403 uword result = Testing_execute_entry(buf, heap);
2404 ASSERT(Object_is_pair(result));
2405 ASSERT_EQ_FMT(Object_encode_integer(1), Object_pair_car(result), "0x%lx");
2406 ASSERT_EQ_FMT(Object_encode_integer(2), Object_pair_cdr(result), "0x%lx");
2407 AST_heap_free(node);
2408 PASS();
2409}
2410
2411TEST compile_two_cons(Buffer *buf, uword *heap) {
2412 ASTNode *node = Reader_read(
2413 "(let ((a (cons 1 2)) (b (cons 3 4))) (cons (cdr a) (cdr b)))");
2414 int compile_result = Testing_compile_expr_entry(buf, node);
2415 ASSERT_EQ(compile_result, 0);
2416 Buffer_make_executable(buf);
2417 uword result = Testing_execute_entry(buf, heap);
2418 ASSERT(Object_is_pair(result));
2419 ASSERT_EQ_FMT(Object_encode_integer(2), Object_pair_car(result), "0x%lx");
2420 ASSERT_EQ_FMT(Object_encode_integer(4), Object_pair_cdr(result), "0x%lx");
2421 AST_heap_free(node);
2422 PASS();
2423}
2424
2425TEST compile_nested_cons(Buffer *buf, uword *heap) {
2426 ASTNode *node = Reader_read("(cons (cons 1 2) (cons 3 4))");
2427 int compile_result = Testing_compile_expr_entry(buf, node);
2428 ASSERT_EQ(compile_result, 0);
2429 Buffer_make_executable(buf);
2430 uword result = Testing_execute_entry(buf, heap);
2431 ASSERT(Object_is_pair(result));
2432 ASSERT(Object_is_pair(Object_pair_car(result)));
2433 ASSERT_EQ_FMT(Object_encode_integer(1),
2434 Object_pair_car(Object_pair_car(result)), "0x%lx");
2435 ASSERT_EQ_FMT(Object_encode_integer(2),
2436 Object_pair_cdr(Object_pair_car(result)), "0x%lx");
2437 ASSERT(Object_is_pair(Object_pair_cdr(result)));
2438 ASSERT_EQ_FMT(Object_encode_integer(3),
2439 Object_pair_car(Object_pair_cdr(result)), "0x%lx");
2440 ASSERT_EQ_FMT(Object_encode_integer(4),
2441 Object_pair_cdr(Object_pair_cdr(result)), "0x%lx");
2442 AST_heap_free(node);
2443 PASS();
2444}
2445
2446TEST compile_car(Buffer *buf, uword *heap) {
2447 ASTNode *node = Reader_read("(car (cons 1 2))");
2448 int compile_result = Testing_compile_expr_entry(buf, node);
2449 ASSERT_EQ(compile_result, 0);
2450 // clang-format off
2451 byte expected[] = {
2452 // mov rax, 0x2
2453 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2454 // mov [rsp-8], rax
2455 0x48, 0x89, 0x44, 0x24, 0xf8,
2456 // mov rax, 0x4
2457 0x48, 0xc7, 0xc0, 0x08, 0x00, 0x00, 0x00,
2458 // mov [rsi+Cdr], rax
2459 0x48, 0x89, 0x46, 0x08,
2460 // mov rax, [rsp-8]
2461 0x48, 0x8b, 0x44, 0x24, 0xf8,
2462 // mov [rsi+Car], rax
2463 0x48, 0x89, 0x46, 0x00,
2464 // mov rax, rsi
2465 0x48, 0x89, 0xf0,
2466 // or rax, kPairTag
2467 0x48, 0x83, 0xc8, 0x01,
2468 // add rsi, 2*kWordSize
2469 0x48, 0x81, 0xc6, 0x10, 0x00, 0x00, 0x00,
2470 // mov rax, [rax-1]
2471 0x48, 0x8b, 0x40, 0xff,
2472 };
2473 // clang-format on
2474 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2475 Buffer_make_executable(buf);
2476 uword result = Testing_execute_entry(buf, heap);
2477 ASSERT_EQ_FMT(Object_encode_integer(1), result, "0x%lx");
2478 AST_heap_free(node);
2479 PASS();
2480}
2481
2482TEST compile_cdr(Buffer *buf, uword *heap) {
2483 ASTNode *node = Reader_read("(cdr (cons 1 2))");
2484 int compile_result = Testing_compile_expr_entry(buf, node);
2485 ASSERT_EQ(compile_result, 0);
2486 // clang-format off
2487 byte expected[] = {
2488 // mov rax, 0x2
2489 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2490 // mov [rsp-8], rax
2491 0x48, 0x89, 0x44, 0x24, 0xf8,
2492 // mov rax, 0x4
2493 0x48, 0xc7, 0xc0, 0x08, 0x00, 0x00, 0x00,
2494 // mov [rsi+Cdr], rax
2495 0x48, 0x89, 0x46, 0x08,
2496 // mov rax, [rsp-8]
2497 0x48, 0x8b, 0x44, 0x24, 0xf8,
2498 // mov [rsi+Car], rax
2499 0x48, 0x89, 0x46, 0x00,
2500 // mov rax, rsi
2501 0x48, 0x89, 0xf0,
2502 // or rax, kPairTag
2503 0x48, 0x83, 0xc8, 0x01,
2504 // add rsi, 2*kWordSize
2505 0x48, 0x81, 0xc6, 0x10, 0x00, 0x00, 0x00,
2506 // mov rax, [rax+7]
2507 0x48, 0x8b, 0x40, 0x07,
2508 };
2509 // clang-format on
2510 EXPECT_ENTRY_CONTAINS_CODE(buf, expected);
2511 Buffer_make_executable(buf);
2512 uword result = Testing_execute_entry(buf, heap);
2513 ASSERT_EQ_FMT(Object_encode_integer(2), result, "0x%lx");
2514 AST_heap_free(node);
2515 PASS();
2516}
2517
2518TEST compile_code_with_no_params(Buffer *buf) {
2519 ASTNode *node = Reader_read("(code () 1)");
2520 int compile_result = Compile_code(buf, node, /*labels=*/NULL);
2521 ASSERT_EQ(compile_result, 0);
2522 // clang-format off
2523 byte expected[] = {
2524 // mov rax, 0x2
2525 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2526 // ret
2527 0xc3,
2528 };
2529 // clang-format on
2530 EXPECT_EQUALS_BYTES(buf, expected);
2531 Buffer_make_executable(buf);
2532 uword result = Testing_execute_expr(buf);
2533 ASSERT_EQ_FMT(Object_encode_integer(1), result, "0x%lx");
2534 AST_heap_free(node);
2535 PASS();
2536}
2537
2538TEST compile_code_with_one_param(Buffer *buf) {
2539 ASTNode *node = Reader_read("(code (x) x)");
2540 int compile_result = Compile_code(buf, node, /*labels=*/NULL);
2541 ASSERT_EQ(compile_result, 0);
2542 // clang-format off
2543 byte expected[] = {
2544 // mov rax, [rsp-8]
2545 0x48, 0x8b, 0x44, 0x24, 0xf8,
2546 // ret
2547 0xc3,
2548 };
2549 // clang-format on
2550 EXPECT_EQUALS_BYTES(buf, expected);
2551 AST_heap_free(node);
2552 PASS();
2553}
2554
2555TEST compile_code_with_two_params(Buffer *buf) {
2556 ASTNode *node = Reader_read("(code (x y) (+ x y))");
2557 int compile_result = Compile_code(buf, node, /*labels=*/NULL);
2558 ASSERT_EQ(compile_result, 0);
2559 // clang-format off
2560 byte expected[] = {
2561 // mov rax, [rsp-16]
2562 0x48, 0x8b, 0x44, 0x24, 0xf0,
2563 // mov [rsp-24], rax
2564 0x48, 0x89, 0x44, 0x24, 0xe8,
2565 // mov rax, [rsp-8]
2566 0x48, 0x8b, 0x44, 0x24, 0xf8,
2567 // add rax, [rsp-24]
2568 0x48, 0x03, 0x44, 0x24, 0xe8,
2569 // ret
2570 0xc3,
2571 };
2572 // clang-format on
2573 EXPECT_EQUALS_BYTES(buf, expected);
2574 AST_heap_free(node);
2575 PASS();
2576}
2577
2578TEST compile_labels_with_no_labels(Buffer *buf) {
2579 ASTNode *node = Reader_read("(labels () 1)");
2580 int compile_result = Compile_entry(buf, node);
2581 ASSERT_EQ(compile_result, 0);
2582 // clang-format off
2583 byte expected[] = {
2584 // mov rsi, rdi
2585 0x48, 0x89, 0xfe,
2586 // mov rax, 0x2
2587 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2588 // ret
2589 0xc3,
2590 };
2591 // clang-format on
2592 EXPECT_EQUALS_BYTES(buf, expected);
2593 Buffer_make_executable(buf);
2594 uword result = Testing_execute_entry(buf, /*heap=*/NULL);
2595 ASSERT_EQ_FMT(Object_encode_integer(1), result, "0x%lx");
2596 AST_heap_free(node);
2597 PASS();
2598}
2599
2600TEST compile_labels_with_one_label(Buffer *buf) {
2601 ASTNode *node = Reader_read("(labels ((const (code () 5))) 1)");
2602 int compile_result = Compile_entry(buf, node);
2603 ASSERT_EQ(compile_result, 0);
2604 // clang-format off
2605 byte expected[] = {
2606 // mov rax, compile(5)
2607 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00,
2608 // ret
2609 0xc3,
2610 // mov rsi, rdi
2611 0x48, 0x89, 0xfe,
2612 // mov rax, 0x2
2613 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2614 // ret
2615 0xc3,
2616 };
2617 // clang-format on
2618 EXPECT_EQUALS_BYTES(buf, expected);
2619 Buffer_make_executable(buf);
2620 uword result = Testing_execute_entry(buf, /*heap=*/NULL);
2621 ASSERT_EQ_FMT(Object_encode_integer(1), result, "0x%lx");
2622 AST_heap_free(node);
2623 PASS();
2624}
2625
2626TEST compile_labelcall_with_no_params(Buffer *buf) {
2627 ASTNode *node =
2628 Reader_read("(labels ((const (code () 5))) (labelcall const))");
2629 int compile_result = Compile_entry(buf, node);
2630 ASSERT_EQ(compile_result, 0);
2631 // clang-format off
2632 byte expected[] = {
2633 // mov rax, compile(5)
2634 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00,
2635 // ret
2636 0xc3,
2637 // mov rsi, rdi
2638 0x48, 0x89, 0xfe,
2639 // call `const`
2640 0xe8, 0xf0, 0xff, 0xff, 0xff,
2641 // ret
2642 0xc3,
2643 };
2644 // clang-format on
2645 EXPECT_EQUALS_BYTES(buf, expected);
2646 Buffer_make_executable(buf);
2647 uword result = Testing_execute_entry(buf, /*heap=*/NULL);
2648 ASSERT_EQ_FMT(Object_encode_integer(5), result, "0x%lx");
2649 AST_heap_free(node);
2650 PASS();
2651}
2652
2653TEST compile_labelcall_with_no_params_and_locals(Buffer *buf) {
2654 ASTNode *node = Reader_read(
2655 "(labels ((const (code () 5))) (let ((a 1)) (labelcall const)))");
2656 int compile_result = Compile_entry(buf, node);
2657 ASSERT_EQ(compile_result, 0);
2658 // clang-format off
2659 byte expected[] = {
2660 // mov rax, compile(5)
2661 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00,
2662 // ret
2663 0xc3,
2664 // mov rsi, rdi
2665 0x48, 0x89, 0xfe,
2666 // mov rax, compile(1)
2667 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2668 // mov [rsp-8], rax
2669 0x48, 0x89, 0x44, 0x24, 0xf8,
2670 // sub rsp, 8
2671 0x48, 0x81, 0xec, 0x08, 0x00, 0x00, 0x00,
2672 // call `const`
2673 0xe8, 0xdd, 0xff, 0xff, 0xff,
2674 // add rsp, 8
2675 0x48, 0x81, 0xc4, 0x08, 0x00, 0x00, 0x00,
2676 // ret
2677 0xc3,
2678 };
2679 // clang-format on
2680 EXPECT_EQUALS_BYTES(buf, expected);
2681 Buffer_make_executable(buf);
2682 uword result = Testing_execute_entry(buf, /*heap=*/NULL);
2683 ASSERT_EQ_FMT(Object_encode_integer(5), result, "0x%lx");
2684 AST_heap_free(node);
2685 PASS();
2686}
2687
2688TEST compile_labelcall_with_one_param(Buffer *buf) {
2689 ASTNode *node = Reader_read("(labels ((id (code (x) x))) (labelcall id 5))");
2690 int compile_result = Compile_entry(buf, node);
2691 ASSERT_EQ(compile_result, 0);
2692 // clang-format off
2693 byte expected[] = {
2694 // mov rax, [rsp-8]
2695 0x48, 0x8b, 0x44, 0x24, 0xf8,
2696 // ret
2697 0xc3,
2698 // mov rsi, rdi
2699 0x48, 0x89, 0xfe,
2700 // mov rax, compile(5)
2701 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00,
2702 // mov [rsp-16], rax
2703 0x48, 0x89, 0x44, 0x24, 0xf0,
2704 // call `id`
2705 0xe8, 0xe6, 0xff, 0xff, 0xff,
2706 // ret
2707 0xc3,
2708 };
2709 // clang-format on
2710 EXPECT_EQUALS_BYTES(buf, expected);
2711 Buffer_make_executable(buf);
2712 uword result = Testing_execute_entry(buf, /*heap=*/NULL);
2713 ASSERT_EQ_FMT(Object_encode_integer(5), result, "0x%lx");
2714 AST_heap_free(node);
2715 PASS();
2716}
2717
2718SUITE(object_tests) {
2719 RUN_TEST(encode_positive_integer);
2720 RUN_TEST(encode_negative_integer);
2721 RUN_TEST(encode_char);
2722 RUN_TEST(decode_char);
2723 RUN_TEST(encode_bool);
2724 RUN_TEST(decode_bool);
2725 RUN_TEST(address);
2726}
2727
2728TEST compile_labelcall_with_one_param_and_locals(Buffer *buf) {
2729 ASTNode *node = Reader_read(
2730 "(labels ((id (code (x) x))) (let ((a 1)) (labelcall id 5)))");
2731 int compile_result = Compile_entry(buf, node);
2732 ASSERT_EQ(compile_result, 0);
2733 // clang-format off
2734 byte expected[] = {
2735 // mov rax, [rsp-8]
2736 0x48, 0x8b, 0x44, 0x24, 0xf8,
2737 // ret
2738 0xc3,
2739 // mov rsi, rdi
2740 0x48, 0x89, 0xfe,
2741 // mov rax, compile(1)
2742 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2743 // mov [rsp-8], rax
2744 0x48, 0x89, 0x44, 0x24, 0xf8,
2745 // mov rax, compile(5)
2746 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00,
2747 // mov [rsp-24], rax
2748 0x48, 0x89, 0x44, 0x24, 0xe8,
2749 // sub rsp, 8
2750 0x48, 0x81, 0xec, 0x08, 0x00, 0x00, 0x00,
2751 // call `id`
2752 0xe8, 0xd3, 0xff, 0xff, 0xff,
2753 // add rsp, 8
2754 0x48, 0x81, 0xc4, 0x08, 0x00, 0x00, 0x00,
2755 // ret
2756 0xc3,
2757 };
2758 // clang-format on
2759 EXPECT_EQUALS_BYTES(buf, expected);
2760 Buffer_make_executable(buf);
2761 uword result = Testing_execute_entry(buf, /*heap=*/NULL);
2762 ASSERT_EQ_FMT(Object_encode_integer(5), result, "0x%lx");
2763 AST_heap_free(node);
2764 PASS();
2765}
2766
2767TEST compile_labelcall_with_two_params_and_locals(Buffer *buf) {
2768 ASTNode *node = Reader_read("(labels ((add (code (x y) (+ x y)))) (let ((a "
2769 "1)) (labelcall add 5 a)))");
2770 int compile_result = Compile_entry(buf, node);
2771 ASSERT_EQ(compile_result, 0);
2772 // clang-format off
2773 byte expected[] = {
2774 // mov rax, [rsp-16]
2775 0x48, 0x8b, 0x44, 0x24, 0xf0,
2776 // mov [rsp-24], rax
2777 0x48, 0x89, 0x44, 0x24, 0xe8,
2778 // mov rax, [rsp-8]
2779 0x48, 0x8b, 0x44, 0x24, 0xf8,
2780 // add rax, [rsp-24]
2781 0x48, 0x03, 0x44, 0x24, 0xe8,
2782 // ret
2783 0xc3,
2784 // mov rsi, rdi
2785 0x48, 0x89, 0xfe,
2786 // mov rax, compile(1)
2787 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00,
2788 // mov [rsp-8], rax
2789 0x48, 0x89, 0x44, 0x24, 0xf8,
2790 // mov rax, compile(5)
2791 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00,
2792 // mov [rsp-24], rax
2793 0x48, 0x89, 0x44, 0x24, 0xe8,
2794 // mov rax, [rsp-8]
2795 0x48, 0x8b, 0x44, 0x24, 0xf8,
2796 // mov [rsp-32], rax
2797 0x48, 0x89, 0x44, 0x24, 0xe0,
2798 // sub rsp, 8
2799 0x48, 0x81, 0xec, 0x08, 0x00, 0x00, 0x00,
2800 // call `add`
2801 0xe8, 0xba, 0xff, 0xff, 0xff,
2802 // add rsp, 8
2803 0x48, 0x81, 0xc4, 0x08, 0x00, 0x00, 0x00,
2804 // ret
2805 0xc3,
2806 };
2807 // clang-format on
2808 EXPECT_EQUALS_BYTES(buf, expected);
2809 Buffer_make_executable(buf);
2810 uword result = Testing_execute_entry(buf, /*heap=*/NULL);
2811 ASSERT_EQ_FMT(Object_encode_integer(6), result, "0x%lx");
2812 AST_heap_free(node);
2813 PASS();
2814}
2815
2816TEST compile_nested_labelcall(Buffer *buf) {
2817 ASTNode *node = Reader_read("(labels ((add (code (x y) (+ x y)))"
2818 " (sub (code (x y) (- x y))))"
2819 " (labelcall sub 4 (labelcall add 1 2)))");
2820 int compile_result = Compile_entry(buf, node);
2821 ASSERT_EQ(compile_result, 0);
2822 Buffer_make_executable(buf);
2823 uword result = Testing_execute_entry(buf, /*heap=*/NULL);
2824 ASSERT_EQ_FMT(Object_encode_integer(1), result, "0x%lx");
2825 AST_heap_free(node);
2826 PASS();
2827}
2828
2829TEST compile_multilevel_labelcall(Buffer *buf) {
2830 ASTNode *node =
2831 Reader_read("(labels ((add (code (x y) (+ x y)))"
2832 " (add2 (code (x y) (labelcall add x y))))"
2833 " (labelcall add2 1 2))");
2834 int compile_result = Compile_entry(buf, node);
2835 ASSERT_EQ(compile_result, 0);
2836 Buffer_make_executable(buf);
2837 uword result = Testing_execute_entry(buf, /*heap=*/NULL);
2838 ASSERT_EQ_FMT(Object_encode_integer(3), result, "0x%lx");
2839 AST_heap_free(node);
2840 PASS();
2841}
2842
2843TEST compile_factorial_labelcall(Buffer *buf) {
2844 ASTNode *node = Reader_read(
2845 "(labels ((factorial (code (x) "
2846 " (if (< x 2) 1 (* x (labelcall factorial (- x 1)))))))"
2847 " (labelcall factorial 5))");
2848 int compile_result = Compile_entry(buf, node);
2849 ASSERT_EQ(compile_result, 0);
2850 Buffer_make_executable(buf);
2851 uword result = Testing_execute_entry(buf, /*heap=*/NULL);
2852 ASSERT_EQ_FMT(Object_encode_integer(120), result, "0x%lx");
2853 AST_heap_free(node);
2854 PASS();
2855}
2856
2857SUITE(ast_tests) {
2858 RUN_TEST(ast_new_pair);
2859 RUN_TEST(ast_pair_car_returns_car);
2860 RUN_TEST(ast_pair_cdr_returns_cdr);
2861 RUN_TEST(ast_new_symbol);
2862}
2863
2864SUITE(reader_tests) {
2865 RUN_TEST(read_with_integer_returns_integer);
2866 RUN_TEST(read_with_negative_integer_returns_integer);
2867 RUN_TEST(read_with_positive_integer_returns_integer);
2868 RUN_TEST(read_with_leading_whitespace_ignores_whitespace);
2869 RUN_TEST(read_with_symbol_returns_symbol);
2870 RUN_TEST(read_with_symbol_with_trailing_digits);
2871 RUN_TEST(read_with_nil_returns_nil);
2872 RUN_TEST(read_with_list_returns_list);
2873 RUN_TEST(read_with_nested_list_returns_list);
2874 RUN_TEST(read_with_char_returns_char);
2875 RUN_TEST(read_with_bool_returns_bool);
2876}
2877
2878SUITE(buffer_tests) {
2879 RUN_BUFFER_TEST(buffer_write8_increases_length);
2880 RUN_TEST(buffer_write8_expands_buffer);
2881 RUN_TEST(buffer_write32_expands_buffer);
2882 RUN_BUFFER_TEST(buffer_write32_writes_little_endian);
2883 RUN_BUFFER_TEST(emit_mov_reg_imm32_emits_modrm);
2884 RUN_BUFFER_TEST(emit_store_reg_indirect_emits_modrm_sib);
2885}
2886
2887SUITE(compiler_tests) {
2888 RUN_BUFFER_TEST(compile_positive_integer);
2889 RUN_BUFFER_TEST(compile_negative_integer);
2890 RUN_BUFFER_TEST(compile_char);
2891 RUN_BUFFER_TEST(compile_true);
2892 RUN_BUFFER_TEST(compile_false);
2893 RUN_BUFFER_TEST(compile_nil);
2894 RUN_BUFFER_TEST(compile_unary_add1);
2895 RUN_BUFFER_TEST(compile_unary_add1_nested);
2896 RUN_BUFFER_TEST(compile_unary_sub1);
2897 RUN_BUFFER_TEST(compile_unary_integer_to_char);
2898 RUN_BUFFER_TEST(compile_unary_char_to_integer);
2899 RUN_BUFFER_TEST(compile_unary_nilp_with_nil_returns_true);
2900 RUN_BUFFER_TEST(compile_unary_nilp_with_non_nil_returns_false);
2901 RUN_BUFFER_TEST(compile_unary_zerop_with_zero_returns_true);
2902 RUN_BUFFER_TEST(compile_unary_zerop_with_non_zero_returns_false);
2903 RUN_BUFFER_TEST(compile_unary_not_with_false_returns_true);
2904 RUN_BUFFER_TEST(compile_unary_not_with_non_false_returns_false);
2905 RUN_BUFFER_TEST(compile_unary_integerp_with_integer_returns_true);
2906 RUN_BUFFER_TEST(compile_unary_integerp_with_non_integer_returns_false);
2907 RUN_BUFFER_TEST(compile_unary_booleanp_with_boolean_returns_true);
2908 RUN_BUFFER_TEST(compile_unary_booleanp_with_non_boolean_returns_false);
2909 RUN_BUFFER_TEST(compile_binary_plus);
2910 RUN_BUFFER_TEST(compile_binary_plus_nested);
2911 RUN_BUFFER_TEST(compile_binary_minus);
2912 RUN_BUFFER_TEST(compile_binary_minus_nested);
2913 RUN_BUFFER_TEST(compile_binary_mul);
2914 RUN_BUFFER_TEST(compile_binary_mul_nested);
2915 RUN_BUFFER_TEST(compile_binary_eq_with_same_address_returns_true);
2916 RUN_BUFFER_TEST(compile_binary_eq_with_different_address_returns_false);
2917 RUN_BUFFER_TEST(compile_binary_lt_with_left_less_than_right_returns_true);
2918 RUN_BUFFER_TEST(compile_binary_lt_with_left_equal_to_right_returns_false);
2919 RUN_BUFFER_TEST(compile_binary_lt_with_left_greater_than_right_returns_false);
2920 RUN_BUFFER_TEST(compile_symbol_in_env_returns_value);
2921 RUN_BUFFER_TEST(compile_symbol_in_env_returns_first_value);
2922 RUN_BUFFER_TEST(compile_symbol_not_in_env_raises_compile_error);
2923 RUN_BUFFER_TEST(compile_let_with_no_bindings);
2924 RUN_BUFFER_TEST(compile_let_with_one_binding);
2925 RUN_BUFFER_TEST(compile_let_with_multiple_bindings);
2926 RUN_BUFFER_TEST(compile_nested_let);
2927 RUN_BUFFER_TEST(compile_let_is_not_let_star);
2928 RUN_BUFFER_TEST(compile_if_with_true_cond);
2929 RUN_BUFFER_TEST(compile_if_with_false_cond);
2930 RUN_BUFFER_TEST(compile_nested_if);
2931 RUN_HEAP_TEST(compile_cons);
2932 RUN_HEAP_TEST(compile_two_cons);
2933 RUN_HEAP_TEST(compile_nested_cons);
2934 RUN_HEAP_TEST(compile_car);
2935 RUN_HEAP_TEST(compile_cdr);
2936 RUN_BUFFER_TEST(compile_code_with_no_params);
2937 RUN_BUFFER_TEST(compile_code_with_one_param);
2938 RUN_BUFFER_TEST(compile_code_with_two_params);
2939 RUN_BUFFER_TEST(compile_labels_with_no_labels);
2940 RUN_BUFFER_TEST(compile_labels_with_one_label);
2941 RUN_BUFFER_TEST(compile_labelcall_with_no_params);
2942 RUN_BUFFER_TEST(compile_labelcall_with_no_params_and_locals);
2943 RUN_BUFFER_TEST(compile_labelcall_with_one_param);
2944 RUN_BUFFER_TEST(compile_labelcall_with_one_param_and_locals);
2945 RUN_BUFFER_TEST(compile_labelcall_with_two_params_and_locals);
2946 RUN_BUFFER_TEST(compile_nested_labelcall);
2947 RUN_BUFFER_TEST(compile_multilevel_labelcall);
2948 RUN_BUFFER_TEST(compile_factorial_labelcall);
2949}
2950
2951// End Tests
2952
2953GREATEST_MAIN_DEFS();
2954
2955int run_tests(int argc, char **argv) {
2956 GREATEST_MAIN_BEGIN();
2957 RUN_SUITE(object_tests);
2958 RUN_SUITE(ast_tests);
2959 RUN_SUITE(reader_tests);
2960 RUN_SUITE(buffer_tests);
2961 RUN_SUITE(compiler_tests);
2962 GREATEST_MAIN_END();
2963}
2964
2965int main(int argc, char **argv) { return run_tests(argc, argv); }