#ifndef BDL_COMPILER_H #define BDL_COMPILER_H #define PRELUDE_FILE "src/x86_64/prelude.asm" #define POSTLUDE_FILE "src/x86_64/postlude.asm" #define NIL_VAL 1 #define BOOL_MASK 3 #define BOOL_TAG 3 #define BOOL_SHIFT 2 #define FIXNUM_MASK 1 #define FIXNUM_TAG 0 #define FIXNUM_SHIFT 1 void compile_object(Object *obj); void compile_fixnum(Object *obj); void compile_proc_call(Object *obj); void compile(Root *roots); void emit_file(char *file_name) { FILE *file = fopen(file_name, "r"); if (!file) { fprintf(stderr, "error: couldn't open input file: %s\n", file_name); exit(EXIT_FAILURE); } char buf[1024]; size_t n = 0; while ((n = fread(&buf, 1, 1024, file)) > 0) { fwrite(buf, 1, n, stdout); } } void compile_fixnum(Object *obj) { printf(" ;; --> compile_fixnum\n"); printf(" mov rax, %ld\n", (obj->fixnum << FIXNUM_SHIFT) | FIXNUM_TAG); printf(" push rax\n"); printf(" ;; <-- compile_fixnum\n"); } void compile_boolean(Object *obj) { printf(" ;; --> compile_boolean\n"); int is_true = obj->type == OBJ_TYPE_TRUE; printf(" mov rax, %d\n", (is_true << BOOL_SHIFT) | BOOL_TAG); printf(" push rax\n"); printf(" ;; <-- compile_boolean\n"); } void compile_nil(void) { printf(" ;; --> compile_nil\n"); printf(" mov rax, %d\n", NIL_VAL); printf(" push rax\n"); printf(" ;; <-- compile_nil\n"); } typedef enum OpType { // Arithmetic. OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, // Type predicates. OP_IS_NIL, OP_IS_ZERO, OP_IS_BOOL, OP_IS_FIXNUM, // TODO: etc. } OpType; void arithmetic_op(OpType type) { printf(" ;; --> arithmetic_op\n"); printf(" pop rcx\n"); printf(" pop rax\n"); switch (type) { case OP_ADD: { printf(" add rax, rcx\n"); } break; case OP_SUB: { printf(" sub rax, rcx\n"); } break; case OP_MUL: { printf(" sar rax, %d\n", FIXNUM_SHIFT); printf(" sar rcx, %d\n", FIXNUM_SHIFT); printf(" mul rcx\n"); printf(" sal rax, %d\n", FIXNUM_SHIFT); } break; case OP_DIV: { printf(" sar rax, %d\n", FIXNUM_SHIFT); printf(" sar rcx, %d\n", FIXNUM_SHIFT); printf(" mov rdx, 0\n"); printf(" div rcx\n"); printf(" sal rax, %d\n", FIXNUM_SHIFT); } break; case OP_MOD: { printf(" sar rax, %d\n", FIXNUM_SHIFT); printf(" sar rcx, %d\n", FIXNUM_SHIFT); printf(" mov rdx, 0\n"); printf(" div rcx\n"); printf(" mov rax, rdx\n"); printf(" sal rax, %d\n", FIXNUM_SHIFT); } break; default: break; } printf(" push rax\n"); printf(" ;; <-- arithmetic_op\n"); } typedef void (CompileProc)(OpType, Object*); void compile_type_predicate(OpType op, Object* args) { printf(" ;; --> compile_type_predicate\n"); compile_object(args->head); printf(" pop rax\n"); switch (op) { case OP_IS_NIL: { printf(" cmp rax, %d\n", NIL_VAL); } break; case OP_IS_ZERO: { printf(" cmp rax, 0\n"); } break; case OP_IS_BOOL: { printf(" and rax, %d\n", BOOL_MASK); printf(" cmp rax, %d\n", BOOL_TAG); } break; case OP_IS_FIXNUM: { printf(" and rax, %d\n", FIXNUM_MASK); printf(" cmp rax, %d\n", FIXNUM_TAG); } break; default: break; } printf(" mov rax, 0\n"); printf(" sete al\n"); printf(" sal rax, %d\n", BOOL_SHIFT); printf(" or rax, %d\n", BOOL_TAG); printf(" push rax\n"); printf(" ;; <-- compile_type_predicate\n"); } void compile_arithmetic_list(OpType op, Object* args) { printf(" ;; --> compile_arithmetic_list\n"); compile_object(args->head); args = args->tail; while (args != NULL) { compile_object(args->head); args = args->tail; arithmetic_op(op); } printf(" ;; <-- compile_arithmetic_list\n"); } void compile_proc_call(Object *obj) { // TODO: Probably we want to use a hash table for these lookups that is // initialized at the start of the compilation procedure. if (sv_equal(&obj->head->text, &STRING("+"))) { compile_arithmetic_list(OP_ADD, obj->tail); } if (sv_equal(&obj->head->text, &STRING("-"))) { compile_arithmetic_list(OP_SUB, obj->tail); } if (sv_equal(&obj->head->text, &STRING("*"))) { compile_arithmetic_list(OP_MUL, obj->tail); } if (sv_equal(&obj->head->text, &STRING("/"))) { compile_arithmetic_list(OP_DIV, obj->tail); } if (sv_equal(&obj->head->text, &STRING("%"))) { compile_arithmetic_list(OP_MOD, obj->tail); } if (sv_equal(&obj->head->text, &STRING("nil?"))) { compile_type_predicate(OP_IS_NIL, obj->tail); } if (sv_equal(&obj->head->text, &STRING("zero?"))) { compile_type_predicate(OP_IS_ZERO, obj->tail); } if (sv_equal(&obj->head->text, &STRING("fixnum?"))) { compile_type_predicate(OP_IS_FIXNUM, obj->tail); } if (sv_equal(&obj->head->text, &STRING("bool?"))) { compile_type_predicate(OP_IS_BOOL, obj->tail); } } void compile_object(Object *obj) { switch (obj->type) { case OBJ_TYPE_NIL: { compile_nil(); } break; case OBJ_TYPE_TRUE: case OBJ_TYPE_FALSE: { compile_boolean(obj); } break; case OBJ_TYPE_FIXNUM: { compile_fixnum(obj); } break; case OBJ_TYPE_PAIR: { compile_proc_call(obj); } break; default: break; } } void compile(Root *roots) { emit_file(PRELUDE_FILE); for (size_t i = 0; i < array_size(roots); i++) { Object *root = roots[i]; compile_object(root); // OBJ_PRINT(root); } emit_file(POSTLUDE_FILE); } #endif // BDL_COMPILER_H