#include #include #include #include #include #include #include "../../../include/oberon.h" #include "../../oberon-internals.h" #include "generator-jvm.h" char * new_string(const char * format, ...) { va_list ptr; va_start(ptr, format); char buf[1024]; vsnprintf(buf, 1024, format, ptr); va_end(ptr); char * result; int size; size = strlen(buf); result = GC_MALLOC(size + 1); memset(result, 0, size); strncpy(result, buf, size); return result; } void oberon_generator_init_context(oberon_context_t * ctx) { gen_context_t * gen_context = GC_MALLOC(sizeof *gen_context); memset(gen_context, 0, sizeof *gen_context); ctx -> gen_context = gen_context; } void oberon_generator_destroy_context(oberon_context_t * ctx) { ctx -> gen_context = NULL; } static char * get_class_full_name(oberon_context_t * ctx, oberon_type_t * type); static char * get_descriptor(oberon_context_t * ctx, oberon_type_t * type) { char * desc; switch(type -> class) { case OBERON_TYPE_VOID: return new_string("V"); break; case OBERON_TYPE_INTEGER: switch(type -> size) { case 1: return new_string("B"); break; case 2: return new_string("S"); break; case 4: return new_string("I"); break; case 8: return new_string("J"); break; default: oberon_error(ctx, "get_descriptor: unsupported int size %i", type -> size); break; } break; case OBERON_TYPE_REAL: switch(type -> size) { case 4: return new_string("F"); break; case 8: return new_string("D"); break; default: oberon_error(ctx, "get_descriptor: unsupported float size %i", type -> size); break; } break; case OBERON_TYPE_BOOLEAN: return new_string("Z"); break; case OBERON_TYPE_PROCEDURE: case OBERON_TYPE_RECORD: desc = get_class_full_name(ctx, type); return new_string("L%s;", desc); break; case OBERON_TYPE_ARRAY: desc = get_descriptor(ctx, type -> base); return new_string("[%s", desc); break; default: oberon_error(ctx, "print_descriptor: unsupported type class %i", type -> class); break; } return NULL; } static char get_prefix(oberon_context_t * ctx, oberon_type_t * type) { int size = type -> size; switch(type -> class) { case OBERON_TYPE_BOOLEAN: case OBERON_TYPE_INTEGER: return (size <= 4) ? ('i') : ('l'); break; case OBERON_TYPE_PROCEDURE: case OBERON_TYPE_ARRAY: case OBERON_TYPE_RECORD: case OBERON_TYPE_POINTER: return 'a'; break; case OBERON_TYPE_REAL: return (size <= 4) ? ('f') : ('d'); break; } oberon_error(ctx, "get_prefix: wat"); return '!'; } static char * get_field_full_name(oberon_context_t * ctx, oberon_object_t * x) { return new_string("%s/%s", x -> module -> name, x -> name); } static char * get_class_full_name(oberon_context_t * ctx, oberon_type_t * type) { int rec_id; char * name = NULL; switch(type -> class) { case OBERON_TYPE_PROCEDURE: name = new_string("SYSTEM$PROCEDURE"); char * desc; desc = get_descriptor(ctx, type -> base); name = new_string("%s$%s", name, desc); int num = type -> num_decl; oberon_object_t * arg = type -> decl; for(int i = 0; i < num; i++) { desc = get_descriptor(ctx, arg -> type); name = new_string("%s%s", name, desc); arg = arg -> next; } break; case OBERON_TYPE_RECORD: assert(type -> module); assert(type -> module -> gen_mod); rec_id = type -> gen_type -> rec_id; name = new_string("%s$RECORD%i", type -> module -> name, rec_id); break; default: oberon_error(ctx, "get_record_full_name: unk type class %i", type -> class); break; } return name; } static char * get_procedure_signature(oberon_context_t * ctx, oberon_type_t * proc) { char * signature; char * desc; signature = new_string("("); int num = proc -> num_decl; oberon_object_t * arg = proc -> decl; for(int i = 0; i < num; i++) { desc = get_descriptor(ctx, arg -> type); signature = new_string("%s%s", signature, desc); arg = arg -> next; } desc = get_descriptor(ctx, proc -> base); signature = new_string("%s)%s", signature, desc); return signature; } static void oberon_generate_procedure_class(oberon_context_t * ctx, oberon_type_t * proc) { FILE * fp; char * cname; char * fname; char * signature; cname = get_class_full_name(ctx, proc); fname = new_string("%s.j", cname); fp = fopen(fname, "w"); fprintf(fp, ".source SYSTEM\n"); fprintf(fp, ".class public abstract %s\n", cname); fprintf(fp, ".super java/lang/Object\n\n"); signature = get_procedure_signature(ctx, proc); fprintf(fp, ".method public ()V\n"); fprintf(fp, " aload_0\n"); fprintf(fp, " invokespecial java/lang/Object/()V\n"); fprintf(fp, " return\n"); fprintf(fp, ".end method\n\n"); fprintf(fp, ".method public abstract invoke%s\n", signature); fprintf(fp, ".end method\n\n"); fclose(fp); } static void oberon_generate_record_class(oberon_context_t * ctx, oberon_type_t * rec) { FILE * fp; char * cname; char * fname; /* Устанавливаем новоый id */ rec -> gen_type -> rec_id = rec -> module -> gen_mod -> rec_id; rec -> module -> gen_mod -> rec_id += 1; cname = get_class_full_name(ctx, rec); fname = new_string("%s.j", cname); fp = fopen(fname, "w"); fprintf(fp, ".source %s\n", rec -> module -> name); fprintf(fp, ".class public %s\n", cname); fprintf(fp, ".super java/lang/Object\n\n"); rec -> gen_type -> fp = fp; } void oberon_generator_init_type(oberon_context_t * ctx, oberon_type_t * type) { gen_type_t * t = GC_MALLOC(sizeof *t); memset(t, 0, sizeof *t); type -> gen_type = t; switch(type -> class) { case OBERON_TYPE_VOID: case OBERON_TYPE_INTEGER: case OBERON_TYPE_BOOLEAN: case OBERON_TYPE_ARRAY: case OBERON_TYPE_REAL: break; case OBERON_TYPE_RECORD: oberon_generate_record_class(ctx, type); break; case OBERON_TYPE_PROCEDURE: oberon_generate_procedure_class(ctx, type); break; case OBERON_TYPE_POINTER: assert(type -> base -> class == OBERON_TYPE_VOID); break; default: oberon_error(ctx, "oberon_generator_init_type: unk calss %i", type -> class); break; } } static void oberon_generate_object(oberon_context_t * ctx, FILE * fp, oberon_object_t * x) { char * name; char * desc; name = x -> name; desc = get_descriptor(ctx, x -> type); switch(x -> class) { case OBERON_CLASS_VAR: fprintf(fp, ".field public static %s %s\n\n", name, desc); break; case OBERON_CLASS_FIELD: fprintf(fp, ".field public %s %s\n\n", name, desc); break; default: oberon_error(ctx, "oberon_generate_object: unk class %i", x -> class); break; } } void oberon_generator_init_record(oberon_context_t * ctx, oberon_type_t * rec) { FILE * fp; fp = rec -> gen_type -> fp; int num = rec -> num_decl; oberon_object_t * field = rec -> decl; for(int i = 0; i < num; i++) { oberon_generate_object(ctx, fp, field); field = field -> next; } fprintf(fp, ".method public ()V\n"); fprintf(fp, " aload_0\n"); fprintf(fp, " invokespecial java/lang/Object/()V\n"); fprintf(fp, " return\n"); fprintf(fp, ".end method\n"); fclose(fp); } void oberon_generator_init_var(oberon_context_t * ctx, oberon_object_t * var) { gen_var_t * v = GC_MALLOC(sizeof *v); memset(v, 0, sizeof *v); var -> gen_var = v; gen_module_t * m = ctx -> mod -> gen_mod; switch(var -> class) { case OBERON_CLASS_VAR_PARAM: oberon_error(ctx, "generator: VAR-parameters not implemented"); break; case OBERON_CLASS_PARAM: case OBERON_CLASS_FIELD: break; case OBERON_CLASS_VAR: oberon_generate_object(ctx, m -> fp, var); break; default: oberon_error(ctx, "oberon_generator_init_var: unk var class %i", var -> class); break; } } void oberon_generator_init_proc(oberon_context_t * ctx, oberon_object_t * proc) { gen_proc_t * p = GC_MALLOC(sizeof *p); memset(p, 0, sizeof *p); proc -> gen_proc = p; struct gen_register_file * rf = GC_MALLOC(sizeof *rf); memset(rf, 0, sizeof *rf); p -> rf = rf; if(proc -> local) { oberon_error(ctx, "generator: local procedures not implemented"); } } void oberon_generator_init_module(oberon_context_t * ctx, oberon_module_t * mod) { gen_module_t * m = GC_MALLOC(sizeof *m); memset(m, 0, sizeof *m); mod -> gen_mod = m; int fnamesz = strlen(mod -> name) + 3; char fname[fnamesz + 1]; snprintf(fname, fnamesz, "%s.j", mod -> name); FILE * fp; fp = fopen(fname, "w"); assert(fp != NULL); fprintf(fp, ".source %s\n", mod -> name); fprintf(fp, ".class %s\n", mod -> name); fprintf(fp, ".super java/lang/Object\n\n"); m -> fp = fp; } // ======================================================================= // GENERATOR // ======================================================================= static void push_expr(oberon_context_t * ctx, FILE * fp, oberon_expr_t * expr); void oberon_generate_begin_module(oberon_context_t * ctx) { gen_module_t * m = ctx -> mod -> gen_mod; fprintf(m -> fp, ".method public ()V\n"); fprintf(m -> fp, " aload_0\n"); fprintf(m -> fp, " invokespecial java/lang/Object/()V\n"); } void oberon_generate_end_module(oberon_context_t * ctx) { gen_module_t * m = ctx -> mod -> gen_mod; fprintf(m -> fp, " .limit stack 32\n"); fprintf(m -> fp, " .limit locals 32\n"); fprintf(m -> fp, " return\n"); fprintf(m -> fp, ".end method\n"); } static int jvm_cell_size_for_type(oberon_type_t * type) { if(type -> class == OBERON_TYPE_INTEGER || type -> class == OBERON_TYPE_REAL) { if(type -> size > 4) { return 2; } } return 1; } static bool jvm_is_wide_type(oberon_type_t * type) { int cell; cell = jvm_cell_size_for_type(type); assert(cell <= 2); return (cell == 2); } static bool jvm_is_free_register(struct gen_register_file * rf, int i, bool wide) { if(wide) { assert(i + 1 < MAX_REGISTERS); return !(rf -> reg[i].used || rf -> reg[i + 1].used); } else { assert(i < MAX_REGISTERS); return !(rf -> reg[i].used); } } static int jvm_alloc_register(struct gen_register_file * rf, oberon_type_t * type) { bool wide; wide = jvm_is_wide_type(type); int i = 0; while(i < MAX_REGISTERS && !jvm_is_free_register(rf, i, wide)) { i += 1; } if(wide) { assert(i + 1 <= MAX_REGISTERS); rf -> num_used += 2; rf -> reg[i].used = true; rf -> reg[i + 1].used = true; rf -> reg[i].used = true; rf -> reg[i + 1].wide = false; } else { assert(i <= MAX_REGISTERS); rf -> num_used += 1; rf -> reg[i].used = true; rf -> reg[i].wide = false; } if(rf -> num_used > rf -> max_used) { rf -> max_used = rf -> num_used; } return i; } static void jvm_init_local_object(oberon_context_t * ctx, FILE * fp, oberon_object_t * x) { int reg; gen_context_t * c; gen_var_t * v; char * desc; c = ctx -> gen_context; v = x -> gen_var; desc = get_descriptor(ctx, x -> type); reg = -1; switch(x -> class) { case OBERON_CLASS_VAR: case OBERON_CLASS_PARAM: if(x -> local) { reg = jvm_alloc_register(c -> rf, x -> type); } v -> reg = reg; fprintf(fp, ".var %i is %s %s from start to end\n", reg, x -> name, desc); break; default: oberon_error(ctx, "jvm_init_local_object: wat"); break; } } void oberon_generate_begin_proc(oberon_context_t * ctx, oberon_object_t * proc) { gen_context_t * c; gen_module_t * m; gen_proc_t * p; char * signature; c = ctx -> gen_context; m = ctx -> mod -> gen_mod; p = proc -> gen_proc; signature = get_procedure_signature(ctx, proc -> type); fprintf(m -> fp, ".method public static %s%s\n", proc -> name, signature); /* Сохраняем регистровый файл в стеке */ p -> rf -> up = c -> rf; c -> rf = p -> rf; oberon_object_t * var = proc -> type -> decl; while(var) { jvm_init_local_object(ctx, m -> fp, var); var = var -> next; } fprintf(m -> fp, "start:\n"); } void oberon_generate_end_proc(oberon_context_t * ctx) { gen_context_t * c; gen_module_t * m; c = ctx -> gen_context; m = ctx -> mod -> gen_mod; fprintf(m -> fp, "end:\n"); fprintf(m -> fp, " .limit stack 32\n"); fprintf(m -> fp, " .limit locals %i\n", c -> rf -> max_used); fprintf(m -> fp, ".end method\n\n"); /* Возвращаем исходный регистровый файл */ c -> rf = c -> rf -> up; } void oberon_generate_call_proc(oberon_context_t * ctx, oberon_expr_t * desig) { oberon_object_t * proc; gen_module_t * m; char * fullname; char * signature; assert(desig -> is_item); assert(desig -> item.mode == MODE_CALL); m = ctx -> mod -> gen_mod; proc = desig -> item.var; fullname = get_field_full_name(ctx, proc); signature = get_procedure_signature(ctx, proc -> type); int num = desig -> item.num_args; oberon_expr_t * arg = desig -> item.args; for(int i = 0; i < num; i++) { push_expr(ctx, m -> fp, arg); arg = arg -> next; } fprintf(m -> fp, "invokestatic %s%s\n", fullname, signature); } void oberon_generate_return(oberon_context_t * ctx, oberon_expr_t * expr) { gen_module_t * m; char prefix; m = ctx -> mod -> gen_mod; if(expr) { push_expr(ctx, m -> fp, expr); prefix = get_prefix(ctx, expr -> result); fprintf(m -> fp, " %creturn\n", prefix); } else { fprintf(m -> fp, " return\n"); } } static void push_int(FILE * fp, long i) { if(i == -1) { fprintf(fp, "iconst_m1\n"); } else if(i >= 0 && i <= 5) { fprintf(fp, "iconst_%li\n", i); } else if(i >= -128 && i <= 127) { fprintf(fp, "bipush %li\n", i); } else if(i >= -32768 && i <= 32767) { fprintf(fp, "sipush %li\n", i); } else if(i >= -2147483648 && i <= 2147483647) { fprintf(fp, "ldc %li\n", i); } else { fprintf(fp, "ldc2 %li\n", i); } } static void push_float(FILE * fp, double f, int size) { if(size <= 4) { if(f == 0.0) { fprintf(fp, "fconst_0\n"); } if(f == 1.0) { fprintf(fp, "fconst_1\n"); } if(f == 2.0) { fprintf(fp, "fconst_2\n"); } else { fprintf(fp, "ldc %lf\n", f); } } else { if(f == 0.0) { fprintf(fp, "dconst_0\n"); } if(f == 1.0) { fprintf(fp, "dconst_1\n"); } else { fprintf(fp, "ldc2 %lf\n", f); } } } static void push_var(oberon_context_t * ctx, FILE * fp, oberon_object_t * var) { if(var -> local) { int reg = var -> gen_var -> reg; char prefix = get_prefix(ctx, var -> type); fprintf(fp, "%cload %i\n", prefix, reg); } else { char * fullname = get_field_full_name(ctx, var); char * desc = get_descriptor(ctx, var -> type); fprintf(fp, "getstatic %s %s\n", fullname, desc); } } static void push_item(oberon_context_t * ctx, FILE * fp, oberon_item_t * item) { switch(item -> mode) { case MODE_VAR: push_var(ctx, fp, item -> var); break; case MODE_INTEGER: push_int(fp, item -> integer); break; case MODE_BOOLEAN: push_int(fp, item -> boolean); break; case MODE_CALL: oberon_generate_call_proc(ctx, (oberon_expr_t *) item); break; case MODE_INDEX: oberon_error(ctx, "push_item: TODO index"); break; case MODE_FIELD: oberon_error(ctx, "push_item: TODO field"); break; case MODE_DEREF: oberon_error(ctx, "push_item: TODO deref"); break; case MODE_NIL: fprintf(fp, "aconst_null\n"); break; case MODE_NEW: oberon_error(ctx, "push_item: TODO new"); break; case MODE_REAL: push_float(fp, item -> real, item -> result -> size); break; default: oberon_error(ctx, "push_item: unk mode %i", item -> mode); break; } } static int jvm_new_label_id(oberon_context_t * ctx) { gen_context_t * c = ctx -> gen_context; int label_id = c -> label_id; c -> label_id += 1; return label_id; } static char * jvm_get_label_name(int label_id) { return new_string("L%i", label_id); } static void jvm_generate_label(FILE * fp, int label_id) { fprintf(fp, "L%i:\n", label_id); } static void jvm_generate_logical_not(oberon_context_t * ctx, FILE * fp) { int label_true = jvm_new_label_id(ctx); int label_false = jvm_new_label_id(ctx); char * label_name_true = jvm_get_label_name(label_true); char * label_name_false = jvm_get_label_name(label_false); fprintf(fp, "ifne %s\n", label_name_false); fprintf(fp, "iconst_1\n"); fprintf(fp, "goto %s\n", label_name_true); jvm_generate_label(fp, label_false); fprintf(fp, "iconst_0\n"); jvm_generate_label(fp, label_true); } static char jvm_get_type_of_prefix(char prefix) { switch(prefix) { case 'b': return 'B'; case 'c': return 'C'; case 'd': return 'D'; case 'f': return 'F'; case 'i': return 'I'; case 'l': return 'J'; } assert(0); } static void jvm_generate_abs(FILE * fp, char prefix) { char t = jvm_get_type_of_prefix(prefix); fprintf(fp, "invokestatic java/lang/Math/abs(%c)%c\n", t, t); } static void jvm_generate_compare_op(oberon_context_t * ctx, FILE *fp, char prefix, int op) { int label_true = jvm_new_label_id(ctx); int label_false = jvm_new_label_id(ctx); char * label_name_true = jvm_get_label_name(label_true); char * label_name_false = jvm_get_label_name(label_false); assert(prefix == 'i' || prefix == 'a'); switch(op) { case OP_EQ: fprintf(fp, "if_%ccmpeq %s\n", prefix, label_name_true); break; case OP_NEQ: fprintf(fp, "if_%ccmpne %s\n", prefix, label_name_true); break; case OP_LSS: fprintf(fp, "if_icmplt %s\n", label_name_true); break; case OP_LEQ: fprintf(fp, "if_icmple %s\n", label_name_true); break; case OP_GRT: fprintf(fp, "if_icmpgt %s\n", label_name_true); break; case OP_GEQ: fprintf(fp, "if_icmpge %s\n", label_name_true); break; default: oberon_error(ctx, "jvm_generate_compare_op: wat"); break; } fprintf(fp, "iconst_0\n"); fprintf(fp, "goto %s\n", label_name_false); jvm_generate_label(fp, label_true); fprintf(fp, "iconst_1\n"); jvm_generate_label(fp, label_false); } static void jvm_generate_operator(oberon_context_t * ctx, FILE *fp, char prefix, int op) { switch(op) { case OP_UNARY_MINUS: fprintf(fp, "%cneg\n", prefix); break; case OP_BITWISE_NOT: push_int(fp, -1); fprintf(fp, "%cxor\n", prefix); break; case OP_LOGIC_NOT: jvm_generate_logical_not(ctx, fp); break; case OP_ABS: jvm_generate_abs(fp, prefix); break; case OP_ADD: fprintf(fp, "%cadd\n", prefix); break; case OP_SUB: fprintf(fp, "%csub\n", prefix); break; case OP_MUL: fprintf(fp, "%cmul\n", prefix); break; case OP_DIV: fprintf(fp, "%cdiv\n", prefix); break; case OP_MOD: fprintf(fp, "%crem\n", prefix); break; case OP_BITWISE_AND: fprintf(fp, "%cand\n", prefix); break; case OP_BITWISE_XOR: fprintf(fp, "%cxor\n", prefix); break; case OP_BITWISE_OR: fprintf(fp, "%cor\n", prefix); break; case OP_EQ: case OP_NEQ: case OP_LSS: case OP_LEQ: case OP_GRT: case OP_GEQ: jvm_generate_compare_op(ctx, fp, prefix, op); break; default: oberon_error(ctx, "jvm_generate_operator: unk op %i", op); break; } } static void push_operator(oberon_context_t * ctx, FILE * fp, oberon_oper_t * oper) { char prefix = get_prefix(ctx, oper -> result); int op = oper -> op; switch(op) { case OP_UNARY_MINUS: case OP_BITWISE_NOT: case OP_LOGIC_NOT: case OP_ABS: push_expr(ctx, fp, oper -> left); jvm_generate_operator(ctx, fp, prefix, op); break; case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: case OP_MOD: case OP_BITWISE_AND: case OP_BITWISE_XOR: case OP_BITWISE_OR: case OP_EQ: case OP_NEQ: case OP_LSS: case OP_LEQ: case OP_GRT: case OP_GEQ: push_expr(ctx, fp, oper -> left); push_expr(ctx, fp, oper -> right); jvm_generate_operator(ctx, fp, prefix, op); break; default: oberon_error(ctx, "push_oper: unk op %i", op); break; } } static void push_expr(oberon_context_t * ctx, FILE * fp, oberon_expr_t * expr) { if(expr -> is_item) { push_item(ctx, fp, (oberon_item_t *) expr); } else { push_operator(ctx, fp, (oberon_oper_t *) expr); } } static void store_expr(oberon_context_t * ctx, FILE * fp, oberon_expr_t * expr) { assert(expr -> is_item); oberon_item_t * item = (oberon_item_t *) expr; char prefix; switch(item -> mode) { case MODE_VAR: if(item -> var -> local) { int reg = item -> var -> gen_var -> reg; prefix = get_prefix(ctx, item -> result); fprintf(fp, "%cstore %i\n", prefix, reg); } else { char * fullname = get_field_full_name(ctx, item -> var); char * desc = get_descriptor(ctx, item -> result); fprintf(fp, "putstatic %s %s\n", fullname, desc); } break; default: oberon_error(ctx, "store_expr: unk mode %i", item -> mode); break; } } void oberon_generate_assign(oberon_context_t * ctx, oberon_expr_t * src, oberon_expr_t * dst) { gen_module_t * m; m = ctx -> mod -> gen_mod; push_expr(ctx, m -> fp, src); store_expr(ctx, m -> fp, dst); } void oberon_generate_code(oberon_context_t * ctx) { printf("generate code\n"); } void oberon_generator_dump(oberon_context_t * ctx, char * path) { printf("jit: dump code\n"); } void * oberon_generator_get_procedure(oberon_context_t * ctx, const char * name) { printf("jit: get pointer to procedure %s\n", name); return NULL; } void * oberon_generator_get_var(oberon_context_t * ctx, const char * name) { printf("jit: get pointer to var %s\n", name); return NULL; }