commit 76bf8fafa4fa760fbe7c5ecf65ef60c753a1b7a9
parent a670812602ab986eb7543f905b4734b2f94de40c
Author: citbl <citbl@citbl.org>
Date: Thu, 25 Jun 2026 17:14:22 +1000
gen
Diffstat:
| A | src/gen.c | | | 1428 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | src/gen.h | | | 20 | -------------------- |
| D | src/gen/gen.c | | | 1372 | ------------------------------------------------------------------------------- |
3 files changed, 1428 insertions(+), 1392 deletions(-)
diff --git a/src/gen.c b/src/gen.c
@@ -0,0 +1,1428 @@
+#include "gen.h"
+#include "parser.h"
+#include "utils.h"
+#include "sem.h"
+
+#include <_string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/param.h>
+
+static gcc_jit_type* type_boolean;
+static gcc_jit_type* type_int;
+static gcc_jit_type* type_i8;
+static gcc_jit_type* type_i16;
+static gcc_jit_type* type_i32;
+static gcc_jit_type* type_i64;
+static gcc_jit_type* type_i128;
+static gcc_jit_type* type_u8;
+static gcc_jit_type* type_u16;
+static gcc_jit_type* type_u32;
+static gcc_jit_type* type_u64;
+static gcc_jit_type* type_u128;
+static gcc_jit_type* type_f32;
+static gcc_jit_type* type_f64;
+static gcc_jit_type* type_f128;
+static gcc_jit_type* type_uint;
+static gcc_jit_type* type_void;
+static gcc_jit_type* type_cstr;
+static gcc_jit_type* type_char;
+static gcc_jit_type* type_voidp;
+
+static const char* type_func = "function";
+static const char* type_var = "variable";
+static const char* type_struct = "struct";
+
+static gcc_jit_location*
+loc_from_node(Gen* gen, Node* node)
+{
+ if (node->filename == NULL) return NULL;
+ return gcc_jit_context_new_location(gen->ctx, node->filename, node->line, node->col);
+}
+
+static void
+push_loop(Gen* gen, gcc_jit_block* brk, gcc_jit_block* cont)
+{
+ LoopContext* lctx = (LoopContext*)malloc(sizeof(LoopContext));
+ if (lctx == NULL) { panic("push_loop: could not alloc"); }
+ lctx->break_target = brk;
+ lctx->continue_target = cont;
+ lctx->prev = gen->loop;
+ gen->loop = lctx;
+}
+
+static void
+pop_loop(Gen* gen)
+{
+ LoopContext* lctx = gen->loop;
+ gen->loop = lctx->prev;
+ free(lctx);
+}
+
+static inline gcc_jit_block*
+current_break(Gen* gen)
+{
+ return gen->loop ? gen->loop->break_target : NULL;
+}
+
+static inline gcc_jit_block*
+current_continue(Gen* gen)
+{
+ return gen->loop ? gen->loop->continue_target : NULL;
+}
+
+__attribute__((unused)) static const char*
+get_english_type(gcc_jit_type* T)
+{
+ return gcc_jit_object_get_debug_string(gcc_jit_type_as_object(T));
+}
+
+Gen
+gen_init(Scope* scope, const char* src, Node* node, bool quiet)
+{
+ if (scope == NULL || src == NULL) { panic("gen_init: no Scope or AST provided"); }
+
+ gcc_jit_context* ctx;
+
+ ctx = gcc_jit_context_acquire();
+
+ if (!ctx) { panic("could not acquire gcc jit context"); }
+
+ // needs loc* to work
+ // gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DEBUGINFO,
+ // 1); high level
+
+ gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DEBUGINFO, 1);
+
+ if (quiet == false) { gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, 1); }
+
+ // gcc_jit_context_set_bool_option(ctx,
+ // GCC_JIT_BOOL_OPTION_DUMP_SUMMARY, 1);
+
+ gcc_jit_context_set_str_option(ctx, GCC_JIT_STR_OPTION_PROGNAME, "ox");
+ // keep FP
+ gcc_jit_context_add_driver_option(ctx, "-fno-omit-frame-pointer");
+
+ gcc_jit_context_set_int_option(ctx,
+ GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL,
+ /*0-3 for O3*/ 0);
+
+ type_boolean = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_BOOL);
+
+ type_int = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT64_T);
+ type_i8 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT8_T);
+ type_i16 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT16_T);
+ type_i32 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT32_T);
+ type_i64 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT64_T);
+ type_i128 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT128_T);
+
+ type_u8 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT8_T);
+ type_u16 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT16_T);
+ type_u32 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT32_T);
+ type_u64 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT64_T);
+ type_u128 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT128_T);
+
+ type_f32 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_FLOAT);
+ type_f64 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_DOUBLE);
+ type_f128 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG_DOUBLE);
+
+ type_uint = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT64_T);
+ type_char = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR);
+ type_void = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_VOID);
+ type_cstr = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_CONST_CHAR_PTR);
+ type_voidp = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_VOID_PTR);
+ /* gcc_jit_type_dyncast_function_ptr_type; */
+
+ gcc_jit_location* loc = gcc_jit_context_new_location(ctx, node->filename, 0, 0);
+
+ gcc_jit_param* pm_puts[] = { gcc_jit_context_new_param(ctx, loc, type_cstr, "s") };
+ gcc_jit_function* fn_puts = gcc_jit_context_new_function(ctx, loc, GCC_JIT_FUNCTION_IMPORTED, type_int, "puts", 1, pm_puts, 0);
+
+ gcc_jit_param* pm_printf[] = { gcc_jit_context_new_param(ctx, loc, type_cstr, "fmt") };
+ gcc_jit_function* fn_printf = gcc_jit_context_new_function(ctx,
+ loc,
+ GCC_JIT_FUNCTION_IMPORTED,
+ type_int,
+ "printf",
+ 1,
+ pm_printf,
+ /*is_variadic=*/1);
+
+ return (Gen) {
+ .ctx = ctx,
+ .scope = scope,
+ .prev_func = NULL,
+ .curr_func = NULL,
+ .prev_block = NULL,
+ .curr_block = NULL,
+ .puts_fn = fn_puts,
+ .printf_fn = fn_printf,
+ .src = src,
+ };
+}
+
+static void
+push_scope(Gen* gen)
+{
+ printf("push_scope\n");
+ Scope* scope = calloc(1, sizeof(Scope));
+ *scope = (Scope) { 0 };
+ scope->symbols = (Symbol**)calloc(16, sizeof(Symbol*));
+ if (scope->symbols == NULL) panic("push_scope: symbols: could not alloc");
+ scope->cap = 16;
+ scope->len = 0;
+
+ scope->parent = gen->scope;
+ gen->scope = scope;
+}
+
+static void
+pop_scope(Gen* gen)
+{
+ printf("pop_scope\n");
+ Scope* s = gen->scope;
+ gen->scope = s->parent;
+}
+
+static Symbol*
+lookup_symbol(Gen* gen, Scope* scope, Span name)
+{
+ const char* ident_name = span_str(gen->src, name, (char[IDENTSZ]) { 0 });
+ printf("lookup_symbol: %s\n", ident_name);
+ // Look up the symbol in the current scope // TODO look up in parent
+ // scope with utility function and recursion
+ for (size_t i = 0; i < scope->len; i++) {
+ Symbol* sym = scope->symbols[i];
+ const char* sym_name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 });
+ // printf("symbol %s\n", sym_name);
+ // printf("ident %s\n", ident_name);
+ if (strcmp(sym_name, ident_name) == 0) {
+ printf("found symbol %s\n", sym_name);
+ return sym;
+ }
+ }
+ printf("find_symbol: not found locally: %s\n", ident_name);
+
+ if (scope->parent != NULL) {
+ printf("looking up symbol %s in parent\n", ident_name);
+ return lookup_symbol(gen, scope->parent, name);
+ }
+
+ return NULL;
+}
+
+static void
+add_symbol(Gen* gen, Symbol* sym)
+{
+ if (gen->scope->len == gen->scope->cap) {
+ gen->scope->cap *= 2;
+ gen->scope->symbols = (Symbol**)realloc(gen->scope->symbols, sizeof(Symbol*) * gen->scope->cap);
+ }
+
+ gen->scope->symbols[gen->scope->len++] = sym;
+
+ printf("add_symbol: we now have %zu symbols: ", gen->scope->len);
+ for (size_t i = 0; i < gen->scope->len; i++) {
+ Symbol* symy = gen->scope->symbols[i];
+ const char* name = span_str(gen->src, symy->name, (char[IDENTSZ]) { 0 });
+ printf("%s, ", name);
+ }
+ printf("\n");
+}
+
+static gcc_jit_rvalue*
+handle_ident_call(Gen* gen, Node* node)
+{
+ // Look up the symbol in the current scope // TODO look up in parent
+ // scope with utility function and recursion
+ Symbol* sym = lookup_symbol(gen, gen->scope, node->data.ident.name);
+
+ if (sym == NULL) {
+ panic_at(node,
+ "handle_ident_call: undefined variable: %s\n",
+ span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 }));
+ }
+ return gcc_jit_lvalue_as_rvalue(sym->d.lvalue);
+}
+
+static gcc_jit_rvalue* handle_expr(Gen*, Node*);
+
+static inline int
+is_intlike(gcc_jit_context* ctx, gcc_jit_type* t) // TODO support all new types
+{
+ return t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT) || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_INT)
+ || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_SHORT)
+ || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_SHORT)
+ || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG)
+ || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG)
+ || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG_LONG)
+ || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG)
+ || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT64_T) || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT32_T)
+ || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_CHAR)
+ || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR);
+}
+
+static inline int
+is_floatlike(gcc_jit_context* ctx, gcc_jit_type* t)
+{
+ return t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_FLOAT) || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_DOUBLE)
+ || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG_DOUBLE);
+}
+
+static inline int
+is_cstr(gcc_jit_context* ctx, gcc_jit_type* t)
+{
+ return t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_CONST_CHAR_PTR);
+}
+
+// > < >= <= + - / * %
+static gcc_jit_rvalue*
+handle_bin_expr(Gen* gen, Node* node, int gcc_jit_op, bool cmp)
+{
+ Node* lhs = node->data.binary_expr.lhs;
+ Node* rhs = node->data.binary_expr.rhs;
+ gcc_jit_context* ctx = gen->ctx;
+ gcc_jit_location* loc = loc_from_node(gen, node);
+
+ gcc_jit_rvalue* L = handle_expr(gen, lhs);
+ gcc_jit_rvalue* R = handle_expr(gen, rhs);
+ gcc_jit_type* Lt = gcc_jit_rvalue_get_type(L);
+ gcc_jit_type* Rt = gcc_jit_rvalue_get_type(R);
+ gcc_jit_rvalue* result = NULL;
+
+ // floats: cast both to double
+ if (is_floatlike(ctx, Lt) || is_floatlike(ctx, Rt)) {
+ gcc_jit_type* T = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_DOUBLE);
+ L = gcc_jit_context_new_cast(ctx, loc, L, T);
+ R = gcc_jit_context_new_cast(ctx, loc, R, T);
+ result = cmp ? gcc_jit_context_new_comparison(ctx, loc, gcc_jit_op, L, R)
+ : gcc_jit_context_new_binary_op(ctx, loc, gcc_jit_op, T, L, R);
+ ;
+ }
+
+ // integers: cast both to signed/unsigned long long (cheap, predictable)
+ if (is_intlike(ctx, Lt) && is_intlike(ctx, Rt)) {
+ int any_unsigned = (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR))
+ || (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_SHORT))
+ || (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_INT))
+ || (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG))
+ || (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG))
+ || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR))
+ || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_SHORT))
+ || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_INT))
+ || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG))
+ || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG));
+
+ gcc_jit_type* T = gcc_jit_context_get_type(ctx, any_unsigned ? GCC_JIT_TYPE_UINT64_T : GCC_JIT_TYPE_INT64_T);
+
+ L = gcc_jit_context_new_cast(ctx, loc, L, T);
+ R = gcc_jit_context_new_cast(ctx, loc, R, T);
+ result = cmp ? gcc_jit_context_new_comparison(ctx, loc, gcc_jit_op, L, R)
+ : gcc_jit_context_new_binary_op(ctx, loc, gcc_jit_op, T, L, R);
+ }
+
+ // pointers: either reject or compare addresses (UB in C for unrelated objs).
+ if (
+#ifdef GCC_JIT_HAVE_gcc_jit_type_is_pointer
+ gcc_jit_type_is_pointer(Lt) && gcc_jit_type_is_pointer(Rt)
+#else
+ 0
+#endif
+ ) {
+ gcc_jit_type* T = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG);
+ L = gcc_jit_context_new_cast(ctx, loc, L, T);
+ R = gcc_jit_context_new_cast(ctx, loc, R, T);
+ result = cmp ? gcc_jit_context_new_comparison(ctx, loc, gcc_jit_op, L, R)
+ : gcc_jit_context_new_binary_op(ctx, loc, gcc_jit_op, T, L, R);
+ }
+
+ return result;
+}
+
+static gcc_jit_rvalue*
+handle_binary_expr(Gen* gen, Node* node)
+{
+ int gcc_jit_op = -1;
+ OpType op = node->data.binary_expr.op;
+
+ switch (op) {
+ case OP_PLUS:
+ gcc_jit_op = GCC_JIT_BINARY_OP_PLUS;
+ return handle_bin_expr(gen, node, gcc_jit_op, false);
+ break;
+ case OP_MINUS:
+ gcc_jit_op = GCC_JIT_BINARY_OP_MINUS;
+ return handle_bin_expr(gen, node, gcc_jit_op, false);
+ break;
+ case OP_MUL:
+ gcc_jit_op = GCC_JIT_BINARY_OP_MULT;
+ return handle_bin_expr(gen, node, gcc_jit_op, false);
+ break;
+ case OP_DIV:
+ gcc_jit_op = GCC_JIT_BINARY_OP_DIVIDE;
+ return handle_bin_expr(gen, node, gcc_jit_op, false);
+ break;
+ case OP_MOD:
+ gcc_jit_op = GCC_JIT_BINARY_OP_MODULO;
+ return handle_bin_expr(gen, node, gcc_jit_op, false);
+ break;
+ case OP_LT:
+ gcc_jit_op = GCC_JIT_COMPARISON_LT;
+ break;
+ case OP_GT:
+ gcc_jit_op = GCC_JIT_COMPARISON_GT;
+ break;
+ case OP_GT_EQ:
+ gcc_jit_op = GCC_JIT_COMPARISON_GE;
+ break;
+ case OP_LT_EQ:
+ gcc_jit_op = GCC_JIT_COMPARISON_LE;
+ break;
+ default:
+ printf("handle_binary_expr unhandled OpType %d (can be ignored in var assignment "
+ "(for now))\n",
+ op);
+ return NULL;
+ break;
+ }
+
+ if (gcc_jit_op < 0) {
+ // unsupported types (e.g., strings or structs)
+ printf("handle_binary_expr OP_LT: unsupported operand types\n");
+ return NULL;
+ }
+
+ return handle_bin_expr(gen, node, gcc_jit_op, true);
+}
+
+static gcc_jit_rvalue*
+handle_unary_expr(Gen* gen, Node* node)
+{
+ gcc_jit_context* ctx = gen->ctx;
+ gcc_jit_block* bb = gen->curr_block;
+
+ Node* opnd = node->data.unary_expr.operand;
+ Symbol* sym = lookup_symbol(gen, gen->scope, opnd->data.ident.name);
+ if (sym == NULL) {
+ panic_at(node,
+ "handle_unary_expr: undefined variable: %s\n",
+ span_str(gen->src, opnd->data.ident.name, (char[IDENTSZ]) { 0 }));
+ }
+
+ gcc_jit_lvalue* lv = sym->d.lvalue;
+ if (lv == NULL) {
+ printf("handle_unary_expr: no lvalue\n");
+ return NULL;
+ }
+ gcc_jit_rvalue* orig = gcc_jit_lvalue_as_rvalue(lv);
+ gcc_jit_type* ty = gcc_jit_rvalue_get_type(orig);
+ gcc_jit_rvalue* one = gcc_jit_context_one(ctx, ty);
+
+ gcc_jit_rvalue* inc = gcc_jit_context_new_binary_op(ctx, NULL, GCC_JIT_BINARY_OP_PLUS, ty, orig, one);
+ switch (node->data.unary_expr.op) {
+ case OPER_POSTINC: { // i++
+
+ if (!sym->is_variadic) {
+ panic_at(node,
+ "OPER_POSTINC: cannot change non variadic variable %s\n",
+ span_str(gen->src, opnd->data.ident_type.name, (char[IDENTSZ]) { 0 }));
+ } else if (sym->is_comp_time) {
+ panic_at(node,
+ "OPER_POSTINC: cannot change comp time constant %s\n",
+ span_str(gen->src, opnd->data.ident_type.name, (char[IDENTSZ]) { 0 }));
+ }
+ gcc_jit_block_add_assignment(bb, NULL, lv, inc);
+ return orig; // return value before incr as ++ is postfix
+
+ break;
+ }
+ default:
+ printf("handle_unary_expr, unhandled op %d\n", node->data.unary_expr.op);
+ return NULL;
+ break;
+ }
+ return NULL;
+}
+
+static gcc_jit_rvalue*
+emit_literal_bool(Gen* gen, Node* node)
+{
+ int v = node->data.boolean.value ? 1 : 0;
+ return gcc_jit_context_new_rvalue_from_int(gen->ctx, type_boolean, v);
+}
+
+static gcc_jit_rvalue*
+emit_literal_string(Gen* gen, Node* node)
+{
+ size_t len = node->data.string.value.end - node->data.string.value.start;
+ char* str = calloc(len + 1, sizeof(char));
+ if (str == NULL) panic_at(node, "emit_literal_string: could not alloc");
+ memcpy(str, gen->src + node->data.string.value.start, len);
+ str[len] = '\0';
+ return gcc_jit_context_new_string_literal(gen->ctx, str);
+}
+
+static gcc_jit_rvalue*
+emit_literal_int(Gen* gen, Node* node)
+{
+ return gcc_jit_context_new_rvalue_from_int(gen->ctx, type_int, (int)node->data.number.value);
+}
+
+static gcc_jit_rvalue*
+emit_literal_float(Gen* gen, Node* node)
+{
+ return gcc_jit_context_new_rvalue_from_double(gen->ctx, type_f32, node->data.number.value);
+}
+
+static void
+build_program(Gen* gen, Node* node)
+{
+ size_t cnt = node->data.program.len;
+ for (size_t i = 0; i < cnt; i++) {
+ gen_next(gen, node->data.program.decl[i]);
+ }
+}
+
+static gcc_jit_rvalue*
+lower_builtin_print(Gen* gen, Node* node)
+{
+ printf("lower_builtin_print\n");
+
+ size_t argc = node->data.call_expr.len;
+
+ // for (size_t i = 0; i < argc; i++) {
+ // Node* arg = node->data.call_expr.args[i];
+ // }
+
+ // 1-arg, treat as puts(arg)
+ if (argc == 1) {
+ gcc_jit_rvalue* arg = handle_expr(gen, node->data.call_expr.args[0]);
+ gcc_jit_type* t = gcc_jit_rvalue_get_type(arg);
+ gcc_jit_location* loc = loc_from_node(gen, node->data.call_expr.args[0]);
+ // print a string
+ if (is_cstr(gen->ctx, t)) {
+ gcc_jit_rvalue* args[] = { arg };
+ return gcc_jit_context_new_call(gen->ctx, loc, gen->puts_fn, 1, args);
+ }
+ // print an integer or bool
+ else if (is_intlike(gen->ctx, t) || t == type_boolean) {
+ // cast to int for a clean %d
+ // gcc_jit_type* t_int
+ // = gcc_jit_context_get_type(gen->ctx, GCC_JIT_TYPE_INT64_T);
+ gcc_jit_rvalue* fmt = gcc_jit_context_new_string_literal(gen->ctx, "%d\n");
+ gcc_jit_rvalue* ival = arg;
+ gcc_jit_rvalue* args[] = { fmt, ival };
+ return gcc_jit_context_new_call(gen->ctx, loc, gen->printf_fn, 2, args);
+ }
+ // cast common cases to const char*
+ // if (gcc_jit_rvalue_get_type(arg) != type_cstr) arg =
+ // gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node),
+ // arg, type_cstr);
+
+ // gcc_jit_rvalue* args[] = { arg };
+ // return gcc_jit_context_new_call(
+ // gen->ctx, loc_from_node(gen, node), gen->puts_fn, 1, args);
+
+ // gcc_jit_type* type_of_args = gcc_jit_rvalue_get_type(arg);
+ // switch (type_of_args) {
+ // case GCC_JIT_TYPE_CONST_CHAR_PTR:
+
+ // break;
+ // default:
+ // printf("lower_builtin_print: unhandled type passed %s\n",
+ // type_of_args); break;
+ // }
+ }
+
+ return NULL;
+
+ // softpanic("we don't currently handle formatted strings to print");
+
+ // n>=1, treat as printf(fmt, ...) // Part of TODO about args as list
+ // and not
+ //
+ // through each args, form the ("formatted %s string %d etc.", str,
+ // intv) for clib's printf
+
+ // TODO we're talking about formatting here, which we plan on doing as a
+ // string interpolation, something along the lines of {{variable}}
+ // without defining its type would involve lookup split of the string
+ // and then formatting
+
+ // we need to discuss and decide what we'd do when the user inevitably
+ // would print out a ref to a struct. Do we say [[struct]] or do we have
+ // some automatic unwrap and display of struct data... probably, yes.
+
+ gcc_jit_rvalue** args = (gcc_jit_rvalue**)calloc(argc, sizeof(gcc_jit_rvalue*));
+
+ for (size_t i = 0; i < argc; i++) {
+ gcc_jit_rvalue* arg = handle_expr(gen, node->data.call_expr.args[i]);
+ if (i == 0) {
+ if (gcc_jit_rvalue_get_type(arg) != type_cstr) {
+ // note this is probably not going to work as
+ // limited cast supported and string isn't one
+ // of them
+ arg = gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), arg, type_cstr);
+ }
+ } else {
+ //
+ // simple widening for common scalar types
+ //
+ gcc_jit_type* ty = gcc_jit_rvalue_get_type(arg);
+ if (ty == type_int) {
+ arg = gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), arg, type_cstr);
+ } else if (ty == type_f32) { // TODO print() for other types
+ // variadics already promote float→double;
+ // double is
+ } else if (ty == type_cstr) {
+ // leave as const char*
+ } else if (ty == type_boolean) {
+ arg = gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), arg, type_cstr);
+ } else {
+ // fallback: pass pointer as void*
+ arg = gcc_jit_context_new_cast(gen->ctx,
+ loc_from_node(gen, node),
+ arg,
+ gcc_jit_context_get_type(gen->ctx, GCC_JIT_TYPE_VOID_PTR));
+ }
+ }
+ // TODO auto grow
+ args[i] = arg;
+ }
+ return gcc_jit_context_new_call(gen->ctx, NULL, gen->printf_fn, argc, args);
+ return NULL;
+}
+
+static gcc_jit_function*
+lookup_function(Gen* gen, Scope* scope, const char* func_name)
+{
+ for (size_t i = 0; i < scope->len; i++) {
+ Symbol* sym = scope->symbols[i];
+
+ if (sym->ctype == type_voidp) {
+ const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 });
+ if (strcmp(name, func_name) == 0) {
+ return sym->d.funcvalue; //
+ }
+ }
+ }
+
+ printf("lookup_function: not found locally: %s\n", func_name);
+
+ if (scope->parent != NULL) {
+ printf("looking up function %s in parent\n", func_name);
+ return lookup_function(gen, scope->parent, func_name);
+ }
+
+ return NULL;
+}
+
+static gcc_jit_rvalue*
+handle_func_call(Gen* gen, Node* node)
+{
+ Node* fcallee = node->data.call_expr.callee;
+ const char* func_name = span_str(gen->src, fcallee->data.ident.name, (char[IDENTSZ]) { 0 });
+
+ // short circuit to print
+ if (strcmp(func_name, "print") == 0) return lower_builtin_print(gen, node);
+
+ gcc_jit_function* callee = lookup_function(gen, gen->scope, func_name);
+ gcc_jit_location* loc = loc_from_node(gen, node);
+
+ // args handling
+ size_t argc = node->data.call_expr.len;
+
+ // alloc args
+ gcc_jit_rvalue** args = NULL;
+
+ if (argc > 0) {
+ args = calloc(argc, sizeof *args); // or alloca, or a fixed upper bound
+ for (size_t i = 0; i < argc; ++i) {
+ args[i] = handle_expr(gen, node->data.call_expr.args[i]);
+ }
+ }
+
+ for (size_t i = 0; i < argc; i++) {
+ args[i] = handle_expr(gen, node->data.call_expr.args[i]);
+ }
+
+ /*
+ When generating the identifier expression message in the AST:
+ Lookup env["message"] -> you get a gcc_jit_param* or gcc_jit_lvalue*.
+ Use gcc_jit_param_as_rvalue / gcc_jit_lvalue_as_rvalue.
+
+ gcc_jit_rvalue *msg = gcc_jit_param_as_rvalue(callee_param);
+
+ gcc_jit_rvalue *args[1] = { msg };
+ gcc_jit_block_add_eval(
+ b, loc,
+ gcc_jit_context_new_call(ctx, loc, print_fn, 1, args));
+ */
+
+ // gcc_jit_rvalue *msg = gcc_jit_param_as_rvalue(callee_param);
+ // gcc_jit_rvalue *args[1] = { msg };
+ assert(callee != NULL && "callee not found");
+ return gcc_jit_context_new_call(gen->ctx, loc, callee, argc, args);
+
+ // TODO consider
+ /*
+ // calling bob() directly
+ void emit_direct_call(gcc_jit_context *ctxt, SymbolId bob, gcc_jit_function *caller) {
+ GccObj *cal = ensure_func(ctxt, bob);
+ gcc_jit_block *b = gcc_jit_function_new_block(caller, "call_bob");
+ gcc_jit_rvalue *args[] = {};
+ gcc_jit_block_add_eval(b, NULL, gcc_jit_context_new_call(ctxt, cal->func, 0, args));
+ gcc_jit_block_end_with_void_return(b, NULL);
+ }
+ */
+
+ // return gcc_jit_context_new_call(gen->ctx, NULL, callee, 0, NULL);
+
+ // TODO handle return values
+ // almost unrelated could be useful to deal with return values:
+ // gcc_jit_rvalue* rv = handle_func_call(gen, node);
+ // if (rv) { gcc_jit_block_add_eval(gen->curr_block, loc_from_node(gen, node), rv); }
+ // return false;
+ return NULL;
+}
+
+static gcc_jit_rvalue*
+handle_expr(Gen* gen, Node* node)
+{
+ switch (node->type) {
+ case NODE_INT_LITERAL:
+ return emit_literal_int(gen, node);
+ break;
+ case NODE_FLOAT_LITERAL:
+ return emit_literal_float(gen, node);
+ break;
+ case NODE_STRING_LITERAL:
+ return emit_literal_string(gen, node);
+ break;
+ case NODE_BOOL_LITERAL:
+ return emit_literal_bool(gen, node);
+ case NODE_CALL_EXPR:
+ return handle_func_call(gen, node);
+ break;
+ case NODE_IDENT:
+ return handle_ident_call(gen, node);
+ break;
+ case NODE_UNARY_EXPR: // ++ etc.
+ return handle_unary_expr(gen, node);
+ break;
+ case NODE_BINARY_EXPR:
+ return handle_binary_expr(gen, node);
+ break;
+
+ // case NODE_IDENT: {
+ // return NULL; // fixme
+ // break;
+ // }
+ default:
+ printf("handle_expr unhandled, %s\n", node_type_str(node->type));
+ }
+ return NULL;
+}
+
+static gcc_jit_type*
+ox_type_to_c_type(Gen* gen, Node* node)
+{
+ if (node == NULL) return type_void;
+
+ const char* type_name = span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 });
+
+ if (strcmp(type_name, "int") == 0) {
+ return type_int;
+
+ } else if (strcmp(type_name, "i8") == 0) {
+ return type_i8;
+ } else if (strcmp(type_name, "i16") == 0) {
+ return type_i16;
+ } else if (strcmp(type_name, "i32") == 0) {
+ return type_i32;
+ } else if (strcmp(type_name, "i64") == 0) {
+ return type_i64;
+ } else if (strcmp(type_name, "i128") == 0) {
+ return type_i128;
+
+ } else if (strcmp(type_name, "u8") == 0) {
+ return type_u8;
+ } else if (strcmp(type_name, "u16") == 0) {
+ return type_u16;
+ } else if (strcmp(type_name, "u32") == 0) {
+ return type_u32;
+ } else if (strcmp(type_name, "u64") == 0) {
+ return type_u64;
+ } else if (strcmp(type_name, "u128") == 0) {
+ return type_u128;
+
+ } else if (strcmp(type_name, "f32") == 0) {
+ return type_f32;
+ } else if (strcmp(type_name, "f64") == 0) {
+ return type_f64;
+ } else if (strcmp(type_name, "f128") == 0) {
+ return type_f128;
+
+ } else if (strcmp(type_name, "str") == 0) {
+ return type_cstr;
+ } else if (strcmp(type_name, "float") == 0) {
+ return type_f32;
+ } else if (strcmp(type_name, "double") == 0) {
+ return type_f64;
+ } else if (strcmp(type_name, "longdouble") == 0) {
+ return type_f128;
+ } else if (strcmp(type_name, "uint") == 0) {
+ return type_uint;
+ } else if (strcmp(type_name, "void") == 0) {
+ return type_void;
+ } else {
+ // TODO custom user types, lookup by name to see if it exists, and if it does allow it.
+ // A user type is Capitalised and should be listed somewhere...
+ // otherwise panic
+ panic_at(node, "Unknown type in gen: %s", type_name);
+ }
+ return NULL;
+}
+
+static void
+print_symbols_here(Gen* gen)
+{
+ for (size_t i = 0; i < gen->scope->len; i++) {
+ Symbol* sym = gen->scope->symbols[i];
+ const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 });
+ printf("[%zu/%zu] symbol: %s (%s)\n", i + 1, gen->scope->len, name, sym->english_type);
+ }
+}
+
+static bool build_block(Gen*, Node*);
+static bool build_statement(Gen*, Node*);
+
+static gcc_jit_rvalue* build_bool_value(Gen*, Node*);
+
+static int block_counter = 0, loop_counter = 0, while_counter = 0;
+
+static bool
+build_while_statement(Gen* gen, Node* node)
+{
+ gcc_jit_location* loc = loc_from_node(gen, node);
+
+ Node* cond = node->data.while_statement.cond;
+ Node* body = node->data.while_statement.body;
+
+ while_counter++;
+ char label_cond[64], label_body[64], label_end[64];
+ snprintf(label_cond, sizeof label_cond, "while.cond%d", while_counter);
+ snprintf(label_body, sizeof label_body, "while.body%d", while_counter);
+ snprintf(label_end, sizeof label_end, "while.end%d", while_counter);
+
+ gcc_jit_block* cond_block = gcc_jit_function_new_block(gen->curr_func, label_cond);
+ gcc_jit_block* body_block = gcc_jit_function_new_block(gen->curr_func, label_body);
+ gcc_jit_block* end_block = gcc_jit_function_new_block(gen->curr_func, label_end);
+
+ /* jump from current block into the loop */
+ gcc_jit_block_end_with_jump(gen->curr_block, loc, cond_block);
+
+ gen->curr_block = cond_block;
+ gcc_jit_rvalue* cnd = NULL;
+
+ if (cond != NULL) {
+ cnd = build_bool_value(gen, cond);
+ gcc_jit_block_end_with_conditional(cond_block, loc, cnd, body_block, end_block);
+ } else {
+ /* while (1) */
+ gcc_jit_block_end_with_jump(cond_block, loc, body_block);
+ }
+
+ gcc_jit_block* continue_target = cond != NULL ? cond_block : body_block;
+ push_loop(gen, /*break to*/ end_block, /*continue to*/ continue_target);
+
+ gen->curr_block = body_block;
+ bool body_ended = build_block(gen, body);
+ if (!body_ended) { gcc_jit_block_end_with_jump(gen->curr_block, loc, continue_target); }
+
+ pop_loop(gen);
+
+ /* resume after loop */
+ gen->curr_block = end_block;
+ return false;
+}
+
+static bool
+build_for_statement(Gen* gen, Node* node)
+{
+ gcc_jit_location* loc = loc_from_node(gen, node);
+
+ Node* init = node->data.for_statement.init;
+ Node* cond = node->data.for_statement.cond;
+ Node* step = node->data.for_statement.increment;
+ Node* body = node->data.for_statement.body;
+
+ loop_counter++;
+
+ char label_cond[64], label_body[64], label_step[64], label_end[64];
+ snprintf(label_cond, 64, "for.cond%d", loop_counter);
+ snprintf(label_body, 64, "for.body%d", loop_counter);
+ snprintf(label_step, 64, "for.step%d", loop_counter);
+ snprintf(label_end, 64, "for.end%d", loop_counter);
+
+ gcc_jit_block* cond_block = gcc_jit_function_new_block(gen->curr_func, label_cond);
+ gcc_jit_block* body_block = gcc_jit_function_new_block(gen->curr_func, label_body);
+ gcc_jit_block* step_block = gcc_jit_function_new_block(gen->curr_func, label_step);
+ gcc_jit_block* end_block = gcc_jit_function_new_block(gen->curr_func, label_end);
+
+ // gcc_jit_block* saved_break = gen->loop->break_target;
+ // gcc_jit_block* saved_cont = gen->loop->continue_target;
+ // gen->loop->break_target = end_block;
+ // gen->loop->continue_target = step_block;
+
+ // header, e.g. for(int = 0 <-
+ if (init) { build_statement(gen, init); }
+
+ // jump to cond e.g. ; i < 5 <-
+ gcc_jit_block_end_with_jump(gen->curr_block, loc, cond_block);
+
+ // cond: evaluate
+ gen->curr_block = cond_block;
+ gcc_jit_rvalue* cnd = NULL;
+
+ if (cond != NULL) {
+ cnd = build_bool_value(gen, cond);
+ gcc_jit_block_end_with_conditional(cond_block, loc, cnd, body_block, end_block);
+ } else {
+ gcc_jit_block_end_with_jump(cond_block, loc, body_block); // presume for(;;)
+ }
+
+ push_loop(gen, /*break to*/ end_block, /*continue to*/ step_block);
+
+ // build for body
+
+ gen->curr_block = body_block;
+ bool for_body_ended = build_block(gen, body);
+ if (!for_body_ended) { gcc_jit_block_end_with_jump(gen->curr_block, loc, step_block); }
+
+ pop_loop(gen);
+
+ // step incr etc.
+
+ gen->curr_block = step_block;
+ if (step) { build_statement(gen, step); }
+ gcc_jit_block_end_with_jump(step_block, loc, cond != NULL ? cond_block : body_block);
+
+ // resume after loop
+
+ gen->curr_block = end_block;
+ return false;
+}
+
+static bool
+build_if_statement(Gen* gen, Node* node)
+{
+ gcc_jit_location* loc = loc_from_node(gen, node);
+ // build the condition
+
+ OpType op = node->data.if_statement.cond->data.binary_expr.op;
+ Node* lhs = node->data.if_statement.cond->data.binary_expr.lhs;
+ Node* rhs = node->data.if_statement.cond->data.binary_expr.rhs;
+
+ Node* then_body = node->data.if_statement.then_body;
+ Node* else_body = node->data.if_statement.else_body;
+
+ enum gcc_jit_comparison cmp;
+ switch (op) {
+ case OP_EQUALITY:
+ cmp = GCC_JIT_COMPARISON_EQ;
+ break;
+ case OP_INEQUALITY:
+ cmp = GCC_JIT_COMPARISON_NE;
+ break;
+ case OP_GT:
+ cmp = GCC_JIT_COMPARISON_GT;
+ break;
+ case OP_GT_EQ:
+ cmp = GCC_JIT_COMPARISON_GE;
+ break;
+ case OP_LT_EQ:
+ cmp = GCC_JIT_COMPARISON_LE;
+ break;
+ case OP_LT:
+ cmp = GCC_JIT_COMPARISON_LT;
+ break;
+ default:
+ printf("/!\\ build_statement NODE_IF unhandled, %d\n", op);
+ cmp = GCC_JIT_COMPARISON_NE;
+ }
+
+ gcc_jit_rvalue* lhs_val = handle_expr(gen, lhs);
+ gcc_jit_rvalue* rhs_val = handle_expr(gen, rhs);
+
+ gcc_jit_rvalue* cond = gcc_jit_context_new_comparison(gen->ctx, loc, cmp, lhs_val, rhs_val);
+
+ // create the BLOCKS
+
+ // labels
+ block_counter++;
+ char label_then[64], label_else[64], label_end[64];
+ snprintf(label_then, 64, "if.then%d", block_counter);
+ snprintf(label_else, 64, "if.else%d", block_counter);
+ snprintf(label_end, 64, "if.end%d", block_counter);
+
+ // blocks
+ gcc_jit_block* then_bb = gcc_jit_function_new_block(gen->curr_func, label_then);
+ gcc_jit_block* else_bb = else_body ? gcc_jit_function_new_block(gen->curr_func, label_else) : NULL;
+ gcc_jit_block* merge_bb = NULL;
+
+ if (!else_bb) {
+ // no else: need merge now for the false edge
+ merge_bb = gcc_jit_function_new_block(gen->curr_func, label_end);
+ gcc_jit_block_end_with_conditional(gen->curr_block, loc, cond, then_bb, merge_bb);
+ } else {
+ // with else: branch to then/else; decide on merge later
+ gcc_jit_block_end_with_conditional(gen->curr_block, loc, cond, then_bb, else_bb);
+ }
+
+ // THEN
+ gen->curr_block = then_bb;
+ bool then_ended = build_block(gen, then_body);
+ gcc_jit_block* then_open = gen->curr_block; // last open block in THEN
+ // (may differ from then_bb)
+
+ // ELSE
+ bool else_ended = false;
+ gcc_jit_block* else_open = NULL;
+ if (else_bb) {
+ gen->curr_block = else_bb;
+ else_ended = build_block(gen, else_body);
+ else_open = gen->curr_block; // last open block in ELSE
+ }
+
+ // If both branches ended, no merge needed, we notify we have ended as
+ // well
+ if (else_bb && then_ended && else_ended) return true;
+
+ // Ensure we have a merge if any branch continues.
+ if (!merge_bb) merge_bb = gcc_jit_function_new_block(gen->curr_func, label_end);
+
+ if (!then_ended) gcc_jit_block_end_with_jump(then_open, loc, merge_bb);
+ if (else_bb && !else_ended) gcc_jit_block_end_with_jump(else_open, loc, merge_bb);
+
+ gen->curr_block = merge_bb;
+ return false;
+}
+
+static bool
+build_var_decl_statement(Gen* gen, Node* node)
+{
+ gcc_jit_location* loc = loc_from_node(gen, node);
+ const char* var_name = span_str(gen->src, node->data.var_decl.name, (char[IDENTSZ]) { 0 });
+ gcc_jit_type* declared_type = ox_type_to_c_type(gen, node->data.var_decl.type);
+
+ gcc_jit_lvalue* var_decl = NULL;
+
+ gcc_jit_rvalue* rvalue = NULL;
+ Node* init = node->data.var_decl.init;
+ if (init != NULL) { rvalue = handle_expr(gen, init); }
+
+ if (gen->curr_func == NULL && gen->curr_block == NULL) {
+ // global var
+ var_decl = gcc_jit_context_new_global(gen->ctx, loc, GCC_JIT_GLOBAL_INTERNAL, declared_type, strdup(var_name));
+ if (rvalue) gcc_jit_global_set_initializer_rvalue(var_decl, rvalue);
+ } else {
+ // local var
+ var_decl = gcc_jit_function_new_local(gen->curr_func, loc, declared_type, strdup(var_name));
+ if (rvalue) gcc_jit_block_add_assignment(gen->curr_block, loc, var_decl, rvalue);
+ }
+
+ // this is not an error, there is just no initial value
+ // if (node->data.var_decl.init == NULL) { panic("could not instanciate gcc jit new local"); }
+
+ printf("adding 1 symbol: %s\n", var_name);
+
+ Symbol* sym = (Symbol*)calloc(1, sizeof(Symbol));
+ sym->name = node->data.var_decl.name;
+ sym->decl = node;
+ sym->ctype = declared_type;
+ sym->d.lvalue = var_decl;
+ sym->english_type = type_var;
+ sym->is_variadic = node->data.var_decl.type->data.ident_type.is_variadic;
+ sym->is_comp_time = node->data.var_decl.type->data.ident_type.is_comp_time;
+ add_symbol(gen, sym); // add var_decl symbol
+ return false;
+}
+
+static void build_func_decl(Gen*, Node*);
+
+static gcc_jit_field*
+build_field(Gen* gen, Node* node)
+{
+ const char* name = span_str(gen->src, node->data.field_decl.name, (char[IDENTSZ]) { 0 });
+ gcc_jit_type* type = ox_type_to_c_type(gen, node->data.field_decl.type);
+ gcc_jit_field* field = gcc_jit_context_new_field(gen->ctx, NULL, type, strdup(name));
+ // TODO build_field in struct to allow init_value (see field_decl.init) -- note, it cannot be done via gccjit, it has to be
+ // stored somewhere and then upon the user creating an instance of the type, we then evaluate the expression and provide the
+ // value as a default of the field.
+ return field;
+}
+
+static bool
+build_statement(Gen* gen, Node* node)
+{
+ gcc_jit_location* loc = loc_from_node(gen, node);
+ switch (node->type) {
+ case NODE_RETURN: {
+ Node* return_expr = node->data.ret.expr;
+ if (return_expr) {
+ gcc_jit_rvalue* ret_rval = handle_expr(gen, return_expr);
+ gcc_jit_type* expect_type = gcc_jit_function_get_return_type(gen->curr_func);
+ gcc_jit_type* ret_type = gcc_jit_rvalue_get_type(ret_rval);
+ types_match_or_panic(node, expect_type, ret_type);
+ gcc_jit_block_end_with_return(gen->curr_block, loc, ret_rval);
+ } else {
+ gcc_jit_block_end_with_void_return(gen->curr_block, loc);
+ }
+ gen->curr_block = NULL; // important
+ return true; // we end the block here
+ }
+ case NODE_BREAK: {
+ gcc_jit_block* t = current_break(gen);
+ if (!t) return true; // break outside the loop
+ gcc_jit_block_end_with_jump(gen->curr_block, loc, t);
+ gen->curr_block = NULL;
+ return true;
+ }
+ case NODE_CONTINUE: {
+ gcc_jit_block* t = current_continue(gen);
+ if (!t) return true; // continue outside of a loop
+ gcc_jit_block_end_with_jump(gen->curr_block, loc, t);
+ gen->curr_block = NULL;
+ return true;
+ }
+ case NODE_VAR_DECL: {
+ return build_var_decl_statement(gen, node);
+ }
+ case NODE_VAR_ASSIGN: {
+ gcc_jit_rvalue* rvalue = handle_expr(gen, node->data.var_assign.rhs);
+ // find the var_decl (d.lvalue) from the symbols table's symbol.
+ for (size_t i = 0; i < gen->scope->len; i++) {
+ Symbol* sym = gen->scope->symbols[i];
+ if (span_ident_same(sym->name, node->data.var_assign.lhs->data.ident.name, gen->src)) {
+ // we found the symbol we are assigning to
+ // TODO check for variadic and comp_time
+ if (!sym->is_variadic) {
+ const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 });
+ panic_at(node, "symbol %s is not variadic, cannot mutate.", name);
+ }
+ if (sym->is_comp_time) {
+ const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 });
+ panic_at(node, "symbol %s is comp time, cannot mutate.", name);
+ }
+
+ gcc_jit_lvalue* lvalue = sym->d.lvalue;
+
+ // check the type of lvalue matches the rvalue
+ gcc_jit_type* ltype = sym->ctype;
+ gcc_jit_type* rtype = gcc_jit_rvalue_get_type(rvalue);
+ if (rtype != ltype) {
+ panic_at(node,
+ "right hand side of assigment "
+ "doesn't match the "
+ "type of the left hand side: %s <-- %s",
+ get_english_type(ltype),
+ get_english_type(rtype));
+ }
+ if (lvalue) gcc_jit_block_add_assignment(gen->curr_block, loc, lvalue, rvalue);
+ }
+ }
+ break;
+ }
+ case NODE_BINARY_EXPR: {
+ Symbol* sym = lookup_symbol(gen, gen->scope, node->data.binary_expr.lhs->data.ident.name);
+ if (!sym)
+ panic_at(node,
+ "NODE_BINARY_EXPR: Undefined symbol: %s\n",
+ span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 }));
+
+ gcc_jit_lvalue* lv = sym->d.lvalue;
+ gcc_jit_rvalue* rhs = handle_expr(gen, node->data.binary_expr.rhs);
+ gcc_jit_rvalue* lhs = handle_expr(gen, node->data.binary_expr.lhs);
+ gcc_jit_rvalue* temp = gcc_jit_lvalue_as_rvalue(lv);
+ gcc_jit_type* lvtype = gcc_jit_rvalue_get_type(temp);
+ gcc_jit_rvalue* sum
+ = gcc_jit_context_new_binary_op(gen->ctx, loc_from_node(gen, node), GCC_JIT_BINARY_OP_PLUS, lvtype, lhs, rhs);
+
+ gcc_jit_block_add_assignment(gen->curr_block, loc_from_node(gen, node), lv, sum);
+ }
+ case NODE_UNARY_EXPR: {
+ gcc_jit_rvalue* rv = handle_unary_expr(gen, node);
+ if (rv) gcc_jit_block_add_eval(gen->curr_block, loc_from_node(gen, node), rv);
+ break;
+ }
+ case NODE_EXPR_STATEMENT: {
+ gcc_jit_rvalue* rv = handle_expr(gen, node->data.expr_statement.expr);
+ if (rv) gcc_jit_block_add_eval(gen->curr_block, loc_from_node(gen, node), rv);
+ break;
+ }
+ case NODE_IF:
+ return build_if_statement(gen, node);
+ case NODE_FOR:
+ return build_for_statement(gen, node);
+ case NODE_WHILE:
+ return build_while_statement(gen, node);
+ case NODE_FUNCTION_DECL:
+ // "closure"
+ build_func_decl(gen, node);
+ return false;
+ case NODE_BLOCK:
+ build_block(gen, node);
+ default:
+ printf("--- WARNING: build_statement unhandled, %s\n", node_type_str(node->type));
+ break;
+ }
+ return false;
+}
+
+static gcc_jit_rvalue*
+build_bool_value(Gen* gen, Node* node)
+{
+ gcc_jit_location* loc = loc_from_node(gen, node);
+
+ switch (node->type) {
+ case NODE_BOOL_LITERAL:
+ /* true/false literal */
+ return gcc_jit_context_new_rvalue_from_int(gen->ctx, type_boolean, node->data.boolean.value ? 1 : 0);
+ break;
+ case NODE_INT_LITERAL: {
+ /* while (1) style – C truthiness */
+ gcc_jit_rvalue* iv = emit_literal_int(gen, node);
+ return gcc_jit_context_new_cast(gen->ctx, loc, iv, type_boolean);
+ }
+ case NODE_STRING_LITERAL: {
+ /* while("word") should also be truthy if the pointer is non null */
+ gcc_jit_rvalue* v = emit_literal_string(gen, node);
+ gcc_jit_type* ty = gcc_jit_rvalue_get_type(v);
+ gcc_jit_rvalue* nullv = gcc_jit_context_null(gen->ctx, ty);
+ return gcc_jit_context_new_comparison(gen->ctx, loc, GCC_JIT_COMPARISON_NE, v, nullv);
+ }
+ case NODE_BINARY_EXPR: {
+ gcc_jit_rvalue* lvalue = handle_expr(gen, node->data.binary_expr.lhs);
+ gcc_jit_rvalue* rvalue = handle_expr(gen, node->data.binary_expr.rhs);
+ enum gcc_jit_comparison op;
+ switch (node->data.binary_expr.op) {
+ case OP_LT:
+ op = GCC_JIT_COMPARISON_LT;
+ break;
+ case OP_GT:
+ op = GCC_JIT_COMPARISON_GT;
+ break;
+ case OP_LT_EQ:
+ op = GCC_JIT_COMPARISON_LE;
+ break;
+ case OP_GT_EQ:
+ op = GCC_JIT_COMPARISON_GE;
+ break;
+ default:
+ printf("build_bool_rvalue nodebinary op unhandled, "
+ "%s\n",
+ node_type_str(node->type));
+ return NULL;
+ }
+ return gcc_jit_context_new_comparison(gen->ctx, loc, op, lvalue, rvalue);
+ break;
+ }
+ default:
+ printf("/!\\ build_bool_rvalue unhandled, %s\n", node_type_str(node->type));
+ break;
+ }
+
+ return NULL;
+}
+
+static gcc_jit_struct*
+build_struct_block(Gen* gen, Node* body, const char* struct_name)
+{
+ if (body->data.block.len == 0) { panic_at(body, "No field definition were found in the struct block"); }
+
+ // TODO build_struct_block do we need to push and pop scope?
+
+ size_t fields_count = body->data.block.len;
+ gcc_jit_field** fields = (gcc_jit_field**)calloc(fields_count, sizeof(gcc_jit_field*));
+ if (fields == NULL) { panic("build_struct_block: fields: could not alloc"); }
+
+ for (size_t i = 0; i < fields_count; i++) {
+ gcc_jit_field* field = build_field(gen, body->data.block.stmts[i]);
+ fields[i] = field;
+ }
+ gcc_jit_location* loc = loc_from_node(gen, body);
+ gcc_jit_struct* strct = gcc_jit_context_new_struct_type(gen->ctx, loc, strdup(struct_name), fields_count, fields);
+ return strct;
+}
+
+// build_block: returns true if block ended with a terminator
+static bool
+build_block(Gen* gen, Node* body)
+{
+ // TODO if there is no block ({ ... }), we presume there is only 1 statement.
+ // This might not always be valid, func declaration do require a block {}, but there might be cases...
+ // TODO FIXME: a symbol declared in a child block is accessible in the parent or in a sibling block...
+ if (body->data.block.len == 0) { return build_statement(gen, body); }
+
+ for (size_t i = 0; i < body->data.block.len; i++) {
+ if (gen->curr_block == NULL) return true;
+ bool ended = build_statement(gen, body->data.block.stmts[i]);
+ if (ended) return true;
+ }
+ return false;
+}
+
+static void
+build_struct_decl(Gen* gen, Node* node)
+{
+ // TODO the exported vs internal structs, for now they're all exported.
+ // TODO opaque struct so we allow circular dependencies A {B*}, B {A*};
+ const char* struct_name = span_str(gen->src, node->data.struct_decl.name, (char[IDENTSZ]) { 0 });
+ gcc_jit_struct* user_struct_def = build_struct_block(gen, node->data.struct_decl.body, struct_name);
+ gcc_jit_type* user_struct_type = gcc_jit_struct_as_type(user_struct_def);
+ // TODO @next addsymbol struct
+ printf("adding 1 symbol: %s\n", struct_name);
+
+ Symbol* sym = (Symbol*)calloc(1, sizeof(Symbol));
+ sym->name = node->data.struct_decl.name;
+ sym->decl = node;
+ sym->ctype = user_struct_type;
+ sym->d.struct_value = user_struct_def;
+ sym->english_type = type_struct;
+ sym->is_variadic = false;
+ sym->is_comp_time = false;
+ add_symbol(gen, sym); // add struct_decl symbol
+}
+
+static void
+build_func_decl(Gen* gen, Node* node)
+{
+ const char* func_name = span_str(gen->src, node->data.function_decl.name, (char[IDENTSZ]) { 0 });
+
+ Node* return_type = node->data.function_decl.return_type;
+ gcc_jit_type* ret_type = ox_type_to_c_type(gen, return_type);
+ gcc_jit_location* loc = loc_from_node(gen, node);
+
+ size_t argc = node->data.function_decl.p_len;
+
+ gcc_jit_param** params = calloc(argc, sizeof(gcc_jit_param*));
+
+ // 1) Create ONE gcc_jit_param per AST param
+ for (size_t i = 0; i < argc; i++) {
+ Node* pnode = node->data.function_decl.params[i];
+ const char* pname = span_str(gen->src, pnode->data.param.name, (char[IDENTSZ]) { 0 });
+
+ gcc_jit_type* declared_type = ox_type_to_c_type(gen, pnode->data.param.type);
+
+ gcc_jit_param* p = gcc_jit_context_new_param(gen->ctx, loc, declared_type, strdup(pname));
+
+ params[i] = p;
+ }
+
+ // 2) Create the function with those params
+ gcc_jit_function* func = gcc_jit_context_new_function(gen->ctx,
+ loc,
+ GCC_JIT_FUNCTION_EXPORTED,
+ ret_type,
+ strdup(func_name),
+ argc,
+ params, // <-- these are now owned by 'func'
+ 0);
+
+ gcc_jit_block* block = gcc_jit_function_new_block(func, "entry");
+
+ gcc_jit_function* prev_func = gen->curr_func;
+ gcc_jit_block* prev_block = gen->curr_block;
+ gen->curr_func = func;
+ gen->curr_block = block;
+
+ // 3) Add the function symbol to the current (enclosing) scope
+
+ Symbol* sym = (Symbol*)calloc(1, sizeof(Symbol));
+ sym->name = node->data.function_decl.name;
+ sym->decl = node;
+ sym->ctype = type_voidp; // whatever you use for function type metadata
+ sym->d.funcvalue = func;
+ sym->english_type = type_func;
+ add_symbol(gen, sym); // add function symbol -- to the 'parent scope', before we push_scope
+
+ // ENTER FUNCTION SCOPE
+ push_scope(gen);
+
+ // 4) Add parameter symbols to the FUNCTION scope, reusing the same gcc_jit_param*
+ for (size_t i = 0; i < argc; i++) {
+ Node* pnode = node->data.function_decl.params[i];
+
+ gcc_jit_param* p = params[i]; // <-- reuse, DO NOT new_param again
+
+ gcc_jit_type* declared_type = ox_type_to_c_type(gen, pnode->data.param.type);
+
+ Symbol* ps = (Symbol*)calloc(1, sizeof(Symbol));
+ ps->name = pnode->data.param.name; // param identifier span
+ ps->decl = pnode;
+ ps->ctype = declared_type;
+ ps->d.param = p; // <-- this is associated with 'func'
+ ps->english_type = type_var; // or type_param if you distinguish
+ add_symbol(gen, ps); // add param symbol
+ }
+
+ print_symbols_here(gen);
+
+ build_block(gen, node->data.function_decl.body);
+
+ if (gen->curr_block == NULL) {
+ // no open block: all paths already terminated, don't add an implicit return
+ } else if (ret_type == type_int) {
+ gcc_jit_rvalue* ret_value = gcc_jit_context_new_rvalue_from_int(gen->ctx, type_int, 0);
+ gcc_jit_block_end_with_return(gen->curr_block, loc, ret_value);
+ } else if (ret_type == type_void) {
+ gcc_jit_block_end_with_void_return(gen->curr_block, loc);
+ } else {
+ printf("build_func_decl unhandled return type in func: %s - "
+ "defaulting to void\n",
+ func_name);
+ gcc_jit_block_end_with_void_return(gen->curr_block, loc);
+ }
+
+ // LEAVE FUNCTION SCOPE
+ pop_scope(gen);
+
+ gen->curr_func = prev_func;
+ gen->curr_block = prev_block;
+}
+
+void
+gen_next(Gen* gen, Node* node)
+{
+ // printf("gen_next, %s\n", node_type_str(node->type));
+
+ switch (node->type) {
+ case NODE_PROGRAM:
+ build_program(gen, node);
+ break;
+ case NODE_FUNCTION_DECL:
+ build_func_decl(gen, node);
+ break;
+ case NODE_STRUCT_DECL:
+ build_struct_decl(gen, node);
+ break;
+ case NODE_STRING_LITERAL:
+ emit_literal_string(gen, node);
+ break;
+ case NODE_INT_LITERAL:
+ emit_literal_int(gen, node);
+ break;
+ case NODE_FLOAT_LITERAL:
+ emit_literal_float(gen, node);
+ break;
+ case NODE_VAR_DECL:
+ build_statement(gen, node);
+ break;
+ default:
+ printf("gen: unhandled, %s\n", node_type_str(node->type));
+ }
+}
diff --git a/src/gen.h b/src/gen.h
@@ -5,25 +5,5 @@
#include "types.h"
-typedef struct LoopContext {
- gcc_jit_block* break_target;
- gcc_jit_block* continue_target;
- struct LoopContext* prev;
-} LoopContext;
-
-typedef struct {
- gcc_jit_context* ctx;
- gcc_jit_function* prev_func;
- gcc_jit_function* curr_func;
- gcc_jit_function* printf_fn;
- gcc_jit_function* puts_fn;
- gcc_jit_block* prev_block;
- gcc_jit_block* curr_block;
- // gcc_jit_type *type_kind; need type too?
- Scope* scope;
- LoopContext* loop;
- const char* src;
-} Gen;
-
Gen gen_init(Scope*, const char*, Node*, bool);
void gen_next(Gen*, Node*);
diff --git a/src/gen/gen.c b/src/gen/gen.c
@@ -1,1372 +0,0 @@
-#include "../gen.h"
-#include "../parser.h"
-#include "../utils.h"
-#include "../typer.h"
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <sys/param.h>
-
-static gcc_jit_type* type_boolean;
-static gcc_jit_type* type_int;
-static gcc_jit_type* type_i8;
-static gcc_jit_type* type_i16;
-static gcc_jit_type* type_i32;
-static gcc_jit_type* type_i64;
-static gcc_jit_type* type_i128;
-static gcc_jit_type* type_u8;
-static gcc_jit_type* type_u16;
-static gcc_jit_type* type_u32;
-static gcc_jit_type* type_u64;
-static gcc_jit_type* type_u128;
-static gcc_jit_type* type_f32;
-static gcc_jit_type* type_f64;
-static gcc_jit_type* type_f128;
-static gcc_jit_type* type_uint;
-static gcc_jit_type* type_void;
-static gcc_jit_type* type_cstr;
-static gcc_jit_type* type_char;
-static gcc_jit_type* type_voidp;
-
-static const char* type_func = "function";
-static const char* type_var = "variable";
-
-#define MAXARGS 16
-
-static gcc_jit_location*
-loc_from_node(Gen* gen, Node* node)
-{
- if (node->filename == NULL) return NULL;
- return gcc_jit_context_new_location(gen->ctx, node->filename, node->line, node->col);
-}
-
-static void
-push_loop(Gen* gen, gcc_jit_block* brk, gcc_jit_block* cont)
-{
- LoopContext* lctx = (LoopContext*)malloc(sizeof(LoopContext));
- if (lctx == NULL) { panic("push_loop: could not alloc"); }
- lctx->break_target = brk;
- lctx->continue_target = cont;
- lctx->prev = gen->loop;
- gen->loop = lctx;
-}
-
-static void
-pop_loop(Gen* gen)
-{
- LoopContext* lctx = gen->loop;
- gen->loop = lctx->prev;
- free(lctx);
-}
-
-static inline gcc_jit_block*
-current_break(Gen* gen)
-{
- return gen->loop ? gen->loop->break_target : NULL;
-}
-
-static inline gcc_jit_block*
-current_continue(Gen* gen)
-{
- return gen->loop ? gen->loop->continue_target : NULL;
-}
-
-__attribute__((unused)) static const char*
-get_english_type(gcc_jit_type* T)
-{
- return gcc_jit_object_get_debug_string(gcc_jit_type_as_object(T));
-}
-
-Gen
-gen_init(Scope* scope, const char* src, Node* node, bool quiet)
-{
- if (scope == NULL || src == NULL) { panic("gen_init: no Scope or AST provided"); }
-
- gcc_jit_context* ctx;
-
- ctx = gcc_jit_context_acquire();
-
- if (!ctx) { panic("could not acquire gcc jit context"); }
-
- // needs loc* to work
- // gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DEBUGINFO,
- // 1); high level
-
- gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DEBUGINFO, 1);
-
- if (quiet == false) { gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, 1); }
-
- // gcc_jit_context_set_bool_option(ctx,
- // GCC_JIT_BOOL_OPTION_DUMP_SUMMARY, 1);
-
- gcc_jit_context_set_str_option(ctx, GCC_JIT_STR_OPTION_PROGNAME, "ox");
- // keep FP
- gcc_jit_context_add_driver_option(ctx, "-fno-omit-frame-pointer");
-
- gcc_jit_context_set_int_option(ctx,
- GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL,
- /*0-3 for O3*/ 0);
-
- type_boolean = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_BOOL);
-
- type_int = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT64_T);
- type_i8 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT8_T);
- type_i16 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT16_T);
- type_i32 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT32_T);
- type_i64 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT64_T);
- type_i128 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT128_T);
-
- type_u8 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT8_T);
- type_u16 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT16_T);
- type_u32 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT32_T);
- type_u64 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT64_T);
- type_u128 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT128_T);
-
- type_f32 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_FLOAT);
- type_f64 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_DOUBLE);
- type_f128 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG_DOUBLE);
-
- type_uint = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT64_T);
- type_char = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR);
- type_void = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_VOID);
- type_cstr = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_CONST_CHAR_PTR);
- type_voidp = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_VOID_PTR);
- /* gcc_jit_type_dyncast_function_ptr_type; */
-
- gcc_jit_location* loc = gcc_jit_context_new_location(ctx, node->filename, 0, 0);
-
- gcc_jit_param* pm_puts[] = { gcc_jit_context_new_param(ctx, loc, type_cstr, "s") };
- gcc_jit_function* fn_puts = gcc_jit_context_new_function(ctx, loc, GCC_JIT_FUNCTION_IMPORTED, type_int, "puts", 1, pm_puts, 0);
-
- gcc_jit_param* pm_printf[] = { gcc_jit_context_new_param(ctx, loc, type_cstr, "fmt") };
- gcc_jit_function* fn_printf = gcc_jit_context_new_function(ctx,
- loc,
- GCC_JIT_FUNCTION_IMPORTED,
- type_int,
- "printf",
- 1,
- pm_printf,
- /*is_variadic=*/1);
-
- return (Gen) {
- .ctx = ctx,
- .scope = scope,
- .prev_func = NULL,
- .curr_func = NULL,
- .prev_block = NULL,
- .curr_block = NULL,
- .puts_fn = fn_puts,
- .printf_fn = fn_printf,
- .src = src,
- };
-}
-
-static void
-push_scope(Gen* gen)
-{
- printf("push_scope\n");
- Scope* scope = calloc(1, sizeof(Scope));
- *scope = (Scope) { 0 };
- scope->symbols = (Symbol**)calloc(16, sizeof(Symbol*));
- if (scope->symbols == NULL) panic("push_scope: symbols: could not alloc");
- scope->cap = 16;
- scope->len = 0;
-
- scope->parent = gen->scope;
- gen->scope = scope;
-}
-
-static void
-pop_scope(Gen* gen)
-{
- printf("pop_scope\n");
- Scope* s = gen->scope;
- gen->scope = s->parent;
-}
-
-static Symbol*
-find_symbol(Gen* gen, Scope* scope, Span name)
-{
- const char* ident_name = span_str(gen->src, name, (char[IDENTSZ]) { 0 });
- // Look up the symbol in the current scope // TODO look up in parent
- // scope with utility function and recursion
- for (size_t i = 0; i < scope->len; i++) {
- Symbol* sym = scope->symbols[i];
- const char* sym_name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 });
- // printf("symbol %s\n", sym_name);
- // printf("ident %s\n", ident_name);
- if (strcmp(sym_name, ident_name) == 0) {
- printf("found symbol %s\n", sym_name);
- return sym;
- }
- }
- printf("find_symbol: not found locally: %s\n", ident_name);
-
- if (scope->parent != NULL) {
- printf("looking up symbol %s in parent\n", ident_name);
- return find_symbol(gen, scope->parent, name);
- }
-
- return NULL;
-}
-
-static void
-add_symbol(Gen* gen, Symbol* sym)
-{
- if (gen->scope->len == gen->scope->cap) {
- gen->scope->cap *= 2;
- gen->scope->symbols = (Symbol**)realloc(gen->scope->symbols, sizeof(Symbol*) * gen->scope->cap);
- }
-
- gen->scope->symbols[gen->scope->len++] = sym;
-
- printf("add_symbol: we now have %zu symbols: ", gen->scope->len);
- for (size_t i = 0; i < gen->scope->len; i++) {
- Symbol* symy = gen->scope->symbols[i];
- const char* name = span_str(gen->src, symy->name, (char[IDENTSZ]) { 0 });
- printf("%s, ", name);
- }
- printf("\n");
-}
-
-static gcc_jit_rvalue*
-handle_ident_call(Gen* gen, Node* node)
-{
- // Look up the symbol in the current scope // TODO look up in parent
- // scope with utility function and recursion
- Symbol* sym = find_symbol(gen, gen->scope, node->data.ident.name);
-
- if (sym == NULL) {
- panic_at(node,
- "handle_ident_call: undefined variable: %s\n",
- span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 }));
- }
- return gcc_jit_lvalue_as_rvalue(sym->d.lvalue);
-}
-
-static gcc_jit_rvalue* handle_expr(Gen*, Node*);
-
-static inline int
-is_intlike(gcc_jit_context* ctx, gcc_jit_type* t) // TODO support all new types
-{
- return t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT) || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_INT)
- || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_SHORT)
- || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_SHORT)
- || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG)
- || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG)
- || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG_LONG)
- || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG)
- || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT64_T) || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT32_T)
- || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_CHAR)
- || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR);
-}
-
-static inline int
-is_floatlike(gcc_jit_context* ctx, gcc_jit_type* t)
-{
- return t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_FLOAT) || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_DOUBLE)
- || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG_DOUBLE);
-}
-
-static inline int
-is_cstr(gcc_jit_context* ctx, gcc_jit_type* t)
-{
- return t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_CONST_CHAR_PTR);
-}
-
-// > < >= <= + - / * %
-static gcc_jit_rvalue*
-handle_bin_expr(Gen* gen, Node* node, int gcc_jit_op, bool cmp)
-{
- Node* lhs = node->data.binary_expr.lhs;
- Node* rhs = node->data.binary_expr.rhs;
- gcc_jit_context* ctx = gen->ctx;
- gcc_jit_location* loc = loc_from_node(gen, node);
-
- gcc_jit_rvalue* L = handle_expr(gen, lhs);
- gcc_jit_rvalue* R = handle_expr(gen, rhs);
- gcc_jit_type* Lt = gcc_jit_rvalue_get_type(L);
- gcc_jit_type* Rt = gcc_jit_rvalue_get_type(R);
- gcc_jit_rvalue* result = NULL;
-
- // floats: cast both to double
- if (is_floatlike(ctx, Lt) || is_floatlike(ctx, Rt)) {
- gcc_jit_type* T = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_DOUBLE);
- L = gcc_jit_context_new_cast(ctx, loc, L, T);
- R = gcc_jit_context_new_cast(ctx, loc, R, T);
- result = cmp ? gcc_jit_context_new_comparison(ctx, loc, gcc_jit_op, L, R)
- : gcc_jit_context_new_binary_op(ctx, loc, gcc_jit_op, T, L, R);
- ;
- }
-
- // integers: cast both to signed/unsigned long long (cheap, predictable)
- if (is_intlike(ctx, Lt) && is_intlike(ctx, Rt)) {
- int any_unsigned = (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR))
- || (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_SHORT))
- || (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_INT))
- || (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG))
- || (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG))
- || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR))
- || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_SHORT))
- || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_INT))
- || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG))
- || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG));
-
- gcc_jit_type* T = gcc_jit_context_get_type(ctx, any_unsigned ? GCC_JIT_TYPE_UINT64_T : GCC_JIT_TYPE_INT64_T);
-
- L = gcc_jit_context_new_cast(ctx, loc, L, T);
- R = gcc_jit_context_new_cast(ctx, loc, R, T);
- result = cmp ? gcc_jit_context_new_comparison(ctx, loc, gcc_jit_op, L, R)
- : gcc_jit_context_new_binary_op(ctx, loc, gcc_jit_op, T, L, R);
- }
-
- // pointers: either reject or compare addresses (UB in C for unrelated objs).
- if (
-#ifdef GCC_JIT_HAVE_gcc_jit_type_is_pointer
- gcc_jit_type_is_pointer(Lt) && gcc_jit_type_is_pointer(Rt)
-#else
- 0
-#endif
- ) {
- gcc_jit_type* T = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG);
- L = gcc_jit_context_new_cast(ctx, loc, L, T);
- R = gcc_jit_context_new_cast(ctx, loc, R, T);
- result = cmp ? gcc_jit_context_new_comparison(ctx, loc, gcc_jit_op, L, R)
- : gcc_jit_context_new_binary_op(ctx, loc, gcc_jit_op, T, L, R);
- }
-
- return result;
-}
-
-static gcc_jit_rvalue*
-handle_binary_expr(Gen* gen, Node* node)
-{
- int gcc_jit_op = -1;
- OpType op = node->data.binary_expr.op;
-
- switch (op) {
- case OP_PLUS:
- gcc_jit_op = GCC_JIT_BINARY_OP_PLUS;
- return handle_bin_expr(gen, node, gcc_jit_op, false);
- break;
- case OP_MINUS:
- gcc_jit_op = GCC_JIT_BINARY_OP_MINUS;
- return handle_bin_expr(gen, node, gcc_jit_op, false);
- break;
- case OP_MUL:
- gcc_jit_op = GCC_JIT_BINARY_OP_MULT;
- return handle_bin_expr(gen, node, gcc_jit_op, false);
- break;
- case OP_DIV:
- gcc_jit_op = GCC_JIT_BINARY_OP_DIVIDE;
- return handle_bin_expr(gen, node, gcc_jit_op, false);
- break;
- case OP_MOD:
- gcc_jit_op = GCC_JIT_BINARY_OP_MODULO;
- return handle_bin_expr(gen, node, gcc_jit_op, false);
- break;
- case OP_LT:
- gcc_jit_op = GCC_JIT_COMPARISON_LT;
- break;
- case OP_GT:
- gcc_jit_op = GCC_JIT_COMPARISON_GT;
- break;
- case OP_GT_EQ:
- gcc_jit_op = GCC_JIT_COMPARISON_GE;
- break;
- case OP_LT_EQ:
- gcc_jit_op = GCC_JIT_COMPARISON_LE;
- break;
- default:
- printf("handle_binary_expr unhandled OpType %d (can be ignored in var assignment "
- "(for now))\n",
- op);
- return NULL;
- break;
- }
-
- if (gcc_jit_op < 0) {
- // unsupported types (e.g., strings or structs)
- printf("handle_binary_expr OP_LT: unsupported operand types\n");
- return NULL;
- }
-
- return handle_bin_expr(gen, node, gcc_jit_op, true);
-}
-
-static gcc_jit_rvalue*
-handle_unary_expr(Gen* gen, Node* node)
-{
- gcc_jit_context* ctx = gen->ctx;
- gcc_jit_block* bb = gen->curr_block;
-
- Node* opnd = node->data.unary_expr.operand;
- Symbol* sym = find_symbol(gen, gen->scope, opnd->data.ident.name);
- if (sym == NULL) {
- panic_at(node,
- "handle_unary_expr: undefined variable: %s\n",
- span_str(gen->src, opnd->data.ident.name, (char[IDENTSZ]) { 0 }));
- }
-
- gcc_jit_lvalue* lv = sym->d.lvalue;
- if (lv == NULL) {
- printf("handle_unary_expr: no lvalue\n");
- return NULL;
- }
- gcc_jit_rvalue* orig = gcc_jit_lvalue_as_rvalue(lv);
- gcc_jit_type* ty = gcc_jit_rvalue_get_type(orig);
- gcc_jit_rvalue* one = gcc_jit_context_one(ctx, ty);
-
- gcc_jit_rvalue* inc = gcc_jit_context_new_binary_op(ctx, NULL, GCC_JIT_BINARY_OP_PLUS, ty, orig, one);
- switch (node->data.unary_expr.op) {
- case OPER_POSTINC: { // i++
-
- if (!sym->is_variadic) {
- panic_at(node,
- "OPER_POSTINC: cannot change non variadic variable %s\n",
- span_str(gen->src, opnd->data.ident_type.name, (char[IDENTSZ]) { 0 }));
- } else if (sym->is_comp_time) {
- panic_at(node,
- "OPER_POSTINC: cannot change comp time constant %s\n",
- span_str(gen->src, opnd->data.ident_type.name, (char[IDENTSZ]) { 0 }));
- }
- gcc_jit_block_add_assignment(bb, NULL, lv, inc);
- return orig; // return value before incr as ++ is postfix
-
- break;
- }
- default:
- printf("handle_unary_expr, unhandled op %d\n", node->data.unary_expr.op);
- return NULL;
- break;
- }
- return NULL;
-}
-
-static gcc_jit_rvalue*
-emit_literal_bool(Gen* gen, Node* node)
-{
- int v = node->data.boolean.value ? 1 : 0;
- return gcc_jit_context_new_rvalue_from_int(gen->ctx, type_boolean, v);
-}
-
-static gcc_jit_rvalue*
-emit_literal_string(Gen* gen, Node* node)
-{
- size_t len = node->data.string.value.end - node->data.string.value.start;
- char* str = calloc(len + 1, sizeof(char));
- if (str == NULL) panic_at(node, "emit_literal_string: could not alloc");
- memcpy(str, gen->src + node->data.string.value.start, len);
- str[len] = '\0';
- return gcc_jit_context_new_string_literal(gen->ctx, str);
-}
-
-static gcc_jit_rvalue*
-emit_literal_int(Gen* gen, Node* node)
-{
- return gcc_jit_context_new_rvalue_from_int(gen->ctx, type_int, (int)node->data.number.value);
-}
-
-static gcc_jit_rvalue*
-emit_literal_float(Gen* gen, Node* node)
-{
- return gcc_jit_context_new_rvalue_from_double(gen->ctx, type_f32, node->data.number.value);
-}
-
-static void
-build_program(Gen* gen, Node* node)
-{
- size_t cnt = node->data.program.len;
- for (size_t i = 0; i < cnt; i++) {
- gen_next(gen, node->data.program.decl[i]);
- }
-}
-
-static gcc_jit_rvalue*
-lower_builtin_print(Gen* gen, Node* node)
-{
- printf("lower_builtin_print\n");
-
- size_t argc = node->data.call_expr.len;
-
- // for (size_t i = 0; i < argc; i++) {
- // Node* arg = node->data.call_expr.args[i];
- // }
-
- // 1-arg, treat as puts(arg)
- if (argc == 1) {
- gcc_jit_rvalue* arg = handle_expr(gen, node->data.call_expr.args[0]);
- gcc_jit_type* t = gcc_jit_rvalue_get_type(arg);
- gcc_jit_location* loc = loc_from_node(gen, node->data.call_expr.args[0]);
- // print a string
- if (is_cstr(gen->ctx, t)) {
- gcc_jit_rvalue* args[] = { arg };
- return gcc_jit_context_new_call(gen->ctx, loc, gen->puts_fn, 1, args);
- }
- // print an integer or bool
- else if (is_intlike(gen->ctx, t) || t == type_boolean) {
- // cast to int for a clean %d
- // gcc_jit_type* t_int
- // = gcc_jit_context_get_type(gen->ctx, GCC_JIT_TYPE_INT64_T);
- gcc_jit_rvalue* fmt = gcc_jit_context_new_string_literal(gen->ctx, "%d\n");
- gcc_jit_rvalue* ival = arg;
- gcc_jit_rvalue* args[] = { fmt, ival };
- return gcc_jit_context_new_call(gen->ctx, loc, gen->printf_fn, 2, args);
- }
- // cast common cases to const char*
- // if (gcc_jit_rvalue_get_type(arg) != type_cstr) arg =
- // gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node),
- // arg, type_cstr);
-
- // gcc_jit_rvalue* args[] = { arg };
- // return gcc_jit_context_new_call(
- // gen->ctx, loc_from_node(gen, node), gen->puts_fn, 1, args);
-
- // gcc_jit_type* type_of_args = gcc_jit_rvalue_get_type(arg);
- // switch (type_of_args) {
- // case GCC_JIT_TYPE_CONST_CHAR_PTR:
-
- // break;
- // default:
- // printf("lower_builtin_print: unhandled type passed %s\n",
- // type_of_args); break;
- // }
- }
-
- return NULL;
-
- // softpanic("we don't currently handle formatted strings to print");
-
- // n>=1, treat as printf(fmt, ...) // Part of TODO about args as list
- // and not
- //
- // through each args, form the ("formatted %s string %d etc.", str,
- // intv) for clib's printf
-
- // TODO we're talking about formatting here, which we plan on doing as a
- // string interpolation, something along the lines of {{variable}}
- // without defining its type would involve lookup split of the string
- // and then formatting
-
- // we need to discuss and decide what we'd do when the user inevitably
- // would print out a ref to a struct. Do we say [[struct]] or do we have
- // some automatic unwrap and display of struct data... probably, yes.
-
- gcc_jit_rvalue** args = (gcc_jit_rvalue**)calloc(MAXARGS, sizeof(gcc_jit_rvalue*));
-
- if (argc > MAXARGS) {
- panic_at(node,
- "we do not currently support more than 16 args to a "
- "print call");
- }
-
- for (size_t i = 0; i < argc; i++) {
- gcc_jit_rvalue* arg = handle_expr(gen, node->data.call_expr.args[i]);
- if (i == 0) {
- if (gcc_jit_rvalue_get_type(arg) != type_cstr) {
- // note this is probably not going to work as
- // limited cast supported and string isn't one
- // of them
- arg = gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), arg, type_cstr);
- }
- } else {
- //
- // simple widening for common scalar types
- //
- gcc_jit_type* ty = gcc_jit_rvalue_get_type(arg);
- if (ty == type_int) {
- arg = gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), arg, type_cstr);
- } else if (ty == type_f32) { // TODO print() for other types
- // variadics already promote float→double;
- // double is
- } else if (ty == type_cstr) {
- // leave as const char*
- } else if (ty == type_boolean) {
- arg = gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), arg, type_cstr);
- } else {
- // fallback: pass pointer as void*
- arg = gcc_jit_context_new_cast(gen->ctx,
- loc_from_node(gen, node),
- arg,
- gcc_jit_context_get_type(gen->ctx, GCC_JIT_TYPE_VOID_PTR));
- }
- }
- // TODO auto grow
- args[i] = arg;
- }
- return gcc_jit_context_new_call(gen->ctx, NULL, gen->printf_fn, argc, args);
- return NULL;
-}
-
-static gcc_jit_function*
-lookup_function(Gen* gen, Scope* scope, const char* func_name)
-{
- for (size_t i = 0; i < scope->len; i++) {
- Symbol* sym = scope->symbols[i];
-
- if (sym->ctype == type_voidp) {
- const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 });
- if (strcmp(name, func_name) == 0) {
- return sym->d.funcvalue; //
- }
- }
- }
-
- printf("lookup_function: not found locally: %s\n", func_name);
-
- if (scope->parent != NULL) {
- printf("looking up function %s in parent\n", func_name);
- return lookup_function(gen, scope->parent, func_name);
- }
-
- return NULL;
-}
-
-static gcc_jit_rvalue*
-handle_func_call(Gen* gen, Node* node)
-{
- Node* fcallee = node->data.call_expr.callee;
- const char* func_name = span_str(gen->src, fcallee->data.ident.name, (char[IDENTSZ]) { 0 });
-
- // short circuit to print
- if (strcmp(func_name, "print") == 0) return lower_builtin_print(gen, node);
-
- gcc_jit_function* callee = lookup_function(gen, gen->scope, func_name);
- gcc_jit_location* loc = loc_from_node(gen, node);
-
- // args handling
- size_t argc = node->data.call_expr.len;
-
- // alloc args
- gcc_jit_rvalue** args = NULL;
-
- if (argc > 0) {
- args = calloc(argc, sizeof *args); // or alloca, or a fixed upper bound
- for (size_t i = 0; i < argc; ++i) {
- args[i] = handle_expr(gen, node->data.call_expr.args[i]);
- }
- }
-
- for (size_t i = 0; i < argc; i++) {
- args[i] = handle_expr(gen, node->data.call_expr.args[i]);
- }
-
- /*
- When generating the identifier expression message in the AST:
- Lookup env["message"] -> you get a gcc_jit_param* or gcc_jit_lvalue*.
- Use gcc_jit_param_as_rvalue / gcc_jit_lvalue_as_rvalue.
-
- gcc_jit_rvalue *msg = gcc_jit_param_as_rvalue(callee_param);
-
- gcc_jit_rvalue *args[1] = { msg };
- gcc_jit_block_add_eval(
- b, loc,
- gcc_jit_context_new_call(ctx, loc, print_fn, 1, args));
- */
-
- // gcc_jit_rvalue *msg = gcc_jit_param_as_rvalue(callee_param);
- // gcc_jit_rvalue *args[1] = { msg };
- assert(callee != NULL && "callee not found");
- return gcc_jit_context_new_call(gen->ctx, loc, callee, argc, args);
-
- // TODO consider
- /*
- // calling bob() directly
- void emit_direct_call(gcc_jit_context *ctxt, SymbolId bob, gcc_jit_function *caller) {
- GccObj *cal = ensure_func(ctxt, bob);
- gcc_jit_block *b = gcc_jit_function_new_block(caller, "call_bob");
- gcc_jit_rvalue *args[] = {};
- gcc_jit_block_add_eval(b, NULL, gcc_jit_context_new_call(ctxt, cal->func, 0, args));
- gcc_jit_block_end_with_void_return(b, NULL);
- }
- */
-
- // return gcc_jit_context_new_call(gen->ctx, NULL, callee, 0, NULL);
-
- // TODO handle return values
- // almost unrelated could be useful to deal with return values:
- // gcc_jit_rvalue* rv = handle_func_call(gen, node);
- // if (rv) { gcc_jit_block_add_eval(gen->curr_block, loc_from_node(gen, node), rv); }
- // return false;
- return NULL;
-}
-
-static gcc_jit_rvalue*
-handle_expr(Gen* gen, Node* node)
-{
- switch (node->type) {
- case NODE_INT_LITERAL:
- return emit_literal_int(gen, node);
- break;
- case NODE_FLOAT_LITERAL:
- return emit_literal_float(gen, node);
- break;
- case NODE_STRING_LITERAL:
- return emit_literal_string(gen, node);
- break;
- case NODE_BOOL_LITERAL:
- return emit_literal_bool(gen, node);
- case NODE_CALL_EXPR:
- return handle_func_call(gen, node);
- break;
- case NODE_IDENT:
- return handle_ident_call(gen, node);
- break;
- case NODE_UNARY_EXPR: // ++ etc.
- return handle_unary_expr(gen, node);
- break;
- case NODE_BINARY_EXPR:
- return handle_binary_expr(gen, node);
- break;
-
- // case NODE_IDENT: {
- // return NULL; // fixme
- // break;
- // }
- default:
- printf("handle_expr unhandled, %s\n", node_type_str(node->type));
- }
- return NULL;
-}
-
-static gcc_jit_type*
-ox_type_to_c_type(Gen* gen, Node* node)
-{
- if (node == NULL) return type_void;
-
- const char* type_name = span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 });
-
- if (strcmp(type_name, "int") == 0) {
- return type_int;
-
- } else if (strcmp(type_name, "i8") == 0) {
- return type_i8;
- } else if (strcmp(type_name, "i16") == 0) {
- return type_i16;
- } else if (strcmp(type_name, "i32") == 0) {
- return type_i32;
- } else if (strcmp(type_name, "i64") == 0) {
- return type_i64;
- } else if (strcmp(type_name, "i128") == 0) {
- return type_i128;
-
- } else if (strcmp(type_name, "u8") == 0) {
- return type_u8;
- } else if (strcmp(type_name, "u16") == 0) {
- return type_u16;
- } else if (strcmp(type_name, "u32") == 0) {
- return type_u32;
- } else if (strcmp(type_name, "u64") == 0) {
- return type_u64;
- } else if (strcmp(type_name, "u128") == 0) {
- return type_u128;
-
- } else if (strcmp(type_name, "f32") == 0) {
- return type_f32;
- } else if (strcmp(type_name, "f64") == 0) {
- return type_f64;
- } else if (strcmp(type_name, "f128") == 0) {
- return type_f128;
-
- } else if (strcmp(type_name, "str") == 0) {
- return type_cstr;
- } else if (strcmp(type_name, "float") == 0) {
- return type_f32;
- } else if (strcmp(type_name, "double") == 0) {
- return type_f64;
- } else if (strcmp(type_name, "longdouble") == 0) {
- return type_f128;
- } else if (strcmp(type_name, "uint") == 0) {
- return type_uint;
- } else if (strcmp(type_name, "void") == 0) {
- return type_void;
- } else {
- panic_at(node, "unhandled type in gen %s", type_name);
- }
- return NULL;
-}
-
-static void
-print_symbols_here(Gen* gen)
-{
- for (size_t i = 0; i < gen->scope->len; i++) {
- Symbol* sym = gen->scope->symbols[i];
- const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 });
- printf("[%zu/%zu] symbol: %s (%s)\n", i + 1, gen->scope->len, name, sym->english_type);
- }
-}
-
-static bool build_block(Gen*, Node*);
-static bool build_statement(Gen*, Node*);
-
-static gcc_jit_rvalue* build_bool_value(Gen*, Node*);
-
-static int block_counter = 0, loop_counter = 0, while_counter = 0;
-
-static bool
-build_while_statement(Gen* gen, Node* node)
-{
- gcc_jit_location* loc = loc_from_node(gen, node);
-
- Node* cond = node->data.while_statement.cond;
- Node* body = node->data.while_statement.body;
-
- while_counter++;
- char label_cond[64], label_body[64], label_end[64];
- snprintf(label_cond, sizeof label_cond, "while.cond%d", while_counter);
- snprintf(label_body, sizeof label_body, "while.body%d", while_counter);
- snprintf(label_end, sizeof label_end, "while.end%d", while_counter);
-
- gcc_jit_block* cond_block = gcc_jit_function_new_block(gen->curr_func, label_cond);
- gcc_jit_block* body_block = gcc_jit_function_new_block(gen->curr_func, label_body);
- gcc_jit_block* end_block = gcc_jit_function_new_block(gen->curr_func, label_end);
-
- /* jump from current block into the loop */
- gcc_jit_block_end_with_jump(gen->curr_block, loc, cond_block);
-
- gen->curr_block = cond_block;
- gcc_jit_rvalue* cnd = NULL;
-
- if (cond != NULL) {
- cnd = build_bool_value(gen, cond);
- gcc_jit_block_end_with_conditional(cond_block, loc, cnd, body_block, end_block);
- } else {
- /* while (1) */
- gcc_jit_block_end_with_jump(cond_block, loc, body_block);
- }
-
- gcc_jit_block* continue_target = cond != NULL ? cond_block : body_block;
- push_loop(gen, /*break to*/ end_block, /*continue to*/ continue_target);
-
- gen->curr_block = body_block;
- bool body_ended = build_block(gen, body);
- if (!body_ended) { gcc_jit_block_end_with_jump(gen->curr_block, loc, continue_target); }
-
- pop_loop(gen);
-
- /* resume after loop */
- gen->curr_block = end_block;
- return false;
-}
-
-static bool
-build_for_statement(Gen* gen, Node* node)
-{
- gcc_jit_location* loc = loc_from_node(gen, node);
-
- Node* init = node->data.for_statement.init;
- Node* cond = node->data.for_statement.cond;
- Node* step = node->data.for_statement.increment;
- Node* body = node->data.for_statement.body;
-
- loop_counter++;
-
- char label_cond[64], label_body[64], label_step[64], label_end[64];
- snprintf(label_cond, 64, "for.cond%d", loop_counter);
- snprintf(label_body, 64, "for.body%d", loop_counter);
- snprintf(label_step, 64, "for.step%d", loop_counter);
- snprintf(label_end, 64, "for.end%d", loop_counter);
-
- gcc_jit_block* cond_block = gcc_jit_function_new_block(gen->curr_func, label_cond);
- gcc_jit_block* body_block = gcc_jit_function_new_block(gen->curr_func, label_body);
- gcc_jit_block* step_block = gcc_jit_function_new_block(gen->curr_func, label_step);
- gcc_jit_block* end_block = gcc_jit_function_new_block(gen->curr_func, label_end);
-
- // gcc_jit_block* saved_break = gen->loop->break_target;
- // gcc_jit_block* saved_cont = gen->loop->continue_target;
- // gen->loop->break_target = end_block;
- // gen->loop->continue_target = step_block;
-
- // header, e.g. for(int = 0 <-
- if (init) { build_statement(gen, init); }
-
- // jump to cond e.g. ; i < 5 <-
- gcc_jit_block_end_with_jump(gen->curr_block, loc, cond_block);
-
- // cond: evaluate
- gen->curr_block = cond_block;
- gcc_jit_rvalue* cnd = NULL;
-
- if (cond != NULL) {
- cnd = build_bool_value(gen, cond);
- gcc_jit_block_end_with_conditional(cond_block, loc, cnd, body_block, end_block);
- } else {
- gcc_jit_block_end_with_jump(cond_block, loc, body_block); // presume for(;;)
- }
-
- push_loop(gen, /*break to*/ end_block, /*continue to*/ step_block);
-
- // build for body
-
- gen->curr_block = body_block;
- bool for_body_ended = build_block(gen, body);
- if (!for_body_ended) { gcc_jit_block_end_with_jump(gen->curr_block, loc, step_block); }
-
- pop_loop(gen);
-
- // step incr etc.
-
- gen->curr_block = step_block;
- if (step) { build_statement(gen, step); }
- gcc_jit_block_end_with_jump(step_block, loc, cond != NULL ? cond_block : body_block);
-
- // resume after loop
-
- gen->curr_block = end_block;
- return false;
-}
-
-static bool
-build_if_statement(Gen* gen, Node* node)
-{
- gcc_jit_location* loc = loc_from_node(gen, node);
- // build the condition
-
- OpType op = node->data.if_statement.cond->data.binary_expr.op;
- Node* lhs = node->data.if_statement.cond->data.binary_expr.lhs;
- Node* rhs = node->data.if_statement.cond->data.binary_expr.rhs;
-
- Node* then_body = node->data.if_statement.then_body;
- Node* else_body = node->data.if_statement.else_body;
-
- enum gcc_jit_comparison cmp;
- switch (op) {
- case OP_EQUALITY:
- cmp = GCC_JIT_COMPARISON_EQ;
- break;
- case OP_INEQUALITY:
- cmp = GCC_JIT_COMPARISON_NE;
- break;
- case OP_GT:
- cmp = GCC_JIT_COMPARISON_GT;
- break;
- case OP_GT_EQ:
- cmp = GCC_JIT_COMPARISON_GE;
- break;
- case OP_LT_EQ:
- cmp = GCC_JIT_COMPARISON_LE;
- break;
- case OP_LT:
- cmp = GCC_JIT_COMPARISON_LT;
- break;
- default:
- printf("/!\\ build_statement NODE_IF unhandled, %d\n", op);
- cmp = GCC_JIT_COMPARISON_NE;
- }
-
- gcc_jit_rvalue* lhs_val = handle_expr(gen, lhs);
- gcc_jit_rvalue* rhs_val = handle_expr(gen, rhs);
-
- gcc_jit_rvalue* cond = gcc_jit_context_new_comparison(gen->ctx, loc, cmp, lhs_val, rhs_val);
-
- // create the BLOCKS
-
- // labels
- block_counter++;
- char label_then[64], label_else[64], label_end[64];
- snprintf(label_then, 64, "if.then%d", block_counter);
- snprintf(label_else, 64, "if.else%d", block_counter);
- snprintf(label_end, 64, "if.end%d", block_counter);
-
- // blocks
- gcc_jit_block* then_bb = gcc_jit_function_new_block(gen->curr_func, label_then);
- gcc_jit_block* else_bb = else_body ? gcc_jit_function_new_block(gen->curr_func, label_else) : NULL;
- gcc_jit_block* merge_bb = NULL;
-
- if (!else_bb) {
- // no else: need merge now for the false edge
- merge_bb = gcc_jit_function_new_block(gen->curr_func, label_end);
- gcc_jit_block_end_with_conditional(gen->curr_block, loc, cond, then_bb, merge_bb);
- } else {
- // with else: branch to then/else; decide on merge later
- gcc_jit_block_end_with_conditional(gen->curr_block, loc, cond, then_bb, else_bb);
- }
-
- // THEN
- gen->curr_block = then_bb;
- bool then_ended = build_block(gen, then_body);
- gcc_jit_block* then_open = gen->curr_block; // last open block in THEN
- // (may differ from then_bb)
-
- // ELSE
- bool else_ended = false;
- gcc_jit_block* else_open = NULL;
- if (else_bb) {
- gen->curr_block = else_bb;
- else_ended = build_block(gen, else_body);
- else_open = gen->curr_block; // last open block in ELSE
- }
-
- // If both branches ended, no merge needed, we notify we have ended as
- // well
- if (else_bb && then_ended && else_ended) return true;
-
- // Ensure we have a merge if any branch continues.
- if (!merge_bb) merge_bb = gcc_jit_function_new_block(gen->curr_func, label_end);
-
- if (!then_ended) gcc_jit_block_end_with_jump(then_open, loc, merge_bb);
- if (else_bb && !else_ended) gcc_jit_block_end_with_jump(else_open, loc, merge_bb);
-
- gen->curr_block = merge_bb;
- return false;
-}
-
-static bool
-build_var_decl_statement(Gen* gen, Node* node)
-{
- gcc_jit_location* loc = loc_from_node(gen, node);
- const char* var_name = span_str(gen->src, node->data.var_decl.name, (char[IDENTSZ]) { 0 });
- gcc_jit_type* declared_type = ox_type_to_c_type(gen, node->data.var_decl.type);
-
- gcc_jit_lvalue* var_decl = NULL;
-
- gcc_jit_rvalue* rvalue = NULL;
- Node* init = node->data.var_decl.init;
- if (init != NULL) { rvalue = handle_expr(gen, init); }
-
- if (gen->curr_func == NULL && gen->curr_block == NULL) {
- // global var
- var_decl = gcc_jit_context_new_global(gen->ctx, loc, GCC_JIT_GLOBAL_INTERNAL, declared_type, strdup(var_name));
- if (rvalue) gcc_jit_global_set_initializer_rvalue(var_decl, rvalue);
- } else {
- // local var
- var_decl = gcc_jit_function_new_local(gen->curr_func, loc, declared_type, strdup(var_name));
- if (rvalue) gcc_jit_block_add_assignment(gen->curr_block, loc, var_decl, rvalue);
- }
-
- // this is not an error, there is just no initial value
- // if (node->data.var_decl.init == NULL) { panic("could not instanciate gcc jit new local"); }
-
- printf("adding 1 symbol: %s\n", var_name);
-
- Symbol* sym = (Symbol*)calloc(1, sizeof(Symbol));
- sym->name = node->data.var_decl.name;
- sym->decl = node;
- sym->ctype = declared_type;
- sym->d.lvalue = var_decl;
- sym->english_type = type_var;
- sym->is_variadic = node->data.var_decl.type->data.ident_type.is_variadic;
- sym->is_comp_time = node->data.var_decl.type->data.ident_type.is_comp_time;
- add_symbol(gen, sym); // add var_decl symbol
- return false;
-}
-
-static void build_func_decl(Gen*, Node*);
-
-static bool
-build_statement(Gen* gen, Node* node)
-{
- gcc_jit_location* loc = loc_from_node(gen, node);
- switch (node->type) {
- case NODE_RETURN: {
- Node* return_expr = node->data.ret.expr;
- if (return_expr) {
- gcc_jit_rvalue* ret_rval = handle_expr(gen, return_expr);
- gcc_jit_type* expect_type = gcc_jit_function_get_return_type(gen->curr_func);
- gcc_jit_type* ret_type = gcc_jit_rvalue_get_type(ret_rval);
- types_match_or_panic(node, expect_type, ret_type);
- gcc_jit_block_end_with_return(gen->curr_block, loc, ret_rval);
- } else {
- gcc_jit_block_end_with_void_return(gen->curr_block, loc);
- }
- gen->curr_block = NULL; // important
- return true; // we end the block here
- }
- case NODE_BREAK: {
- gcc_jit_block* t = current_break(gen);
- if (!t) return true; // break outside the loop
- gcc_jit_block_end_with_jump(gen->curr_block, loc, t);
- gen->curr_block = NULL;
- return true;
- }
- case NODE_CONTINUE: {
- gcc_jit_block* t = current_continue(gen);
- if (!t) return true; // continue outside of a loop
- gcc_jit_block_end_with_jump(gen->curr_block, loc, t);
- gen->curr_block = NULL;
- return true;
- }
- case NODE_VAR_DECL: {
- return build_var_decl_statement(gen, node);
- }
- case NODE_VAR_ASSIGN: {
- gcc_jit_rvalue* rvalue = handle_expr(gen, node->data.var_assign.rhs);
- // find the var_decl (d.lvalue) from the symbols table's symbol.
- for (size_t i = 0; i < gen->scope->len; i++) {
- Symbol* sym = gen->scope->symbols[i];
- if (span_ident_same(sym->name, node->data.var_assign.lhs->data.ident.name, gen->src)) {
- // we found the symbol we are assigning to
- // TODO check for variadic and comp_time
- if (!sym->is_variadic) {
- const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 });
- panic_at(node, "symbol %s is not variadic, cannot mutate.", name);
- }
- if (sym->is_comp_time) {
- const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 });
- panic_at(node, "symbol %s is comp time, cannot mutate.", name);
- }
-
- gcc_jit_lvalue* lvalue = sym->d.lvalue;
-
- // check the type of lvalue matches the rvalue
- gcc_jit_type* ltype = sym->ctype;
- gcc_jit_type* rtype = gcc_jit_rvalue_get_type(rvalue);
- if (rtype != ltype) {
- panic_at(node,
- "right hand side of assigment "
- "doesn't match the "
- "type of the left hand side: %s <-- %s",
- get_english_type(ltype),
- get_english_type(rtype));
- }
- if (lvalue) gcc_jit_block_add_assignment(gen->curr_block, loc, lvalue, rvalue);
- }
- }
- break;
- }
- case NODE_BINARY_EXPR: {
- Symbol* sym = find_symbol(gen, gen->scope, node->data.binary_expr.lhs->data.ident.name);
- if (!sym)
- panic_at(node,
- "NODE_BINARY_EXPR: Undefined symbol: %s\n",
- span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 }));
-
- gcc_jit_lvalue* lv = sym->d.lvalue;
- gcc_jit_rvalue* rhs = handle_expr(gen, node->data.binary_expr.rhs);
- gcc_jit_rvalue* lhs = handle_expr(gen, node->data.binary_expr.lhs);
- gcc_jit_rvalue* temp = gcc_jit_lvalue_as_rvalue(lv);
- gcc_jit_type* lvtype = gcc_jit_rvalue_get_type(temp);
- gcc_jit_rvalue* sum
- = gcc_jit_context_new_binary_op(gen->ctx, loc_from_node(gen, node), GCC_JIT_BINARY_OP_PLUS, lvtype, lhs, rhs);
-
- gcc_jit_block_add_assignment(gen->curr_block, loc_from_node(gen, node), lv, sum);
- }
- case NODE_UNARY_EXPR: {
- gcc_jit_rvalue* rv = handle_unary_expr(gen, node);
- if (rv) gcc_jit_block_add_eval(gen->curr_block, loc_from_node(gen, node), rv);
- break;
- }
- case NODE_EXPR_STATEMENT: {
- gcc_jit_rvalue* rv = handle_expr(gen, node->data.expr_statement.expr);
- if (rv) gcc_jit_block_add_eval(gen->curr_block, loc_from_node(gen, node), rv);
- break;
- }
- case NODE_IF:
- return build_if_statement(gen, node);
- case NODE_FOR:
- return build_for_statement(gen, node);
- case NODE_WHILE:
- return build_while_statement(gen, node);
- case NODE_FUNCTION_DECL:
- // "closure"
- build_func_decl(gen, node);
- return false;
- case NODE_BLOCK:
- build_block(gen, node);
- default:
- printf("--- WARNING: build_statement unhandled, %s\n", node_type_str(node->type));
- break;
- }
- return false;
-}
-
-static gcc_jit_rvalue*
-build_bool_value(Gen* gen, Node* node)
-{
- gcc_jit_location* loc = loc_from_node(gen, node);
-
- switch (node->type) {
- case NODE_BOOL_LITERAL:
- /* true/false literal */
- return gcc_jit_context_new_rvalue_from_int(gen->ctx, type_boolean, node->data.boolean.value ? 1 : 0);
- break;
- case NODE_INT_LITERAL: {
- /* while (1) style – C truthiness */
- gcc_jit_rvalue* iv = emit_literal_int(gen, node);
- return gcc_jit_context_new_cast(gen->ctx, loc, iv, type_boolean);
- }
- case NODE_STRING_LITERAL: {
- /* while("word") should also be truthy if the pointer is non null */
- gcc_jit_rvalue* v = emit_literal_string(gen, node);
- gcc_jit_type* ty = gcc_jit_rvalue_get_type(v);
- gcc_jit_rvalue* nullv = gcc_jit_context_null(gen->ctx, ty);
- return gcc_jit_context_new_comparison(gen->ctx, loc, GCC_JIT_COMPARISON_NE, v, nullv);
- }
- case NODE_BINARY_EXPR: {
- gcc_jit_rvalue* lvalue = handle_expr(gen, node->data.binary_expr.lhs);
- gcc_jit_rvalue* rvalue = handle_expr(gen, node->data.binary_expr.rhs);
- enum gcc_jit_comparison op;
- switch (node->data.binary_expr.op) {
- case OP_LT:
- op = GCC_JIT_COMPARISON_LT;
- break;
- case OP_GT:
- op = GCC_JIT_COMPARISON_GT;
- break;
- case OP_LT_EQ:
- op = GCC_JIT_COMPARISON_LE;
- break;
- case OP_GT_EQ:
- op = GCC_JIT_COMPARISON_GE;
- break;
- default:
- printf("build_bool_rvalue nodebinary op unhandled, "
- "%s\n",
- node_type_str(node->type));
- return NULL;
- }
- return gcc_jit_context_new_comparison(gen->ctx, loc, op, lvalue, rvalue);
- break;
- }
- default:
- printf("/!\\ build_bool_rvalue unhandled, %s\n", node_type_str(node->type));
- break;
- }
-
- return NULL;
-}
-
-// build_block: returns true if block ended with a terminator
-static bool
-build_block(Gen* gen, Node* body)
-{
- // TODO if there is no block ({ ... }), we presume there is only 1 statement.
- // This might not always be valid, func declaration do require a block {}, but there might be cases...
- // FIXME: a symbol declared in a child block is accessible in the parent or in a sibling block...
- if (body->data.block.len == 0) { return build_statement(gen, body); }
-
- for (size_t i = 0; i < body->data.block.len; i++) {
- if (gen->curr_block == NULL) return true;
- bool ended = build_statement(gen, body->data.block.stmts[i]);
- if (ended) return true;
- }
- return false;
-}
-
-static void
-build_func_decl(Gen* gen, Node* node)
-{
- const char* func_name = span_str(gen->src, node->data.function_decl.name, (char[IDENTSZ]) { 0 });
-
- Node* return_type = node->data.function_decl.return_type;
- gcc_jit_type* ret_type = ox_type_to_c_type(gen, return_type);
- gcc_jit_location* loc = loc_from_node(gen, node);
-
- size_t argc = node->data.function_decl.p_len;
-
- gcc_jit_param** params = calloc(argc, sizeof(gcc_jit_param*));
-
- // 1) Create ONE gcc_jit_param per AST param
- for (size_t i = 0; i < argc; i++) {
- Node* pnode = node->data.function_decl.params[i];
- const char* pname = span_str(gen->src, pnode->data.param.name, (char[IDENTSZ]) { 0 });
-
- gcc_jit_type* declared_type = ox_type_to_c_type(gen, pnode->data.param.type);
-
- gcc_jit_param* p = gcc_jit_context_new_param(gen->ctx, loc, declared_type, strdup(pname));
-
- params[i] = p;
- }
-
- // 2) Create the function with those params
- gcc_jit_function* func = gcc_jit_context_new_function(gen->ctx,
- loc,
- GCC_JIT_FUNCTION_EXPORTED,
- ret_type,
- strdup(func_name),
- argc,
- params, // <-- these are now owned by 'func'
- 0);
-
- gcc_jit_block* block = gcc_jit_function_new_block(func, "entry");
-
- gcc_jit_function* prev_func = gen->curr_func;
- gcc_jit_block* prev_block = gen->curr_block;
- gen->curr_func = func;
- gen->curr_block = block;
-
- // 3) Add the function symbol to the current (enclosing) scope
- Symbol* sym = (Symbol*)calloc(1, sizeof(Symbol));
- sym->name = node->data.function_decl.name;
- sym->decl = node;
- sym->ctype = type_voidp; // whatever you use for function type metadata
- sym->d.funcvalue = func;
- sym->english_type = type_func;
- add_symbol(gen, sym); // add function symbol -- to the 'parent scope', before we push_scope
-
- // ENTER FUNCTION SCOPE
- push_scope(gen);
-
- // 4) Add parameter symbols to the FUNCTION scope, reusing the same gcc_jit_param*
- for (size_t i = 0; i < argc; i++) {
- Node* pnode = node->data.function_decl.params[i];
-
- gcc_jit_param* p = params[i]; // <-- reuse, DO NOT new_param again
-
- gcc_jit_type* declared_type = ox_type_to_c_type(gen, pnode->data.param.type);
-
- Symbol* ps = (Symbol*)calloc(1, sizeof(Symbol));
- ps->name = pnode->data.param.name; // param identifier span
- ps->decl = pnode;
- ps->ctype = declared_type;
- ps->d.param = p; // <-- this is associated with 'func'
- ps->english_type = type_var; // or type_param if you distinguish
- add_symbol(gen, ps); // add param symbol
- }
-
- print_symbols_here(gen);
-
- build_block(gen, node->data.function_decl.body);
-
- if (gen->curr_block == NULL) {
- // no open block: all paths already terminated, don't add an implicit return
- } else if (ret_type == type_int) {
- gcc_jit_rvalue* ret_value = gcc_jit_context_new_rvalue_from_int(gen->ctx, type_int, 0);
- gcc_jit_block_end_with_return(gen->curr_block, loc, ret_value);
- } else if (ret_type == type_void) {
- gcc_jit_block_end_with_void_return(gen->curr_block, loc);
- } else {
- printf("build_func_decl unhandled return type in func: %s - "
- "defaulting to void\n",
- func_name);
- gcc_jit_block_end_with_void_return(gen->curr_block, loc);
- }
-
- // LEAVE FUNCTION SCOPE
- pop_scope(gen);
-
- gen->curr_func = prev_func;
- gen->curr_block = prev_block;
-}
-
-void
-gen_next(Gen* gen, Node* node)
-{
- // printf("gen_next, %s\n", node_type_str(node->type));
-
- switch (node->type) {
- case NODE_PROGRAM:
- build_program(gen, node);
- break;
- case NODE_FUNCTION_DECL:
- build_func_decl(gen, node);
- break;
- case NODE_STRING_LITERAL:
- emit_literal_string(gen, node);
- break;
- case NODE_INT_LITERAL:
- emit_literal_int(gen, node);
- break;
- case NODE_FLOAT_LITERAL:
- emit_literal_float(gen, node);
- break;
- case NODE_VAR_DECL:
- build_statement(gen, node);
- break;
- default:
- printf("gen: unhandled, %s\n", node_type_str(node->type));
- }
-}