commit 372ae52525e47ba31d84ece3f558b8ead2cb1000
Author: William Djupström <william@deepztream.com>
Date: Sat, 22 Aug 2026 22:33:46 +0200
Initialise repo
Diffstat:
| A | .gitignore | | | 2 | ++ |
| A | Makefile | | | 6 | ++++++ |
| A | backend.c | | | 207 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | dlib.h | | | 324 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | ir_backend.c | | | 195 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
5 files changed, 734 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,2 @@
+tags
+backend
diff --git a/Makefile b/Makefile
@@ -0,0 +1,6 @@
+INPUT_FILES = backend.c \
+ ir_backend.c \
+ dlib.h
+CFLAGS=-ggdb -Og -Wall -Wextra
+backend: $(INPUT_FILES)
+ gcc $(CFLAGS) -o $@ backend.c
diff --git a/backend.c b/backend.c
@@ -0,0 +1,207 @@
+#include <stdbool.h>
+
+#define DLIB_IMPLEMENTATION
+#include "dlib.h"
+
+typedef struct Ir_program Ir_program;
+typedef bool (*Compile_fn)(Ir_program, string_builder *);
+
+typedef struct {
+ const char *name;
+ Compile_fn compile;
+} Backend;
+
+enum Ir_type {
+ TYPE_VOID,
+ TYPE_I8,
+ TYPE_U8,
+ TYPE_I16,
+ TYPE_U16,
+ TYPE_I32,
+ TYPE_U32,
+ TYPE_I64,
+ TYPE_U64,
+ TYPE_PTR,
+ TYPE_F32,
+ TYPE_F64,
+};
+
+enum Ir_kind {
+ IR_LOAD,
+ IR_STORE,
+ IR_ASSIGN,
+ IR_BINOP,
+ IR_CALL,
+ IR_RETURN,
+ IR_LABEL,
+ IR_JUMP,
+ IR_JUMP_IF_NOT,
+};
+
+enum Ir_arg_kind {
+ ARG_NONE,
+ ARG_INT_LITERAL,
+ /*ARG_FLOAT_LITERAL,*/
+ ARG_CSTRING_LITERAL,
+ ARG_TEMP_STORAGE,
+ ARG_ADDRESS,
+};
+
+enum Ir_binop {
+ OP_ADD,
+ OP_SUB,
+ OP_MUL,
+ OP_DIV,
+ OP_LT,
+ OP_GT,
+ OP_EQ,
+ OP_LEQ,
+ OP_GEQ,
+};
+
+typedef struct {
+ enum Ir_arg_kind type;
+ union {
+ int64_t i64;
+ /* double f64; */
+ ssize_t temp_id;
+ void *addr;
+ char *cstr;
+ };
+} Ir_arg;
+
+typedef struct {
+ Ir_arg *items;
+ size_t count;
+ size_t capacity;
+} Ir_arg_list;
+
+typedef struct {
+ enum Ir_kind type;
+ Ir_arg result;
+ union {
+ struct {
+ enum Ir_binop op;
+ Ir_arg left;
+ Ir_arg right;
+ } binop;
+ struct {
+ Ir_arg loc;
+ } load;
+ struct {
+ Ir_arg loc;
+ Ir_arg data;
+ } store;
+ Ir_arg assign;
+ string_view label;
+ struct {
+ Ir_arg cond;
+ string_view label;
+ } jump;
+ Ir_arg ret;
+ struct {
+ Ir_arg indirect;
+ string_view name;
+ Ir_arg_list args;
+ } call;
+ };
+} Ir;
+
+typedef struct {
+ Ir *items;
+ size_t count;
+ size_t capacity;
+} Ir_list;
+
+typedef struct {
+ enum Ir_type *items;
+ size_t count;
+ size_t capacity;
+} Ir_type_list;
+
+typedef struct {
+ enum Ir_type type;
+ union {
+ int8_t i8;
+ uint8_t u8;
+ int16_t i16;
+ uint16_t u16;
+ int32_t i32;
+ uint32_t u32;
+ int64_t i64;
+ uint64_t u64;
+ /* double f64;
+ float f32; */
+ string_view str;
+ char *cstr;
+ };
+} Ir_data;
+
+typedef struct {
+ string_view name;
+ Ir_data default_value;
+} Ir_static_data;
+
+typedef struct {
+ Ir_static_data *items;
+ size_t count;
+ size_t capacity;
+} Ir_static_data_list;
+
+typedef struct {
+ string_view name;
+ enum Ir_type return_type;
+ Ir_type_list argument_types;
+ Ir_list body;
+} Ir_function;
+
+typedef struct {
+ Ir_function *items;
+ size_t count;
+ size_t capacity;
+} Ir_function_list;
+
+struct Ir_program {
+ Ir_function_list functions;
+ Ir_static_data_list data;
+};
+
+bool ir_compile(Ir_program, string_builder *);
+
+Backend ir_backend = {
+ "IR Dump",
+ ir_compile,
+};
+
+int
+main(int argc, char **argv)
+{
+ (void) argc;
+ (void) argv;
+ string_builder sb = {0};
+ Ir_list ir_list = {0};
+ da_append(&ir_list, ((Ir) { .type = IR_LABEL, .label = {.data = "start", .count = 5}}));
+ da_append(&ir_list, ((Ir) { .type = IR_ASSIGN, .result = { .type = ARG_TEMP_STORAGE, .temp_id = 0}, .assign = {.type = ARG_INT_LITERAL, .i64 = 0}}));
+ da_append(&ir_list, ((Ir) { .type = IR_LABEL, .label = {.data = "loop", .count = 4}}));
+ da_append(&ir_list, ((Ir) { .type = IR_BINOP, .result = { .type = ARG_TEMP_STORAGE, .temp_id = 1}, .binop = {.op = OP_LT, .left = {.type = ARG_TEMP_STORAGE, .temp_id = 0}, .right = {.type = ARG_INT_LITERAL, .i64 = 10}}}));
+ da_append(&ir_list, ((Ir) { .type = IR_JUMP_IF_NOT, .jump = {.cond = {.type = ARG_TEMP_STORAGE, .temp_id = 1}, .label = {.data = "loop_end", .count = 8}}}));
+ da_append(&ir_list, ((Ir) { .type = IR_CALL, .result = {.type = ARG_TEMP_STORAGE, .temp_id = 2}, .call = { .name = {.data = "printf", .count = 6}, .args = {0}}}));
+ da_append(&ir_list.items[ir_list.count - 1].call.args, ((Ir_arg) {.type = ARG_CSTRING_LITERAL, .cstr = "%i\\n"}));
+ da_append(&ir_list.items[ir_list.count - 1].call.args, ((Ir_arg) {.type = ARG_TEMP_STORAGE, .temp_id = 0}));
+ da_append(&ir_list, ((Ir) { .type = IR_BINOP, .result = { .type = ARG_TEMP_STORAGE, .temp_id = 0}, .binop = {.op = OP_ADD, .left = {.type = ARG_TEMP_STORAGE, .temp_id = 0}, .right = {.type = ARG_INT_LITERAL, .i64 = 1}}}));
+ da_append(&ir_list, ((Ir) { .type = IR_JUMP, .jump = {.label = {.data = "loop", .count = 4}}}));
+ da_append(&ir_list, ((Ir) { .type = IR_LABEL, .label = {.data = "loop_end", .count = 8}}));
+ da_append(&ir_list, ((Ir) { .type = IR_RETURN, .ret = {.type = ARG_INT_LITERAL, .i64 = 69}}));
+ Ir_program prog = {0};
+ da_append(&prog.functions, ((Ir_function) {.name = {.data = "main", .count = 4}, .return_type = TYPE_I32, .body = ir_list}));
+ da_append(&prog.functions.items[0].argument_types, TYPE_I32);
+ da_append(&prog.functions.items[0].argument_types, TYPE_PTR);
+
+ if (!ir_backend.compile(prog, &sb)) {
+ return 1;
+ }
+ sb_write(1, sb);
+ return 0;
+}
+
+#include "ir_backend.c"
diff --git a/dlib.h b/dlib.h
@@ -0,0 +1,324 @@
+#ifndef DLIB_H
+#define DLIB_H 1
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#define da_append(da, new)\
+ do {\
+ if ((da)->capacity <= (da)->count) {\
+ (da)->capacity = (da)->capacity ? (da)->capacity * 2 : 16;\
+ (da)->items = realloc((da)->items, sizeof(*(da)->items) * (da)->capacity);\
+ }\
+ (da)->items[(da)->count++] = (new);\
+ } while(0)
+
+#define da_prepend(da, new)\
+ do {\
+ if ((da)->capacity <= (da)->count) {\
+ (da)->capacity = (da)->capacity ? (da)->capacity * 2 : 16;\
+ (da)->items = realloc((da)->items, sizeof(*(da)->items) * (da)->capacity);\
+ }\
+ memmove((da)->items + 1, (da)->items, sizeof(*(da)->items) * ((da)->count++));\
+ (da)->items[0] = (new);\
+ } while(0)
+
+#define da_reserve(da, len)\
+ do {\
+ if ((da)->capacity >= (len)) break;\
+ (da)->capacity = (len);\
+ (da)->items = realloc((da)->items, sizeof(*(da)->items) * (len));\
+ } while(0)
+
+#define da_delete(da, index)\
+ do {\
+ assert(index >= 0 && index < (da)->count);\
+ (da)->items[index] = (da)->items[--(da)->count];\
+ } while (0)
+
+typedef struct {
+ union {
+ char *items;
+ const char *data;
+ };
+ union {
+ ssize_t count;
+ ssize_t length;
+ };
+ ssize_t capacity;
+} string_builder;
+
+typedef struct {
+ union {
+ const char *items;
+ const char *data;
+ };
+ union {
+ ssize_t count;
+ ssize_t length;
+ };
+} string_view;
+
+#define sb_append_char da_append
+void sb_printf(string_builder *sb, const char *fmt, ...);
+void sb_append_cstr(string_builder *sb, const char *cstr);
+void sb_append_sv(string_builder *sb, string_view sv);
+
+void sb_read(string_builder *sb, int fd);
+void sb_read_file(string_builder *sb, int fd);
+void sb_write(int fd, string_builder sb);
+
+void sv_write(int fd, string_view sv);
+
+#define sv_literal(str) ((string_view){.data = (str), .count = (sizeof (str) - 1)})
+
+string_view sv_from_sb(string_builder sb);
+ssize_t sv_find_char(string_view sv, char c);
+
+string_view sv_chop(string_view sv, ssize_t count, string_view *rest);
+string_view sv_chop_delim(string_view sv, char c, string_view *rest);
+
+string_view sv_getline(string_view *sv);
+
+string_view sv_trim_left(string_view sv);
+string_view sv_trim_right(string_view sv);
+string_view sv_trim(string_view sv);
+
+string_view sv_substring(string_view sv, ssize_t start, ssize_t req_count);
+
+int sv_compare(string_view sv1, string_view sv2);
+bool sv_equal(string_view sv1, string_view sv2);
+
+
+#ifdef DLIB_IMPLEMENTATION
+
+void
+sb_printf(string_builder *sb, const char *fmt, ...) {
+ va_list args, args_copy;
+ va_start(args, fmt);
+ va_copy(args_copy, args);
+ int len = vsnprintf(NULL, 0, fmt, args_copy) + 1;
+ ssize_t new_size = sb->count + len;
+ da_reserve(sb, new_size);
+ va_end(args_copy);
+ vsnprintf(sb->items + sb->count, len, fmt, args);
+ va_end(args);
+ sb->count = new_size - 1;
+}
+
+void
+sb_append_cstr(string_builder *sb, const char *cstr)
+{
+ ssize_t len = strlen(cstr);
+ da_reserve(sb, sb->count + len);
+ strcpy(sb->items + sb->count, cstr);
+ sb->count += len;
+}
+
+void
+sb_append_sv(string_builder *sb, string_view sv)
+{
+ da_reserve(sb, sb->count + sv.count);
+ memcpy(sb->items + sb->count, sv.data, sv.count);
+ sb->count += sv.count;
+}
+
+void
+sb_read(string_builder *sb, int fd)
+{
+ ssize_t read_count;
+ ssize_t buf_size = 4096;
+ char *buf = malloc(buf_size);
+ do {
+ read_count = read(fd, buf, buf_size);
+ if (read_count < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ perror(NULL);
+ exit(1);
+ }
+ if (read_count == 0) {
+ break;
+ }
+ sb_append_sv(sb, ((string_view) {.data = buf, .length = read_count}));
+ if (read_count < buf_size) {
+ break;
+ }
+ } while (1);
+ free(buf);
+}
+
+void
+sb_read_file(string_builder *sb, int fd)
+{
+ ssize_t read_count;
+ ssize_t buf_size = 4096;
+ char *buf = malloc(buf_size);
+ do {
+ read_count = read(fd, buf, buf_size);
+ if (read_count < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ perror(NULL);
+ exit(1);
+ }
+ if (read_count == 0) {
+ break;
+ }
+ sb_append_sv(sb, ((string_view) {.data = buf, .length = read_count}));
+ } while (1);
+ free(buf);
+}
+
+void
+sb_write(int fd, string_builder sb)
+{
+ write(fd, sb.data, sb.length);
+}
+
+/* String view functions */
+
+void
+sv_write(int fd, string_view sv)
+{
+ write(fd, sv.data, sv.count);
+}
+
+string_view
+sv_from_sb(string_builder sb)
+{
+ return (string_view) {.data = sb.items, .count = sb.count};
+}
+
+ssize_t
+sv_find_char(string_view sv, char c)
+{
+ ssize_t pos = 0;
+ while (sv.count > pos && sv.data[pos] != c)
+ pos++;
+ for (pos = 0; pos < sv.count && sv.data[pos] != c; pos++);
+ return pos < sv.count ? pos : -1;
+}
+
+string_view
+sv_chop_delim(string_view sv, char c, string_view *rest)
+{
+ ssize_t pos = sv_find_char(sv, c);
+ string_view res = sv;
+ if (pos > 0) {
+ res.count = pos;
+
+ rest->count -= pos + 1;
+ rest->data += pos + 1;
+ }
+ return res;
+}
+
+string_view
+sv_chop(string_view sv, ssize_t count, string_view *rest)
+{
+ string_view left = {0};
+ ssize_t lcount, rcount;
+ if (count > sv.count) {
+ lcount = sv.count;
+ rcount = 0;
+ } else if (-count > sv.count) {
+ lcount = 0;
+ rcount = sv.count;
+ } else if (count < 0) {
+ lcount = sv.count + count;
+ rcount = -count;
+ } else {
+ lcount = count;
+ rcount = sv.count - count;
+ }
+ left.data = sv.data;
+ left.count = lcount;
+ if (rest) {
+ rest->data = sv.data + lcount;
+ rest->count = rcount;
+ }
+ return left;
+}
+
+string_view
+sv_trim_left(string_view sv)
+{
+ while (sv.count && isspace(sv.data[0])) {
+ sv.count--;
+ sv.data++;
+ }
+ return sv;
+}
+
+string_view
+sv_trim_right(string_view sv)
+{
+ while (sv.count && isspace(sv.data[sv.count - 1])) {
+ sv.count--;
+ }
+ return sv;
+}
+
+string_view
+sv_trim(string_view sv)
+{
+ return sv_trim_right(sv_trim_left(sv));
+}
+
+string_view
+sv_substring(string_view sv, ssize_t start, ssize_t req_count)
+{
+ string_view ss = {0};
+ if (start >= sv.count) return ss;
+ ss.count = (req_count < sv.count - start) ? req_count : sv.count - start;
+ ss.data = sv.data + start;
+ return ss;
+}
+
+int
+sv_compare(string_view sv1, string_view sv2)
+{
+ if (sv1.data == sv2.data && sv1.count == sv2.count) return 0;
+ signed char diff;
+ ssize_t pos;
+ for (pos = 0; pos < sv1.count; pos++) {
+ if (pos == sv2.count) return 1;
+ if ((diff = sv1.data[pos] - sv2.data[pos])) return diff;
+ }
+ return pos < sv2.count ? -1 : 0;
+}
+
+bool
+sv_equal(string_view sv1, string_view sv2)
+{
+ if (sv1.count != sv2.count) return false;
+ if (sv1.data == sv2.data) return true;
+ ssize_t pos;
+ for (pos = 0; pos < sv1.count && sv1.data[pos] == sv2.data[pos]; pos++);
+ return pos == sv1.count;
+}
+
+string_view
+sv_getline(string_view *sv)
+{
+ string_view line = sv_chop_delim(*sv, '\n', sv);
+ if (line.count && line.data[line.count - 1] == '\r') {
+ line.count--;
+ }
+ return line;
+}
+
+#endif // DLIB_IMPLEMENTATION
+
+#endif // DLIB_H
diff --git a/ir_backend.c b/ir_backend.c
@@ -0,0 +1,195 @@
+typedef struct {
+ void *loc;
+ void *end;
+} Temp_allocator;
+
+Temp_allocator temp_allocator = {0};
+
+void *
+temp_alloc(size_t size)
+{
+ if (!temp_allocator.loc) {
+ size_t allocator_size = 1024*1024;
+ temp_allocator.loc = malloc(allocator_size);
+ temp_allocator.end = temp_allocator.loc + allocator_size;
+ temp_allocator.loc = (void *)((intptr_t)(temp_allocator.loc + 0xF) & ~0xFul);
+ }
+ void *result = temp_allocator.loc;
+ temp_allocator.loc += (size + 0xF) & ~0xFul;
+ if (temp_allocator.loc >= temp_allocator.end) {
+ fprintf(stderr, "Error: Temp allocator out of memory\n");
+ exit(1);
+ }
+ return result;
+}
+
+const char *
+ir_arg_to_cstr(Ir_arg arg)
+{
+ int len;
+ char *str;
+ if (arg.type == ARG_INT_LITERAL) {
+ len = snprintf(NULL, 0, "%li", arg.i64) + 1;
+ str = temp_alloc(len);
+ snprintf(str, len, "%li", arg.i64);
+ } else if (arg.type == ARG_TEMP_STORAGE) {
+ len = snprintf(NULL, 0, "%%%li", arg.temp_id) + 1;
+ str = temp_alloc(len);
+ snprintf(str, len, "%%%li", arg.temp_id);
+ } else if (arg.type == ARG_ADDRESS) {
+ len = snprintf(NULL, 0, "0x%p", arg.addr) + 1;
+ str = temp_alloc(len);
+ snprintf(str, len, "0x%p", arg.addr);
+ } else if (arg.type == ARG_CSTRING_LITERAL) {
+ len = strlen(arg.cstr);
+ str = temp_alloc(len + 3);
+ memcpy(str + 1, arg.cstr, len);
+ str[0] = '"';
+ str[len + 1] = '"';
+ str[len + 2] = '\0';
+ } else {
+ assert(0 && "Invalid arg");
+ }
+ return str;
+}
+
+const char *
+ir_binop_to_cstr(enum Ir_binop binop)
+{
+ const char *str;
+ switch (binop) {
+ case OP_ADD: str = "+"; break;
+ case OP_SUB: str = "-"; break;
+ case OP_MUL: str = "*"; break;
+ case OP_DIV: str = "/"; break;
+ case OP_LT: str = "<"; break;
+ case OP_GT: str = ">"; break;
+ case OP_EQ: str = "=="; break;
+ case OP_LEQ: str = "<="; break;
+ case OP_GEQ: str = "=>"; break;
+ default: assert(0 && "Unknown binop");
+ }
+ return str;
+}
+
+const char *
+ir_type_to_cstr(enum Ir_type type)
+{
+ const char *str;
+ switch (type) {
+ case TYPE_VOID: str = "void"; break;
+ case TYPE_I8: str = "i8"; break;
+ case TYPE_U8: str = "u8"; break;
+ case TYPE_I16: str = "i16"; break;
+ case TYPE_U16: str = "u16"; break;
+ case TYPE_I32: str = "i32"; break;
+ case TYPE_U32: str = "u32"; break;
+ case TYPE_I64: str = "i64"; break;
+ case TYPE_U64: str = "u64"; break;
+ case TYPE_PTR: str = "ptr"; break;
+ case TYPE_F32: str = "f32"; break;
+ case TYPE_F64: str = "f64"; break;
+ default: assert(0 && "Unknown type");
+ }
+ return str;
+}
+
+bool
+ir_compile_body(Ir_list irs, string_builder *sb)
+{
+ size_t i, j;
+ Ir ir;
+ Temp_allocator saved_allocator;
+ for (i = 0; i < irs.count; i++) {
+ saved_allocator = temp_allocator;
+ ir = irs.items[i];
+ assert(ir.result.type == ARG_TEMP_STORAGE || ir.result.type == ARG_NONE);
+ sb_append_cstr(sb, " ");
+ switch (ir.type) {
+ case IR_LOAD:
+ assert(ir.result.type != ARG_NONE);
+ sb_printf(sb, " %s = load(%s)", ir_arg_to_cstr(ir.result), ir_arg_to_cstr(ir.load.loc));
+ break;
+ case IR_STORE:
+ assert(ir.result.type == ARG_NONE);
+ sb_printf(sb, " store(%s, %s)", ir_arg_to_cstr(ir.store.loc), ir_arg_to_cstr(ir.store.data));
+ break;
+ case IR_ASSIGN:
+ assert(ir.result.type != ARG_NONE);
+ sb_printf(sb, " %s = %s", ir_arg_to_cstr(ir.result), ir_arg_to_cstr(ir.assign));
+ break;
+ case IR_BINOP:
+ assert(ir.result.type != ARG_NONE);
+ sb_printf(sb, " %s = %s %s %s", ir_arg_to_cstr(ir.result), ir_arg_to_cstr(ir.binop.left), ir_binop_to_cstr(ir.binop.op), ir_arg_to_cstr(ir.binop.right));
+ break;
+ case IR_CALL:
+ assert(ir.result.type != ARG_NONE);
+ if (ir.call.indirect.type != ARG_NONE) {
+ sb_printf(sb, " %s = call %s (", ir_arg_to_cstr(ir.result), ir_arg_to_cstr(ir.call.indirect));
+ } else {
+ sb_printf(sb, " %s = call %.*s (", ir_arg_to_cstr(ir.result), ir.call.name.count, ir.call.name.data);
+ }
+ for (j = 0; j < ir.call.args.count; j++) {
+ if (j) {
+ sb_append_cstr(sb, ", ");
+ }
+ sb_append_cstr(sb, ir_arg_to_cstr(ir.call.args.items[j]));
+ }
+ sb_append_char(sb, ')');
+ break;
+ case IR_RETURN:
+ assert(ir.result.type == ARG_NONE);
+ sb_printf(sb, " return %s", ir_arg_to_cstr(ir.ret));
+ break;
+ case IR_LABEL:
+ assert(ir.result.type == ARG_NONE);
+ sb_printf(sb, "#%.*s:", ir.label.count, ir.label.data);
+ break;
+ case IR_JUMP:
+ assert(ir.result.type == ARG_NONE);
+ sb_printf(sb, " jump #%.*s", ir.jump.label.count, ir.jump.label.data);
+ break;
+ case IR_JUMP_IF_NOT:
+ assert(ir.result.type == ARG_NONE);
+ sb_printf(sb, " jump_if_not %s #%.*s", ir_arg_to_cstr(ir.jump.cond), ir.jump.label.count, ir.jump.label.data);
+ break;
+ default:
+ fprintf(stderr, "Error: Unknown IR %lu\n", i);
+ temp_allocator = saved_allocator;
+ return false;
+ }
+ sb_append_char(sb, '\n');
+ temp_allocator = saved_allocator;
+ }
+ return true;
+}
+
+bool
+ir_compile(Ir_program prog, string_builder *sb)
+{
+ size_t i, j;
+ Ir_function function;
+ Temp_allocator saved_allocator;
+ for (i = 0; i < prog.functions.count; i++) {
+ saved_allocator = temp_allocator;
+ if (i) {
+ sb_append_char(sb, '\n');
+ }
+ function = prog.functions.items[i];
+ sb_printf(sb, "$%.*s(", function.name.count, function.name.data);
+ for (j = 0; j < function.argument_types.count; j++) {
+ if (j) {
+ sb_append_cstr(sb, ", ");
+ }
+ sb_append_cstr(sb, ir_type_to_cstr(function.argument_types.items[j]));
+ }
+ sb_printf(sb, ") -> %s {\n", ir_type_to_cstr(function.return_type));
+ if (!ir_compile_body(function.body, sb)) {
+ temp_allocator = saved_allocator;
+ return false;
+ }
+ sb_append_cstr(sb, "}\n");
+ temp_allocator = saved_allocator;
+ }
+ return true;
+}