ox

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

gen.c (10519B)


      1 #include "../gen.h"
      2 #include "../utils.h"
      3 
      4 #include <stdint.h>
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 #include <sys/param.h>
      9 
     10 static gcc_jit_type *type_int;
     11 static gcc_jit_type *type_uint;
     12 static gcc_jit_type *type_float;
     13 static gcc_jit_type *type_void;
     14 static gcc_jit_type *type_cstr;
     15 
     16 #define MAXARGS 16
     17 
     18 gcc_jit_location *
     19 loc_from_node(Gen *gen, Node *node)
     20 {
     21 	if (node->filename == NULL) return NULL;
     22 	return gcc_jit_context_new_location(gen->ctx, node->filename, node->line, node->col);
     23 }
     24 
     25 Gen
     26 gen_init(Scope *scope, const char *src)
     27 {
     28 	if (scope == NULL || src == NULL) { panic("gen_init: no Scope or AST provided"); }
     29 
     30 	gcc_jit_context *ctx;
     31 
     32 	ctx = gcc_jit_context_acquire();
     33 
     34 	if (!ctx) { panic("could not acquire gcc jit context"); }
     35 
     36 	// needs loc* to work
     37 	// gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DEBUGINFO, 1);
     38 	// high level
     39 	// gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE,
     40 	// 1); low level
     41 	gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, 1);
     42 	// gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DUMP_SUMMARY, 1);
     43 
     44 	gcc_jit_context_set_str_option(ctx, GCC_JIT_STR_OPTION_PROGNAME, "ox");
     45 	// keep FP
     46 	gcc_jit_context_add_driver_option(ctx, "-fno-omit-frame-pointer");
     47 
     48 	gcc_jit_context_set_int_option(ctx,
     49 		GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL,
     50 		/*0-3 for O3*/ 0);
     51 
     52 	type_int = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT64_T);
     53 	type_uint = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT64_T);
     54 	type_float = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_DOUBLE);
     55 	type_void = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_VOID);
     56 	type_cstr = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_CONST_CHAR_PTR);
     57 
     58 	gcc_jit_param *pm_puts[] = { gcc_jit_context_new_param(ctx, NULL, type_cstr, "s") };
     59 	gcc_jit_function *fn_puts = gcc_jit_context_new_function(ctx, NULL, GCC_JIT_FUNCTION_IMPORTED, type_int, "puts", 1, pm_puts, 0);
     60 
     61 	gcc_jit_param *pm_printf[] = { gcc_jit_context_new_param(ctx, NULL, type_cstr, "fmt") };
     62 	gcc_jit_function *fn_printf = gcc_jit_context_new_function(ctx,
     63 		NULL,
     64 		GCC_JIT_FUNCTION_IMPORTED,
     65 		type_int,
     66 		"printf",
     67 		1,
     68 		pm_printf,
     69 		/*is_variadic=*/1);
     70 
     71 	return (Gen) {
     72 		.ctx = ctx,
     73 		.scope = scope,
     74 		.prev_func = NULL,
     75 		.curr_func = NULL,
     76 		.prev_block = NULL,
     77 		.curr_block = NULL,
     78 		.puts_fn = fn_puts,
     79 		.printf_fn = fn_printf,
     80 		.src = src,
     81 	};
     82 }
     83 
     84 static gcc_jit_rvalue *handle_expr(Gen *, Node *);
     85 
     86 static gcc_jit_rvalue *
     87 emit_literal_string(Gen *gen, Node *node)
     88 {
     89 	size_t len = node->data.string.value.end - node->data.string.value.start;
     90 	char *str = calloc(len + 1, sizeof(char));
     91 	if (str == NULL) panic("emit_literal_string: could not alloc");
     92 	memcpy(str, gen->src + node->data.string.value.start, len);
     93 	str[len] = '\0';
     94 	return gcc_jit_context_new_string_literal(gen->ctx, str);
     95 }
     96 
     97 static gcc_jit_rvalue *
     98 emit_literal_int(Gen *gen, Node *node)
     99 {
    100 	return gcc_jit_context_new_rvalue_from_int(gen->ctx, type_int, (int)node->data.number.value);
    101 }
    102 
    103 static void
    104 build_program(Gen *gen, Node *node)
    105 {
    106 	size_t cnt = node->data.program.len;
    107 	for (size_t i = 0; i < cnt; i++) {
    108 		gen_next(gen, node->data.program.decl[i]);
    109 	}
    110 }
    111 
    112 static gcc_jit_rvalue *
    113 lower_builtin_print(Gen *gen, Node *node)
    114 {
    115 	size_t argc = node->data.call_expr.len;
    116 
    117 	// 1-arg, treat as puts(arg)
    118 	if (argc == 1) {
    119 		gcc_jit_rvalue *arg = handle_expr(gen, node->data.call_expr.args[0]); // TODO [0] when many
    120 		// cast common cases to const char*
    121 		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);
    122 		gcc_jit_rvalue *args[] = { arg };
    123 		return gcc_jit_context_new_call(gen->ctx, loc_from_node(gen, node), gen->puts_fn, 1, args);
    124 	}
    125 
    126 	// softpanic("we don't currently handle formatted strings to print");
    127 
    128 	// n>=1, treat as printf(fmt, ...) // Part of TODO about args as list and not
    129 	//
    130 	// through each args, form the ("formatted %s string %d etc.", str, intv) for clib's printf
    131 
    132 	// TODO we're talking about formatting here, which we plan on doing as a string
    133 	// interpolation, something along the lines of {{variable}} without defining its type would
    134 	// involve lookup split of the string and then formatting
    135 
    136 	// we need to discuss and decide what we'd do when the user inevitably would print out a ref
    137 	// to a struct. Do we say [[struct]] or do we have some automatic unwrap and display of
    138 	// struct data... probably, yes.
    139 
    140 	gcc_jit_rvalue **args = (gcc_jit_rvalue **)calloc(MAXARGS, sizeof(gcc_jit_rvalue *));
    141 
    142 	if (argc > MAXARGS) { softpanic("we do not currently support more than 16 args to a print call"); }
    143 
    144 	for (size_t i = 0; i < argc; i++) {
    145 		gcc_jit_rvalue *arg = handle_expr(gen, node->data.call_expr.args[i]);
    146 		if (i == 0) {
    147 			if (gcc_jit_rvalue_get_type(arg) != type_cstr) {
    148 				// note this is probably not going to work as limited cast supported
    149 				// and string isn't one of them
    150 				arg = gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), arg, type_cstr);
    151 			}
    152 		} else {
    153 			//
    154 			// simple widening for common scalar types
    155 			//
    156 			gcc_jit_type *ty = gcc_jit_rvalue_get_type(arg);
    157 			if (ty == type_int) {
    158 				arg = gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), arg, type_cstr);
    159 			} else if (ty == type_float) {
    160 				// variadics already promote float→double; double is
    161 			} else if (ty == type_cstr) {
    162 				// leave as const char*
    163 			} else {
    164 				// fallback: pass pointer as void*
    165 				arg = gcc_jit_context_new_cast(
    166 					gen->ctx, loc_from_node(gen, node), arg, gcc_jit_context_get_type(gen->ctx, GCC_JIT_TYPE_VOID_PTR));
    167 			}
    168 		}
    169 		// TODO auto grow
    170 		args[i] = arg;
    171 	}
    172 	return gcc_jit_context_new_call(gen->ctx, NULL, gen->printf_fn, argc, args);
    173 	return NULL;
    174 }
    175 
    176 // static gcc_jit_function*
    177 // lookup_function(Gen* gen, const char* func_name)
    178 // {
    179 // 	// TODO see todo below about linked list parameters...
    180 // }
    181 
    182 void
    183 lookup_symbol(Gen *gen)
    184 {
    185 	// @next
    186 }
    187 
    188 static gcc_jit_rvalue *
    189 handle_func_call(Gen *gen, Node *node)
    190 {
    191 	Node *fcallee = node->data.call_expr.callee;
    192 	const char *func_name = span_str(gen->src, fcallee->data.ident.name, (char[IDENTSZ]) { 0 });
    193 	if (strcmp(func_name, "print") == 0) return lower_builtin_print(gen, node);
    194 
    195 	softpanic("unhandled func call named: %s", func_name);
    196 	return NULL;
    197 
    198 	//
    199 	// TODO handle any function other than print...
    200 	//
    201 	// int argc = node->data.call_expr.len;
    202 	// gcc_jit_function* callee = lookup_function(gen, func_name);
    203 	// gcc_jit_rvalue* args[16]; // @future fixed at 16 parameters in call
    204 	// for (int i = 0; i < argc; i++) {
    205 	// 	args[i] = handle_expr(gen, node->data.call_expr.args[i]);
    206 	// }
    207 	// return gcc_jit_context_new_call(gen->ctx, NULL, callee, argc, args);
    208 	// return NULL;
    209 }
    210 
    211 static gcc_jit_rvalue *
    212 handle_expr(Gen *gen, Node *node)
    213 {
    214 	switch (node->type) {
    215 	case NODE_NUMBER_LITERAL:
    216 		return emit_literal_int(gen, node);
    217 		break;
    218 	case NODE_STRING_LITERAL:
    219 		return emit_literal_string(gen, node);
    220 		break;
    221 	case NODE_CALL_EXPR: {
    222 		return handle_func_call(gen, node);
    223 	} break;
    224 	// case NODE_IDENT: {
    225 	// 	return NULL; // fixme
    226 	// } break;
    227 	default:
    228 		printf("handle_expr unhandled, %s\n", node_type_str(node->type));
    229 	}
    230 	return NULL;
    231 }
    232 
    233 static gcc_jit_type *
    234 ox_type_to_c_type(Gen *gen, Node *node)
    235 {
    236 	const char *type_name = span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 });
    237 
    238 	if (strcmp(type_name, "int") == 0) {
    239 		return type_int;
    240 	} else if (strcmp(type_name, "string") == 0) {
    241 		return type_cstr;
    242 	} else {
    243 		softpanic("unhandled type in gen %s", type_name);
    244 	}
    245 	return NULL;
    246 }
    247 
    248 static void
    249 build_statement(Gen *gen, Node *node)
    250 {
    251 	switch (node->type) {
    252 	case NODE_BLOCK:
    253 		break;
    254 	case NODE_RETURN:
    255 		break;
    256 	case NODE_VAR_DECL: {
    257 		gcc_jit_location *loc = loc_from_node(gen, node);
    258 		const char *var_name = span_str(gen->src, node->data.var_decl.name, (char[IDENTSZ]) { 0 });
    259 		gcc_jit_type *declared_type = ox_type_to_c_type(gen, node->data.var_decl.type);
    260 		gcc_jit_lvalue *var_decl = gcc_jit_function_new_local(gen->curr_func, loc, declared_type, strdup(var_name));
    261 
    262 		if (node->data.var_decl.init != NULL) {
    263 			gcc_jit_rvalue *rvalue = handle_expr(gen, node->data.var_decl.init);
    264 			gcc_jit_block_add_assignment(gen->curr_block, loc, var_decl, rvalue);
    265 
    266 			printf("add the lvalue to node scope to be found later\n");
    267 
    268 			for (size_t i = 0; i < node->scope->len; i++) {
    269 				Symbol *sym = node->scope->symbols[i];
    270 				if (sym->name.start == node->data.var_decl.name.start && sym->name.end == node->data.var_decl.name.end) {
    271 					sym->ctype = declared_type;
    272 					sym->d.lvalue = var_decl;
    273 
    274 					printf("@next, when we parse the print(x) we know we can find the x in the symbols \n");
    275 
    276 					break;
    277 				}
    278 			}
    279 		}
    280 	} break;
    281 	case NODE_EXPR_STATEMENT: {
    282 		gcc_jit_rvalue *rv = handle_expr(gen, node->data.expr_statement.expr);
    283 		if (rv) gcc_jit_block_add_eval(gen->curr_block, loc_from_node(gen, node), rv);
    284 	} break;
    285 	default:
    286 		printf("build_statement unhandled, %s\n", node_type_str(node->type));
    287 		break;
    288 	}
    289 }
    290 
    291 static void
    292 build_block(Gen *gen, Node *body)
    293 {
    294 	for (size_t i = 0; i < body->data.block.len; i++) {
    295 		build_statement(gen, body->data.block.stmts[i]);
    296 	}
    297 }
    298 
    299 static void
    300 build_func_decl(Gen *gen, Node *node)
    301 {
    302 	gcc_jit_function *func = gcc_jit_context_new_function(gen->ctx,
    303 		loc_from_node(gen, node),
    304 		GCC_JIT_FUNCTION_EXPORTED, // declared
    305 		type_int,                  // ret
    306 		"main",                    // name
    307 		0,                         // num params
    308 		NULL,                      // params
    309 		0);                        // is variadic
    310 
    311 	gcc_jit_block *block = gcc_jit_function_new_block(func, "entry");
    312 
    313 	gcc_jit_function *prev_func = gen->curr_func;
    314 	gcc_jit_block *prev_block = gen->curr_block;
    315 	gen->curr_block = block;
    316 	gen->curr_func = func;
    317 
    318 	build_block(gen, node->data.function_decl.body);
    319 
    320 	if (gen->curr_block) {
    321 		gcc_jit_rvalue *ret_value = gcc_jit_context_new_rvalue_from_int(gen->ctx, type_int, 0);
    322 		gcc_jit_block_end_with_return(gen->curr_block, NULL, ret_value);
    323 		gen->curr_block = NULL;
    324 	}
    325 
    326 	gen->curr_func = prev_func;
    327 	gen->curr_block = prev_block;
    328 }
    329 
    330 void
    331 gen_next(Gen *gen, Node *node)
    332 {
    333 	// printf("gen_next, %s\n", node_type_str(node->type));
    334 
    335 	switch (node->type) {
    336 	case NODE_PROGRAM:
    337 		build_program(gen, node);
    338 		break;
    339 	case NODE_FUNCTION_DECL:
    340 		build_func_decl(gen, node);
    341 		break;
    342 	case NODE_STRING_LITERAL:
    343 		emit_literal_string(gen, node);
    344 		break;
    345 	case NODE_NUMBER_LITERAL:
    346 		emit_literal_int(gen, node);
    347 		break;
    348 	default:
    349 		printf("unhandled, %s\n", node_type_str(node->type));
    350 	}
    351 }