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 }