/* This file is a part of MIR project. Copyright (C) 2018-2021 Vladimir Makarov . */ #include "mir.h" DEF_VARR (MIR_insn_t); DEF_VARR (MIR_reg_t); DEF_VARR (MIR_op_t); DEF_VARR (MIR_type_t); DEF_HTAB (MIR_item_t); DEF_VARR (MIR_module_t); DEF_VARR (size_t); DEF_VARR (char); DEF_VARR (uint8_t); DEF_VARR (MIR_proto_t); struct gen_ctx; struct c2mir_ctx; struct string_ctx; struct reg_ctx; struct simplify_ctx; struct machine_code_ctx; struct io_ctx; struct scan_ctx; struct interp_ctx; struct MIR_context { struct gen_ctx *gen_ctx; /* should be the 1st member */ struct c2mir_ctx *c2mir_ctx; /* should be the 2nd member */ #if MIR_PARALLEL_GEN mir_mutex_t ctx_mutex; #endif MIR_error_func_t error_func; VARR (size_t) * insn_nops; /* constant after initialization */ VARR (MIR_proto_t) * unspec_protos; /* protos of unspec insns (set only during initialization) */ VARR (char) * temp_string; VARR (uint8_t) * temp_data; HTAB (MIR_item_t) * module_item_tab; /* Module to keep items potentially used by all modules: */ struct MIR_module environment_module; MIR_module_t curr_module; MIR_func_t curr_func; int curr_label_num; DLIST (MIR_module_t) all_modules; VARR (MIR_module_t) * modules_to_link; struct string_ctx *string_ctx; struct reg_ctx *reg_ctx; struct simplify_ctx *simplify_ctx; struct machine_code_ctx *machine_code_ctx; struct io_ctx *io_ctx; struct scan_ctx *scan_ctx; struct interp_ctx *interp_ctx; void *setjmp_addr; /* used in interpreter to call setjmp directly not from a shim and FFI */ }; #define ctx_mutex ctx->ctx_mutex #define error_func ctx->error_func #define unspec_protos ctx->unspec_protos #define insn_nops ctx->insn_nops #define temp_string ctx->temp_string #define temp_data ctx->temp_data #define module_item_tab ctx->module_item_tab #define environment_module ctx->environment_module #define curr_module ctx->curr_module #define curr_func ctx->curr_func #define curr_label_num ctx->curr_label_num #define all_modules ctx->all_modules #define modules_to_link ctx->modules_to_link #define setjmp_addr ctx->setjmp_addr static void util_error (MIR_context_t ctx, const char *message); #define MIR_VARR_ERROR util_error #define MIR_HTAB_ERROR MIR_VARR_ERROR #include "mir-hash.h" #include "mir-htab.h" #include "mir-reduce.h" #include #include #include #include #include #include static void interp_init (MIR_context_t ctx); static void finish_func_interpretation (MIR_item_t func_item); static void interp_finish (MIR_context_t ctx); static void MIR_NO_RETURN default_error (enum MIR_error_type error_type, const char *format, ...) { va_list ap; va_start (ap, format); vfprintf (stderr, format, ap); fprintf (stderr, "\n"); va_end (ap); exit (1); } static void MIR_NO_RETURN MIR_UNUSED util_error (MIR_context_t ctx, const char *message) { MIR_get_error_func (ctx) (MIR_alloc_error, message); } #define HARD_REG_NAME_PREFIX "hr" #define TEMP_REG_NAME_PREFIX "t" #define TEMP_ITEM_NAME_PREFIX ".lc" int _MIR_reserved_ref_name_p (MIR_context_t ctx, const char *name) { return strncmp (name, TEMP_ITEM_NAME_PREFIX, strlen (TEMP_ITEM_NAME_PREFIX)) == 0; } /* Reserved names: fp - frame pointer hr - a hardware reg lc - a temp item */ int _MIR_reserved_name_p (MIR_context_t ctx, const char *name) { size_t i, start; if (_MIR_reserved_ref_name_p (ctx, name)) return TRUE; else if (strncmp (name, HARD_REG_NAME_PREFIX, strlen (HARD_REG_NAME_PREFIX)) == 0) start = strlen (HARD_REG_NAME_PREFIX); else return FALSE; for (i = start; name[i] != '\0'; i++) if (name[i] < '0' || name[i] > '9') return FALSE; return TRUE; } struct insn_desc { MIR_insn_code_t code; const char *name; unsigned char op_modes[5]; }; #define OUTPUT_FLAG (1 << 7) static const struct insn_desc insn_descs[] = { {MIR_MOV, "mov", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_FMOV, "fmov", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_DMOV, "dmov", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LDMOV, "ldmov", {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_EXT8, "ext8", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_EXT16, "ext16", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_EXT32, "ext32", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UEXT8, "uext8", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UEXT16, "uext16", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UEXT32, "uext32", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_I2F, "i2f", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_I2D, "i2d", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_I2LD, "i2ld", {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UI2F, "ui2f", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UI2D, "ui2d", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UI2LD, "ui2ld", {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_F2I, "f2i", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_D2I, "d2i", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LD2I, "ld2i", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_F2D, "f2d", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_F2LD, "f2ld", {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_D2F, "d2f", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_D2LD, "d2ld", {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LD2F, "ld2f", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_LD2D, "ld2d", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_NEG, "neg", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_NEGS, "negs", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_FNEG, "fneg", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_DNEG, "dneg", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LDNEG, "ldneg", {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_ADD, "add", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_ADDS, "adds", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_FADD, "fadd", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_DADD, "dadd", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LDADD, "ldadd", {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_SUB, "sub", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_SUBS, "subs", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_FSUB, "fsub", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_DSUB, "dsub", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LDSUB, "ldsub", {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_MUL, "mul", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_MULS, "muls", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_FMUL, "fmul", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_DMUL, "dmul", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LDMUL, "ldmul", {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_DIV, "div", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_DIVS, "divs", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UDIV, "udiv", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UDIVS, "udivs", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_FDIV, "fdiv", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_DDIV, "ddiv", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LDDIV, "lddiv", {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_MOD, "mod", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_MODS, "mods", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UMOD, "umod", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UMODS, "umods", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_AND, "and", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_ANDS, "ands", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_OR, "or", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_ORS, "ors", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_XOR, "xor", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_XORS, "xors", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_LSH, "lsh", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_LSHS, "lshs", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_RSH, "rsh", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_RSHS, "rshs", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_URSH, "ursh", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_URSHS, "urshs", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_EQ, "eq", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_EQS, "eqs", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_FEQ, "feq", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_DEQ, "deq", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LDEQ, "ldeq", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_NE, "ne", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_NES, "nes", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_FNE, "fne", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_DNE, "dne", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LDNE, "ldne", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_LT, "lt", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_LTS, "lts", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_ULT, "ult", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_ULTS, "ults", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_FLT, "flt", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_DLT, "dlt", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LDLT, "ldlt", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_LE, "le", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_LES, "les", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_ULE, "ule", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_ULES, "ules", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_FLE, "fle", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_DLE, "dle", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LDLE, "ldle", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_GT, "gt", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_GTS, "gts", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UGT, "ugt", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UGTS, "ugts", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_FGT, "fgt", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_DGT, "dgt", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LDGT, "ldgt", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_GE, "ge", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_GES, "ges", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UGE, "uge", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UGES, "uges", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_FGE, "fge", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_DGE, "dge", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LDGE, "ldge", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_JMP, "jmp", {MIR_OP_LABEL, MIR_OP_BOUND}}, {MIR_BT, "bt", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_BTS, "bts", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_BF, "bf", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_BFS, "bfs", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_BEQ, "beq", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_BEQS, "beqs", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_FBEQ, "fbeq", {MIR_OP_LABEL, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_DBEQ, "dbeq", {MIR_OP_LABEL, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LDBEQ, "ldbeq", {MIR_OP_LABEL, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_BNE, "bne", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_BNES, "bnes", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_FBNE, "fbne", {MIR_OP_LABEL, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_DBNE, "dbne", {MIR_OP_LABEL, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LDBNE, "ldbne", {MIR_OP_LABEL, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_BLT, "blt", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_BLTS, "blts", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UBLT, "ublt", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UBLTS, "ublts", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_FBLT, "fblt", {MIR_OP_LABEL, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_DBLT, "dblt", {MIR_OP_LABEL, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LDBLT, "ldblt", {MIR_OP_LABEL, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_BLE, "ble", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_BLES, "bles", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UBLE, "uble", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UBLES, "ubles", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_FBLE, "fble", {MIR_OP_LABEL, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_DBLE, "dble", {MIR_OP_LABEL, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LDBLE, "ldble", {MIR_OP_LABEL, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_BGT, "bgt", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_BGTS, "bgts", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UBGT, "ubgt", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UBGTS, "ubgts", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_FBGT, "fbgt", {MIR_OP_LABEL, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_DBGT, "dbgt", {MIR_OP_LABEL, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LDBGT, "ldbgt", {MIR_OP_LABEL, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_BGE, "bge", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_BGES, "bges", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UBGE, "ubge", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_UBGES, "ubges", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_FBGE, "fbge", {MIR_OP_LABEL, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, {MIR_DBGE, "dbge", {MIR_OP_LABEL, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, {MIR_LDBGE, "ldbge", {MIR_OP_LABEL, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, {MIR_CALL, "call", {MIR_OP_BOUND}}, {MIR_INLINE, "inline", {MIR_OP_BOUND}}, {MIR_SWITCH, "switch", {MIR_OP_BOUND}}, {MIR_RET, "ret", {MIR_OP_BOUND}}, {MIR_ALLOCA, "alloca", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_BSTART, "bstart", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_BOUND}}, {MIR_BEND, "bend", {MIR_OP_INT, MIR_OP_BOUND}}, {MIR_VA_ARG, "va_arg", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_UNDEF, MIR_OP_BOUND}}, {MIR_VA_BLOCK_ARG, "va_block_arg", {MIR_OP_INT, MIR_OP_INT, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, {MIR_VA_START, "va_start", {MIR_OP_INT, MIR_OP_BOUND}}, {MIR_VA_END, "va_end", {MIR_OP_INT, MIR_OP_BOUND}}, {MIR_LABEL, "label", {MIR_OP_BOUND}}, {MIR_UNSPEC, "unspec", {MIR_OP_BOUND}}, {MIR_PHI, "phi", {MIR_OP_BOUND}}, {MIR_INVALID_INSN, "invalid-insn", {MIR_OP_BOUND}}, }; static void check_and_prepare_insn_descs (MIR_context_t ctx) { size_t i, j; VARR_CREATE (size_t, insn_nops, 0); for (i = 0; i < MIR_INSN_BOUND; i++) { mir_assert (insn_descs[i].code == i); for (j = 0; insn_descs[i].op_modes[j] != MIR_OP_BOUND; j++) ; VARR_PUSH (size_t, insn_nops, j); } } static MIR_op_mode_t type2mode (MIR_type_t type) { return (type == MIR_T_UNDEF ? MIR_OP_UNDEF : type == MIR_T_F ? MIR_OP_FLOAT : type == MIR_T_D ? MIR_OP_DOUBLE : type == MIR_T_LD ? MIR_OP_LDOUBLE : MIR_OP_INT); } /* New Page */ typedef struct string { size_t num; /* string number starting with 1 */ MIR_str_t str; } string_t; DEF_VARR (string_t); DEF_HTAB (string_t); struct string_ctx { VARR (string_t) * strings; HTAB (string_t) * string_tab; }; #define strings ctx->string_ctx->strings #define string_tab ctx->string_ctx->string_tab static htab_hash_t str_hash (string_t str, void *arg) { return mir_hash (str.str.s, str.str.len, 0); } static int str_eq (string_t str1, string_t str2, void *arg) { return str1.str.len == str2.str.len && memcmp (str1.str.s, str2.str.s, str1.str.len) == 0; } static void string_init (VARR (string_t) * *strs, HTAB (string_t) * *str_tab) { string_t string = {0, {0, NULL}}; VARR_CREATE (string_t, *strs, 0); VARR_PUSH (string_t, *strs, string); /* don't use 0th string */ HTAB_CREATE (string_t, *str_tab, 1000, str_hash, str_eq, NULL); } static int string_find (VARR (string_t) * *strs, HTAB (string_t) * *str_tab, MIR_str_t str, string_t *s) { string_t string; string.str = str; return HTAB_DO (string_t, *str_tab, string, HTAB_FIND, *s); } static string_t string_store (MIR_context_t ctx, VARR (string_t) * *strs, HTAB (string_t) * *str_tab, MIR_str_t str) { char *heap_str; string_t el, string; if (string_find (strs, str_tab, str, &el)) return el; if ((heap_str = malloc (str.len)) == NULL) MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory for strings"); memcpy (heap_str, str.s, str.len); string.str.s = heap_str; string.str.len = str.len; string.num = VARR_LENGTH (string_t, *strs); VARR_PUSH (string_t, *strs, string); HTAB_DO (string_t, *str_tab, string, HTAB_INSERT, el); return string; } static struct string get_ctx_string (MIR_context_t ctx, MIR_str_t str) { return string_store (ctx, &strings, &string_tab, str); } static const char *get_ctx_str (MIR_context_t ctx, const char *string) { return get_ctx_string (ctx, (MIR_str_t){strlen (string) + 1, string}).str.s; } static void string_finish (VARR (string_t) * *strs, HTAB (string_t) * *str_tab) { size_t i; for (i = 1; i < VARR_LENGTH (string_t, *strs); i++) free ((char *) VARR_ADDR (string_t, *strs)[i].str.s); VARR_DESTROY (string_t, *strs); HTAB_DESTROY (string_t, *str_tab); } /* New Page */ typedef struct reg_desc { char *name; /* 1st key for the name2rdn hash tab */ MIR_type_t type; MIR_reg_t reg; /* 1st key reg2rdn hash tab */ } reg_desc_t; DEF_VARR (reg_desc_t); DEF_HTAB (size_t); typedef struct func_regs { VARR (reg_desc_t) * reg_descs; HTAB (size_t) * name2rdn_tab; HTAB (size_t) * reg2rdn_tab; } * func_regs_t; static int name2rdn_eq (size_t rdn1, size_t rdn2, void *arg) { func_regs_t func_regs = arg; reg_desc_t *addr = VARR_ADDR (reg_desc_t, func_regs->reg_descs); return strcmp (addr[rdn1].name, addr[rdn2].name) == 0; } static htab_hash_t name2rdn_hash (size_t rdn, void *arg) { func_regs_t func_regs = arg; reg_desc_t *addr = VARR_ADDR (reg_desc_t, func_regs->reg_descs); return mir_hash (addr[rdn].name, strlen (addr[rdn].name), 0); } static int reg2rdn_eq (size_t rdn1, size_t rdn2, void *arg) { func_regs_t func_regs = arg; reg_desc_t *addr = VARR_ADDR (reg_desc_t, func_regs->reg_descs); return addr[rdn1].reg == addr[rdn2].reg; } static htab_hash_t reg2rdn_hash (size_t rdn, void *arg) { func_regs_t func_regs = arg; reg_desc_t *addr = VARR_ADDR (reg_desc_t, func_regs->reg_descs); return mir_hash_finish (mir_hash_step (mir_hash_init (0), addr[rdn].reg)); } static void func_regs_init (MIR_context_t ctx, MIR_func_t func) { func_regs_t func_regs; reg_desc_t rd = {NULL, MIR_T_I64, 0}; if ((func_regs = func->internal = malloc (sizeof (struct func_regs))) == NULL) MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory for func regs info"); VARR_CREATE (reg_desc_t, func_regs->reg_descs, 50); VARR_PUSH (reg_desc_t, func_regs->reg_descs, rd); /* for 0 reg */ HTAB_CREATE (size_t, func_regs->name2rdn_tab, 100, name2rdn_hash, name2rdn_eq, func_regs); HTAB_CREATE (size_t, func_regs->reg2rdn_tab, 100, reg2rdn_hash, reg2rdn_eq, func_regs); } static MIR_reg_t create_func_reg (MIR_context_t ctx, MIR_func_t func, const char *name, MIR_reg_t reg, MIR_type_t type, int any_p, char **name_ptr) { func_regs_t func_regs = func->internal; reg_desc_t rd; size_t rdn, tab_rdn; int htab_res; if (!any_p && _MIR_reserved_name_p (ctx, name)) MIR_get_error_func (ctx) (MIR_reserved_name_error, "redefining a reserved name %s", name); rd.name = (char *) name; rd.type = type; rd.reg = reg; /* 0 is reserved */ rdn = VARR_LENGTH (reg_desc_t, func_regs->reg_descs); VARR_PUSH (reg_desc_t, func_regs->reg_descs, rd); if (HTAB_DO (size_t, func_regs->name2rdn_tab, rdn, HTAB_FIND, tab_rdn)) { VARR_POP (reg_desc_t, func_regs->reg_descs); MIR_get_error_func (ctx) (MIR_repeated_decl_error, "Repeated reg declaration %s", name); } if ((rd.name = malloc (strlen (name) + 1)) == NULL) MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory for reg names"); VARR_ADDR (reg_desc_t, func_regs->reg_descs)[rdn].name = *name_ptr = rd.name; strcpy (*name_ptr, name); htab_res = HTAB_DO (size_t, func_regs->name2rdn_tab, rdn, HTAB_INSERT, tab_rdn); mir_assert (!htab_res); htab_res = HTAB_DO (size_t, func_regs->reg2rdn_tab, rdn, HTAB_INSERT, tab_rdn); mir_assert (!htab_res); return reg; } static void func_regs_finish (MIR_context_t ctx, MIR_func_t func) { func_regs_t func_regs = func->internal; char *name; for (size_t i = 0; i < VARR_LENGTH (reg_desc_t, func_regs->reg_descs); i++) if ((name = VARR_GET (reg_desc_t, func_regs->reg_descs, i).name) != NULL) free (name); VARR_DESTROY (reg_desc_t, func_regs->reg_descs); HTAB_DESTROY (size_t, func_regs->name2rdn_tab); HTAB_DESTROY (size_t, func_regs->reg2rdn_tab); free (func->internal); func->internal = NULL; } /* New Page */ static void push_data (MIR_context_t ctx, uint8_t *els, size_t size) { for (size_t i = 0; i < size; i++) VARR_PUSH (uint8_t, temp_data, els[i]); } const char *MIR_item_name (MIR_context_t ctx, MIR_item_t item) { mir_assert (item != NULL); switch (item->item_type) { case MIR_func_item: return item->u.func->name; case MIR_proto_item: return item->u.proto->name; case MIR_import_item: return item->u.import_id; case MIR_export_item: return item->u.export_id; case MIR_forward_item: return item->u.forward_id; case MIR_bss_item: return item->u.bss->name; case MIR_data_item: return item->u.data->name; case MIR_ref_data_item: return item->u.ref_data->name; case MIR_expr_data_item: return item->u.expr_data->name; default: mir_assert (FALSE); return NULL; } } #if !MIR_NO_IO static void io_init (MIR_context_t ctx); static void io_finish (MIR_context_t ctx); #endif #if !MIR_NO_SCAN static void scan_init (MIR_context_t ctx); static void scan_finish (MIR_context_t ctx); #endif static void simplify_init (MIR_context_t ctx); static void simplify_finish (MIR_context_t ctx); MIR_error_func_t MIR_get_error_func (MIR_context_t ctx) { return error_func; } // ??? atomic void MIR_set_error_func (MIR_context_t ctx, MIR_error_func_t func) { // ?? atomic access error_func = func; } static htab_hash_t item_hash (MIR_item_t it, void *arg) { return mir_hash_finish ( mir_hash_step (mir_hash_step (mir_hash_init (28), (uint64_t) MIR_item_name (NULL, it)), (uint64_t) it->module)); } static int item_eq (MIR_item_t it1, MIR_item_t it2, void *arg) { return it1->module == it2->module && MIR_item_name (NULL, it1) == MIR_item_name (NULL, it2); } static MIR_item_t item_tab_find (MIR_context_t ctx, const char *name, MIR_module_t module) { MIR_item_t tab_item; struct MIR_item item_s; struct MIR_func func_s; item_s.item_type = MIR_func_item; func_s.name = name; item_s.module = module; item_s.u.func = &func_s; if (HTAB_DO (MIR_item_t, module_item_tab, &item_s, HTAB_FIND, tab_item)) return tab_item; return NULL; } static MIR_item_t item_tab_insert (MIR_context_t ctx, MIR_item_t item) { MIR_item_t tab_item; HTAB_DO (MIR_item_t, module_item_tab, item, HTAB_INSERT, tab_item); return tab_item; } static void item_tab_remove (MIR_context_t ctx, MIR_item_t item) { HTAB_DO (MIR_item_t, module_item_tab, item, HTAB_DELETE, item); } static void init_module (MIR_context_t ctx, MIR_module_t m, const char *name) { m->data = NULL; m->last_temp_item_num = 0; m->name = get_ctx_str (ctx, name); DLIST_INIT (MIR_item_t, m->items); } static void code_init (MIR_context_t ctx); static void code_finish (MIR_context_t ctx); static void parallel_error (MIR_context_t ctx, const char *err_message) { MIR_get_error_func (ctx) (MIR_parallel_error, err_message); } MIR_context_t MIR_init (void) { MIR_context_t ctx; mir_assert (MIR_OP_BOUND < OUTPUT_FLAG); if ((ctx = malloc (sizeof (struct MIR_context))) == NULL) default_error (MIR_alloc_error, "Not enough memory for ctx"); ctx->string_ctx = NULL; ctx->reg_ctx = NULL; ctx->simplify_ctx = NULL; ctx->machine_code_ctx = NULL; ctx->io_ctx = NULL; ctx->scan_ctx = NULL; ctx->interp_ctx = NULL; if (mir_mutex_init (&ctx_mutex, NULL)) parallel_error (ctx, "error in mutex init"); #ifndef NDEBUG for (MIR_insn_code_t c = 0; c < MIR_INVALID_INSN; c++) mir_assert (c == insn_descs[c].code); #endif error_func = default_error; curr_module = NULL; curr_func = NULL; curr_label_num = 0; if ((ctx->string_ctx = malloc (sizeof (struct string_ctx))) == NULL) MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory for ctx"); string_init (&strings, &string_tab); VARR_CREATE (MIR_proto_t, unspec_protos, 0); check_and_prepare_insn_descs (ctx); DLIST_INIT (MIR_module_t, all_modules); simplify_init (ctx); VARR_CREATE (char, temp_string, 64); VARR_CREATE (uint8_t, temp_data, 512); #if !MIR_NO_IO io_init (ctx); #endif #if !MIR_NO_SCAN scan_init (ctx); #endif VARR_CREATE (MIR_module_t, modules_to_link, 0); init_module (ctx, &environment_module, ".environment"); HTAB_CREATE (MIR_item_t, module_item_tab, 512, item_hash, item_eq, NULL); setjmp_addr = NULL; code_init (ctx); interp_init (ctx); return ctx; } void MIR_remove_insn (MIR_context_t ctx, MIR_item_t func_item, MIR_insn_t insn) { mir_assert (func_item != NULL); if (func_item->item_type != MIR_func_item) MIR_get_error_func (ctx) (MIR_wrong_param_value_error, "MIR_remove_insn: wrong func item"); DLIST_REMOVE (MIR_insn_t, func_item->u.func->insns, insn); free (insn); } static void remove_func_insns (MIR_context_t ctx, MIR_item_t func_item, DLIST (MIR_insn_t) * insns) { MIR_insn_t insn; mir_assert (func_item->item_type == MIR_func_item); while ((insn = DLIST_HEAD (MIR_insn_t, *insns)) != NULL) { MIR_remove_insn (ctx, func_item, insn); } } static void remove_item (MIR_context_t ctx, MIR_item_t item) { switch (item->item_type) { case MIR_func_item: remove_func_insns (ctx, item, &item->u.func->insns); remove_func_insns (ctx, item, &item->u.func->original_insns); VARR_DESTROY (MIR_var_t, item->u.func->vars); func_regs_finish (ctx, item->u.func); free (item->u.func); break; case MIR_proto_item: VARR_DESTROY (MIR_var_t, item->u.proto->args); free (item->u.proto); break; case MIR_import_item: case MIR_export_item: case MIR_forward_item: break; case MIR_data_item: if (item->addr != NULL && item->section_head_p) free (item->addr); free (item->u.data); break; case MIR_ref_data_item: if (item->addr != NULL && item->section_head_p) free (item->addr); free (item->u.ref_data); break; case MIR_expr_data_item: if (item->addr != NULL && item->section_head_p) free (item->addr); free (item->u.expr_data); break; case MIR_bss_item: if (item->addr != NULL && item->section_head_p) free (item->addr); free (item->u.bss); break; default: mir_assert (FALSE); } if (item->data != NULL) free (item->data); free (item); } static void remove_module (MIR_context_t ctx, MIR_module_t module, int free_module_p) { MIR_item_t item; while ((item = DLIST_HEAD (MIR_item_t, module->items)) != NULL) { DLIST_REMOVE (MIR_item_t, module->items, item); remove_item (ctx, item); } if (module->data != NULL) free (module->data); if (free_module_p) free (module); } static void remove_all_modules (MIR_context_t ctx) { MIR_module_t module; while ((module = DLIST_HEAD (MIR_module_t, all_modules)) != NULL) { DLIST_REMOVE (MIR_module_t, all_modules, module); remove_module (ctx, module, TRUE); } remove_module (ctx, &environment_module, FALSE); } void MIR_finish (MIR_context_t ctx) { interp_finish (ctx); remove_all_modules (ctx); HTAB_DESTROY (MIR_item_t, module_item_tab); VARR_DESTROY (MIR_module_t, modules_to_link); #if !MIR_NO_SCAN scan_finish (ctx); #endif #if !MIR_NO_IO io_finish (ctx); #endif VARR_DESTROY (uint8_t, temp_data); VARR_DESTROY (char, temp_string); while (VARR_LENGTH (MIR_proto_t, unspec_protos) != 0) { MIR_proto_t proto = VARR_POP (MIR_proto_t, unspec_protos); VARR_DESTROY (MIR_var_t, proto->args); free (proto); } VARR_DESTROY (MIR_proto_t, unspec_protos); string_finish (&strings, &string_tab); simplify_finish (ctx); VARR_DESTROY (size_t, insn_nops); code_finish (ctx); if (curr_func != NULL) MIR_get_error_func (ctx) (MIR_finish_error, "finish when function %s is not finished", curr_func->name); if (curr_module != NULL) MIR_get_error_func (ctx) (MIR_finish_error, "finish when module %s is not finished", curr_module->name); free (ctx->string_ctx); if (mir_mutex_destroy (&ctx_mutex)) parallel_error (ctx, "error in mutex destroy"); free (ctx); ctx = NULL; } MIR_module_t MIR_new_module (MIR_context_t ctx, const char *name) { if (curr_module != NULL) MIR_get_error_func (ctx) (MIR_nested_module_error, "Creating module when previous module %s is not finished", curr_module->name); if ((curr_module = malloc (sizeof (struct MIR_module))) == NULL) MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory for module %s creation", name); init_module (ctx, curr_module, name); DLIST_APPEND (MIR_module_t, all_modules, curr_module); return curr_module; } DLIST (MIR_module_t) * MIR_get_module_list (MIR_context_t ctx) { return &all_modules; } static const char *type_str (MIR_context_t ctx, MIR_type_t tp) { int n; char str[100]; switch (tp) { case MIR_T_I8: return "i8"; case MIR_T_U8: return "u8"; case MIR_T_I16: return "i16"; case MIR_T_U16: return "u16"; case MIR_T_I32: return "i32"; case MIR_T_U32: return "u32"; case MIR_T_I64: return "i64"; case MIR_T_U64: return "u64"; case MIR_T_F: return "f"; case MIR_T_D: return "d"; case MIR_T_LD: return "ld"; case MIR_T_P: return "p"; case MIR_T_RBLK: return "rblk"; case MIR_T_UNDEF: return "undef"; default: if (MIR_blk_type_p (tp) && (n = tp - MIR_T_BLK) >= 0 && n < MIR_BLK_NUM) { sprintf (str, "blk%d", n); return get_ctx_str (ctx, str); } return ""; } } const char *MIR_type_str (MIR_context_t ctx, MIR_type_t tp) { const char *str = type_str (ctx, tp); if (strcmp (str, "") == 0) MIR_get_error_func (ctx) (MIR_wrong_param_value_error, "MIR_type_str: wrong type"); return str; } static const char *mode_str (MIR_op_mode_t mode) { switch (mode) { case MIR_OP_REG: return "reg"; case MIR_OP_HARD_REG: return "hard_reg"; case MIR_OP_INT: return "int"; case MIR_OP_UINT: return "uint"; case MIR_OP_FLOAT: return "float"; case MIR_OP_DOUBLE: return "double"; case MIR_OP_LDOUBLE: return "ldouble"; case MIR_OP_REF: return "ref"; case MIR_OP_STR: return "str"; case MIR_OP_MEM: return "mem"; case MIR_OP_HARD_REG_MEM: return "hard_reg_mem"; case MIR_OP_LABEL: return "label"; case MIR_OP_BOUND: return "bound"; case MIR_OP_UNDEF: return "undef"; default: return ""; } } static MIR_item_t add_item (MIR_context_t ctx, MIR_item_t item) { int replace_p; MIR_item_t tab_item; if ((tab_item = item_tab_find (ctx, MIR_item_name (ctx, item), item->module)) == NULL) { DLIST_APPEND (MIR_item_t, curr_module->items, item); HTAB_DO (MIR_item_t, module_item_tab, item, HTAB_INSERT, item); return item; } switch (tab_item->item_type) { case MIR_import_item: if (item->item_type != MIR_import_item) MIR_get_error_func (ctx) (MIR_import_export_error, "existing module definition %s already defined as import", tab_item->u.import_id); item = tab_item; break; case MIR_export_item: case MIR_forward_item: replace_p = FALSE; if (item->item_type == MIR_import_item) { MIR_get_error_func (ctx) (MIR_import_export_error, "export/forward of import %s", item->u.import_id); } else if (item->item_type != MIR_export_item && item->item_type != MIR_forward_item) { replace_p = TRUE; DLIST_APPEND (MIR_item_t, curr_module->items, item); } else { if (tab_item->item_type == item->item_type) item = tab_item; else DLIST_APPEND (MIR_item_t, curr_module->items, item); if (item->item_type == MIR_export_item && tab_item->item_type == MIR_forward_item) replace_p = TRUE; } if (replace_p) { /* replace forward by export or export/forward by its definition: */ tab_item->ref_def = item; if (tab_item->item_type == MIR_export_item) item->export_p = TRUE; item_tab_remove (ctx, tab_item); tab_item = item_tab_insert (ctx, item); mir_assert (item == tab_item); } break; case MIR_proto_item: MIR_get_error_func (ctx) (MIR_repeated_decl_error, "item %s was already defined as proto", tab_item->u.proto->name); break; case MIR_bss_item: case MIR_data_item: case MIR_ref_data_item: case MIR_expr_data_item: case MIR_func_item: if (item->item_type == MIR_export_item) { if (tab_item->export_p) { item = tab_item; } else { /* just keep one export: */ tab_item->export_p = TRUE; DLIST_APPEND (MIR_item_t, curr_module->items, item); item->ref_def = tab_item; } } else if (item->item_type == MIR_forward_item) { DLIST_APPEND (MIR_item_t, curr_module->items, item); item->ref_def = tab_item; } else if (item->item_type == MIR_import_item) { MIR_get_error_func (ctx) (MIR_import_export_error, "import of local definition %s", item->u.import_id); } else { MIR_get_error_func (ctx) (MIR_repeated_decl_error, "Repeated item declaration %s", MIR_item_name (ctx, item)); } break; default: mir_assert (FALSE); } return item; } static MIR_item_t create_item (MIR_context_t ctx, MIR_item_type_t item_type, const char *item_name) { MIR_item_t item; if (curr_module == NULL) MIR_get_error_func (ctx) (MIR_no_module_error, "%s outside module", item_name); if ((item = malloc (sizeof (struct MIR_item))) == NULL) MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory for creation of item %s", item_name); item->data = NULL; item->module = curr_module; item->item_type = item_type; item->ref_def = NULL; item->export_p = FALSE; item->section_head_p = FALSE; item->addr = NULL; return item; } static MIR_item_t new_export_import_forward (MIR_context_t ctx, const char *name, MIR_item_type_t item_type, const char *item_name, int create_only_p) { MIR_item_t item, tab_item; const char *uniq_name; item = create_item (ctx, item_type, item_name); uniq_name = get_ctx_str (ctx, name); if (item_type == MIR_export_item) item->u.export_id = uniq_name; else if (item_type == MIR_import_item) item->u.import_id = uniq_name; else item->u.forward_id = uniq_name; if (create_only_p) return item; if ((tab_item = add_item (ctx, item)) != item) { free (item); item = tab_item; } return item; } MIR_item_t MIR_new_export (MIR_context_t ctx, const char *name) { return new_export_import_forward (ctx, name, MIR_export_item, "export", FALSE); } MIR_item_t MIR_new_import (MIR_context_t ctx, const char *name) { return new_export_import_forward (ctx, name, MIR_import_item, "import", FALSE); } MIR_item_t MIR_new_forward (MIR_context_t ctx, const char *name) { return new_export_import_forward (ctx, name, MIR_forward_item, "forward", FALSE); } MIR_item_t MIR_new_bss (MIR_context_t ctx, const char *name, size_t len) { MIR_item_t tab_item, item = create_item (ctx, MIR_bss_item, "bss"); item->u.bss = malloc (sizeof (struct MIR_bss)); if (item->u.bss == NULL) { free (item); MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory for creation of bss %s", name); } if (name != NULL) name = get_ctx_str (ctx, name); item->u.bss->name = name; item->u.bss->len = len; if (name == NULL) { DLIST_APPEND (MIR_item_t, curr_module->items, item); } else if ((tab_item = add_item (ctx, item)) != item) { free (item); item = tab_item; } return item; } static MIR_type_t canon_type (MIR_type_t type) { #if defined(_WIN32) || __SIZEOF_LONG_DOUBLE__ == 8 if (type == MIR_T_LD) type = MIR_T_D; #endif return type; } size_t _MIR_type_size (MIR_context_t ctx, MIR_type_t type) { switch (type) { case MIR_T_I8: return sizeof (int8_t); case MIR_T_U8: return sizeof (uint8_t); case MIR_T_I16: return sizeof (int16_t); case MIR_T_U16: return sizeof (uint16_t); case MIR_T_I32: return sizeof (int32_t); case MIR_T_U32: return sizeof (uint32_t); case MIR_T_I64: return sizeof (int64_t); case MIR_T_U64: return sizeof (uint64_t); case MIR_T_F: return sizeof (float); case MIR_T_D: return sizeof (double); case MIR_T_LD: return sizeof (long double); case MIR_T_P: return sizeof (void *); default: mir_assert (FALSE); return 1; } } static int wrong_type_p (MIR_type_t type) { return type < MIR_T_I8 || type >= MIR_T_BLK; } MIR_item_t MIR_new_data (MIR_context_t ctx, const char *name, MIR_type_t el_type, size_t nel, const void *els) { MIR_item_t tab_item, item = create_item (ctx, MIR_data_item, "data"); MIR_data_t data; size_t el_len; if (wrong_type_p (el_type)) { free (item); MIR_get_error_func (ctx) (MIR_wrong_type_error, "wrong type in data %s", name); } el_len = _MIR_type_size (ctx, el_type); item->u.data = data = malloc (sizeof (struct MIR_data) + el_len * nel); if (data == NULL) { free (item); MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory for creation of data %s", name == NULL ? "" : name); } if (name != NULL) name = get_ctx_str (ctx, name); data->name = name; if (name == NULL) { DLIST_APPEND (MIR_item_t, curr_module->items, item); } else if ((tab_item = add_item (ctx, item)) != item) { free (item); item = tab_item; } data->el_type = canon_type (el_type); data->nel = nel; memcpy (data->u.els, els, el_len * nel); return item; } MIR_item_t MIR_new_string_data (MIR_context_t ctx, const char *name, MIR_str_t str) { return MIR_new_data (ctx, name, MIR_T_U8, str.len, str.s); } MIR_item_t MIR_new_ref_data (MIR_context_t ctx, const char *name, MIR_item_t ref_item, int64_t disp) { MIR_item_t tab_item, item = create_item (ctx, MIR_ref_data_item, "ref data"); MIR_ref_data_t ref_data; item->u.ref_data = ref_data = malloc (sizeof (struct MIR_ref_data)); if (ref_data == NULL) { free (item); MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory for creation of ref data %s", name == NULL ? "" : name); } if (name != NULL) name = get_ctx_str (ctx, name); ref_data->name = name; ref_data->ref_item = ref_item; ref_data->disp = disp; if (name == NULL) { DLIST_APPEND (MIR_item_t, curr_module->items, item); } else if ((tab_item = add_item (ctx, item)) != item) { free (item); item = tab_item; } return item; } MIR_item_t MIR_new_expr_data (MIR_context_t ctx, const char *name, MIR_item_t expr_item) { MIR_item_t tab_item, item = create_item (ctx, MIR_expr_data_item, "expr data"); MIR_expr_data_t expr_data; item->u.expr_data = expr_data = malloc (sizeof (struct MIR_expr_data)); if (expr_data == NULL) { free (item); MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory for creation of expr data %s", name == NULL ? "" : name); } mir_assert (expr_item != NULL); if (expr_item->item_type != MIR_func_item || expr_item->u.func->vararg_p || expr_item->u.func->nargs != 0 || expr_item->u.func->nres != 1) MIR_get_error_func ( ctx) (MIR_binary_io_error, "%s can not be an expr which should be non-argument, one result function", MIR_item_name (ctx, expr_item)); if (name != NULL) name = get_ctx_str (ctx, name); expr_data->name = name; expr_data->expr_item = expr_item; if (name == NULL) { DLIST_APPEND (MIR_item_t, curr_module->items, item); } else if ((tab_item = add_item (ctx, item)) != item) { free (item); item = tab_item; } return item; } static MIR_proto_t create_proto (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, size_t nargs, int vararg_p, MIR_var_t *args) { MIR_proto_t proto = malloc (sizeof (struct MIR_proto) + nres * sizeof (MIR_type_t)); if (proto == NULL) MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory for creation of proto %s", name); proto->name = get_ctx_str (ctx, name); proto->res_types = (MIR_type_t *) ((char *) proto + sizeof (struct MIR_proto)); if (nres != 0) memcpy (proto->res_types, res_types, nres * sizeof (MIR_type_t)); proto->nres = nres; proto->vararg_p = vararg_p != 0; VARR_CREATE (MIR_var_t, proto->args, nargs); for (size_t i = 0; i < nargs; i++) VARR_PUSH (MIR_var_t, proto->args, args[i]); return proto; } static MIR_item_t new_proto_arr (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, size_t nargs, int vararg_p, MIR_var_t *args) { MIR_item_t proto_item, tab_item; if (curr_module == NULL) MIR_get_error_func (ctx) (MIR_no_module_error, "Creating proto %s outside module", name); for (size_t i = 0; i < nres; i++) if (wrong_type_p (res_types[i])) MIR_get_error_func (ctx) (MIR_wrong_type_error, "wrong result type in proto %s", name); proto_item = create_item (ctx, MIR_proto_item, "proto"); proto_item->u.proto = create_proto (ctx, name, nres, res_types, nargs, vararg_p, args); tab_item = add_item (ctx, proto_item); mir_assert (tab_item == proto_item); return proto_item; } MIR_item_t MIR_new_proto_arr (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, size_t nargs, MIR_var_t *args) { return new_proto_arr (ctx, name, nres, res_types, nargs, FALSE, args); } MIR_item_t MIR_new_vararg_proto_arr (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, size_t nargs, MIR_var_t *args) { return new_proto_arr (ctx, name, nres, res_types, nargs, TRUE, args); } #if defined(_MSC_VER) #define alloca _alloca #endif static MIR_item_t new_proto (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, size_t nargs, int vararg_p, va_list argp) { MIR_var_t *args = alloca (nargs * sizeof (MIR_var_t)); size_t i; for (i = 0; i < nargs; i++) { args[i].type = va_arg (argp, MIR_type_t); args[i].name = va_arg (argp, const char *); } return new_proto_arr (ctx, name, nres, res_types, nargs, vararg_p, args); } MIR_item_t MIR_new_proto (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, size_t nargs, ...) { va_list argp; MIR_item_t proto_item; va_start (argp, nargs); proto_item = new_proto (ctx, name, nres, res_types, nargs, FALSE, argp); va_end (argp); return proto_item; } MIR_item_t MIR_new_vararg_proto (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, size_t nargs, ...) { va_list argp; MIR_item_t proto_item; va_start (argp, nargs); proto_item = new_proto (ctx, name, nres, res_types, nargs, TRUE, argp); va_end (argp); return proto_item; } static MIR_item_t new_func_arr (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, size_t nargs, int vararg_p, MIR_var_t *vars) { MIR_item_t func_item, tab_item; MIR_func_t func; if (curr_func != NULL) MIR_get_error_func (ctx) (MIR_nested_func_error, "Creating function when previous function %s is not finished", curr_func->name); if (nargs == 0 && vararg_p) MIR_get_error_func (ctx) (MIR_vararg_func_error, "Variable arg function %s w/o any mandatory argument", name); for (size_t i = 0; i < nres; i++) if (wrong_type_p (res_types[i])) MIR_get_error_func (ctx) (MIR_wrong_type_error, "wrong result type in func %s", name); func_item = create_item (ctx, MIR_func_item, "function"); curr_func = func_item->u.func = func = malloc (sizeof (struct MIR_func) + nres * sizeof (MIR_type_t)); if (func == NULL) { free (func_item); MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory for creation of func %s", name); } func->name = get_ctx_str (ctx, name); func->nres = nres; func->res_types = (MIR_type_t *) ((char *) func + sizeof (struct MIR_func)); for (size_t i = 0; i < nres; i++) func->res_types[i] = canon_type (res_types[i]); tab_item = add_item (ctx, func_item); mir_assert (tab_item == func_item); DLIST_INIT (MIR_insn_t, func->insns); DLIST_INIT (MIR_insn_t, func->original_insns); VARR_CREATE (MIR_var_t, func->vars, nargs + 8); func->nargs = nargs; func->last_temp_num = 0; func->vararg_p = vararg_p != 0; func->expr_p = FALSE; func->n_inlines = 0; func->machine_code = func->call_addr = NULL; func_regs_init (ctx, func); for (size_t i = 0; i < nargs; i++) { char *stored_name; MIR_type_t type = canon_type (vars[i].type); create_func_reg (ctx, func, vars[i].name, i + 1, type == MIR_T_F || type == MIR_T_D || type == MIR_T_LD ? type : MIR_T_I64, FALSE, &stored_name); vars[i].name = stored_name; VARR_PUSH (MIR_var_t, func->vars, vars[i]); } return func_item; } MIR_item_t MIR_new_func_arr (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, size_t nargs, MIR_var_t *vars) { return new_func_arr (ctx, name, nres, res_types, nargs, FALSE, vars); } MIR_item_t MIR_new_vararg_func_arr (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, size_t nargs, MIR_var_t *vars) { return new_func_arr (ctx, name, nres, res_types, nargs, TRUE, vars); } static MIR_item_t new_func (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, size_t nargs, int vararg_p, va_list argp) { size_t i; MIR_var_t *vars = alloca (sizeof (MIR_var_t) * nargs); for (i = 0; i < nargs; i++) { vars[i].type = va_arg (argp, MIR_type_t); vars[i].name = va_arg (argp, const char *); } return new_func_arr (ctx, name, nres, res_types, nargs, vararg_p, vars); } MIR_item_t MIR_new_func (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, size_t nargs, ...) { va_list argp; MIR_item_t func_item; va_start (argp, nargs); func_item = new_func (ctx, name, nres, res_types, nargs, FALSE, argp); va_end (argp); return func_item; } MIR_item_t MIR_new_vararg_func (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, size_t nargs, ...) { va_list argp; MIR_item_t func_item; va_start (argp, nargs); func_item = new_func (ctx, name, nres, res_types, nargs, TRUE, argp); va_end (argp); return func_item; } MIR_reg_t MIR_new_func_reg (MIR_context_t ctx, MIR_func_t func, MIR_type_t type, const char *name) { MIR_var_t var; MIR_reg_t res; char *stored_name; if (type != MIR_T_I64 && type != MIR_T_F && type != MIR_T_D && type != MIR_T_LD) MIR_get_error_func (ctx) (MIR_reg_type_error, "wrong type for register %s: got '%s'", name, type_str (ctx, type)); mir_assert (func != NULL); res = create_func_reg (ctx, func, name, VARR_LENGTH (MIR_var_t, func->vars) + 1, type, FALSE, &stored_name); var.type = type; var.name = stored_name; VARR_PUSH (MIR_var_t, func->vars, var); return res; } static reg_desc_t *find_rd_by_name (MIR_context_t ctx, const char *name, MIR_func_t func) { func_regs_t func_regs = func->internal; size_t rdn, temp_rdn; reg_desc_t rd; rd.name = (char *) name; rd.type = MIR_T_I64; rd.reg = 0; /* to eliminate warnings */ temp_rdn = VARR_LENGTH (reg_desc_t, func_regs->reg_descs); VARR_PUSH (reg_desc_t, func_regs->reg_descs, rd); if (!HTAB_DO (size_t, func_regs->name2rdn_tab, temp_rdn, HTAB_FIND, rdn)) { VARR_POP (reg_desc_t, func_regs->reg_descs); return NULL; /* undeclared */ } VARR_POP (reg_desc_t, func_regs->reg_descs); return &VARR_ADDR (reg_desc_t, func_regs->reg_descs)[rdn]; } static reg_desc_t *find_rd_by_reg (MIR_context_t ctx, MIR_reg_t reg, MIR_func_t func) { func_regs_t func_regs = func->internal; size_t rdn, temp_rdn; reg_desc_t rd; rd.reg = reg; rd.name = NULL; rd.type = MIR_T_I64; /* to eliminate warnings */ temp_rdn = VARR_LENGTH (reg_desc_t, func_regs->reg_descs); VARR_PUSH (reg_desc_t, func_regs->reg_descs, rd); if (!HTAB_DO (size_t, func_regs->reg2rdn_tab, temp_rdn, HTAB_FIND, rdn)) { VARR_POP (reg_desc_t, func_regs->reg_descs); MIR_get_error_func (ctx) (MIR_undeclared_func_reg_error, "undeclared reg %u of func %s", reg, func->name); } VARR_POP (reg_desc_t, func_regs->reg_descs); return &VARR_ADDR (reg_desc_t, func_regs->reg_descs)[rdn]; } void MIR_finish_func (MIR_context_t ctx) { int expr_p = TRUE; MIR_insn_t insn; MIR_insn_code_t code; const char *func_name; if (curr_func == NULL) MIR_get_error_func (ctx) (MIR_no_func_error, "finish of non-existing function"); func_name = curr_func->name; if (curr_func->vararg_p || curr_func->nargs != 0 || curr_func->nres != 1) expr_p = FALSE; for (insn = DLIST_HEAD (MIR_insn_t, curr_func->insns); insn != NULL; insn = DLIST_NEXT (MIR_insn_t, insn)) { size_t i, actual_nops = MIR_insn_nops (ctx, insn); MIR_op_mode_t mode, expected_mode; reg_desc_t *rd; int out_p, can_be_out_p; code = insn->code; if (code == MIR_PHI) { curr_func = NULL; MIR_get_error_func (ctx) (MIR_vararg_func_error, "phi can be used only internally"); } else if (!curr_func->vararg_p && code == MIR_VA_START) { curr_func = NULL; MIR_get_error_func (ctx) (MIR_vararg_func_error, "va_start is not in vararg function"); } else if (code == MIR_RET && actual_nops != curr_func->nres) { int nres = curr_func->nres; curr_func = NULL; MIR_get_error_func ( ctx) (MIR_vararg_func_error, "func %s: in instruction '%s': number of operands in return does not " "correspond number of function returns. Expected %d, got %d", func_name, insn_descs[code].name, nres, actual_nops); } else if (MIR_call_code_p (code)) expr_p = FALSE; for (i = 0; i < actual_nops; i++) { if (code == MIR_UNSPEC && i == 0) { mir_assert (insn->ops[i].mode == MIR_OP_INT); continue; } else if (MIR_call_code_p (code)) { if (i == 0) { mir_assert (insn->ops[i].mode == MIR_OP_REF && insn->ops[i].u.ref->item_type == MIR_proto_item); continue; /* We checked the operand during insn creation -- skip the prototype */ } else if (i == 1 && insn->ops[i].mode == MIR_OP_REF) { mir_assert (insn->ops[i].u.ref->item_type == MIR_import_item || insn->ops[i].u.ref->item_type == MIR_export_item || insn->ops[i].u.ref->item_type == MIR_forward_item || insn->ops[i].u.ref->item_type == MIR_func_item); continue; /* We checked the operand during insn creation -- skip the func */ } } if (code == MIR_VA_ARG && i == 2) { mir_assert (insn->ops[i].mode == MIR_OP_MEM); continue; /* We checked the operand during insn creation -- skip va_arg type */ } if (code == MIR_SWITCH) { out_p = FALSE; expected_mode = i == 0 ? MIR_OP_INT : MIR_OP_LABEL; } else if (code != MIR_RET) { expected_mode = MIR_insn_op_mode (ctx, insn, i, &out_p); } else { out_p = FALSE; expected_mode = type2mode (curr_func->res_types[i]); } can_be_out_p = TRUE; switch (insn->ops[i].mode) { case MIR_OP_REG: rd = find_rd_by_reg (ctx, insn->ops[i].u.reg, curr_func); mir_assert (rd != NULL && insn->ops[i].u.reg == rd->reg); mode = type2mode (rd->type); break; case MIR_OP_MEM: expr_p = FALSE; if (wrong_type_p (insn->ops[i].u.mem.type) && (!MIR_all_blk_type_p (insn->ops[i].u.mem.type) || !MIR_call_code_p (code))) { curr_func = NULL; MIR_get_error_func (ctx) (MIR_wrong_type_error, "func %s: in instruction '%s': wrong type memory", func_name, insn_descs[code].name); } if (MIR_all_blk_type_p (insn->ops[i].u.mem.type) && insn->ops[i].u.mem.disp < 0) { curr_func = NULL; MIR_get_error_func (ctx) (MIR_wrong_type_error, "func %s: in instruction '%s': block type memory with disp < 0", func_name, insn_descs[code].name); } if (insn->ops[i].u.mem.base != 0) { rd = find_rd_by_reg (ctx, insn->ops[i].u.mem.base, curr_func); mir_assert (rd != NULL && insn->ops[i].u.mem.base == rd->reg); if (type2mode (rd->type) != MIR_OP_INT) { curr_func = NULL; MIR_get_error_func ( ctx) (MIR_reg_type_error, "func %s: in instruction '%s': base reg of non-integer type for operand " "#%d", func_name, insn_descs[code].name, i + 1); } } if (insn->ops[i].u.mem.index != 0) { rd = find_rd_by_reg (ctx, insn->ops[i].u.mem.index, curr_func); mir_assert (rd != NULL && insn->ops[i].u.mem.index == rd->reg); if (type2mode (rd->type) != MIR_OP_INT) { curr_func = NULL; MIR_get_error_func ( ctx) (MIR_reg_type_error, "func %s: in instruction '%s': index reg of non-integer type for " "operand #%d", func_name, insn_descs[code].name, i + 1); } } mode = type2mode (insn->ops[i].u.mem.type); break; case MIR_OP_HARD_REG: case MIR_OP_HARD_REG_MEM: expr_p = FALSE; mode = expected_mode; mir_assert (FALSE); /* Should not be here */ break; default: can_be_out_p = FALSE; mode = insn->ops[i].mode; if (mode == MIR_OP_REF || mode == MIR_OP_STR) mode = MIR_OP_INT; /* just an address */ break; } insn->ops[i].value_mode = mode; if (mode == MIR_OP_UNDEF && insn->ops[i].mode == MIR_OP_MEM && ((code == MIR_VA_START && i == 0) || ((code == MIR_VA_ARG || code == MIR_VA_BLOCK_ARG) && i == 1) || (code == MIR_VA_END && i == 1))) { /* a special case: va_list as undef type mem */ insn->ops[i].value_mode = expected_mode; } else if (expected_mode != MIR_OP_UNDEF && (mode == MIR_OP_UINT ? MIR_OP_INT : mode) != expected_mode) { curr_func = NULL; MIR_get_error_func ( ctx) (MIR_op_mode_error, "func %s: in instruction '%s': unexpected operand mode for operand #%d. Got " "'%s', expected '%s'", func_name, insn_descs[code].name, i + 1, mode_str (mode), mode_str (expected_mode)); } if (out_p && !can_be_out_p) { curr_func = NULL; MIR_get_error_func (ctx) (MIR_out_op_error, "func %s; in instruction '%s': wrong operand #%d for insn output", func_name, insn_descs[code].name, i + 1); } } } curr_func->expr_p = expr_p; curr_func = NULL; } void MIR_finish_module (MIR_context_t ctx) { if (curr_module == NULL) MIR_get_error_func (ctx) (MIR_no_module_error, "finish of non-existing module"); curr_module = NULL; } static void setup_global (MIR_context_t ctx, const char *name, void *addr, MIR_item_t def) { MIR_item_t item, tab_item; MIR_module_t saved = curr_module; curr_module = &environment_module; /* Use import for proto representation: */ item = new_export_import_forward (ctx, name, MIR_import_item, "import", TRUE); if ((tab_item = item_tab_find (ctx, MIR_item_name (ctx, item), &environment_module)) != item && tab_item != NULL) { free (item); } else { HTAB_DO (MIR_item_t, module_item_tab, item, HTAB_INSERT, tab_item); DLIST_APPEND (MIR_item_t, environment_module.items, item); tab_item = item; } tab_item->addr = addr; tab_item->ref_def = def; curr_module = saved; } static void undefined_interface (MIR_context_t ctx) { MIR_get_error_func (ctx) (MIR_call_op_error, "undefined call interface"); } static MIR_item_t load_bss_data_section (MIR_context_t ctx, MIR_item_t item, int first_only_p) { const char *name; MIR_item_t curr_item, last_item, expr_item; size_t len, section_size = 0; uint8_t *addr; if (item->addr == NULL) { /* Calculate section size: */ for (curr_item = item; curr_item != NULL && curr_item->addr == NULL; curr_item = first_only_p ? NULL : DLIST_NEXT (MIR_item_t, curr_item)) if (curr_item->item_type == MIR_bss_item && (curr_item == item || curr_item->u.bss->name == NULL)) section_size += curr_item->u.bss->len; else if (curr_item->item_type == MIR_data_item && (curr_item == item || curr_item->u.data->name == NULL)) section_size += (curr_item->u.data->nel * _MIR_type_size (ctx, curr_item->u.data->el_type)); else if (curr_item->item_type == MIR_ref_data_item && (curr_item == item || curr_item->u.ref_data->name == NULL)) section_size += _MIR_type_size (ctx, MIR_T_P); else if (curr_item->item_type == MIR_expr_data_item && (curr_item == item || curr_item->u.expr_data->name == NULL)) { expr_item = curr_item->u.expr_data->expr_item; if (expr_item->item_type != MIR_func_item || !expr_item->u.func->expr_p || expr_item->u.func->nres != 1) MIR_get_error_func ( ctx) (MIR_binary_io_error, "%s can not be an expr which should be a func w/o calls and memory ops", MIR_item_name (ctx, expr_item)); section_size += _MIR_type_size (ctx, expr_item->u.func->res_types[0]); } else break; if ((item->addr = malloc (section_size)) == NULL) { name = MIR_item_name (ctx, item); MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory to allocate data/bss %s", name == NULL ? "" : name); } item->section_head_p = TRUE; } /* Set up section memory: */ for (last_item = item, curr_item = item, addr = item->addr; curr_item != NULL && (curr_item == item || curr_item->addr == NULL); last_item = curr_item, curr_item = first_only_p ? NULL : DLIST_NEXT (MIR_item_t, curr_item)) if (curr_item->item_type == MIR_bss_item && (curr_item == item || curr_item->u.bss->name == NULL)) { memset (addr, 0, curr_item->u.bss->len); curr_item->addr = addr; addr += curr_item->u.bss->len; } else if (curr_item->item_type == MIR_data_item && (curr_item == item || curr_item->u.data->name == NULL)) { len = curr_item->u.data->nel * _MIR_type_size (ctx, curr_item->u.data->el_type); memmove (addr, curr_item->u.data->u.els, len); curr_item->addr = addr; addr += len; } else if (curr_item->item_type == MIR_ref_data_item && (curr_item == item || curr_item->u.ref_data->name == NULL)) { curr_item->u.ref_data->load_addr = addr; curr_item->addr = addr; addr += _MIR_type_size (ctx, MIR_T_P); } else if (curr_item->item_type == MIR_expr_data_item && (curr_item == item || curr_item->u.expr_data->name == NULL)) { expr_item = curr_item->u.expr_data->expr_item; len = _MIR_type_size (ctx, expr_item->u.func->res_types[0]); curr_item->u.expr_data->load_addr = addr; curr_item->addr = addr; addr += len; } else { break; } return last_item; } void MIR_load_module (MIR_context_t ctx, MIR_module_t m) { mir_assert (m != NULL); for (MIR_item_t item = DLIST_HEAD (MIR_item_t, m->items); item != NULL; item = DLIST_NEXT (MIR_item_t, item)) { MIR_item_t first_item = item; if (item->item_type == MIR_bss_item || item->item_type == MIR_data_item || item->item_type == MIR_ref_data_item || item->item_type == MIR_expr_data_item) { item = load_bss_data_section (ctx, item, FALSE); } else if (item->item_type == MIR_func_item) { if (item->addr == NULL) { item->addr = _MIR_get_thunk (ctx); #if defined(MIR_DEBUG) fprintf (stderr, "%016llx: %s\n", (unsigned long long) item->addr, item->u.func->name); #endif } _MIR_redirect_thunk (ctx, item->addr, undefined_interface); } if (first_item->export_p) { /* update global item table */ mir_assert (first_item->item_type != MIR_export_item && first_item->item_type != MIR_import_item && first_item->item_type != MIR_forward_item); setup_global (ctx, MIR_item_name (ctx, first_item), first_item->addr, first_item); } } VARR_PUSH (MIR_module_t, modules_to_link, m); } #define SETJMP_NAME "setjmp" #define SETJMP_NAME2 "_setjmp" void MIR_load_external (MIR_context_t ctx, const char *name, void *addr) { if (strcmp (name, SETJMP_NAME) == 0 || (SETJMP_NAME2 != NULL && strcmp (name, SETJMP_NAME2) == 0)) setjmp_addr = addr; setup_global (ctx, name, addr, NULL); } static int simplify_func (MIR_context_t ctx, MIR_item_t func_item, int mem_float_p); static void process_inlines (MIR_context_t ctx, MIR_item_t func_item); void MIR_link (MIR_context_t ctx, void (*set_interface) (MIR_context_t ctx, MIR_item_t item), void *import_resolver (const char *)) { MIR_item_t item, tab_item, expr_item; MIR_type_t type; MIR_val_t res; MIR_module_t m; void *addr; union { int8_t i8; int16_t i16; int32_t i32; int64_t i64; float f; double d; long double ld; void *a; } v; for (size_t i = 0; i < VARR_LENGTH (MIR_module_t, modules_to_link); i++) { m = VARR_GET (MIR_module_t, modules_to_link, i); for (item = DLIST_HEAD (MIR_item_t, m->items); item != NULL; item = DLIST_NEXT (MIR_item_t, item)) if (item->item_type == MIR_func_item) { assert (item->data == NULL); if (simplify_func (ctx, item, TRUE)) item->data = (void *) 1; /* flag inlining */ } else if (item->item_type == MIR_import_item) { if ((tab_item = item_tab_find (ctx, item->u.import_id, &environment_module)) == NULL) { if (import_resolver == NULL || (addr = import_resolver (item->u.import_id)) == NULL) MIR_get_error_func (ctx) (MIR_undeclared_op_ref_error, "import of undefined item %s", item->u.import_id); MIR_load_external (ctx, item->u.import_id, addr); tab_item = item_tab_find (ctx, item->u.import_id, &environment_module); mir_assert (tab_item != NULL); } item->addr = tab_item->addr; item->ref_def = tab_item; } else if (item->item_type == MIR_export_item) { if ((tab_item = item_tab_find (ctx, item->u.export_id, m)) == NULL) MIR_get_error_func (ctx) (MIR_undeclared_op_ref_error, "export of undefined item %s", item->u.export_id); item->addr = tab_item->addr; item->ref_def = tab_item; } else if (item->item_type == MIR_forward_item) { if ((tab_item = item_tab_find (ctx, item->u.forward_id, m)) == NULL) MIR_get_error_func (ctx) (MIR_undeclared_op_ref_error, "forward of undefined item %s", item->u.forward_id); item->addr = tab_item->addr; item->ref_def = tab_item; } } for (size_t i = 0; i < VARR_LENGTH (MIR_module_t, modules_to_link); i++) { m = VARR_GET (MIR_module_t, modules_to_link, i); for (item = DLIST_HEAD (MIR_item_t, m->items); item != NULL; item = DLIST_NEXT (MIR_item_t, item)) { if (item->item_type == MIR_func_item && item->data != NULL) { process_inlines (ctx, item); item->data = NULL; #if 0 fprintf (stderr, "+++++ Function after inlining:\n"); MIR_output_item (ctx, stderr, item); #endif } else if (item->item_type == MIR_ref_data_item) { assert (item->u.ref_data->ref_item->addr != NULL); addr = (char *) item->u.ref_data->ref_item->addr + item->u.ref_data->disp; memcpy (item->u.ref_data->load_addr, &addr, _MIR_type_size (ctx, MIR_T_P)); continue; } if (item->item_type != MIR_expr_data_item) continue; expr_item = item->u.expr_data->expr_item; MIR_interp (ctx, expr_item, &res, 0); type = expr_item->u.func->res_types[0]; switch (type) { case MIR_T_I8: case MIR_T_U8: v.i8 = (int8_t) res.i; break; case MIR_T_I16: case MIR_T_U16: v.i16 = (int16_t) res.i; break; case MIR_T_I32: case MIR_T_U32: v.i32 = (int32_t) res.i; break; case MIR_T_I64: case MIR_T_U64: v.i64 = (int64_t) res.i; break; case MIR_T_F: v.f = res.f; break; case MIR_T_D: v.d = res.d; break; case MIR_T_LD: v.ld = res.ld; break; case MIR_T_P: v.a = res.a; break; default: assert (FALSE); break; } memcpy (item->u.expr_data->load_addr, &v, _MIR_type_size (ctx, expr_item->u.func->res_types[0])); } } if (set_interface != NULL) { while (VARR_LENGTH (MIR_module_t, modules_to_link) != 0) { m = VARR_POP (MIR_module_t, modules_to_link); for (item = DLIST_HEAD (MIR_item_t, m->items); item != NULL; item = DLIST_NEXT (MIR_item_t, item)) if (item->item_type == MIR_func_item) { finish_func_interpretation (item); /* in case if it was used for expr data */ set_interface (ctx, item); } } set_interface (ctx, NULL); /* finish interface setting */ } } static const char *insn_name (MIR_insn_code_t code) { return (unsigned) code >= MIR_INSN_BOUND ? "" : insn_descs[code].name; } const char *MIR_insn_name (MIR_context_t ctx, MIR_insn_code_t code) { if ((unsigned) code >= MIR_INSN_BOUND) MIR_get_error_func (ctx) (MIR_wrong_param_value_error, "MIR_insn_name: wrong insn code %d", (int) code); return insn_descs[code].name; } static size_t insn_code_nops (MIR_context_t ctx, MIR_insn_code_t code) { /* 0 for calls */ if ((unsigned) code >= MIR_INSN_BOUND) MIR_get_error_func (ctx) (MIR_wrong_param_value_error, "insn_code_nops: wrong insn code %d", (int) code); return VARR_GET (size_t, insn_nops, code); } size_t MIR_insn_nops (MIR_context_t ctx, MIR_insn_t insn) { mir_assert (insn != NULL); return insn->nops; } MIR_op_mode_t _MIR_insn_code_op_mode (MIR_context_t ctx, MIR_insn_code_t code, size_t nop, int *out_p) { unsigned mode; mir_assert (out_p != NULL); if (nop >= insn_code_nops (ctx, code)) return MIR_OP_BOUND; mode = insn_descs[code].op_modes[nop]; mir_assert (out_p != NULL); *out_p = (mode & OUTPUT_FLAG) != 0; return *out_p ? mode ^ OUTPUT_FLAG : mode; } MIR_op_mode_t MIR_insn_op_mode (MIR_context_t ctx, MIR_insn_t insn, size_t nop, int *out_p) { MIR_insn_code_t code = insn->code; size_t nargs, nops = MIR_insn_nops (ctx, insn); unsigned mode; *out_p = FALSE; /* to remove unitialized warning */ if (nop >= nops) return MIR_OP_BOUND; mir_assert (out_p != NULL); if (code == MIR_RET || code == MIR_SWITCH) { *out_p = FALSE; /* should be already checked in MIR_finish_func */ return nop == 0 && code == MIR_SWITCH ? MIR_OP_INT : insn->ops[nop].mode; } else if (code == MIR_PHI) { *out_p = nop == 0; return insn->ops[nop].mode; } else if (MIR_call_code_p (code) || code == MIR_UNSPEC) { MIR_op_t proto_op; MIR_proto_t proto; size_t args_start; if (code == MIR_UNSPEC) { args_start = 1; mir_assert (insn->ops[0].mode == MIR_OP_INT); mir_assert (insn->ops[0].u.u < VARR_LENGTH (MIR_proto_t, unspec_protos)); proto = VARR_GET (MIR_proto_t, unspec_protos, insn->ops[0].u.u); } else { args_start = 2; proto_op = insn->ops[0]; mir_assert (proto_op.mode == MIR_OP_REF && proto_op.u.ref->item_type == MIR_proto_item); proto = proto_op.u.ref->u.proto; } *out_p = args_start <= nop && nop < proto->nres + args_start; nargs = proto->nres + args_start + (proto->args == NULL ? 0 : VARR_LENGTH (MIR_var_t, proto->args)); if (proto->vararg_p && nop >= nargs) return MIR_OP_UNDEF; /* unknown */ mir_assert (nops >= nargs && (proto->vararg_p || nops == nargs)); if (nop == 0) return insn->ops[nop].mode; if (nop == 1 && code != MIR_UNSPEC) return MIR_OP_INT; /* call func addr */ if (args_start <= nop && nop < proto->nres + args_start) return type2mode (proto->res_types[nop - args_start]); return type2mode (VARR_GET (MIR_var_t, proto->args, nop - args_start - proto->nres).type); } mode = insn_descs[code].op_modes[nop]; *out_p = (mode & OUTPUT_FLAG) != 0; return *out_p ? mode ^ OUTPUT_FLAG : mode; } static MIR_insn_t create_insn (MIR_context_t ctx, size_t nops, MIR_insn_code_t code) { MIR_insn_t insn; if (nops == 0) nops = 1; insn = malloc (sizeof (struct MIR_insn) + sizeof (MIR_op_t) * (nops - 1)); if (insn == NULL) MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory for insn creation"); #if defined(_WIN32) || __SIZEOF_LONG_DOUBLE__ == 8 switch (code) { case MIR_LDMOV: code = MIR_DMOV; break; case MIR_I2LD: code = MIR_I2D; break; case MIR_UI2LD: code = MIR_UI2D; break; case MIR_LD2I: code = MIR_D2I; break; case MIR_F2LD: code = MIR_F2D; break; case MIR_D2LD: code = MIR_DMOV; break; case MIR_LD2F: code = MIR_D2F; break; case MIR_LD2D: code = MIR_DMOV; break; case MIR_LDNEG: code = MIR_DNEG; break; case MIR_LDADD: code = MIR_DADD; break; case MIR_LDSUB: code = MIR_DSUB; break; case MIR_LDMUL: code = MIR_DMUL; break; case MIR_LDDIV: code = MIR_DDIV; break; case MIR_LDEQ: code = MIR_DEQ; break; case MIR_LDNE: code = MIR_DNE; break; case MIR_LDLT: code = MIR_DLT; break; case MIR_LDLE: code = MIR_DLE; break; case MIR_LDGT: code = MIR_DGT; break; case MIR_LDGE: code = MIR_DGE; break; case MIR_LDBEQ: code = MIR_DBEQ; break; case MIR_LDBNE: code = MIR_DBNE; break; case MIR_LDBLT: code = MIR_DBLT; break; case MIR_LDBLE: code = MIR_DBLE; break; case MIR_LDBGT: code = MIR_DBGT; break; case MIR_LDBGE: code = MIR_DBGE; break; } #endif insn->code = code; insn->data = NULL; return insn; } static MIR_insn_t new_insn1 (MIR_context_t ctx, MIR_insn_code_t code) { return create_insn (ctx, 1, code); } MIR_insn_t MIR_new_insn_arr (MIR_context_t ctx, MIR_insn_code_t code, size_t nops, MIR_op_t *ops) { MIR_insn_t insn; MIR_proto_t proto; size_t args_start, narg, i = 0, expected_nops = insn_code_nops (ctx, code); mir_assert (ops != NULL); if (!MIR_call_code_p (code) && code != MIR_UNSPEC && code != MIR_PHI && code != MIR_RET && code != MIR_SWITCH && nops != expected_nops) { MIR_get_error_func (ctx) (MIR_ops_num_error, "wrong number of operands for insn %s", insn_descs[code].name); } else if (code == MIR_SWITCH) { if (nops < 2) MIR_get_error_func (ctx) (MIR_ops_num_error, "number of MIR_SWITCH operands is less 2"); } else if (code == MIR_PHI) { if (nops < 3) MIR_get_error_func (ctx) (MIR_ops_num_error, "number of MIR_PHI operands is less 3"); } else if (MIR_call_code_p (code) || code == MIR_UNSPEC) { args_start = code == MIR_UNSPEC ? 1 : 2; if (nops < args_start) MIR_get_error_func (ctx) (MIR_ops_num_error, "wrong number of call/unspec operands"); if (code == MIR_UNSPEC) { if (ops[0].mode != MIR_OP_INT || ops[0].u.u >= VARR_LENGTH (MIR_proto_t, unspec_protos)) MIR_get_error_func (ctx) (MIR_unspec_op_error, "the 1st unspec operand should be valid unspec code"); proto = VARR_GET (MIR_proto_t, unspec_protos, ops[0].u.u); } else { if (ops[0].mode != MIR_OP_REF || ops[0].u.ref->item_type != MIR_proto_item) MIR_get_error_func (ctx) (MIR_call_op_error, "the 1st call operand should be a prototype"); proto = ops[0].u.ref->u.proto; } i = proto->nres; if (proto->args != NULL) i += VARR_LENGTH (MIR_var_t, proto->args); if (nops < i + args_start || (nops != i + args_start && !proto->vararg_p)) MIR_get_error_func ( ctx) (code == MIR_UNSPEC ? MIR_unspec_op_error : MIR_call_op_error, "number of %s operands or results does not correspond to prototype %s", code == MIR_UNSPEC ? "unspec" : "call", proto->name); for (i = args_start; i < nops; i++) { if (ops[i].mode == MIR_OP_MEM && MIR_all_blk_type_p (ops[i].u.mem.type)) { if (i - args_start < proto->nres) MIR_get_error_func (ctx) (MIR_wrong_type_error, "result of %s is block type memory", code == MIR_UNSPEC ? "unspec" : "call"); else if ((narg = i - args_start - proto->nres) < VARR_LENGTH (MIR_var_t, proto->args)) { if (VARR_GET (MIR_var_t, proto->args, narg).type != ops[i].u.mem.type) { MIR_get_error_func ( ctx) (MIR_wrong_type_error, "arg of %s is block type memory but param is not of block type", code == MIR_UNSPEC ? "unspec" : "call"); } else if (VARR_GET (MIR_var_t, proto->args, narg).size != ops[i].u.mem.disp) { MIR_get_error_func ( ctx) (MIR_wrong_type_error, "different sizes (%lu vs %lu) of arg and param block memory in %s insn", (unsigned long) VARR_GET (MIR_var_t, proto->args, narg).size, (unsigned long) ops[i].u.mem.disp, code == MIR_UNSPEC ? "unspec" : "call"); } } else if (ops[i].u.mem.type == MIR_T_RBLK) { MIR_get_error_func (ctx) (MIR_wrong_type_error, "RBLK memory can not correspond to unnamed param in %s insn", code == MIR_UNSPEC ? "unspec" : "call"); } } else if (i - args_start >= proto->nres && (narg = i - args_start - proto->nres) < VARR_LENGTH (MIR_var_t, proto->args) && MIR_all_blk_type_p (VARR_GET (MIR_var_t, proto->args, narg).type)) { MIR_get_error_func ( ctx) (MIR_wrong_type_error, "param of %s is of block type but arg is not of block type memory", code == MIR_UNSPEC ? "unspec" : "call"); } } } else if (code == MIR_VA_ARG) { // undef mem ??? if (ops[2].mode != MIR_OP_MEM) MIR_get_error_func (ctx) (MIR_op_mode_error, "3rd operand of va_arg should be any memory with given type"); } insn = create_insn (ctx, nops, code); insn->nops = nops; for (i = 0; i < nops; i++) insn->ops[i] = ops[i]; return insn; } static MIR_insn_t new_insn (MIR_context_t ctx, MIR_insn_code_t code, size_t nops, va_list argp) { MIR_op_t *insn_ops = alloca (sizeof (MIR_op_t) * nops); for (size_t i = 0; i < nops; i++) insn_ops[i] = va_arg (argp, MIR_op_t); va_end (argp); return MIR_new_insn_arr (ctx, code, nops, insn_ops); } MIR_insn_t MIR_new_insn (MIR_context_t ctx, MIR_insn_code_t code, ...) { va_list argp; size_t nops = insn_code_nops (ctx, code); if (code == MIR_PHI) MIR_get_error_func (ctx) (MIR_call_op_error, "Use only MIR_new_insn_arr for creating a phi insn"); else if (MIR_call_code_p (code) || code == MIR_UNSPEC || code == MIR_RET || code == MIR_SWITCH) MIR_get_error_func ( ctx) (MIR_call_op_error, "Use only MIR_new_insn_arr or MIR_new_{call,unspec,ret}_insn for creating a " "call/unspec/ret/switch insn"); va_start (argp, code); return new_insn (ctx, code, nops, argp); } MIR_insn_t MIR_new_call_insn (MIR_context_t ctx, size_t nops, ...) { va_list argp; va_start (argp, nops); return new_insn (ctx, MIR_CALL, nops, argp); } MIR_insn_t MIR_new_ret_insn (MIR_context_t ctx, size_t nops, ...) { va_list argp; va_start (argp, nops); return new_insn (ctx, MIR_RET, nops, argp); } MIR_insn_t _MIR_new_unspec_insn (MIR_context_t ctx, size_t nops, ...) { va_list argp; va_start (argp, nops); return new_insn (ctx, MIR_UNSPEC, nops, argp); } void _MIR_register_unspec_insn (MIR_context_t ctx, uint64_t code, const char *name, size_t nres, MIR_type_t *res_types, size_t nargs, int vararg_p, MIR_var_t *args) { MIR_proto_t proto; while (VARR_LENGTH (MIR_proto_t, unspec_protos) <= code) VARR_PUSH (MIR_proto_t, unspec_protos, NULL); if ((proto = VARR_GET (MIR_proto_t, unspec_protos, code)) == NULL) { VARR_SET (MIR_proto_t, unspec_protos, code, create_proto (ctx, name, nres, res_types, nargs, vararg_p, args)); } else { assert (strcmp (proto->name, name) == 0); } } MIR_insn_t MIR_copy_insn (MIR_context_t ctx, MIR_insn_t insn) { size_t size; mir_assert (insn != NULL); size = sizeof (struct MIR_insn) + sizeof (MIR_op_t) * (insn->nops == 0 ? 0 : insn->nops - 1); MIR_insn_t new_insn = malloc (size); if (new_insn == NULL) MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory to copy insn %s", insn_name (insn->code)); memcpy (new_insn, insn, size); return new_insn; } static MIR_insn_t create_label (MIR_context_t ctx, int64_t label_num) { MIR_insn_t insn = new_insn1 (ctx, MIR_LABEL); insn->ops[0] = MIR_new_int_op (ctx, label_num); insn->nops = 0; return insn; } MIR_insn_t MIR_new_label (MIR_context_t ctx) { return create_label (ctx, ++curr_label_num); } MIR_reg_t _MIR_new_temp_reg (MIR_context_t ctx, MIR_type_t type, MIR_func_t func) { char reg_name[30]; if (type != MIR_T_I64 && type != MIR_T_F && type != MIR_T_D && type != MIR_T_LD) MIR_get_error_func (ctx) (MIR_reg_type_error, "wrong type %s for temporary register", type_str (ctx, type)); mir_assert (func != NULL); for (;;) { func->last_temp_num++; if (func->last_temp_num == 0) MIR_get_error_func (ctx) (MIR_unique_reg_error, "out of unique regs"); sprintf (reg_name, "%s%d", TEMP_REG_NAME_PREFIX, func->last_temp_num); if (find_rd_by_name (ctx, reg_name, func) == NULL) return MIR_new_func_reg (ctx, func, type, reg_name); } } static reg_desc_t *get_func_rd_by_name (MIR_context_t ctx, const char *reg_name, MIR_func_t func) { reg_desc_t *rd; rd = find_rd_by_name (ctx, reg_name, func); if (rd == NULL) MIR_get_error_func (ctx) (MIR_undeclared_func_reg_error, "undeclared func reg %s", reg_name); return rd; } static reg_desc_t *get_func_rd_by_reg (MIR_context_t ctx, MIR_reg_t reg, MIR_func_t func) { reg_desc_t *rd; rd = find_rd_by_reg (ctx, reg, func); return rd; } MIR_reg_t MIR_reg (MIR_context_t ctx, const char *reg_name, MIR_func_t func) { return get_func_rd_by_name (ctx, reg_name, func)->reg; } MIR_type_t MIR_reg_type (MIR_context_t ctx, MIR_reg_t reg, MIR_func_t func) { return get_func_rd_by_reg (ctx, reg, func)->type; } const char *MIR_reg_name (MIR_context_t ctx, MIR_reg_t reg, MIR_func_t func) { return get_func_rd_by_reg (ctx, reg, func)->name; } /* Functions to create operands. */ static void init_op (MIR_op_t *op, MIR_op_mode_t mode) { op->mode = mode; op->data = NULL; } MIR_op_t MIR_new_reg_op (MIR_context_t ctx, MIR_reg_t reg) { MIR_op_t op; init_op (&op, MIR_OP_REG); op.u.reg = reg; return op; } MIR_op_t _MIR_new_hard_reg_op (MIR_context_t ctx, MIR_reg_t hard_reg) { /* used only internally */ MIR_op_t op; init_op (&op, MIR_OP_HARD_REG); op.u.hard_reg = hard_reg; return op; } MIR_op_t MIR_new_int_op (MIR_context_t ctx, int64_t i) { MIR_op_t op; init_op (&op, MIR_OP_INT); op.u.i = i; return op; } MIR_op_t MIR_new_uint_op (MIR_context_t ctx, uint64_t u) { MIR_op_t op; init_op (&op, MIR_OP_UINT); op.u.u = u; return op; } MIR_op_t MIR_new_float_op (MIR_context_t ctx, float f) { MIR_op_t op; mir_assert (sizeof (float) == 4); /* IEEE single */ init_op (&op, MIR_OP_FLOAT); op.u.f = f; return op; } MIR_op_t MIR_new_double_op (MIR_context_t ctx, double d) { MIR_op_t op; mir_assert (sizeof (double) == 8); /* IEEE double */ init_op (&op, MIR_OP_DOUBLE); op.u.d = d; return op; } MIR_op_t MIR_new_ldouble_op (MIR_context_t ctx, long double ld) { MIR_op_t op; #if defined(_WIN32) || __SIZEOF_LONG_DOUBLE__ == 8 return MIR_new_double_op (ctx, ld); #endif mir_assert (sizeof (long double) == 16); /* machine-defined 80- or 128-bit FP */ init_op (&op, MIR_OP_LDOUBLE); op.u.ld = ld; return op; } MIR_op_t MIR_new_ref_op (MIR_context_t ctx, MIR_item_t item) { MIR_op_t op; init_op (&op, MIR_OP_REF); op.u.ref = item; return op; } MIR_op_t MIR_new_str_op (MIR_context_t ctx, MIR_str_t str) { MIR_op_t op; init_op (&op, MIR_OP_STR); op.u.str = get_ctx_string (ctx, str).str; return op; } MIR_op_t MIR_new_mem_op (MIR_context_t ctx, MIR_type_t type, MIR_disp_t disp, MIR_reg_t base, MIR_reg_t index, MIR_scale_t scale) { MIR_op_t op; init_op (&op, MIR_OP_MEM); op.u.mem.type = canon_type (type); op.u.mem.disp = disp; op.u.mem.base = base; op.u.mem.index = index; op.u.mem.scale = scale; return op; } MIR_op_t _MIR_new_hard_reg_mem_op (MIR_context_t ctx, MIR_type_t type, MIR_disp_t disp, MIR_reg_t base, MIR_reg_t index, MIR_scale_t scale) { MIR_op_t op; init_op (&op, MIR_OP_HARD_REG_MEM); op.u.hard_reg_mem.type = type; op.u.hard_reg_mem.disp = disp; op.u.hard_reg_mem.base = base; op.u.hard_reg_mem.index = index; op.u.hard_reg_mem.scale = scale; return op; } MIR_op_t MIR_new_label_op (MIR_context_t ctx, MIR_label_t label) { MIR_op_t op; init_op (&op, MIR_OP_LABEL); op.u.label = label; return op; } int MIR_op_eq_p (MIR_context_t ctx, MIR_op_t op1, MIR_op_t op2) { if (op1.mode != op2.mode) return FALSE; switch (op1.mode) { case MIR_OP_REG: return op1.u.reg == op2.u.reg; case MIR_OP_HARD_REG: return op1.u.hard_reg == op2.u.hard_reg; case MIR_OP_INT: return op1.u.i == op2.u.i; case MIR_OP_UINT: return op1.u.u == op2.u.u; case MIR_OP_FLOAT: return op1.u.f == op2.u.f; case MIR_OP_DOUBLE: return op1.u.d == op2.u.d; case MIR_OP_LDOUBLE: return op1.u.ld == op2.u.ld; case MIR_OP_REF: return strcmp (MIR_item_name (ctx, op1.u.ref), MIR_item_name (ctx, op2.u.ref)) == 0; case MIR_OP_STR: return op1.u.str.len == op2.u.str.len && memcmp (op1.u.str.s, op2.u.str.s, op1.u.str.len) == 0; case MIR_OP_MEM: return (op1.u.mem.type == op2.u.mem.type && op1.u.mem.disp == op2.u.mem.disp && op1.u.mem.base == op2.u.mem.base && op1.u.mem.index == op2.u.mem.index && (op1.u.mem.index == 0 || op1.u.mem.scale == op2.u.mem.scale)); case MIR_OP_HARD_REG_MEM: return (op1.u.hard_reg_mem.type == op2.u.hard_reg_mem.type && op1.u.hard_reg_mem.disp == op2.u.hard_reg_mem.disp && op1.u.hard_reg_mem.base == op2.u.hard_reg_mem.base && op1.u.hard_reg_mem.index == op2.u.hard_reg_mem.index && (op1.u.hard_reg_mem.index == MIR_NON_HARD_REG || op1.u.hard_reg_mem.scale == op2.u.hard_reg_mem.scale)); case MIR_OP_LABEL: return op1.u.label == op2.u.label; default: mir_assert (FALSE); /* we should not have other operands here */ } return FALSE; } htab_hash_t MIR_op_hash_step (MIR_context_t ctx, htab_hash_t h, MIR_op_t op) { h = mir_hash_step (h, (uint64_t) op.mode); switch (op.mode) { case MIR_OP_REG: return mir_hash_step (h, (uint64_t) op.u.reg); case MIR_OP_HARD_REG: return mir_hash_step (h, (uint64_t) op.u.hard_reg); case MIR_OP_INT: return mir_hash_step (h, (uint64_t) op.u.i); case MIR_OP_UINT: return mir_hash_step (h, (uint64_t) op.u.u); case MIR_OP_FLOAT: { union { double d; uint64_t u; } u; u.d = op.u.f; return mir_hash_step (h, u.u); } case MIR_OP_DOUBLE: return mir_hash_step (h, op.u.u); case MIR_OP_LDOUBLE: { union { long double ld; uint64_t u[2]; } u; u.ld = op.u.ld; return mir_hash_step (mir_hash_step (h, u.u[0]), u.u[1]); } case MIR_OP_REF: return mir_hash_step (h, (uint64_t) MIR_item_name (ctx, op.u.ref)); case MIR_OP_STR: return mir_hash_step (h, (uint64_t) op.u.str.s); case MIR_OP_MEM: h = mir_hash_step (h, (uint64_t) op.u.mem.type); h = mir_hash_step (h, (uint64_t) op.u.mem.disp); h = mir_hash_step (h, (uint64_t) op.u.mem.base); h = mir_hash_step (h, (uint64_t) op.u.mem.index); if (op.u.mem.index != 0) h = mir_hash_step (h, (uint64_t) op.u.mem.scale); break; case MIR_OP_HARD_REG_MEM: h = mir_hash_step (h, (uint64_t) op.u.hard_reg_mem.type); h = mir_hash_step (h, (uint64_t) op.u.hard_reg_mem.disp); h = mir_hash_step (h, (uint64_t) op.u.hard_reg_mem.base); h = mir_hash_step (h, (uint64_t) op.u.hard_reg_mem.index); if (op.u.hard_reg_mem.index != MIR_NON_HARD_REG) h = mir_hash_step (h, (uint64_t) op.u.hard_reg_mem.scale); break; case MIR_OP_LABEL: return mir_hash_step (h, (uint64_t) op.u.label); default: mir_assert (FALSE); /* we should not have other operands here */ } return h; } void MIR_append_insn (MIR_context_t ctx, MIR_item_t func_item, MIR_insn_t insn) { mir_assert (func_item != NULL); if (func_item->item_type != MIR_func_item) MIR_get_error_func (ctx) (MIR_wrong_param_value_error, "MIR_append_insn: wrong func item"); DLIST_APPEND (MIR_insn_t, func_item->u.func->insns, insn); } void MIR_prepend_insn (MIR_context_t ctx, MIR_item_t func_item, MIR_insn_t insn) { mir_assert (func_item != NULL); if (func_item->item_type != MIR_func_item) MIR_get_error_func (ctx) (MIR_wrong_param_value_error, "MIR_prepend_insn: wrong func item"); DLIST_PREPEND (MIR_insn_t, func_item->u.func->insns, insn); } void MIR_insert_insn_after (MIR_context_t ctx, MIR_item_t func_item, MIR_insn_t after, MIR_insn_t insn) { mir_assert (func_item != NULL); if (func_item->item_type != MIR_func_item) MIR_get_error_func (ctx) (MIR_wrong_param_value_error, "MIR_insert_insn_after: wrong func item"); DLIST_INSERT_AFTER (MIR_insn_t, func_item->u.func->insns, after, insn); } void MIR_insert_insn_before (MIR_context_t ctx, MIR_item_t func_item, MIR_insn_t before, MIR_insn_t insn) { mir_assert (func_item != NULL); if (func_item->item_type != MIR_func_item) MIR_get_error_func (ctx) (MIR_wrong_param_value_error, "MIR_insert_insn_before: wrong func item"); DLIST_INSERT_BEFORE (MIR_insn_t, func_item->u.func->insns, before, insn); } static void store_labels_for_duplication (MIR_context_t ctx, VARR (MIR_insn_t) * labels, VARR (MIR_insn_t) * branch_insns, MIR_insn_t insn, MIR_insn_t new_insn) { if (MIR_branch_code_p (insn->code) || insn->code == MIR_SWITCH) { VARR_PUSH (MIR_insn_t, branch_insns, new_insn); } else if (insn->code == MIR_LABEL) { mir_assert (insn->data == NULL); insn->data = new_insn; VARR_PUSH (MIR_insn_t, labels, insn); } } static void redirect_duplicated_labels (MIR_context_t ctx, VARR (MIR_insn_t) * labels, VARR (MIR_insn_t) * branch_insns) { MIR_insn_t insn; while (VARR_LENGTH (MIR_insn_t, branch_insns) != 0) { /* redirect new label operands */ size_t start_label_nop = 0, bound_label_nop = 1, n; insn = VARR_POP (MIR_insn_t, branch_insns); if (insn->code == MIR_SWITCH) { start_label_nop = 1; bound_label_nop = start_label_nop + insn->nops - 1; } for (n = start_label_nop; n < bound_label_nop; n++) insn->ops[n].u.label = insn->ops[n].u.label->data; } while (VARR_LENGTH (MIR_insn_t, labels) != 0) { /* reset data */ insn = VARR_POP (MIR_insn_t, labels); insn->data = NULL; } } void _MIR_duplicate_func_insns (MIR_context_t ctx, MIR_item_t func_item) { MIR_func_t func; MIR_insn_t insn, new_insn; VARR (MIR_insn_t) * labels, *branch_insns; mir_assert (func_item != NULL && func_item->item_type == MIR_func_item); func = func_item->u.func; mir_assert (DLIST_HEAD (MIR_insn_t, func->original_insns) == NULL); func->original_insns = func->insns; DLIST_INIT (MIR_insn_t, func->insns); VARR_CREATE (MIR_insn_t, labels, 0); VARR_CREATE (MIR_insn_t, branch_insns, 0); for (insn = DLIST_HEAD (MIR_insn_t, func->original_insns); insn != NULL; insn = DLIST_NEXT (MIR_insn_t, insn)) { /* copy insns and collect label info */ new_insn = MIR_copy_insn (ctx, insn); DLIST_APPEND (MIR_insn_t, func->insns, new_insn); store_labels_for_duplication (ctx, labels, branch_insns, insn, new_insn); } redirect_duplicated_labels (ctx, labels, branch_insns); VARR_DESTROY (MIR_insn_t, labels); VARR_DESTROY (MIR_insn_t, branch_insns); } void _MIR_restore_func_insns (MIR_context_t ctx, MIR_item_t func_item) { MIR_func_t func; MIR_insn_t insn; mir_assert (func_item != NULL && func_item->item_type == MIR_func_item); func = func_item->u.func; while ((insn = DLIST_HEAD (MIR_insn_t, func->insns)) != NULL) MIR_remove_insn (ctx, func_item, insn); func->insns = func->original_insns; DLIST_INIT (MIR_insn_t, func->original_insns); } static void set_item_name (MIR_item_t item, const char *name) { mir_assert (item != NULL); switch (item->item_type) { case MIR_func_item: item->u.func->name = name; break; case MIR_proto_item: item->u.proto->name = name; break; case MIR_import_item: item->u.import_id = name; break; case MIR_export_item: item->u.export_id = name; break; case MIR_forward_item: item->u.forward_id = name; break; case MIR_bss_item: item->u.bss->name = name; break; case MIR_data_item: item->u.data->name = name; break; case MIR_ref_data_item: item->u.ref_data->name = name; break; case MIR_expr_data_item: item->u.expr_data->name = name; break; default: mir_assert (FALSE); } } static void change_var_names (MIR_context_t new_ctx, VARR (MIR_var_t) * vars) { for (size_t i = 0; i < VARR_LENGTH (MIR_var_t, vars); i++) { MIR_var_t *var_ptr = &VARR_ADDR (MIR_var_t, vars)[i]; var_ptr->name = get_ctx_str (new_ctx, var_ptr->name); } } /* It is not thread-safe */ void MIR_change_module_ctx (MIR_context_t old_ctx, MIR_module_t m, MIR_context_t new_ctx) { MIR_item_t item, tab_item; MIR_op_mode_t mode; const char *name, *new_name; DLIST_REMOVE (MIR_module_t, *MIR_get_module_list (old_ctx), m); DLIST_APPEND (MIR_module_t, *MIR_get_module_list (new_ctx), m); m->name = get_ctx_str (new_ctx, m->name); for (item = DLIST_HEAD (MIR_item_t, m->items); item != NULL; item = DLIST_NEXT (MIR_item_t, item)) { if (item->addr != NULL) MIR_get_error_func (old_ctx) (MIR_ctx_change_error, "Change context of a loaded module"); if ((name = MIR_item_name (old_ctx, item)) != NULL) { new_name = get_ctx_str (new_ctx, name); if (item_tab_find (old_ctx, name, m) != item) { set_item_name (item, new_name); } else { item_tab_remove (old_ctx, item); set_item_name (item, new_name); tab_item = item_tab_insert (new_ctx, item); mir_assert (item == tab_item); } } if (item->item_type == MIR_proto_item) { change_var_names (new_ctx, item->u.proto->args); } else if (item->item_type == MIR_func_item) { change_var_names (new_ctx, item->u.func->vars); for (MIR_insn_t insn = DLIST_HEAD (MIR_insn_t, item->u.func->insns); insn != NULL; insn = DLIST_NEXT (MIR_insn_t, insn)) for (size_t i = 0; i < insn->nops; i++) { if ((mode = insn->ops[i].mode) == MIR_OP_STR) insn->ops[i].u.str = get_ctx_string (new_ctx, insn->ops[i].u.str).str; } } } } static void output_type (MIR_context_t ctx, FILE *f, MIR_type_t tp) { fprintf (f, "%s", MIR_type_str (ctx, tp)); } static void output_disp (FILE *f, MIR_disp_t disp) { fprintf (f, "%" PRId64, (int64_t) disp); } static void output_scale (FILE *f, unsigned scale) { fprintf (f, "%u", scale); } static void output_reg (MIR_context_t ctx, FILE *f, MIR_func_t func, MIR_reg_t reg) { fprintf (f, "%s", MIR_reg_name (ctx, reg, func)); } static void output_hard_reg (FILE *f, MIR_reg_t reg) { fprintf (f, "hr%u", reg); } static void output_label (MIR_context_t ctx, FILE *f, MIR_func_t func, MIR_label_t label); static void out_str (FILE *f, MIR_str_t str) { fprintf (f, "\""); for (size_t i = 0; i < str.len; i++) if (str.s[i] == '\\') fprintf (f, "\\\\"); else if (str.s[i] == '"') fprintf (f, "\\\""); else if (isprint (str.s[i])) fprintf (f, "%c", str.s[i]); else if (str.s[i] == '\n') fprintf (f, "\\n"); else if (str.s[i] == '\t') fprintf (f, "\\t"); else if (str.s[i] == '\v') fprintf (f, "\\v"); else if (str.s[i] == '\a') fprintf (f, "\\a"); else if (str.s[i] == '\b') fprintf (f, "\\b"); else if (str.s[i] == '\f') fprintf (f, "\\f"); else fprintf (f, "\\%03o", str.s[i]); fprintf (f, "\""); } void MIR_output_op (MIR_context_t ctx, FILE *f, MIR_op_t op, MIR_func_t func) { switch (op.mode) { case MIR_OP_REG: output_reg (ctx, f, func, op.u.reg); break; case MIR_OP_HARD_REG: output_hard_reg (f, op.u.hard_reg); break; case MIR_OP_INT: fprintf (f, "%" PRId64, op.u.i); break; case MIR_OP_UINT: fprintf (f, "%" PRIu64, op.u.u); break; case MIR_OP_FLOAT: fprintf (f, "%.*ef", FLT_MANT_DIG, op.u.f); break; case MIR_OP_DOUBLE: fprintf (f, "%.*e", DBL_MANT_DIG, op.u.d); break; case MIR_OP_LDOUBLE: fprintf (f, "%.*LeL", LDBL_MANT_DIG, op.u.ld); break; case MIR_OP_MEM: case MIR_OP_HARD_REG_MEM: { MIR_reg_t no_reg = op.mode == MIR_OP_MEM ? 0 : MIR_NON_HARD_REG; output_type (ctx, f, op.u.mem.type); fprintf (f, ":"); if (op.u.mem.disp != 0 || (op.u.mem.base == no_reg && op.u.mem.index == no_reg)) output_disp (f, op.u.mem.disp); if (op.u.mem.base != no_reg || op.u.mem.index != no_reg) { fprintf (f, "("); if (op.u.mem.base != no_reg) { if (op.mode == MIR_OP_MEM) output_reg (ctx, f, func, op.u.mem.base); else output_hard_reg (f, op.u.hard_reg_mem.base); } if (op.u.mem.index != no_reg) { fprintf (f, ", "); if (op.mode == MIR_OP_MEM) output_reg (ctx, f, func, op.u.mem.index); else output_hard_reg (f, op.u.hard_reg_mem.index); if (op.u.mem.scale != 1) { fprintf (f, ", "); output_scale (f, op.u.mem.scale); } } fprintf (f, ")"); } break; } case MIR_OP_REF: fprintf (f, "%s", MIR_item_name (ctx, op.u.ref)); break; case MIR_OP_STR: out_str (f, op.u.str); break; case MIR_OP_LABEL: output_label (ctx, f, func, op.u.label); break; default: mir_assert (FALSE); } } static void output_label (MIR_context_t ctx, FILE *f, MIR_func_t func, MIR_label_t label) { fprintf (f, "L"); MIR_output_op (ctx, f, label->ops[0], func); } void MIR_output_insn (MIR_context_t ctx, FILE *f, MIR_insn_t insn, MIR_func_t func, int newline_p) { size_t i, nops; mir_assert (insn != NULL); if (insn->code == MIR_LABEL) { output_label (ctx, f, func, insn); if (newline_p) fprintf (f, ":\n"); return; } fprintf (f, "\t%s", MIR_insn_name (ctx, insn->code)); nops = MIR_insn_nops (ctx, insn); for (i = 0; i < nops; i++) { fprintf (f, i == 0 ? "\t" : ", "); MIR_output_op (ctx, f, insn->ops[i], func); } if (insn->code == MIR_UNSPEC) fprintf (f, " # %s", VARR_GET (MIR_proto_t, unspec_protos, insn->ops[0].u.u)->name); if (newline_p) fprintf (f, "\n"); } static void output_func_proto (MIR_context_t ctx, FILE *f, size_t nres, MIR_type_t *types, size_t nargs, VARR (MIR_var_t) * args, int vararg_p) { size_t i; MIR_var_t var; for (i = 0; i < nres; i++) { if (i != 0) fprintf (f, ", "); fprintf (f, "%s", MIR_type_str (ctx, types[i])); } for (i = 0; i < nargs; i++) { var = VARR_GET (MIR_var_t, args, i); if (i != 0 || nres != 0) fprintf (f, ", "); mir_assert (var.name != NULL); if (!MIR_all_blk_type_p (var.type)) fprintf (f, "%s:%s", MIR_type_str (ctx, var.type), var.name); else fprintf (f, "%s:%lu(%s)", MIR_type_str (ctx, var.type), (unsigned long) var.size, var.name); } if (vararg_p) fprintf (f, nargs == 0 && nres == 0 ? "..." : ", ..."); fprintf (f, "\n"); } void MIR_output_item (MIR_context_t ctx, FILE *f, MIR_item_t item) { MIR_insn_t insn; MIR_func_t func; MIR_proto_t proto; MIR_var_t var; MIR_data_t data; MIR_ref_data_t ref_data; MIR_expr_data_t expr_data; size_t i, nlocals; mir_assert (f != NULL && item != NULL); if (item->item_type == MIR_export_item) { fprintf (f, "\texport\t%s\n", item->u.export_id); return; } if (item->item_type == MIR_import_item) { fprintf (f, "\timport\t%s\n", item->u.import_id); return; } if (item->item_type == MIR_forward_item) { fprintf (f, "\tforward\t%s\n", item->u.forward_id); return; } if (item->item_type == MIR_bss_item) { if (item->u.bss->name != NULL) fprintf (f, "%s:", item->u.bss->name); fprintf (f, "\tbss\t%" PRIu64 "\n", item->u.bss->len); return; } if (item->item_type == MIR_ref_data_item) { ref_data = item->u.ref_data; if (ref_data->name != NULL) fprintf (f, "%s:", ref_data->name); fprintf (f, "\tref\t%s, %" PRId64 "\n", MIR_item_name (ctx, ref_data->ref_item), (int64_t) ref_data->disp); return; } if (item->item_type == MIR_expr_data_item) { expr_data = item->u.expr_data; if (expr_data->name != NULL) fprintf (f, "%s:", expr_data->name); fprintf (f, "\texpr\t%s", MIR_item_name (ctx, expr_data->expr_item)); } if (item->item_type == MIR_data_item) { data = item->u.data; if (data->name != NULL) fprintf (f, "%s:", data->name); fprintf (f, "\t%s\t", MIR_type_str (ctx, data->el_type)); for (size_t i = 0; i < data->nel; i++) { switch (data->el_type) { case MIR_T_I8: fprintf (f, "%" PRId8, ((int8_t *) data->u.els)[i]); break; case MIR_T_U8: fprintf (f, "%" PRIu8, ((uint8_t *) data->u.els)[i]); break; case MIR_T_I16: fprintf (f, "%" PRId16, ((int16_t *) data->u.els)[i]); break; case MIR_T_U16: fprintf (f, "%" PRIu16, ((uint16_t *) data->u.els)[i]); break; case MIR_T_I32: fprintf (f, "%" PRId32, ((int32_t *) data->u.els)[i]); break; case MIR_T_U32: fprintf (f, "%" PRIu32, ((uint32_t *) data->u.els)[i]); break; case MIR_T_I64: fprintf (f, "%" PRId64, ((int64_t *) data->u.els)[i]); break; case MIR_T_U64: fprintf (f, "%" PRIu64, ((uint64_t *) data->u.els)[i]); break; case MIR_T_F: fprintf (f, "%.*ef", FLT_MANT_DIG, ((float *) data->u.els)[i]); break; case MIR_T_D: fprintf (f, "%.*e", DBL_MANT_DIG, ((double *) data->u.els)[i]); break; case MIR_T_LD: fprintf (f, "%.*LeL", LDBL_MANT_DIG, ((long double *) data->u.els)[i]); break; /* only ptr as ref ??? */ case MIR_T_P: fprintf (f, "0x%" PRIxPTR, ((uintptr_t *) data->u.els)[i]); break; default: mir_assert (FALSE); } if (i + 1 < data->nel) fprintf (f, ", "); } if (data->el_type == MIR_T_U8 && data->nel != 0 && data->u.els[data->nel - 1] == '\0') { fprintf (f, " # "); /* print possible string as a comment */ out_str (f, (MIR_str_t){data->nel, (char *) data->u.els}); } fprintf (f, "\n"); return; } if (item->item_type == MIR_proto_item) { proto = item->u.proto; fprintf (f, "%s:\tproto\t", proto->name); output_func_proto (ctx, f, proto->nres, proto->res_types, VARR_LENGTH (MIR_var_t, proto->args), proto->args, proto->vararg_p); return; } func = item->u.func; fprintf (f, "%s:\tfunc\t", func->name); output_func_proto (ctx, f, func->nres, func->res_types, func->nargs, func->vars, func->vararg_p); nlocals = VARR_LENGTH (MIR_var_t, func->vars) - func->nargs; for (i = 0; i < nlocals; i++) { var = VARR_GET (MIR_var_t, func->vars, i + func->nargs); if (i % 8 == 0) { if (i != 0) fprintf (f, "\n"); fprintf (f, "\tlocal\t"); } fprintf (f, i % 8 == 0 ? "%s:%s" : ", %s:%s", MIR_type_str (ctx, var.type), var.name); } fprintf (f, "\n# %u arg%s, %u local%s\n", func->nargs, func->nargs == 1 ? "" : "s", (unsigned) nlocals, nlocals == 1 ? "" : "s"); for (insn = DLIST_HEAD (MIR_insn_t, func->insns); insn != NULL; insn = DLIST_NEXT (MIR_insn_t, insn)) MIR_output_insn (ctx, f, insn, func, TRUE); fprintf (f, "\tendfunc\n"); } void MIR_output_module (MIR_context_t ctx, FILE *f, MIR_module_t module) { mir_assert (f != NULL && module != NULL); fprintf (f, "%s:\tmodule\n", module->name); for (MIR_item_t item = DLIST_HEAD (MIR_item_t, module->items); item != NULL; item = DLIST_NEXT (MIR_item_t, item)) MIR_output_item (ctx, f, item); fprintf (f, "\tendmodule\n"); } void MIR_output (MIR_context_t ctx, FILE *f) { mir_assert (f != NULL); for (MIR_module_t module = DLIST_HEAD (MIR_module_t, all_modules); module != NULL; module = DLIST_NEXT (MIR_module_t, module)) MIR_output_module (ctx, f, module); } /* New Page */ /* This page contains code for simplification and inlining */ static MIR_insn_t insert_op_insn (MIR_context_t ctx, int out_p, MIR_item_t func_item, MIR_insn_t anchor, MIR_insn_t insn) { if (!out_p) { MIR_insert_insn_before (ctx, func_item, anchor, insn); return anchor; } MIR_insert_insn_after (ctx, func_item, anchor, insn); return insn; } typedef struct { MIR_insn_code_t code; MIR_type_t type; MIR_op_t op1, op2; MIR_reg_t reg; } val_t; DEF_HTAB (val_t); struct simplify_ctx { HTAB (val_t) * val_tab; VARR (MIR_insn_t) * temp_insns, *labels; /* temp_insns is for branch or ret insns */ VARR (MIR_reg_t) * inline_reg_map; size_t inlined_calls, inline_insns_before, inline_insns_after; }; #define val_tab ctx->simplify_ctx->val_tab #define temp_insns ctx->simplify_ctx->temp_insns #define labels ctx->simplify_ctx->labels #define inline_reg_map ctx->simplify_ctx->inline_reg_map #define inlined_calls ctx->simplify_ctx->inlined_calls #define inline_insns_before ctx->simplify_ctx->inline_insns_before #define inline_insns_after ctx->simplify_ctx->inline_insns_after static htab_hash_t val_hash (val_t v, void *arg) { MIR_context_t ctx = arg; htab_hash_t h; h = mir_hash_step (mir_hash_init (0), (uint64_t) v.code); h = mir_hash_step (h, (uint64_t) v.type); h = MIR_op_hash_step (ctx, h, v.op1); if (v.code != MIR_INSN_BOUND) h = MIR_op_hash_step (ctx, h, v.op2); return mir_hash_finish (h); } static int val_eq (val_t v1, val_t v2, void *arg) { MIR_context_t ctx = arg; if (v1.code != v2.code || v1.type != v2.type || !MIR_op_eq_p (ctx, v1.op1, v2.op1)) return FALSE; return v1.code == MIR_INSN_BOUND || MIR_op_eq_p (ctx, v1.op2, v2.op2); } static void simplify_init (MIR_context_t ctx) { if ((ctx->simplify_ctx = malloc (sizeof (struct simplify_ctx))) == NULL) MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory for ctx"); HTAB_CREATE (val_t, val_tab, 512, val_hash, val_eq, ctx); VARR_CREATE (MIR_insn_t, temp_insns, 0); VARR_CREATE (MIR_insn_t, labels, 0); VARR_CREATE (MIR_reg_t, inline_reg_map, 256); inlined_calls = inline_insns_before = inline_insns_after = 0; } static void simplify_finish (MIR_context_t ctx) { VARR_DESTROY (MIR_reg_t, inline_reg_map); #if 0 if (inlined_calls != 0) fprintf (stderr, "inlined calls = %lu, insns before = %lu, insns_after = %lu, ratio = %.2f\n", inlined_calls, inline_insns_before, inline_insns_after, (double) inline_insns_after / inline_insns_before); #endif VARR_DESTROY (MIR_insn_t, labels); VARR_DESTROY (MIR_insn_t, temp_insns); HTAB_DESTROY (val_t, val_tab); free (ctx->simplify_ctx); ctx->simplify_ctx = NULL; } static void vn_empty (MIR_context_t ctx) { HTAB_CLEAR (val_t, val_tab); } static MIR_reg_t vn_add_val (MIR_context_t ctx, MIR_func_t func, MIR_type_t type, MIR_insn_code_t code, MIR_op_t op1, MIR_op_t op2) { val_t val, tab_val; val.type = type; val.code = code; val.op1 = op1; val.op2 = op2; if (HTAB_DO (val_t, val_tab, val, HTAB_FIND, tab_val)) return tab_val.reg; val.reg = _MIR_new_temp_reg (ctx, type, func); HTAB_DO (val_t, val_tab, val, HTAB_INSERT, tab_val); return val.reg; } void _MIR_get_temp_item_name (MIR_context_t ctx, MIR_module_t module, char *buff, size_t buff_len) { mir_assert (module != NULL); module->last_temp_item_num++; snprintf (buff, buff_len, "%s%u", TEMP_ITEM_NAME_PREFIX, (unsigned) module->last_temp_item_num); } void MIR_simplify_op (MIR_context_t ctx, MIR_item_t func_item, MIR_insn_t insn, int nop, int out_p, MIR_insn_code_t code, int keep_ref_p, int mem_float_p) { mir_assert (insn != NULL && func_item != NULL); MIR_op_t new_op, mem_op, *op = &insn->ops[nop]; MIR_insn_t new_insn; MIR_func_t func = func_item->u.func; MIR_type_t type; MIR_op_mode_t value_mode = op->value_mode; int move_p = code == MIR_MOV || code == MIR_FMOV || code == MIR_DMOV || code == MIR_LDMOV; if (code == MIR_PHI) return; /* do nothing: it is a phi insn */ if (code == MIR_UNSPEC && nop == 0) return; /* do nothing: it is an unspec code */ if (MIR_call_code_p (code)) { if (nop == 0) return; /* do nothing: it is a prototype */ if (nop == 1 && op->mode == MIR_OP_REF && (op->u.ref->item_type == MIR_import_item || op->u.ref->item_type == MIR_func_item)) return; /* do nothing: it is an immediate oeprand */ } if (code == MIR_VA_ARG && nop == 2) return; /* do nothing: this operand is used as a type */ switch (op->mode) { case MIR_OP_REF: if (keep_ref_p) break; case MIR_OP_INT: case MIR_OP_UINT: case MIR_OP_FLOAT: case MIR_OP_DOUBLE: case MIR_OP_LDOUBLE: case MIR_OP_STR: mir_assert (!out_p); if (op->mode == MIR_OP_REF) { for (MIR_item_t item = op->u.ref; item != NULL; item = item->ref_def) if (item->item_type != MIR_export_item && item->item_type != MIR_forward_item) { op->u.ref = item; break; } } else if (op->mode == MIR_OP_STR || (mem_float_p && (op->mode == MIR_OP_FLOAT || op->mode == MIR_OP_DOUBLE || op->mode == MIR_OP_LDOUBLE))) { const char *name; char buff[50]; MIR_item_t item; MIR_module_t m = curr_module; curr_module = func_item->module; _MIR_get_temp_item_name (ctx, curr_module, buff, sizeof (buff)); name = buff; if (op->mode == MIR_OP_STR) { item = MIR_new_string_data (ctx, name, op->u.str); *op = MIR_new_ref_op (ctx, item); } else { if (op->mode == MIR_OP_FLOAT) item = MIR_new_data (ctx, name, MIR_T_F, 1, (uint8_t *) &op->u.f); else if (op->mode == MIR_OP_DOUBLE) item = MIR_new_data (ctx, name, MIR_T_D, 1, (uint8_t *) &op->u.d); else item = MIR_new_data (ctx, name, MIR_T_LD, 1, (uint8_t *) &op->u.ld); type = op->mode == MIR_OP_FLOAT ? MIR_T_F : op->mode == MIR_OP_DOUBLE ? MIR_T_D : MIR_T_LD; *op = MIR_new_ref_op (ctx, item); new_op = MIR_new_reg_op (ctx, vn_add_val (ctx, func, MIR_T_I64, MIR_INSN_BOUND, *op, *op)); MIR_insert_insn_before (ctx, func_item, insn, MIR_new_insn (ctx, MIR_MOV, new_op, *op)); *op = MIR_new_mem_op (ctx, type, 0, new_op.u.reg, 0, 1); } if (func_item->addr != NULL) /* The function was already loaded: we should load new data */ load_bss_data_section (ctx, item, TRUE); curr_module = m; } if (move_p) return; type = (op->mode == MIR_OP_FLOAT ? MIR_T_F : op->mode == MIR_OP_DOUBLE ? MIR_T_D : op->mode == MIR_OP_LDOUBLE ? MIR_T_LD : op->mode == MIR_OP_MEM ? op->u.mem.type : MIR_T_I64); new_op = MIR_new_reg_op (ctx, vn_add_val (ctx, func, type, MIR_INSN_BOUND, *op, *op)); MIR_insert_insn_before (ctx, func_item, insn, MIR_new_insn (ctx, type == MIR_T_F ? MIR_FMOV : type == MIR_T_D ? MIR_DMOV : type == MIR_T_LD ? MIR_LDMOV : MIR_MOV, new_op, *op)); *op = new_op; break; case MIR_OP_REG: case MIR_OP_HARD_REG: case MIR_OP_LABEL: break; /* Do nothing */ case MIR_OP_MEM: { MIR_reg_t addr_reg = 0; mem_op = *op; type = mem_op.u.mem.type; if (op->u.mem.base != 0 && op->u.mem.disp == 0 && (op->u.mem.index == 0 || op->u.mem.scale == 0)) { addr_reg = op->u.mem.base; } else if (op->u.mem.base == 0 && op->u.mem.index != 0 && op->u.mem.scale == 1 && op->u.mem.disp == 0) { addr_reg = op->u.mem.index; } else { int after_p = !move_p && out_p; MIR_reg_t disp_reg = 0, scale_ind_reg = op->u.mem.index; MIR_reg_t base_reg = op->u.mem.base, base_ind_reg = 0; if (op->u.mem.disp != 0) { MIR_op_t disp_op = MIR_new_int_op (ctx, op->u.mem.disp); disp_reg = vn_add_val (ctx, func, MIR_T_I64, MIR_INSN_BOUND, disp_op, disp_op); insn = insert_op_insn (ctx, after_p, func_item, insn, MIR_new_insn (ctx, MIR_MOV, MIR_new_reg_op (ctx, disp_reg), disp_op)); } if (scale_ind_reg != 0 && op->u.mem.scale > 1) { MIR_op_t ind_op = MIR_new_reg_op (ctx, op->u.mem.index); MIR_op_t scale_reg_op, scale_int_op = MIR_new_int_op (ctx, op->u.mem.scale); scale_reg_op = MIR_new_reg_op (ctx, vn_add_val (ctx, func, MIR_T_I64, MIR_INSN_BOUND, scale_int_op, scale_int_op)); insn = insert_op_insn (ctx, after_p, func_item, insn, MIR_new_insn (ctx, MIR_MOV, scale_reg_op, scale_int_op)); scale_ind_reg = vn_add_val (ctx, func, MIR_T_I64, MIR_MUL, ind_op, scale_reg_op); insn = insert_op_insn (ctx, after_p, func_item, insn, MIR_new_insn (ctx, MIR_MUL, MIR_new_reg_op (ctx, scale_ind_reg), ind_op, scale_reg_op)); } if (base_reg != 0 && scale_ind_reg != 0) { MIR_op_t base_op = MIR_new_reg_op (ctx, base_reg), ind_op = MIR_new_reg_op (ctx, scale_ind_reg); base_ind_reg = vn_add_val (ctx, func, MIR_T_I64, MIR_ADD, base_op, ind_op); insn = insert_op_insn (ctx, after_p, func_item, insn, MIR_new_insn (ctx, MIR_ADD, MIR_new_reg_op (ctx, base_ind_reg), base_op, ind_op)); } else { base_ind_reg = base_reg != 0 ? base_reg : scale_ind_reg; } if (base_ind_reg == 0) { mir_assert (disp_reg != 0); addr_reg = disp_reg; } else if (disp_reg == 0) { mir_assert (base_ind_reg != 0); addr_reg = base_ind_reg; } else { MIR_op_t base_ind_op = MIR_new_reg_op (ctx, base_ind_reg); MIR_op_t disp_op = MIR_new_reg_op (ctx, disp_reg); addr_reg = vn_add_val (ctx, func, MIR_T_I64, MIR_ADD, base_ind_op, disp_op); insn = insert_op_insn (ctx, after_p, func_item, insn, MIR_new_insn (ctx, MIR_ADD, MIR_new_reg_op (ctx, addr_reg), base_ind_op, disp_op)); } } mem_op.u.mem.base = addr_reg; mem_op.u.mem.disp = 0; mem_op.u.mem.index = 0; mem_op.u.mem.scale = 0; if (move_p && (nop == 1 || insn->ops[1].mode == MIR_OP_REG)) { *op = mem_op; } else if (((code == MIR_VA_START && nop == 0) || ((code == MIR_VA_ARG || code == MIR_VA_BLOCK_ARG) && nop == 1) || (code == MIR_VA_END && nop == 0)) && mem_op.u.mem.type == MIR_T_UNDEF) { *op = MIR_new_reg_op (ctx, addr_reg); } else if (!MIR_all_blk_type_p (mem_op.u.mem.type) || !MIR_call_code_p (code)) { type = (mem_op.u.mem.type == MIR_T_F || mem_op.u.mem.type == MIR_T_D || mem_op.u.mem.type == MIR_T_LD ? mem_op.u.mem.type : MIR_T_I64); code = (type == MIR_T_F ? MIR_FMOV : type == MIR_T_D ? MIR_DMOV : type == MIR_T_LD ? MIR_LDMOV : MIR_MOV); new_op = MIR_new_reg_op (ctx, vn_add_val (ctx, func, type, MIR_INSN_BOUND, mem_op, mem_op)); if (out_p) new_insn = MIR_new_insn (ctx, code, mem_op, new_op); else new_insn = MIR_new_insn (ctx, code, new_op, mem_op); insn = insert_op_insn (ctx, out_p, func_item, insn, new_insn); *op = new_op; } break; } default: /* We don't simplify code with hard regs. */ mir_assert (FALSE); } op->value_mode = value_mode; } void _MIR_simplify_insn (MIR_context_t ctx, MIR_item_t func_item, MIR_insn_t insn, int keep_ref_p, int mem_float_p) { int out_p; mir_assert (insn != NULL); MIR_insn_code_t code = insn->code; size_t i, nops = MIR_insn_nops (ctx, insn); for (i = 0; i < nops; i++) { MIR_insn_op_mode (ctx, insn, i, &out_p); MIR_simplify_op (ctx, func_item, insn, i, out_p, code, (insn->code == MIR_INLINE || insn->code == MIR_CALL) && i == 1 && keep_ref_p, mem_float_p); } } static void make_one_ret (MIR_context_t ctx, MIR_item_t func_item) { size_t i, j; MIR_insn_code_t mov_code, ext_code; MIR_reg_t ret_reg; MIR_op_t reg_op, ret_reg_op; MIR_func_t func = func_item->u.func; MIR_type_t *res_types = func->res_types; MIR_insn_t ret_label, insn, first_ret_insn = NULL; VARR (MIR_op_t) * ret_ops; int one_last_ret_p; one_last_ret_p = (VARR_LENGTH (MIR_insn_t, temp_insns) == 1 && VARR_GET (MIR_insn_t, temp_insns, 0) == DLIST_TAIL (MIR_insn_t, func->insns)); ret_label = NULL; if (one_last_ret_p) { first_ret_insn = VARR_GET (MIR_insn_t, temp_insns, 0); } else { ret_label = MIR_new_label (ctx); MIR_append_insn (ctx, func_item, ret_label); } VARR_CREATE (MIR_op_t, ret_ops, 16); for (i = 0; i < func->nres; i++) { if (one_last_ret_p) { ret_reg_op = first_ret_insn->ops[i]; } else { mov_code = (res_types[i] == MIR_T_F ? MIR_FMOV : res_types[i] == MIR_T_D ? MIR_DMOV : res_types[i] == MIR_T_LD ? MIR_LDMOV : MIR_MOV); ret_reg = _MIR_new_temp_reg (ctx, mov_code == MIR_MOV ? MIR_T_I64 : res_types[i], func); ret_reg_op = MIR_new_reg_op (ctx, ret_reg); VARR_PUSH (MIR_op_t, ret_ops, ret_reg_op); } switch (res_types[i]) { case MIR_T_I8: ext_code = MIR_EXT8; break; case MIR_T_U8: ext_code = MIR_UEXT8; break; case MIR_T_I16: ext_code = MIR_EXT16; break; case MIR_T_U16: ext_code = MIR_UEXT16; break; case MIR_T_I32: ext_code = MIR_EXT32; break; case MIR_T_U32: ext_code = MIR_UEXT32; break; default: ext_code = MIR_INVALID_INSN; break; } if (ext_code == MIR_INVALID_INSN) continue; if (one_last_ret_p) MIR_insert_insn_before (ctx, func_item, first_ret_insn, MIR_new_insn (ctx, ext_code, ret_reg_op, ret_reg_op)); else MIR_append_insn (ctx, func_item, MIR_new_insn (ctx, ext_code, ret_reg_op, ret_reg_op)); } if (!one_last_ret_p) { MIR_append_insn (ctx, func_item, MIR_new_insn_arr (ctx, MIR_RET, func->nres, VARR_ADDR (MIR_op_t, ret_ops))); for (i = 0; i < VARR_LENGTH (MIR_insn_t, temp_insns); i++) { insn = VARR_GET (MIR_insn_t, temp_insns, i); mir_assert (func->nres == MIR_insn_nops (ctx, insn)); for (j = 0; j < func->nres; j++) { mov_code = (res_types[j] == MIR_T_F ? MIR_FMOV : res_types[j] == MIR_T_D ? MIR_DMOV : res_types[j] == MIR_T_LD ? MIR_LDMOV : MIR_MOV); reg_op = insn->ops[j]; mir_assert (reg_op.mode == MIR_OP_REG); ret_reg_op = VARR_GET (MIR_op_t, ret_ops, j); MIR_insert_insn_before (ctx, func_item, insn, MIR_new_insn (ctx, mov_code, ret_reg_op, reg_op)); } MIR_insert_insn_before (ctx, func_item, insn, MIR_new_insn (ctx, MIR_JMP, MIR_new_label_op (ctx, ret_label))); MIR_remove_insn (ctx, func_item, insn); } } VARR_DESTROY (MIR_op_t, ret_ops); } static void remove_unused_labels (MIR_context_t ctx, MIR_item_t func_item) { while (VARR_LENGTH (MIR_insn_t, labels) != 0) { MIR_insn_t label = VARR_POP (MIR_insn_t, labels); int64_t label_num = label->ops[0].u.i; if (label_num < VARR_LENGTH (uint8_t, temp_data) && VARR_GET (uint8_t, temp_data, label_num)) continue; MIR_remove_insn (ctx, func_item, label); } } MIR_insn_code_t MIR_reverse_branch_code (MIR_insn_code_t code) { switch (code) { case MIR_BT: return MIR_BF; case MIR_BTS: return MIR_BFS; case MIR_BF: return MIR_BT; case MIR_BFS: return MIR_BTS; case MIR_BEQ: return MIR_BNE; case MIR_BEQS: return MIR_BNES; case MIR_BNE: return MIR_BEQ; case MIR_BNES: return MIR_BEQS; case MIR_BLT: return MIR_BGE; case MIR_BLTS: return MIR_BGES; case MIR_UBLT: return MIR_UBGE; case MIR_UBLTS: return MIR_UBGES; case MIR_BLE: return MIR_BGT; case MIR_BLES: return MIR_BGTS; case MIR_UBLE: return MIR_UBGT; case MIR_UBLES: return MIR_UBGTS; case MIR_BGT: return MIR_BLE; case MIR_BGTS: return MIR_BLES; case MIR_UBGT: return MIR_UBLE; case MIR_UBGTS: return MIR_UBLES; case MIR_BGE: return MIR_BLT; case MIR_BGES: return MIR_BLTS; case MIR_UBGE: return MIR_UBLT; case MIR_UBGES: return MIR_UBLTS; default: return MIR_INSN_BOUND; } } static MIR_insn_t skip_labels (MIR_label_t label, MIR_label_t stop) { for (MIR_insn_t insn = label;; insn = DLIST_NEXT (MIR_insn_t, insn)) if (insn == NULL || insn->code != MIR_LABEL || insn == stop) return insn; } static int64_t natural_alignment (int64_t s) { return s <= 2 ? s : s <= 4 ? 4 : s <= 8 ? 8 : 16; } static const int MAX_JUMP_CHAIN_LEN = 32; static int simplify_func (MIR_context_t ctx, MIR_item_t func_item, int mem_float_p) { MIR_func_t func = func_item->u.func; MIR_insn_t insn, next_insn, next_next_insn, jmp_insn, new_insn; MIR_insn_code_t ext_code, rev_code; int jmps_num = 0, inline_p = FALSE; if (func_item->item_type != MIR_func_item) MIR_get_error_func (ctx) (MIR_wrong_param_value_error, "MIR_remove_simplify: wrong func item"); vn_empty (ctx); func = func_item->u.func; for (size_t i = 0; i < func->nargs; i++) { MIR_var_t var = VARR_GET (MIR_var_t, func->vars, i); if (var.type == MIR_T_I64 || var.type == MIR_T_U64 || var.type == MIR_T_F || var.type == MIR_T_D || var.type == MIR_T_LD) continue; switch (var.type) { case MIR_T_I8: ext_code = MIR_EXT8; break; case MIR_T_U8: ext_code = MIR_UEXT8; break; case MIR_T_I16: ext_code = MIR_EXT16; break; case MIR_T_U16: ext_code = MIR_UEXT16; break; case MIR_T_I32: ext_code = MIR_EXT32; break; case MIR_T_U32: ext_code = MIR_UEXT32; break; default: ext_code = MIR_INVALID_INSN; break; } if (ext_code != MIR_INVALID_INSN) { MIR_reg_t reg = MIR_reg (ctx, var.name, func); MIR_insn_t new_insn = MIR_new_insn (ctx, ext_code, MIR_new_reg_op (ctx, reg), MIR_new_reg_op (ctx, reg)); MIR_prepend_insn (ctx, func_item, new_insn); } } VARR_TRUNC (MIR_insn_t, temp_insns, 0); VARR_TRUNC (MIR_insn_t, labels, 0); VARR_TRUNC (uint8_t, temp_data, 0); for (insn = DLIST_HEAD (MIR_insn_t, func->insns); insn != NULL; insn = next_insn) { MIR_insn_code_t code = insn->code; MIR_op_t temp_op; if ((code == MIR_MOV || code == MIR_FMOV || code == MIR_DMOV || code == MIR_LDMOV) && insn->ops[0].mode == MIR_OP_MEM && insn->ops[1].mode == MIR_OP_MEM) { temp_op = MIR_new_reg_op (ctx, _MIR_new_temp_reg (ctx, code == MIR_MOV ? MIR_T_I64 : code == MIR_FMOV ? MIR_T_F : code == MIR_DMOV ? MIR_T_D : MIR_T_LD, func)); MIR_insert_insn_after (ctx, func_item, insn, MIR_new_insn (ctx, code, insn->ops[0], temp_op)); insn->ops[0] = temp_op; } if (code == MIR_RET) VARR_PUSH (MIR_insn_t, temp_insns, insn); if (code == MIR_LABEL) VARR_PUSH (MIR_insn_t, labels, insn); next_insn = DLIST_NEXT (MIR_insn_t, insn); if (code == MIR_ALLOCA && (insn->ops[1].mode == MIR_OP_INT || insn->ops[1].mode == MIR_OP_UINT)) { /* Consolidate allocas */ int64_t size, overall_size, align, max_align; size = insn->ops[1].u.i; overall_size = size <= 0 ? 1 : size; max_align = align = natural_alignment (overall_size); overall_size = (overall_size + align - 1) / align * align; while (next_insn != NULL && next_insn->code == MIR_ALLOCA && (next_insn->ops[1].mode == MIR_OP_INT || next_insn->ops[1].mode == MIR_OP_UINT) && !MIR_op_eq_p (ctx, insn->ops[0], next_insn->ops[0])) { size = next_insn->ops[1].u.i; size = size <= 0 ? 1 : size; align = natural_alignment (size); size = (size + align - 1) / align * align; if (max_align < align) { max_align = align; overall_size = (overall_size + align - 1) / align * align; } new_insn = MIR_new_insn (ctx, MIR_PTR32 ? MIR_ADDS : MIR_ADD, next_insn->ops[0], insn->ops[0], MIR_new_int_op (ctx, overall_size)); overall_size += size; MIR_insert_insn_before (ctx, func_item, next_insn, new_insn); MIR_remove_insn (ctx, func_item, next_insn); next_insn = DLIST_NEXT (MIR_insn_t, new_insn); } insn->ops[1].u.i = overall_size; next_insn = DLIST_NEXT (MIR_insn_t, insn); /* to process the current and new insns */ } if (code == MIR_INLINE || code == MIR_CALL) inline_p = TRUE; if ((MIR_int_branch_code_p (code) || code == MIR_JMP) && insn->ops[0].mode == MIR_OP_LABEL && skip_labels (next_insn, insn->ops[0].u.label) == insn->ops[0].u.label) { /* BR L|JMP L; L: => L: Also Remember signaling NAN*/ MIR_remove_insn (ctx, func_item, insn); } else if (((code == MIR_MUL || code == MIR_MULS || code == MIR_DIV || code == MIR_DIVS) && insn->ops[2].mode == MIR_OP_INT && insn->ops[2].u.i == 1) || ((code == MIR_ADD || code == MIR_ADDS || code == MIR_SUB || code == MIR_SUBS || code == MIR_OR || code == MIR_ORS || code == MIR_XOR || code == MIR_XORS || code == MIR_LSH || code == MIR_LSHS || code == MIR_RSH || code == MIR_RSHS || code == MIR_URSH || code == MIR_URSHS) && insn->ops[2].mode == MIR_OP_INT && insn->ops[2].u.i == 0)) { if (!MIR_op_eq_p (ctx, insn->ops[0], insn->ops[1])) { next_insn = MIR_new_insn (ctx, MIR_MOV, insn->ops[0], insn->ops[1]); MIR_insert_insn_before (ctx, func_item, insn, next_insn); } MIR_remove_insn (ctx, func_item, insn); } else if (MIR_int_branch_code_p (code) && next_insn != NULL && next_insn->code == MIR_JMP && insn->ops[0].mode == MIR_OP_LABEL && next_insn->ops[0].mode == MIR_OP_LABEL && (skip_labels (next_insn->ops[0].u.label, insn->ops[0].u.label) == insn->ops[0].u.label || skip_labels (insn->ops[0].u.label, next_insn->ops[0].u.label) == next_insn->ops[0].u.label)) { /* BR L1;JMP L2; L2:L1: or L1:L2: => JMP L2*/ MIR_remove_insn (ctx, func_item, insn); } else if ((code == MIR_BT || code == MIR_BTS || code == MIR_BF || code == MIR_BFS) && insn->ops[1].mode == MIR_OP_INT && (insn->ops[1].u.i == 0 || insn->ops[1].u.i == 1)) { if ((code == MIR_BT || code == MIR_BTS) == (insn->ops[1].u.i == 1)) { new_insn = MIR_new_insn (ctx, MIR_JMP, insn->ops[0]); MIR_insert_insn_before (ctx, func_item, insn, new_insn); next_insn = new_insn; } MIR_remove_insn (ctx, func_item, insn); // ??? make imm always second, what is about mem? } else if ((rev_code = MIR_reverse_branch_code (insn->code)) != MIR_INSN_BOUND && next_insn != NULL && next_insn->code == MIR_JMP && (next_next_insn = DLIST_NEXT (MIR_insn_t, next_insn)) != NULL && next_next_insn->code == MIR_LABEL && insn->ops[0].mode == MIR_OP_LABEL && skip_labels (next_next_insn, insn->ops[0].u.label) == insn->ops[0].u.label) { /* BCond L;JMP L2;L: => BNCond L2;L: */ insn->ops[0] = next_insn->ops[0]; insn->code = rev_code; MIR_remove_insn (ctx, func_item, next_insn); next_insn = insn; } else if (MIR_branch_code_p (code) && insn->ops[0].mode == MIR_OP_LABEL && (jmp_insn = skip_labels (insn->ops[0].u.label, NULL)) != NULL && jmp_insn->code == MIR_JMP && ++jmps_num < MAX_JUMP_CHAIN_LEN) { /* B L;...;L:JMP L2 => B L2; ... Also constrain processing to avoid infinite loops */ insn->ops[0] = jmp_insn->ops[0]; next_insn = insn; continue; } else { if (MIR_branch_code_p (code) || code == MIR_SWITCH) { int64_t label_num; size_t start_label_nop = 0, bound_label_nop = 1, n; if (code == MIR_SWITCH) { start_label_nop = 1; bound_label_nop = start_label_nop + insn->nops - 1; } for (n = start_label_nop; n < bound_label_nop; n++) { label_num = insn->ops[n].u.label->ops[0].u.i; while (label_num >= VARR_LENGTH (uint8_t, temp_data)) VARR_PUSH (uint8_t, temp_data, FALSE); VARR_SET (uint8_t, temp_data, label_num, TRUE); } } _MIR_simplify_insn (ctx, func_item, insn, TRUE, mem_float_p); } jmps_num = 0; } make_one_ret (ctx, func_item); remove_unused_labels (ctx, func_item); #if 0 fprintf (stderr, "+++++ Function after simplification:\n"); MIR_output_item (ctx, stderr, func_item); #endif return inline_p; } static void set_inline_reg_map (MIR_context_t ctx, MIR_reg_t old_reg, MIR_reg_t new_reg) { while (VARR_LENGTH (MIR_reg_t, inline_reg_map) <= old_reg) VARR_PUSH (MIR_reg_t, inline_reg_map, 0); VARR_SET (MIR_reg_t, inline_reg_map, old_reg, new_reg); } #ifndef MIR_MAX_INSNS_FOR_INLINE #define MIR_MAX_INSNS_FOR_INLINE 200 #endif #ifndef MIR_MAX_INSNS_FOR_CALL_INLINE #define MIR_MAX_INSNS_FOR_CALL_INLINE 50 #endif #ifndef MIR_MAX_FUNC_INLINE_GROWTH #define MIR_MAX_FUNC_INLINE_GROWTH 50 #endif #ifndef MIR_MAX_CALLER_SIZE_FOR_ANY_GROWTH_INLINE #define MIR_MAX_CALLER_SIZE_FOR_ANY_GROWTH_INLINE MIR_MAX_INSNS_FOR_INLINE #endif static void add_blk_move (MIR_context_t ctx, MIR_item_t func_item, MIR_insn_t before, MIR_op_t dest, MIR_op_t src, size_t src_size) { /* simplified MIR only */ MIR_func_t func = func_item->u.func; size_t blk_size = (src_size + 7) / 8 * 8; MIR_insn_t insn; MIR_reg_t addr_reg = _MIR_new_temp_reg (ctx, MIR_T_I64, func); MIR_op_t addr = MIR_new_reg_op (ctx, addr_reg); MIR_op_t disp = MIR_new_reg_op (ctx, _MIR_new_temp_reg (ctx, MIR_T_I64, func)); MIR_op_t size = MIR_new_reg_op (ctx, _MIR_new_temp_reg (ctx, MIR_T_I64, func)); MIR_op_t step = MIR_new_reg_op (ctx, _MIR_new_temp_reg (ctx, MIR_T_I64, func)); MIR_op_t temp = MIR_new_reg_op (ctx, _MIR_new_temp_reg (ctx, MIR_T_I64, func)); MIR_label_t loop = MIR_new_label (ctx), skip = MIR_new_label (ctx); insn = MIR_new_insn (ctx, MIR_MOV, size, MIR_new_int_op (ctx, blk_size)); MIR_insert_insn_before (ctx, func_item, before, insn); insn = MIR_new_insn (ctx, MIR_ALLOCA, dest, size); MIR_insert_insn_before (ctx, func_item, before, insn); insn = MIR_new_insn (ctx, MIR_MOV, disp, MIR_new_int_op (ctx, 0)); MIR_insert_insn_before (ctx, func_item, before, insn); insn = MIR_new_insn (ctx, MIR_BLT, MIR_new_label_op (ctx, skip), size, disp); MIR_insert_insn_before (ctx, func_item, before, insn); MIR_insert_insn_before (ctx, func_item, before, loop); insn = MIR_new_insn (ctx, MIR_ADD, addr, src, disp); MIR_insert_insn_before (ctx, func_item, before, insn); insn = MIR_new_insn (ctx, MIR_MOV, temp, MIR_new_mem_op (ctx, MIR_T_I64, 0, addr_reg, 0, 1)); MIR_insert_insn_before (ctx, func_item, before, insn); insn = MIR_new_insn (ctx, MIR_ADD, addr, dest, disp); MIR_insert_insn_before (ctx, func_item, before, insn); insn = MIR_new_insn (ctx, MIR_MOV, MIR_new_mem_op (ctx, MIR_T_I64, 0, addr_reg, 0, 1), temp); MIR_insert_insn_before (ctx, func_item, before, insn); insn = MIR_new_insn (ctx, MIR_MOV, step, MIR_new_int_op (ctx, 8)); MIR_insert_insn_before (ctx, func_item, before, insn); insn = MIR_new_insn (ctx, MIR_ADD, disp, disp, step); MIR_insert_insn_before (ctx, func_item, before, insn); insn = MIR_new_insn (ctx, MIR_BLT, MIR_new_label_op (ctx, loop), disp, size); MIR_insert_insn_before (ctx, func_item, before, insn); MIR_insert_insn_before (ctx, func_item, before, skip); } /* Only simplified code should be inlined because we need already extensions and one return. */ static void process_inlines (MIR_context_t ctx, MIR_item_t func_item) { int alloca_p; size_t i, actual_nops, nargs, nvars; MIR_type_t type, *res_types; MIR_var_t var; MIR_reg_t ret_reg, old_reg, new_reg, temp_reg; MIR_insn_t func_insn, next_func_insn, call, insn, new_insn, ret_insn, ret_label; MIR_item_t called_func_item; MIR_func_t func, called_func; size_t func_insns_num, called_func_insns_num; char buff[50]; mir_assert (func_item->item_type == MIR_func_item); vn_empty (ctx); func = func_item->u.func; func_insns_num = DLIST_LENGTH (MIR_insn_t, func->insns); for (func_insn = DLIST_HEAD (MIR_insn_t, func->insns); func_insn != NULL; func_insn = next_func_insn) { inline_insns_before++; inline_insns_after++; next_func_insn = DLIST_NEXT (MIR_insn_t, func_insn); if (func_insn->code != MIR_INLINE && func_insn->code != MIR_CALL) continue; call = func_insn; if (call->ops[1].mode != MIR_OP_REF) { MIR_simplify_op (ctx, func_item, func_insn, 1, FALSE, func_insn->code, FALSE, TRUE); continue; } called_func_item = call->ops[1].u.ref; while (called_func_item != NULL && (called_func_item->item_type == MIR_import_item || called_func_item->item_type == MIR_export_item || called_func_item->item_type == MIR_forward_item)) called_func_item = called_func_item->ref_def; if (called_func_item == NULL || called_func_item->item_type != MIR_func_item || func_item == called_func_item) { /* Simplify function operand in the inline insn */ MIR_simplify_op (ctx, func_item, func_insn, 1, FALSE, func_insn->code, FALSE, TRUE); continue; } called_func = called_func_item->u.func; called_func_insns_num = DLIST_LENGTH (MIR_insn_t, called_func->insns); if (called_func->vararg_p || called_func_insns_num > (func_insn->code == MIR_CALL ? MIR_MAX_INSNS_FOR_CALL_INLINE : MIR_MAX_INSNS_FOR_INLINE) || (called_func_insns_num > MIR_MAX_FUNC_INLINE_GROWTH * func_insns_num / 100 && func_insns_num > MIR_MAX_CALLER_SIZE_FOR_ANY_GROWTH_INLINE)) { MIR_simplify_op (ctx, func_item, func_insn, 1, FALSE, func_insn->code, FALSE, TRUE); continue; } func_insns_num += called_func_insns_num; inlined_calls++; res_types = call->ops[0].u.ref->u.proto->res_types; ret_label = MIR_new_label (ctx); MIR_insert_insn_after (ctx, func_item, call, ret_label); func->n_inlines++; nargs = called_func->nargs; nvars = VARR_LENGTH (MIR_var_t, called_func->vars); for (i = 0; i < nvars; i++) { VARR_TRUNC (char, temp_string, 0); sprintf (buff, ".c%d_", func->n_inlines); VARR_PUSH_ARR (char, temp_string, buff, strlen (buff)); var = VARR_GET (MIR_var_t, called_func->vars, i); type = (var.type == MIR_T_F || var.type == MIR_T_D || var.type == MIR_T_LD ? var.type : MIR_T_I64); old_reg = MIR_reg (ctx, var.name, called_func); VARR_PUSH_ARR (char, temp_string, var.name, strlen (var.name) + 1); new_reg = MIR_new_func_reg (ctx, func, type, VARR_ADDR (char, temp_string)); set_inline_reg_map (ctx, old_reg, new_reg); if (i < nargs && call->nops > i + 2 + called_func->nres) { /* Parameter passing */ MIR_op_t op = call->ops[i + 2 + called_func->nres]; mir_assert (!MIR_all_blk_type_p (type) || (op.mode == MIR_OP_MEM && type == MIR_T_I64)); if (MIR_blk_type_p (var.type)) { /* alloca and block move: */ add_blk_move (ctx, func_item, ret_label, MIR_new_reg_op (ctx, new_reg), MIR_new_reg_op (ctx, op.u.mem.base), var.size); } else { if (var.type == MIR_T_RBLK) op = MIR_new_reg_op (ctx, op.u.mem.base); new_insn = MIR_new_insn (ctx, type == MIR_T_F ? MIR_FMOV : type == MIR_T_D ? MIR_DMOV : type == MIR_T_LD ? MIR_LDMOV : MIR_MOV, MIR_new_reg_op (ctx, new_reg), op); MIR_insert_insn_before (ctx, func_item, ret_label, new_insn); } } } /* ??? No frame only alloca */ /* Add new insns: */ ret_reg = 0; alloca_p = FALSE; VARR_TRUNC (MIR_insn_t, temp_insns, 0); VARR_TRUNC (MIR_insn_t, labels, 0); VARR_TRUNC (uint8_t, temp_data, 0); for (insn = DLIST_HEAD (MIR_insn_t, called_func->insns); insn != NULL; insn = DLIST_NEXT (MIR_insn_t, insn)) { inline_insns_after++; actual_nops = MIR_insn_nops (ctx, insn); new_insn = MIR_copy_insn (ctx, insn); /* va insns are possible here as va_list can be passed as arg */ if (insn->code == MIR_ALLOCA) alloca_p = TRUE; for (i = 0; i < actual_nops; i++) switch (new_insn->ops[i].mode) { case MIR_OP_REG: new_insn->ops[i].u.reg = VARR_GET (MIR_reg_t, inline_reg_map, new_insn->ops[i].u.reg); break; case MIR_OP_MEM: if (insn->ops[i].u.mem.base != 0) new_insn->ops[i].u.mem.base = VARR_GET (MIR_reg_t, inline_reg_map, new_insn->ops[i].u.mem.base); if (insn->ops[i].u.mem.index != 0) new_insn->ops[i].u.mem.index = VARR_GET (MIR_reg_t, inline_reg_map, new_insn->ops[i].u.mem.index); break; default: /* do nothing */ break; } if (new_insn->code != MIR_RET) { MIR_insert_insn_before (ctx, func_item, ret_label, new_insn); store_labels_for_duplication (ctx, labels, temp_insns, insn, new_insn); } else { /* should be the last insn after simplification */ mir_assert (DLIST_NEXT (MIR_insn_t, insn) == NULL && call->ops[0].mode == MIR_OP_REF && call->ops[0].u.ref->item_type == MIR_proto_item); mir_assert (called_func->nres == actual_nops); ret_insn = new_insn; for (i = 0; i < actual_nops; i++) { mir_assert (ret_insn->ops[i].mode == MIR_OP_REG); ret_reg = ret_insn->ops[i].u.reg; new_insn = MIR_new_insn (ctx, res_types[i] == MIR_T_F ? MIR_FMOV : res_types[i] == MIR_T_D ? MIR_DMOV : res_types[i] == MIR_T_LD ? MIR_LDMOV : MIR_MOV, call->ops[i + 2], MIR_new_reg_op (ctx, ret_reg)); MIR_insert_insn_before (ctx, func_item, ret_label, new_insn); } free (ret_insn); } } redirect_duplicated_labels (ctx, labels, temp_insns); if (alloca_p) { temp_reg = _MIR_new_temp_reg (ctx, MIR_T_I64, func); new_insn = MIR_new_insn (ctx, MIR_BSTART, MIR_new_reg_op (ctx, temp_reg)); MIR_insert_insn_after (ctx, func_item, call, new_insn); new_insn = MIR_new_insn (ctx, MIR_BEND, MIR_new_reg_op (ctx, temp_reg)); MIR_insert_insn_before (ctx, func_item, ret_label, new_insn); } MIR_remove_insn (ctx, func_item, call); } } /* New Page */ const char *_MIR_uniq_string (MIR_context_t ctx, const char *str) { return get_ctx_str (ctx, str); } /* The next two function can be called any time relative to load/linkage. You can also call them many times for the same name but you should always use the same prototype or/and addr for the same proto/func name. */ MIR_item_t _MIR_builtin_proto (MIR_context_t ctx, MIR_module_t module, const char *name, size_t nres, MIR_type_t *res_types, size_t nargs, ...) { size_t i; va_list argp; MIR_var_t *args = alloca (nargs * sizeof (MIR_var_t)); MIR_item_t proto_item; MIR_module_t saved_module = curr_module; va_start (argp, nargs); if (mir_mutex_lock (&ctx_mutex)) parallel_error (ctx, "error in mutex lock"); for (i = 0; i < nargs; i++) { args[i].type = va_arg (argp, MIR_type_t); args[i].name = va_arg (argp, const char *); } va_end (argp); name = _MIR_uniq_string (ctx, name); proto_item = item_tab_find (ctx, name, module); if (proto_item != NULL) { if (proto_item->item_type == MIR_proto_item && proto_item->u.proto->nres == nres && VARR_LENGTH (MIR_var_t, proto_item->u.proto->args) == nargs) { for (i = 0; i < nres; i++) if (res_types[i] != proto_item->u.proto->res_types[i]) break; if (i >= nres) { for (i = 0; i < nargs; i++) if (args[i].type != VARR_GET (MIR_var_t, proto_item->u.proto->args, i).type) break; if (i >= nargs) { if (mir_mutex_unlock (&ctx_mutex)) parallel_error (ctx, "error in mutex unlock"); return proto_item; } } } MIR_get_error_func (ctx) (MIR_repeated_decl_error, "_MIR_builtin_proto: proto item %s was already defined differently", name); } saved_module = curr_module; curr_module = module; proto_item = MIR_new_proto_arr (ctx, name, nres, res_types, nargs, args); DLIST_REMOVE (MIR_item_t, curr_module->items, proto_item); DLIST_PREPEND (MIR_item_t, curr_module->items, proto_item); /* make it first in the list */ curr_module = saved_module; if (mir_mutex_unlock (&ctx_mutex)) parallel_error (ctx, "error in mutex unlock"); return proto_item; } MIR_item_t _MIR_builtin_func (MIR_context_t ctx, MIR_module_t module, const char *name, void *addr) { MIR_item_t item, ref_item; MIR_module_t saved_module = curr_module; if (mir_mutex_lock (&ctx_mutex)) parallel_error (ctx, "error in mutex lock"); name = _MIR_uniq_string (ctx, name); if ((ref_item = item_tab_find (ctx, name, &environment_module)) != NULL) { if (ref_item->item_type != MIR_import_item || ref_item->addr != addr) MIR_get_error_func (ctx) (MIR_repeated_decl_error, "_MIR_builtin_func: func %s has already another address", name); } else { curr_module = &environment_module; /* Use import for builtin func: */ item = new_export_import_forward (ctx, name, MIR_import_item, "import", TRUE); HTAB_DO (MIR_item_t, module_item_tab, item, HTAB_INSERT, ref_item); mir_assert (item == ref_item); DLIST_APPEND (MIR_item_t, environment_module.items, item); ref_item->addr = addr; curr_module = saved_module; } if ((item = item_tab_find (ctx, name, module)) != NULL) { if (item->item_type != MIR_import_item || item->addr != addr || item->ref_def != ref_item) MIR_get_error_func ( ctx) (MIR_repeated_decl_error, "_MIR_builtin_func: func name %s was already defined differently in the " "module", name); } else { curr_module = module; item = new_export_import_forward (ctx, name, MIR_import_item, "import", FALSE); DLIST_REMOVE (MIR_item_t, curr_module->items, item); DLIST_PREPEND (MIR_item_t, curr_module->items, item); /* make it first in the list */ item->addr = ref_item->addr; item->ref_def = ref_item; curr_module = saved_module; } if (mir_mutex_unlock (&ctx_mutex)) parallel_error (ctx, "error in mutex unlock"); return item; } /* New Page */ /* This page is for dealing with generated machine code */ #ifndef _WIN32 #include #include #define PROT_WRITE_EXEC (PROT_WRITE | PROT_EXEC) #define PROT_READ_EXEC (PROT_READ | PROT_EXEC) #define mem_protect mprotect #define mem_unmap munmap static void *mem_map (size_t len) { return mmap (NULL, len, PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); } static size_t mem_page_size () { return sysconf (_SC_PAGE_SIZE); } #else #define WIN32_LEAN_AND_MEAN #include #define PROT_WRITE_EXEC PAGE_EXECUTE_READWRITE #define PROT_READ_EXEC PAGE_EXECUTE_READ #define MAP_FAILED NULL static int mem_protect (void *addr, size_t len, int prot) { DWORD old_prot = 0; return VirtualProtect (addr, len, prot, &old_prot) ? 0 : -1; } static int mem_unmap (void *addr, size_t len) { return VirtualFree (addr, len, MEM_RELEASE) ? 0 : -1; } static void *mem_map (size_t len) { return VirtualAlloc (NULL, len, MEM_COMMIT, PAGE_EXECUTE); } static size_t mem_page_size () { SYSTEM_INFO sysInfo; GetSystemInfo (&sysInfo); return sysInfo.dwPageSize; } #endif struct code_holder { uint8_t *start, *free, *bound; }; typedef struct code_holder code_holder_t; DEF_VARR (code_holder_t); struct machine_code_ctx { #if MIR_PARALLEL_GEN mir_mutex_t code_mutex; #endif VARR (code_holder_t) * code_holders; size_t page_size; }; #define code_mutex ctx->machine_code_ctx->code_mutex #define code_holders ctx->machine_code_ctx->code_holders #define page_size ctx->machine_code_ctx->page_size static code_holder_t *get_last_code_holder (MIR_context_t ctx, size_t size) { uint8_t *mem; size_t len, npages; code_holder_t ch, *ch_ptr; if ((len = VARR_LENGTH (code_holder_t, code_holders)) > 0) { ch_ptr = VARR_ADDR (code_holder_t, code_holders) + len - 1; ch_ptr->free = (uint8_t *) ((uint64_t) (ch_ptr->free + 15) / 16 * 16); /* align */ if (ch_ptr->free + size <= ch_ptr->bound) return ch_ptr; } npages = (size + page_size) / page_size; len = page_size * npages; mem = (uint8_t *) mem_map (len); if (mem == MAP_FAILED) return NULL; ch.start = mem; ch.free = mem; ch.bound = mem + len; VARR_PUSH (code_holder_t, code_holders, ch); len = VARR_LENGTH (code_holder_t, code_holders); return VARR_ADDR (code_holder_t, code_holders) + len - 1; } #ifndef __MIRC__ void _MIR_flush_code_cache (void *start, void *bound) { #ifdef __GNUC__ __builtin___clear_cache (start, bound); #endif } #endif static uint8_t *add_code (MIR_context_t ctx, code_holder_t *ch_ptr, const uint8_t *code, size_t code_len) { uint8_t *mem = ch_ptr->free; ch_ptr->free += code_len; mir_assert (ch_ptr->free <= ch_ptr->bound); mem_protect (ch_ptr->start, ch_ptr->bound - ch_ptr->start, PROT_WRITE_EXEC); memcpy (mem, code, code_len); mem_protect (ch_ptr->start, ch_ptr->bound - ch_ptr->start, PROT_READ_EXEC); _MIR_flush_code_cache (mem, ch_ptr->free); return mem; } uint8_t *_MIR_publish_code (MIR_context_t ctx, const uint8_t *code, size_t code_len) { /* thread safe */ code_holder_t *ch_ptr; uint8_t *res = NULL; if (mir_mutex_lock (&code_mutex)) parallel_error (ctx, "error in mutex lock"); if ((ch_ptr = get_last_code_holder (ctx, code_len)) != NULL) res = add_code (ctx, ch_ptr, code, code_len); if (mir_mutex_unlock (&code_mutex)) parallel_error (ctx, "error in mutex unlock"); return res; } uint8_t *_MIR_get_new_code_addr (MIR_context_t ctx, size_t size) { code_holder_t *ch_ptr = get_last_code_holder (ctx, size); return ch_ptr == NULL ? NULL : ch_ptr->free; } uint8_t *_MIR_publish_code_by_addr (MIR_context_t ctx, void *addr, const uint8_t *code, size_t code_len) { code_holder_t *ch_ptr = get_last_code_holder (ctx, 0); uint8_t *res = NULL; if (ch_ptr != NULL && ch_ptr->free == addr && ch_ptr->free + code_len < ch_ptr->bound) res = add_code (ctx, ch_ptr, code, code_len); return res; } void _MIR_change_code (MIR_context_t ctx, uint8_t *addr, const uint8_t *code, size_t code_len) { /* thread safe */ size_t len, start; start = (size_t) addr / page_size * page_size; len = (size_t) addr + code_len - start; if (mir_mutex_lock (&code_mutex)) parallel_error (ctx, "error in mutex lock"); mem_protect ((uint8_t *) start, len, PROT_WRITE_EXEC); memcpy (addr, code, code_len); mem_protect ((uint8_t *) start, len, PROT_READ_EXEC); _MIR_flush_code_cache (addr, addr + code_len); if (mir_mutex_unlock (&code_mutex)) parallel_error (ctx, "error in mutex unlock"); } void _MIR_update_code_arr (MIR_context_t ctx, uint8_t *base, size_t nloc, const MIR_code_reloc_t *relocs) { /* thread safe */ size_t i, len, start, max_offset = 0; mir_assert (relocs != NULL); for (i = 0; i < nloc; i++) if (max_offset < relocs[i].offset) max_offset = relocs[i].offset; start = (size_t) base / page_size * page_size; len = (size_t) base + max_offset + sizeof (void *) - start; if (mir_mutex_lock (&code_mutex)) parallel_error (ctx, "error in mutex lock"); mem_protect ((uint8_t *) start, len, PROT_WRITE_EXEC); for (i = 0; i < nloc; i++) memcpy (base + relocs[i].offset, &relocs[i].value, sizeof (void *)); mem_protect ((uint8_t *) start, len, PROT_READ_EXEC); _MIR_flush_code_cache (base, base + max_offset + sizeof (void *)); if (mir_mutex_unlock (&code_mutex)) parallel_error (ctx, "error in mutex unlock"); } void _MIR_update_code (MIR_context_t ctx, uint8_t *base, size_t nloc, ...) { /* thread safe */ size_t start, len, offset, max_offset = 0; void *value; va_list args; va_start (args, nloc); for (size_t i = 0; i < nloc; i++) { offset = va_arg (args, size_t); value = va_arg (args, void *); if (max_offset < offset) max_offset = offset; } va_end (args); start = (size_t) base / page_size * page_size; len = (size_t) base + max_offset + sizeof (void *) - start; va_start (args, nloc); if (mir_mutex_lock (&code_mutex)) parallel_error (ctx, "error in mutex lock"); mem_protect ((uint8_t *) start, len, PROT_WRITE_EXEC); for (size_t i = 0; i < nloc; i++) { offset = va_arg (args, size_t); value = va_arg (args, void *); memcpy (base + offset, &value, sizeof (void *)); } mem_protect ((uint8_t *) start, len, PROT_READ_EXEC); _MIR_flush_code_cache (base, base + max_offset + sizeof (void *)); if (mir_mutex_unlock (&code_mutex)) parallel_error (ctx, "error in mutex unlock"); va_end (args); } static void code_init (MIR_context_t ctx) { if ((ctx->machine_code_ctx = malloc (sizeof (struct machine_code_ctx))) == NULL) MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory for ctx"); page_size = mem_page_size (); VARR_CREATE (code_holder_t, code_holders, 128); if (mir_mutex_init (&code_mutex, NULL)) parallel_error (ctx, "error in mutex init"); } static void code_finish (MIR_context_t ctx) { if (mir_mutex_destroy (&code_mutex)) parallel_error (ctx, "error in mutex destroy"); while (VARR_LENGTH (code_holder_t, code_holders) != 0) { code_holder_t ch = VARR_POP (code_holder_t, code_holders); mem_unmap (ch.start, ch.bound - ch.start); } VARR_DESTROY (code_holder_t, code_holders); free (ctx->machine_code_ctx); ctx->machine_code_ctx = NULL; } /* New Page */ #if !MIR_NO_IO /* Input/output of binary MIR. Major goal of binary MIR is fast reading, not compression ratio. Text MIR major CPU time consumer is a scanner. Mostly in reading binary MIR we skip the scanner part by using tokens. Each token starts with a tag which describes subsequent optional bytes. */ #define TAG_EL(t) TAG_##t #define REP_SEP , typedef enum { TAG_EL (U0), REP8 (TAG_EL, U1, U2, U3, U4, U5, U6, U7, U8), REP8 (TAG_EL, I1, I2, I3, I4, I5, I6, I7, I8), REP3 (TAG_EL, F, D, LD), /* 4, 8, 16 bytes for floating point numbers */ REP4 (TAG_EL, REG1, REG2, REG3, REG4), /* Reg string number in 1, 2, 3, 4 bytes */ REP4 (TAG_EL, NAME1, NAME2, NAME3, NAME4), /* Name string number in 1, 2, 3, 4 bytes */ REP4 (TAG_EL, STR1, STR2, STR3, STR4), /* String number in 1, 2, 3, 4 bytes */ REP4 (TAG_EL, LAB1, LAB2, LAB3, LAB4), /* Label number in 1, 2, 3, 4 bytes */ /* Tags for memory operands. The memory address parts are the subsequent tokens */ REP4 (TAG_EL, MEM_DISP, MEM_BASE, MEM_INDEX, MEM_DISP_BASE), REP3 (TAG_EL, MEM_DISP_INDEX, MEM_BASE_INDEX, MEM_DISP_BASE_INDEX), /* MIR types. The same order as MIR types: */ REP8 (TAG_EL, TI8, TU8, TI16, TU16, TI32, TU32, TI64, TU64), REP5 (TAG_EL, TF, TD, TP, TV, TBLOCK), TAG_EL (TRBLOCK) = TAG_EL (TBLOCK) + MIR_BLK_NUM, TAG_EL (EOI), TAG_EL (EOFILE), /* end of insn with variable number operands (e.g. a call) or end of file */ /* unsigned integer 0..127 is kept in one byte. The most significant bit of the byte is 1: */ U0_MASK = 0x7f, U0_FLAG = 0x80, } bin_tag_t; #undef REP_SEP /* MIR binary format: VERSION NSTR (string)* ( ((label)* (insn code) (operand)* | STRN=(func|local|import|export|forward|) ...) EOI? )* EOF where o VERSION and NSTR are unsigned tokens o insn code is unsigned token o string is string number tokens o operand is unsigned, signed, float, double, string, label, memory tokens o EOI, EOF - tokens for end of insn (optional for most insns) and end of file */ static const int CURR_BIN_VERSION = 1; DEF_VARR (MIR_str_t); DEF_VARR (uint64_t); DEF_VARR (MIR_label_t); struct io_ctx { FILE *io_file; int (*io_writer) (MIR_context_t, uint8_t); int (*io_reader) (MIR_context_t); struct reduce_data *io_reduce_data; VARR (MIR_var_t) * proto_vars; VARR (MIR_type_t) * proto_types; VARR (MIR_op_t) * read_insn_ops; VARR (string_t) * output_strings; HTAB (string_t) * output_string_tab; VARR (MIR_str_t) * bin_strings; VARR (uint64_t) * insn_label_string_nums; VARR (MIR_label_t) * func_labels; size_t output_insns_len, output_labs_len; size_t output_regs_len, output_mem_len, output_int_len, output_float_len; }; #define io_file ctx->io_ctx->io_file #define io_writer ctx->io_ctx->io_writer #define io_reader ctx->io_ctx->io_reader #define io_reduce_data ctx->io_ctx->io_reduce_data #define proto_vars ctx->io_ctx->proto_vars #define proto_types ctx->io_ctx->proto_types #define read_insn_ops ctx->io_ctx->read_insn_ops #define output_strings ctx->io_ctx->output_strings #define output_string_tab ctx->io_ctx->output_string_tab #define bin_strings ctx->io_ctx->bin_strings #define insn_label_string_nums ctx->io_ctx->insn_label_string_nums #define func_labels ctx->io_ctx->func_labels #define output_insns_len ctx->io_ctx->output_insns_len #define output_labs_len ctx->io_ctx->output_labs_len #define output_regs_len ctx->io_ctx->output_regs_len #define output_mem_len ctx->io_ctx->output_mem_len #define output_int_len ctx->io_ctx->output_int_len #define output_float_len ctx->io_ctx->output_float_len typedef reduce_writer_t writer_func_t; static size_t put_byte (MIR_context_t ctx, writer_func_t writer, int ch) { if (writer == NULL) return 0; #ifdef MIR_NO_BIN_COMPRESSION io_writer (ctx, ch); #else reduce_encode_put (io_reduce_data, ch); #endif return 1; } static size_t uint_length (uint64_t u) { size_t n; if (u <= 127) return 0; for (n = 0; u != 0; n++) u >>= CHAR_BIT; return n; } static size_t put_uint (MIR_context_t ctx, writer_func_t writer, uint64_t u, int nb) { if (writer == NULL) return 0; for (int n = 0; n < nb; n++) { put_byte (ctx, writer, u & 0xff); u >>= CHAR_BIT; } return nb; } static size_t int_length (int64_t i) { uint64_t u = i; size_t n = 0; for (n = 0; u != 0; n++) u >>= CHAR_BIT; return n == 0 ? 1 : n; } static size_t put_int (MIR_context_t ctx, writer_func_t writer, int64_t i, int nb) { return put_uint (ctx, writer, (uint64_t) i, nb); } static size_t put_float (MIR_context_t ctx, writer_func_t writer, float fl) { union { uint32_t u; float f; } u; if (writer == NULL) return 0; u.f = fl; return put_uint (ctx, writer, u.u, sizeof (uint32_t)); } static size_t put_double (MIR_context_t ctx, writer_func_t writer, double d) { union { uint64_t u; double d; } u; if (writer == NULL) return 0; u.d = d; return put_uint (ctx, writer, u.u, sizeof (uint64_t)); } static size_t put_ldouble (MIR_context_t ctx, writer_func_t writer, long double ld) { union { uint64_t u[2]; long double ld; } u; size_t len; if (writer == NULL) return 0; u.ld = ld; len = put_uint (ctx, writer, u.u[0], sizeof (uint64_t)); return put_uint (ctx, writer, u.u[1], sizeof (uint64_t)) + len; } /* Write binary MIR */ static size_t write_int (MIR_context_t ctx, writer_func_t writer, int64_t i) { size_t nb, len; if (writer == NULL) return 0; nb = int_length (i); assert (nb > 0); put_byte (ctx, writer, TAG_I1 + nb - 1); len = put_int (ctx, writer, i, nb) + 1; output_int_len += len; return len; } static size_t write_uint (MIR_context_t ctx, writer_func_t writer, uint64_t u) { size_t nb, len; if (writer == NULL) return 0; if ((nb = uint_length (u)) == 0) { put_byte (ctx, writer, 0x80 | u); return 1; } put_byte (ctx, writer, TAG_U1 + nb - 1); len = put_uint (ctx, writer, u, nb) + 1; output_int_len += len; return len; } static size_t write_float (MIR_context_t ctx, writer_func_t writer, float fl) { size_t len; if (writer == NULL) return 0; put_byte (ctx, writer, TAG_F); len = put_float (ctx, writer, fl) + 1; output_float_len += len; return len; } static size_t write_double (MIR_context_t ctx, writer_func_t writer, double d) { size_t len; if (writer == NULL) return 0; put_byte (ctx, writer, TAG_D); len = put_double (ctx, writer, d) + 1; output_float_len += len; return len; } static size_t write_ldouble (MIR_context_t ctx, writer_func_t writer, long double ld) { size_t len; if (writer == NULL) return 0; put_byte (ctx, writer, TAG_LD); len = put_ldouble (ctx, writer, ld) + 1; output_int_len += len; return len; } static size_t write_str_tag (MIR_context_t ctx, writer_func_t writer, MIR_str_t str, bin_tag_t start_tag) { size_t nb; int ok_p; string_t string; if (writer == NULL) { string_store (ctx, &output_strings, &output_string_tab, str); return 0; } ok_p = string_find (&output_strings, &output_string_tab, str, &string); mir_assert (ok_p && string.num >= 1); nb = uint_length (string.num - 1); mir_assert (nb <= 4); if (nb == 0) nb = 1; put_byte (ctx, writer, start_tag + nb - 1); return put_uint (ctx, writer, string.num - 1, nb) + 1; } static size_t write_str (MIR_context_t ctx, writer_func_t writer, MIR_str_t str) { return write_str_tag (ctx, writer, str, TAG_STR1); } static size_t write_name (MIR_context_t ctx, writer_func_t writer, const char *name) { return write_str_tag (ctx, writer, (MIR_str_t){strlen (name) + 1, name}, TAG_NAME1); } static size_t write_reg (MIR_context_t ctx, writer_func_t writer, const char *reg_name) { size_t len = write_str_tag (ctx, writer, (MIR_str_t){strlen (reg_name) + 1, reg_name}, TAG_REG1); output_regs_len += len; return len; } static size_t write_type (MIR_context_t ctx, writer_func_t writer, MIR_type_t t) { return put_byte (ctx, writer, TAG_TI8 + (t - MIR_T_I8)); } static size_t write_lab (MIR_context_t ctx, writer_func_t writer, MIR_label_t lab) { size_t nb, len; uint64_t lab_num; if (writer == NULL) return 0; lab_num = lab->ops[0].u.u; nb = uint_length (lab_num); mir_assert (nb <= 4); if (nb == 0) nb = 1; put_byte (ctx, writer, TAG_LAB1 + nb - 1); len = put_uint (ctx, writer, lab_num, nb) + 1; output_labs_len += len; return len; } static size_t write_op (MIR_context_t ctx, writer_func_t writer, MIR_func_t func, MIR_op_t op) { switch (op.mode) { case MIR_OP_REG: return write_reg (ctx, writer, MIR_reg_name (ctx, op.u.reg, func)); case MIR_OP_INT: return write_int (ctx, writer, op.u.i); case MIR_OP_UINT: return write_uint (ctx, writer, op.u.u); case MIR_OP_FLOAT: return write_float (ctx, writer, op.u.f); case MIR_OP_DOUBLE: return write_double (ctx, writer, op.u.d); case MIR_OP_LDOUBLE: return write_ldouble (ctx, writer, op.u.ld); case MIR_OP_MEM: { bin_tag_t tag; size_t len; if (op.u.mem.disp != 0) { if (op.u.mem.base != 0) tag = op.u.mem.index != 0 ? TAG_MEM_DISP_BASE_INDEX : TAG_MEM_DISP_BASE; else tag = op.u.mem.index != 0 ? TAG_MEM_DISP_INDEX : TAG_MEM_DISP; } else if (op.u.mem.base != 0) { tag = op.u.mem.index != 0 ? TAG_MEM_BASE_INDEX : TAG_MEM_BASE; } else if (op.u.mem.index != 0) { tag = TAG_MEM_INDEX; } else { tag = TAG_MEM_DISP; } put_byte (ctx, writer, tag); len = write_type (ctx, writer, op.u.mem.type) + 1; if (op.u.mem.disp != 0 || (op.u.mem.base == 0 && op.u.mem.index == 0)) write_int (ctx, writer, op.u.mem.disp); if (op.u.mem.base != 0) write_reg (ctx, writer, MIR_reg_name (ctx, op.u.mem.base, func)); if (op.u.mem.index != 0) { len += write_reg (ctx, writer, MIR_reg_name (ctx, op.u.mem.index, func)); len += write_uint (ctx, writer, op.u.mem.scale); } output_mem_len += len; return len; } case MIR_OP_REF: return write_name (ctx, writer, MIR_item_name (ctx, op.u.ref)); case MIR_OP_STR: return write_str (ctx, writer, op.u.str); case MIR_OP_LABEL: return write_lab (ctx, writer, op.u.label); default: mir_assert (FALSE); return 0; } } static size_t write_insn (MIR_context_t ctx, writer_func_t writer, MIR_func_t func, MIR_insn_t insn) { size_t i, nops; MIR_insn_code_t code = insn->code; size_t len; if (code == MIR_UNSPEC || code == MIR_PHI) MIR_get_error_func (ctx) (MIR_binary_io_error, "UNSPEC or PHI is not portable and can not be output"); if (code == MIR_LABEL) return write_lab (ctx, writer, insn); nops = MIR_insn_nops (ctx, insn); len = write_uint (ctx, writer, code); for (i = 0; i < nops; i++) len += write_op (ctx, writer, func, insn->ops[i]); if (insn_descs[code].op_modes[0] == MIR_OP_BOUND) { /* first operand mode is undefined if it is a variable operand insn */ mir_assert (MIR_call_code_p (code) || code == MIR_RET || code == MIR_SWITCH); put_byte (ctx, writer, TAG_EOI); len++; } output_insns_len += len; return len; } static size_t write_item (MIR_context_t ctx, writer_func_t writer, MIR_item_t item) { MIR_insn_t insn; MIR_func_t func; MIR_proto_t proto; MIR_var_t var; size_t i, nlocals, len = 0; if (item->item_type == MIR_import_item) { len += write_name (ctx, writer, "import"); len += write_name (ctx, writer, item->u.import_id); return len; } if (item->item_type == MIR_export_item) { len += write_name (ctx, writer, "export"); len += write_name (ctx, writer, item->u.export_id); return len; } if (item->item_type == MIR_forward_item) { len += write_name (ctx, writer, "forward"); len += write_name (ctx, writer, item->u.forward_id); return len; } if (item->item_type == MIR_bss_item) { if (item->u.bss->name == NULL) { len += write_name (ctx, writer, "bss"); } else { len += write_name (ctx, writer, "nbss"); len += write_name (ctx, writer, item->u.bss->name); } len += write_uint (ctx, writer, item->u.bss->len); return len; } if (item->item_type == MIR_ref_data_item) { if (item->u.ref_data->name == NULL) { len += write_name (ctx, writer, "ref"); } else { len += write_name (ctx, writer, "nref"); len += write_name (ctx, writer, item->u.ref_data->name); } len += write_name (ctx, writer, MIR_item_name (ctx, item->u.ref_data->ref_item)); len += write_int (ctx, writer, item->u.ref_data->disp); return len; } if (item->item_type == MIR_expr_data_item) { if (item->u.expr_data->name == NULL) { len += write_name (ctx, writer, "expr"); } else { len += write_name (ctx, writer, "nexpr"); len += write_name (ctx, writer, item->u.expr_data->name); } len += write_name (ctx, writer, MIR_item_name (ctx, item->u.expr_data->expr_item)); return len; } if (item->item_type == MIR_data_item) { MIR_data_t data = item->u.data; if (data->name == NULL) { len += write_name (ctx, writer, "data"); } else { len += write_name (ctx, writer, "ndata"); len += write_name (ctx, writer, data->name); } write_type (ctx, writer, data->el_type); for (i = 0; i < data->nel; i++) switch (data->el_type) { case MIR_T_I8: len += write_int (ctx, writer, ((int8_t *) data->u.els)[i]); break; case MIR_T_U8: len += write_uint (ctx, writer, ((uint8_t *) data->u.els)[i]); break; case MIR_T_I16: len += write_int (ctx, writer, ((int16_t *) data->u.els)[i]); break; case MIR_T_U16: len += write_uint (ctx, writer, ((uint16_t *) data->u.els)[i]); break; case MIR_T_I32: len += write_int (ctx, writer, ((int32_t *) data->u.els)[i]); break; case MIR_T_U32: len += write_uint (ctx, writer, ((uint32_t *) data->u.els)[i]); break; case MIR_T_I64: len += write_int (ctx, writer, ((int64_t *) data->u.els)[i]); break; case MIR_T_U64: len += write_uint (ctx, writer, ((uint64_t *) data->u.els)[i]); break; case MIR_T_F: len += write_float (ctx, writer, ((float *) data->u.els)[i]); break; case MIR_T_D: len += write_double (ctx, writer, ((double *) data->u.els)[i]); break; case MIR_T_LD: len += write_ldouble (ctx, writer, ((long double *) data->u.els)[i]); break; /* only ptr as ref ??? */ case MIR_T_P: len += write_uint (ctx, writer, ((uintptr_t *) data->u.els)[i]); break; default: mir_assert (FALSE); } len += put_byte (ctx, writer, TAG_EOI); return len; } if (item->item_type == MIR_proto_item) { proto = item->u.proto; len += write_name (ctx, writer, "proto"); len += write_name (ctx, writer, proto->name); len += write_uint (ctx, writer, proto->vararg_p != 0); len += write_uint (ctx, writer, proto->nres); for (i = 0; i < proto->nres; i++) write_type (ctx, writer, proto->res_types[i]); for (i = 0; i < VARR_LENGTH (MIR_var_t, proto->args); i++) { var = VARR_GET (MIR_var_t, proto->args, i); len += write_type (ctx, writer, var.type); len += write_name (ctx, writer, var.name); if (MIR_all_blk_type_p (var.type)) len += write_uint (ctx, writer, var.size); } len += put_byte (ctx, writer, TAG_EOI); return len; } func = item->u.func; len += write_name (ctx, writer, "func"); len += write_name (ctx, writer, func->name); len += write_uint (ctx, writer, func->vararg_p != 0); len += write_uint (ctx, writer, func->nres); for (i = 0; i < func->nres; i++) len += write_type (ctx, writer, func->res_types[i]); for (i = 0; i < func->nargs; i++) { var = VARR_GET (MIR_var_t, func->vars, i); len += write_type (ctx, writer, var.type); len += write_name (ctx, writer, var.name); if (MIR_all_blk_type_p (var.type)) len += write_uint (ctx, writer, var.size); } len += put_byte (ctx, writer, TAG_EOI); nlocals = VARR_LENGTH (MIR_var_t, func->vars) - func->nargs; if (nlocals != 0) { len += write_name (ctx, writer, "local"); for (i = 0; i < nlocals; i++) { var = VARR_GET (MIR_var_t, func->vars, i + func->nargs); len += write_type (ctx, writer, var.type); len += write_name (ctx, writer, var.name); } len += put_byte (ctx, writer, TAG_EOI); } for (insn = DLIST_HEAD (MIR_insn_t, func->insns); insn != NULL; insn = DLIST_NEXT (MIR_insn_t, insn)) len += write_insn (ctx, writer, func, insn); len += write_name (ctx, writer, "endfunc"); return len; } static size_t write_module (MIR_context_t ctx, writer_func_t writer, MIR_module_t module) { size_t len = write_name (ctx, writer, "module"); len += write_name (ctx, writer, module->name); for (MIR_item_t item = DLIST_HEAD (MIR_item_t, module->items); item != NULL; item = DLIST_NEXT (MIR_item_t, item)) len += write_item (ctx, writer, item); len += write_name (ctx, writer, "endmodule"); return len; } static size_t write_modules (MIR_context_t ctx, writer_func_t writer, MIR_module_t module) { size_t len = 0; for (MIR_module_t m = DLIST_HEAD (MIR_module_t, all_modules); m != NULL; m = DLIST_NEXT (MIR_module_t, m)) if (module == NULL || m == module) len += write_module (ctx, writer, m); return len; } static size_t reduce_writer (const void *start, size_t len, void *aux_data) { MIR_context_t ctx = aux_data; size_t i, n = 0; for (i = n = 0; i < len; i++, n++) if (io_writer (ctx, ((uint8_t *) start)[i]) == EOF) break; return n; } void MIR_write_module_with_func (MIR_context_t ctx, int (*const writer) (MIR_context_t, uint8_t), MIR_module_t module) { size_t len, str_len; io_writer = writer; #ifndef MIR_NO_BIN_COMPRESSION if ((io_reduce_data = reduce_encode_start (reduce_writer, ctx)) == NULL) MIR_get_error_func (ctx) (MIR_binary_io_error, "can not alloc data for MIR binary compression"); #endif output_insns_len = output_labs_len = 0; output_regs_len = output_mem_len = output_int_len = output_float_len = 0; string_init (&output_strings, &output_string_tab); write_modules (ctx, NULL, module); /* store strings */ len = write_uint (ctx, reduce_writer, CURR_BIN_VERSION); str_len = write_uint (ctx, reduce_writer, VARR_LENGTH (string_t, output_strings) - 1); for (size_t i = 1; i < VARR_LENGTH (string_t, output_strings); i++) { /* output strings */ MIR_str_t str = VARR_GET (string_t, output_strings, i).str; str_len += write_uint (ctx, reduce_writer, str.len); for (size_t j = 0; j < str.len; j++) { put_byte (ctx, reduce_writer, str.s[j]); str_len++; } } len += write_modules (ctx, reduce_writer, module) + str_len; #if 0 fprintf (stderr, "Overall output length = %lu. Number of strings = %lu.\n" "Lengths of: strings = %lu, insns = %lu, labs = %lu,\n" " reg ops = %lu, mem ops = %lu, int ops = %lu, float ops = %lu\n", len, VARR_LENGTH (string_t, output_strings), str_len, output_insns_len, output_labs_len, output_regs_len, output_mem_len, output_int_len, output_float_len); #endif put_byte (ctx, reduce_writer, TAG_EOFILE); string_finish (&output_strings, &output_string_tab); #ifndef MIR_NO_BIN_COMPRESSION if (!reduce_encode_finish (io_reduce_data)) MIR_get_error_func (ctx) (MIR_binary_io_error, "error in writing MIR binary"); #endif } void MIR_write_with_func (MIR_context_t ctx, int (*const writer) (MIR_context_t, uint8_t)) { MIR_write_module_with_func (ctx, writer, NULL); } static int file_writer (MIR_context_t ctx, uint8_t byte) { return fputc (byte, io_file); } void MIR_write_module (MIR_context_t ctx, FILE *f, MIR_module_t module) { io_file = f; MIR_write_module_with_func (ctx, file_writer, module); } void MIR_write (MIR_context_t ctx, FILE *f) { MIR_write_module (ctx, f, NULL); } /* New Page */ static int get_byte (MIR_context_t ctx) { #ifdef MIR_NO_BIN_COMPRESSION int c = io_reader (ctx); #else int c = reduce_decode_get (io_reduce_data); #endif if (c == EOF) MIR_get_error_func (ctx) (MIR_binary_io_error, "unfinished binary MIR"); return c; } typedef union { uint64_t u; int64_t i; float f; double d; long double ld; MIR_type_t t; MIR_reg_t reg; } token_attr_t; static uint64_t get_uint (MIR_context_t ctx, int nb) { uint64_t res = 0; for (int i = 0; i < nb; i++) res |= (uint64_t) get_byte (ctx) << (i * CHAR_BIT); return res; } static int64_t get_int (MIR_context_t ctx, int nb) { return (int64_t) get_uint (ctx, nb); } static float get_float (MIR_context_t ctx) { union { uint32_t u; float f; } u; u.u = get_uint (ctx, sizeof (uint32_t)); return u.f; } static double get_double (MIR_context_t ctx) { union { uint64_t u; double d; } u; u.u = get_uint (ctx, sizeof (uint64_t)); return u.d; } static long double get_ldouble (MIR_context_t ctx) { union { uint64_t u[2]; long double ld; } u; u.u[0] = get_uint (ctx, sizeof (uint64_t)); u.u[1] = get_uint (ctx, sizeof (uint64_t)); return u.ld; } static MIR_str_t to_str (MIR_context_t ctx, uint64_t str_num) { if (str_num >= VARR_LENGTH (MIR_str_t, bin_strings)) MIR_get_error_func (ctx) (MIR_binary_io_error, "wrong string num %lu", str_num); return VARR_GET (MIR_str_t, bin_strings, str_num); } static void process_reserved_name (const char *s, const char *prefix, uint32_t *max_num) { char *end; uint32_t num; size_t len = strlen (prefix); if (strncmp (s, prefix, len) != 0) return; num = strtoul (s + len, &end, 10); if (*end != '\0') return; if (*max_num < num) *max_num = num; } static MIR_reg_t to_reg (MIR_context_t ctx, uint64_t reg_str_num, MIR_item_t func) { const char *s = to_str (ctx, reg_str_num).s; process_reserved_name (s, TEMP_REG_NAME_PREFIX, &func->u.func->last_temp_num); return MIR_reg (ctx, s, func->u.func); } static MIR_label_t to_lab (MIR_context_t ctx, uint64_t lab_num) { MIR_label_t lab; while (lab_num >= VARR_LENGTH (MIR_label_t, func_labels)) VARR_PUSH (MIR_label_t, func_labels, NULL); if ((lab = VARR_GET (MIR_label_t, func_labels, lab_num)) != NULL) return lab; lab = create_label (ctx, lab_num); VARR_SET (MIR_label_t, func_labels, lab_num, lab); return lab; } static int64_t read_int (MIR_context_t ctx, const char *err_msg) { int c = get_byte (ctx); if (TAG_I1 > c || c > TAG_I8) MIR_get_error_func (ctx) (MIR_binary_io_error, err_msg); return get_int (ctx, c - TAG_I1 + 1); } static uint64_t read_uint (MIR_context_t ctx, const char *err_msg) { int c = get_byte (ctx); if (c & U0_FLAG) return c & U0_MASK; if (TAG_U1 > c || c > TAG_U8) MIR_get_error_func (ctx) (MIR_binary_io_error, err_msg); return get_uint (ctx, c - TAG_U1 + 1); } static void read_all_strings (MIR_context_t ctx, uint64_t nstr) { int c; MIR_str_t str; uint64_t len, l; VARR_TRUNC (MIR_str_t, bin_strings, 0); for (uint64_t i = 0; i < nstr; i++) { VARR_TRUNC (char, temp_string, 0); len = read_uint (ctx, "wrong string length"); for (l = 0; l < len; l++) { c = get_byte (ctx); VARR_PUSH (char, temp_string, c); } str.s = VARR_ADDR (char, temp_string); str.len = len; str = get_ctx_string (ctx, str).str; VARR_PUSH (MIR_str_t, bin_strings, str); } } static MIR_type_t tag_type (bin_tag_t tag) { return (MIR_type_t) (tag - TAG_TI8) + MIR_T_I8; } static MIR_type_t read_type (MIR_context_t ctx, const char *err_msg) { int c = get_byte (ctx); if (TAG_TI8 > c || c > TAG_TRBLOCK) MIR_get_error_func (ctx) (MIR_binary_io_error, err_msg); return tag_type (c); } static const char *read_name (MIR_context_t ctx, MIR_module_t module, const char *err_msg) { int c = get_byte (ctx); const char *s; if (TAG_NAME1 > c || c > TAG_NAME4) MIR_get_error_func (ctx) (MIR_binary_io_error, err_msg); s = to_str (ctx, get_uint (ctx, c - TAG_NAME1 + 1)).s; if (module != NULL) process_reserved_name (s, TEMP_ITEM_NAME_PREFIX, &module->last_temp_item_num); return s; } #define TAG_CASE(t) case TAG_##t: #define REP_SEP static bin_tag_t read_token (MIR_context_t ctx, token_attr_t *attr) { int c = get_byte (ctx); if (c & U0_FLAG) { attr->u = c & U0_MASK; return TAG_U0; } switch (c) { REP8 (TAG_CASE, U1, U2, U3, U4, U5, U6, U7, U8) attr->u = get_uint (ctx, c - TAG_U1 + 1); break; REP8 (TAG_CASE, I1, I2, I3, I4, I5, I6, I7, I8) attr->i = get_int (ctx, c - TAG_I1 + 1); break; TAG_CASE (F) attr->f = get_float (ctx); break; TAG_CASE (D) attr->d = get_double (ctx); break; TAG_CASE (LD) attr->ld = get_ldouble (ctx); break; REP4 (TAG_CASE, REG1, REG2, REG3, REG4) attr->u = get_uint (ctx, c - TAG_REG1 + 1); break; REP4 (TAG_CASE, NAME1, NAME2, NAME3, NAME4) attr->u = get_uint (ctx, c - TAG_NAME1 + 1); break; REP4 (TAG_CASE, STR1, STR2, STR3, STR4) attr->u = get_uint (ctx, c - TAG_STR1 + 1); break; REP4 (TAG_CASE, LAB1, LAB2, LAB3, LAB4) attr->u = get_uint (ctx, c - TAG_LAB1 + 1); break; REP6 (TAG_CASE, MEM_DISP, MEM_BASE, MEM_INDEX, MEM_DISP_BASE, MEM_DISP_INDEX, MEM_BASE_INDEX) REP3 (TAG_CASE, MEM_DISP_BASE_INDEX, EOI, EOFILE) break; REP8 (TAG_CASE, TI8, TU8, TI16, TU16, TI32, TU32, TI64, TU64) REP5 (TAG_CASE, TF, TD, TP, TV, TRBLOCK) attr->t = (MIR_type_t) (c - TAG_TI8) + MIR_T_I8; break; default: if (TAG_TBLOCK <= c && c < TAG_TBLOCK + MIR_BLK_NUM) { attr->t = (MIR_type_t) (c - TAG_TBLOCK) + MIR_T_BLK; break; } MIR_get_error_func (ctx) (MIR_binary_io_error, "wrong tag %d", c); } return c; } static MIR_disp_t read_disp (MIR_context_t ctx) { bin_tag_t tag; token_attr_t attr; tag = read_token (ctx, &attr); if (TAG_I1 > tag || tag > TAG_I8) MIR_get_error_func (ctx) (MIR_binary_io_error, "memory disp has wrong tag %d", tag); return attr.i; } static MIR_reg_t read_reg (MIR_context_t ctx, MIR_item_t func) { bin_tag_t tag; token_attr_t attr; tag = read_token (ctx, &attr); if (TAG_REG1 > tag || tag > TAG_REG4) MIR_get_error_func (ctx) (MIR_binary_io_error, "register has wrong tag %d", tag); return to_reg (ctx, attr.u, func); } static int read_operand (MIR_context_t ctx, MIR_op_t *op, MIR_item_t func) { bin_tag_t tag; token_attr_t attr; MIR_type_t t; MIR_disp_t disp; MIR_reg_t base, index; MIR_scale_t scale; tag = read_token (ctx, &attr); switch (tag) { TAG_CASE (U0) REP8 (TAG_CASE, U1, U2, U3, U4, U5, U6, U7, U8) *op = MIR_new_uint_op (ctx, attr.u); break; REP8 (TAG_CASE, I1, I2, I3, I4, I5, I6, I7, I8) *op = MIR_new_int_op (ctx, attr.i); break; TAG_CASE (F) *op = MIR_new_float_op (ctx, attr.f); break; TAG_CASE (D) *op = MIR_new_double_op (ctx, attr.d); break; TAG_CASE (LD) *op = MIR_new_ldouble_op (ctx, attr.ld); break; REP4 (TAG_CASE, REG1, REG2, REG3, REG4) *op = MIR_new_reg_op (ctx, to_reg (ctx, attr.u, func)); break; REP4 (TAG_CASE, NAME1, NAME2, NAME3, NAME4) { const char *name = to_str (ctx, attr.u).s; MIR_item_t item = item_tab_find (ctx, name, func->module); if (item == NULL) MIR_get_error_func (ctx) (MIR_binary_io_error, "not found item %s", name); *op = MIR_new_ref_op (ctx, item); break; } REP4 (TAG_CASE, STR1, STR2, STR3, STR4) *op = MIR_new_str_op (ctx, to_str (ctx, attr.u)); break; REP4 (TAG_CASE, LAB1, LAB2, LAB3, LAB4) *op = MIR_new_label_op (ctx, to_lab (ctx, attr.u)); break; REP7 (TAG_CASE, MEM_DISP, MEM_BASE, MEM_INDEX, MEM_DISP_BASE, MEM_DISP_INDEX, MEM_BASE_INDEX, MEM_DISP_BASE_INDEX) t = read_type (ctx, "wrong memory type"); disp = (tag == TAG_MEM_DISP || tag == TAG_MEM_DISP_BASE || tag == TAG_MEM_DISP_INDEX || tag == TAG_MEM_DISP_BASE_INDEX ? read_disp (ctx) : 0); base = (tag == TAG_MEM_BASE || tag == TAG_MEM_DISP_BASE || tag == TAG_MEM_BASE_INDEX || tag == TAG_MEM_DISP_BASE_INDEX ? read_reg (ctx, func) : 0); index = 0; scale = 0; if (tag == TAG_MEM_INDEX || tag == TAG_MEM_DISP_INDEX || tag == TAG_MEM_BASE_INDEX || tag == TAG_MEM_DISP_BASE_INDEX) { index = read_reg (ctx, func); scale = read_uint (ctx, "wrong memory index scale"); } *op = MIR_new_mem_op (ctx, t, disp, base, index, scale); break; case TAG_EOI: return FALSE; default: mir_assert (FALSE); } return TRUE; } #undef REP_SEP static int func_proto_read (MIR_context_t ctx, MIR_module_t module, uint64_t *nres_ptr) { bin_tag_t tag; token_attr_t attr; MIR_var_t var; int vararg_p = read_uint (ctx, "wrong vararg flag") != 0; uint64_t i, nres = read_uint (ctx, "wrong func nres"); VARR_TRUNC (MIR_type_t, proto_types, 0); for (i = 0; i < nres; i++) { tag = read_token (ctx, &attr); if (TAG_TI8 > tag || tag > TAG_TRBLOCK) MIR_get_error_func (ctx) (MIR_binary_io_error, "wrong prototype result type tag %d", tag); VARR_PUSH (MIR_type_t, proto_types, tag_type (tag)); } VARR_TRUNC (MIR_var_t, proto_vars, 0); for (;;) { tag = read_token (ctx, &attr); if (tag == TAG_EOI) break; if (TAG_TI8 > tag || tag > TAG_TRBLOCK) MIR_get_error_func (ctx) (MIR_binary_io_error, "wrong prototype arg type tag %d", tag); var.type = tag_type (tag); var.name = read_name (ctx, module, "wrong arg name"); if (MIR_all_blk_type_p (var.type)) var.size = read_uint (ctx, "wrong block arg size"); VARR_PUSH (MIR_var_t, proto_vars, var); } *nres_ptr = nres; return vararg_p; } #ifndef MIR_NO_BIN_COMPRESSION static size_t reduce_reader (void *start, size_t len, void *data) { MIR_context_t ctx = data; size_t i; int c; for (i = 0; i < len && (c = io_reader (ctx)) != EOF; i++) ((char *) start)[i] = c; return i; } #endif void MIR_read_with_func (MIR_context_t ctx, int (*const reader) (MIR_context_t)) { int version; bin_tag_t tag; token_attr_t attr; MIR_label_t lab; uint64_t nstr, nres, u; int64_t i; MIR_op_t op; size_t n, nop; const char *name, *item_name; MIR_module_t module; MIR_item_t func, item; io_reader = reader; #ifndef MIR_NO_BIN_COMPRESSION if ((io_reduce_data = reduce_decode_start (reduce_reader, ctx)) == NULL) MIR_get_error_func (ctx) (MIR_binary_io_error, "can not alloc data for MIR binary decompression"); #endif version = read_uint (ctx, "wrong header"); if (version > CURR_BIN_VERSION) MIR_get_error_func (ctx) (MIR_binary_io_error, "can not read version %d MIR binary: expected %d or less", version, CURR_BIN_VERSION); nstr = read_uint (ctx, "wrong header"); read_all_strings (ctx, nstr); module = NULL; func = NULL; for (;;) { VARR_TRUNC (uint64_t, insn_label_string_nums, 0); tag = read_token (ctx, &attr); while (TAG_LAB1 <= tag && tag <= TAG_LAB4) { VARR_PUSH (uint64_t, insn_label_string_nums, attr.u); tag = read_token (ctx, &attr); } VARR_TRUNC (MIR_op_t, read_insn_ops, 0); if (TAG_NAME1 <= tag && tag <= TAG_NAME4) { name = to_str (ctx, attr.u).s; if (strcmp (name, "module") == 0) { name = read_name (ctx, module, "wrong module name"); if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) MIR_get_error_func (ctx) (MIR_binary_io_error, "insn label before module %s", name); if (module != NULL) MIR_get_error_func (ctx) (MIR_binary_io_error, "nested module %s", name); module = MIR_new_module (ctx, name); } else if (strcmp (name, "endmodule") == 0) { if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) MIR_get_error_func (ctx) (MIR_binary_io_error, "endmodule should have no labels"); if (module == NULL) MIR_get_error_func (ctx) (MIR_binary_io_error, "endmodule without module"); MIR_finish_module (ctx); module = NULL; } else if (strcmp (name, "proto") == 0) { name = read_name (ctx, module, "wrong prototype name"); if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) MIR_get_error_func (ctx) (MIR_binary_io_error, "insn label before proto %s", name); if (module == NULL) MIR_get_error_func (ctx) (MIR_binary_io_error, "prototype %s outside module", name); if (func_proto_read (ctx, module, &nres)) MIR_new_vararg_proto_arr (ctx, name, nres, VARR_ADDR (MIR_type_t, proto_types), VARR_LENGTH (MIR_var_t, proto_vars), VARR_ADDR (MIR_var_t, proto_vars)); else MIR_new_proto_arr (ctx, name, nres, VARR_ADDR (MIR_type_t, proto_types), VARR_LENGTH (MIR_var_t, proto_vars), VARR_ADDR (MIR_var_t, proto_vars)); } else if (strcmp (name, "func") == 0) { name = read_name (ctx, module, "wrong func name"); if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) MIR_get_error_func (ctx) (MIR_binary_io_error, "insn label before func %s", name); if (func != NULL) MIR_get_error_func (ctx) (MIR_binary_io_error, "nested func %s", name); if (module == NULL) MIR_get_error_func (ctx) (MIR_binary_io_error, "func %s outside module", name); if (func_proto_read (ctx, module, &nres)) func = MIR_new_vararg_func_arr (ctx, name, nres, VARR_ADDR (MIR_type_t, proto_types), VARR_LENGTH (MIR_var_t, proto_vars), VARR_ADDR (MIR_var_t, proto_vars)); else func = MIR_new_func_arr (ctx, name, nres, VARR_ADDR (MIR_type_t, proto_types), VARR_LENGTH (MIR_var_t, proto_vars), VARR_ADDR (MIR_var_t, proto_vars)); VARR_TRUNC (MIR_label_t, func_labels, 0); } else if (strcmp (name, "endfunc") == 0) { if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) MIR_get_error_func (ctx) (MIR_binary_io_error, "endfunc should have no labels"); if (func == NULL) MIR_get_error_func (ctx) (MIR_binary_io_error, "endfunc without func"); MIR_finish_func (ctx); func = NULL; } else if (strcmp (name, "export") == 0) { name = read_name (ctx, module, "wrong export name"); if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) MIR_get_error_func (ctx) (MIR_binary_io_error, "export %s should have no labels", name); MIR_new_export (ctx, name); } else if (strcmp (name, "import") == 0) { name = read_name (ctx, module, "wrong import name"); if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) MIR_get_error_func (ctx) (MIR_binary_io_error, "import %s should have no labels", name); MIR_new_import (ctx, name); } else if (strcmp (name, "forward") == 0) { name = read_name (ctx, module, "wrong forward name"); if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) MIR_get_error_func (ctx) (MIR_binary_io_error, "forward %s should have no labels", name); MIR_new_forward (ctx, name); } else if (strcmp (name, "nbss") == 0 || strcmp (name, "bss") == 0) { name = strcmp (name, "nbss") == 0 ? read_name (ctx, module, "wrong bss name") : NULL; if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) MIR_get_error_func (ctx) (MIR_binary_io_error, "bss %s should have no labels", name == NULL ? "" : name); u = read_uint (ctx, "wrong bss len"); MIR_new_bss (ctx, name, u); } else if (strcmp (name, "nref") == 0 || strcmp (name, "ref") == 0) { name = strcmp (name, "nref") == 0 ? read_name (ctx, module, "wrong ref data name") : NULL; if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) MIR_get_error_func (ctx) (MIR_binary_io_error, "ref data %s should have no labels", name == NULL ? "" : name); item_name = read_name (ctx, module, "wrong ref data item name"); if ((item = item_tab_find (ctx, item_name, module)) == NULL) MIR_get_error_func (ctx) (MIR_binary_io_error, "ref data refers to non-existing item %s", item_name); i = read_int (ctx, "wrong ref disp"); MIR_new_ref_data (ctx, name, item, i); } else if (strcmp (name, "nexpr") == 0 || strcmp (name, "expr") == 0) { name = strcmp (name, "nexpr") == 0 ? read_name (ctx, module, "wrong expr name") : NULL; if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) MIR_get_error_func (ctx) (MIR_binary_io_error, "expr %s should have no labels", name == NULL ? "" : name); item_name = read_name (ctx, module, "wrong expr func name"); if ((item = item_tab_find (ctx, item_name, module)) == NULL || item->item_type != MIR_func_item) MIR_get_error_func (ctx) (MIR_binary_io_error, "expr refers to non-function %s", item_name); MIR_new_expr_data (ctx, name, item); } else if (strcmp (name, "ndata") == 0 || strcmp (name, "data") == 0) { MIR_type_t type; size_t nel; union { uint8_t u8; uint16_t u16; uint32_t u32; uint64_t u64; int8_t i8; int16_t i16; int32_t i32; int64_t i64; } v; name = strcmp (name, "ndata") == 0 ? read_name (ctx, module, "wrong data name") : NULL; if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) MIR_get_error_func (ctx) (MIR_binary_io_error, "data %s should have no labels", name == NULL ? "" : name); tag = read_token (ctx, &attr); if (TAG_TI8 > tag || tag > TAG_TRBLOCK) MIR_get_error_func (ctx) (MIR_binary_io_error, "wrong data type tag %d", tag); type = tag_type (tag); VARR_TRUNC (uint8_t, temp_data, 0); for (nel = 0;; nel++) { tag = read_token (ctx, &attr); if (tag == TAG_EOI) break; switch (tag) { case TAG_U0: case TAG_U1: case TAG_U2: case TAG_U3: case TAG_U4: case TAG_U5: case TAG_U6: case TAG_U7: case TAG_U8: switch (type) { case MIR_T_U8: v.u8 = attr.u; push_data (ctx, &v.u8, sizeof (uint8_t)); break; case MIR_T_U16: v.u16 = attr.u; push_data (ctx, (uint8_t *) &v.u16, sizeof (uint16_t)); break; case MIR_T_U32: v.u32 = attr.u; push_data (ctx, (uint8_t *) &v.u32, sizeof (uint32_t)); break; case MIR_T_U64: v.u64 = attr.u; push_data (ctx, (uint8_t *) &v.i64, sizeof (uint64_t)); break; default: MIR_get_error_func (ctx) (MIR_binary_io_error, "data type %s does not correspond value type", type_str (ctx, type)); } break; case TAG_I1: case TAG_I2: case TAG_I3: case TAG_I4: case TAG_I5: case TAG_I6: case TAG_I7: case TAG_I8: switch (type) { case MIR_T_I8: v.i8 = attr.i; push_data (ctx, (uint8_t *) &v.i8, sizeof (int8_t)); break; case MIR_T_I16: v.i16 = attr.i; push_data (ctx, (uint8_t *) &v.i16, sizeof (int16_t)); break; case MIR_T_I32: v.i32 = attr.i; push_data (ctx, (uint8_t *) &v.i32, sizeof (int32_t)); break; case MIR_T_I64: v.i64 = attr.i; push_data (ctx, (uint8_t *) &v.i64, sizeof (int64_t)); break; default: MIR_get_error_func (ctx) (MIR_binary_io_error, "data type %s does not correspond value type", type_str (ctx, type)); } break; case TAG_F: if (type != MIR_T_F) MIR_get_error_func (ctx) (MIR_binary_io_error, "data type %s does not correspond value type", type_str (ctx, type)); push_data (ctx, (uint8_t *) &attr.f, sizeof (float)); break; case TAG_D: if (type != MIR_T_D) MIR_get_error_func (ctx) (MIR_binary_io_error, "data type %s does not correspond value type", type_str (ctx, type)); push_data (ctx, (uint8_t *) &attr.d, sizeof (double)); break; case TAG_LD: if (type != MIR_T_LD) MIR_get_error_func (ctx) (MIR_binary_io_error, "data type %s does not correspond value type", type_str (ctx, type)); push_data (ctx, (uint8_t *) &attr.ld, sizeof (long double)); break; /* ??? ptr */ default: MIR_get_error_func (ctx) (MIR_binary_io_error, "wrong data value tag %d", tag); } } MIR_new_data (ctx, name, type, VARR_LENGTH (uint8_t, temp_data) / _MIR_type_size (ctx, type), VARR_ADDR (uint8_t, temp_data)); } else if (strcmp (name, "local") == 0) { if (func == NULL) MIR_get_error_func (ctx) (MIR_binary_io_error, "local outside func"); if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) MIR_get_error_func (ctx) (MIR_binary_io_error, "local should have no labels"); for (;;) { tag = read_token (ctx, &attr); if (tag == TAG_EOI) break; if (TAG_TI8 > tag || tag > TAG_TRBLOCK) MIR_get_error_func (ctx) (MIR_binary_io_error, "wrong local var type tag %d", tag); MIR_new_func_reg (ctx, func->u.func, tag_type (tag), read_name (ctx, module, "wrong local var name")); } } else { MIR_get_error_func (ctx) (MIR_binary_io_error, "unknown insn name %s", name); } } else if (TAG_U0 <= tag && tag <= TAG_U8) { /* insn code */ MIR_insn_code_t insn_code = attr.u; if (insn_code >= MIR_LABEL) MIR_get_error_func (ctx) (MIR_binary_io_error, "wrong insn code %d", insn_code); if (insn_code == MIR_UNSPEC || insn_code == MIR_PHI) MIR_get_error_func (ctx) (MIR_binary_io_error, "UNSPEC or PHI is not portable and can not be read"); for (uint64_t i = 0; i < VARR_LENGTH (uint64_t, insn_label_string_nums); i++) { lab = to_lab (ctx, VARR_GET (uint64_t, insn_label_string_nums, i)); MIR_append_insn (ctx, func, lab); } nop = insn_code_nops (ctx, insn_code); mir_assert (nop != 0 || MIR_call_code_p (insn_code) || insn_code == MIR_RET || insn_code == MIR_SWITCH); for (n = 0; (nop == 0 || n < nop) && read_operand (ctx, &op, func); n++) VARR_PUSH (MIR_op_t, read_insn_ops, op); if (nop != 0 && n < nop) MIR_get_error_func (ctx) (MIR_binary_io_error, "wrong number of operands of insn %s", insn_name (insn_code)); MIR_append_insn (ctx, func, MIR_new_insn_arr (ctx, insn_code, n, VARR_ADDR (MIR_op_t, read_insn_ops))); } else if (tag == TAG_EOFILE) { break; } else { MIR_get_error_func (ctx) (MIR_binary_io_error, "wrong token %d", tag); } } if (func != NULL) MIR_get_error_func (ctx) (MIR_binary_io_error, "unfinished func %s", func->u.func->name); if (module != NULL) MIR_get_error_func (ctx) (MIR_binary_io_error, "unfinished module %s", module->name); if (reader (ctx) != EOF) MIR_get_error_func (ctx) (MIR_binary_io_error, "garbage at the end of file"); #ifndef MIR_NO_BIN_COMPRESSION reduce_decode_finish (io_reduce_data); #endif } static int file_reader (MIR_context_t ctx) { return fgetc (io_file); } void MIR_read (MIR_context_t ctx, FILE *f) { io_file = f; MIR_read_with_func (ctx, file_reader); } static void io_init (MIR_context_t ctx) { mir_assert (TAG_EOFILE < 127); /* see bin_tag_t */ if ((ctx->io_ctx = malloc (sizeof (struct io_ctx))) == NULL) MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory for ctx"); VARR_CREATE (MIR_var_t, proto_vars, 0); VARR_CREATE (MIR_type_t, proto_types, 0); VARR_CREATE (MIR_op_t, read_insn_ops, 0); VARR_CREATE (MIR_str_t, bin_strings, 512); VARR_CREATE (uint64_t, insn_label_string_nums, 64); VARR_CREATE (MIR_label_t, func_labels, 512); } static void io_finish (MIR_context_t ctx) { VARR_DESTROY (MIR_label_t, func_labels); VARR_DESTROY (uint64_t, insn_label_string_nums); VARR_DESTROY (MIR_str_t, bin_strings); VARR_DESTROY (MIR_op_t, read_insn_ops); VARR_DESTROY (MIR_var_t, proto_vars); VARR_DESTROY (MIR_type_t, proto_types); free (ctx->io_ctx); ctx->io_ctx = NULL; } #endif /* if !MIR_NO_IO */ /* New Page */ /* Reading MIR text file */ #if !MIR_NO_SCAN #include #include #include #include #include typedef struct insn_name { const char *name; MIR_insn_code_t code; } insn_name_t; static int insn_name_eq (insn_name_t in1, insn_name_t in2, void *arg) { return strcmp (in1.name, in2.name) == 0; } static htab_hash_t insn_name_hash (insn_name_t in, void *arg) { return mir_hash (in.name, strlen (in.name), 0); } #define TC_EL(t) TC_##t #define REP_SEP , enum token_code { REP8 (TC_EL, INT, FLOAT, DOUBLE, LDOUBLE, NAME, STR, NL, EOFILE), REP5 (TC_EL, LEFT_PAR, RIGHT_PAR, COMMA, SEMICOL, COL), }; #undef REP_SEP typedef struct token { int code; /* enum token_code and EOF */ union { int64_t i; float f; double d; long double ld; const char *name; MIR_str_t str; } u; } token_t; DEF_HTAB (insn_name_t); typedef const char *label_name_t; DEF_VARR (label_name_t); typedef struct label_desc { const char *name; MIR_label_t label; } label_desc_t; DEF_HTAB (label_desc_t); struct scan_ctx { jmp_buf error_jmp_buf; /* keep it here to provide malloc alignment */ VARR (char) * error_msg_buf; VARR (MIR_var_t) * scan_vars; VARR (MIR_type_t) * scan_types; VARR (MIR_op_t) * scan_insn_ops; size_t curr_lno; HTAB (insn_name_t) * insn_name_tab; const char *input_string; size_t input_string_char_num; VARR (label_name_t) * label_names; HTAB (label_desc_t) * label_desc_tab; }; #define error_jmp_buf ctx->scan_ctx->error_jmp_buf #define error_msg_buf ctx->scan_ctx->error_msg_buf #define scan_vars ctx->scan_ctx->scan_vars #define scan_types ctx->scan_ctx->scan_types #define scan_insn_ops ctx->scan_ctx->scan_insn_ops #define curr_lno ctx->scan_ctx->curr_lno #define insn_name_tab ctx->scan_ctx->insn_name_tab #define input_string ctx->scan_ctx->input_string #define input_string_char_num ctx->scan_ctx->input_string_char_num #define label_names ctx->scan_ctx->label_names #define label_desc_tab ctx->scan_ctx->label_desc_tab static void scan_error (MIR_context_t ctx, const char *format, ...) { char message[150]; size_t len; va_list va; va_start (va, format); if (VARR_LENGTH (char, error_msg_buf) != 0) VARR_POP (char, error_msg_buf); /* remove last '\0' */ sprintf (message, "ln %lu: ", (unsigned long) curr_lno); VARR_PUSH_ARR (char, error_msg_buf, message, strlen (message)); len = vsnprintf (message, sizeof (message), format, va); VARR_PUSH_ARR (char, error_msg_buf, message, len); VARR_PUSH_ARR (char, error_msg_buf, "\n", 2); /* add '\n' and '\0' */ va_end (va); longjmp (error_jmp_buf, TRUE); } /* Read number using GET_CHAR and UNGET_CHAR and already read character CH. It should be guaranted that the input has a righ prefix (+|-)?[0-9]. Return base, float and double flag through BASE, FLOAT_P, DOUBLE_P. Put number representation (0x or 0X prefix is removed) into TEMP_STRING. */ static void scan_number (MIR_context_t ctx, int ch, int get_char (MIR_context_t), void unget_char (MIR_context_t, int), int *base, int *float_p, int *double_p, int *ldouble_p) { enum scan_number_code { NUMBER_OK, ABSENT_EXPONENT, NON_DECIMAL_FLOAT, WRONG_OCTAL_INT }; enum scan_number_code err_code = NUMBER_OK; int dec_p, hex_p, hex_char_p; *base = 10; *ldouble_p = *double_p = *float_p = FALSE; if (ch == '+' || ch == '-') { VARR_PUSH (char, temp_string, ch); ch = get_char (ctx); } mir_assert ('0' <= ch && ch <= '9'); if (ch == '0') { ch = get_char (ctx); if (ch != 'x' && ch != 'X') { *base = 8; unget_char (ctx, ch); ch = '0'; } else { ch = get_char (ctx); *base = 16; } } dec_p = hex_p = FALSE; for (;;) { if (ch != '_') VARR_PUSH (char, temp_string, ch); ch = get_char (ctx); if (ch == '8' || ch == '9') dec_p = TRUE; hex_char_p = (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')); if (ch != '_' && !isdigit (ch) && (*base != 16 || !hex_char_p)) break; if (hex_char_p) hex_p = TRUE; } mir_assert (*base == 16 || !hex_p); if (ch == '.') { *double_p = TRUE; do { if (ch != '_') VARR_PUSH (char, temp_string, ch); ch = get_char (ctx); } while (isdigit (ch) || ch == '_'); } if (ch == 'e' || ch == 'E') { *double_p = TRUE; ch = get_char (ctx); if (ch != '+' && ch != '-' && !isdigit (ch)) err_code = ABSENT_EXPONENT; else { VARR_PUSH (char, temp_string, 'e'); if (ch == '+' || ch == '-') { VARR_PUSH (char, temp_string, ch); ch = get_char (ctx); if (!isdigit (ch)) err_code = ABSENT_EXPONENT; } if (err_code == NUMBER_OK) do { if (ch != '_') VARR_PUSH (char, temp_string, ch); ch = get_char (ctx); } while (isdigit (ch) || ch == '_'); } } if (*double_p) { if (*base == 16) err_code = NON_DECIMAL_FLOAT; else if (ch == 'f' || ch == 'F') { *float_p = TRUE; *double_p = FALSE; ch = get_char (ctx); } else if (ch == 'l' || ch == 'L') { #if !defined(_WIN32) && __SIZEOF_LONG_DOUBLE__ != 8 *ldouble_p = TRUE; *double_p = FALSE; #endif ch = get_char (ctx); } } else if (*base == 8 && dec_p) err_code = WRONG_OCTAL_INT; VARR_PUSH (char, temp_string, '\0'); unget_char (ctx, ch); } static void scan_string (MIR_context_t ctx, token_t *t, int c, int get_char (MIR_context_t), void unget_char (MIR_context_t, int)) { int ch_code; mir_assert (c == '\"'); VARR_TRUNC (char, temp_string, 0); for (;;) { if ((c = get_char (ctx)) == EOF || c == '\n') { VARR_PUSH (char, temp_string, '\0'); scan_error (ctx, "unfinished string \"%s", VARR_ADDR (char, temp_string)); } if (c == '"') break; if (c == '\\') { if ((c = get_char (ctx)) == 'n') c = '\n'; else if (c == 't') c = '\t'; else if (c == 'v') c = '\v'; else if (c == 'a') c = '\a'; else if (c == 'b') c = '\b'; else if (c == 'r') c = '\r'; else if (c == 'f') c = '\f'; else if (c == '\\' || c == '\'' || c == '\"') ; else if (c == '\n') { curr_lno++; continue; } else if (isdigit (c) && c != '8' && c != '9') { ch_code = c - '0'; c = get_char (ctx); if (!isdigit (c) || c == '8' || c == '9') unget_char (ctx, c); else { ch_code = ch_code * 8 + c - '0'; c = get_char (ctx); if (!isdigit (c) || c == '8' || c == '9') unget_char (ctx, c); else ch_code = ch_code * 8 + c - '0'; } c = ch_code; } else if (c == 'x') { /* Hex escape code. */ ch_code = 0; for (int i = 2; i > 0; i--) { c = get_char (ctx); if (!isxdigit (c)) { VARR_PUSH (char, temp_string, '\0'); scan_error (ctx, "wrong hexadecimal escape in %s", VARR_ADDR (char, temp_string)); } c = '0' <= c && c <= '9' ? c - '0' : 'a' <= c && c <= 'f' ? c - 'a' + 10 : c - 'A' + 10; ch_code = (ch_code << 4) | c; } c = ch_code; } } VARR_PUSH (char, temp_string, c); } if (VARR_LENGTH (char, temp_string) > 0 && VARR_LAST (char, temp_string) != 0) VARR_PUSH (char, temp_string, 0); t->code = TC_STR; t->u.str = string_store (ctx, &strings, &string_tab, (MIR_str_t){VARR_LENGTH (char, temp_string), VARR_ADDR (char, temp_string)}) .str; } static int get_string_char (MIR_context_t ctx) { int ch = input_string[input_string_char_num]; if (ch == '\0') return EOF; input_string_char_num++; if (ch == '\n') curr_lno++; return ch; } static void unget_string_char (MIR_context_t ctx, int ch) { if (input_string_char_num == 0 || ch == EOF) return; input_string_char_num--; mir_assert (input_string[input_string_char_num] == ch); if (ch == '\n') curr_lno--; } static void scan_token (MIR_context_t ctx, token_t *token, int (*get_char) (MIR_context_t), void (*unget_char) (MIR_context_t, int)) { int ch; for (;;) { ch = get_char (ctx); switch (ch) { case EOF: token->code = TC_EOFILE; return; case ' ': case '\t': break; case '#': while ((ch = get_char (ctx)) != '\n' && ch != EOF) ; /* Fall through: */ case '\n': token->code = TC_NL; return; case '(': token->code = TC_LEFT_PAR; return; case ')': token->code = TC_RIGHT_PAR; return; case ',': token->code = TC_COMMA; return; case ';': token->code = TC_SEMICOL; return; case ':': token->code = TC_COL; return; case '"': scan_string (ctx, token, ch, get_char, unget_char); return; default: VARR_TRUNC (char, temp_string, 0); if (isalpha (ch) || ch == '_' || ch == '$' || ch == '%' || ch == '.') { do { VARR_PUSH (char, temp_string, ch); ch = get_char (ctx); } while (isalpha (ch) || isdigit (ch) || ch == '_' || ch == '$' || ch == '%' || ch == '.'); VARR_PUSH (char, temp_string, '\0'); unget_char (ctx, ch); token->u.name = _MIR_uniq_string (ctx, VARR_ADDR (char, temp_string)); token->code = TC_NAME; return; } else if (ch == '+' || ch == '-' || isdigit (ch)) { const char *repr; char *end; int next_ch, base, float_p, double_p, ldouble_p; if (ch == '+' || ch == '-') { next_ch = get_char (ctx); if (!isdigit (next_ch)) scan_error (ctx, "no number after a sign %c", ch); unget_char (ctx, next_ch); } scan_number (ctx, ch, get_char, unget_char, &base, &float_p, &double_p, &ldouble_p); repr = VARR_ADDR (char, temp_string); errno = 0; if (float_p) { token->code = TC_FLOAT; token->u.f = strtof (repr, &end); } else if (double_p) { token->code = TC_DOUBLE; token->u.d = strtod (repr, &end); } else if (ldouble_p) { token->code = TC_LDOUBLE; token->u.ld = strtold (repr, &end); } else { token->code = TC_INT; token->u.i = (sizeof (long) == sizeof (int64_t) ? strtoul (repr, &end, base) : strtoull (repr, &end, base)); } mir_assert (*end == '\0'); if (errno != 0) ; return; } else { VARR_PUSH (char, temp_string, '\0'); scan_error (ctx, "wrong char after %s", VARR_ADDR (char, temp_string)); } } } } static int label_eq (label_desc_t l1, label_desc_t l2, void *arg) { return strcmp (l1.name, l2.name) == 0; } static htab_hash_t label_hash (label_desc_t l, void *arg) { return mir_hash (l.name, strlen (l.name), 0); } static MIR_label_t create_label_desc (MIR_context_t ctx, const char *name) { MIR_label_t label; label_desc_t label_desc; label_desc.name = name; if (HTAB_DO (label_desc_t, label_desc_tab, label_desc, HTAB_FIND, label_desc)) { label = label_desc.label; } else { label_desc.label = label = MIR_new_label (ctx); HTAB_DO (label_desc_t, label_desc_tab, label_desc, HTAB_INSERT, label_desc); } return label; } static int func_reg_p (MIR_context_t ctx, MIR_func_t func, const char *name) { func_regs_t func_regs = func->internal; size_t rdn, tab_rdn; reg_desc_t rd; int res; rd.name = (char *) name; rdn = VARR_LENGTH (reg_desc_t, func_regs->reg_descs); VARR_PUSH (reg_desc_t, func_regs->reg_descs, rd); res = HTAB_DO (size_t, func_regs->name2rdn_tab, rdn, HTAB_FIND, tab_rdn); VARR_POP (reg_desc_t, func_regs->reg_descs); return res; } static void read_func_proto (MIR_context_t ctx, size_t nops, MIR_op_t *ops) { MIR_var_t var; size_t i; VARR_TRUNC (MIR_type_t, scan_types, 0); for (i = 0; i < nops; i++) { var.name = (const char *) ops[i].u.mem.disp; if ((var.name = (const char *) ops[i].u.mem.disp) != NULL) break; var.type = ops[i].u.mem.type; VARR_PUSH (MIR_type_t, scan_types, var.type); } VARR_TRUNC (MIR_var_t, scan_vars, 0); for (; i < nops; i++) { if (ops[i].mode != MIR_OP_MEM) scan_error (ctx, "wrong prototype/func arg"); var.type = ops[i].u.mem.type; var.name = (const char *) ops[i].u.mem.disp; if (var.name == NULL) scan_error (ctx, "all func/prototype args should have form type:name or (r)blk:size(name)"); if (MIR_all_blk_type_p (var.type)) var.size = ops[i].u.mem.base; VARR_PUSH (MIR_var_t, scan_vars, var); } } static MIR_type_t str2type (const char *type_name) { if (strcmp (type_name, "i64") == 0) return MIR_T_I64; if (strcmp (type_name, "u64") == 0) return MIR_T_U64; if (strcmp (type_name, "f") == 0) return MIR_T_F; if (strcmp (type_name, "d") == 0) return MIR_T_D; if (strcmp (type_name, "ld") == 0) return MIR_T_LD; if (strcmp (type_name, "p") == 0) return MIR_T_P; if (strcmp (type_name, "i32") == 0) return MIR_T_I32; if (strcmp (type_name, "u32") == 0) return MIR_T_U32; if (strcmp (type_name, "i16") == 0) return MIR_T_I16; if (strcmp (type_name, "u16") == 0) return MIR_T_U16; if (strcmp (type_name, "i8") == 0) return MIR_T_I8; if (strcmp (type_name, "u8") == 0) return MIR_T_U8; if (strncmp (type_name, "blk", 3) == 0) { int i, n = 0; for (i = 3; isdigit (type_name[i]) && n < MIR_BLK_NUM; i++) n = n * 10 + (type_name[i] - '0'); if (type_name[i] == 0 && n < MIR_BLK_NUM) return MIR_T_BLK + n; } if (strcmp (type_name, "rblk") == 0) return MIR_T_RBLK; return MIR_T_BOUND; } /* Syntax: program: { insn / sep } sep : ';' | NL insn : {label ':'}* [ code [ {op / ','} ] ] label : name code : name op : name | int | float | double | long double | mem | str mem : type ':' addr addr : disp | [ disp ] '(' sib ')' sib : name | [ name ] ',' name [ ',' scale] disp : int | name scale : int */ void MIR_scan_string (MIR_context_t ctx, const char *str) { token_t t; const char *name; MIR_module_t module = NULL; MIR_item_t item, func = NULL; MIR_insn_code_t insn_code; MIR_insn_t insn; MIR_type_t type, data_type = MIR_T_BOUND; MIR_op_t op, *op_addr; MIR_label_t label; size_t n; int64_t i; int module_p, end_module_p, proto_p, func_p, end_func_p, dots_p, export_p, import_p, forward_p; int bss_p, ref_p, expr_p, string_p, local_p, push_op_p, read_p, disp_p; insn_name_t in, el; VARR_TRUNC (char, error_msg_buf, 0); curr_lno = 1; input_string = str; input_string_char_num = 0; t.code = TC_NL; for (;;) { if (setjmp (error_jmp_buf)) { while (t.code != TC_NL && t.code != EOF) scan_token (ctx, &t, get_string_char, unget_string_char); if (t.code == TC_EOFILE) break; } VARR_TRUNC (label_name_t, label_names, 0); scan_token (ctx, &t, get_string_char, unget_string_char); while (t.code == TC_NL) scan_token (ctx, &t, get_string_char, unget_string_char); if (t.code == TC_EOFILE) break; for (;;) { /* label_names */ if (t.code != TC_NAME) scan_error (ctx, "insn should start with label or insn name"); name = t.u.name; scan_token (ctx, &t, get_string_char, unget_string_char); if (t.code != TC_COL) break; VARR_PUSH (label_name_t, label_names, name); scan_token (ctx, &t, get_string_char, unget_string_char); if (t.code == TC_NL) scan_token (ctx, &t, get_string_char, unget_string_char); /* label_names without insn */ } module_p = end_module_p = proto_p = func_p = end_func_p = FALSE; export_p = import_p = forward_p = bss_p = ref_p = expr_p = string_p = local_p = FALSE; if (strcmp (name, "module") == 0) { module_p = TRUE; if (VARR_LENGTH (label_name_t, label_names) != 1) scan_error (ctx, "only one label should be used for module"); } else if (strcmp (name, "endmodule") == 0) { end_module_p = TRUE; if (VARR_LENGTH (label_name_t, label_names) != 0) scan_error (ctx, "endmodule should have no labels"); } else if (strcmp (name, "proto") == 0) { proto_p = TRUE; if (VARR_LENGTH (label_name_t, label_names) != 1) scan_error (ctx, "only one label should be used for proto"); } else if (strcmp (name, "func") == 0) { func_p = TRUE; if (VARR_LENGTH (label_name_t, label_names) != 1) scan_error (ctx, "only one label should be used for func"); } else if (strcmp (name, "endfunc") == 0) { end_func_p = TRUE; if (VARR_LENGTH (label_name_t, label_names) != 0) scan_error (ctx, "endfunc should have no labels"); } else if (strcmp (name, "export") == 0) { export_p = TRUE; if (VARR_LENGTH (label_name_t, label_names) != 0) scan_error (ctx, "export should have no labels"); } else if (strcmp (name, "import") == 0) { import_p = TRUE; if (VARR_LENGTH (label_name_t, label_names) != 0) scan_error (ctx, "import should have no labels"); } else if (strcmp (name, "forward") == 0) { forward_p = TRUE; if (VARR_LENGTH (label_name_t, label_names) != 0) scan_error (ctx, "forward should have no labels"); } else if (strcmp (name, "bss") == 0) { bss_p = TRUE; if (VARR_LENGTH (label_name_t, label_names) > 1) scan_error (ctx, "at most one label should be used for bss"); } else if (strcmp (name, "ref") == 0) { ref_p = TRUE; if (VARR_LENGTH (label_name_t, label_names) > 1) scan_error (ctx, "at most one label should be used for ref"); } else if (strcmp (name, "expr") == 0) { expr_p = TRUE; if (VARR_LENGTH (label_name_t, label_names) > 1) scan_error (ctx, "at most one label should be used for expr"); } else if (strcmp (name, "string") == 0) { string_p = TRUE; if (VARR_LENGTH (label_name_t, label_names) > 1) scan_error (ctx, "at most one label should be used for string"); } else if (strcmp (name, "local") == 0) { local_p = TRUE; if (func == NULL) scan_error (ctx, "local outside func"); if (VARR_LENGTH (label_name_t, label_names) != 0) scan_error (ctx, "local should have no labels"); } else if ((data_type = str2type (name)) != MIR_T_BOUND) { if (VARR_LENGTH (label_name_t, label_names) > 1) scan_error (ctx, "at most one label should be used for data"); } else { in.name = name; if (!HTAB_DO (insn_name_t, insn_name_tab, in, HTAB_FIND, el)) scan_error (ctx, "Unknown insn %s", name); insn_code = el.code; if (insn_code == MIR_UNSPEC || insn_code == MIR_PHI) scan_error (ctx, "UNSPEC or PHI is not portable and can not be scanned", name); for (n = 0; n < VARR_LENGTH (label_name_t, label_names); n++) { label = create_label_desc (ctx, VARR_GET (label_name_t, label_names, n)); if (func != NULL) MIR_append_insn (ctx, func, label); } } VARR_TRUNC (MIR_op_t, scan_insn_ops, 0); dots_p = FALSE; for (;;) { /* ops */ if (t.code == TC_NL || t.code == TC_SEMICOL) { /* insn end */ break; } push_op_p = read_p = TRUE; switch (t.code) { case TC_NAME: { name = t.u.name; scan_token (ctx, &t, get_string_char, unget_string_char); if ((func_p || proto_p) && strcmp (name, "...") == 0) { dots_p = TRUE; break; } read_p = FALSE; if (t.code != TC_COL && !proto_p && !func_p && !local_p) { if (export_p) { MIR_new_export (ctx, name); push_op_p = FALSE; } else if (import_p) { MIR_new_import (ctx, name); push_op_p = FALSE; } else if (forward_p) { MIR_new_forward (ctx, name); push_op_p = FALSE; } else if (!module_p && !end_module_p && !proto_p && !func_p && !end_func_p && !local_p && ((MIR_branch_code_p (insn_code) && VARR_LENGTH (MIR_op_t, scan_insn_ops) == 0) || (insn_code == MIR_SWITCH && VARR_LENGTH (MIR_op_t, scan_insn_ops) > 0))) { op = MIR_new_label_op (ctx, create_label_desc (ctx, name)); } else if (!expr_p && !ref_p && func_reg_p (ctx, func->u.func, name)) { op.mode = MIR_OP_REG; op.u.reg = MIR_reg (ctx, name, func->u.func); } else if ((item = item_tab_find (ctx, name, module)) != NULL) { op = MIR_new_ref_op (ctx, item); } else { scan_error (ctx, "undeclared name %s", name); } break; } /* Memory, type only, arg, or var */ type = str2type (name); if (type == MIR_T_BOUND) scan_error (ctx, "Unknown type %s", name); else if (local_p && type != MIR_T_I64 && type != MIR_T_F && type != MIR_T_D && type != MIR_T_LD) scan_error (ctx, "wrong type %s for local var", name); op = MIR_new_mem_op (ctx, type, 0, 0, 0, 1); if (proto_p || func_p || local_p) { if (t.code == TC_COL) { scan_token (ctx, &t, get_string_char, unget_string_char); if (t.code == TC_NAME) { op.u.mem.disp = (MIR_disp_t) t.u.name; } else if (local_p || t.code != TC_INT || !MIR_all_blk_type_p (type)) { scan_error (ctx, local_p ? "wrong var" : "wrong arg"); } else { op.u.mem.base = t.u.i; if (t.u.i <= 0 || t.u.i >= (1ll << sizeof (MIR_reg_t) * 8)) scan_error (ctx, "invalid block arg size"); scan_token (ctx, &t, get_string_char, unget_string_char); if (t.code != TC_LEFT_PAR) scan_error (ctx, "wrong block arg"); scan_token (ctx, &t, get_string_char, unget_string_char); if (t.code != TC_NAME) scan_error (ctx, "wrong block arg"); op.u.mem.disp = (MIR_disp_t) t.u.name; scan_token (ctx, &t, get_string_char, unget_string_char); if (t.code != TC_RIGHT_PAR) scan_error (ctx, "wrong block arg"); } scan_token (ctx, &t, get_string_char, unget_string_char); } } else { scan_token (ctx, &t, get_string_char, unget_string_char); disp_p = FALSE; if (t.code == TC_INT) { op.u.mem.disp = t.u.i; scan_token (ctx, &t, get_string_char, unget_string_char); disp_p = TRUE; } else if (t.code == TC_NAME) { op.u.mem.disp = (MIR_disp_t) t.u.name; scan_token (ctx, &t, get_string_char, unget_string_char); disp_p = TRUE; } if (t.code == TC_LEFT_PAR) { scan_token (ctx, &t, get_string_char, unget_string_char); if (t.code == TC_NAME) { op.u.mem.base = MIR_reg (ctx, t.u.name, func->u.func); scan_token (ctx, &t, get_string_char, unget_string_char); } if (t.code == TC_COMMA) { scan_token (ctx, &t, get_string_char, unget_string_char); if (t.code != TC_NAME) scan_error (ctx, "wrong index"); op.u.mem.index = MIR_reg (ctx, t.u.name, func->u.func); scan_token (ctx, &t, get_string_char, unget_string_char); if (t.code == TC_COMMA) { scan_token (ctx, &t, get_string_char, unget_string_char); if (t.code != TC_INT) scan_error (ctx, "wrong scale"); op.u.mem.scale = t.u.i; scan_token (ctx, &t, get_string_char, unget_string_char); } } if (t.code != TC_RIGHT_PAR) scan_error (ctx, "wrong memory op"); scan_token (ctx, &t, get_string_char, unget_string_char); } else if (!disp_p) scan_error (ctx, "wrong memory"); } break; } case TC_INT: op.mode = MIR_OP_INT; op.u.i = t.u.i; break; case TC_FLOAT: op.mode = MIR_OP_FLOAT; op.u.f = t.u.f; break; case TC_LDOUBLE: op.mode = MIR_OP_LDOUBLE; op.u.ld = t.u.ld; #if !defined(_WIN32) && __SIZEOF_LONG_DOUBLE__ != 8 break; #endif case TC_DOUBLE: op.mode = MIR_OP_DOUBLE; op.u.d = t.u.d; break; case TC_STR: op.mode = MIR_OP_STR; op.u.str = t.u.str; break; default: break; } if (dots_p) break; if (push_op_p) VARR_PUSH (MIR_op_t, scan_insn_ops, op); if (read_p) scan_token (ctx, &t, get_string_char, unget_string_char); if (t.code != TC_COMMA) break; scan_token (ctx, &t, get_string_char, unget_string_char); } if (t.code != TC_NL && t.code != TC_EOFILE && t.code != TC_SEMICOL) scan_error (ctx, "wrong insn end"); if (module_p) { if (module != NULL) scan_error (ctx, "nested module"); if (VARR_LENGTH (MIR_op_t, scan_insn_ops) != 0) scan_error (ctx, "module should have no params"); module = MIR_new_module (ctx, VARR_GET (label_name_t, label_names, 0)); } else if (end_module_p) { if (module == NULL) scan_error (ctx, "standalone endmodule"); if (VARR_LENGTH (MIR_op_t, scan_insn_ops) != 0) scan_error (ctx, "endmodule should have no params"); MIR_finish_module (ctx); module = NULL; } else if (bss_p) { if (VARR_LENGTH (MIR_op_t, scan_insn_ops) != 1) scan_error (ctx, "bss should have one operand"); op_addr = VARR_ADDR (MIR_op_t, scan_insn_ops); if (op_addr[0].mode != MIR_OP_INT || op_addr[0].u.i < 0) scan_error (ctx, "wrong bss operand type or value"); name = (VARR_LENGTH (label_name_t, label_names) == 0 ? NULL : VARR_GET (label_name_t, label_names, 0)); MIR_new_bss (ctx, name, op_addr[0].u.i); } else if (ref_p) { if (VARR_LENGTH (MIR_op_t, scan_insn_ops) != 2) scan_error (ctx, "ref should have two operands"); op_addr = VARR_ADDR (MIR_op_t, scan_insn_ops); if (op_addr[0].mode != MIR_OP_REF) scan_error (ctx, "wrong ref operand"); if (op_addr[1].mode != MIR_OP_INT) scan_error (ctx, "wrong ref disp operand"); name = (VARR_LENGTH (label_name_t, label_names) == 0 ? NULL : VARR_GET (label_name_t, label_names, 0)); MIR_new_ref_data (ctx, name, op_addr[0].u.ref, op_addr[1].u.i); } else if (expr_p) { if (VARR_LENGTH (MIR_op_t, scan_insn_ops) != 1) scan_error (ctx, "expr should have one operand"); op_addr = VARR_ADDR (MIR_op_t, scan_insn_ops); if (op_addr[0].mode != MIR_OP_REF || op_addr[0].u.ref->item_type != MIR_func_item) scan_error (ctx, "wrong expr operand"); name = (VARR_LENGTH (label_name_t, label_names) == 0 ? NULL : VARR_GET (label_name_t, label_names, 0)); MIR_new_expr_data (ctx, name, op_addr[0].u.ref); } else if (string_p) { if (VARR_LENGTH (MIR_op_t, scan_insn_ops) != 1) scan_error (ctx, "string should have one operand"); op_addr = VARR_ADDR (MIR_op_t, scan_insn_ops); if (op_addr[0].mode != MIR_OP_STR) scan_error (ctx, "wrong string data operand type"); name = (VARR_LENGTH (label_name_t, label_names) == 0 ? NULL : VARR_GET (label_name_t, label_names, 0)); MIR_new_string_data (ctx, name, op_addr[0].u.str); } else if (proto_p) { if (module == NULL) scan_error (ctx, "prototype outside module"); read_func_proto (ctx, VARR_LENGTH (MIR_op_t, scan_insn_ops), VARR_ADDR (MIR_op_t, scan_insn_ops)); if (dots_p) MIR_new_vararg_proto_arr (ctx, VARR_GET (label_name_t, label_names, 0), VARR_LENGTH (MIR_type_t, scan_types), VARR_ADDR (MIR_type_t, scan_types), VARR_LENGTH (MIR_var_t, scan_vars), VARR_ADDR (MIR_var_t, scan_vars)); else MIR_new_proto_arr (ctx, VARR_GET (label_name_t, label_names, 0), VARR_LENGTH (MIR_type_t, scan_types), VARR_ADDR (MIR_type_t, scan_types), VARR_LENGTH (MIR_var_t, scan_vars), VARR_ADDR (MIR_var_t, scan_vars)); } else if (func_p) { if (module == NULL) scan_error (ctx, "func outside module"); if (func != NULL) scan_error (ctx, "nested func"); read_func_proto (ctx, VARR_LENGTH (MIR_op_t, scan_insn_ops), VARR_ADDR (MIR_op_t, scan_insn_ops)); if (dots_p) func = MIR_new_vararg_func_arr (ctx, VARR_GET (label_name_t, label_names, 0), VARR_LENGTH (MIR_type_t, scan_types), VARR_ADDR (MIR_type_t, scan_types), VARR_LENGTH (MIR_var_t, scan_vars), VARR_ADDR (MIR_var_t, scan_vars)); else func = MIR_new_func_arr (ctx, VARR_GET (label_name_t, label_names, 0), VARR_LENGTH (MIR_type_t, scan_types), VARR_ADDR (MIR_type_t, scan_types), VARR_LENGTH (MIR_var_t, scan_vars), VARR_ADDR (MIR_var_t, scan_vars)); HTAB_CLEAR (label_desc_t, label_desc_tab); } else if (end_func_p) { if (func == NULL) scan_error (ctx, "standalone endfunc"); if (VARR_LENGTH (MIR_op_t, scan_insn_ops) != 0) scan_error (ctx, "endfunc should have no params"); func = NULL; MIR_finish_func (ctx); } else if (export_p || import_p || forward_p) { /* we already created items, now do nothing: */ mir_assert (VARR_LENGTH (MIR_op_t, scan_insn_ops) == 0); } else if (local_p) { op_addr = VARR_ADDR (MIR_op_t, scan_insn_ops); n = VARR_LENGTH (MIR_op_t, scan_insn_ops); for (i = 0; i < n; i++) { if (op_addr[i].mode != MIR_OP_MEM || (const char *) op_addr[i].u.mem.disp == NULL) scan_error (ctx, "wrong local var"); MIR_new_func_reg (ctx, func->u.func, op_addr[i].u.mem.type, (const char *) op_addr[i].u.mem.disp); } } else if (data_type != MIR_T_BOUND) { union { uint8_t u8; uint16_t u16; uint32_t u32; uint64_t u64; int8_t i8; int16_t i16; int32_t i32; int64_t i64; } v; n = VARR_LENGTH (MIR_op_t, scan_insn_ops); op_addr = VARR_ADDR (MIR_op_t, scan_insn_ops); VARR_TRUNC (uint8_t, temp_data, 0); for (i = 0; i < n; i++) { if (op_addr[i].mode != type2mode (data_type)) scan_error (ctx, "data operand is not of data type"); switch (data_type) { case MIR_T_I8: v.i8 = op_addr[i].u.i; push_data (ctx, (uint8_t *) &v.i8, sizeof (int8_t)); break; case MIR_T_U8: v.u8 = op_addr[i].u.u; push_data (ctx, (uint8_t *) &v.u8, sizeof (uint8_t)); break; case MIR_T_I16: v.i16 = op_addr[i].u.i; push_data (ctx, (uint8_t *) &v.i16, sizeof (int16_t)); break; case MIR_T_U16: v.u16 = op_addr[i].u.u; push_data (ctx, (uint8_t *) &v.u16, sizeof (uint16_t)); break; case MIR_T_I32: v.i32 = op_addr[i].u.i; push_data (ctx, (uint8_t *) &v.i32, sizeof (int32_t)); break; case MIR_T_U32: v.u32 = op_addr[i].u.u; push_data (ctx, (uint8_t *) &v.u32, sizeof (uint32_t)); break; case MIR_T_I64: v.i64 = op_addr[i].u.i; push_data (ctx, (uint8_t *) &v.i64, sizeof (int64_t)); break; case MIR_T_U64: v.u64 = op_addr[i].u.u; push_data (ctx, (uint8_t *) &v.u64, sizeof (uint64_t)); break; case MIR_T_F: push_data (ctx, (uint8_t *) &op_addr[i].u.f, sizeof (float)); break; case MIR_T_D: push_data (ctx, (uint8_t *) &op_addr[i].u.d, sizeof (double)); break; case MIR_T_LD: push_data (ctx, (uint8_t *) &op_addr[i].u.ld, sizeof (long double)); break; /* ptr ??? */ default: scan_error (ctx, "wrong data clause"); } } name = (VARR_LENGTH (label_name_t, label_names) == 0 ? NULL : VARR_GET (label_name_t, label_names, 0)); MIR_new_data (ctx, name, data_type, VARR_LENGTH (uint8_t, temp_data) / _MIR_type_size (ctx, data_type), VARR_ADDR (uint8_t, temp_data)); } else { insn = MIR_new_insn_arr (ctx, insn_code, VARR_LENGTH (MIR_op_t, scan_insn_ops), VARR_ADDR (MIR_op_t, scan_insn_ops)); if (func != NULL) MIR_append_insn (ctx, func, insn); } } if (func != NULL) scan_error (ctx, "absent endfunc"); if (module != NULL) scan_error (ctx, "absent endmodule"); if (VARR_LENGTH (char, error_msg_buf) != 0) MIR_get_error_func (ctx) (MIR_syntax_error, VARR_ADDR (char, error_msg_buf)); } static void scan_init (MIR_context_t ctx) { insn_name_t in, el; size_t i; if ((ctx->scan_ctx = malloc (sizeof (struct scan_ctx))) == NULL) MIR_get_error_func (ctx) (MIR_alloc_error, "Not enough memory for ctx"); VARR_CREATE (char, error_msg_buf, 0); VARR_CREATE (MIR_var_t, scan_vars, 0); VARR_CREATE (MIR_type_t, scan_types, 0); VARR_CREATE (MIR_op_t, scan_insn_ops, 0); VARR_CREATE (label_name_t, label_names, 0); HTAB_CREATE (label_desc_t, label_desc_tab, 100, label_hash, label_eq, NULL); HTAB_CREATE (insn_name_t, insn_name_tab, MIR_INSN_BOUND, insn_name_hash, insn_name_eq, NULL); for (i = 0; i < MIR_INSN_BOUND; i++) { in.code = i; in.name = MIR_insn_name (ctx, i); HTAB_DO (insn_name_t, insn_name_tab, in, HTAB_INSERT, el); } } static void scan_finish (MIR_context_t ctx) { VARR_DESTROY (char, error_msg_buf); VARR_DESTROY (MIR_var_t, scan_vars); VARR_DESTROY (MIR_type_t, scan_types); VARR_DESTROY (MIR_op_t, scan_insn_ops); VARR_DESTROY (label_name_t, label_names); HTAB_DESTROY (label_desc_t, label_desc_tab); HTAB_DESTROY (insn_name_t, insn_name_tab); free (ctx->scan_ctx); ctx->scan_ctx = NULL; } #endif /* if !MIR_NO_SCAN */ /* New Page */ #if defined(__x86_64__) || defined(_M_AMD64) #include "mir-x86_64.c" #elif defined(__aarch64__) #include "mir-aarch64.c" #elif defined(__PPC64__) #include "mir-ppc64.c" #elif defined(__s390x__) #include "mir-s390x.c" #else #error "undefined or unsupported generation target" #endif /* New Page */ #include "mir-interp.c" /* Local Variables: */ /* mode: c */ /* page-delimiter: "/\\* New Page" */ /* End: */