aboutsummaryrefslogtreecommitdiff
path: root/src/generator.c
diff options
context:
space:
mode:
authorMustafa Quraish <[email protected]>2022-01-31 01:26:07 -0500
committerMustafa Quraish <[email protected]>2022-01-31 01:26:07 -0500
commitcb0d77c3dc83d6c7cb39642e8708b12a29b7b8c7 (patch)
tree904a6c70509da3f9274bb2df8e214f2b9e6fd9ac /src/generator.c
parentPut tokens in their own macro to allow looping over them (diff)
downloadcup-cb0d77c3dc83d6c7cb39642e8708b12a29b7b8c7.tar.xz
cup-cb0d77c3dc83d6c7cb39642e8708b12a29b7b8c7.zip
Add basic builtin-function support
This isn't really super extendible for now, but it's a start and gives us the `print` builtin which allows us to finally actually print out values to the screen, so we can move away from testing with exit codes eventually.
Diffstat (limited to 'src/generator.c')
-rw-r--r--src/generator.c101
1 files changed, 95 insertions, 6 deletions
diff --git a/src/generator.c b/src/generator.c
index 6a95d0c..5b1f630 100644
--- a/src/generator.c
+++ b/src/generator.c
@@ -12,6 +12,14 @@
static int label_counter = 0;
static Node *current_function = NULL;
+void make_syscall(i64 syscall_no, FILE *out) {
+#if __APPLE__
+ syscall_no += 0x2000000;
+#endif
+ fprintf(out, " mov rax, %lld\n", syscall_no);
+ fprintf(out, " syscall\n");
+}
+
void generate_expr_into_rax(Node *expr, FILE *out);
void generate_func_call(Node *node, FILE *out)
@@ -319,8 +327,17 @@ void generate_function(Node *func, FILE *out)
current_function = func;
generate_function_header(func, out);
generate_block(func->func.body, out);
+ // TODO: This is a hack, we should make sure a function contains a return statement
+ // if it says it's going to return something
+ fprintf(out, " mov rsp, rbp\n");
+ fprintf(out, " pop rbp\n");
+ // Return 0 by default if we don't have a return statement
+ fprintf(out, " mov qword rax, 0\n");
+ fprintf(out, " ret\n");
}
+void generate_builtins(FILE *out);
+
void generate_asm(Node *root, FILE *out)
{
assert(root->type == AST_PROGRAM);
@@ -343,13 +360,85 @@ void generate_asm(Node *root, FILE *out)
#endif
fprintf(out, " call main\n");
-#if __APPLE__
- fprintf(out, " ret\n");
-#else
fprintf(out, " mov rdi, rax\n");
- fprintf(out, " mov rax, %d\n", SYS_exit);
- fprintf(out, " syscall\n");
-#endif
+ make_syscall(SYS_exit, out);
// TODO: Add implementations of some primitives?
+ generate_builtins(out);
+}
+
+void generate_builtins(FILE *out)
+{
+ // Stolen shamelessly from tsoding/porth:
+ // https://gitlab.com/tsoding/porth
+ fprintf(out,
+ "print:\n"
+ " mov rdi, [rsp+8]\n"
+ " mov r9, -3689348814741910323\n"
+ " sub rsp, 40\n"
+ " mov BYTE [rsp+31], 10\n"
+ " lea rcx, [rsp+30]\n"
+ " mov qword rbx, 0\n"
+
+ // Check if < 0, and set rbx=0, negate value
+ // " cmp rdi, 0\n"
+ // " jge .L2\n"
+ // " mov qword rbx, 1\n"
+ // " neg rdi\n"
+ // " sub rcx, 1\n"
+
+ ".L2:\n"
+ " mov rax, rdi\n"
+ " lea r8, [rsp+32]\n"
+ " mul r9\n"
+ " mov rax, rdi\n"
+ " sub r8, rcx\n"
+ " shr rdx, 3\n"
+ " lea rsi, [rdx+rdx*4]\n"
+ " add rsi, rsi\n"
+ " sub rax, rsi\n"
+ " add eax, 48\n"
+ " mov BYTE [rcx], al\n"
+ " mov rax, rdi\n"
+ " mov rdi, rdx\n"
+ " mov rdx, rcx\n"
+ " sub rcx, 1\n"
+ " cmp rax, 9\n"
+ " ja .L2\n"
+
+ // If rbx=1, then we need to add a minus sign, not sure how
+ // the above code works so there's probably a nicer way.
+ // " cmp rbx, 0\n"
+ // " je .end_neg_sign\n"
+ // " add eax, 48\n"
+ // " mov BYTE [rcx], 45\n"
+ // " mov rax, rdi\n"
+ // " mov rdi, rdx\n"
+ // " mov rdx, rcx\n"
+ // " sub rcx, 1\n"
+ // ".end_neg_sign:\n"
+
+ " lea rax, [rsp+32]\n"
+ " mov edi, 1\n"
+ " sub rdx, rax\n"
+ " xor eax, eax\n"
+ " lea rsi, [rsp+32+rdx]\n"
+ " mov rdx, r8\n"
+ );
+ make_syscall(SYS_write, out);
+ fprintf(out, " add rsp, 40\n");
+ fprintf(out, " ret\n");
+
+ /////////////////////////////////////////////////////////////////
+
+ // Print out a single character
+ fprintf(out,
+ "putc:\n"
+ " mov rdi, 1\n" // stdout
+ " mov rsi, rsp\n"
+ " add rsi, 8\n"
+ " mov rdx, 1\n" // 1 byte
+ );
+ make_syscall(SYS_write, out);
+ fprintf(out, " ret\n");
} \ No newline at end of file