├── .builds ├── alpine.yml └── ubuntu.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── build ├── build.py ├── compiling-binary.c ├── compiling-closures.c ├── compiling-elf.c ├── compiling-heap.c ├── compiling-if.c ├── compiling-immediates.c ├── compiling-integers.c ├── compiling-let.c ├── compiling-procedures.c ├── compiling-reader.c ├── compiling-unary.c ├── greatest.h └── mmap-demo.c /.builds/alpine.yml: -------------------------------------------------------------------------------- 1 | # Minimal system to just run tests 2 | image: alpine/edge 3 | packages: 4 | - gcc 5 | tasks: 6 | - build: | 7 | cd ghuloum 8 | make 9 | - test: | 10 | cd ghuloum 11 | make test 12 | 13 | -------------------------------------------------------------------------------- /.builds/ubuntu.yml: -------------------------------------------------------------------------------- 1 | # For testing older compiler versions 2 | image: ubuntu/18.04 3 | packages: 4 | - gcc 5 | tasks: 6 | - build: | 7 | cd ghuloum 8 | make 9 | - test: | 10 | cd ghuloum 11 | make test 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build.ninja 2 | /.ninja* 3 | /.gdb* 4 | /tags 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Maxwell Bernstein (max@bernsteinbear.com) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OUT = bin 2 | CFLAGS = -O0 -g -Wall -Wextra -pedantic -fno-strict-aliasing -std=c99 3 | TARGETS = mmap-demo compiling-integers compiling-immediates compiling-unary \ 4 | compiling-binary compiling-reader compiling-let compiling-if \ 5 | compiling-heap compiling-procedures compiling-closures compiling-elf 6 | BINARIES = $(addprefix $(OUT)/, $(TARGETS)) 7 | TESTS = $(addprefix test-, $(TARGETS)) 8 | 9 | # $@ means the name of the target that caused the rule to run 10 | # $^ means all of the prerequisites with spaces in between 11 | # $< means the name of the first prerequisite 12 | 13 | all: $(OUT) $(BINARIES) 14 | 15 | test: $(OUT) $(TESTS) 16 | 17 | $(OUT): 18 | mkdir -p $@ 19 | 20 | clean: 21 | rm $(OUT)/* 22 | 23 | $(OUT)/%: %.c greatest.h 24 | $(CC) $(CFLAGS) $< -o $@ 25 | 26 | test-compiling-elf: $(OUT)/compiling-elf 27 | ./$< ./$(OUT)/generated-elf 28 | chmod +x ./$(OUT)/generated-elf 29 | @./$(OUT)/generated-elf || if [ $$? -ne 120 ]; then exit 1; fi 30 | 31 | test-%: $(OUT)/% 32 | ./$< 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lisp compiler source 2 | 3 | [![builds.sr.ht status](https://builds.sr.ht/~max/ghuloum.svg)](https://builds.sr.ht/~max/ghuloum?) 4 | 5 | This source is provided in stages that roughly correspond to the blog posts in 6 | the [Lisp compiler series](https://bernsteinbear.com/blog/compiling-a-lisp-0/). 7 | -------------------------------------------------------------------------------- /build: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | # Originally written by akkartik. Modified by tekknolagi. 4 | 5 | test "$CC" || export CC=cc 6 | export CFLAGS="$CFLAGS -O0 -g -Wall -Wextra -pedantic -fno-strict-aliasing" 7 | 8 | # return 1 if $1 is older than _any_ of the remaining args 9 | older_than() { 10 | local target=$1 11 | shift 12 | if [ ! -e $target ] 13 | then 14 | echo "updating $target" >&2 15 | return 0 # success 16 | fi 17 | local f 18 | for f in $* 19 | do 20 | if [ $f -nt $target ] 21 | then 22 | echo "updating $target" >&2 23 | return 0 # success 24 | fi 25 | done 26 | return 1 # failure 27 | } 28 | 29 | update_if_necessary() { 30 | older_than ./bin/$1 $1.c greatest.h build && { 31 | $CC $CFLAGS $1.c -o ./bin/$1 32 | } 33 | return 0 # success 34 | } 35 | 36 | update_if_necessary mmap-demo 37 | update_if_necessary compiling-integers 38 | update_if_necessary compiling-immediates 39 | update_if_necessary compiling-unary 40 | update_if_necessary compiling-binary 41 | update_if_necessary compiling-reader 42 | update_if_necessary compiling-let 43 | update_if_necessary compiling-if 44 | update_if_necessary compiling-heap 45 | update_if_necessary compiling-procedures 46 | update_if_necessary compiling-closures 47 | update_if_necessary compiling-elf 48 | 49 | exit 0 50 | -------------------------------------------------------------------------------- /build.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | class Gen: 3 | def __init__(self, stream): 4 | self.stream = stream 5 | 6 | def writeline(self, line): 7 | self.stream.write(line) 8 | self.stream.write("\n") 9 | 10 | def rule(self, name, **kwargs): 11 | self.writeline(f"rule {name}") 12 | if "description" not in kwargs: 13 | kwargs["description"] = f"{name} $out" 14 | for key, value in kwargs.items(): 15 | self.writeline(f" {key} = {value}") 16 | self.writeline("") 17 | 18 | def var(self, name, value): 19 | self.writeline(f"{name} = {value}") 20 | 21 | def __getattr__(self, name): 22 | def rule(output, input): 23 | self.writeline(f"build {output}: {name} {input}") 24 | 25 | return rule 26 | 27 | 28 | def remove_ext(name): 29 | return name.rpartition(".")[0] 30 | 31 | 32 | CC = "gcc" 33 | CFLAGS = "-O0 -g -Wall -Wextra -pedantic -fno-strict-aliasing -std=c99" 34 | OUTDIR = "bin" 35 | SRCS = [ 36 | "mmap-demo.c", 37 | "compiling-integers.c", 38 | "compiling-immediates.c", 39 | "compiling-unary.c", 40 | "compiling-binary.c", 41 | "compiling-reader.c", 42 | "compiling-let.c", 43 | "compiling-if.c", 44 | "compiling-heap.c", 45 | "compiling-procedures.c", 46 | "compiling-closures.c", 47 | "compiling-elf.c", 48 | ] 49 | NINJA = "build.ninja" 50 | BINS = {src: f"./{OUTDIR}/{remove_ext(src)}" for src in SRCS} 51 | 52 | with open(NINJA, "w+") as f: 53 | g = Gen(f) 54 | g.var("CC", CC) 55 | g.var("CFLAGS", CFLAGS) 56 | g.rule("CC", command="$CC $CFLAGS ${opts} $in -o $out") 57 | g.rule( 58 | "REGEN", 59 | command=f"python3 {__file__}", 60 | description="Regenerating build.ninja...", 61 | ) 62 | 63 | for src in SRCS: 64 | g.CC(BINS[src], src) 65 | 66 | g.REGEN(NINJA, f"| {__file__}") 67 | g.phony("all", " ".join(BINS.values())) 68 | g.writeline("default all") 69 | -------------------------------------------------------------------------------- /compiling-binary.c: -------------------------------------------------------------------------------- 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-binary.c 4 | 5 | #define _GNU_SOURCE 6 | #include // for assert 7 | #include // for bool 8 | #include // for NULL 9 | #include // for int32_t, etc 10 | #include // for memcpy 11 | #include // for mmap 12 | #undef _GNU_SOURCE 13 | 14 | #include "greatest.h" 15 | 16 | // Objects 17 | 18 | typedef int64_t word; 19 | typedef uint64_t uword; 20 | 21 | // These constants are defined in a enum because the right hand side of a 22 | // statement like 23 | // static const int kFoo = ...; 24 | // must be a so-called "Integer Constant Expression". Compilers are required to 25 | // support a certain set of these expressions, but are not required to support 26 | // arbitrary arithmetic with other integer constants. Compilers such as gcc 27 | // before gcc-8 just decided not to play this game, while gcc-8+ and Clang play 28 | // just fine. 29 | // Since this arithmetic with constant values works just fine for enums, make 30 | // all these constants enum values instead. 31 | // See https://twitter.com/tekknolagi/status/1328449329472835586 for more info. 32 | enum { 33 | kBitsPerByte = 8, // bits 34 | kWordSize = sizeof(word), // bytes 35 | kBitsPerWord = kWordSize * kBitsPerByte, // bits 36 | 37 | kIntegerTag = 0x0, // 0b00 38 | kIntegerTagMask = 0x3, // 0b11 39 | kIntegerShift = 2, 40 | kIntegerBits = kBitsPerWord - kIntegerShift, 41 | 42 | kImmediateTagMask = 0x3f, 43 | 44 | kCharTag = 0x0f, // 0b00001111 45 | kCharMask = 0xff, // 0b11111111 46 | kCharShift = 8, 47 | 48 | kBoolTag = 0x1f, // 0b0011111 49 | kBoolMask = 0x80, // 0b10000000 50 | kBoolShift = 7, 51 | 52 | kNilTag = 0x2f, // 0b101111 53 | 54 | kErrorTag = 0x3f, // 0b111111 55 | 56 | kPairTag = 0x1, // 0b001 57 | kSymbolTag = 0x5, // 0b101 58 | kClosureTag = 0x6, // 0b110 59 | kHeapTagMask = ((uword)0x7), // 0b000...111 60 | kHeapPtrMask = ~kHeapTagMask, // 0b1111...1000 61 | }; 62 | 63 | // These are defined as macros because they will not work as static const int 64 | // constants (per above explanation), and enum constants are only required to 65 | // be an int wide (per ISO C). 66 | #define INTEGER_MAX ((1LL << (kIntegerBits - 1)) - 1) 67 | #define INTEGER_MIN (-(1LL << (kIntegerBits - 1))) 68 | 69 | uword Object_encode_integer(word value) { 70 | assert(value < INTEGER_MAX && "too big"); 71 | assert(value > INTEGER_MIN && "too small"); 72 | return value << kIntegerShift; 73 | } 74 | 75 | word Object_decode_integer(uword value) { return (word)value >> kIntegerShift; } 76 | 77 | uword Object_encode_char(char value) { 78 | return ((uword)value << kCharShift) | kCharTag; 79 | } 80 | 81 | char Object_decode_char(uword value) { 82 | return (value >> kCharShift) & kCharMask; 83 | } 84 | 85 | uword Object_encode_bool(bool value) { 86 | return ((uword)value << kBoolShift) | kBoolTag; 87 | } 88 | 89 | bool Object_decode_bool(uword value) { return value & kBoolMask; } 90 | 91 | uword Object_true() { return Object_encode_bool(true); } 92 | 93 | uword Object_false() { return Object_encode_bool(false); } 94 | 95 | uword Object_nil() { return 0x2f; } 96 | 97 | uword Object_address(void *obj) { return (uword)obj & kHeapPtrMask; } 98 | 99 | // End Objects 100 | 101 | // Buffer 102 | 103 | typedef unsigned char byte; 104 | 105 | typedef enum { 106 | kWritable, 107 | kExecutable, 108 | } BufferState; 109 | 110 | typedef struct { 111 | byte *address; 112 | BufferState state; 113 | size_t len; 114 | size_t capacity; 115 | } Buffer; 116 | 117 | byte *Buffer_alloc_writable(size_t capacity) { 118 | byte *result = mmap(/*addr=*/NULL, capacity, PROT_READ | PROT_WRITE, 119 | MAP_ANONYMOUS | MAP_PRIVATE, 120 | /*filedes=*/-1, /*off=*/0); 121 | assert(result != MAP_FAILED); 122 | return result; 123 | } 124 | 125 | void Buffer_init(Buffer *result, size_t capacity) { 126 | result->address = Buffer_alloc_writable(capacity); 127 | assert(result->address != MAP_FAILED); 128 | result->state = kWritable; 129 | result->len = 0; 130 | result->capacity = capacity; 131 | } 132 | 133 | void Buffer_deinit(Buffer *buf) { 134 | munmap(buf->address, buf->capacity); 135 | buf->address = NULL; 136 | buf->len = 0; 137 | buf->capacity = 0; 138 | } 139 | 140 | int Buffer_make_executable(Buffer *buf) { 141 | int result = mprotect(buf->address, buf->len, PROT_EXEC); 142 | buf->state = kExecutable; 143 | return result; 144 | } 145 | 146 | byte Buffer_at8(Buffer *buf, size_t pos) { return buf->address[pos]; } 147 | 148 | void Buffer_at_put8(Buffer *buf, size_t pos, byte b) { buf->address[pos] = b; } 149 | 150 | word max(word left, word right) { return left > right ? left : right; } 151 | 152 | void Buffer_ensure_capacity(Buffer *buf, word additional_capacity) { 153 | if (buf->len + additional_capacity <= buf->capacity) { 154 | return; 155 | } 156 | word new_capacity = 157 | max(buf->capacity * 2, buf->capacity + additional_capacity); 158 | byte *address = Buffer_alloc_writable(new_capacity); 159 | memcpy(address, buf->address, buf->len); 160 | int result = munmap(buf->address, buf->capacity); 161 | assert(result == 0 && "munmap failed"); 162 | buf->address = address; 163 | buf->capacity = new_capacity; 164 | } 165 | 166 | void Buffer_write8(Buffer *buf, byte b) { 167 | Buffer_ensure_capacity(buf, sizeof b); 168 | Buffer_at_put8(buf, buf->len++, b); 169 | } 170 | 171 | void Buffer_write32(Buffer *buf, int32_t value) { 172 | for (size_t i = 0; i < 4; i++) { 173 | Buffer_write8(buf, (value >> (i * kBitsPerByte)) & 0xff); 174 | } 175 | } 176 | 177 | void Buffer_write_arr(Buffer *buf, const byte *arr, word arr_size) { 178 | Buffer_ensure_capacity(buf, arr_size); 179 | for (word i = 0; i < arr_size; i++) { 180 | Buffer_write8(buf, arr[i]); 181 | } 182 | } 183 | 184 | // End Buffer 185 | 186 | // Emit 187 | 188 | typedef enum { 189 | kRax = 0, 190 | kRcx, 191 | kRdx, 192 | kRbx, 193 | kRsp, 194 | kRbp, 195 | kRsi, 196 | kRdi, 197 | } Register; 198 | 199 | typedef enum { 200 | kAl = 0, 201 | kCl, 202 | kDl, 203 | kBl, 204 | kAh, 205 | kCh, 206 | kDh, 207 | kBh, 208 | } PartialRegister; 209 | 210 | typedef enum { 211 | kOverflow = 0, 212 | kNotOverflow, 213 | kBelow, 214 | kCarry = kBelow, 215 | kNotAboveOrEqual = kBelow, 216 | kAboveOrEqual, 217 | kNotBelow = kAboveOrEqual, 218 | kNotCarry = kAboveOrEqual, 219 | kEqual, 220 | kZero = kEqual, 221 | kLess = 0xc, 222 | kNotGreaterOrEqual = kLess, 223 | // TODO(max): Add more 224 | } Condition; 225 | 226 | typedef struct Indirect { 227 | Register reg; 228 | int8_t disp; 229 | } Indirect; 230 | 231 | Indirect Ind(Register reg, int8_t disp) { 232 | return (Indirect){.reg = reg, .disp = disp}; 233 | } 234 | 235 | enum { 236 | kRexPrefix = 0x48, 237 | }; 238 | 239 | void Emit_mov_reg_imm32(Buffer *buf, Register dst, int32_t src) { 240 | Buffer_write8(buf, kRexPrefix); 241 | Buffer_write8(buf, 0xc7); 242 | Buffer_write8(buf, 0xc0 + dst); 243 | Buffer_write32(buf, src); 244 | } 245 | 246 | void Emit_ret(Buffer *buf) { Buffer_write8(buf, 0xc3); } 247 | 248 | void Emit_add_reg_imm32(Buffer *buf, Register dst, int32_t src) { 249 | Buffer_write8(buf, kRexPrefix); 250 | if (dst == kRax) { 251 | // Optimization: add eax, {imm32} can either be encoded as 05 {imm32} or 81 252 | // c0 {imm32}. 253 | Buffer_write8(buf, 0x05); 254 | } else { 255 | Buffer_write8(buf, 0x81); 256 | Buffer_write8(buf, 0xc0 + dst); 257 | } 258 | Buffer_write32(buf, src); 259 | } 260 | 261 | void Emit_sub_reg_imm32(Buffer *buf, Register dst, int32_t src) { 262 | Buffer_write8(buf, kRexPrefix); 263 | if (dst == kRax) { 264 | // Optimization: sub eax, {imm32} can either be encoded as 2d {imm32} or 81 265 | // e8 {imm32}. 266 | Buffer_write8(buf, 0x2d); 267 | } else { 268 | Buffer_write8(buf, 0x81); 269 | Buffer_write8(buf, 0xe8 + dst); 270 | } 271 | Buffer_write32(buf, src); 272 | } 273 | 274 | void Emit_shl_reg_imm8(Buffer *buf, Register dst, int8_t bits) { 275 | Buffer_write8(buf, kRexPrefix); 276 | Buffer_write8(buf, 0xc1); 277 | Buffer_write8(buf, 0xe0 + dst); 278 | Buffer_write8(buf, bits); 279 | } 280 | 281 | void Emit_shr_reg_imm8(Buffer *buf, Register dst, int8_t bits) { 282 | Buffer_write8(buf, kRexPrefix); 283 | Buffer_write8(buf, 0xc1); 284 | Buffer_write8(buf, 0xe8 + dst); 285 | Buffer_write8(buf, bits); 286 | } 287 | 288 | void Emit_or_reg_imm8(Buffer *buf, Register dst, uint8_t tag) { 289 | Buffer_write8(buf, kRexPrefix); 290 | Buffer_write8(buf, 0x83); 291 | Buffer_write8(buf, 0xc8 + dst); 292 | Buffer_write8(buf, tag); 293 | } 294 | 295 | void Emit_and_reg_imm8(Buffer *buf, Register dst, uint8_t tag) { 296 | Buffer_write8(buf, kRexPrefix); 297 | Buffer_write8(buf, 0x83); 298 | Buffer_write8(buf, 0xe0 + dst); 299 | Buffer_write8(buf, tag); 300 | } 301 | 302 | void Emit_cmp_reg_imm32(Buffer *buf, Register left, int32_t right) { 303 | Buffer_write8(buf, kRexPrefix); 304 | if (left == kRax) { 305 | // Optimization: cmp rax, {imm32} can either be encoded as 3d {imm32} or 81 306 | // f8 {imm32}. 307 | Buffer_write8(buf, 0x3d); 308 | } else { 309 | Buffer_write8(buf, 0x81); 310 | Buffer_write8(buf, 0xf8 + left); 311 | } 312 | Buffer_write32(buf, right); 313 | } 314 | 315 | void Emit_setcc_imm8(Buffer *buf, Condition cond, PartialRegister dst) { 316 | Buffer_write8(buf, 0x0f); 317 | Buffer_write8(buf, 0x90 + cond); 318 | Buffer_write8(buf, 0xc0 + dst); 319 | } 320 | 321 | uint8_t disp8(int8_t disp) { return disp >= 0 ? disp : 0x100 + disp; } 322 | 323 | // mov [dst+disp], src 324 | // or 325 | // mov %src, disp(%dst) 326 | void Emit_store_reg_indirect(Buffer *buf, Indirect dst, Register src) { 327 | Buffer_write8(buf, kRexPrefix); 328 | Buffer_write8(buf, 0x89); 329 | Buffer_write8(buf, 0x40 + src * 8 + dst.reg); 330 | Buffer_write8(buf, disp8(dst.disp)); 331 | } 332 | 333 | // add dst, [src+disp] 334 | // or 335 | // add disp(%src), %dst 336 | void Emit_add_reg_indirect(Buffer *buf, Register dst, Indirect src) { 337 | Buffer_write8(buf, kRexPrefix); 338 | Buffer_write8(buf, 0x03); 339 | Buffer_write8(buf, 0x40 + dst * 8 + src.reg); 340 | Buffer_write8(buf, disp8(src.disp)); 341 | } 342 | 343 | // sub dst, [src+disp] 344 | // or 345 | // sub disp(%src), %dst 346 | void Emit_sub_reg_indirect(Buffer *buf, Register dst, Indirect src) { 347 | Buffer_write8(buf, kRexPrefix); 348 | Buffer_write8(buf, 0x2b); 349 | Buffer_write8(buf, 0x40 + dst * 8 + src.reg); 350 | Buffer_write8(buf, disp8(src.disp)); 351 | } 352 | 353 | // mul rax, [src+disp] 354 | // or 355 | // mul disp(%src), %rax 356 | void Emit_mul_reg_indirect(Buffer *buf, Indirect src) { 357 | Buffer_write8(buf, kRexPrefix); 358 | Buffer_write8(buf, 0xf7); 359 | Buffer_write8(buf, 0x60 + src.reg); 360 | Buffer_write8(buf, disp8(src.disp)); 361 | } 362 | 363 | // cmp left, [right+disp] 364 | // or 365 | // cmp disp(%right), %left 366 | void Emit_cmp_reg_indirect(Buffer *buf, Register left, Indirect right) { 367 | Buffer_write8(buf, kRexPrefix); 368 | Buffer_write8(buf, 0x3b); 369 | Buffer_write8(buf, 0x40 + left * 8 + right.reg); 370 | Buffer_write8(buf, disp8(right.disp)); 371 | } 372 | 373 | // End Emit 374 | 375 | // AST 376 | 377 | typedef struct ASTNode ASTNode; 378 | 379 | typedef struct Pair { 380 | ASTNode *car; 381 | ASTNode *cdr; 382 | } Pair; 383 | 384 | typedef struct Symbol { 385 | word length; 386 | char cstr[]; 387 | } Symbol; 388 | 389 | bool AST_is_integer(ASTNode *node) { 390 | return ((uword)node & kIntegerTagMask) == kIntegerTag; 391 | } 392 | 393 | word AST_get_integer(ASTNode *node) { 394 | return Object_decode_integer((uword)node); 395 | } 396 | 397 | ASTNode *AST_new_integer(word value) { 398 | return (ASTNode *)Object_encode_integer(value); 399 | } 400 | 401 | bool AST_is_char(ASTNode *node) { 402 | return ((uword)node & kImmediateTagMask) == kCharTag; 403 | } 404 | 405 | char AST_get_char(ASTNode *node) { return Object_decode_char((uword)node); } 406 | 407 | ASTNode *AST_new_char(char value) { 408 | return (ASTNode *)Object_encode_char(value); 409 | } 410 | 411 | bool AST_is_bool(ASTNode *node) { 412 | return ((uword)node & kImmediateTagMask) == kBoolTag; 413 | } 414 | 415 | bool AST_get_bool(ASTNode *node) { return Object_decode_bool((uword)node); } 416 | 417 | ASTNode *AST_new_bool(bool value) { 418 | return (ASTNode *)Object_encode_bool(value); 419 | } 420 | 421 | bool AST_is_nil(ASTNode *node) { return (uword)node == Object_nil(); } 422 | 423 | ASTNode *AST_nil() { return (ASTNode *)Object_nil(); } 424 | 425 | ASTNode *AST_heap_alloc(unsigned char tag, uword size) { 426 | // Initialize to 0 427 | uword address = (uword)calloc(size, 1); 428 | return (ASTNode *)(address | tag); 429 | } 430 | 431 | bool AST_is_heap_object(ASTNode *node) { 432 | // For some reason masking out the tag first and then doing the comparison 433 | // makes this branchless 434 | unsigned char tag = (uword)node & kHeapTagMask; 435 | // Heap object tags are between 0b001 and 0b110 except for 0b100 (which is an 436 | // integer) 437 | return (tag & kIntegerTagMask) > 0 && (tag & kImmediateTagMask) != 0x7; 438 | } 439 | 440 | void AST_pair_set_car(ASTNode *node, ASTNode *car); 441 | void AST_pair_set_cdr(ASTNode *node, ASTNode *cdr); 442 | 443 | ASTNode *AST_new_pair(ASTNode *car, ASTNode *cdr) { 444 | ASTNode *node = AST_heap_alloc(kPairTag, sizeof(Pair)); 445 | AST_pair_set_car(node, car); 446 | AST_pair_set_cdr(node, cdr); 447 | return node; 448 | } 449 | 450 | bool AST_is_pair(ASTNode *node) { 451 | return ((uword)node & kHeapTagMask) == kPairTag; 452 | } 453 | 454 | Pair *AST_as_pair(ASTNode *node) { 455 | assert(AST_is_pair(node)); 456 | return (Pair *)Object_address(node); 457 | } 458 | 459 | ASTNode *AST_pair_car(ASTNode *node) { return AST_as_pair(node)->car; } 460 | 461 | void AST_pair_set_car(ASTNode *node, ASTNode *car) { 462 | AST_as_pair(node)->car = car; 463 | } 464 | 465 | ASTNode *AST_pair_cdr(ASTNode *node) { return AST_as_pair(node)->cdr; } 466 | 467 | void AST_pair_set_cdr(ASTNode *node, ASTNode *cdr) { 468 | AST_as_pair(node)->cdr = cdr; 469 | } 470 | 471 | void AST_heap_free(ASTNode *node) { 472 | if (!AST_is_heap_object(node)) { 473 | return; 474 | } 475 | if (AST_is_pair(node)) { 476 | AST_heap_free(AST_pair_car(node)); 477 | AST_heap_free(AST_pair_cdr(node)); 478 | } 479 | free((void *)Object_address(node)); 480 | } 481 | 482 | Symbol *AST_as_symbol(ASTNode *node); 483 | 484 | ASTNode *AST_new_symbol(const char *str) { 485 | word data_length = strlen(str) + 1; // for NUL 486 | ASTNode *node = AST_heap_alloc(kSymbolTag, sizeof(Symbol) + data_length); 487 | Symbol *s = AST_as_symbol(node); 488 | s->length = data_length; 489 | memcpy(s->cstr, str, data_length); 490 | return node; 491 | } 492 | 493 | bool AST_is_symbol(ASTNode *node) { 494 | return ((uword)node & kHeapTagMask) == kSymbolTag; 495 | } 496 | 497 | Symbol *AST_as_symbol(ASTNode *node) { 498 | assert(AST_is_symbol(node)); 499 | return (Symbol *)Object_address(node); 500 | } 501 | 502 | const char *AST_symbol_cstr(ASTNode *node) { 503 | return (const char *)AST_as_symbol(node)->cstr; 504 | } 505 | 506 | bool AST_symbol_matches(ASTNode *node, const char *cstr) { 507 | return strcmp(AST_symbol_cstr(node), cstr) == 0; 508 | } 509 | 510 | int node_to_str(ASTNode *node, char *buf, word size); 511 | 512 | int list_to_str(ASTNode *node, char *buf, word size) { 513 | if (AST_is_pair(node)) { 514 | word result = 0; 515 | result += snprintf(buf + result, size, " "); 516 | result += node_to_str(AST_pair_car(node), buf + result, size); 517 | result += list_to_str(AST_pair_cdr(node), buf + result, size); 518 | return result; 519 | } 520 | if (AST_is_nil(node)) { 521 | return snprintf(buf, size, ")"); 522 | } 523 | word result = 0; 524 | result += snprintf(buf + result, size, " . "); 525 | result += node_to_str(node, buf + result, size); 526 | result += snprintf(buf + result, size, ")"); 527 | return result; 528 | } 529 | 530 | int node_to_str(ASTNode *node, char *buf, word size) { 531 | assert(node != NULL); 532 | if (AST_is_integer(node)) { 533 | return snprintf(buf, size, "%ld", AST_get_integer(node)); 534 | } 535 | if (AST_is_char(node)) { 536 | return snprintf(buf, size, "'%c'", AST_get_char(node)); 537 | } 538 | if (AST_is_bool(node)) { 539 | return snprintf(buf, size, "%s", AST_get_bool(node) ? "true" : "false"); 540 | } 541 | if (AST_is_nil(node)) { 542 | return snprintf(buf, size, "nil"); 543 | } 544 | if (AST_is_pair(node)) { 545 | word result = 0; 546 | result += snprintf(buf + result, size, "("); 547 | result += node_to_str(AST_pair_car(node), buf + result, size); 548 | result += list_to_str(AST_pair_cdr(node), buf + result, size); 549 | return result; 550 | } 551 | if (AST_is_symbol(node)) { 552 | return snprintf(buf, size, "%s", AST_symbol_cstr(node)); 553 | } 554 | assert(0 && "unknown ast"); 555 | } 556 | 557 | char *AST_to_cstr(ASTNode *node) { 558 | int size = node_to_str(node, NULL, 0); 559 | char *buf = malloc(size + 1); 560 | assert(buf != NULL); 561 | node_to_str(node, buf, size + 1); 562 | buf[size] = '\0'; 563 | return buf; 564 | } 565 | 566 | // End AST 567 | 568 | // Compile 569 | 570 | int Compile_expr(Buffer *buf, ASTNode *node, word stack_index); 571 | 572 | ASTNode *operand1(ASTNode *args) { return AST_pair_car(args); } 573 | 574 | ASTNode *operand2(ASTNode *args) { return AST_pair_car(AST_pair_cdr(args)); } 575 | 576 | #define _(exp) \ 577 | do { \ 578 | int result = exp; \ 579 | if (result != 0) \ 580 | return result; \ 581 | } while (0) 582 | 583 | void Compile_compare_imm32(Buffer *buf, int32_t value) { 584 | Emit_cmp_reg_imm32(buf, kRax, value); 585 | Emit_mov_reg_imm32(buf, kRax, 0); 586 | Emit_setcc_imm8(buf, kEqual, kAl); 587 | Emit_shl_reg_imm8(buf, kRax, kBoolShift); 588 | Emit_or_reg_imm8(buf, kRax, kBoolTag); 589 | } 590 | 591 | int Compile_call(Buffer *buf, ASTNode *callable, ASTNode *args, 592 | word stack_index) { 593 | if (AST_is_symbol(callable)) { 594 | if (AST_symbol_matches(callable, "add1")) { 595 | _(Compile_expr(buf, operand1(args), stack_index)); 596 | Emit_add_reg_imm32(buf, kRax, Object_encode_integer(1)); 597 | return 0; 598 | } 599 | if (AST_symbol_matches(callable, "sub1")) { 600 | _(Compile_expr(buf, operand1(args), stack_index)); 601 | Emit_sub_reg_imm32(buf, kRax, Object_encode_integer(1)); 602 | return 0; 603 | } 604 | if (AST_symbol_matches(callable, "integer->char")) { 605 | _(Compile_expr(buf, operand1(args), stack_index)); 606 | Emit_shl_reg_imm8(buf, kRax, kCharShift - kIntegerShift); 607 | Emit_or_reg_imm8(buf, kRax, kCharTag); 608 | return 0; 609 | } 610 | if (AST_symbol_matches(callable, "char->integer")) { 611 | _(Compile_expr(buf, operand1(args), stack_index)); 612 | Emit_shr_reg_imm8(buf, kRax, kCharShift - kIntegerShift); 613 | return 0; 614 | } 615 | if (AST_symbol_matches(callable, "nil?")) { 616 | _(Compile_expr(buf, operand1(args), stack_index)); 617 | Compile_compare_imm32(buf, Object_nil()); 618 | return 0; 619 | } 620 | if (AST_symbol_matches(callable, "zero?")) { 621 | _(Compile_expr(buf, operand1(args), stack_index)); 622 | Compile_compare_imm32(buf, Object_encode_integer(0)); 623 | return 0; 624 | } 625 | if (AST_symbol_matches(callable, "not")) { 626 | _(Compile_expr(buf, operand1(args), stack_index)); 627 | // All non #f values are truthy 628 | // ...this might be a problem if we want to make nil falsey 629 | Compile_compare_imm32(buf, Object_false()); 630 | return 0; 631 | } 632 | if (AST_symbol_matches(callable, "integer?")) { 633 | _(Compile_expr(buf, operand1(args), stack_index)); 634 | Emit_and_reg_imm8(buf, kRax, kIntegerTagMask); 635 | Compile_compare_imm32(buf, kIntegerTag); 636 | return 0; 637 | } 638 | if (AST_symbol_matches(callable, "boolean?")) { 639 | _(Compile_expr(buf, operand1(args), stack_index)); 640 | Emit_and_reg_imm8(buf, kRax, kImmediateTagMask); 641 | Compile_compare_imm32(buf, kBoolTag); 642 | return 0; 643 | } 644 | if (AST_symbol_matches(callable, "+")) { 645 | _(Compile_expr(buf, operand2(args), stack_index)); 646 | Emit_store_reg_indirect(buf, /*dst=*/Ind(kRbp, stack_index), 647 | /*src=*/kRax); 648 | _(Compile_expr(buf, operand1(args), stack_index - kWordSize)); 649 | Emit_add_reg_indirect(buf, /*dst=*/kRax, /*src=*/Ind(kRbp, stack_index)); 650 | return 0; 651 | } 652 | if (AST_symbol_matches(callable, "-")) { 653 | _(Compile_expr(buf, operand2(args), stack_index)); 654 | Emit_store_reg_indirect(buf, /*dst=*/Ind(kRbp, stack_index), 655 | /*src=*/kRax); 656 | _(Compile_expr(buf, operand1(args), stack_index - kWordSize)); 657 | Emit_sub_reg_indirect(buf, /*dst=*/kRax, /*src=*/Ind(kRbp, stack_index)); 658 | return 0; 659 | } 660 | if (AST_symbol_matches(callable, "*")) { 661 | _(Compile_expr(buf, operand2(args), stack_index)); 662 | // Remove the tag so that the result is still only tagged with 0b00 663 | // instead of 0b0000 664 | Emit_shr_reg_imm8(buf, kRax, kIntegerShift); 665 | Emit_store_reg_indirect(buf, /*dst=*/Ind(kRbp, stack_index), 666 | /*src=*/kRax); 667 | _(Compile_expr(buf, operand1(args), stack_index - kWordSize)); 668 | Emit_mul_reg_indirect(buf, /*src=*/Ind(kRbp, stack_index)); 669 | return 0; 670 | } 671 | if (AST_symbol_matches(callable, "=")) { 672 | _(Compile_expr(buf, operand2(args), stack_index)); 673 | Emit_store_reg_indirect(buf, /*dst=*/Ind(kRbp, stack_index), 674 | /*src=*/kRax); 675 | _(Compile_expr(buf, operand1(args), stack_index - kWordSize)); 676 | Emit_cmp_reg_indirect(buf, kRax, Ind(kRbp, stack_index)); 677 | Emit_mov_reg_imm32(buf, kRax, 0); 678 | Emit_setcc_imm8(buf, kEqual, kAl); 679 | Emit_shl_reg_imm8(buf, kRax, kBoolShift); 680 | Emit_or_reg_imm8(buf, kRax, kBoolTag); 681 | return 0; 682 | } 683 | if (AST_symbol_matches(callable, "<")) { 684 | _(Compile_expr(buf, operand2(args), stack_index)); 685 | Emit_store_reg_indirect(buf, /*dst=*/Ind(kRbp, stack_index), 686 | /*src=*/kRax); 687 | _(Compile_expr(buf, operand1(args), stack_index - kWordSize)); 688 | Emit_cmp_reg_indirect(buf, kRax, Ind(kRbp, stack_index)); 689 | Emit_mov_reg_imm32(buf, kRax, 0); 690 | Emit_setcc_imm8(buf, kLess, kAl); 691 | Emit_shl_reg_imm8(buf, kRax, kBoolShift); 692 | Emit_or_reg_imm8(buf, kRax, kBoolTag); 693 | return 0; 694 | } 695 | } 696 | assert(0 && "unexpected call type"); 697 | } 698 | 699 | int Compile_expr(Buffer *buf, ASTNode *node, word stack_index) { 700 | if (AST_is_integer(node)) { 701 | word value = AST_get_integer(node); 702 | Emit_mov_reg_imm32(buf, kRax, Object_encode_integer(value)); 703 | return 0; 704 | } 705 | if (AST_is_char(node)) { 706 | char value = AST_get_char(node); 707 | Emit_mov_reg_imm32(buf, kRax, Object_encode_char(value)); 708 | return 0; 709 | } 710 | if (AST_is_bool(node)) { 711 | bool value = AST_get_bool(node); 712 | Emit_mov_reg_imm32(buf, kRax, Object_encode_bool(value)); 713 | return 0; 714 | } 715 | if (AST_is_nil(node)) { 716 | Emit_mov_reg_imm32(buf, kRax, Object_nil()); 717 | return 0; 718 | } 719 | if (AST_is_pair(node)) { 720 | return Compile_call(buf, AST_pair_car(node), AST_pair_cdr(node), 721 | stack_index); 722 | } 723 | assert(0 && "unexpected node type"); 724 | } 725 | 726 | static const byte kFunctionPrologue[] = { 727 | // push rbp 728 | 0x55, 729 | // mov rbp, rsp 730 | kRexPrefix, 731 | 0x89, 732 | 0xe5, 733 | }; 734 | 735 | static const byte kFunctionEpilogue[] = { 736 | // pop rbp 737 | 0x5d, 738 | // ret 739 | 0xc3, 740 | }; 741 | 742 | int Compile_function(Buffer *buf, ASTNode *node) { 743 | Buffer_write_arr(buf, kFunctionPrologue, sizeof kFunctionPrologue); 744 | _(Compile_expr(buf, node, -kWordSize)); 745 | Buffer_write_arr(buf, kFunctionEpilogue, sizeof kFunctionEpilogue); 746 | return 0; 747 | } 748 | 749 | // End Compile 750 | 751 | typedef int (*JitFunction)(); 752 | 753 | // Testing 754 | 755 | uword Testing_execute_expr(Buffer *buf) { 756 | assert(buf != NULL); 757 | assert(buf->address != NULL); 758 | assert(buf->state == kExecutable); 759 | // The pointer-pointer cast is allowed but the underlying 760 | // data-to-function-pointer back-and-forth is only guaranteed to work on 761 | // POSIX systems (because of eg dlsym). 762 | JitFunction function = *(JitFunction *)(&buf->address); 763 | return function(); 764 | } 765 | 766 | TEST Testing_expect_function_has_contents(Buffer *buf, byte *arr, 767 | size_t arr_size) { 768 | size_t total_size = 769 | sizeof kFunctionPrologue + arr_size + sizeof kFunctionEpilogue; 770 | ASSERT_EQ(total_size, buf->len); 771 | 772 | byte *ptr = buf->address; 773 | ASSERT_MEM_EQ(kFunctionPrologue, ptr, sizeof kFunctionPrologue); 774 | ptr += sizeof kFunctionPrologue; 775 | ASSERT_MEM_EQ(arr, ptr, arr_size); 776 | ptr += arr_size; 777 | ASSERT_MEM_EQ(kFunctionEpilogue, ptr, sizeof kFunctionEpilogue); 778 | ptr += sizeof kFunctionEpilogue; 779 | PASS(); 780 | } 781 | 782 | #define EXPECT_EQUALS_BYTES(buf, arr) \ 783 | ASSERT_MEM_EQ(arr, (buf)->address, sizeof arr) 784 | 785 | #define EXPECT_FUNCTION_CONTAINS_CODE(buf, arr) \ 786 | CHECK_CALL(Testing_expect_function_has_contents(buf, arr, sizeof arr)) 787 | 788 | #define RUN_BUFFER_TEST(test_name) \ 789 | do { \ 790 | Buffer buf; \ 791 | Buffer_init(&buf, 1); \ 792 | GREATEST_RUN_TEST1(test_name, &buf); \ 793 | Buffer_deinit(&buf); \ 794 | } while (0) 795 | 796 | ASTNode *list1(ASTNode *item0) { return AST_new_pair(item0, AST_nil()); } 797 | 798 | ASTNode *list2(ASTNode *item0, ASTNode *item1) { 799 | return AST_new_pair(item0, list1(item1)); 800 | } 801 | 802 | ASTNode *list3(ASTNode *item0, ASTNode *item1, ASTNode *item2) { 803 | return AST_new_pair(item0, list2(item1, item2)); 804 | } 805 | 806 | ASTNode *new_unary_call(const char *name, ASTNode *arg) { 807 | return list2(AST_new_symbol(name), arg); 808 | } 809 | 810 | ASTNode *new_binary_call(const char *name, ASTNode *arg0, ASTNode *arg1) { 811 | return list3(AST_new_symbol(name), arg0, arg1); 812 | } 813 | 814 | // End Testing 815 | 816 | // Tests 817 | 818 | TEST encode_positive_integer(void) { 819 | ASSERT_EQ(Object_encode_integer(0), 0x0); 820 | ASSERT_EQ(Object_encode_integer(1), 0x4); 821 | ASSERT_EQ(Object_encode_integer(10), 0x28); 822 | PASS(); 823 | } 824 | 825 | TEST encode_negative_integer(void) { 826 | ASSERT_EQ(Object_encode_integer(0), 0x0); 827 | ASSERT_EQ(Object_encode_integer(-1), 0xfffffffffffffffc); 828 | ASSERT_EQ(Object_encode_integer(-10), 0xffffffffffffffd8); 829 | PASS(); 830 | } 831 | 832 | TEST encode_char(void) { 833 | ASSERT_EQ(Object_encode_char('\0'), 0xf); 834 | ASSERT_EQ(Object_encode_char('a'), 0x610f); 835 | PASS(); 836 | } 837 | 838 | TEST decode_char(void) { 839 | ASSERT_EQ(Object_decode_char(0xf), '\0'); 840 | ASSERT_EQ(Object_decode_char(0x610f), 'a'); 841 | PASS(); 842 | } 843 | 844 | TEST encode_bool(void) { 845 | ASSERT_EQ(Object_encode_bool(true), 0x9f); 846 | ASSERT_EQ(Object_encode_bool(false), 0x1f); 847 | ASSERT_EQ(Object_true(), 0x9f); 848 | ASSERT_EQ(Object_false(), 0x1f); 849 | PASS(); 850 | } 851 | 852 | TEST decode_bool(void) { 853 | ASSERT_EQ(Object_decode_bool(0x9f), true); 854 | ASSERT_EQ(Object_decode_bool(0x1f), false); 855 | PASS(); 856 | } 857 | 858 | TEST address(void) { 859 | ASSERT_EQ(Object_address((void *)0xFF01), 0xFF00); 860 | PASS(); 861 | } 862 | 863 | TEST ast_new_pair(void) { 864 | ASTNode *node = AST_new_pair(NULL, NULL); 865 | ASSERT(AST_is_pair(node)); 866 | AST_heap_free(node); 867 | PASS(); 868 | } 869 | 870 | TEST ast_pair_car_returns_car(void) { 871 | ASTNode *node = AST_new_pair(AST_new_integer(123), NULL); 872 | ASTNode *car = AST_pair_car(node); 873 | ASSERT(AST_is_integer(car)); 874 | ASSERT_EQ(Object_decode_integer((uword)car), 123); 875 | AST_heap_free(node); 876 | PASS(); 877 | } 878 | 879 | TEST ast_pair_cdr_returns_cdr(void) { 880 | ASTNode *node = AST_new_pair(NULL, AST_new_integer(123)); 881 | ASTNode *cdr = AST_pair_cdr(node); 882 | ASSERT(AST_is_integer(cdr)); 883 | ASSERT_EQ(Object_decode_integer((uword)cdr), 123); 884 | AST_heap_free(node); 885 | PASS(); 886 | } 887 | 888 | TEST ast_new_symbol(void) { 889 | const char *value = "my symbol"; 890 | ASTNode *node = AST_new_symbol(value); 891 | ASSERT(AST_is_symbol(node)); 892 | ASSERT_STR_EQ(AST_symbol_cstr(node), value); 893 | AST_heap_free(node); 894 | PASS(); 895 | } 896 | 897 | TEST buffer_write8_increases_length(Buffer *buf) { 898 | ASSERT_EQ(buf->len, 0); 899 | Buffer_write8(buf, 0xdb); 900 | ASSERT_EQ(Buffer_at8(buf, 0), 0xdb); 901 | ASSERT_EQ(buf->len, 1); 902 | PASS(); 903 | } 904 | 905 | TEST buffer_write8_expands_buffer(void) { 906 | Buffer buf; 907 | Buffer_init(&buf, 1); 908 | ASSERT_EQ(buf.capacity, 1); 909 | ASSERT_EQ(buf.len, 0); 910 | Buffer_write8(&buf, 0xdb); 911 | Buffer_write8(&buf, 0xef); 912 | ASSERT(buf.capacity > 1); 913 | ASSERT_EQ(buf.len, 2); 914 | Buffer_deinit(&buf); 915 | PASS(); 916 | } 917 | 918 | TEST buffer_write32_expands_buffer(void) { 919 | Buffer buf; 920 | Buffer_init(&buf, 1); 921 | ASSERT_EQ(buf.capacity, 1); 922 | ASSERT_EQ(buf.len, 0); 923 | Buffer_write32(&buf, 0xdeadbeef); 924 | ASSERT(buf.capacity > 1); 925 | ASSERT_EQ(buf.len, 4); 926 | Buffer_deinit(&buf); 927 | PASS(); 928 | } 929 | 930 | TEST buffer_write32_writes_little_endian(Buffer *buf) { 931 | Buffer_write32(buf, 0xdeadbeef); 932 | ASSERT_EQ(Buffer_at8(buf, 0), 0xef); 933 | ASSERT_EQ(Buffer_at8(buf, 1), 0xbe); 934 | ASSERT_EQ(Buffer_at8(buf, 2), 0xad); 935 | ASSERT_EQ(Buffer_at8(buf, 3), 0xde); 936 | PASS(); 937 | } 938 | 939 | TEST compile_positive_integer(Buffer *buf) { 940 | word value = 123; 941 | ASTNode *node = AST_new_integer(value); 942 | int compile_result = Compile_function(buf, node); 943 | ASSERT_EQ(compile_result, 0); 944 | // mov eax, imm(123) 945 | byte expected[] = {0x48, 0xc7, 0xc0, 0xec, 0x01, 0x00, 0x00}; 946 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 947 | Buffer_make_executable(buf); 948 | uword result = Testing_execute_expr(buf); 949 | ASSERT_EQ(result, Object_encode_integer(value)); 950 | PASS(); 951 | } 952 | 953 | TEST compile_negative_integer(Buffer *buf) { 954 | word value = -123; 955 | ASTNode *node = AST_new_integer(value); 956 | int compile_result = Compile_function(buf, node); 957 | ASSERT_EQ(compile_result, 0); 958 | // mov eax, imm(-123) 959 | byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0xfe, 0xff, 0xff}; 960 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 961 | Buffer_make_executable(buf); 962 | uword result = Testing_execute_expr(buf); 963 | ASSERT_EQ(result, Object_encode_integer(value)); 964 | PASS(); 965 | } 966 | 967 | TEST compile_char(Buffer *buf) { 968 | char value = 'a'; 969 | ASTNode *node = AST_new_char(value); 970 | int compile_result = Compile_function(buf, node); 971 | ASSERT_EQ(compile_result, 0); 972 | // mov eax, imm('a') 973 | byte expected[] = {0x48, 0xc7, 0xc0, 0x0f, 0x61, 0x00, 0x00}; 974 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 975 | Buffer_make_executable(buf); 976 | uword result = Testing_execute_expr(buf); 977 | ASSERT_EQ(result, Object_encode_char(value)); 978 | PASS(); 979 | } 980 | 981 | TEST compile_true(Buffer *buf) { 982 | ASTNode *node = AST_new_bool(true); 983 | int compile_result = Compile_function(buf, node); 984 | ASSERT_EQ(compile_result, 0); 985 | // mov eax, imm(true) 986 | byte expected[] = {0x48, 0xc7, 0xc0, 0x9f, 0x0, 0x0, 0x0}; 987 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 988 | Buffer_make_executable(buf); 989 | uword result = Testing_execute_expr(buf); 990 | ASSERT_EQ(result, Object_true()); 991 | PASS(); 992 | } 993 | 994 | TEST compile_false(Buffer *buf) { 995 | ASTNode *node = AST_new_bool(false); 996 | int compile_result = Compile_function(buf, node); 997 | ASSERT_EQ(compile_result, 0); 998 | // mov eax, imm(false) 999 | byte expected[] = {0x48, 0xc7, 0xc0, 0x1f, 0x00, 0x00, 0x00}; 1000 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1001 | Buffer_make_executable(buf); 1002 | uword result = Testing_execute_expr(buf); 1003 | ASSERT_EQ(result, Object_false()); 1004 | PASS(); 1005 | } 1006 | 1007 | TEST compile_nil(Buffer *buf) { 1008 | ASTNode *node = AST_nil(); 1009 | int compile_result = Compile_function(buf, node); 1010 | ASSERT_EQ(compile_result, 0); 1011 | // mov eax, imm(nil) 1012 | byte expected[] = {0x48, 0xc7, 0xc0, 0x2f, 0x00, 0x00, 0x00}; 1013 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1014 | Buffer_make_executable(buf); 1015 | uword result = Testing_execute_expr(buf); 1016 | ASSERT_EQ(result, Object_nil()); 1017 | PASS(); 1018 | } 1019 | 1020 | TEST compile_unary_add1(Buffer *buf) { 1021 | ASTNode *node = new_unary_call("add1", AST_new_integer(123)); 1022 | int compile_result = Compile_function(buf, node); 1023 | ASSERT_EQ(compile_result, 0); 1024 | // mov rax, imm(123); add rax, imm(1) 1025 | byte expected[] = {0x48, 0xc7, 0xc0, 0xec, 0x01, 0x00, 0x00, 1026 | 0x48, 0x05, 0x04, 0x00, 0x00, 0x00}; 1027 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1028 | Buffer_make_executable(buf); 1029 | uword result = Testing_execute_expr(buf); 1030 | ASSERT_EQ(result, Object_encode_integer(124)); 1031 | AST_heap_free(node); 1032 | PASS(); 1033 | } 1034 | 1035 | TEST compile_unary_add1_nested(Buffer *buf) { 1036 | ASTNode *node = 1037 | new_unary_call("add1", new_unary_call("add1", AST_new_integer(123))); 1038 | int compile_result = Compile_function(buf, node); 1039 | ASSERT_EQ(compile_result, 0); 1040 | // mov rax, imm(123); add rax, imm(1); add rax, imm(1) 1041 | byte expected[] = {0x48, 0xc7, 0xc0, 0xec, 0x01, 0x00, 0x00, 0x48, 0x05, 0x04, 1042 | 0x00, 0x00, 0x00, 0x48, 0x05, 0x04, 0x00, 0x00, 0x00}; 1043 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1044 | Buffer_make_executable(buf); 1045 | uword result = Testing_execute_expr(buf); 1046 | ASSERT_EQ(result, Object_encode_integer(125)); 1047 | AST_heap_free(node); 1048 | PASS(); 1049 | } 1050 | 1051 | TEST compile_unary_sub1(Buffer *buf) { 1052 | ASTNode *node = new_unary_call("sub1", AST_new_integer(123)); 1053 | int compile_result = Compile_function(buf, node); 1054 | ASSERT_EQ(compile_result, 0); 1055 | // mov rax, imm(123); sub rax, imm(1) 1056 | byte expected[] = {0x48, 0xc7, 0xc0, 0xec, 0x01, 0x00, 0x00, 1057 | 0x48, 0x2d, 0x04, 0x00, 0x00, 0x00}; 1058 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1059 | Buffer_make_executable(buf); 1060 | uword result = Testing_execute_expr(buf); 1061 | ASSERT_EQ(result, Object_encode_integer(122)); 1062 | AST_heap_free(node); 1063 | PASS(); 1064 | } 1065 | 1066 | TEST compile_unary_integer_to_char(Buffer *buf) { 1067 | ASTNode *node = new_unary_call("integer->char", AST_new_integer(97)); 1068 | int compile_result = Compile_function(buf, node); 1069 | ASSERT_EQ(compile_result, 0); 1070 | // mov rax, imm(97); shl rax, 6; or rax, 0xf 1071 | byte expected[] = {0x48, 0xc7, 0xc0, 0x84, 0x01, 0x00, 0x00, 0x48, 1072 | 0xc1, 0xe0, 0x06, 0x48, 0x83, 0xc8, 0x0f}; 1073 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1074 | Buffer_make_executable(buf); 1075 | uword result = Testing_execute_expr(buf); 1076 | ASSERT_EQ(result, Object_encode_char('a')); 1077 | AST_heap_free(node); 1078 | PASS(); 1079 | } 1080 | 1081 | TEST compile_unary_char_to_integer(Buffer *buf) { 1082 | ASTNode *node = new_unary_call("char->integer", AST_new_char('a')); 1083 | int compile_result = Compile_function(buf, node); 1084 | ASSERT_EQ(compile_result, 0); 1085 | // mov rax, imm('a'); shr rax, 6 1086 | byte expected[] = {0x48, 0xc7, 0xc0, 0x0f, 0x61, 0x00, 1087 | 0x00, 0x48, 0xc1, 0xe8, 0x06}; 1088 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1089 | Buffer_make_executable(buf); 1090 | uword result = Testing_execute_expr(buf); 1091 | ASSERT_EQ(result, Object_encode_integer(97)); 1092 | AST_heap_free(node); 1093 | PASS(); 1094 | } 1095 | 1096 | TEST compile_unary_nilp_with_nil_returns_true(Buffer *buf) { 1097 | ASTNode *node = new_unary_call("nil?", AST_nil()); 1098 | int compile_result = Compile_function(buf, node); 1099 | ASSERT_EQ(compile_result, 0); 1100 | // 0: 48 c7 c0 2f 00 00 00 mov rax,0x2f 1101 | // 7: 48 3d 2f 00 00 00 cmp rax,0x0000002f 1102 | // d: 48 c7 c0 00 00 00 00 mov rax,0x0 1103 | // 14: 0f 94 c0 sete al 1104 | // 17: 48 c1 e0 07 shl rax,0x7 1105 | // 1b: 48 83 c8 1f or rax,0x1f 1106 | byte expected[] = {0x48, 0xc7, 0xc0, 0x2f, 0x00, 0x00, 0x00, 0x48, 1107 | 0x3d, 0x2f, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 1108 | 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48, 1109 | 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 1110 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1111 | Buffer_make_executable(buf); 1112 | uword result = Testing_execute_expr(buf); 1113 | ASSERT_EQ(result, Object_true()); 1114 | AST_heap_free(node); 1115 | PASS(); 1116 | } 1117 | 1118 | TEST compile_unary_nilp_with_non_nil_returns_false(Buffer *buf) { 1119 | ASTNode *node = new_unary_call("nil?", AST_new_integer(5)); 1120 | int compile_result = Compile_function(buf, node); 1121 | ASSERT_EQ(compile_result, 0); 1122 | // 0: 48 c7 c0 14 00 00 00 mov rax,0x14 1123 | // 7: 48 3d 2f 00 00 00 cmp rax,0x0000002f 1124 | // d: 48 c7 c0 00 00 00 00 mov rax,0x0 1125 | // 14: 0f 94 c0 sete al 1126 | // 17: 48 c1 e0 07 shl rax,0x7 1127 | // 1b: 48 83 c8 1f or rax,0x1f 1128 | byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48, 1129 | 0x3d, 0x2f, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 1130 | 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48, 1131 | 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 1132 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1133 | Buffer_make_executable(buf); 1134 | uword result = Testing_execute_expr(buf); 1135 | ASSERT_EQ(result, Object_false()); 1136 | AST_heap_free(node); 1137 | PASS(); 1138 | } 1139 | 1140 | TEST compile_unary_zerop_with_zero_returns_true(Buffer *buf) { 1141 | ASTNode *node = new_unary_call("zero?", AST_new_integer(0)); 1142 | int compile_result = Compile_function(buf, node); 1143 | ASSERT_EQ(compile_result, 0); 1144 | // 0: 48 c7 c0 00 00 00 00 mov rax,0x0 1145 | // 7: 48 3d 00 00 00 00 cmp rax,0x00000000 1146 | // d: 48 c7 c0 00 00 00 00 mov rax,0x0 1147 | // 14: 0f 94 c0 sete al 1148 | // 17: 48 c1 e0 07 shl rax,0x7 1149 | // 1b: 48 83 c8 1f or rax,0x1f 1150 | byte expected[] = {0x48, 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x48, 1151 | 0x3d, 0x00, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 1152 | 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48, 1153 | 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 1154 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1155 | Buffer_make_executable(buf); 1156 | uword result = Testing_execute_expr(buf); 1157 | ASSERT_EQ(result, Object_true()); 1158 | AST_heap_free(node); 1159 | PASS(); 1160 | } 1161 | 1162 | TEST compile_unary_zerop_with_non_zero_returns_false(Buffer *buf) { 1163 | ASTNode *node = new_unary_call("zero?", AST_new_integer(5)); 1164 | int compile_result = Compile_function(buf, node); 1165 | ASSERT_EQ(compile_result, 0); 1166 | // 0: 48 c7 c0 14 00 00 00 mov rax,0x14 1167 | // 7: 48 3d 00 00 00 00 cmp rax,0x00000000 1168 | // d: 48 c7 c0 00 00 00 00 mov rax,0x0 1169 | // 14: 0f 94 c0 sete al 1170 | // 17: 48 c1 e0 07 shl rax,0x7 1171 | // 1b: 48 83 c8 1f or rax,0x1f 1172 | byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48, 1173 | 0x3d, 0x00, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 1174 | 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48, 1175 | 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 1176 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1177 | Buffer_make_executable(buf); 1178 | uword result = Testing_execute_expr(buf); 1179 | ASSERT_EQ(result, Object_false()); 1180 | AST_heap_free(node); 1181 | PASS(); 1182 | } 1183 | 1184 | TEST compile_unary_not_with_false_returns_true(Buffer *buf) { 1185 | ASTNode *node = new_unary_call("not", AST_new_bool(false)); 1186 | int compile_result = Compile_function(buf, node); 1187 | ASSERT_EQ(compile_result, 0); 1188 | // 0: 48 c7 c0 1f 00 00 00 mov rax,0x1f 1189 | // 7: 48 3d 1f 00 00 00 cmp rax,0x0000001f 1190 | // d: 48 c7 c0 00 00 00 00 mov rax,0x0 1191 | // 14: 0f 94 c0 sete al 1192 | // 17: 48 c1 e0 07 shl rax,0x7 1193 | // 1b: 48 83 c8 1f or rax,0x1f 1194 | byte expected[] = {0x48, 0xc7, 0xc0, 0x1f, 0x00, 0x00, 0x00, 0x48, 1195 | 0x3d, 0x1f, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 1196 | 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48, 1197 | 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 1198 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1199 | Buffer_make_executable(buf); 1200 | uword result = Testing_execute_expr(buf); 1201 | ASSERT_EQ(result, Object_true()); 1202 | AST_heap_free(node); 1203 | PASS(); 1204 | } 1205 | 1206 | TEST compile_unary_not_with_non_false_returns_false(Buffer *buf) { 1207 | ASTNode *node = new_unary_call("not", AST_new_integer(5)); 1208 | int compile_result = Compile_function(buf, node); 1209 | ASSERT_EQ(compile_result, 0); 1210 | // 0: 48 c7 c0 14 00 00 00 mov rax,0x14 1211 | // 7: 48 3d 1f 00 00 00 cmp rax,0x0000001f 1212 | // d: 48 c7 c0 00 00 00 00 mov rax,0x0 1213 | // 14: 0f 94 c0 sete al 1214 | // 17: 48 c1 e0 07 shl rax,0x7 1215 | // 1b: 48 83 c8 1f or rax,0x1f 1216 | byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48, 1217 | 0x3d, 0x1f, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 1218 | 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48, 1219 | 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 1220 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1221 | Buffer_make_executable(buf); 1222 | uword result = Testing_execute_expr(buf); 1223 | ASSERT_EQ(result, Object_false()); 1224 | AST_heap_free(node); 1225 | PASS(); 1226 | } 1227 | 1228 | TEST compile_unary_integerp_with_integer_returns_true(Buffer *buf) { 1229 | ASTNode *node = new_unary_call("integer?", AST_new_integer(5)); 1230 | int compile_result = Compile_function(buf, node); 1231 | ASSERT_EQ(compile_result, 0); 1232 | // 0: 48 c7 c0 14 00 00 00 mov rax,0x14 1233 | // 7: 48 83 e0 03 and rax,0x3 1234 | // b: 48 3d 00 00 00 00 cmp rax,0x00000000 1235 | // 11: 48 c7 c0 00 00 00 00 mov rax,0x0 1236 | // 18: 0f 94 c0 sete al 1237 | // 1b: 48 c1 e0 07 shl rax,0x7 1238 | // 1f: 48 83 c8 1f or rax,0x1f 1239 | byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48, 0x83, 1240 | 0xe0, 0x03, 0x48, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x48, 1241 | 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 1242 | 0x48, 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 1243 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1244 | Buffer_make_executable(buf); 1245 | uword result = Testing_execute_expr(buf); 1246 | ASSERT_EQ(result, Object_true()); 1247 | AST_heap_free(node); 1248 | PASS(); 1249 | } 1250 | 1251 | TEST compile_unary_integerp_with_non_integer_returns_false(Buffer *buf) { 1252 | ASTNode *node = new_unary_call("integer?", AST_nil()); 1253 | int compile_result = Compile_function(buf, node); 1254 | ASSERT_EQ(compile_result, 0); 1255 | // 0: 48 c7 c0 2f 00 00 00 mov rax,0x2f 1256 | // 7: 48 83 e0 03 and rax,0x3 1257 | // b: 48 3d 00 00 00 00 cmp rax,0x00000000 1258 | // 11: 48 c7 c0 00 00 00 00 mov rax,0x0 1259 | // 18: 0f 94 c0 sete al 1260 | // 1b: 48 c1 e0 07 shl rax,0x7 1261 | // 1f: 48 83 c8 1f or rax,0x1f 1262 | byte expected[] = {0x48, 0xc7, 0xc0, 0x2f, 0x00, 0x00, 0x00, 0x48, 0x83, 1263 | 0xe0, 0x03, 0x48, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x48, 1264 | 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 1265 | 0x48, 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 1266 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1267 | Buffer_make_executable(buf); 1268 | uword result = Testing_execute_expr(buf); 1269 | ASSERT_EQ(result, Object_false()); 1270 | AST_heap_free(node); 1271 | PASS(); 1272 | } 1273 | 1274 | TEST compile_unary_booleanp_with_boolean_returns_true(Buffer *buf) { 1275 | ASTNode *node = new_unary_call("boolean?", AST_new_bool(true)); 1276 | int compile_result = Compile_function(buf, node); 1277 | ASSERT_EQ(compile_result, 0); 1278 | // 0: 48 c7 c0 9f 00 00 00 mov rax,0x9f 1279 | // 7: 48 83 e0 3f and rax,0x3f 1280 | // b: 48 3d 1f 00 00 00 cmp rax,0x0000001f 1281 | // 11: 48 c7 c0 00 00 00 00 mov rax,0x0 1282 | // 18: 0f 94 c0 sete al 1283 | // 1b: 48 c1 e0 07 shl rax,0x7 1284 | // 1f: 48 83 c8 1f or rax,0x1f 1285 | byte expected[] = {0x48, 0xc7, 0xc0, 0x9f, 0x00, 0x00, 0x00, 0x48, 0x83, 1286 | 0xe0, 0x3f, 0x48, 0x3d, 0x1f, 0x00, 0x00, 0x00, 0x48, 1287 | 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 1288 | 0x48, 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 1289 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1290 | Buffer_make_executable(buf); 1291 | uword result = Testing_execute_expr(buf); 1292 | ASSERT_EQ(result, Object_true()); 1293 | AST_heap_free(node); 1294 | PASS(); 1295 | } 1296 | 1297 | TEST compile_unary_booleanp_with_non_boolean_returns_false(Buffer *buf) { 1298 | ASTNode *node = new_unary_call("boolean?", AST_new_integer(5)); 1299 | int compile_result = Compile_function(buf, node); 1300 | ASSERT_EQ(compile_result, 0); 1301 | // 0: 48 c7 c0 14 00 00 00 mov rax,0x14 1302 | // 7: 48 83 e0 3f and rax,0x3f 1303 | // b: 48 3d 1f 00 00 00 cmp rax,0x0000001f 1304 | // 11: 48 c7 c0 00 00 00 00 mov rax,0x0 1305 | // 18: 0f 94 c0 sete al 1306 | // 1b: 48 c1 e0 07 shl rax,0x7 1307 | // 1f: 48 83 c8 1f or rax,0x1f 1308 | byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48, 0x83, 1309 | 0xe0, 0x3f, 0x48, 0x3d, 0x1f, 0x00, 0x00, 0x00, 0x48, 1310 | 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 1311 | 0x48, 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 1312 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1313 | Buffer_make_executable(buf); 1314 | uword result = Testing_execute_expr(buf); 1315 | ASSERT_EQ(result, Object_false()); 1316 | AST_heap_free(node); 1317 | PASS(); 1318 | } 1319 | 1320 | TEST compile_binary_plus(Buffer *buf) { 1321 | ASTNode *node = new_binary_call("+", AST_new_integer(5), AST_new_integer(8)); 1322 | int compile_result = Compile_function(buf, node); 1323 | ASSERT_EQ(compile_result, 0); 1324 | byte expected[] = { 1325 | // 0: 48 c7 c0 20 00 00 00 mov rax,0x20 1326 | 0x48, 0xc7, 0xc0, 0x20, 0x00, 0x00, 0x00, 1327 | // 7: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax 1328 | 0x48, 0x89, 0x45, 0xf8, 1329 | // b: 48 c7 c0 14 00 00 00 mov rax,0x14 1330 | 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 1331 | // 12: 48 03 45 f8 add rax,QWORD PTR [rbp-0x8] 1332 | 0x48, 0x03, 0x45, 0xf8}; 1333 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1334 | Buffer_make_executable(buf); 1335 | uword result = Testing_execute_expr(buf); 1336 | ASSERT_EQ(result, Object_encode_integer(13)); 1337 | AST_heap_free(node); 1338 | PASS(); 1339 | } 1340 | 1341 | TEST compile_binary_plus_nested(Buffer *buf) { 1342 | ASTNode *node = new_binary_call( 1343 | "+", new_binary_call("+", AST_new_integer(1), AST_new_integer(2)), 1344 | new_binary_call("+", AST_new_integer(3), AST_new_integer(4))); 1345 | int compile_result = Compile_function(buf, node); 1346 | ASSERT_EQ(compile_result, 0); 1347 | byte expected[] = { 1348 | // 4: 48 c7 c0 10 00 00 00 mov rax,0x10 1349 | 0x48, 0xc7, 0xc0, 0x10, 0x00, 0x00, 0x00, 1350 | // b: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax 1351 | 0x48, 0x89, 0x45, 0xf8, 1352 | // f: 48 c7 c0 0c 00 00 00 mov rax,0xc 1353 | 0x48, 0xc7, 0xc0, 0x0c, 0x00, 0x00, 0x00, 1354 | // 16: 48 03 45 f8 add rax,QWORD PTR [rbp-0x8] 1355 | 0x48, 0x03, 0x45, 0xf8, 1356 | // 1a: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax 1357 | 0x48, 0x89, 0x45, 0xf8, 1358 | // 1e: 48 c7 c0 08 00 00 00 mov rax,0x8 1359 | 0x48, 0xc7, 0xc0, 0x08, 0x00, 0x00, 0x00, 1360 | // 25: 48 89 45 f0 mov QWORD PTR [rbp-0x10],rax 1361 | 0x48, 0x89, 0x45, 0xf0, 1362 | // 29: 48 c7 c0 04 00 00 00 mov rax,0x4 1363 | 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00, 1364 | // 30: 48 03 45 f0 add rax,QWORD PTR [rbp-0x10] 1365 | 0x48, 0x03, 0x45, 0xf0, 1366 | // 34: 48 03 45 f8 add rax,QWORD PTR [rbp-0x8] 1367 | 0x48, 0x03, 0x45, 0xf8}; 1368 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1369 | Buffer_make_executable(buf); 1370 | uword result = Testing_execute_expr(buf); 1371 | ASSERT_EQ(result, Object_encode_integer(10)); 1372 | AST_heap_free(node); 1373 | PASS(); 1374 | } 1375 | 1376 | TEST compile_binary_minus(Buffer *buf) { 1377 | ASTNode *node = new_binary_call("-", AST_new_integer(5), AST_new_integer(8)); 1378 | int compile_result = Compile_function(buf, node); 1379 | ASSERT_EQ(compile_result, 0); 1380 | byte expected[] = { 1381 | // 0: 48 c7 c0 20 00 00 00 mov rax,0x20 1382 | 0x48, 0xc7, 0xc0, 0x20, 0x00, 0x00, 0x00, 1383 | // 7: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax 1384 | 0x48, 0x89, 0x45, 0xf8, 1385 | // b: 48 c7 c0 14 00 00 00 mov rax,0x14 1386 | 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 1387 | // 12: 48 2b 45 f8 add rax,QWORD PTR [rbp-0x8] 1388 | 0x48, 0x2b, 0x45, 0xf8}; 1389 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1390 | Buffer_make_executable(buf); 1391 | uword result = Testing_execute_expr(buf); 1392 | ASSERT_EQ(result, Object_encode_integer(-3)); 1393 | AST_heap_free(node); 1394 | PASS(); 1395 | } 1396 | 1397 | TEST compile_binary_minus_nested(Buffer *buf) { 1398 | ASTNode *node = new_binary_call( 1399 | "-", new_binary_call("-", AST_new_integer(5), AST_new_integer(1)), 1400 | new_binary_call("-", AST_new_integer(4), AST_new_integer(3))); 1401 | int compile_result = Compile_function(buf, node); 1402 | ASSERT_EQ(compile_result, 0); 1403 | byte expected[] = { 1404 | // 4: 48 c7 c0 0c 00 00 00 mov rax,0xc 1405 | 0x48, 0xc7, 0xc0, 0x0c, 0x00, 0x00, 0x00, 1406 | // b: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax 1407 | 0x48, 0x89, 0x45, 0xf8, 1408 | // f: 48 c7 c0 10 00 00 00 mov rax,0x10 1409 | 0x48, 0xc7, 0xc0, 0x10, 0x00, 0x00, 0x00, 1410 | // 16: 48 2b 45 f8 add rax,QWORD PTR [rbp-0x8] 1411 | 0x48, 0x2b, 0x45, 0xf8, 1412 | // 1a: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax 1413 | 0x48, 0x89, 0x45, 0xf8, 1414 | // 1e: 48 c7 c0 04 00 00 00 mov rax,0x4 1415 | 0x48, 0xc7, 0xc0, 0x04, 0x00, 0x00, 0x00, 1416 | // 25: 48 89 45 f0 mov QWORD PTR [rbp-0x10],rax 1417 | 0x48, 0x89, 0x45, 0xf0, 1418 | // 29: 48 c7 c0 14 00 00 00 mov rax,0x14 1419 | 0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 1420 | // 30: 48 2b 45 f0 add rax,QWORD PTR [rbp-0x10] 1421 | 0x48, 0x2b, 0x45, 0xf0, 1422 | // 34: 48 2b 45 f8 add rax,QWORD PTR [rbp-0x8] 1423 | 0x48, 0x2b, 0x45, 0xf8}; 1424 | EXPECT_FUNCTION_CONTAINS_CODE(buf, expected); 1425 | Buffer_make_executable(buf); 1426 | uword result = Testing_execute_expr(buf); 1427 | ASSERT_EQ(result, Object_encode_integer(3)); 1428 | AST_heap_free(node); 1429 | PASS(); 1430 | } 1431 | 1432 | TEST compile_binary_mul(Buffer *buf) { 1433 | ASTNode *node = new_binary_call("*", AST_new_integer(5), AST_new_integer(8)); 1434 | int compile_result = Compile_function(buf, node); 1435 | ASSERT_EQ(compile_result, 0); 1436 | Buffer_make_executable(buf); 1437 | uword result = Testing_execute_expr(buf); 1438 | ASSERT_EQ_FMT(Object_encode_integer(40), result, "0x%lx"); 1439 | AST_heap_free(node); 1440 | PASS(); 1441 | } 1442 | 1443 | TEST compile_binary_mul_nested(Buffer *buf) { 1444 | ASTNode *node = new_binary_call( 1445 | "*", new_binary_call("*", AST_new_integer(1), AST_new_integer(2)), 1446 | new_binary_call("*", AST_new_integer(3), AST_new_integer(4))); 1447 | int compile_result = Compile_function(buf, node); 1448 | ASSERT_EQ(compile_result, 0); 1449 | Buffer_make_executable(buf); 1450 | uword result = Testing_execute_expr(buf); 1451 | ASSERT_EQ_FMT(Object_encode_integer(24), result, "0x%lx"); 1452 | AST_heap_free(node); 1453 | PASS(); 1454 | } 1455 | 1456 | TEST compile_binary_eq_with_same_address_returns_true(Buffer *buf) { 1457 | ASTNode *node = new_binary_call("=", AST_new_integer(5), AST_new_integer(5)); 1458 | int compile_result = Compile_function(buf, node); 1459 | ASSERT_EQ(compile_result, 0); 1460 | Buffer_make_executable(buf); 1461 | uword result = Testing_execute_expr(buf); 1462 | ASSERT_EQ_FMT(Object_true(), result, "0x%lx"); 1463 | AST_heap_free(node); 1464 | PASS(); 1465 | } 1466 | 1467 | TEST compile_binary_eq_with_different_address_returns_false(Buffer *buf) { 1468 | ASTNode *node = new_binary_call("=", AST_new_integer(5), AST_new_integer(4)); 1469 | int compile_result = Compile_function(buf, node); 1470 | ASSERT_EQ(compile_result, 0); 1471 | Buffer_make_executable(buf); 1472 | uword result = Testing_execute_expr(buf); 1473 | ASSERT_EQ_FMT(Object_false(), result, "0x%lx"); 1474 | AST_heap_free(node); 1475 | PASS(); 1476 | } 1477 | 1478 | TEST compile_binary_lt_with_left_less_than_right_returns_true(Buffer *buf) { 1479 | ASTNode *node = new_binary_call("<", AST_new_integer(-5), AST_new_integer(5)); 1480 | int compile_result = Compile_function(buf, node); 1481 | ASSERT_EQ(compile_result, 0); 1482 | Buffer_make_executable(buf); 1483 | uword result = Testing_execute_expr(buf); 1484 | ASSERT_EQ_FMT(Object_true(), result, "0x%lx"); 1485 | AST_heap_free(node); 1486 | PASS(); 1487 | } 1488 | 1489 | TEST compile_binary_lt_with_left_equal_to_right_returns_false(Buffer *buf) { 1490 | ASTNode *node = new_binary_call("<", AST_new_integer(5), AST_new_integer(5)); 1491 | int compile_result = Compile_function(buf, node); 1492 | ASSERT_EQ(compile_result, 0); 1493 | Buffer_make_executable(buf); 1494 | uword result = Testing_execute_expr(buf); 1495 | ASSERT_EQ_FMT(Object_false(), result, "0x%lx"); 1496 | AST_heap_free(node); 1497 | PASS(); 1498 | } 1499 | 1500 | TEST compile_binary_lt_with_left_greater_than_right_returns_false(Buffer *buf) { 1501 | ASTNode *node = new_binary_call("<", AST_new_integer(6), AST_new_integer(5)); 1502 | int compile_result = Compile_function(buf, node); 1503 | ASSERT_EQ(compile_result, 0); 1504 | Buffer_make_executable(buf); 1505 | uword result = Testing_execute_expr(buf); 1506 | ASSERT_EQ_FMT(Object_false(), result, "0x%lx"); 1507 | AST_heap_free(node); 1508 | PASS(); 1509 | } 1510 | 1511 | SUITE(object_tests) { 1512 | RUN_TEST(encode_positive_integer); 1513 | RUN_TEST(encode_negative_integer); 1514 | RUN_TEST(encode_char); 1515 | RUN_TEST(decode_char); 1516 | RUN_TEST(encode_bool); 1517 | RUN_TEST(decode_bool); 1518 | RUN_TEST(address); 1519 | } 1520 | 1521 | SUITE(ast_tests) { 1522 | RUN_TEST(ast_new_pair); 1523 | RUN_TEST(ast_pair_car_returns_car); 1524 | RUN_TEST(ast_pair_cdr_returns_cdr); 1525 | RUN_TEST(ast_new_symbol); 1526 | } 1527 | 1528 | SUITE(buffer_tests) { 1529 | RUN_BUFFER_TEST(buffer_write8_increases_length); 1530 | RUN_TEST(buffer_write8_expands_buffer); 1531 | RUN_TEST(buffer_write32_expands_buffer); 1532 | RUN_BUFFER_TEST(buffer_write32_writes_little_endian); 1533 | } 1534 | 1535 | SUITE(compiler_tests) { 1536 | RUN_BUFFER_TEST(compile_positive_integer); 1537 | RUN_BUFFER_TEST(compile_negative_integer); 1538 | RUN_BUFFER_TEST(compile_char); 1539 | RUN_BUFFER_TEST(compile_true); 1540 | RUN_BUFFER_TEST(compile_false); 1541 | RUN_BUFFER_TEST(compile_nil); 1542 | RUN_BUFFER_TEST(compile_unary_add1); 1543 | RUN_BUFFER_TEST(compile_unary_add1_nested); 1544 | RUN_BUFFER_TEST(compile_unary_sub1); 1545 | RUN_BUFFER_TEST(compile_unary_integer_to_char); 1546 | RUN_BUFFER_TEST(compile_unary_char_to_integer); 1547 | RUN_BUFFER_TEST(compile_unary_nilp_with_nil_returns_true); 1548 | RUN_BUFFER_TEST(compile_unary_nilp_with_non_nil_returns_false); 1549 | RUN_BUFFER_TEST(compile_unary_zerop_with_zero_returns_true); 1550 | RUN_BUFFER_TEST(compile_unary_zerop_with_non_zero_returns_false); 1551 | RUN_BUFFER_TEST(compile_unary_not_with_false_returns_true); 1552 | RUN_BUFFER_TEST(compile_unary_not_with_non_false_returns_false); 1553 | RUN_BUFFER_TEST(compile_unary_integerp_with_integer_returns_true); 1554 | RUN_BUFFER_TEST(compile_unary_integerp_with_non_integer_returns_false); 1555 | RUN_BUFFER_TEST(compile_unary_booleanp_with_boolean_returns_true); 1556 | RUN_BUFFER_TEST(compile_unary_booleanp_with_non_boolean_returns_false); 1557 | RUN_BUFFER_TEST(compile_binary_plus); 1558 | RUN_BUFFER_TEST(compile_binary_plus_nested); 1559 | RUN_BUFFER_TEST(compile_binary_minus); 1560 | RUN_BUFFER_TEST(compile_binary_minus_nested); 1561 | RUN_BUFFER_TEST(compile_binary_mul); 1562 | RUN_BUFFER_TEST(compile_binary_mul_nested); 1563 | RUN_BUFFER_TEST(compile_binary_eq_with_same_address_returns_true); 1564 | RUN_BUFFER_TEST(compile_binary_eq_with_different_address_returns_false); 1565 | RUN_BUFFER_TEST(compile_binary_lt_with_left_less_than_right_returns_true); 1566 | RUN_BUFFER_TEST(compile_binary_lt_with_left_equal_to_right_returns_false); 1567 | RUN_BUFFER_TEST(compile_binary_lt_with_left_greater_than_right_returns_false); 1568 | } 1569 | 1570 | // End Tests 1571 | 1572 | GREATEST_MAIN_DEFS(); 1573 | 1574 | int main(int argc, char **argv) { 1575 | GREATEST_MAIN_BEGIN(); 1576 | RUN_SUITE(object_tests); 1577 | RUN_SUITE(ast_tests); 1578 | RUN_SUITE(buffer_tests); 1579 | RUN_SUITE(compiler_tests); 1580 | GREATEST_MAIN_END(); 1581 | } 1582 | -------------------------------------------------------------------------------- /compiling-immediates.c: -------------------------------------------------------------------------------- 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-immediates.c 4 | 5 | #define _GNU_SOURCE 6 | #include // for assert 7 | #include // for bool 8 | #include // for NULL 9 | #include // for int32_t, etc 10 | #include // for memcpy 11 | #include // for mmap 12 | #undef _GNU_SOURCE 13 | 14 | #include "greatest.h" 15 | 16 | // Objects 17 | 18 | typedef int64_t word; 19 | typedef uint64_t uword; 20 | 21 | // These constants are defined in a enum because the right hand side of a 22 | // statement like 23 | // static const int kFoo = ...; 24 | // must be a so-called "Integer Constant Expression". Compilers are required to 25 | // support a certain set of these expressions, but are not required to support 26 | // arbitrary arithmetic with other integer constants. Compilers such as gcc 27 | // before gcc-8 just decided not to play this game, while gcc-8+ and Clang play 28 | // just fine. 29 | // Since this arithmetic with constant values works just fine for enums, make 30 | // all these constants enum values instead. 31 | // See https://twitter.com/tekknolagi/status/1328449329472835586 for more info. 32 | enum { 33 | kBitsPerByte = 8, // bits 34 | kWordSize = sizeof(word), // bytes 35 | kBitsPerWord = kWordSize * kBitsPerByte, // bits 36 | 37 | kIntegerTag = 0x0, // 0b00 38 | kIntegerTagMask = 0x3, // 0b11 39 | kIntegerShift = 2, 40 | kIntegerBits = kBitsPerWord - kIntegerShift, 41 | 42 | kImmediateTagMask = 0x3f, 43 | 44 | kCharTag = 0x0f, // 0b00001111 45 | kCharMask = 0xff, // 0b11111111 46 | kCharShift = 8, 47 | 48 | kBoolTag = 0x1f, // 0b0011111 49 | kBoolMask = 0x80, // 0b10000000 50 | kBoolShift = 7, 51 | }; 52 | 53 | // These are defined as macros because they will not work as static const int 54 | // constants (per above explanation), and enum constants are only required to 55 | // be an int wide (per ISO C). 56 | #define INTEGER_MAX ((1LL << (kIntegerBits - 1)) - 1) 57 | #define INTEGER_MIN (-(1LL << (kIntegerBits - 1))) 58 | 59 | word Object_encode_integer(word value) { 60 | assert(value < INTEGER_MAX && "too big"); 61 | assert(value > INTEGER_MIN && "too small"); 62 | return value << kIntegerShift; 63 | } 64 | 65 | word Object_decode_integer(word value) { return value >> kIntegerShift; } 66 | 67 | word Object_encode_char(char value) { 68 | return ((word)value << kCharShift) | kCharTag; 69 | } 70 | 71 | char Object_decode_char(word value) { 72 | return (value >> kCharShift) & kCharMask; 73 | } 74 | 75 | word Object_encode_bool(bool value) { 76 | return ((word)value << kBoolShift) | kBoolTag; 77 | } 78 | 79 | bool Object_decode_bool(word value) { return value & kBoolMask; } 80 | 81 | word Object_true() { return Object_encode_bool(true); } 82 | 83 | word Object_false() { return Object_encode_bool(false); } 84 | 85 | word Object_nil() { return 0x2f; } 86 | 87 | // End Objects 88 | 89 | // Buffer 90 | 91 | typedef unsigned char byte; 92 | 93 | typedef enum { 94 | kWritable, 95 | kExecutable, 96 | } BufferState; 97 | 98 | typedef struct { 99 | byte *address; 100 | BufferState state; 101 | size_t len; 102 | size_t capacity; 103 | } Buffer; 104 | 105 | byte *Buffer_alloc_writable(size_t capacity) { 106 | byte *result = mmap(/*addr=*/NULL, capacity, PROT_READ | PROT_WRITE, 107 | MAP_ANONYMOUS | MAP_PRIVATE, 108 | /*filedes=*/-1, /*off=*/0); 109 | assert(result != MAP_FAILED); 110 | return result; 111 | } 112 | 113 | void Buffer_init(Buffer *result, size_t capacity) { 114 | result->address = Buffer_alloc_writable(capacity); 115 | assert(result->address != MAP_FAILED); 116 | result->state = kWritable; 117 | result->len = 0; 118 | result->capacity = capacity; 119 | } 120 | 121 | void Buffer_deinit(Buffer *buf) { 122 | munmap(buf->address, buf->capacity); 123 | buf->address = NULL; 124 | buf->len = 0; 125 | buf->capacity = 0; 126 | } 127 | 128 | int Buffer_make_executable(Buffer *buf) { 129 | int result = mprotect(buf->address, buf->len, PROT_EXEC); 130 | buf->state = kExecutable; 131 | return result; 132 | } 133 | 134 | byte Buffer_at8(Buffer *buf, size_t pos) { return buf->address[pos]; } 135 | 136 | void Buffer_at_put8(Buffer *buf, size_t pos, byte b) { buf->address[pos] = b; } 137 | 138 | word max(word left, word right) { return left > right ? left : right; } 139 | 140 | void Buffer_ensure_capacity(Buffer *buf, word additional_capacity) { 141 | if (buf->len + additional_capacity <= buf->capacity) { 142 | return; 143 | } 144 | word new_capacity = 145 | max(buf->capacity * 2, buf->capacity + additional_capacity); 146 | byte *address = Buffer_alloc_writable(new_capacity); 147 | memcpy(address, buf->address, buf->len); 148 | int result = munmap(buf->address, buf->capacity); 149 | assert(result == 0 && "munmap failed"); 150 | buf->address = address; 151 | buf->capacity = new_capacity; 152 | } 153 | 154 | void Buffer_write8(Buffer *buf, byte b) { 155 | Buffer_ensure_capacity(buf, sizeof b); 156 | Buffer_at_put8(buf, buf->len++, b); 157 | } 158 | 159 | void Buffer_write32(Buffer *buf, int32_t value) { 160 | for (size_t i = 0; i < 4; i++) { 161 | Buffer_write8(buf, (value >> (i * kBitsPerByte)) & 0xff); 162 | } 163 | } 164 | 165 | // End Buffer 166 | 167 | // Emit 168 | 169 | typedef enum { 170 | kRax = 0, 171 | kRcx, 172 | kRdx, 173 | kRbx, 174 | kRsp, 175 | kRbp, 176 | kRsi, 177 | kRdi, 178 | } Register; 179 | 180 | enum { 181 | kRexPrefix = 0x48, 182 | }; 183 | 184 | void Emit_mov_reg_imm32(Buffer *buf, Register dst, int32_t src) { 185 | Buffer_write8(buf, kRexPrefix); 186 | Buffer_write8(buf, 0xc7); 187 | Buffer_write8(buf, 0xc0 + dst); 188 | Buffer_write32(buf, src); 189 | } 190 | 191 | void Emit_ret(Buffer *buf) { Buffer_write8(buf, 0xc3); } 192 | 193 | // End Emit 194 | 195 | // AST 196 | 197 | typedef struct ASTNode ASTNode; 198 | 199 | bool AST_is_integer(ASTNode *node) { 200 | return ((word)node & kIntegerTagMask) == kIntegerTag; 201 | } 202 | 203 | word AST_get_integer(ASTNode *node) { 204 | return Object_decode_integer((word)node); 205 | } 206 | 207 | ASTNode *AST_new_integer(word value) { 208 | return (ASTNode *)Object_encode_integer(value); 209 | } 210 | 211 | bool AST_is_char(ASTNode *node) { 212 | return ((word)node & kImmediateTagMask) == kCharTag; 213 | } 214 | 215 | char AST_get_char(ASTNode *node) { return Object_decode_char((word)node); } 216 | 217 | ASTNode *AST_new_char(char value) { 218 | return (ASTNode *)Object_encode_char(value); 219 | } 220 | 221 | bool AST_is_bool(ASTNode *node) { 222 | return ((word)node & kImmediateTagMask) == kBoolTag; 223 | } 224 | 225 | bool AST_get_bool(ASTNode *node) { return Object_decode_bool((word)node); } 226 | 227 | ASTNode *AST_new_bool(bool value) { 228 | return (ASTNode *)Object_encode_bool(value); 229 | } 230 | 231 | bool AST_is_nil(ASTNode *node) { return (word)node == Object_nil(); } 232 | 233 | ASTNode *AST_nil() { return (ASTNode *)Object_nil(); } 234 | 235 | // End AST 236 | 237 | // Compile 238 | 239 | int Compile_expr(Buffer *buf, ASTNode *node) { 240 | if (AST_is_integer(node)) { 241 | word value = AST_get_integer(node); 242 | Emit_mov_reg_imm32(buf, kRax, Object_encode_integer(value)); 243 | return 0; 244 | } 245 | if (AST_is_char(node)) { 246 | char value = AST_get_char(node); 247 | Emit_mov_reg_imm32(buf, kRax, Object_encode_char(value)); 248 | return 0; 249 | } 250 | if (AST_is_bool(node)) { 251 | bool value = AST_get_bool(node); 252 | Emit_mov_reg_imm32(buf, kRax, Object_encode_bool(value)); 253 | return 0; 254 | } 255 | if (AST_is_nil(node)) { 256 | Emit_mov_reg_imm32(buf, kRax, Object_nil()); 257 | return 0; 258 | } 259 | assert(0 && "unexpected node type"); 260 | } 261 | 262 | int Compile_function(Buffer *buf, ASTNode *node) { 263 | int result = Compile_expr(buf, node); 264 | if (result != 0) 265 | return result; 266 | Emit_ret(buf); 267 | return 0; 268 | } 269 | 270 | // End Compile 271 | 272 | typedef int (*JitFunction)(); 273 | 274 | // Testing 275 | 276 | word Testing_execute_expr(Buffer *buf) { 277 | assert(buf != NULL); 278 | assert(buf->address != NULL); 279 | assert(buf->state == kExecutable); 280 | // The pointer-pointer cast is allowed but the underlying 281 | // data-to-function-pointer back-and-forth is only guaranteed to work on 282 | // POSIX systems (because of eg dlsym). 283 | JitFunction function = *(JitFunction *)(&buf->address); 284 | return function(); 285 | } 286 | 287 | void Testing_print_hex_array(FILE *fp, byte *arr, size_t arr_size) { 288 | for (size_t i = 0; i < arr_size; i++) { 289 | fprintf(fp, "%.2x ", arr[i]); 290 | } 291 | } 292 | 293 | #define EXPECT_EQUALS_BYTES(buf, arr) \ 294 | ASSERT_MEM_EQ(arr, (buf)->address, sizeof arr) 295 | 296 | #define RUN_BUFFER_TEST(test_name) \ 297 | do { \ 298 | Buffer buf; \ 299 | Buffer_init(&buf, 1); \ 300 | GREATEST_RUN_TEST1(test_name, &buf); \ 301 | Buffer_deinit(&buf); \ 302 | } while (0) 303 | 304 | // End Testing 305 | 306 | // Tests 307 | 308 | TEST encode_positive_integer(void) { 309 | ASSERT_EQ(Object_encode_integer(0), 0x0); 310 | ASSERT_EQ(Object_encode_integer(1), 0x4); 311 | ASSERT_EQ(Object_encode_integer(10), 0x28); 312 | PASS(); 313 | } 314 | 315 | TEST encode_negative_integer(void) { 316 | ASSERT_EQ(Object_encode_integer(0), 0x0); 317 | ASSERT_EQ(Object_encode_integer(-1), (word)0xfffffffffffffffc); 318 | ASSERT_EQ(Object_encode_integer(-10), (word)0xffffffffffffffd8); 319 | PASS(); 320 | } 321 | 322 | TEST encode_char(void) { 323 | ASSERT_EQ(Object_encode_char('\0'), 0xf); 324 | ASSERT_EQ(Object_encode_char('a'), 0x610f); 325 | PASS(); 326 | } 327 | 328 | TEST decode_char(void) { 329 | ASSERT_EQ(Object_decode_char(0xf), '\0'); 330 | ASSERT_EQ(Object_decode_char(0x610f), 'a'); 331 | PASS(); 332 | } 333 | 334 | TEST encode_bool(void) { 335 | ASSERT_EQ(Object_encode_bool(true), 0x9f); 336 | ASSERT_EQ(Object_encode_bool(false), 0x1f); 337 | ASSERT_EQ(Object_true(), 0x9f); 338 | ASSERT_EQ(Object_false(), 0x1f); 339 | PASS(); 340 | } 341 | 342 | TEST decode_bool(void) { 343 | ASSERT_EQ(Object_decode_bool(0x9f), true); 344 | ASSERT_EQ(Object_decode_bool(0x1f), false); 345 | PASS(); 346 | } 347 | 348 | TEST buffer_write8_increases_length(Buffer *buf) { 349 | ASSERT_EQ(buf->len, 0); 350 | Buffer_write8(buf, 0xdb); 351 | ASSERT_EQ(Buffer_at8(buf, 0), 0xdb); 352 | ASSERT_EQ(buf->len, 1); 353 | PASS(); 354 | } 355 | 356 | TEST buffer_write8_expands_buffer(void) { 357 | Buffer buf; 358 | Buffer_init(&buf, 1); 359 | ASSERT_EQ(buf.capacity, 1); 360 | ASSERT_EQ(buf.len, 0); 361 | Buffer_write8(&buf, 0xdb); 362 | Buffer_write8(&buf, 0xef); 363 | ASSERT(buf.capacity > 1); 364 | ASSERT_EQ(buf.len, 2); 365 | Buffer_deinit(&buf); 366 | PASS(); 367 | } 368 | 369 | TEST buffer_write32_expands_buffer(void) { 370 | Buffer buf; 371 | Buffer_init(&buf, 1); 372 | ASSERT_EQ(buf.capacity, 1); 373 | ASSERT_EQ(buf.len, 0); 374 | Buffer_write32(&buf, 0xdeadbeef); 375 | ASSERT(buf.capacity > 1); 376 | ASSERT_EQ(buf.len, 4); 377 | Buffer_deinit(&buf); 378 | PASS(); 379 | } 380 | 381 | TEST buffer_write32_writes_little_endian(Buffer *buf) { 382 | Buffer_write32(buf, 0xdeadbeef); 383 | ASSERT_EQ(Buffer_at8(buf, 0), 0xef); 384 | ASSERT_EQ(Buffer_at8(buf, 1), 0xbe); 385 | ASSERT_EQ(Buffer_at8(buf, 2), 0xad); 386 | ASSERT_EQ(Buffer_at8(buf, 3), 0xde); 387 | PASS(); 388 | } 389 | 390 | TEST compile_positive_integer(Buffer *buf) { 391 | word value = 123; 392 | ASTNode *node = AST_new_integer(value); 393 | int compile_result = Compile_function(buf, node); 394 | ASSERT_EQ(compile_result, 0); 395 | // mov eax, imm(123); ret 396 | byte expected[] = {0x48, 0xc7, 0xc0, 0xec, 0x01, 0x00, 0x00, 0xc3}; 397 | EXPECT_EQUALS_BYTES(buf, expected); 398 | Buffer_make_executable(buf); 399 | word result = Testing_execute_expr(buf); 400 | ASSERT_EQ(result, Object_encode_integer(value)); 401 | PASS(); 402 | } 403 | 404 | TEST compile_negative_integer(Buffer *buf) { 405 | word value = -123; 406 | ASTNode *node = AST_new_integer(value); 407 | int compile_result = Compile_function(buf, node); 408 | ASSERT_EQ(compile_result, 0); 409 | // mov eax, imm(-123); ret 410 | byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0xfe, 0xff, 0xff, 0xc3}; 411 | EXPECT_EQUALS_BYTES(buf, expected); 412 | Buffer_make_executable(buf); 413 | word result = Testing_execute_expr(buf); 414 | ASSERT_EQ(result, Object_encode_integer(value)); 415 | PASS(); 416 | } 417 | 418 | TEST compile_char(Buffer *buf) { 419 | char value = 'a'; 420 | ASTNode *node = AST_new_char(value); 421 | int compile_result = Compile_function(buf, node); 422 | ASSERT_EQ(compile_result, 0); 423 | // mov eax, imm('a'); ret 424 | byte expected[] = {0x48, 0xc7, 0xc0, 0x0f, 0x61, 0x00, 0x00, 0xc3}; 425 | EXPECT_EQUALS_BYTES(buf, expected); 426 | Buffer_make_executable(buf); 427 | word result = Testing_execute_expr(buf); 428 | ASSERT_EQ(result, Object_encode_char(value)); 429 | PASS(); 430 | } 431 | 432 | TEST compile_true(Buffer *buf) { 433 | ASTNode *node = AST_new_bool(true); 434 | int compile_result = Compile_function(buf, node); 435 | ASSERT_EQ(compile_result, 0); 436 | // mov eax, imm(true); ret 437 | byte expected[] = {0x48, 0xc7, 0xc0, 0x9f, 0x0, 0x0, 0x0, 0xc3}; 438 | EXPECT_EQUALS_BYTES(buf, expected); 439 | Buffer_make_executable(buf); 440 | word result = Testing_execute_expr(buf); 441 | ASSERT_EQ(result, Object_true()); 442 | PASS(); 443 | } 444 | 445 | TEST compile_false(Buffer *buf) { 446 | ASTNode *node = AST_new_bool(false); 447 | int compile_result = Compile_function(buf, node); 448 | ASSERT_EQ(compile_result, 0); 449 | // mov eax, imm(false); ret 450 | byte expected[] = {0x48, 0xc7, 0xc0, 0x1f, 0x00, 0x00, 0x00, 0xc3}; 451 | EXPECT_EQUALS_BYTES(buf, expected); 452 | Buffer_make_executable(buf); 453 | word result = Testing_execute_expr(buf); 454 | ASSERT_EQ(result, Object_false()); 455 | PASS(); 456 | } 457 | 458 | TEST compile_nil(Buffer *buf) { 459 | ASTNode *node = AST_nil(); 460 | int compile_result = Compile_function(buf, node); 461 | ASSERT_EQ(compile_result, 0); 462 | // mov eax, imm(nil); ret 463 | byte expected[] = {0x48, 0xc7, 0xc0, 0x2f, 0x00, 0x00, 0x00, 0xc3}; 464 | EXPECT_EQUALS_BYTES(buf, expected); 465 | Buffer_make_executable(buf); 466 | word result = Testing_execute_expr(buf); 467 | ASSERT_EQ(result, Object_nil()); 468 | PASS(); 469 | } 470 | 471 | SUITE(object_tests) { 472 | RUN_TEST(encode_positive_integer); 473 | RUN_TEST(encode_negative_integer); 474 | RUN_TEST(encode_char); 475 | RUN_TEST(decode_char); 476 | RUN_TEST(encode_bool); 477 | RUN_TEST(decode_bool); 478 | } 479 | 480 | SUITE(buffer_tests) { 481 | RUN_BUFFER_TEST(buffer_write8_increases_length); 482 | RUN_TEST(buffer_write8_expands_buffer); 483 | RUN_TEST(buffer_write32_expands_buffer); 484 | RUN_BUFFER_TEST(buffer_write32_writes_little_endian); 485 | } 486 | 487 | SUITE(compiler_tests) { 488 | RUN_BUFFER_TEST(compile_positive_integer); 489 | RUN_BUFFER_TEST(compile_negative_integer); 490 | RUN_BUFFER_TEST(compile_char); 491 | RUN_BUFFER_TEST(compile_true); 492 | RUN_BUFFER_TEST(compile_false); 493 | RUN_BUFFER_TEST(compile_nil); 494 | } 495 | 496 | // End Tests 497 | 498 | GREATEST_MAIN_DEFS(); 499 | 500 | int main(int argc, char **argv) { 501 | GREATEST_MAIN_BEGIN(); 502 | RUN_SUITE(object_tests); 503 | RUN_SUITE(buffer_tests); 504 | RUN_SUITE(compiler_tests); 505 | GREATEST_MAIN_END(); 506 | } 507 | -------------------------------------------------------------------------------- /compiling-integers.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include // for assert 3 | #include // for bool 4 | #include // for NULL 5 | #include // for int32_t, etc 6 | #include // for memcpy 7 | #include // for mmap 8 | #undef _GNU_SOURCE 9 | 10 | #include "greatest.h" 11 | 12 | // Objects 13 | 14 | typedef int64_t word; 15 | typedef uint64_t uword; 16 | 17 | // These constants are defined in a enum because the right hand side of a 18 | // statement like 19 | // static const int kFoo = ...; 20 | // must be a so-called "Integer Constant Expression". Compilers are required to 21 | // support a certain set of these expressions, but are not required to support 22 | // arbitrary arithmetic with other integer constants. Compilers such as gcc 23 | // before gcc-8 just decided not to play this game, while gcc-8+ and Clang play 24 | // just fine. 25 | // Since this arithmetic with constant values works just fine for enums, make 26 | // all these constants enum values instead. 27 | // See https://twitter.com/tekknolagi/status/1328449329472835586 for more info. 28 | enum { 29 | kBitsPerByte = 8, // bits 30 | kWordSize = sizeof(word), // bytes 31 | kBitsPerWord = kWordSize * kBitsPerByte, // bits 32 | 33 | kIntegerTag = 0x0, // 0b00 34 | kIntegerTagMask = 0x3, // 0b11 35 | kIntegerShift = 2, 36 | kIntegerBits = kBitsPerWord - kIntegerShift, 37 | }; 38 | 39 | // These are defined as macros because they will not work as static const int 40 | // constants (per above explanation), and enum constants are only required to 41 | // be an int wide (per ISO C). 42 | #define INTEGER_MAX ((1LL << (kIntegerBits - 1)) - 1) 43 | #define INTEGER_MIN (-(1LL << (kIntegerBits - 1))) 44 | 45 | word Object_encode_integer(word value) { 46 | assert(value < INTEGER_MAX && "too big"); 47 | assert(value > INTEGER_MIN && "too small"); 48 | return value << kIntegerShift; 49 | } 50 | 51 | // End Objects 52 | 53 | // Buffer 54 | 55 | typedef unsigned char byte; 56 | 57 | typedef enum { 58 | kWritable, 59 | kExecutable, 60 | } BufferState; 61 | 62 | typedef struct { 63 | byte *address; 64 | BufferState state; 65 | size_t len; 66 | size_t capacity; 67 | } Buffer; 68 | 69 | byte *Buffer_alloc_writable(size_t capacity) { 70 | byte *result = mmap(/*addr=*/NULL, capacity, PROT_READ | PROT_WRITE, 71 | MAP_ANONYMOUS | MAP_PRIVATE, 72 | /*filedes=*/-1, /*off=*/0); 73 | assert(result != MAP_FAILED); 74 | return result; 75 | } 76 | 77 | void Buffer_init(Buffer *result, size_t capacity) { 78 | result->address = Buffer_alloc_writable(capacity); 79 | assert(result->address != MAP_FAILED); 80 | result->state = kWritable; 81 | result->len = 0; 82 | result->capacity = capacity; 83 | } 84 | 85 | void Buffer_deinit(Buffer *buf) { 86 | munmap(buf->address, buf->capacity); 87 | buf->address = NULL; 88 | buf->len = 0; 89 | buf->capacity = 0; 90 | } 91 | 92 | int Buffer_make_executable(Buffer *buf) { 93 | int result = mprotect(buf->address, buf->len, PROT_EXEC); 94 | buf->state = kExecutable; 95 | return result; 96 | } 97 | 98 | byte Buffer_at8(Buffer *buf, size_t pos) { return buf->address[pos]; } 99 | 100 | void Buffer_at_put8(Buffer *buf, size_t pos, byte b) { buf->address[pos] = b; } 101 | 102 | word max(word left, word right) { return left > right ? left : right; } 103 | 104 | void Buffer_ensure_capacity(Buffer *buf, word additional_capacity) { 105 | if (buf->len + additional_capacity <= buf->capacity) { 106 | return; 107 | } 108 | word new_capacity = 109 | max(buf->capacity * 2, buf->capacity + additional_capacity); 110 | byte *address = Buffer_alloc_writable(new_capacity); 111 | memcpy(address, buf->address, buf->len); 112 | int result = munmap(buf->address, buf->capacity); 113 | assert(result == 0 && "munmap failed"); 114 | buf->address = address; 115 | buf->capacity = new_capacity; 116 | } 117 | 118 | void Buffer_write8(Buffer *buf, byte b) { 119 | Buffer_ensure_capacity(buf, sizeof b); 120 | Buffer_at_put8(buf, buf->len++, b); 121 | } 122 | 123 | void Buffer_write32(Buffer *buf, int32_t value) { 124 | for (size_t i = 0; i < 4; i++) { 125 | Buffer_write8(buf, (value >> (i * kBitsPerByte)) & 0xff); 126 | } 127 | } 128 | 129 | // End Buffer 130 | 131 | // Emit 132 | 133 | typedef enum { 134 | kRax = 0, 135 | kRcx, 136 | kRdx, 137 | kRbx, 138 | kRsp, 139 | kRbp, 140 | kRsi, 141 | kRdi, 142 | } Register; 143 | 144 | enum { 145 | kRexPrefix = 0x48, 146 | }; 147 | 148 | void Emit_mov_reg_imm32(Buffer *buf, Register dst, int32_t src) { 149 | Buffer_write8(buf, kRexPrefix); 150 | Buffer_write8(buf, 0xc7); 151 | Buffer_write8(buf, 0xc0 + dst); 152 | Buffer_write32(buf, src); 153 | } 154 | 155 | void Emit_ret(Buffer *buf) { Buffer_write8(buf, 0xc3); } 156 | 157 | // End Emit 158 | 159 | // AST 160 | 161 | typedef enum { 162 | kInteger, 163 | } ASTNodeType; 164 | 165 | struct ASTNode; 166 | typedef struct ASTNode ASTNode; 167 | 168 | ASTNodeType AST_type_of(ASTNode *node) { 169 | uint64_t address = (uint64_t)node; 170 | if ((address & kIntegerTagMask) == kIntegerTag) { 171 | return kInteger; 172 | } 173 | assert(0 && "unexpected node type"); 174 | } 175 | 176 | bool AST_is_integer(ASTNode *node) { return AST_type_of(node) == kInteger; } 177 | 178 | word AST_get_integer(ASTNode *node) { return (word)node >> kIntegerShift; } 179 | 180 | ASTNode *AST_new_integer(word value) { 181 | return (ASTNode *)Object_encode_integer(value); 182 | } 183 | 184 | // End AST 185 | 186 | // Compile 187 | 188 | int Compile_expr(Buffer *buf, ASTNode *node) { 189 | if (AST_is_integer(node)) { 190 | word value = AST_get_integer(node); 191 | Emit_mov_reg_imm32(buf, kRax, Object_encode_integer(value)); 192 | return 0; 193 | } 194 | assert(0 && "unexpected node type"); 195 | } 196 | 197 | int Compile_function(Buffer *buf, ASTNode *node) { 198 | int result = Compile_expr(buf, node); 199 | if (result != 0) 200 | return result; 201 | Emit_ret(buf); 202 | return 0; 203 | } 204 | 205 | // End Compile 206 | 207 | typedef int (*JitFunction)(); 208 | 209 | // Testing 210 | 211 | word Testing_execute_expr(Buffer *buf) { 212 | assert(buf != NULL); 213 | assert(buf->address != NULL); 214 | assert(buf->state == kExecutable); 215 | // The pointer-pointer cast is allowed but the underlying 216 | // data-to-function-pointer back-and-forth is only guaranteed to work on 217 | // POSIX systems (because of eg dlsym). 218 | JitFunction function = *(JitFunction *)(&buf->address); 219 | return function(); 220 | } 221 | 222 | #define EXPECT_EQUALS_BYTES(buf, arr) \ 223 | ASSERT_MEM_EQ((arr), (buf)->address, sizeof arr) 224 | 225 | // End Testing 226 | 227 | // Tests 228 | 229 | TEST encode_positive_integer(void) { 230 | ASSERT_EQ(Object_encode_integer(0), 0x0); 231 | ASSERT_EQ(Object_encode_integer(1), 0x4); 232 | ASSERT_EQ(Object_encode_integer(10), 0x28); 233 | PASS(); 234 | } 235 | 236 | TEST encode_negative_integer(void) { 237 | ASSERT_EQ(0x0, Object_encode_integer(0)); 238 | ASSERT_EQ(Object_encode_integer(-1), (word)0xfffffffffffffffc); 239 | ASSERT_EQ(Object_encode_integer(-10), (word)0xffffffffffffffd8); 240 | PASS(); 241 | } 242 | 243 | TEST buffer_write8_increases_length(void) { 244 | Buffer buf; 245 | Buffer_init(&buf, 5); 246 | ASSERT_EQ(buf.len, 0); 247 | Buffer_write8(&buf, 0xdb); 248 | ASSERT_EQ(Buffer_at8(&buf, 0), 0xdb); 249 | ASSERT_EQ(buf.len, 1); 250 | Buffer_deinit(&buf); 251 | PASS(); 252 | } 253 | 254 | TEST buffer_write8_expands_buffer(void) { 255 | Buffer buf; 256 | Buffer_init(&buf, 1); 257 | ASSERT_EQ(buf.capacity, 1); 258 | ASSERT_EQ(buf.len, 0); 259 | Buffer_write8(&buf, 0xdb); 260 | Buffer_write8(&buf, 0xef); 261 | ASSERT(buf.capacity > 1); 262 | ASSERT_EQ(buf.len, 2); 263 | Buffer_deinit(&buf); 264 | PASS(); 265 | } 266 | 267 | TEST buffer_write32_expands_buffer(void) { 268 | Buffer buf; 269 | Buffer_init(&buf, 1); 270 | ASSERT_EQ(buf.capacity, 1); 271 | ASSERT_EQ(buf.len, 0); 272 | Buffer_write32(&buf, 0xdeadbeef); 273 | ASSERT(buf.capacity > 1); 274 | ASSERT_EQ(buf.len, 4); 275 | Buffer_deinit(&buf); 276 | PASS(); 277 | } 278 | 279 | TEST buffer_write32_writes_little_endian(void) { 280 | Buffer buf; 281 | Buffer_init(&buf, 4); 282 | Buffer_write32(&buf, 0xdeadbeef); 283 | ASSERT_EQ(Buffer_at8(&buf, 0), 0xef); 284 | ASSERT_EQ(Buffer_at8(&buf, 1), 0xbe); 285 | ASSERT_EQ(Buffer_at8(&buf, 2), 0xad); 286 | ASSERT_EQ(Buffer_at8(&buf, 3), 0xde); 287 | Buffer_deinit(&buf); 288 | PASS(); 289 | } 290 | 291 | TEST compile_positive_integer(void) { 292 | word value = 123; 293 | ASTNode *node = AST_new_integer(value); 294 | Buffer buf; 295 | Buffer_init(&buf, 1); 296 | int compile_result = Compile_function(&buf, node); 297 | ASSERT_EQ(compile_result, 0); 298 | // mov eax, imm(123); ret 299 | byte expected[] = {0x48, 0xc7, 0xc0, 0xec, 0x01, 0x00, 0x00, 0xc3}; 300 | EXPECT_EQUALS_BYTES(&buf, expected); 301 | Buffer_make_executable(&buf); 302 | word result = Testing_execute_expr(&buf); 303 | ASSERT_EQ(result, Object_encode_integer(value)); 304 | PASS(); 305 | } 306 | 307 | TEST compile_negative_integer(void) { 308 | word value = -123; 309 | ASTNode *node = AST_new_integer(value); 310 | Buffer buf; 311 | Buffer_init(&buf, 1); 312 | int compile_result = Compile_function(&buf, node); 313 | ASSERT_EQ(compile_result, 0); 314 | // mov eax, imm(-123); ret 315 | byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0xfe, 0xff, 0xff, 0xc3}; 316 | EXPECT_EQUALS_BYTES(&buf, expected); 317 | Buffer_make_executable(&buf); 318 | word result = Testing_execute_expr(&buf); 319 | ASSERT_EQ(result, Object_encode_integer(value)); 320 | PASS(); 321 | } 322 | 323 | SUITE(object_tests) { 324 | RUN_TEST(encode_positive_integer); 325 | RUN_TEST(encode_negative_integer); 326 | } 327 | 328 | SUITE(buffer_tests) { 329 | RUN_TEST(buffer_write8_increases_length); 330 | RUN_TEST(buffer_write8_expands_buffer); 331 | RUN_TEST(buffer_write32_expands_buffer); 332 | RUN_TEST(buffer_write32_writes_little_endian); 333 | } 334 | 335 | SUITE(compiler_tests) { 336 | RUN_TEST(compile_positive_integer); 337 | RUN_TEST(compile_negative_integer); 338 | } 339 | 340 | // End Tests 341 | 342 | GREATEST_MAIN_DEFS(); 343 | 344 | int main(int argc, char **argv) { 345 | GREATEST_MAIN_BEGIN(); 346 | RUN_SUITE(object_tests); 347 | RUN_SUITE(buffer_tests); 348 | RUN_SUITE(compiler_tests); 349 | GREATEST_MAIN_END(); 350 | } 351 | -------------------------------------------------------------------------------- /compiling-unary.c: -------------------------------------------------------------------------------- 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-unary.c 4 | 5 | #define _GNU_SOURCE 6 | #include // for assert 7 | #include // for bool 8 | #include // for NULL 9 | #include // for int32_t, etc 10 | #include // for memcpy 11 | #include // for mmap 12 | #undef _GNU_SOURCE 13 | 14 | #include "greatest.h" 15 | 16 | // Objects 17 | 18 | typedef int64_t word; 19 | typedef uint64_t uword; 20 | 21 | // These constants are defined in a enum because the right hand side of a 22 | // statement like 23 | // static const int kFoo = ...; 24 | // must be a so-called "Integer Constant Expression". Compilers are required to 25 | // support a certain set of these expressions, but are not required to support 26 | // arbitrary arithmetic with other integer constants. Compilers such as gcc 27 | // before gcc-8 just decided not to play this game, while gcc-8+ and Clang play 28 | // just fine. 29 | // Since this arithmetic with constant values works just fine for enums, make 30 | // all these constants enum values instead. 31 | // See https://twitter.com/tekknolagi/status/1328449329472835586 for more info. 32 | enum { 33 | kBitsPerByte = 8, // bits 34 | kWordSize = sizeof(word), // bytes 35 | kBitsPerWord = kWordSize * kBitsPerByte, // bits 36 | 37 | kIntegerTag = 0x0, // 0b00 38 | kIntegerTagMask = 0x3, // 0b11 39 | kIntegerShift = 2, 40 | kIntegerBits = kBitsPerWord - kIntegerShift, 41 | 42 | kImmediateTagMask = 0x3f, 43 | 44 | kCharTag = 0x0f, // 0b00001111 45 | kCharMask = 0xff, // 0b11111111 46 | kCharShift = 8, 47 | 48 | kBoolTag = 0x1f, // 0b0011111 49 | kBoolMask = 0x80, // 0b10000000 50 | kBoolShift = 7, 51 | 52 | kNilTag = 0x2f, // 0b101111 53 | 54 | kErrorTag = 0x3f, // 0b111111 55 | 56 | kPairTag = 0x1, // 0b001 57 | kSymbolTag = 0x5, // 0b101 58 | kClosureTag = 0x6, // 0b110 59 | kHeapTagMask = ((uword)0x7), // 0b000...111 60 | kHeapPtrMask = ~kHeapTagMask, // 0b1111...1000 61 | }; 62 | 63 | // These are defined as macros because they will not work as static const int 64 | // constants (per above explanation), and enum constants are only required to 65 | // be an int wide (per ISO C). 66 | #define INTEGER_MAX ((1LL << (kIntegerBits - 1)) - 1) 67 | #define INTEGER_MIN (-(1LL << (kIntegerBits - 1))) 68 | 69 | uword Object_encode_integer(word value) { 70 | assert(value < INTEGER_MAX && "too big"); 71 | assert(value > INTEGER_MIN && "too small"); 72 | return value << kIntegerShift; 73 | } 74 | 75 | word Object_decode_integer(uword value) { return (word)value >> kIntegerShift; } 76 | 77 | uword Object_encode_char(char value) { 78 | return ((uword)value << kCharShift) | kCharTag; 79 | } 80 | 81 | char Object_decode_char(uword value) { 82 | return (value >> kCharShift) & kCharMask; 83 | } 84 | 85 | uword Object_encode_bool(bool value) { 86 | return ((uword)value << kBoolShift) | kBoolTag; 87 | } 88 | 89 | bool Object_decode_bool(uword value) { return value & kBoolMask; } 90 | 91 | uword Object_true() { return Object_encode_bool(true); } 92 | 93 | uword Object_false() { return Object_encode_bool(false); } 94 | 95 | uword Object_nil() { return 0x2f; } 96 | 97 | uword Object_address(void *obj) { return (uword)obj & kHeapPtrMask; } 98 | 99 | // End Objects 100 | 101 | // Buffer 102 | 103 | typedef unsigned char byte; 104 | 105 | typedef enum { 106 | kWritable, 107 | kExecutable, 108 | } BufferState; 109 | 110 | typedef struct { 111 | byte *address; 112 | BufferState state; 113 | size_t len; 114 | size_t capacity; 115 | } Buffer; 116 | 117 | byte *Buffer_alloc_writable(size_t capacity) { 118 | byte *result = mmap(/*addr=*/NULL, capacity, PROT_READ | PROT_WRITE, 119 | MAP_ANONYMOUS | MAP_PRIVATE, 120 | /*filedes=*/-1, /*off=*/0); 121 | assert(result != MAP_FAILED); 122 | return result; 123 | } 124 | 125 | void Buffer_init(Buffer *result, size_t capacity) { 126 | result->address = Buffer_alloc_writable(capacity); 127 | assert(result->address != MAP_FAILED); 128 | result->state = kWritable; 129 | result->len = 0; 130 | result->capacity = capacity; 131 | } 132 | 133 | void Buffer_deinit(Buffer *buf) { 134 | munmap(buf->address, buf->capacity); 135 | buf->address = NULL; 136 | buf->len = 0; 137 | buf->capacity = 0; 138 | } 139 | 140 | int Buffer_make_executable(Buffer *buf) { 141 | int result = mprotect(buf->address, buf->len, PROT_EXEC); 142 | buf->state = kExecutable; 143 | return result; 144 | } 145 | 146 | byte Buffer_at8(Buffer *buf, size_t pos) { return buf->address[pos]; } 147 | 148 | void Buffer_at_put8(Buffer *buf, size_t pos, byte b) { buf->address[pos] = b; } 149 | 150 | word max(word left, word right) { return left > right ? left : right; } 151 | 152 | void Buffer_ensure_capacity(Buffer *buf, word additional_capacity) { 153 | if (buf->len + additional_capacity <= buf->capacity) { 154 | return; 155 | } 156 | word new_capacity = 157 | max(buf->capacity * 2, buf->capacity + additional_capacity); 158 | byte *address = Buffer_alloc_writable(new_capacity); 159 | memcpy(address, buf->address, buf->len); 160 | int result = munmap(buf->address, buf->capacity); 161 | assert(result == 0 && "munmap failed"); 162 | buf->address = address; 163 | buf->capacity = new_capacity; 164 | } 165 | 166 | void Buffer_write8(Buffer *buf, byte b) { 167 | Buffer_ensure_capacity(buf, sizeof b); 168 | Buffer_at_put8(buf, buf->len++, b); 169 | } 170 | 171 | void Buffer_write32(Buffer *buf, int32_t value) { 172 | for (size_t i = 0; i < 4; i++) { 173 | Buffer_write8(buf, (value >> (i * kBitsPerByte)) & 0xff); 174 | } 175 | } 176 | 177 | // End Buffer 178 | 179 | // Emit 180 | 181 | typedef enum { 182 | kRax = 0, 183 | kRcx, 184 | kRdx, 185 | kRbx, 186 | kRsp, 187 | kRbp, 188 | kRsi, 189 | kRdi, 190 | } Register; 191 | 192 | typedef enum { 193 | kAl = 0, 194 | kCl, 195 | kDl, 196 | kBl, 197 | kAh, 198 | kCh, 199 | kDh, 200 | kBh, 201 | } PartialRegister; 202 | 203 | typedef enum { 204 | kOverflow = 0, 205 | kNotOverflow, 206 | kBelow, 207 | kCarry = kBelow, 208 | kNotAboveOrEqual = kBelow, 209 | kAboveOrEqual, 210 | kNotBelow = kAboveOrEqual, 211 | kNotCarry = kAboveOrEqual, 212 | kEqual, 213 | kZero = kEqual, 214 | // TODO(max): Add more 215 | } Condition; 216 | 217 | enum { 218 | kRexPrefix = 0x48, 219 | }; 220 | 221 | void Emit_mov_reg_imm32(Buffer *buf, Register dst, int32_t src) { 222 | Buffer_write8(buf, kRexPrefix); 223 | Buffer_write8(buf, 0xc7); 224 | Buffer_write8(buf, 0xc0 + dst); 225 | Buffer_write32(buf, src); 226 | } 227 | 228 | void Emit_ret(Buffer *buf) { Buffer_write8(buf, 0xc3); } 229 | 230 | void Emit_add_reg_imm32(Buffer *buf, Register dst, int32_t src) { 231 | Buffer_write8(buf, kRexPrefix); 232 | if (dst == kRax) { 233 | // Optimization: add eax, {imm32} can either be encoded as 05 {imm32} or 81 234 | // c0 {imm32}. 235 | Buffer_write8(buf, 0x05); 236 | } else { 237 | Buffer_write8(buf, 0x81); 238 | Buffer_write8(buf, 0xc0 + dst); 239 | } 240 | Buffer_write32(buf, src); 241 | } 242 | 243 | void Emit_sub_reg_imm32(Buffer *buf, Register dst, int32_t src) { 244 | Buffer_write8(buf, kRexPrefix); 245 | if (dst == kRax) { 246 | // Optimization: sub eax, {imm32} can either be encoded as 2d {imm32} or 81 247 | // e8 {imm32}. 248 | Buffer_write8(buf, 0x2d); 249 | } else { 250 | Buffer_write8(buf, 0x81); 251 | Buffer_write8(buf, 0xe8 + dst); 252 | } 253 | Buffer_write32(buf, src); 254 | } 255 | 256 | void Emit_shl_reg_imm8(Buffer *buf, Register dst, int8_t bits) { 257 | Buffer_write8(buf, kRexPrefix); 258 | Buffer_write8(buf, 0xc1); 259 | Buffer_write8(buf, 0xe0 + dst); 260 | Buffer_write8(buf, bits); 261 | } 262 | 263 | void Emit_shr_reg_imm8(Buffer *buf, Register dst, int8_t bits) { 264 | Buffer_write8(buf, kRexPrefix); 265 | Buffer_write8(buf, 0xc1); 266 | Buffer_write8(buf, 0xe8 + dst); 267 | Buffer_write8(buf, bits); 268 | } 269 | 270 | void Emit_or_reg_imm8(Buffer *buf, Register dst, uint8_t tag) { 271 | Buffer_write8(buf, kRexPrefix); 272 | Buffer_write8(buf, 0x83); 273 | Buffer_write8(buf, 0xc8 + dst); 274 | Buffer_write8(buf, tag); 275 | } 276 | 277 | void Emit_and_reg_imm8(Buffer *buf, Register dst, uint8_t tag) { 278 | Buffer_write8(buf, kRexPrefix); 279 | Buffer_write8(buf, 0x83); 280 | Buffer_write8(buf, 0xe0 + dst); 281 | Buffer_write8(buf, tag); 282 | } 283 | 284 | void Emit_cmp_reg_imm32(Buffer *buf, Register left, int32_t right) { 285 | Buffer_write8(buf, kRexPrefix); 286 | if (left == kRax) { 287 | // Optimization: cmp rax, {imm32} can either be encoded as 3d {imm32} or 81 288 | // f8 {imm32}. 289 | Buffer_write8(buf, 0x3d); 290 | } else { 291 | Buffer_write8(buf, 0x81); 292 | Buffer_write8(buf, 0xf8 + left); 293 | } 294 | Buffer_write32(buf, right); 295 | } 296 | 297 | void Emit_setcc_imm8(Buffer *buf, Condition cond, PartialRegister dst) { 298 | Buffer_write8(buf, 0x0f); 299 | Buffer_write8(buf, 0x90 + cond); 300 | Buffer_write8(buf, 0xc0 + dst); 301 | } 302 | 303 | // End Emit 304 | 305 | // AST 306 | 307 | typedef struct ASTNode ASTNode; 308 | 309 | typedef struct Pair { 310 | ASTNode *car; 311 | ASTNode *cdr; 312 | } Pair; 313 | 314 | typedef struct Symbol { 315 | word length; 316 | char cstr[]; 317 | } Symbol; 318 | 319 | bool AST_is_integer(ASTNode *node) { 320 | return ((uword)node & kIntegerTagMask) == kIntegerTag; 321 | } 322 | 323 | word AST_get_integer(ASTNode *node) { 324 | return Object_decode_integer((uword)node); 325 | } 326 | 327 | ASTNode *AST_new_integer(word value) { 328 | return (ASTNode *)Object_encode_integer(value); 329 | } 330 | 331 | bool AST_is_char(ASTNode *node) { 332 | return ((uword)node & kImmediateTagMask) == kCharTag; 333 | } 334 | 335 | char AST_get_char(ASTNode *node) { return Object_decode_char((uword)node); } 336 | 337 | ASTNode *AST_new_char(char value) { 338 | return (ASTNode *)Object_encode_char(value); 339 | } 340 | 341 | bool AST_is_bool(ASTNode *node) { 342 | return ((uword)node & kImmediateTagMask) == kBoolTag; 343 | } 344 | 345 | bool AST_get_bool(ASTNode *node) { return Object_decode_bool((uword)node); } 346 | 347 | ASTNode *AST_new_bool(bool value) { 348 | return (ASTNode *)Object_encode_bool(value); 349 | } 350 | 351 | bool AST_is_nil(ASTNode *node) { return (uword)node == Object_nil(); } 352 | 353 | ASTNode *AST_nil() { return (ASTNode *)Object_nil(); } 354 | 355 | ASTNode *AST_heap_alloc(unsigned char tag, uword size) { 356 | // Initialize to 0 357 | uword address = (uword)calloc(size, 1); 358 | return (ASTNode *)(address | tag); 359 | } 360 | 361 | bool AST_is_heap_object(ASTNode *node) { 362 | // For some reason masking out the tag first and then doing the comparison 363 | // makes this branchless 364 | unsigned char tag = (uword)node & kHeapTagMask; 365 | // Heap object tags are between 0b001 and 0b110 except for 0b100 (which is an 366 | // integer) 367 | return (tag & kIntegerTagMask) > 0 && (tag & kImmediateTagMask) != 0x7; 368 | } 369 | 370 | void AST_pair_set_car(ASTNode *node, ASTNode *car); 371 | void AST_pair_set_cdr(ASTNode *node, ASTNode *cdr); 372 | 373 | ASTNode *AST_new_pair(ASTNode *car, ASTNode *cdr) { 374 | ASTNode *node = AST_heap_alloc(kPairTag, sizeof(Pair)); 375 | AST_pair_set_car(node, car); 376 | AST_pair_set_cdr(node, cdr); 377 | return node; 378 | } 379 | 380 | bool AST_is_pair(ASTNode *node) { 381 | return ((uword)node & kHeapTagMask) == kPairTag; 382 | } 383 | 384 | Pair *AST_as_pair(ASTNode *node) { 385 | assert(AST_is_pair(node)); 386 | return (Pair *)Object_address(node); 387 | } 388 | 389 | ASTNode *AST_pair_car(ASTNode *node) { return AST_as_pair(node)->car; } 390 | 391 | void AST_pair_set_car(ASTNode *node, ASTNode *car) { 392 | AST_as_pair(node)->car = car; 393 | } 394 | 395 | ASTNode *AST_pair_cdr(ASTNode *node) { return AST_as_pair(node)->cdr; } 396 | 397 | void AST_pair_set_cdr(ASTNode *node, ASTNode *cdr) { 398 | AST_as_pair(node)->cdr = cdr; 399 | } 400 | 401 | void AST_heap_free(ASTNode *node) { 402 | if (!AST_is_heap_object(node)) { 403 | return; 404 | } 405 | if (AST_is_pair(node)) { 406 | AST_heap_free(AST_pair_car(node)); 407 | AST_heap_free(AST_pair_cdr(node)); 408 | } 409 | free((void *)Object_address(node)); 410 | } 411 | 412 | Symbol *AST_as_symbol(ASTNode *node); 413 | 414 | ASTNode *AST_new_symbol(const char *str) { 415 | word data_length = strlen(str) + 1; // for NUL 416 | ASTNode *node = AST_heap_alloc(kSymbolTag, sizeof(Symbol) + data_length); 417 | Symbol *s = AST_as_symbol(node); 418 | s->length = data_length; 419 | memcpy(s->cstr, str, data_length); 420 | return node; 421 | } 422 | 423 | bool AST_is_symbol(ASTNode *node) { 424 | return ((uword)node & kHeapTagMask) == kSymbolTag; 425 | } 426 | 427 | Symbol *AST_as_symbol(ASTNode *node) { 428 | assert(AST_is_symbol(node)); 429 | return (Symbol *)Object_address(node); 430 | } 431 | 432 | const char *AST_symbol_cstr(ASTNode *node) { 433 | return (const char *)AST_as_symbol(node)->cstr; 434 | } 435 | 436 | bool AST_symbol_matches(ASTNode *node, const char *cstr) { 437 | return strcmp(AST_symbol_cstr(node), cstr) == 0; 438 | } 439 | 440 | // End AST 441 | 442 | // Compile 443 | 444 | int Compile_expr(Buffer *buf, ASTNode *node); 445 | 446 | ASTNode *operand1(ASTNode *args) { return AST_pair_car(args); } 447 | 448 | #define _(exp) \ 449 | do { \ 450 | int result = exp; \ 451 | if (result != 0) \ 452 | return result; \ 453 | } while (0) 454 | 455 | void Compile_compare_imm32(Buffer *buf, int32_t value) { 456 | Emit_cmp_reg_imm32(buf, kRax, value); 457 | Emit_mov_reg_imm32(buf, kRax, 0); 458 | Emit_setcc_imm8(buf, kEqual, kAl); 459 | Emit_shl_reg_imm8(buf, kRax, kBoolShift); 460 | Emit_or_reg_imm8(buf, kRax, kBoolTag); 461 | } 462 | 463 | int Compile_call(Buffer *buf, ASTNode *callable, ASTNode *args) { 464 | assert(AST_pair_cdr(args) == AST_nil() && 465 | "only unary function calls supported"); 466 | if (AST_is_symbol(callable)) { 467 | if (AST_symbol_matches(callable, "add1")) { 468 | _(Compile_expr(buf, operand1(args))); 469 | Emit_add_reg_imm32(buf, kRax, Object_encode_integer(1)); 470 | return 0; 471 | } 472 | if (AST_symbol_matches(callable, "sub1")) { 473 | _(Compile_expr(buf, operand1(args))); 474 | Emit_sub_reg_imm32(buf, kRax, Object_encode_integer(1)); 475 | return 0; 476 | } 477 | if (AST_symbol_matches(callable, "integer->char")) { 478 | _(Compile_expr(buf, operand1(args))); 479 | Emit_shl_reg_imm8(buf, kRax, kCharShift - kIntegerShift); 480 | Emit_or_reg_imm8(buf, kRax, kCharTag); 481 | return 0; 482 | } 483 | if (AST_symbol_matches(callable, "char->integer")) { 484 | _(Compile_expr(buf, operand1(args))); 485 | Emit_shr_reg_imm8(buf, kRax, kCharShift - kIntegerShift); 486 | return 0; 487 | } 488 | if (AST_symbol_matches(callable, "nil?")) { 489 | _(Compile_expr(buf, operand1(args))); 490 | Compile_compare_imm32(buf, Object_nil()); 491 | return 0; 492 | } 493 | if (AST_symbol_matches(callable, "zero?")) { 494 | _(Compile_expr(buf, operand1(args))); 495 | Compile_compare_imm32(buf, Object_encode_integer(0)); 496 | return 0; 497 | } 498 | if (AST_symbol_matches(callable, "not")) { 499 | _(Compile_expr(buf, operand1(args))); 500 | // All non #f values are truthy 501 | // ...this might be a problem if we want to make nil falsey 502 | Compile_compare_imm32(buf, Object_false()); 503 | return 0; 504 | } 505 | if (AST_symbol_matches(callable, "integer?")) { 506 | _(Compile_expr(buf, operand1(args))); 507 | Emit_and_reg_imm8(buf, kRax, kIntegerTagMask); 508 | Compile_compare_imm32(buf, kIntegerTag); 509 | return 0; 510 | } 511 | if (AST_symbol_matches(callable, "boolean?")) { 512 | _(Compile_expr(buf, operand1(args))); 513 | Emit_and_reg_imm8(buf, kRax, kImmediateTagMask); 514 | Compile_compare_imm32(buf, kBoolTag); 515 | return 0; 516 | } 517 | } 518 | assert(0 && "unexpected call type"); 519 | } 520 | 521 | int Compile_expr(Buffer *buf, ASTNode *node) { 522 | if (AST_is_integer(node)) { 523 | word value = AST_get_integer(node); 524 | Emit_mov_reg_imm32(buf, kRax, Object_encode_integer(value)); 525 | return 0; 526 | } 527 | if (AST_is_char(node)) { 528 | char value = AST_get_char(node); 529 | Emit_mov_reg_imm32(buf, kRax, Object_encode_char(value)); 530 | return 0; 531 | } 532 | if (AST_is_bool(node)) { 533 | bool value = AST_get_bool(node); 534 | Emit_mov_reg_imm32(buf, kRax, Object_encode_bool(value)); 535 | return 0; 536 | } 537 | if (AST_is_nil(node)) { 538 | Emit_mov_reg_imm32(buf, kRax, Object_nil()); 539 | return 0; 540 | } 541 | if (AST_is_pair(node)) { 542 | return Compile_call(buf, AST_pair_car(node), AST_pair_cdr(node)); 543 | } 544 | assert(0 && "unexpected node type"); 545 | } 546 | 547 | int Compile_function(Buffer *buf, ASTNode *node) { 548 | _(Compile_expr(buf, node)); 549 | Emit_ret(buf); 550 | return 0; 551 | } 552 | 553 | // End Compile 554 | 555 | typedef int (*JitFunction)(); 556 | 557 | // Testing 558 | 559 | uword Testing_execute_expr(Buffer *buf) { 560 | assert(buf != NULL); 561 | assert(buf->address != NULL); 562 | assert(buf->state == kExecutable); 563 | // The pointer-pointer cast is allowed but the underlying 564 | // data-to-function-pointer back-and-forth is only guaranteed to work on 565 | // POSIX systems (because of eg dlsym). 566 | JitFunction function = *(JitFunction *)(&buf->address); 567 | return function(); 568 | } 569 | 570 | #define EXPECT_EQUALS_BYTES(buf, arr) \ 571 | ASSERT_MEM_EQ(arr, (buf)->address, sizeof arr) 572 | 573 | #define RUN_BUFFER_TEST(test_name) \ 574 | do { \ 575 | Buffer buf; \ 576 | Buffer_init(&buf, 1); \ 577 | GREATEST_RUN_TEST1(test_name, &buf); \ 578 | Buffer_deinit(&buf); \ 579 | } while (0) 580 | 581 | ASTNode *list1(ASTNode *item0) { return AST_new_pair(item0, AST_nil()); } 582 | 583 | ASTNode *list2(ASTNode *item0, ASTNode *item1) { 584 | return AST_new_pair(item0, list1(item1)); 585 | } 586 | 587 | ASTNode *new_unary_call(const char *name, ASTNode *arg) { 588 | return list2(AST_new_symbol(name), arg); 589 | } 590 | 591 | // End Testing 592 | 593 | // Tests 594 | 595 | TEST encode_positive_integer(void) { 596 | ASSERT_EQ(Object_encode_integer(0), 0x0); 597 | ASSERT_EQ(Object_encode_integer(1), 0x4); 598 | ASSERT_EQ(Object_encode_integer(10), 0x28); 599 | PASS(); 600 | } 601 | 602 | TEST encode_negative_integer(void) { 603 | ASSERT_EQ(Object_encode_integer(0), 0x0); 604 | ASSERT_EQ(Object_encode_integer(-1), 0xfffffffffffffffc); 605 | ASSERT_EQ(Object_encode_integer(-10), 0xffffffffffffffd8); 606 | PASS(); 607 | } 608 | 609 | TEST encode_char(void) { 610 | ASSERT_EQ(Object_encode_char('\0'), 0xf); 611 | ASSERT_EQ(Object_encode_char('a'), 0x610f); 612 | PASS(); 613 | } 614 | 615 | TEST decode_char(void) { 616 | ASSERT_EQ(Object_decode_char(0xf), '\0'); 617 | ASSERT_EQ(Object_decode_char(0x610f), 'a'); 618 | PASS(); 619 | } 620 | 621 | TEST encode_bool(void) { 622 | ASSERT_EQ(Object_encode_bool(true), 0x9f); 623 | ASSERT_EQ(Object_encode_bool(false), 0x1f); 624 | ASSERT_EQ(Object_true(), 0x9f); 625 | ASSERT_EQ(Object_false(), 0x1f); 626 | PASS(); 627 | } 628 | 629 | TEST decode_bool(void) { 630 | ASSERT_EQ(Object_decode_bool(0x9f), true); 631 | ASSERT_EQ(Object_decode_bool(0x1f), false); 632 | PASS(); 633 | } 634 | 635 | TEST address(void) { 636 | ASSERT_EQ(Object_address((void *)0xFF01), 0xFF00); 637 | PASS(); 638 | } 639 | 640 | TEST ast_new_pair(void) { 641 | ASTNode *node = AST_new_pair(NULL, NULL); 642 | ASSERT(AST_is_pair(node)); 643 | AST_heap_free(node); 644 | PASS(); 645 | } 646 | 647 | TEST ast_pair_car_returns_car(void) { 648 | ASTNode *node = AST_new_pair(AST_new_integer(123), NULL); 649 | ASTNode *car = AST_pair_car(node); 650 | ASSERT(AST_is_integer(car)); 651 | ASSERT_EQ(Object_decode_integer((uword)car), 123); 652 | AST_heap_free(node); 653 | PASS(); 654 | } 655 | 656 | TEST ast_pair_cdr_returns_cdr(void) { 657 | ASTNode *node = AST_new_pair(NULL, AST_new_integer(123)); 658 | ASTNode *cdr = AST_pair_cdr(node); 659 | ASSERT(AST_is_integer(cdr)); 660 | ASSERT_EQ(Object_decode_integer((uword)cdr), 123); 661 | AST_heap_free(node); 662 | PASS(); 663 | } 664 | 665 | TEST ast_new_symbol(void) { 666 | const char *value = "my symbol"; 667 | ASTNode *node = AST_new_symbol(value); 668 | ASSERT(AST_is_symbol(node)); 669 | ASSERT_STR_EQ(AST_symbol_cstr(node), value); 670 | AST_heap_free(node); 671 | PASS(); 672 | } 673 | 674 | TEST buffer_write8_increases_length(Buffer *buf) { 675 | ASSERT_EQ(buf->len, 0); 676 | Buffer_write8(buf, 0xdb); 677 | ASSERT_EQ(Buffer_at8(buf, 0), 0xdb); 678 | ASSERT_EQ(buf->len, 1); 679 | PASS(); 680 | } 681 | 682 | TEST buffer_write8_expands_buffer(void) { 683 | Buffer buf; 684 | Buffer_init(&buf, 1); 685 | ASSERT_EQ(buf.capacity, 1); 686 | ASSERT_EQ(buf.len, 0); 687 | Buffer_write8(&buf, 0xdb); 688 | Buffer_write8(&buf, 0xef); 689 | ASSERT(buf.capacity > 1); 690 | ASSERT_EQ(buf.len, 2); 691 | Buffer_deinit(&buf); 692 | PASS(); 693 | } 694 | 695 | TEST buffer_write32_expands_buffer(void) { 696 | Buffer buf; 697 | Buffer_init(&buf, 1); 698 | ASSERT_EQ(buf.capacity, 1); 699 | ASSERT_EQ(buf.len, 0); 700 | Buffer_write32(&buf, 0xdeadbeef); 701 | ASSERT(buf.capacity > 1); 702 | ASSERT_EQ(buf.len, 4); 703 | Buffer_deinit(&buf); 704 | PASS(); 705 | } 706 | 707 | TEST buffer_write32_writes_little_endian(Buffer *buf) { 708 | Buffer_write32(buf, 0xdeadbeef); 709 | ASSERT_EQ(Buffer_at8(buf, 0), 0xef); 710 | ASSERT_EQ(Buffer_at8(buf, 1), 0xbe); 711 | ASSERT_EQ(Buffer_at8(buf, 2), 0xad); 712 | ASSERT_EQ(Buffer_at8(buf, 3), 0xde); 713 | PASS(); 714 | } 715 | 716 | TEST compile_positive_integer(Buffer *buf) { 717 | word value = 123; 718 | ASTNode *node = AST_new_integer(value); 719 | int compile_result = Compile_function(buf, node); 720 | ASSERT_EQ(compile_result, 0); 721 | // mov eax, imm(123); ret 722 | byte expected[] = {0x48, 0xc7, 0xc0, 0xec, 0x01, 0x00, 0x00, 0xc3}; 723 | EXPECT_EQUALS_BYTES(buf, expected); 724 | Buffer_make_executable(buf); 725 | uword result = Testing_execute_expr(buf); 726 | ASSERT_EQ(result, Object_encode_integer(value)); 727 | PASS(); 728 | } 729 | 730 | TEST compile_negative_integer(Buffer *buf) { 731 | word value = -123; 732 | ASTNode *node = AST_new_integer(value); 733 | int compile_result = Compile_function(buf, node); 734 | ASSERT_EQ(compile_result, 0); 735 | // mov eax, imm(-123); ret 736 | byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0xfe, 0xff, 0xff, 0xc3}; 737 | EXPECT_EQUALS_BYTES(buf, expected); 738 | Buffer_make_executable(buf); 739 | uword result = Testing_execute_expr(buf); 740 | ASSERT_EQ(result, Object_encode_integer(value)); 741 | PASS(); 742 | } 743 | 744 | TEST compile_char(Buffer *buf) { 745 | char value = 'a'; 746 | ASTNode *node = AST_new_char(value); 747 | int compile_result = Compile_function(buf, node); 748 | ASSERT_EQ(compile_result, 0); 749 | // mov eax, imm('a'); ret 750 | byte expected[] = {0x48, 0xc7, 0xc0, 0x0f, 0x61, 0x00, 0x00, 0xc3}; 751 | EXPECT_EQUALS_BYTES(buf, expected); 752 | Buffer_make_executable(buf); 753 | uword result = Testing_execute_expr(buf); 754 | ASSERT_EQ(result, Object_encode_char(value)); 755 | PASS(); 756 | } 757 | 758 | TEST compile_true(Buffer *buf) { 759 | ASTNode *node = AST_new_bool(true); 760 | int compile_result = Compile_function(buf, node); 761 | ASSERT_EQ(compile_result, 0); 762 | // mov eax, imm(true); ret 763 | byte expected[] = {0x48, 0xc7, 0xc0, 0x9f, 0x0, 0x0, 0x0, 0xc3}; 764 | EXPECT_EQUALS_BYTES(buf, expected); 765 | Buffer_make_executable(buf); 766 | uword result = Testing_execute_expr(buf); 767 | ASSERT_EQ(result, Object_true()); 768 | PASS(); 769 | } 770 | 771 | TEST compile_false(Buffer *buf) { 772 | ASTNode *node = AST_new_bool(false); 773 | int compile_result = Compile_function(buf, node); 774 | ASSERT_EQ(compile_result, 0); 775 | // mov eax, imm(false); ret 776 | byte expected[] = {0x48, 0xc7, 0xc0, 0x1f, 0x00, 0x00, 0x00, 0xc3}; 777 | EXPECT_EQUALS_BYTES(buf, expected); 778 | Buffer_make_executable(buf); 779 | uword result = Testing_execute_expr(buf); 780 | ASSERT_EQ(result, Object_false()); 781 | PASS(); 782 | } 783 | 784 | TEST compile_nil(Buffer *buf) { 785 | ASTNode *node = AST_nil(); 786 | int compile_result = Compile_function(buf, node); 787 | ASSERT_EQ(compile_result, 0); 788 | // mov eax, imm(nil); ret 789 | byte expected[] = {0x48, 0xc7, 0xc0, 0x2f, 0x00, 0x00, 0x00, 0xc3}; 790 | EXPECT_EQUALS_BYTES(buf, expected); 791 | Buffer_make_executable(buf); 792 | uword result = Testing_execute_expr(buf); 793 | ASSERT_EQ(result, Object_nil()); 794 | PASS(); 795 | } 796 | 797 | TEST compile_unary_add1(Buffer *buf) { 798 | ASTNode *node = new_unary_call("add1", AST_new_integer(123)); 799 | int compile_result = Compile_function(buf, node); 800 | ASSERT_EQ(compile_result, 0); 801 | // mov rax, imm(123); add rax, imm(1); ret 802 | byte expected[] = {0x48, 0xc7, 0xc0, 0xec, 0x01, 0x00, 0x00, 803 | 0x48, 0x05, 0x04, 0x00, 0x00, 0x00, 0xc3}; 804 | EXPECT_EQUALS_BYTES(buf, expected); 805 | Buffer_make_executable(buf); 806 | uword result = Testing_execute_expr(buf); 807 | ASSERT_EQ(result, Object_encode_integer(124)); 808 | AST_heap_free(node); 809 | PASS(); 810 | } 811 | 812 | TEST compile_unary_add1_nested(Buffer *buf) { 813 | ASTNode *node = 814 | new_unary_call("add1", new_unary_call("add1", AST_new_integer(123))); 815 | int compile_result = Compile_function(buf, node); 816 | ASSERT_EQ(compile_result, 0); 817 | // mov rax, imm(123); add rax, imm(1); add rax, imm(1); ret 818 | byte expected[] = {0x48, 0xc7, 0xc0, 0xec, 0x01, 0x00, 0x00, 819 | 0x48, 0x05, 0x04, 0x00, 0x00, 0x00, 0x48, 820 | 0x05, 0x04, 0x00, 0x00, 0x00, 0xc3}; 821 | EXPECT_EQUALS_BYTES(buf, expected); 822 | Buffer_make_executable(buf); 823 | uword result = Testing_execute_expr(buf); 824 | ASSERT_EQ(result, Object_encode_integer(125)); 825 | AST_heap_free(node); 826 | PASS(); 827 | } 828 | 829 | TEST compile_unary_sub1(Buffer *buf) { 830 | ASTNode *node = new_unary_call("sub1", AST_new_integer(123)); 831 | int compile_result = Compile_function(buf, node); 832 | ASSERT_EQ(compile_result, 0); 833 | // mov rax, imm(123); sub rax, imm(1); ret 834 | byte expected[] = {0x48, 0xc7, 0xc0, 0xec, 0x01, 0x00, 0x00, 835 | 0x48, 0x2d, 0x04, 0x00, 0x00, 0x00, 0xc3}; 836 | EXPECT_EQUALS_BYTES(buf, expected); 837 | Buffer_make_executable(buf); 838 | uword result = Testing_execute_expr(buf); 839 | ASSERT_EQ(result, Object_encode_integer(122)); 840 | AST_heap_free(node); 841 | PASS(); 842 | } 843 | 844 | TEST compile_unary_integer_to_char(Buffer *buf) { 845 | ASTNode *node = new_unary_call("integer->char", AST_new_integer(97)); 846 | int compile_result = Compile_function(buf, node); 847 | ASSERT_EQ(compile_result, 0); 848 | // mov rax, imm(97); shl rax, 6; or rax, 0xf; ret 849 | byte expected[] = {0x48, 0xc7, 0xc0, 0x84, 0x01, 0x00, 0x00, 0x48, 850 | 0xc1, 0xe0, 0x06, 0x48, 0x83, 0xc8, 0xf, 0xc3}; 851 | EXPECT_EQUALS_BYTES(buf, expected); 852 | Buffer_make_executable(buf); 853 | uword result = Testing_execute_expr(buf); 854 | ASSERT_EQ(result, Object_encode_char('a')); 855 | AST_heap_free(node); 856 | PASS(); 857 | } 858 | 859 | TEST compile_unary_char_to_integer(Buffer *buf) { 860 | ASTNode *node = new_unary_call("char->integer", AST_new_char('a')); 861 | int compile_result = Compile_function(buf, node); 862 | ASSERT_EQ(compile_result, 0); 863 | // mov rax, imm('a'); shr rax, 6; ret 864 | byte expected[] = {0x48, 0xc7, 0xc0, 0x0f, 0x61, 0x00, 865 | 0x00, 0x48, 0xc1, 0xe8, 0x06, 0xc3}; 866 | EXPECT_EQUALS_BYTES(buf, expected); 867 | Buffer_make_executable(buf); 868 | uword result = Testing_execute_expr(buf); 869 | ASSERT_EQ(result, Object_encode_integer(97)); 870 | AST_heap_free(node); 871 | PASS(); 872 | } 873 | 874 | TEST compile_unary_nilp_with_nil_returns_true(Buffer *buf) { 875 | ASTNode *node = new_unary_call("nil?", AST_nil()); 876 | int compile_result = Compile_function(buf, node); 877 | ASSERT_EQ(compile_result, 0); 878 | // 0: 48 c7 c0 2f 00 00 00 mov rax,0x2f 879 | // 7: 48 3d 2f 00 00 00 cmp rax,0x0000002f 880 | // d: 48 c7 c0 00 00 00 00 mov rax,0x0 881 | // 14: 0f 94 c0 sete al 882 | // 17: 48 c1 e0 07 shl rax,0x7 883 | // 1b: 48 83 c8 1f or rax,0x1f 884 | byte expected[] = {0x48, 0xc7, 0xc0, 0x2f, 0x00, 0x00, 0x00, 0x48, 885 | 0x3d, 0x2f, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 886 | 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48, 887 | 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 888 | EXPECT_EQUALS_BYTES(buf, expected); 889 | Buffer_make_executable(buf); 890 | uword result = Testing_execute_expr(buf); 891 | ASSERT_EQ(result, Object_true()); 892 | AST_heap_free(node); 893 | PASS(); 894 | } 895 | 896 | TEST compile_unary_nilp_with_non_nil_returns_false(Buffer *buf) { 897 | ASTNode *node = new_unary_call("nil?", AST_new_integer(5)); 898 | int compile_result = Compile_function(buf, node); 899 | ASSERT_EQ(compile_result, 0); 900 | // 0: 48 c7 c0 14 00 00 00 mov rax,0x14 901 | // 7: 48 3d 2f 00 00 00 cmp rax,0x0000002f 902 | // d: 48 c7 c0 00 00 00 00 mov rax,0x0 903 | // 14: 0f 94 c0 sete al 904 | // 17: 48 c1 e0 07 shl rax,0x7 905 | // 1b: 48 83 c8 1f or rax,0x1f 906 | byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48, 907 | 0x3d, 0x2f, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 908 | 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48, 909 | 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 910 | EXPECT_EQUALS_BYTES(buf, expected); 911 | Buffer_make_executable(buf); 912 | uword result = Testing_execute_expr(buf); 913 | ASSERT_EQ(result, Object_false()); 914 | AST_heap_free(node); 915 | PASS(); 916 | } 917 | 918 | TEST compile_unary_zerop_with_zero_returns_true(Buffer *buf) { 919 | ASTNode *node = new_unary_call("zero?", AST_new_integer(0)); 920 | int compile_result = Compile_function(buf, node); 921 | ASSERT_EQ(compile_result, 0); 922 | // 0: 48 c7 c0 00 00 00 00 mov rax,0x0 923 | // 7: 48 3d 00 00 00 00 cmp rax,0x00000000 924 | // d: 48 c7 c0 00 00 00 00 mov rax,0x0 925 | // 14: 0f 94 c0 sete al 926 | // 17: 48 c1 e0 07 shl rax,0x7 927 | // 1b: 48 83 c8 1f or rax,0x1f 928 | byte expected[] = {0x48, 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x48, 929 | 0x3d, 0x00, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 930 | 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48, 931 | 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 932 | EXPECT_EQUALS_BYTES(buf, expected); 933 | Buffer_make_executable(buf); 934 | uword result = Testing_execute_expr(buf); 935 | ASSERT_EQ(result, Object_true()); 936 | AST_heap_free(node); 937 | PASS(); 938 | } 939 | 940 | TEST compile_unary_zerop_with_non_zero_returns_false(Buffer *buf) { 941 | ASTNode *node = new_unary_call("zero?", AST_new_integer(5)); 942 | int compile_result = Compile_function(buf, node); 943 | ASSERT_EQ(compile_result, 0); 944 | // 0: 48 c7 c0 14 00 00 00 mov rax,0x14 945 | // 7: 48 3d 00 00 00 00 cmp rax,0x00000000 946 | // d: 48 c7 c0 00 00 00 00 mov rax,0x0 947 | // 14: 0f 94 c0 sete al 948 | // 17: 48 c1 e0 07 shl rax,0x7 949 | // 1b: 48 83 c8 1f or rax,0x1f 950 | byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48, 951 | 0x3d, 0x00, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 952 | 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48, 953 | 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 954 | EXPECT_EQUALS_BYTES(buf, expected); 955 | Buffer_make_executable(buf); 956 | uword result = Testing_execute_expr(buf); 957 | ASSERT_EQ(result, Object_false()); 958 | AST_heap_free(node); 959 | PASS(); 960 | } 961 | 962 | TEST compile_unary_not_with_false_returns_true(Buffer *buf) { 963 | ASTNode *node = new_unary_call("not", AST_new_bool(false)); 964 | int compile_result = Compile_function(buf, node); 965 | ASSERT_EQ(compile_result, 0); 966 | // 0: 48 c7 c0 1f 00 00 00 mov rax,0x1f 967 | // 7: 48 3d 1f 00 00 00 cmp rax,0x0000001f 968 | // d: 48 c7 c0 00 00 00 00 mov rax,0x0 969 | // 14: 0f 94 c0 sete al 970 | // 17: 48 c1 e0 07 shl rax,0x7 971 | // 1b: 48 83 c8 1f or rax,0x1f 972 | byte expected[] = {0x48, 0xc7, 0xc0, 0x1f, 0x00, 0x00, 0x00, 0x48, 973 | 0x3d, 0x1f, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 974 | 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48, 975 | 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 976 | EXPECT_EQUALS_BYTES(buf, expected); 977 | Buffer_make_executable(buf); 978 | uword result = Testing_execute_expr(buf); 979 | ASSERT_EQ(result, Object_true()); 980 | AST_heap_free(node); 981 | PASS(); 982 | } 983 | 984 | TEST compile_unary_not_with_non_false_returns_false(Buffer *buf) { 985 | ASTNode *node = new_unary_call("not", AST_new_integer(5)); 986 | int compile_result = Compile_function(buf, node); 987 | ASSERT_EQ(compile_result, 0); 988 | // 0: 48 c7 c0 14 00 00 00 mov rax,0x14 989 | // 7: 48 3d 1f 00 00 00 cmp rax,0x0000001f 990 | // d: 48 c7 c0 00 00 00 00 mov rax,0x0 991 | // 14: 0f 94 c0 sete al 992 | // 17: 48 c1 e0 07 shl rax,0x7 993 | // 1b: 48 83 c8 1f or rax,0x1f 994 | byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48, 995 | 0x3d, 0x1f, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 996 | 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 0x48, 997 | 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 998 | EXPECT_EQUALS_BYTES(buf, expected); 999 | Buffer_make_executable(buf); 1000 | uword result = Testing_execute_expr(buf); 1001 | ASSERT_EQ(result, Object_false()); 1002 | AST_heap_free(node); 1003 | PASS(); 1004 | } 1005 | 1006 | TEST compile_unary_integerp_with_integer_returns_true(Buffer *buf) { 1007 | ASTNode *node = new_unary_call("integer?", AST_new_integer(5)); 1008 | int compile_result = Compile_function(buf, node); 1009 | ASSERT_EQ(compile_result, 0); 1010 | // 0: 48 c7 c0 14 00 00 00 mov rax,0x14 1011 | // 7: 48 83 e0 03 and rax,0x3 1012 | // b: 48 3d 00 00 00 00 cmp rax,0x00000000 1013 | // 11: 48 c7 c0 00 00 00 00 mov rax,0x0 1014 | // 18: 0f 94 c0 sete al 1015 | // 1b: 48 c1 e0 07 shl rax,0x7 1016 | // 1f: 48 83 c8 1f or rax,0x1f 1017 | byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48, 0x83, 1018 | 0xe0, 0x03, 0x48, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x48, 1019 | 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 1020 | 0x48, 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 1021 | EXPECT_EQUALS_BYTES(buf, expected); 1022 | Buffer_make_executable(buf); 1023 | uword result = Testing_execute_expr(buf); 1024 | ASSERT_EQ(result, Object_true()); 1025 | AST_heap_free(node); 1026 | PASS(); 1027 | } 1028 | 1029 | TEST compile_unary_integerp_with_non_integer_returns_false(Buffer *buf) { 1030 | ASTNode *node = new_unary_call("integer?", AST_nil()); 1031 | int compile_result = Compile_function(buf, node); 1032 | ASSERT_EQ(compile_result, 0); 1033 | // 0: 48 c7 c0 2f 00 00 00 mov rax,0x2f 1034 | // 7: 48 83 e0 03 and rax,0x3 1035 | // b: 48 3d 00 00 00 00 cmp rax,0x00000000 1036 | // 11: 48 c7 c0 00 00 00 00 mov rax,0x0 1037 | // 18: 0f 94 c0 sete al 1038 | // 1b: 48 c1 e0 07 shl rax,0x7 1039 | // 1f: 48 83 c8 1f or rax,0x1f 1040 | byte expected[] = {0x48, 0xc7, 0xc0, 0x2f, 0x00, 0x00, 0x00, 0x48, 0x83, 1041 | 0xe0, 0x03, 0x48, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x48, 1042 | 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 1043 | 0x48, 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 1044 | EXPECT_EQUALS_BYTES(buf, expected); 1045 | Buffer_make_executable(buf); 1046 | uword result = Testing_execute_expr(buf); 1047 | ASSERT_EQ(result, Object_false()); 1048 | AST_heap_free(node); 1049 | PASS(); 1050 | } 1051 | 1052 | TEST compile_unary_booleanp_with_boolean_returns_true(Buffer *buf) { 1053 | ASTNode *node = new_unary_call("boolean?", AST_new_bool(true)); 1054 | int compile_result = Compile_function(buf, node); 1055 | ASSERT_EQ(compile_result, 0); 1056 | // 0: 48 c7 c0 9f 00 00 00 mov rax,0x9f 1057 | // 7: 48 83 e0 3f and rax,0x3f 1058 | // b: 48 3d 1f 00 00 00 cmp rax,0x0000001f 1059 | // 11: 48 c7 c0 00 00 00 00 mov rax,0x0 1060 | // 18: 0f 94 c0 sete al 1061 | // 1b: 48 c1 e0 07 shl rax,0x7 1062 | // 1f: 48 83 c8 1f or rax,0x1f 1063 | byte expected[] = {0x48, 0xc7, 0xc0, 0x9f, 0x00, 0x00, 0x00, 0x48, 0x83, 1064 | 0xe0, 0x3f, 0x48, 0x3d, 0x1f, 0x00, 0x00, 0x00, 0x48, 1065 | 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 1066 | 0x48, 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 1067 | EXPECT_EQUALS_BYTES(buf, expected); 1068 | Buffer_make_executable(buf); 1069 | uword result = Testing_execute_expr(buf); 1070 | ASSERT_EQ(result, Object_true()); 1071 | AST_heap_free(node); 1072 | PASS(); 1073 | } 1074 | 1075 | TEST compile_unary_booleanp_with_non_boolean_returns_false(Buffer *buf) { 1076 | ASTNode *node = new_unary_call("boolean?", AST_new_integer(5)); 1077 | int compile_result = Compile_function(buf, node); 1078 | ASSERT_EQ(compile_result, 0); 1079 | // 0: 48 c7 c0 14 00 00 00 mov rax,0x14 1080 | // 7: 48 83 e0 3f and rax,0x3f 1081 | // b: 48 3d 1f 00 00 00 cmp rax,0x0000001f 1082 | // 11: 48 c7 c0 00 00 00 00 mov rax,0x0 1083 | // 18: 0f 94 c0 sete al 1084 | // 1b: 48 c1 e0 07 shl rax,0x7 1085 | // 1f: 48 83 c8 1f or rax,0x1f 1086 | byte expected[] = {0x48, 0xc7, 0xc0, 0x14, 0x00, 0x00, 0x00, 0x48, 0x83, 1087 | 0xe0, 0x3f, 0x48, 0x3d, 0x1f, 0x00, 0x00, 0x00, 0x48, 1088 | 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x94, 0xc0, 1089 | 0x48, 0xc1, 0xe0, 0x07, 0x48, 0x83, 0xc8, 0x1f}; 1090 | EXPECT_EQUALS_BYTES(buf, expected); 1091 | Buffer_make_executable(buf); 1092 | uword result = Testing_execute_expr(buf); 1093 | ASSERT_EQ(result, Object_false()); 1094 | AST_heap_free(node); 1095 | PASS(); 1096 | } 1097 | 1098 | SUITE(object_tests) { 1099 | RUN_TEST(encode_positive_integer); 1100 | RUN_TEST(encode_negative_integer); 1101 | RUN_TEST(encode_char); 1102 | RUN_TEST(decode_char); 1103 | RUN_TEST(encode_bool); 1104 | RUN_TEST(decode_bool); 1105 | RUN_TEST(address); 1106 | } 1107 | 1108 | SUITE(ast_tests) { 1109 | RUN_TEST(ast_new_pair); 1110 | RUN_TEST(ast_pair_car_returns_car); 1111 | RUN_TEST(ast_pair_cdr_returns_cdr); 1112 | RUN_TEST(ast_new_symbol); 1113 | } 1114 | 1115 | SUITE(buffer_tests) { 1116 | RUN_BUFFER_TEST(buffer_write8_increases_length); 1117 | RUN_TEST(buffer_write8_expands_buffer); 1118 | RUN_TEST(buffer_write32_expands_buffer); 1119 | RUN_BUFFER_TEST(buffer_write32_writes_little_endian); 1120 | } 1121 | 1122 | SUITE(compiler_tests) { 1123 | RUN_BUFFER_TEST(compile_positive_integer); 1124 | RUN_BUFFER_TEST(compile_negative_integer); 1125 | RUN_BUFFER_TEST(compile_char); 1126 | RUN_BUFFER_TEST(compile_true); 1127 | RUN_BUFFER_TEST(compile_false); 1128 | RUN_BUFFER_TEST(compile_nil); 1129 | RUN_BUFFER_TEST(compile_unary_add1); 1130 | RUN_BUFFER_TEST(compile_unary_add1_nested); 1131 | RUN_BUFFER_TEST(compile_unary_sub1); 1132 | RUN_BUFFER_TEST(compile_unary_integer_to_char); 1133 | RUN_BUFFER_TEST(compile_unary_char_to_integer); 1134 | RUN_BUFFER_TEST(compile_unary_nilp_with_nil_returns_true); 1135 | RUN_BUFFER_TEST(compile_unary_nilp_with_non_nil_returns_false); 1136 | RUN_BUFFER_TEST(compile_unary_zerop_with_zero_returns_true); 1137 | RUN_BUFFER_TEST(compile_unary_zerop_with_non_zero_returns_false); 1138 | RUN_BUFFER_TEST(compile_unary_not_with_false_returns_true); 1139 | RUN_BUFFER_TEST(compile_unary_not_with_non_false_returns_false); 1140 | RUN_BUFFER_TEST(compile_unary_integerp_with_integer_returns_true); 1141 | RUN_BUFFER_TEST(compile_unary_integerp_with_non_integer_returns_false); 1142 | RUN_BUFFER_TEST(compile_unary_booleanp_with_boolean_returns_true); 1143 | RUN_BUFFER_TEST(compile_unary_booleanp_with_non_boolean_returns_false); 1144 | } 1145 | 1146 | // End Tests 1147 | 1148 | GREATEST_MAIN_DEFS(); 1149 | 1150 | int main(int argc, char **argv) { 1151 | GREATEST_MAIN_BEGIN(); 1152 | RUN_SUITE(object_tests); 1153 | RUN_SUITE(ast_tests); 1154 | RUN_SUITE(buffer_tests); 1155 | RUN_SUITE(compiler_tests); 1156 | GREATEST_MAIN_END(); 1157 | } 1158 | -------------------------------------------------------------------------------- /greatest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2019 Scott Vokes 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef GREATEST_H 18 | #define GREATEST_H 19 | 20 | #if defined(__cplusplus) && !defined(GREATEST_NO_EXTERN_CPLUSPLUS) 21 | extern "C" { 22 | #endif 23 | 24 | /* 1.4.2 */ 25 | #define GREATEST_VERSION_MAJOR 1 26 | #define GREATEST_VERSION_MINOR 4 27 | #define GREATEST_VERSION_PATCH 2 28 | 29 | /* A unit testing system for C, contained in 1 file. 30 | * It doesn't use dynamic allocation or depend on anything 31 | * beyond ANSI C89. 32 | * 33 | * An up-to-date version can be found at: 34 | * https://github.com/silentbicycle/greatest/ 35 | */ 36 | 37 | 38 | /********************************************************************* 39 | * Minimal test runner template 40 | *********************************************************************/ 41 | #if 0 42 | 43 | #include "greatest.h" 44 | 45 | TEST foo_should_foo(void) { 46 | PASS(); 47 | } 48 | 49 | static void setup_cb(void *data) { 50 | printf("setup callback for each test case\n"); 51 | } 52 | 53 | static void teardown_cb(void *data) { 54 | printf("teardown callback for each test case\n"); 55 | } 56 | 57 | SUITE(suite) { 58 | /* Optional setup/teardown callbacks which will be run before/after 59 | * every test case. If using a test suite, they will be cleared when 60 | * the suite finishes. */ 61 | SET_SETUP(setup_cb, voidp_to_callback_data); 62 | SET_TEARDOWN(teardown_cb, voidp_to_callback_data); 63 | 64 | RUN_TEST(foo_should_foo); 65 | } 66 | 67 | /* Add definitions that need to be in the test runner's main file. */ 68 | GREATEST_MAIN_DEFS(); 69 | 70 | /* Set up, run suite(s) of tests, report pass/fail/skip stats. */ 71 | int run_tests(void) { 72 | GREATEST_INIT(); /* init. greatest internals */ 73 | /* List of suites to run (if any). */ 74 | RUN_SUITE(suite); 75 | 76 | /* Tests can also be run directly, without using test suites. */ 77 | RUN_TEST(foo_should_foo); 78 | 79 | GREATEST_PRINT_REPORT(); /* display results */ 80 | return greatest_all_passed(); 81 | } 82 | 83 | /* main(), for a standalone command-line test runner. 84 | * This replaces run_tests above, and adds command line option 85 | * handling and exiting with a pass/fail status. */ 86 | int main(int argc, char **argv) { 87 | GREATEST_MAIN_BEGIN(); /* init & parse command-line args */ 88 | RUN_SUITE(suite); 89 | GREATEST_MAIN_END(); /* display results */ 90 | } 91 | 92 | #endif 93 | /*********************************************************************/ 94 | 95 | 96 | #include 97 | #include 98 | #include 99 | #include 100 | 101 | /*********** 102 | * Options * 103 | ***********/ 104 | 105 | /* Default column width for non-verbose output. */ 106 | #ifndef GREATEST_DEFAULT_WIDTH 107 | #define GREATEST_DEFAULT_WIDTH 72 108 | #endif 109 | 110 | /* FILE *, for test logging. */ 111 | #ifndef GREATEST_STDOUT 112 | #define GREATEST_STDOUT stdout 113 | #endif 114 | 115 | /* Remove GREATEST_ prefix from most commonly used symbols? */ 116 | #ifndef GREATEST_USE_ABBREVS 117 | #define GREATEST_USE_ABBREVS 1 118 | #endif 119 | 120 | /* Set to 0 to disable all use of setjmp/longjmp. */ 121 | #ifndef GREATEST_USE_LONGJMP 122 | #define GREATEST_USE_LONGJMP 1 123 | #endif 124 | 125 | /* Make it possible to replace fprintf with another 126 | * function with the same interface. */ 127 | #ifndef GREATEST_FPRINTF 128 | #define GREATEST_FPRINTF fprintf 129 | #endif 130 | 131 | #if GREATEST_USE_LONGJMP 132 | #include 133 | #endif 134 | 135 | /* Set to 0 to disable all use of time.h / clock(). */ 136 | #ifndef GREATEST_USE_TIME 137 | #define GREATEST_USE_TIME 1 138 | #endif 139 | 140 | #if GREATEST_USE_TIME 141 | #include 142 | #endif 143 | 144 | /* Floating point type, for ASSERT_IN_RANGE. */ 145 | #ifndef GREATEST_FLOAT 146 | #define GREATEST_FLOAT double 147 | #define GREATEST_FLOAT_FMT "%g" 148 | #endif 149 | 150 | /* Size of buffer for test name + optional '_' separator and suffix */ 151 | #ifndef GREATEST_TESTNAME_BUF_SIZE 152 | #define GREATEST_TESTNAME_BUF_SIZE 128 153 | #endif 154 | 155 | 156 | /********* 157 | * Types * 158 | *********/ 159 | 160 | /* Info for the current running suite. */ 161 | typedef struct greatest_suite_info { 162 | unsigned int tests_run; 163 | unsigned int passed; 164 | unsigned int failed; 165 | unsigned int skipped; 166 | 167 | #if GREATEST_USE_TIME 168 | /* timers, pre/post running suite and individual tests */ 169 | clock_t pre_suite; 170 | clock_t post_suite; 171 | clock_t pre_test; 172 | clock_t post_test; 173 | #endif 174 | } greatest_suite_info; 175 | 176 | /* Type for a suite function. */ 177 | typedef void greatest_suite_cb(void); 178 | 179 | /* Types for setup/teardown callbacks. If non-NULL, these will be run 180 | * and passed the pointer to their additional data. */ 181 | typedef void greatest_setup_cb(void *udata); 182 | typedef void greatest_teardown_cb(void *udata); 183 | 184 | /* Type for an equality comparison between two pointers of the same type. 185 | * Should return non-0 if equal, otherwise 0. 186 | * UDATA is a closure value, passed through from ASSERT_EQUAL_T[m]. */ 187 | typedef int greatest_equal_cb(const void *expd, const void *got, void *udata); 188 | 189 | /* Type for a callback that prints a value pointed to by T. 190 | * Return value has the same meaning as printf's. 191 | * UDATA is a closure value, passed through from ASSERT_EQUAL_T[m]. */ 192 | typedef int greatest_printf_cb(const void *t, void *udata); 193 | 194 | /* Callbacks for an arbitrary type; needed for type-specific 195 | * comparisons via GREATEST_ASSERT_EQUAL_T[m].*/ 196 | typedef struct greatest_type_info { 197 | greatest_equal_cb *equal; 198 | greatest_printf_cb *print; 199 | } greatest_type_info; 200 | 201 | typedef struct greatest_memory_cmp_env { 202 | const unsigned char *exp; 203 | const unsigned char *got; 204 | size_t size; 205 | } greatest_memory_cmp_env; 206 | 207 | /* Callbacks for string and raw memory types. */ 208 | extern greatest_type_info greatest_type_info_string; 209 | extern greatest_type_info greatest_type_info_memory; 210 | 211 | typedef enum { 212 | GREATEST_FLAG_FIRST_FAIL = 0x01, 213 | GREATEST_FLAG_LIST_ONLY = 0x02, 214 | GREATEST_FLAG_ABORT_ON_FAIL = 0x04 215 | } greatest_flag_t; 216 | 217 | /* Internal state for a PRNG, used to shuffle test order. */ 218 | struct greatest_prng { 219 | unsigned char random_order; /* use random ordering? */ 220 | unsigned char initialized; /* is random ordering initialized? */ 221 | unsigned char pad_0[6]; 222 | unsigned long state; /* PRNG state */ 223 | unsigned long count; /* how many tests, this pass */ 224 | unsigned long count_ceil; /* total number of tests */ 225 | unsigned long count_run; /* total tests run */ 226 | unsigned long a; /* LCG multiplier */ 227 | unsigned long c; /* LCG increment */ 228 | unsigned long m; /* LCG modulus, based on count_ceil */ 229 | }; 230 | 231 | /* Struct containing all test runner state. */ 232 | typedef struct greatest_run_info { 233 | unsigned char flags; 234 | unsigned char verbosity; 235 | unsigned char pad_0[2]; 236 | 237 | unsigned int tests_run; /* total test count */ 238 | 239 | /* currently running test suite */ 240 | greatest_suite_info suite; 241 | 242 | /* overall pass/fail/skip counts */ 243 | unsigned int passed; 244 | unsigned int failed; 245 | unsigned int skipped; 246 | unsigned int assertions; 247 | 248 | /* info to print about the most recent failure */ 249 | unsigned int fail_line; 250 | unsigned int pad_1; 251 | const char *fail_file; 252 | const char *msg; 253 | 254 | /* current setup/teardown hooks and userdata */ 255 | greatest_setup_cb *setup; 256 | void *setup_udata; 257 | greatest_teardown_cb *teardown; 258 | void *teardown_udata; 259 | 260 | /* formatting info for ".....s...F"-style output */ 261 | unsigned int col; 262 | unsigned int width; 263 | 264 | /* only run a specific suite or test */ 265 | const char *suite_filter; 266 | const char *test_filter; 267 | const char *test_exclude; 268 | const char *name_suffix; /* print suffix with test name */ 269 | char name_buf[GREATEST_TESTNAME_BUF_SIZE]; 270 | 271 | struct greatest_prng prng[2]; /* 0: suites, 1: tests */ 272 | 273 | #if GREATEST_USE_TIME 274 | /* overall timers */ 275 | clock_t begin; 276 | clock_t end; 277 | #endif 278 | 279 | #if GREATEST_USE_LONGJMP 280 | int pad_jmp_buf; 281 | unsigned char pad_2[4]; 282 | jmp_buf jump_dest; 283 | #endif 284 | } greatest_run_info; 285 | 286 | struct greatest_report_t { 287 | /* overall pass/fail/skip counts */ 288 | unsigned int passed; 289 | unsigned int failed; 290 | unsigned int skipped; 291 | unsigned int assertions; 292 | }; 293 | 294 | /* Global var for the current testing context. 295 | * Initialized by GREATEST_MAIN_DEFS(). */ 296 | extern greatest_run_info greatest_info; 297 | 298 | /* Type for ASSERT_ENUM_EQ's ENUM_STR argument. */ 299 | typedef const char *greatest_enum_str_fun(int value); 300 | 301 | 302 | /********************** 303 | * Exported functions * 304 | **********************/ 305 | 306 | /* These are used internally by greatest macros. */ 307 | int greatest_test_pre(const char *name); 308 | void greatest_test_post(int res); 309 | int greatest_do_assert_equal_t(const void *expd, const void *got, 310 | greatest_type_info *type_info, void *udata); 311 | void greatest_prng_init_first_pass(int id); 312 | int greatest_prng_init_second_pass(int id, unsigned long seed); 313 | void greatest_prng_step(int id); 314 | 315 | /* These are part of the public greatest API. */ 316 | void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata); 317 | void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata); 318 | void GREATEST_INIT(void); 319 | void GREATEST_PRINT_REPORT(void); 320 | int greatest_all_passed(void); 321 | void greatest_set_suite_filter(const char *filter); 322 | void greatest_set_test_filter(const char *filter); 323 | void greatest_set_test_exclude(const char *filter); 324 | void greatest_stop_at_first_fail(void); 325 | void greatest_abort_on_fail(void); 326 | void greatest_list_only(void); 327 | void greatest_get_report(struct greatest_report_t *report); 328 | unsigned int greatest_get_verbosity(void); 329 | void greatest_set_verbosity(unsigned int verbosity); 330 | void greatest_set_flag(greatest_flag_t flag); 331 | void greatest_set_test_suffix(const char *suffix); 332 | 333 | 334 | /******************** 335 | * Language Support * 336 | ********************/ 337 | 338 | /* If __VA_ARGS__ (C99) is supported, allow parametric testing 339 | * without needing to manually manage the argument struct. */ 340 | #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 19901L) || \ 341 | (defined(_MSC_VER) && _MSC_VER >= 1800) 342 | #define GREATEST_VA_ARGS 343 | #endif 344 | 345 | 346 | /********** 347 | * Macros * 348 | **********/ 349 | 350 | /* Define a suite. (The duplication is intentional -- it eliminates 351 | * a warning from -Wmissing-declarations.) */ 352 | #define GREATEST_SUITE(NAME) void NAME(void); void NAME(void) 353 | 354 | /* Declare a suite, provided by another compilation unit. */ 355 | #define GREATEST_SUITE_EXTERN(NAME) void NAME(void) 356 | 357 | /* Start defining a test function. 358 | * The arguments are not included, to allow parametric testing. */ 359 | #define GREATEST_TEST static enum greatest_test_res 360 | 361 | /* PASS/FAIL/SKIP result from a test. Used internally. */ 362 | typedef enum greatest_test_res { 363 | GREATEST_TEST_RES_PASS = 0, 364 | GREATEST_TEST_RES_FAIL = -1, 365 | GREATEST_TEST_RES_SKIP = 1 366 | } greatest_test_res; 367 | 368 | /* Run a suite. */ 369 | #define GREATEST_RUN_SUITE(S_NAME) greatest_run_suite(S_NAME, #S_NAME) 370 | 371 | /* Run a test in the current suite. */ 372 | #define GREATEST_RUN_TEST(TEST) \ 373 | do { \ 374 | if (greatest_test_pre(#TEST) == 1) { \ 375 | enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \ 376 | if (res == GREATEST_TEST_RES_PASS) { \ 377 | res = TEST(); \ 378 | } \ 379 | greatest_test_post(res); \ 380 | } \ 381 | } while (0) 382 | 383 | /* Ignore a test, don't warn about it being unused. */ 384 | #define GREATEST_IGNORE_TEST(TEST) (void)TEST 385 | 386 | /* Run a test in the current suite with one void * argument, 387 | * which can be a pointer to a struct with multiple arguments. */ 388 | #define GREATEST_RUN_TEST1(TEST, ENV) \ 389 | do { \ 390 | if (greatest_test_pre(#TEST) == 1) { \ 391 | enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \ 392 | if (res == GREATEST_TEST_RES_PASS) { \ 393 | res = TEST(ENV); \ 394 | } \ 395 | greatest_test_post(res); \ 396 | } \ 397 | } while (0) 398 | 399 | #ifdef GREATEST_VA_ARGS 400 | #define GREATEST_RUN_TESTp(TEST, ...) \ 401 | do { \ 402 | if (greatest_test_pre(#TEST) == 1) { \ 403 | enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \ 404 | if (res == GREATEST_TEST_RES_PASS) { \ 405 | res = TEST(__VA_ARGS__); \ 406 | } \ 407 | greatest_test_post(res); \ 408 | } \ 409 | } while (0) 410 | #endif 411 | 412 | 413 | /* Check if the test runner is in verbose mode. */ 414 | #define GREATEST_IS_VERBOSE() ((greatest_info.verbosity) > 0) 415 | #define GREATEST_LIST_ONLY() \ 416 | (greatest_info.flags & GREATEST_FLAG_LIST_ONLY) 417 | #define GREATEST_FIRST_FAIL() \ 418 | (greatest_info.flags & GREATEST_FLAG_FIRST_FAIL) 419 | #define GREATEST_ABORT_ON_FAIL() \ 420 | (greatest_info.flags & GREATEST_FLAG_ABORT_ON_FAIL) 421 | #define GREATEST_FAILURE_ABORT() \ 422 | (GREATEST_FIRST_FAIL() && \ 423 | (greatest_info.suite.failed > 0 || greatest_info.failed > 0)) 424 | 425 | /* Message-less forms of tests defined below. */ 426 | #define GREATEST_PASS() GREATEST_PASSm(NULL) 427 | #define GREATEST_FAIL() GREATEST_FAILm(NULL) 428 | #define GREATEST_SKIP() GREATEST_SKIPm(NULL) 429 | #define GREATEST_ASSERT(COND) \ 430 | GREATEST_ASSERTm(#COND, COND) 431 | #define GREATEST_ASSERT_OR_LONGJMP(COND) \ 432 | GREATEST_ASSERT_OR_LONGJMPm(#COND, COND) 433 | #define GREATEST_ASSERT_FALSE(COND) \ 434 | GREATEST_ASSERT_FALSEm(#COND, COND) 435 | #define GREATEST_ASSERT_EQ(EXP, GOT) \ 436 | GREATEST_ASSERT_EQm(#EXP " != " #GOT, EXP, GOT) 437 | #define GREATEST_ASSERT_EQ_FMT(EXP, GOT, FMT) \ 438 | GREATEST_ASSERT_EQ_FMTm(#EXP " != " #GOT, EXP, GOT, FMT) 439 | #define GREATEST_ASSERT_IN_RANGE(EXP, GOT, TOL) \ 440 | GREATEST_ASSERT_IN_RANGEm(#EXP " != " #GOT " +/- " #TOL, EXP, GOT, TOL) 441 | #define GREATEST_ASSERT_EQUAL_T(EXP, GOT, TYPE_INFO, UDATA) \ 442 | GREATEST_ASSERT_EQUAL_Tm(#EXP " != " #GOT, EXP, GOT, TYPE_INFO, UDATA) 443 | #define GREATEST_ASSERT_STR_EQ(EXP, GOT) \ 444 | GREATEST_ASSERT_STR_EQm(#EXP " != " #GOT, EXP, GOT) 445 | #define GREATEST_ASSERT_STRN_EQ(EXP, GOT, SIZE) \ 446 | GREATEST_ASSERT_STRN_EQm(#EXP " != " #GOT, EXP, GOT, SIZE) 447 | #define GREATEST_ASSERT_MEM_EQ(EXP, GOT, SIZE) \ 448 | GREATEST_ASSERT_MEM_EQm(#EXP " != " #GOT, EXP, GOT, SIZE) 449 | #define GREATEST_ASSERT_ENUM_EQ(EXP, GOT, ENUM_STR) \ 450 | GREATEST_ASSERT_ENUM_EQm(#EXP " != " #GOT, EXP, GOT, ENUM_STR) 451 | 452 | /* The following forms take an additional message argument first, 453 | * to be displayed by the test runner. */ 454 | 455 | /* Fail if a condition is not true, with message. */ 456 | #define GREATEST_ASSERTm(MSG, COND) \ 457 | do { \ 458 | greatest_info.assertions++; \ 459 | if (!(COND)) { GREATEST_FAILm(MSG); } \ 460 | } while (0) 461 | 462 | /* Fail if a condition is not true, longjmping out of test. */ 463 | #define GREATEST_ASSERT_OR_LONGJMPm(MSG, COND) \ 464 | do { \ 465 | greatest_info.assertions++; \ 466 | if (!(COND)) { GREATEST_FAIL_WITH_LONGJMPm(MSG); } \ 467 | } while (0) 468 | 469 | /* Fail if a condition is not false, with message. */ 470 | #define GREATEST_ASSERT_FALSEm(MSG, COND) \ 471 | do { \ 472 | greatest_info.assertions++; \ 473 | if ((COND)) { GREATEST_FAILm(MSG); } \ 474 | } while (0) 475 | 476 | /* Fail if EXP != GOT (equality comparison by ==). */ 477 | #define GREATEST_ASSERT_EQm(MSG, EXP, GOT) \ 478 | do { \ 479 | greatest_info.assertions++; \ 480 | if ((EXP) != (GOT)) { GREATEST_FAILm(MSG); } \ 481 | } while (0) 482 | 483 | /* Fail if EXP != GOT (equality comparison by ==). 484 | * Warning: FMT, EXP, and GOT will be evaluated more 485 | * than once on failure. */ 486 | #define GREATEST_ASSERT_EQ_FMTm(MSG, EXP, GOT, FMT) \ 487 | do { \ 488 | greatest_info.assertions++; \ 489 | if ((EXP) != (GOT)) { \ 490 | GREATEST_FPRINTF(GREATEST_STDOUT, "\nExpected: "); \ 491 | GREATEST_FPRINTF(GREATEST_STDOUT, FMT, EXP); \ 492 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n Got: "); \ 493 | GREATEST_FPRINTF(GREATEST_STDOUT, FMT, GOT); \ 494 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ 495 | GREATEST_FAILm(MSG); \ 496 | } \ 497 | } while (0) 498 | 499 | /* Fail if EXP is not equal to GOT, printing enum IDs. */ 500 | #define GREATEST_ASSERT_ENUM_EQm(MSG, EXP, GOT, ENUM_STR) \ 501 | do { \ 502 | int greatest_EXP = (int)(EXP); \ 503 | int greatest_GOT = (int)(GOT); \ 504 | greatest_enum_str_fun *greatest_ENUM_STR = ENUM_STR; \ 505 | if (greatest_EXP != greatest_GOT) { \ 506 | GREATEST_FPRINTF(GREATEST_STDOUT, "\nExpected: %s", \ 507 | greatest_ENUM_STR(greatest_EXP)); \ 508 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n Got: %s\n", \ 509 | greatest_ENUM_STR(greatest_GOT)); \ 510 | GREATEST_FAILm(MSG); \ 511 | } \ 512 | } while (0) \ 513 | 514 | /* Fail if GOT not in range of EXP +|- TOL. */ 515 | #define GREATEST_ASSERT_IN_RANGEm(MSG, EXP, GOT, TOL) \ 516 | do { \ 517 | GREATEST_FLOAT greatest_EXP = (EXP); \ 518 | GREATEST_FLOAT greatest_GOT = (GOT); \ 519 | GREATEST_FLOAT greatest_TOL = (TOL); \ 520 | greatest_info.assertions++; \ 521 | if ((greatest_EXP > greatest_GOT && \ 522 | greatest_EXP - greatest_GOT > greatest_TOL) || \ 523 | (greatest_EXP < greatest_GOT && \ 524 | greatest_GOT - greatest_EXP > greatest_TOL)) { \ 525 | GREATEST_FPRINTF(GREATEST_STDOUT, \ 526 | "\nExpected: " GREATEST_FLOAT_FMT \ 527 | " +/- " GREATEST_FLOAT_FMT \ 528 | "\n Got: " GREATEST_FLOAT_FMT \ 529 | "\n", \ 530 | greatest_EXP, greatest_TOL, greatest_GOT); \ 531 | GREATEST_FAILm(MSG); \ 532 | } \ 533 | } while (0) 534 | 535 | /* Fail if EXP is not equal to GOT, according to strcmp. */ 536 | #define GREATEST_ASSERT_STR_EQm(MSG, EXP, GOT) \ 537 | do { \ 538 | GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, \ 539 | &greatest_type_info_string, NULL); \ 540 | } while (0) \ 541 | 542 | /* Fail if EXP is not equal to GOT, according to strncmp. */ 543 | #define GREATEST_ASSERT_STRN_EQm(MSG, EXP, GOT, SIZE) \ 544 | do { \ 545 | size_t size = SIZE; \ 546 | GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, \ 547 | &greatest_type_info_string, &size); \ 548 | } while (0) \ 549 | 550 | /* Fail if EXP is not equal to GOT, according to memcmp. */ 551 | #define GREATEST_ASSERT_MEM_EQm(MSG, EXP, GOT, SIZE) \ 552 | do { \ 553 | greatest_memory_cmp_env env; \ 554 | env.exp = (const unsigned char *)EXP; \ 555 | env.got = (const unsigned char *)GOT; \ 556 | env.size = SIZE; \ 557 | GREATEST_ASSERT_EQUAL_Tm(MSG, env.exp, env.got, \ 558 | &greatest_type_info_memory, &env); \ 559 | } while (0) \ 560 | 561 | /* Fail if EXP is not equal to GOT, according to a comparison 562 | * callback in TYPE_INFO. If they are not equal, optionally use a 563 | * print callback in TYPE_INFO to print them. */ 564 | #define GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, TYPE_INFO, UDATA) \ 565 | do { \ 566 | greatest_type_info *type_info = (TYPE_INFO); \ 567 | greatest_info.assertions++; \ 568 | if (!greatest_do_assert_equal_t(EXP, GOT, \ 569 | type_info, UDATA)) { \ 570 | if (type_info == NULL || type_info->equal == NULL) { \ 571 | GREATEST_FAILm("type_info->equal callback missing!"); \ 572 | } else { \ 573 | GREATEST_FAILm(MSG); \ 574 | } \ 575 | } \ 576 | } while (0) \ 577 | 578 | /* Pass. */ 579 | #define GREATEST_PASSm(MSG) \ 580 | do { \ 581 | greatest_info.msg = MSG; \ 582 | return GREATEST_TEST_RES_PASS; \ 583 | } while (0) 584 | 585 | /* Fail. */ 586 | #define GREATEST_FAILm(MSG) \ 587 | do { \ 588 | greatest_info.fail_file = __FILE__; \ 589 | greatest_info.fail_line = __LINE__; \ 590 | greatest_info.msg = MSG; \ 591 | if (GREATEST_ABORT_ON_FAIL()) { abort(); } \ 592 | return GREATEST_TEST_RES_FAIL; \ 593 | } while (0) 594 | 595 | /* Optional GREATEST_FAILm variant that longjmps. */ 596 | #if GREATEST_USE_LONGJMP 597 | #define GREATEST_FAIL_WITH_LONGJMP() GREATEST_FAIL_WITH_LONGJMPm(NULL) 598 | #define GREATEST_FAIL_WITH_LONGJMPm(MSG) \ 599 | do { \ 600 | greatest_info.fail_file = __FILE__; \ 601 | greatest_info.fail_line = __LINE__; \ 602 | greatest_info.msg = MSG; \ 603 | longjmp(greatest_info.jump_dest, GREATEST_TEST_RES_FAIL); \ 604 | } while (0) 605 | #endif 606 | 607 | /* Skip the current test. */ 608 | #define GREATEST_SKIPm(MSG) \ 609 | do { \ 610 | greatest_info.msg = MSG; \ 611 | return GREATEST_TEST_RES_SKIP; \ 612 | } while (0) 613 | 614 | /* Check the result of a subfunction using ASSERT, etc. */ 615 | #define GREATEST_CHECK_CALL(RES) \ 616 | do { \ 617 | enum greatest_test_res greatest_RES = RES; \ 618 | if (greatest_RES != GREATEST_TEST_RES_PASS) { \ 619 | return greatest_RES; \ 620 | } \ 621 | } while (0) \ 622 | 623 | #if GREATEST_USE_TIME 624 | #define GREATEST_SET_TIME(NAME) \ 625 | NAME = clock(); \ 626 | if (NAME == (clock_t) -1) { \ 627 | GREATEST_FPRINTF(GREATEST_STDOUT, \ 628 | "clock error: %s\n", #NAME); \ 629 | exit(EXIT_FAILURE); \ 630 | } 631 | 632 | #define GREATEST_CLOCK_DIFF(C1, C2) \ 633 | GREATEST_FPRINTF(GREATEST_STDOUT, " (%lu ticks, %.3f sec)", \ 634 | (long unsigned int) (C2) - (long unsigned int)(C1), \ 635 | (double)((C2) - (C1)) / (1.0 * (double)CLOCKS_PER_SEC)) 636 | #else 637 | #define GREATEST_SET_TIME(UNUSED) 638 | #define GREATEST_CLOCK_DIFF(UNUSED1, UNUSED2) 639 | #endif 640 | 641 | #if GREATEST_USE_LONGJMP 642 | #define GREATEST_SAVE_CONTEXT() \ 643 | /* setjmp returns 0 (GREATEST_TEST_RES_PASS) on first call * \ 644 | * so the test runs, then RES_FAIL from FAIL_WITH_LONGJMP. */ \ 645 | ((enum greatest_test_res)(setjmp(greatest_info.jump_dest))) 646 | #else 647 | #define GREATEST_SAVE_CONTEXT() \ 648 | /*a no-op, since setjmp/longjmp aren't being used */ \ 649 | GREATEST_TEST_RES_PASS 650 | #endif 651 | 652 | /* Run every suite / test function run within BODY in pseudo-random 653 | * order, seeded by SEED. (The top 3 bits of the seed are ignored.) 654 | * 655 | * This should be called like: 656 | * GREATEST_SHUFFLE_TESTS(seed, { 657 | * GREATEST_RUN_TEST(some_test); 658 | * GREATEST_RUN_TEST(some_other_test); 659 | * GREATEST_RUN_TEST(yet_another_test); 660 | * }); 661 | * 662 | * Note that the body of the second argument will be evaluated 663 | * multiple times. */ 664 | #define GREATEST_SHUFFLE_SUITES(SD, BODY) GREATEST_SHUFFLE(0, SD, BODY) 665 | #define GREATEST_SHUFFLE_TESTS(SD, BODY) GREATEST_SHUFFLE(1, SD, BODY) 666 | #define GREATEST_SHUFFLE(ID, SD, BODY) \ 667 | do { \ 668 | struct greatest_prng *prng = &greatest_info.prng[ID]; \ 669 | greatest_prng_init_first_pass(ID); \ 670 | do { \ 671 | prng->count = 0; \ 672 | if (prng->initialized) { greatest_prng_step(ID); } \ 673 | BODY; \ 674 | if (!prng->initialized) { \ 675 | if (!greatest_prng_init_second_pass(ID, SD)) { break; } \ 676 | } else if (prng->count_run == prng->count_ceil) { \ 677 | break; \ 678 | } \ 679 | } while (!GREATEST_FAILURE_ABORT()); \ 680 | prng->count_run = prng->random_order = prng->initialized = 0; \ 681 | } while(0) 682 | 683 | /* Include several function definitions in the main test file. */ 684 | #define GREATEST_MAIN_DEFS() \ 685 | \ 686 | /* Is FILTER a subset of NAME? */ \ 687 | static int greatest_name_match(const char *name, const char *filter, \ 688 | int res_if_none) { \ 689 | size_t offset = 0; \ 690 | size_t filter_len = filter ? strlen(filter) : 0; \ 691 | if (filter_len == 0) { return res_if_none; } /* no filter */ \ 692 | while (name[offset] != '\0') { \ 693 | if (name[offset] == filter[0]) { \ 694 | if (0 == strncmp(&name[offset], filter, filter_len)) { \ 695 | return 1; \ 696 | } \ 697 | } \ 698 | offset++; \ 699 | } \ 700 | \ 701 | return 0; \ 702 | } \ 703 | \ 704 | static void greatest_buffer_test_name(const char *name) { \ 705 | struct greatest_run_info *g = &greatest_info; \ 706 | size_t len = strlen(name), size = sizeof(g->name_buf); \ 707 | memset(g->name_buf, 0x00, size); \ 708 | (void)strncat(g->name_buf, name, size - 1); \ 709 | if (g->name_suffix && (len + 1 < size)) { \ 710 | g->name_buf[len] = '_'; \ 711 | strncat(&g->name_buf[len+1], g->name_suffix, size-(len+2)); \ 712 | } \ 713 | } \ 714 | \ 715 | /* Before running a test, check the name filtering and \ 716 | * test shuffling state, if applicable, and then call setup hooks. */ \ 717 | int greatest_test_pre(const char *name) { \ 718 | struct greatest_run_info *g = &greatest_info; \ 719 | int match; \ 720 | greatest_buffer_test_name(name); \ 721 | match = greatest_name_match(g->name_buf, g->test_filter, 1) && \ 722 | !greatest_name_match(g->name_buf, g->test_exclude, 0); \ 723 | if (GREATEST_LIST_ONLY()) { /* just listing test names */ \ 724 | if (match) { \ 725 | GREATEST_FPRINTF(GREATEST_STDOUT, " %s\n", g->name_buf); \ 726 | } \ 727 | goto clear; \ 728 | } \ 729 | if (match && (!GREATEST_FIRST_FAIL() || g->suite.failed == 0)) { \ 730 | struct greatest_prng *p = &g->prng[1]; \ 731 | if (p->random_order) { \ 732 | p->count++; \ 733 | if (!p->initialized || ((p->count - 1) != p->state)) { \ 734 | goto clear; /* don't run this test yet */ \ 735 | } \ 736 | } \ 737 | GREATEST_SET_TIME(g->suite.pre_test); \ 738 | if (g->setup) { g->setup(g->setup_udata); } \ 739 | p->count_run++; \ 740 | return 1; /* test should be run */ \ 741 | } else { \ 742 | goto clear; /* skipped */ \ 743 | } \ 744 | clear: \ 745 | g->name_suffix = NULL; \ 746 | return 0; \ 747 | } \ 748 | \ 749 | static void greatest_do_pass(void) { \ 750 | struct greatest_run_info *g = &greatest_info; \ 751 | if (GREATEST_IS_VERBOSE()) { \ 752 | GREATEST_FPRINTF(GREATEST_STDOUT, "PASS %s: %s", \ 753 | g->name_buf, g->msg ? g->msg : ""); \ 754 | } else { \ 755 | GREATEST_FPRINTF(GREATEST_STDOUT, "."); \ 756 | } \ 757 | g->suite.passed++; \ 758 | } \ 759 | \ 760 | static void greatest_do_fail(void) { \ 761 | struct greatest_run_info *g = &greatest_info; \ 762 | if (GREATEST_IS_VERBOSE()) { \ 763 | GREATEST_FPRINTF(GREATEST_STDOUT, \ 764 | "FAIL %s: %s (%s:%u)", g->name_buf, \ 765 | g->msg ? g->msg : "", g->fail_file, g->fail_line); \ 766 | } else { \ 767 | GREATEST_FPRINTF(GREATEST_STDOUT, "F"); \ 768 | g->col++; /* add linebreak if in line of '.'s */ \ 769 | if (g->col != 0) { \ 770 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ 771 | g->col = 0; \ 772 | } \ 773 | GREATEST_FPRINTF(GREATEST_STDOUT, "FAIL %s: %s (%s:%u)\n", \ 774 | g->name_buf, g->msg ? g->msg : "", \ 775 | g->fail_file, g->fail_line); \ 776 | } \ 777 | g->suite.failed++; \ 778 | } \ 779 | \ 780 | static void greatest_do_skip(void) { \ 781 | struct greatest_run_info *g = &greatest_info; \ 782 | if (GREATEST_IS_VERBOSE()) { \ 783 | GREATEST_FPRINTF(GREATEST_STDOUT, "SKIP %s: %s", \ 784 | g->name_buf, g->msg ? g->msg : ""); \ 785 | } else { \ 786 | GREATEST_FPRINTF(GREATEST_STDOUT, "s"); \ 787 | } \ 788 | g->suite.skipped++; \ 789 | } \ 790 | \ 791 | void greatest_test_post(int res) { \ 792 | GREATEST_SET_TIME(greatest_info.suite.post_test); \ 793 | if (greatest_info.teardown) { \ 794 | void *udata = greatest_info.teardown_udata; \ 795 | greatest_info.teardown(udata); \ 796 | } \ 797 | \ 798 | if (res <= GREATEST_TEST_RES_FAIL) { \ 799 | greatest_do_fail(); \ 800 | } else if (res >= GREATEST_TEST_RES_SKIP) { \ 801 | greatest_do_skip(); \ 802 | } else if (res == GREATEST_TEST_RES_PASS) { \ 803 | greatest_do_pass(); \ 804 | } \ 805 | greatest_info.name_suffix = NULL; \ 806 | greatest_info.suite.tests_run++; \ 807 | greatest_info.col++; \ 808 | if (GREATEST_IS_VERBOSE()) { \ 809 | GREATEST_CLOCK_DIFF(greatest_info.suite.pre_test, \ 810 | greatest_info.suite.post_test); \ 811 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ 812 | } else if (greatest_info.col % greatest_info.width == 0) { \ 813 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ 814 | greatest_info.col = 0; \ 815 | } \ 816 | fflush(GREATEST_STDOUT); \ 817 | } \ 818 | \ 819 | static void report_suite(void) { \ 820 | if (greatest_info.suite.tests_run > 0) { \ 821 | GREATEST_FPRINTF(GREATEST_STDOUT, \ 822 | "\n%u test%s - %u passed, %u failed, %u skipped", \ 823 | greatest_info.suite.tests_run, \ 824 | greatest_info.suite.tests_run == 1 ? "" : "s", \ 825 | greatest_info.suite.passed, \ 826 | greatest_info.suite.failed, \ 827 | greatest_info.suite.skipped); \ 828 | GREATEST_CLOCK_DIFF(greatest_info.suite.pre_suite, \ 829 | greatest_info.suite.post_suite); \ 830 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ 831 | } \ 832 | } \ 833 | \ 834 | static void update_counts_and_reset_suite(void) { \ 835 | greatest_info.setup = NULL; \ 836 | greatest_info.setup_udata = NULL; \ 837 | greatest_info.teardown = NULL; \ 838 | greatest_info.teardown_udata = NULL; \ 839 | greatest_info.passed += greatest_info.suite.passed; \ 840 | greatest_info.failed += greatest_info.suite.failed; \ 841 | greatest_info.skipped += greatest_info.suite.skipped; \ 842 | greatest_info.tests_run += greatest_info.suite.tests_run; \ 843 | memset(&greatest_info.suite, 0, sizeof(greatest_info.suite)); \ 844 | greatest_info.col = 0; \ 845 | } \ 846 | \ 847 | static int greatest_suite_pre(const char *suite_name) { \ 848 | struct greatest_prng *p = &greatest_info.prng[0]; \ 849 | if (!greatest_name_match(suite_name, greatest_info.suite_filter, 1) \ 850 | || (GREATEST_FAILURE_ABORT())) { return 0; } \ 851 | if (p->random_order) { \ 852 | p->count++; \ 853 | if (!p->initialized || ((p->count - 1) != p->state)) { \ 854 | return 0; /* don't run this suite yet */ \ 855 | } \ 856 | } \ 857 | p->count_run++; \ 858 | update_counts_and_reset_suite(); \ 859 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n* Suite %s:\n", suite_name); \ 860 | GREATEST_SET_TIME(greatest_info.suite.pre_suite); \ 861 | return 1; \ 862 | } \ 863 | \ 864 | static void greatest_suite_post(void) { \ 865 | GREATEST_SET_TIME(greatest_info.suite.post_suite); \ 866 | report_suite(); \ 867 | } \ 868 | \ 869 | static void greatest_run_suite(greatest_suite_cb *suite_cb, \ 870 | const char *suite_name) { \ 871 | if (greatest_suite_pre(suite_name)) { \ 872 | suite_cb(); \ 873 | greatest_suite_post(); \ 874 | } \ 875 | } \ 876 | \ 877 | int greatest_do_assert_equal_t(const void *expd, const void *got, \ 878 | greatest_type_info *type_info, void *udata) { \ 879 | int eq = 0; \ 880 | if (type_info == NULL || type_info->equal == NULL) { \ 881 | return 0; \ 882 | } \ 883 | eq = type_info->equal(expd, got, udata); \ 884 | if (!eq) { \ 885 | if (type_info->print != NULL) { \ 886 | GREATEST_FPRINTF(GREATEST_STDOUT, "\nExpected: "); \ 887 | (void)type_info->print(expd, udata); \ 888 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n Got: "); \ 889 | (void)type_info->print(got, udata); \ 890 | GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ 891 | } \ 892 | } \ 893 | return eq; \ 894 | } \ 895 | \ 896 | static void greatest_usage(const char *name) { \ 897 | GREATEST_FPRINTF(GREATEST_STDOUT, \ 898 | "Usage: %s [-hlfav] [-s SUITE] [-t TEST] [-x EXCLUDE]\n" \ 899 | " -h, --help print this Help\n" \ 900 | " -l List suites and tests, then exit (dry run)\n" \ 901 | " -f Stop runner after first failure\n" \ 902 | " -a Abort on first failure (implies -f)\n" \ 903 | " -v Verbose output\n" \ 904 | " -s SUITE only run suites containing substring SUITE\n" \ 905 | " -t TEST only run tests containing substring TEST\n" \ 906 | " -x EXCLUDE exclude tests containing substring EXCLUDE\n", \ 907 | name); \ 908 | } \ 909 | \ 910 | static void greatest_parse_options(int argc, char **argv) { \ 911 | int i = 0; \ 912 | for (i = 1; i < argc; i++) { \ 913 | if (argv[i][0] == '-') { \ 914 | char f = argv[i][1]; \ 915 | if ((f == 's' || f == 't' || f == 'x') && argc <= i + 1) { \ 916 | greatest_usage(argv[0]); exit(EXIT_FAILURE); \ 917 | } \ 918 | switch (f) { \ 919 | case 's': /* suite name filter */ \ 920 | greatest_set_suite_filter(argv[i + 1]); i++; break; \ 921 | case 't': /* test name filter */ \ 922 | greatest_set_test_filter(argv[i + 1]); i++; break; \ 923 | case 'x': /* test name exclusion */ \ 924 | greatest_set_test_exclude(argv[i + 1]); i++; break; \ 925 | case 'f': /* first fail flag */ \ 926 | greatest_stop_at_first_fail(); break; \ 927 | case 'a': /* abort() on fail flag */ \ 928 | greatest_abort_on_fail(); break; \ 929 | case 'l': /* list only (dry run) */ \ 930 | greatest_list_only(); break; \ 931 | case 'v': /* first fail flag */ \ 932 | greatest_info.verbosity++; break; \ 933 | case 'h': /* help */ \ 934 | greatest_usage(argv[0]); exit(EXIT_SUCCESS); \ 935 | case '-': \ 936 | if (0 == strncmp("--help", argv[i], 6)) { \ 937 | greatest_usage(argv[0]); exit(EXIT_SUCCESS); \ 938 | } else if (0 == strncmp("--", argv[i], 2)) { \ 939 | return; /* ignore following arguments */ \ 940 | } /* fall through */ \ 941 | default: \ 942 | GREATEST_FPRINTF(GREATEST_STDOUT, \ 943 | "Unknown argument '%s'\n", argv[i]); \ 944 | greatest_usage(argv[0]); \ 945 | exit(EXIT_FAILURE); \ 946 | } \ 947 | } \ 948 | } \ 949 | } \ 950 | \ 951 | int greatest_all_passed(void) { return (greatest_info.failed == 0); } \ 952 | \ 953 | void greatest_set_test_filter(const char *filter) { \ 954 | greatest_info.test_filter = filter; \ 955 | } \ 956 | \ 957 | void greatest_set_test_exclude(const char *filter) { \ 958 | greatest_info.test_exclude = filter; \ 959 | } \ 960 | \ 961 | void greatest_set_suite_filter(const char *filter) { \ 962 | greatest_info.suite_filter = filter; \ 963 | } \ 964 | \ 965 | void greatest_stop_at_first_fail(void) { \ 966 | greatest_set_flag(GREATEST_FLAG_FIRST_FAIL); \ 967 | } \ 968 | \ 969 | void greatest_abort_on_fail(void) { \ 970 | greatest_set_flag(GREATEST_FLAG_ABORT_ON_FAIL); \ 971 | } \ 972 | \ 973 | void greatest_list_only(void) { \ 974 | greatest_set_flag(GREATEST_FLAG_LIST_ONLY); \ 975 | } \ 976 | \ 977 | void greatest_get_report(struct greatest_report_t *report) { \ 978 | if (report) { \ 979 | report->passed = greatest_info.passed; \ 980 | report->failed = greatest_info.failed; \ 981 | report->skipped = greatest_info.skipped; \ 982 | report->assertions = greatest_info.assertions; \ 983 | } \ 984 | } \ 985 | \ 986 | unsigned int greatest_get_verbosity(void) { \ 987 | return greatest_info.verbosity; \ 988 | } \ 989 | \ 990 | void greatest_set_verbosity(unsigned int verbosity) { \ 991 | greatest_info.verbosity = (unsigned char)verbosity; \ 992 | } \ 993 | \ 994 | void greatest_set_flag(greatest_flag_t flag) { \ 995 | greatest_info.flags = (unsigned char)(greatest_info.flags | flag); \ 996 | } \ 997 | \ 998 | void greatest_set_test_suffix(const char *suffix) { \ 999 | greatest_info.name_suffix = suffix; \ 1000 | } \ 1001 | \ 1002 | void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata) { \ 1003 | greatest_info.setup = cb; \ 1004 | greatest_info.setup_udata = udata; \ 1005 | } \ 1006 | \ 1007 | void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, \ 1008 | void *udata) { \ 1009 | greatest_info.teardown = cb; \ 1010 | greatest_info.teardown_udata = udata; \ 1011 | } \ 1012 | \ 1013 | static int greatest_string_equal_cb(const void *expd, const void *got, \ 1014 | void *udata) { \ 1015 | size_t *size = (size_t *)udata; \ 1016 | return (size != NULL \ 1017 | ? (0 == strncmp((const char *)expd, (const char *)got, *size)) \ 1018 | : (0 == strcmp((const char *)expd, (const char *)got))); \ 1019 | } \ 1020 | \ 1021 | static int greatest_string_printf_cb(const void *t, void *udata) { \ 1022 | (void)udata; /* note: does not check \0 termination. */ \ 1023 | return GREATEST_FPRINTF(GREATEST_STDOUT, "%s", (const char *)t); \ 1024 | } \ 1025 | \ 1026 | greatest_type_info greatest_type_info_string = { \ 1027 | greatest_string_equal_cb, \ 1028 | greatest_string_printf_cb, \ 1029 | }; \ 1030 | \ 1031 | static int greatest_memory_equal_cb(const void *expd, const void *got, \ 1032 | void *udata) { \ 1033 | greatest_memory_cmp_env *env = (greatest_memory_cmp_env *)udata; \ 1034 | return (0 == memcmp(expd, got, env->size)); \ 1035 | } \ 1036 | \ 1037 | /* Hexdump raw memory, with differences highlighted */ \ 1038 | static int greatest_memory_printf_cb(const void *t, void *udata) { \ 1039 | greatest_memory_cmp_env *env = (greatest_memory_cmp_env *)udata; \ 1040 | const unsigned char *buf = (const unsigned char *)t; \ 1041 | unsigned char diff_mark = ' '; \ 1042 | FILE *out = GREATEST_STDOUT; \ 1043 | size_t i, line_i, line_len = 0; \ 1044 | int len = 0; /* format hexdump with differences highlighted */ \ 1045 | for (i = 0; i < env->size; i+= line_len) { \ 1046 | diff_mark = ' '; \ 1047 | line_len = env->size - i; \ 1048 | if (line_len > 16) { line_len = 16; } \ 1049 | for (line_i = i; line_i < i + line_len; line_i++) { \ 1050 | if (env->exp[line_i] != env->got[line_i]) diff_mark = 'X'; \ 1051 | } \ 1052 | len += GREATEST_FPRINTF(out, "\n%04x %c ", \ 1053 | (unsigned int)i, diff_mark); \ 1054 | for (line_i = i; line_i < i + line_len; line_i++) { \ 1055 | int m = env->exp[line_i] == env->got[line_i]; /* match? */ \ 1056 | len += GREATEST_FPRINTF(out, "%02x%c", \ 1057 | buf[line_i], m ? ' ' : '<'); \ 1058 | } \ 1059 | for (line_i = 0; line_i < 16 - line_len; line_i++) { \ 1060 | len += GREATEST_FPRINTF(out, " "); \ 1061 | } \ 1062 | GREATEST_FPRINTF(out, " "); \ 1063 | for (line_i = i; line_i < i + line_len; line_i++) { \ 1064 | unsigned char c = buf[line_i]; \ 1065 | len += GREATEST_FPRINTF(out, "%c", isprint(c) ? c : '.'); \ 1066 | } \ 1067 | } \ 1068 | len += GREATEST_FPRINTF(out, "\n"); \ 1069 | return len; \ 1070 | } \ 1071 | \ 1072 | void greatest_prng_init_first_pass(int id) { \ 1073 | greatest_info.prng[id].random_order = 1; \ 1074 | greatest_info.prng[id].count_run = 0; \ 1075 | } \ 1076 | \ 1077 | int greatest_prng_init_second_pass(int id, unsigned long seed) { \ 1078 | struct greatest_prng *p = &greatest_info.prng[id]; \ 1079 | if (p->count == 0) { return 0; } \ 1080 | p->count_ceil = p->count; \ 1081 | for (p->m = 1; p->m < p->count; p->m <<= 1) {} \ 1082 | p->state = seed & 0x1fffffff; /* only use lower 29 bits */ \ 1083 | p->a = 4LU * p->state; /* to avoid overflow when */ \ 1084 | p->a = (p->a ? p->a : 4) | 1; /* multiplied by 4 */ \ 1085 | p->c = 2147483647; /* and so p->c ((2 ** 31) - 1) is */ \ 1086 | p->initialized = 1; /* always relatively prime to p->a. */ \ 1087 | fprintf(stderr, "init_second_pass: a %lu, c %lu, state %lu\n", \ 1088 | p->a, p->c, p->state); \ 1089 | return 1; \ 1090 | } \ 1091 | \ 1092 | /* Step the pseudorandom number generator until its state reaches \ 1093 | * another test ID between 0 and the test count. \ 1094 | * This use a linear congruential pseudorandom number generator, \ 1095 | * with the power-of-two ceiling of the test count as the modulus, the \ 1096 | * masked seed as the multiplier, and a prime as the increment. For \ 1097 | * each generated value < the test count, run the corresponding test. \ 1098 | * This will visit all IDs 0 <= X < mod once before repeating, \ 1099 | * with a starting position chosen based on the initial seed. \ 1100 | * For details, see: Knuth, The Art of Computer Programming \ 1101 | * Volume. 2, section 3.2.1. */ \ 1102 | void greatest_prng_step(int id) { \ 1103 | struct greatest_prng *p = &greatest_info.prng[id]; \ 1104 | do { \ 1105 | p->state = ((p->a * p->state) + p->c) & (p->m - 1); \ 1106 | } while (p->state >= p->count_ceil); \ 1107 | } \ 1108 | \ 1109 | void GREATEST_INIT(void) { \ 1110 | /* Suppress unused function warning if features aren't used */ \ 1111 | (void)greatest_run_suite; \ 1112 | (void)greatest_parse_options; \ 1113 | (void)greatest_prng_step; \ 1114 | (void)greatest_prng_init_first_pass; \ 1115 | (void)greatest_prng_init_second_pass; \ 1116 | (void)greatest_set_test_suffix; \ 1117 | \ 1118 | memset(&greatest_info, 0, sizeof(greatest_info)); \ 1119 | greatest_info.width = GREATEST_DEFAULT_WIDTH; \ 1120 | GREATEST_SET_TIME(greatest_info.begin); \ 1121 | } \ 1122 | \ 1123 | /* Report passes, failures, skipped tests, the number of \ 1124 | * assertions, and the overall run time. */ \ 1125 | void GREATEST_PRINT_REPORT(void) { \ 1126 | if (!GREATEST_LIST_ONLY()) { \ 1127 | update_counts_and_reset_suite(); \ 1128 | GREATEST_SET_TIME(greatest_info.end); \ 1129 | GREATEST_FPRINTF(GREATEST_STDOUT, \ 1130 | "\nTotal: %u test%s", \ 1131 | greatest_info.tests_run, \ 1132 | greatest_info.tests_run == 1 ? "" : "s"); \ 1133 | GREATEST_CLOCK_DIFF(greatest_info.begin, \ 1134 | greatest_info.end); \ 1135 | GREATEST_FPRINTF(GREATEST_STDOUT, ", %u assertion%s\n", \ 1136 | greatest_info.assertions, \ 1137 | greatest_info.assertions == 1 ? "" : "s"); \ 1138 | GREATEST_FPRINTF(GREATEST_STDOUT, \ 1139 | "Pass: %u, fail: %u, skip: %u.\n", \ 1140 | greatest_info.passed, \ 1141 | greatest_info.failed, greatest_info.skipped); \ 1142 | } \ 1143 | } \ 1144 | \ 1145 | greatest_type_info greatest_type_info_memory = { \ 1146 | greatest_memory_equal_cb, \ 1147 | greatest_memory_printf_cb, \ 1148 | }; \ 1149 | \ 1150 | greatest_run_info greatest_info 1151 | 1152 | /* Handle command-line arguments, etc. */ 1153 | #define GREATEST_MAIN_BEGIN() \ 1154 | do { \ 1155 | GREATEST_INIT(); \ 1156 | greatest_parse_options(argc, argv); \ 1157 | } while (0) 1158 | 1159 | /* Report results, exit with exit status based on results. */ 1160 | #define GREATEST_MAIN_END() \ 1161 | do { \ 1162 | GREATEST_PRINT_REPORT(); \ 1163 | return (greatest_all_passed() ? EXIT_SUCCESS : EXIT_FAILURE); \ 1164 | } while (0) 1165 | 1166 | /* Make abbreviations without the GREATEST_ prefix for the 1167 | * most commonly used symbols. */ 1168 | #if GREATEST_USE_ABBREVS 1169 | #define TEST GREATEST_TEST 1170 | #define SUITE GREATEST_SUITE 1171 | #define SUITE_EXTERN GREATEST_SUITE_EXTERN 1172 | #define RUN_TEST GREATEST_RUN_TEST 1173 | #define RUN_TEST1 GREATEST_RUN_TEST1 1174 | #define RUN_SUITE GREATEST_RUN_SUITE 1175 | #define IGNORE_TEST GREATEST_IGNORE_TEST 1176 | #define ASSERT GREATEST_ASSERT 1177 | #define ASSERTm GREATEST_ASSERTm 1178 | #define ASSERT_FALSE GREATEST_ASSERT_FALSE 1179 | #define ASSERT_EQ GREATEST_ASSERT_EQ 1180 | #define ASSERT_EQ_FMT GREATEST_ASSERT_EQ_FMT 1181 | #define ASSERT_IN_RANGE GREATEST_ASSERT_IN_RANGE 1182 | #define ASSERT_EQUAL_T GREATEST_ASSERT_EQUAL_T 1183 | #define ASSERT_STR_EQ GREATEST_ASSERT_STR_EQ 1184 | #define ASSERT_STRN_EQ GREATEST_ASSERT_STRN_EQ 1185 | #define ASSERT_MEM_EQ GREATEST_ASSERT_MEM_EQ 1186 | #define ASSERT_ENUM_EQ GREATEST_ASSERT_ENUM_EQ 1187 | #define ASSERT_FALSEm GREATEST_ASSERT_FALSEm 1188 | #define ASSERT_EQm GREATEST_ASSERT_EQm 1189 | #define ASSERT_EQ_FMTm GREATEST_ASSERT_EQ_FMTm 1190 | #define ASSERT_IN_RANGEm GREATEST_ASSERT_IN_RANGEm 1191 | #define ASSERT_EQUAL_Tm GREATEST_ASSERT_EQUAL_Tm 1192 | #define ASSERT_STR_EQm GREATEST_ASSERT_STR_EQm 1193 | #define ASSERT_STRN_EQm GREATEST_ASSERT_STRN_EQm 1194 | #define ASSERT_MEM_EQm GREATEST_ASSERT_MEM_EQm 1195 | #define ASSERT_ENUM_EQm GREATEST_ASSERT_ENUM_EQm 1196 | #define PASS GREATEST_PASS 1197 | #define FAIL GREATEST_FAIL 1198 | #define SKIP GREATEST_SKIP 1199 | #define PASSm GREATEST_PASSm 1200 | #define FAILm GREATEST_FAILm 1201 | #define SKIPm GREATEST_SKIPm 1202 | #define SET_SETUP GREATEST_SET_SETUP_CB 1203 | #define SET_TEARDOWN GREATEST_SET_TEARDOWN_CB 1204 | #define CHECK_CALL GREATEST_CHECK_CALL 1205 | #define SHUFFLE_TESTS GREATEST_SHUFFLE_TESTS 1206 | #define SHUFFLE_SUITES GREATEST_SHUFFLE_SUITES 1207 | 1208 | #ifdef GREATEST_VA_ARGS 1209 | #define RUN_TESTp GREATEST_RUN_TESTp 1210 | #endif 1211 | 1212 | #if GREATEST_USE_LONGJMP 1213 | #define ASSERT_OR_LONGJMP GREATEST_ASSERT_OR_LONGJMP 1214 | #define ASSERT_OR_LONGJMPm GREATEST_ASSERT_OR_LONGJMPm 1215 | #define FAIL_WITH_LONGJMP GREATEST_FAIL_WITH_LONGJMP 1216 | #define FAIL_WITH_LONGJMPm GREATEST_FAIL_WITH_LONGJMPm 1217 | #endif 1218 | 1219 | #endif /* USE_ABBREVS */ 1220 | 1221 | #if defined(__cplusplus) && !defined(GREATEST_NO_EXTERN_CPLUSPLUS) 1222 | } 1223 | #endif 1224 | 1225 | #endif 1226 | -------------------------------------------------------------------------------- /mmap-demo.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include /* for assert */ 3 | #include /* for NULL */ 4 | #include /* for memcpy */ 5 | #include /* for mmap */ 6 | #undef _GNU_SOURCE 7 | 8 | // clang-format off 9 | const unsigned char program[] = { 10 | // mov eax, 42 (0x2a) 11 | 0xb8, 0x2a, 0x00, 0x00, 0x00, 12 | // ret 13 | 0xc3, 14 | }; 15 | // clang-format on 16 | 17 | const int kProgramSize = sizeof program; 18 | 19 | typedef int (*JitFunction)(); 20 | 21 | int main() { 22 | void *memory = mmap(/*addr=*/NULL, kProgramSize, PROT_READ | PROT_WRITE, 23 | MAP_ANONYMOUS | MAP_PRIVATE, 24 | /*filedes=*/-1, /*off=*/0); 25 | memcpy(memory, program, kProgramSize); 26 | int result = mprotect(memory, kProgramSize, PROT_EXEC); 27 | assert(result == 0 && "mprotect failed"); 28 | JitFunction function = *(JitFunction *)&memory; 29 | int return_code = function(); 30 | assert(return_code == 42 && "the assembly was wrong"); 31 | result = munmap(memory, kProgramSize); 32 | assert(result == 0 && "munmap failed"); 33 | // Return 0 so we can run this as a test and not stop Make 34 | return 0; 35 | } 36 | --------------------------------------------------------------------------------