ox

The Ox programming language, compiler and tools (WIP)
Log | Files | Refs | README | LICENSE

gen.c (37311B)


      1 #include "../gen.h"
      2 #include "../parser.h"
      3 #include "../utils.h"
      4 
      5 #include <stdint.h>
      6 #include <stdio.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 #include <assert.h>
     10 #include <sys/param.h>
     11 
     12 static gcc_jit_type* type_int;
     13 static gcc_jit_type* type_float;
     14 static gcc_jit_type* type_uint;
     15 static gcc_jit_type* type_double;
     16 static gcc_jit_type* type_long;
     17 static gcc_jit_type* type_void;
     18 static gcc_jit_type* type_cstr;
     19 static gcc_jit_type* type_char;
     20 static gcc_jit_type* type_voidp;
     21 
     22 static const char* type_func = "function";
     23 static const char* type_var = "variable";
     24 
     25 #define MAXARGS 16
     26 
     27 static gcc_jit_location*
     28 loc_from_node(Gen* gen, Node* node)
     29 {
     30 	if (node->filename == NULL) return NULL;
     31 	return gcc_jit_context_new_location(gen->ctx, node->filename, node->line, node->col);
     32 }
     33 
     34 static void
     35 push_loop(Gen* gen, gcc_jit_block* brk, gcc_jit_block* cont)
     36 {
     37 	LoopContext* lctx = (LoopContext*)malloc(sizeof(LoopContext));
     38 	if (lctx == NULL) { panic("push_loop: could not alloc"); }
     39 	lctx->break_target = brk;
     40 	lctx->continue_target = cont;
     41 	lctx->prev = gen->loop;
     42 	gen->loop = lctx;
     43 }
     44 
     45 static void
     46 pop_loop(Gen* gen)
     47 {
     48 	LoopContext* lctx = gen->loop;
     49 	gen->loop = lctx->prev;
     50 	free(lctx);
     51 }
     52 
     53 static inline gcc_jit_block*
     54 current_break(Gen* gen)
     55 {
     56 	return gen->loop ? gen->loop->break_target : NULL;
     57 }
     58 
     59 static inline gcc_jit_block*
     60 current_continue(Gen* gen)
     61 {
     62 	return gen->loop ? gen->loop->continue_target : NULL;
     63 }
     64 
     65 __attribute__((unused)) static const char*
     66 get_english_type(gcc_jit_type* T)
     67 {
     68 	return gcc_jit_object_get_debug_string(gcc_jit_type_as_object(T));
     69 }
     70 
     71 Gen
     72 gen_init(Scope* scope, const char* src, Node* node, bool quiet)
     73 {
     74 	if (scope == NULL || src == NULL) { panic("gen_init: no Scope or AST provided"); }
     75 
     76 	gcc_jit_context* ctx;
     77 
     78 	ctx = gcc_jit_context_acquire();
     79 
     80 	if (!ctx) { panic("could not acquire gcc jit context"); }
     81 
     82 	// needs loc* to work
     83 	// gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DEBUGINFO,
     84 	// 1); high level
     85 
     86 	gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DEBUGINFO, 1);
     87 
     88 	if (quiet == false) { gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, 1); }
     89 
     90 	// gcc_jit_context_set_bool_option(ctx,
     91 	// GCC_JIT_BOOL_OPTION_DUMP_SUMMARY, 1);
     92 
     93 	gcc_jit_context_set_str_option(ctx, GCC_JIT_STR_OPTION_PROGNAME, "ox");
     94 	// keep FP
     95 	gcc_jit_context_add_driver_option(ctx, "-fno-omit-frame-pointer");
     96 
     97 	gcc_jit_context_set_int_option(ctx,
     98 		GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL,
     99 		/*0-3 for O3*/ 0);
    100 
    101 	type_int = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT64_T);
    102 	type_float = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_BFLOAT16);
    103 	type_uint = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT64_T);
    104 	type_double = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_DOUBLE);
    105 	type_long = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG_LONG);
    106 	type_char = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR);
    107 	type_void = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_VOID);
    108 	type_cstr = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_CONST_CHAR_PTR);
    109 	type_voidp = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_VOID_PTR);
    110 	/* gcc_jit_type_dyncast_function_ptr_type; */
    111 
    112 	gcc_jit_location* loc = gcc_jit_context_new_location(ctx, node->filename, 0, 0);
    113 
    114 	gcc_jit_param* pm_puts[] = { gcc_jit_context_new_param(ctx, loc, type_cstr, "s") };
    115 	gcc_jit_function* fn_puts = gcc_jit_context_new_function(ctx, loc, GCC_JIT_FUNCTION_IMPORTED, type_int, "puts", 1, pm_puts, 0);
    116 
    117 	gcc_jit_param* pm_printf[] = { gcc_jit_context_new_param(ctx, loc, type_cstr, "fmt") };
    118 	gcc_jit_function* fn_printf = gcc_jit_context_new_function(ctx,
    119 		loc,
    120 		GCC_JIT_FUNCTION_IMPORTED,
    121 		type_int,
    122 		"printf",
    123 		1,
    124 		pm_printf,
    125 		/*is_variadic=*/1);
    126 
    127 	return (Gen) {
    128 		.ctx = ctx,
    129 		.scope = scope,
    130 		.prev_func = NULL,
    131 		.curr_func = NULL,
    132 		.prev_block = NULL,
    133 		.curr_block = NULL,
    134 		.puts_fn = fn_puts,
    135 		.printf_fn = fn_printf,
    136 		.src = src,
    137 	};
    138 }
    139 
    140 static void
    141 push_scope(Gen* gen)
    142 {
    143 	printf("push_scope\n");
    144 	Scope* scope = calloc(1, sizeof(Scope));
    145 	*scope = (Scope) { 0 };
    146 	scope->symbols = (Symbol**)calloc(16, sizeof(Symbol*));
    147 	if (scope->symbols == NULL) panic("push_scope: symbols: could not alloc");
    148 	scope->cap = 16;
    149 	scope->len = 0;
    150 
    151 	scope->parent = gen->scope;
    152 	gen->scope = scope;
    153 }
    154 
    155 static void
    156 pop_scope(Gen* gen)
    157 {
    158 	printf("pop_scope\n");
    159 	Scope* s = gen->scope;
    160 	gen->scope = s->parent;
    161 }
    162 
    163 static Symbol*
    164 find_symbol(Gen* gen, Scope* scope, Span name)
    165 {
    166 	const char* ident_name = span_str(gen->src, name, (char[IDENTSZ]) { 0 });
    167 	// Look up the symbol in the current scope // TODO look up in parent
    168 	// scope with utility function and recursion
    169 	for (size_t i = 0; i < scope->len; i++) {
    170 		Symbol* sym = scope->symbols[i];
    171 		const char* sym_name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 });
    172 		// printf("symbol %s\n", sym_name);
    173 		// printf("ident %s\n", ident_name);
    174 		if (strcmp(sym_name, ident_name) == 0) {
    175 			printf("found symbol %s\n", sym_name);
    176 			return sym;
    177 		}
    178 	}
    179 	printf("find_symbol: not found locally: %s\n", ident_name);
    180 
    181 	if (scope->parent != NULL) {
    182 		printf("looking up symbol %s in parent\n", ident_name);
    183 		return find_symbol(gen, scope->parent, name);
    184 	}
    185 
    186 	return NULL;
    187 }
    188 
    189 static void
    190 add_symbol(Gen* gen, Symbol* sym)
    191 {
    192 	if (gen->scope->len == gen->scope->cap) {
    193 		gen->scope->cap *= 2;
    194 		gen->scope->symbols = (Symbol**)realloc(gen->scope->symbols, sizeof(Symbol*) * gen->scope->cap);
    195 	}
    196 
    197 	gen->scope->symbols[gen->scope->len++] = sym;
    198 
    199 	printf("add_symbol: we now have %zu symbols: ", gen->scope->len);
    200 	for (size_t i = 0; i < gen->scope->len; i++) {
    201 		Symbol* symy = gen->scope->symbols[i];
    202 		const char* name = span_str(gen->src, symy->name, (char[IDENTSZ]) { 0 });
    203 		printf("%s, ", name);
    204 	}
    205 	printf("\n");
    206 }
    207 
    208 static gcc_jit_rvalue*
    209 handle_ident_call(Gen* gen, Node* node)
    210 {
    211 	// Look up the symbol in the current scope // TODO look up in parent
    212 	// scope with utility function and recursion
    213 	Symbol* sym = find_symbol(gen, gen->scope, node->data.ident.name);
    214 
    215 	if (sym == NULL) {
    216 		softpanic("handle_ident_call: undefined variable: %s\n",
    217 			span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 }));
    218 	}
    219 	return gcc_jit_lvalue_as_rvalue(sym->d.lvalue);
    220 }
    221 
    222 static gcc_jit_rvalue* handle_expr(Gen*, Node*);
    223 
    224 static inline int
    225 is_intlike(gcc_jit_context* ctx, gcc_jit_type* t)
    226 {
    227 	return t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT) || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_INT)
    228 		|| t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_SHORT)
    229 		|| t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_SHORT)
    230 		|| t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG)
    231 		|| t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG)
    232 		|| t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG_LONG)
    233 		|| t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG)
    234 		|| t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT64_T) || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT32_T)
    235 		|| t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_CHAR)
    236 		|| t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR);
    237 }
    238 
    239 static inline int
    240 is_floatlike(gcc_jit_context* ctx, gcc_jit_type* t)
    241 {
    242 	return t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_FLOAT) || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_DOUBLE)
    243 		|| t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG_DOUBLE);
    244 }
    245 
    246 static inline int
    247 is_cstr(gcc_jit_context* ctx, gcc_jit_type* t)
    248 {
    249 	return t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_CONST_CHAR_PTR);
    250 }
    251 
    252 // > < >= <= + - / * %
    253 static gcc_jit_rvalue*
    254 handle_bin_expr(Gen* gen, Node* node, int gcc_jit_op, bool cmp)
    255 {
    256 	Node* lhs = node->data.binary_expr.lhs;
    257 	Node* rhs = node->data.binary_expr.rhs;
    258 	gcc_jit_context* ctx = gen->ctx;
    259 	gcc_jit_location* loc = loc_from_node(gen, node);
    260 
    261 	gcc_jit_rvalue* L = handle_expr(gen, lhs);
    262 	gcc_jit_rvalue* R = handle_expr(gen, rhs);
    263 	gcc_jit_type* Lt = gcc_jit_rvalue_get_type(L);
    264 	gcc_jit_type* Rt = gcc_jit_rvalue_get_type(R);
    265 	gcc_jit_rvalue* result = NULL;
    266 
    267 	// floats: cast both to double
    268 	if (is_floatlike(ctx, Lt) || is_floatlike(ctx, Rt)) {
    269 		gcc_jit_type* T = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_DOUBLE);
    270 		L = gcc_jit_context_new_cast(ctx, loc, L, T);
    271 		R = gcc_jit_context_new_cast(ctx, loc, R, T);
    272 		result = cmp ? gcc_jit_context_new_comparison(ctx, loc, gcc_jit_op, L, R)
    273 			     : gcc_jit_context_new_binary_op(ctx, loc, gcc_jit_op, T, L, R);
    274 		;
    275 	}
    276 
    277 	// integers: cast both to signed/unsigned long long (cheap, predictable)
    278 	if (is_intlike(ctx, Lt) && is_intlike(ctx, Rt)) {
    279 		int any_unsigned = (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR))
    280 			|| (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_SHORT))
    281 			|| (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_INT))
    282 			|| (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG))
    283 			|| (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG))
    284 			|| (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR))
    285 			|| (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_SHORT))
    286 			|| (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_INT))
    287 			|| (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG))
    288 			|| (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG));
    289 
    290 		gcc_jit_type* T
    291 			= gcc_jit_context_get_type(ctx, any_unsigned ? GCC_JIT_TYPE_UNSIGNED_LONG_LONG : GCC_JIT_TYPE_LONG_LONG);
    292 
    293 		L = gcc_jit_context_new_cast(ctx, loc, L, T);
    294 		R = gcc_jit_context_new_cast(ctx, loc, R, T);
    295 		result = cmp ? gcc_jit_context_new_comparison(ctx, loc, gcc_jit_op, L, R)
    296 			     : gcc_jit_context_new_binary_op(ctx, loc, gcc_jit_op, T, L, R);
    297 	}
    298 
    299 	// pointers: either reject or compare addresses (UB in C for unrelated objs).
    300 	if (
    301 #ifdef GCC_JIT_HAVE_gcc_jit_type_is_pointer
    302 		gcc_jit_type_is_pointer(Lt) && gcc_jit_type_is_pointer(Rt)
    303 #else
    304 		0
    305 #endif
    306 	) {
    307 		gcc_jit_type* T = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG);
    308 		L = gcc_jit_context_new_cast(ctx, loc, L, T);
    309 		R = gcc_jit_context_new_cast(ctx, loc, R, T);
    310 		result = cmp ? gcc_jit_context_new_comparison(ctx, loc, gcc_jit_op, L, R)
    311 			     : gcc_jit_context_new_binary_op(ctx, loc, gcc_jit_op, T, L, R);
    312 	}
    313 
    314 	return result;
    315 }
    316 
    317 static gcc_jit_rvalue*
    318 handle_binary_expr(Gen* gen, Node* node)
    319 {
    320 	int gcc_jit_op = -1;
    321 	OpType op = node->data.binary_expr.op;
    322 
    323 	switch (op) {
    324 	case OP_PLUS:
    325 		gcc_jit_op = GCC_JIT_BINARY_OP_PLUS;
    326 		return handle_bin_expr(gen, node, gcc_jit_op, false);
    327 		break;
    328 	case OP_MINUS:
    329 		gcc_jit_op = GCC_JIT_BINARY_OP_MINUS;
    330 		return handle_bin_expr(gen, node, gcc_jit_op, false);
    331 		break;
    332 	case OP_MUL:
    333 		gcc_jit_op = GCC_JIT_BINARY_OP_MULT;
    334 		return handle_bin_expr(gen, node, gcc_jit_op, false);
    335 		break;
    336 	case OP_DIV:
    337 		gcc_jit_op = GCC_JIT_BINARY_OP_DIVIDE;
    338 		return handle_bin_expr(gen, node, gcc_jit_op, false);
    339 		break;
    340 	case OP_MOD:
    341 		gcc_jit_op = GCC_JIT_BINARY_OP_MODULO;
    342 		return handle_bin_expr(gen, node, gcc_jit_op, false);
    343 		break;
    344 	case OP_LT:
    345 		gcc_jit_op = GCC_JIT_COMPARISON_LT;
    346 		break;
    347 	case OP_GT:
    348 		gcc_jit_op = GCC_JIT_COMPARISON_GT;
    349 		break;
    350 	case OP_GT_EQ:
    351 		gcc_jit_op = GCC_JIT_COMPARISON_GE;
    352 		break;
    353 	case OP_LT_EQ:
    354 		gcc_jit_op = GCC_JIT_COMPARISON_LE;
    355 		break;
    356 	default:
    357 		printf("handle_binary_expr unhandled OpType %d (can be ignored in var assignment "
    358 		       "(for now))\n",
    359 			op);
    360 		return NULL;
    361 		break;
    362 	}
    363 
    364 	if (gcc_jit_op < 0) {
    365 		// unsupported types (e.g., strings or structs)
    366 		printf("handle_binary_expr OP_LT: unsupported operand types\n");
    367 		return NULL;
    368 	}
    369 
    370 	return handle_bin_expr(gen, node, gcc_jit_op, true);
    371 }
    372 
    373 static gcc_jit_rvalue*
    374 handle_unary_expr(Gen* gen, Node* node)
    375 {
    376 	gcc_jit_context* ctx = gen->ctx;
    377 	gcc_jit_block* bb = gen->curr_block;
    378 
    379 	Node* opnd = node->data.unary_expr.operand;
    380 	Symbol* sym = find_symbol(gen, gen->scope, opnd->data.ident.name);
    381 	if (sym == NULL) {
    382 		softpanic("handle_unary_expr: undefined variable: %s\n",
    383 			span_str(gen->src, opnd->data.ident.name, (char[IDENTSZ]) { 0 }));
    384 	}
    385 
    386 	gcc_jit_lvalue* lv = sym->d.lvalue;
    387 	gcc_jit_rvalue* orig = gcc_jit_lvalue_as_rvalue(sym->d.lvalue);
    388 	gcc_jit_type* ty = gcc_jit_rvalue_get_type(orig);
    389 	gcc_jit_rvalue* one = gcc_jit_context_one(ctx, ty);
    390 
    391 	gcc_jit_rvalue* inc = gcc_jit_context_new_binary_op(ctx, NULL, GCC_JIT_BINARY_OP_PLUS, ty, orig, one);
    392 	switch (node->data.unary_expr.op) {
    393 	case OPER_POSTINC: { // i++
    394 		gcc_jit_block_add_assignment(bb, NULL, lv, inc);
    395 		return orig; // return value before incr as ++ is postfix
    396 
    397 		if (lv == NULL) {
    398 			printf("handle_unary_expr: no lvalue\n");
    399 			return NULL;
    400 		}
    401 		printf("wef\n");
    402 		break;
    403 	}
    404 	default:
    405 		printf("handle_unary_expr, unhandled op %d\n", node->data.unary_expr.op);
    406 		return NULL;
    407 		break;
    408 	}
    409 	return NULL;
    410 }
    411 
    412 static gcc_jit_rvalue*
    413 emit_literal_string(Gen* gen, Node* node)
    414 {
    415 	size_t len = node->data.string.value.end - node->data.string.value.start;
    416 	char* str = calloc(len + 1, sizeof(char));
    417 	if (str == NULL) panic("emit_literal_string: could not alloc");
    418 	memcpy(str, gen->src + node->data.string.value.start, len);
    419 	str[len] = '\0';
    420 	return gcc_jit_context_new_string_literal(gen->ctx, str);
    421 }
    422 
    423 static gcc_jit_rvalue*
    424 emit_literal_int(Gen* gen, Node* node)
    425 {
    426 	return gcc_jit_context_new_rvalue_from_int(gen->ctx, type_int, (int)node->data.number.value);
    427 }
    428 
    429 static gcc_jit_rvalue*
    430 emit_literal_float(Gen* gen, Node* node)
    431 {
    432 	return gcc_jit_context_new_rvalue_from_double(gen->ctx, type_double, node->data.number.value);
    433 }
    434 
    435 static void
    436 build_program(Gen* gen, Node* node)
    437 {
    438 	size_t cnt = node->data.program.len;
    439 	for (size_t i = 0; i < cnt; i++) {
    440 		gen_next(gen, node->data.program.decl[i]);
    441 	}
    442 }
    443 
    444 static gcc_jit_rvalue*
    445 lower_builtin_print(Gen* gen, Node* node)
    446 {
    447 	size_t argc = node->data.call_expr.len;
    448 
    449 	// for (size_t i = 0; i < argc; i++) {
    450 	// Node* arg = node->data.call_expr.args[i];
    451 	// }
    452 
    453 	// 1-arg, treat as puts(arg)
    454 	if (argc == 1) {
    455 		gcc_jit_rvalue* arg = handle_expr(gen, node->data.call_expr.args[0]);
    456 		gcc_jit_type* t = gcc_jit_rvalue_get_type(arg);
    457 		gcc_jit_location* loc = loc_from_node(gen, node->data.call_expr.args[0]);
    458 		// print a string
    459 		if (is_cstr(gen->ctx, t)) {
    460 			gcc_jit_rvalue* args[] = { arg };
    461 			return gcc_jit_context_new_call(gen->ctx, loc, gen->puts_fn, 1, args);
    462 		}
    463 		// print a integer
    464 		else if (is_intlike(gen->ctx, t)) {
    465 			// cast to int for a clean %d
    466 			// gcc_jit_type* t_int
    467 			// = gcc_jit_context_get_type(gen->ctx, GCC_JIT_TYPE_INT64_T);
    468 			gcc_jit_rvalue* fmt = gcc_jit_context_new_string_literal(gen->ctx, "%d\n");
    469 			gcc_jit_rvalue* ival = arg; //(t == t_int)
    470 			                            //? arg
    471 			                            //: gcc_jit_context_new_cast(gen->ctx,
    472 			                            //: NULL, arg, t_int);
    473 			gcc_jit_rvalue* args[] = { fmt, ival };
    474 			return gcc_jit_context_new_call(gen->ctx, loc, gen->printf_fn, 2, args);
    475 		}
    476 		// cast common cases to const char*
    477 		// if (gcc_jit_rvalue_get_type(arg) != type_cstr) arg =
    478 		// gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node),
    479 		// arg, type_cstr);
    480 
    481 		// gcc_jit_rvalue* args[] = { arg };
    482 		// return gcc_jit_context_new_call(
    483 		// 	gen->ctx, loc_from_node(gen, node), gen->puts_fn, 1, args);
    484 
    485 		// gcc_jit_type* type_of_args = gcc_jit_rvalue_get_type(arg);
    486 		// switch (type_of_args) {
    487 		// case GCC_JIT_TYPE_CONST_CHAR_PTR:
    488 
    489 		// 	break;
    490 		// default:
    491 		// 	printf("lower_builtin_print: unhandled type passed %s\n",
    492 		// type_of_args); 	break;
    493 		// }
    494 	}
    495 
    496 	return NULL;
    497 
    498 	// softpanic("we don't currently handle formatted strings to print");
    499 
    500 	// n>=1, treat as printf(fmt, ...) // Part of TODO about args as list
    501 	// and not
    502 	//
    503 	// through each args, form the ("formatted %s string %d etc.", str,
    504 	// intv) for clib's printf
    505 
    506 	// TODO we're talking about formatting here, which we plan on doing as a
    507 	// string interpolation, something along the lines of {{variable}}
    508 	// without defining its type would involve lookup split of the string
    509 	// and then formatting
    510 
    511 	// we need to discuss and decide what we'd do when the user inevitably
    512 	// would print out a ref to a struct. Do we say [[struct]] or do we have
    513 	// some automatic unwrap and display of struct data... probably, yes.
    514 
    515 	gcc_jit_rvalue** args = (gcc_jit_rvalue**)calloc(MAXARGS, sizeof(gcc_jit_rvalue*));
    516 
    517 	if (argc > MAXARGS) {
    518 		softpanic("we do not currently support more than 16 args to a "
    519 			  "print call");
    520 	}
    521 
    522 	for (size_t i = 0; i < argc; i++) {
    523 		gcc_jit_rvalue* arg = handle_expr(gen, node->data.call_expr.args[i]);
    524 		if (i == 0) {
    525 			if (gcc_jit_rvalue_get_type(arg) != type_cstr) {
    526 				// note this is probably not going to work as
    527 				// limited cast supported and string isn't one
    528 				// of them
    529 				arg = gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), arg, type_cstr);
    530 			}
    531 		} else {
    532 			//
    533 			// simple widening for common scalar types
    534 			//
    535 			gcc_jit_type* ty = gcc_jit_rvalue_get_type(arg);
    536 			if (ty == type_int) {
    537 				arg = gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), arg, type_cstr);
    538 			} else if (ty == type_double) {
    539 				// variadics already promote float→double;
    540 				// double is
    541 			} else if (ty == type_cstr) {
    542 				// leave as const char*
    543 			} else {
    544 				// fallback: pass pointer as void*
    545 				arg = gcc_jit_context_new_cast(gen->ctx,
    546 					loc_from_node(gen, node),
    547 					arg,
    548 					gcc_jit_context_get_type(gen->ctx, GCC_JIT_TYPE_VOID_PTR));
    549 			}
    550 		}
    551 		// TODO auto grow
    552 		args[i] = arg;
    553 	}
    554 	return gcc_jit_context_new_call(gen->ctx, NULL, gen->printf_fn, argc, args);
    555 	return NULL;
    556 }
    557 
    558 static gcc_jit_function*
    559 lookup_function(Gen* gen, Scope* scope, const char* func_name)
    560 {
    561 	for (size_t i = 0; i < scope->len; i++) {
    562 		Symbol* sym = scope->symbols[i];
    563 
    564 		if (sym->ctype == type_voidp) {
    565 			const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 });
    566 			if (strcmp(name, func_name) == 0) {
    567 				return sym->d.funcvalue; //
    568 			}
    569 		}
    570 	}
    571 
    572 	printf("lookup_function: not found locally: %s\n", func_name);
    573 
    574 	if (scope->parent != NULL) {
    575 		printf("looking up function %s in parent\n", func_name);
    576 		return lookup_function(gen, scope->parent, func_name);
    577 	}
    578 
    579 	return NULL;
    580 }
    581 
    582 static gcc_jit_rvalue*
    583 handle_func_call(Gen* gen, Node* node)
    584 {
    585 	Node* fcallee = node->data.call_expr.callee;
    586 	const char* func_name = span_str(gen->src, fcallee->data.ident.name, (char[IDENTSZ]) { 0 });
    587 
    588 	// short circuit to print
    589 	if (strcmp(func_name, "print") == 0) return lower_builtin_print(gen, node);
    590 
    591 	gcc_jit_function* callee = lookup_function(gen, gen->scope, func_name);
    592 	gcc_jit_location* loc = loc_from_node(gen, node);
    593 
    594 	// args handling
    595 	size_t argc = node->data.call_expr.len;
    596 
    597 	// alloc args
    598 	gcc_jit_rvalue** args = NULL;
    599 
    600 	if (argc > 0) {
    601 		args = calloc(argc, sizeof *args); // or alloca, or a fixed upper bound
    602 		for (size_t i = 0; i < argc; ++i) {
    603 			args[i] = handle_expr(gen, node->data.call_expr.args[i]);
    604 		}
    605 	}
    606 
    607 	for (size_t i = 0; i < argc; i++) {
    608 		args[i] = handle_expr(gen, node->data.call_expr.args[i]);
    609 	}
    610 
    611 	/*
    612 	        When generating the identifier expression message in the AST:
    613 	        Lookup env["message"] -> you get a gcc_jit_param* or gcc_jit_lvalue*.
    614 	        Use gcc_jit_param_as_rvalue / gcc_jit_lvalue_as_rvalue.
    615 
    616 	        gcc_jit_rvalue *msg = gcc_jit_param_as_rvalue(callee_param);
    617 
    618 	        gcc_jit_rvalue *args[1] = { msg };
    619 	        gcc_jit_block_add_eval(
    620 	            b, loc,
    621 	            gcc_jit_context_new_call(ctx, loc, print_fn, 1, args));
    622 	*/
    623 
    624 	// gcc_jit_rvalue *msg = gcc_jit_param_as_rvalue(callee_param);
    625 	// gcc_jit_rvalue *args[1] = { msg };
    626 	assert(callee != NULL && "callee not found");
    627 	return gcc_jit_context_new_call(gen->ctx, loc, callee, argc, args);
    628 
    629 	// TODO consider
    630 	/*
    631 	// calling bob() directly
    632 	   void emit_direct_call(gcc_jit_context *ctxt, SymbolId bob, gcc_jit_function *caller) {
    633 	                 GccObj *cal = ensure_func(ctxt, bob);
    634 	                 gcc_jit_block *b = gcc_jit_function_new_block(caller, "call_bob");
    635 	                 gcc_jit_rvalue *args[] = {};
    636 	                 gcc_jit_block_add_eval(b, NULL, gcc_jit_context_new_call(ctxt, cal->func, 0, args));
    637 	                 gcc_jit_block_end_with_void_return(b, NULL);
    638 	   }
    639 	*/
    640 
    641 	// return gcc_jit_context_new_call(gen->ctx, NULL, callee, 0, NULL);
    642 
    643 	// TODO handle return values
    644 	// almost unrelated could be useful to deal with return values:
    645 	// gcc_jit_rvalue* rv = handle_func_call(gen, node);
    646 	// if (rv) { gcc_jit_block_add_eval(gen->curr_block, loc_from_node(gen, node), rv); }
    647 	// return false;
    648 	return NULL;
    649 }
    650 
    651 static gcc_jit_rvalue*
    652 handle_expr(Gen* gen, Node* node)
    653 {
    654 	switch (node->type) {
    655 	case NODE_INT_LITERAL:
    656 		return emit_literal_int(gen, node);
    657 		break;
    658 	case NODE_FLOAT_LITERAL:
    659 		return emit_literal_float(gen, node);
    660 		break;
    661 	case NODE_STRING_LITERAL:
    662 		return emit_literal_string(gen, node);
    663 		break;
    664 	case NODE_CALL_EXPR:
    665 		return handle_func_call(gen, node);
    666 		break;
    667 	case NODE_IDENT:
    668 		return handle_ident_call(gen, node);
    669 		break;
    670 	case NODE_UNARY_EXPR: // ++ etc.
    671 		return handle_unary_expr(gen, node);
    672 		break;
    673 	case NODE_BINARY_EXPR:
    674 		return handle_binary_expr(gen, node);
    675 		break;
    676 
    677 	// case NODE_IDENT: {
    678 	// 	return NULL; // fixme
    679 	//      break;
    680 	// }
    681 	default:
    682 		printf("handle_expr unhandled, %s\n", node_type_str(node->type));
    683 	}
    684 	return NULL;
    685 }
    686 
    687 static gcc_jit_type*
    688 ox_type_to_c_type(Gen* gen, Node* node)
    689 {
    690 	const char* type_name = span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 });
    691 
    692 	if (strcmp(type_name, "int") == 0) {
    693 		return type_int;
    694 	} else if (strcmp(type_name, "string") == 0) {
    695 		return type_cstr;
    696 	} else if (strcmp(type_name, "float") == 0) {
    697 		return type_double;
    698 	} else if (strcmp(type_name, "uint") == 0) {
    699 		return type_uint;
    700 	} else if (strcmp(type_name, "void") == 0) {
    701 		return type_void;
    702 	} else {
    703 		softpanic("unhandled type in gen %s", type_name);
    704 	}
    705 	return NULL;
    706 }
    707 
    708 static void
    709 print_symbols_here(Gen* gen)
    710 {
    711 	for (size_t i = 0; i < gen->scope->len; i++) {
    712 		Symbol* sym = gen->scope->symbols[i];
    713 		const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 });
    714 		printf("[%zu/%zu] symbol: %s (%s)\n", i + 1, gen->scope->len, name, sym->english_type);
    715 	}
    716 }
    717 
    718 static bool build_block(Gen*, Node*);
    719 static bool build_statement(Gen*, Node*);
    720 
    721 static gcc_jit_rvalue* build_bool_value(Gen*, Node*);
    722 
    723 static int block_counter = 0, loop_counter = 0;
    724 
    725 static bool
    726 build_for_statement(Gen* gen, Node* node)
    727 {
    728 	gcc_jit_location* loc = loc_from_node(gen, node);
    729 
    730 	Node* init = node->data.for_statement.init;
    731 	Node* cond = node->data.for_statement.cond;
    732 	Node* step = node->data.for_statement.increment;
    733 	Node* body = node->data.for_statement.body;
    734 
    735 	loop_counter++;
    736 
    737 	char label_cond[64], label_body[64], label_step[64], label_end[64];
    738 	snprintf(label_cond, 64, "for.cond%d", loop_counter);
    739 	snprintf(label_body, 64, "for.body%d", loop_counter);
    740 	snprintf(label_step, 64, "for.step%d", loop_counter);
    741 	snprintf(label_end, 64, "for.end%d", loop_counter);
    742 
    743 	gcc_jit_block* cond_block = gcc_jit_function_new_block(gen->curr_func, label_cond);
    744 	gcc_jit_block* body_block = gcc_jit_function_new_block(gen->curr_func, label_body);
    745 	gcc_jit_block* step_block = gcc_jit_function_new_block(gen->curr_func, label_step);
    746 	gcc_jit_block* end_block = gcc_jit_function_new_block(gen->curr_func, label_end);
    747 
    748 	// gcc_jit_block* saved_break = gen->loop->break_target;
    749 	// gcc_jit_block* saved_cont = gen->loop->continue_target;
    750 	// gen->loop->break_target = end_block;
    751 	// gen->loop->continue_target = step_block;
    752 
    753 	// header, e.g. for(int = 0 <-
    754 	if (init) { build_statement(gen, init); }
    755 
    756 	// jump to cond e.g. ; i < 5 <-
    757 	gcc_jit_block_end_with_jump(gen->curr_block, loc, cond_block);
    758 
    759 	// cond: evaluate
    760 	gen->curr_block = cond_block;
    761 	gcc_jit_rvalue* cnd = NULL;
    762 
    763 	if (cond != NULL) {
    764 		cnd = build_bool_value(gen, cond);
    765 		gcc_jit_block_end_with_conditional(cond_block, loc, cnd, body_block, end_block);
    766 	} else {
    767 		gcc_jit_block_end_with_jump(cond_block, loc, body_block); // presume for(;;)
    768 	}
    769 
    770 	push_loop(gen, /*break to*/ end_block, /*continue to*/ step_block);
    771 
    772 	// build for body
    773 
    774 	gen->curr_block = body_block;
    775 	bool for_body_ended = build_block(gen, body);
    776 	if (!for_body_ended) { gcc_jit_block_end_with_jump(gen->curr_block, loc, step_block); }
    777 
    778 	pop_loop(gen);
    779 
    780 	// step incr etc.
    781 
    782 	gen->curr_block = step_block;
    783 	if (step) { build_statement(gen, step); }
    784 	gcc_jit_block_end_with_jump(step_block, loc, cond != NULL ? cond_block : body_block);
    785 
    786 	// resume after loop
    787 
    788 	gen->curr_block = end_block;
    789 	return false;
    790 }
    791 
    792 static bool
    793 build_if_statement(Gen* gen, Node* node)
    794 {
    795 	gcc_jit_location* loc = loc_from_node(gen, node);
    796 	// build the condition
    797 
    798 	OpType op = node->data.if_statement.cond->data.binary_expr.op;
    799 	Node* lhs = node->data.if_statement.cond->data.binary_expr.lhs;
    800 	Node* rhs = node->data.if_statement.cond->data.binary_expr.rhs;
    801 
    802 	Node* then_body = node->data.if_statement.then_body;
    803 	Node* else_body = node->data.if_statement.else_body;
    804 
    805 	enum gcc_jit_comparison cmp;
    806 	switch (op) {
    807 	case OP_EQUALITY:
    808 		cmp = GCC_JIT_COMPARISON_EQ;
    809 		break;
    810 	case OP_INEQUALITY:
    811 		cmp = GCC_JIT_COMPARISON_NE;
    812 		break;
    813 	case OP_GT:
    814 		cmp = GCC_JIT_COMPARISON_GT;
    815 		break;
    816 	case OP_GT_EQ:
    817 		cmp = GCC_JIT_COMPARISON_GE;
    818 		break;
    819 	case OP_LT_EQ:
    820 		cmp = GCC_JIT_COMPARISON_LE;
    821 		break;
    822 	case OP_LT:
    823 		cmp = GCC_JIT_COMPARISON_LT;
    824 		break;
    825 	default:
    826 		printf("/!\\ build_statement NODE_IF unhandled, %d\n", op);
    827 		cmp = GCC_JIT_COMPARISON_NE;
    828 	}
    829 
    830 	gcc_jit_rvalue* lhs_val = handle_expr(gen, lhs);
    831 	gcc_jit_rvalue* rhs_val = handle_expr(gen, rhs);
    832 
    833 	gcc_jit_rvalue* cond = gcc_jit_context_new_comparison(gen->ctx, loc, cmp, lhs_val, rhs_val);
    834 
    835 	// create the BLOCKS
    836 
    837 	// labels
    838 	block_counter++;
    839 	char label_then[64], label_else[64], label_end[64];
    840 	snprintf(label_then, 64, "if.then%d", block_counter);
    841 	snprintf(label_else, 64, "if.else%d", block_counter);
    842 	snprintf(label_end, 64, "if.end%d", block_counter);
    843 
    844 	// blocks
    845 	gcc_jit_block* then_bb = gcc_jit_function_new_block(gen->curr_func, label_then);
    846 	gcc_jit_block* else_bb = else_body ? gcc_jit_function_new_block(gen->curr_func, label_else) : NULL;
    847 	gcc_jit_block* merge_bb = NULL;
    848 
    849 	if (!else_bb) {
    850 		// no else: need merge now for the false edge
    851 		merge_bb = gcc_jit_function_new_block(gen->curr_func, label_end);
    852 		gcc_jit_block_end_with_conditional(gen->curr_block, loc, cond, then_bb, merge_bb);
    853 	} else {
    854 		// with else: branch to then/else; decide on merge later
    855 		gcc_jit_block_end_with_conditional(gen->curr_block, loc, cond, then_bb, else_bb);
    856 	}
    857 
    858 	// THEN
    859 	gen->curr_block = then_bb;
    860 	bool then_ended = build_block(gen, then_body);
    861 	gcc_jit_block* then_open = gen->curr_block; // last open block in THEN
    862 	                                            // (may differ from then_bb)
    863 
    864 	// ELSE
    865 	bool else_ended = false;
    866 	gcc_jit_block* else_open = NULL;
    867 	if (else_bb) {
    868 		gen->curr_block = else_bb;
    869 		else_ended = build_block(gen, else_body);
    870 		else_open = gen->curr_block; // last open block in ELSE
    871 	}
    872 
    873 	// If both branches ended, no merge needed, we notify we have ended as
    874 	// well
    875 	if (else_bb && then_ended && else_ended) return true;
    876 
    877 	// Ensure we have a merge if any branch continues.
    878 	if (!merge_bb) merge_bb = gcc_jit_function_new_block(gen->curr_func, label_end);
    879 
    880 	if (!then_ended) gcc_jit_block_end_with_jump(then_open, loc, merge_bb);
    881 	if (else_bb && !else_ended) gcc_jit_block_end_with_jump(else_open, loc, merge_bb);
    882 
    883 	gen->curr_block = merge_bb;
    884 	return false;
    885 }
    886 
    887 static bool
    888 build_var_decl_statement(Gen* gen, Node* node)
    889 {
    890 	gcc_jit_location* loc = loc_from_node(gen, node);
    891 	const char* var_name = span_str(gen->src, node->data.var_decl.name, (char[IDENTSZ]) { 0 });
    892 	gcc_jit_type* declared_type = ox_type_to_c_type(gen, node->data.var_decl.type);
    893 
    894 	gcc_jit_lvalue* var_decl = NULL;
    895 
    896 	gcc_jit_rvalue* rvalue = NULL;
    897 	Node* init = node->data.var_decl.init;
    898 	if (init != NULL) { rvalue = handle_expr(gen, init); }
    899 
    900 	if (gen->curr_func == NULL && gen->curr_block == NULL) {
    901 		// global var
    902 		var_decl = gcc_jit_context_new_global(gen->ctx, loc, GCC_JIT_GLOBAL_INTERNAL, declared_type, strdup(var_name));
    903 		if (rvalue) gcc_jit_global_set_initializer_rvalue(var_decl, rvalue);
    904 	} else {
    905 		// local var
    906 		var_decl = gcc_jit_function_new_local(gen->curr_func, loc, declared_type, strdup(var_name));
    907 		if (rvalue) gcc_jit_block_add_assignment(gen->curr_block, loc, var_decl, rvalue);
    908 	}
    909 
    910 	// this is not an error, there is just no initial value
    911 	// if (node->data.var_decl.init == NULL) { panic("could not instanciate gcc jit new local"); }
    912 
    913 	printf("adding 1 symbol: %s\n", var_name);
    914 
    915 	Symbol* sym = (Symbol*)calloc(1, sizeof(Symbol));
    916 	sym->name = node->data.var_decl.name;
    917 	sym->decl = node;
    918 	sym->ctype = declared_type;
    919 	sym->d.lvalue = var_decl;
    920 	sym->english_type = type_var;
    921 	add_symbol(gen, sym);
    922 	return false;
    923 }
    924 
    925 static void build_func_decl(Gen*, Node*);
    926 
    927 static bool
    928 build_statement(Gen* gen, Node* node)
    929 {
    930 	gcc_jit_location* loc = loc_from_node(gen, node);
    931 	switch (node->type) {
    932 	case NODE_RETURN: {
    933 		Node* return_expr = node->data.ret.expr;
    934 		if (return_expr) {
    935 			gcc_jit_rvalue* rv = handle_expr(gen, return_expr);
    936 			gcc_jit_block_end_with_return(gen->curr_block, loc, rv);
    937 		} else {
    938 			gcc_jit_block_end_with_void_return(gen->curr_block, loc);
    939 		}
    940 		gen->curr_block = NULL; // important
    941 		return true;            // we end the block here
    942 	}
    943 	case NODE_BREAK: {
    944 		gcc_jit_block* t = current_break(gen);
    945 		if (!t) return true; // break outside the loop
    946 		gcc_jit_block_end_with_jump(gen->curr_block, loc, t);
    947 		gen->curr_block = NULL;
    948 		return true;
    949 	}
    950 	case NODE_CONTINUE: {
    951 		gcc_jit_block* t = current_continue(gen);
    952 		if (!t) return true; // continue outside of a loop
    953 		gcc_jit_block_end_with_jump(gen->curr_block, loc, t);
    954 		gen->curr_block = NULL;
    955 		return true;
    956 	}
    957 	case NODE_VAR_DECL: {
    958 		return build_var_decl_statement(gen, node);
    959 	}
    960 	case NODE_VAR_ASSIGN: {
    961 		gcc_jit_rvalue* rvalue = handle_expr(gen, node->data.var_assign.rhs);
    962 		// find the var_decl (d.lvalue) from the symbols table's symbol.
    963 		for (size_t i = 0; i < gen->scope->len; i++) {
    964 			Symbol* sym = gen->scope->symbols[i];
    965 			if (span_ident_same(sym->name, node->data.var_assign.lhs->data.ident.name, gen->src)) {
    966 				// we found the symbol we are assigning to
    967 				gcc_jit_lvalue* lvalue = sym->d.lvalue;
    968 
    969 				// check the type of lvalue matches the rvalue
    970 				// gcc_jit_type* ltype = sym->ctype;
    971 				// gcc_jit_type* rtype = gcc_jit_rvalue_get_type(rvalue);
    972 				// if (rtype != ltype) {
    973 				// 	panic_at(node,
    974 				// 		"right hand side of assigment "
    975 				// 		"doesn't match the "
    976 				// 		"type of the left hand side: %s, %s",
    977 				// 		get_english_type(ltype),
    978 				// 		get_english_type(rtype));
    979 				// }
    980 				if (lvalue) gcc_jit_block_add_assignment(gen->curr_block, loc, lvalue, rvalue);
    981 			}
    982 		}
    983 		break;
    984 	}
    985 	case NODE_BINARY_EXPR: {
    986 		Symbol* sym = find_symbol(gen, gen->scope, node->data.binary_expr.lhs->data.ident.name);
    987 		if (!sym) softpanic("undefined variable: %s\n", span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 }));
    988 
    989 		gcc_jit_lvalue* lv = sym->d.lvalue;
    990 		gcc_jit_rvalue* rhs = handle_expr(gen, node->data.binary_expr.rhs);
    991 		gcc_jit_rvalue* lhs = handle_expr(gen, node->data.binary_expr.lhs);
    992 		gcc_jit_rvalue* temp = gcc_jit_lvalue_as_rvalue(lv);
    993 		gcc_jit_type* lvtype = gcc_jit_rvalue_get_type(temp);
    994 		gcc_jit_rvalue* sum
    995 			= gcc_jit_context_new_binary_op(gen->ctx, loc_from_node(gen, node), GCC_JIT_BINARY_OP_PLUS, lvtype, lhs, rhs);
    996 
    997 		gcc_jit_block_add_assignment(gen->curr_block, loc_from_node(gen, node), lv, sum);
    998 	}
    999 	case NODE_UNARY_EXPR: {
   1000 		gcc_jit_rvalue* rv = handle_unary_expr(gen, node);
   1001 		if (rv) gcc_jit_block_add_eval(gen->curr_block, loc_from_node(gen, node), rv);
   1002 		break;
   1003 	}
   1004 	case NODE_EXPR_STATEMENT: {
   1005 		gcc_jit_rvalue* rv = handle_expr(gen, node->data.expr_statement.expr);
   1006 		if (rv) gcc_jit_block_add_eval(gen->curr_block, loc_from_node(gen, node), rv);
   1007 		break;
   1008 	}
   1009 	case NODE_IF:
   1010 		return build_if_statement(gen, node);
   1011 	case NODE_FOR:
   1012 		return build_for_statement(gen, node);
   1013 	case NODE_FUNCTION_DECL:
   1014 		// "closure"
   1015 		build_func_decl(gen, node);
   1016 		return false;
   1017 	default:
   1018 		printf("build_statement unhandled, %s\n", node_type_str(node->type));
   1019 		break;
   1020 	}
   1021 	return false;
   1022 }
   1023 
   1024 static gcc_jit_rvalue*
   1025 build_bool_value(Gen* gen, Node* node)
   1026 {
   1027 	gcc_jit_location* loc = loc_from_node(gen, node);
   1028 
   1029 	switch (node->type) {
   1030 	case NODE_BINARY_EXPR: {
   1031 		gcc_jit_rvalue* lvalue = handle_expr(gen, node->data.binary_expr.lhs);
   1032 		gcc_jit_rvalue* rvalue = handle_expr(gen, node->data.binary_expr.rhs);
   1033 		enum gcc_jit_comparison op;
   1034 		switch (node->data.binary_expr.op) {
   1035 		case OP_LT:
   1036 			op = GCC_JIT_COMPARISON_LT;
   1037 			break;
   1038 		case OP_GT:
   1039 			op = GCC_JIT_COMPARISON_GT;
   1040 			break;
   1041 		case OP_LT_EQ:
   1042 			op = GCC_JIT_COMPARISON_LE;
   1043 			break;
   1044 		case OP_GT_EQ:
   1045 			op = GCC_JIT_COMPARISON_GE;
   1046 			break;
   1047 		default:
   1048 			printf("build_bool_rvalue nodebinary op unhandled, "
   1049 			       "%s\n",
   1050 				node_type_str(node->type));
   1051 			return NULL;
   1052 		}
   1053 		return gcc_jit_context_new_comparison(gen->ctx, loc, op, lvalue, rvalue);
   1054 		break;
   1055 	}
   1056 	default:
   1057 		printf("build_bool_rvalue unhandled, %s\n", node_type_str(node->type));
   1058 		break;
   1059 	}
   1060 
   1061 	return NULL;
   1062 }
   1063 
   1064 // build_block: returns true if block ended with a terminator
   1065 static bool
   1066 build_block(Gen* gen, Node* body)
   1067 {
   1068 	// TODO if there is no block ({ ... }), we presume there is only 1 statement.
   1069 	// This might not always be valid, func declaration do require a block {}, but there might be cases...
   1070 	if (body->data.block.len == 0) { return build_statement(gen, body); }
   1071 
   1072 	for (size_t i = 0; i < body->data.block.len; i++) {
   1073 		if (gen->curr_block == NULL) return true;
   1074 		bool ended = build_statement(gen, body->data.block.stmts[i]);
   1075 		if (ended) return true;
   1076 	}
   1077 	return false;
   1078 }
   1079 
   1080 static void
   1081 build_func_decl(Gen* gen, Node* node)
   1082 {
   1083 	const char* func_name = span_str(gen->src, node->data.function_decl.name, (char[IDENTSZ]) { 0 });
   1084 
   1085 	Node* return_type = node->data.function_decl.return_type;
   1086 	gcc_jit_type* ret_type = ox_type_to_c_type(gen, return_type);
   1087 	gcc_jit_location* loc = loc_from_node(gen, node);
   1088 
   1089 	size_t argc = node->data.function_decl.p_len;
   1090 
   1091 	gcc_jit_param** params = calloc(argc, sizeof(gcc_jit_param*));
   1092 
   1093 	// 1) Create ONE gcc_jit_param per AST param
   1094 	for (size_t i = 0; i < argc; i++) {
   1095 		Node* pnode = node->data.function_decl.params[i];
   1096 		const char* pname = span_str(gen->src, pnode->data.param.name, (char[IDENTSZ]) { 0 });
   1097 
   1098 		gcc_jit_type* declared_type = ox_type_to_c_type(gen, pnode->data.param.type);
   1099 
   1100 		gcc_jit_param* p = gcc_jit_context_new_param(gen->ctx, loc, declared_type, strdup(pname));
   1101 
   1102 		params[i] = p;
   1103 	}
   1104 
   1105 	// 2) Create the function with those params
   1106 	gcc_jit_function* func = gcc_jit_context_new_function(gen->ctx,
   1107 		loc,
   1108 		GCC_JIT_FUNCTION_EXPORTED,
   1109 		ret_type,
   1110 		strdup(func_name),
   1111 		argc,
   1112 		params, // <-- these are now owned by 'func'
   1113 		0);
   1114 
   1115 	gcc_jit_block* block = gcc_jit_function_new_block(func, "entry");
   1116 
   1117 	gcc_jit_function* prev_func = gen->curr_func;
   1118 	gcc_jit_block* prev_block = gen->curr_block;
   1119 	gen->curr_func = func;
   1120 	gen->curr_block = block;
   1121 
   1122 	// 3) Add the function symbol to the current (enclosing) scope
   1123 	Symbol* sym = (Symbol*)calloc(1, sizeof(Symbol));
   1124 	sym->name = node->data.function_decl.name;
   1125 	sym->decl = node;
   1126 	sym->ctype = type_voidp; // whatever you use for function type metadata
   1127 	sym->d.funcvalue = func;
   1128 	sym->english_type = type_func;
   1129 	add_symbol(gen, sym); // to the 'parent scope', before we push_scope
   1130 
   1131 	// ENTER FUNCTION SCOPE
   1132 	push_scope(gen);
   1133 
   1134 	// 4) Add parameter symbols to the FUNCTION scope, reusing the same gcc_jit_param*
   1135 	for (size_t i = 0; i < argc; i++) {
   1136 		Node* pnode = node->data.function_decl.params[i];
   1137 
   1138 		gcc_jit_param* p = params[i]; // <-- reuse, DO NOT new_param again
   1139 
   1140 		gcc_jit_type* declared_type = ox_type_to_c_type(gen, pnode->data.param.type);
   1141 
   1142 		Symbol* ps = (Symbol*)calloc(1, sizeof(Symbol));
   1143 		ps->name = pnode->data.param.name; // param identifier span
   1144 		ps->decl = pnode;
   1145 		ps->ctype = declared_type;
   1146 		ps->d.param = p;             // <-- this is associated with 'func'
   1147 		ps->english_type = type_var; // or type_param if you distinguish
   1148 		add_symbol(gen, ps);
   1149 	}
   1150 
   1151 	print_symbols_here(gen);
   1152 
   1153 	build_block(gen, node->data.function_decl.body);
   1154 
   1155 	if (gen->curr_block == NULL) {
   1156 		// no open block: all paths already terminated, don't add an implicit return
   1157 	} else if (ret_type == type_int) {
   1158 		gcc_jit_rvalue* ret_value = gcc_jit_context_new_rvalue_from_int(gen->ctx, type_int, 0);
   1159 		gcc_jit_block_end_with_return(gen->curr_block, loc, ret_value);
   1160 	} else if (ret_type == type_void) {
   1161 		gcc_jit_block_end_with_void_return(gen->curr_block, loc);
   1162 	} else {
   1163 		printf("build_func_decl unhandled return type in func: %s - "
   1164 		       "defaulting to void\n",
   1165 			func_name);
   1166 		gcc_jit_block_end_with_void_return(gen->curr_block, loc);
   1167 	}
   1168 
   1169 	// LEAVE FUNCTION SCOPE
   1170 	pop_scope(gen);
   1171 
   1172 	gen->curr_func = prev_func;
   1173 	gen->curr_block = prev_block;
   1174 }
   1175 
   1176 void
   1177 gen_next(Gen* gen, Node* node)
   1178 {
   1179 	// printf("gen_next, %s\n", node_type_str(node->type));
   1180 
   1181 	switch (node->type) {
   1182 	case NODE_PROGRAM:
   1183 		build_program(gen, node);
   1184 		break;
   1185 	case NODE_FUNCTION_DECL:
   1186 		build_func_decl(gen, node);
   1187 		break;
   1188 	case NODE_STRING_LITERAL:
   1189 		emit_literal_string(gen, node);
   1190 		break;
   1191 	case NODE_INT_LITERAL:
   1192 		emit_literal_int(gen, node);
   1193 		break;
   1194 	case NODE_FLOAT_LITERAL:
   1195 		emit_literal_float(gen, node);
   1196 		break;
   1197 	case NODE_VAR_DECL:
   1198 		build_statement(gen, node);
   1199 		break;
   1200 	default:
   1201 		printf("gen: unhandled, %s\n", node_type_str(node->type));
   1202 	}
   1203 }