backend

Unnamed repository; edit this file 'description' to name the repository.
git clone https://git.deepztream.com/backend
Log | Files | Refs

commit 372ae52525e47ba31d84ece3f558b8ead2cb1000
Author: William Djupström <william@deepztream.com>
Date:   Sat, 22 Aug 2026 22:33:46 +0200

Initialise repo

Diffstat:
A.gitignore | 2++
AMakefile | 6++++++
Abackend.c | 207+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adlib.h | 324+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Air_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; +}