X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=tools%2Ftranslate.c;h=a894f17d0137d1ee306af98009963a186c112a08;hb=db63af5172f159c5a1983579b081b3bac7f03f0e;hp=5acdd11f817094ea1096e147a240805553dfdd38;hpb=c0de901581e6edfc9b0a18fe6bb89ee56da12804;p=ia32rtools.git diff --git a/tools/translate.c b/tools/translate.c index 5acdd11..a894f17 100644 --- a/tools/translate.c +++ b/tools/translate.c @@ -57,6 +57,7 @@ enum op_flags { OPF_LOCK = (1 << 17), /* op has lock prefix */ OPF_VAPUSH = (1 << 18), /* vararg ptr push (as call arg) */ OPF_DONE = (1 << 19), /* already fully handled by analysis */ + OPF_PPUSH = (1 << 20), /* part of complex push-pop graph */ }; enum op_op { @@ -113,7 +114,7 @@ enum op_op { // x87 // mmx OP_EMMS, - // mmx + // undefined OP_UD2, }; @@ -176,9 +177,10 @@ struct parsed_op { }; // datap: -// OP_CALL - parser proto hint (str) +// OP_CALL - parser proto hint (str) // (OPF_CC) - points to one of (OPF_FLAGS) that affects cc op -// OP_POP - points to OP_PUSH in push/pop pair +// OP_PUSH - points to OP_POP in complex push/pop graph +// OP_POP - points to OP_PUSH in simple push/pop pair struct parsed_equ { char name[64]; @@ -234,6 +236,7 @@ static int g_sp_frame; static int g_stack_frame_used; static int g_stack_fsz; static int g_ida_func_attr; +static int g_skip_func; static int g_allow_regfunc; static int g_quiet_pp; static int g_header_mode; @@ -984,7 +987,8 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) } if (i == ARRAY_SIZE(op_table)) { - anote("unhandled op: '%s'\n", words[0]); + if (!g_skip_func) + aerr("unhandled op: '%s'\n", words[0]); i--; // OP_UD2 } w++; @@ -2137,6 +2141,10 @@ static void op_set_clear_flag(struct parsed_op *po, || ((ops[_i].flags & (OPF_JMP|OPF_CJMP|OPF_RMD)) == OPF_JMP \ && ops[_i].op != OP_CALL)) +#define check_i(po, i) \ + if ((i) < 0) \ + ferr(po, "bad " #i ": %d\n", i) + static int scan_for_pop(int i, int opcnt, const char *reg, int magic, int depth, int *maxdepth, int do_flags) { @@ -2169,6 +2177,7 @@ static int scan_for_pop(int i, int opcnt, const char *reg, if (po->btj != NULL) { // jumptable for (j = 0; j < po->btj->count; j++) { + check_i(po, po->btj->d[j].bt_i); ret |= scan_for_pop(po->btj->d[j].bt_i, opcnt, reg, magic, depth, maxdepth, do_flags); if (ret < 0) @@ -2177,11 +2186,7 @@ static int scan_for_pop(int i, int opcnt, const char *reg, return ret; } - if (po->bt_i < 0) { - ferr(po, "dead branch\n"); - return -1; - } - + check_i(po, po->bt_i); if (po->flags & OPF_CJMP) { ret |= scan_for_pop(po->bt_i, opcnt, reg, magic, depth, maxdepth, do_flags); @@ -2259,22 +2264,44 @@ static int scan_for_pop_ret(int i, int opcnt, const char *reg, return found ? 0 : -1; } -static void scan_for_pop_const(int i, int opcnt) +static void scan_for_pop_const(int i, int opcnt, int *regmask_pp) { + struct parsed_op *po; + int is_multipath = 0; int j; for (j = i + 1; j < opcnt; j++) { - if ((ops[j].flags & (OPF_JMP|OPF_TAIL|OPF_RSAVE)) - || ops[j].op == OP_PUSH || g_labels[i] != NULL) + po = &ops[j]; + + if (po->op == OP_JMP && po->btj == NULL) { + ferr_assert(po, po->bt_i >= 0); + j = po->bt_i - 1; + continue; + } + + if ((po->flags & (OPF_JMP|OPF_TAIL|OPF_RSAVE)) + || po->op == OP_PUSH) { break; } - if (ops[j].op == OP_POP && !(ops[j].flags & (OPF_RMD|OPF_DONE))) + if (g_labels[j] != NULL) + is_multipath = 1; + + if (po->op == OP_POP && !(po->flags & OPF_RMD)) { - ops[i].flags |= OPF_RMD | OPF_DONE; - ops[j].flags |= OPF_DONE; - ops[j].datap = &ops[i]; + is_multipath |= !!(po->flags & OPF_PPUSH); + if (is_multipath) { + ops[i].flags |= OPF_PPUSH | OPF_DONE; + ops[i].datap = po; + po->flags |= OPF_PPUSH | OPF_DONE; + *regmask_pp |= 1 << po->operand[0].reg; + } + else { + ops[i].flags |= OPF_RMD | OPF_DONE; + po->flags |= OPF_DONE; + po->datap = &ops[i]; + } break; } } @@ -2297,16 +2324,14 @@ static void scan_propagate_df(int i, int opcnt) if (po->flags & OPF_JMP) { if (po->btj != NULL) { // jumptable - for (j = 0; j < po->btj->count; j++) + for (j = 0; j < po->btj->count; j++) { + check_i(po, po->btj->d[j].bt_i); scan_propagate_df(po->btj->d[j].bt_i, opcnt); + } return; } - if (po->bt_i < 0) { - ferr(po, "dead branch\n"); - return; - } - + check_i(po, po->bt_i); if (po->flags & OPF_CJMP) scan_propagate_df(po->bt_i, opcnt); else @@ -2326,13 +2351,57 @@ static void scan_propagate_df(int i, int opcnt) ferr(po, "missing DF clear?\n"); } +// is operand 'opr' referenced by parsed_op 'po'? +static int is_opr_referenced(const struct parsed_opr *opr, + const struct parsed_op *po) +{ + int i, mask; + + if (opr->type == OPT_REG) { + mask = po->regmask_dst | po->regmask_src; + if (po->op == OP_CALL) + mask |= (1 << xAX) | (1 << xCX) | (1 << xDX); + if ((1 << opr->reg) & mask) + return 1; + else + return 0; + } + + for (i = 0; i < po->operand_cnt; i++) + if (IS(po->operand[0].name, opr->name)) + return 1; + + return 0; +} + +// is operand 'opr' read by parsed_op 'po'? +static int is_opr_read(const struct parsed_opr *opr, + const struct parsed_op *po) +{ + int mask; + + if (opr->type == OPT_REG) { + mask = po->regmask_src; + if (po->op == OP_CALL) + // assume worst case + mask |= (1 << xAX) | (1 << xCX) | (1 << xDX); + if ((1 << opr->reg) & mask) + return 1; + else + return 0; + } + + // yes I'm lazy + return 0; +} + // is operand 'opr' modified by parsed_op 'po'? static int is_opr_modified(const struct parsed_opr *opr, const struct parsed_op *po) { int mask; - if ((po->flags & OPF_RMD) || !(po->flags & OPF_DATA)) + if (!(po->flags & OPF_DATA)) return 0; if (opr->type == OPT_REG) { @@ -2415,10 +2484,6 @@ static int scan_for_mod_opr0(struct parsed_op *po_test, return -1; } -#define check_i(po, i) \ - if ((i) < 0) \ - ferr(po, "bad " #i ": %d\n", i) - static int scan_for_flag_set(int i, int magic, int *branched, int *setters, int *setter_cnt) { @@ -2712,7 +2777,8 @@ static const struct parsed_proto *try_recover_pp( } static void scan_for_call_type(int i, const struct parsed_opr *opr, - int magic, const struct parsed_proto **pp_found, int *multi) + int magic, const struct parsed_proto **pp_found, int *pp_i, + int *multi) { const struct parsed_proto *pp = NULL; struct parsed_op *po; @@ -2725,7 +2791,7 @@ static void scan_for_call_type(int i, const struct parsed_opr *opr, lr = &g_label_refs[i]; for (; lr != NULL; lr = lr->next) { check_i(&ops[i], lr->i); - scan_for_call_type(lr->i, opr, magic, pp_found, multi); + scan_for_call_type(lr->i, opr, magic, pp_found, pp_i, multi); } if (i > 0 && LAST_OP(i - 1)) return; @@ -2790,8 +2856,10 @@ static void scan_for_call_type(int i, const struct parsed_opr *opr, } *multi = 1; } - if (pp != NULL) + if (pp != NULL) { *pp_found = pp; + *pp_i = po - ops; + } } // early check for tail call or branch back @@ -3013,12 +3081,13 @@ static void scan_prologue_epilogue(int opcnt) } static const struct parsed_proto *resolve_icall(int i, int opcnt, - int *multi_src) + int *pp_i, int *multi_src) { const struct parsed_proto *pp = NULL; int search_advice = 0; *multi_src = 0; + *pp_i = -1; switch (ops[i].operand[0].type) { case OPT_REGMEM: @@ -3030,7 +3099,7 @@ static const struct parsed_proto *resolve_icall(int i, int opcnt, // fallthrough default: scan_for_call_type(i, &ops[i].operand[0], i + opcnt * 9, &pp, - multi_src); + pp_i, multi_src); break; } @@ -3038,7 +3107,7 @@ static const struct parsed_proto *resolve_icall(int i, int opcnt, } // find an instruction that changed opr before i op -// *op_i must be set to -1 by caller +// *op_i must be set to -1 by the caller // *entry is set to 1 if one source is determined to be the caller // returns 1 if found, *op_i is then set to origin static int resolve_origin(int i, const struct parsed_opr *opr, @@ -3090,6 +3159,110 @@ static int resolve_origin(int i, const struct parsed_opr *opr, } } +// find an instruction that previously referenced opr +// if multiple results are found - fail +// *op_i must be set to -1 by the caller +// returns 1 if found, *op_i is then set to referencer insn +static int resolve_last_ref(int i, const struct parsed_opr *opr, + int magic, int *op_i) +{ + struct label_ref *lr; + int ret = 0; + + if (ops[i].cc_scratch == magic) + return 0; + ops[i].cc_scratch = magic; + + while (1) { + if (g_labels[i] != NULL) { + lr = &g_label_refs[i]; + for (; lr != NULL; lr = lr->next) { + check_i(&ops[i], lr->i); + ret |= resolve_last_ref(lr->i, opr, magic, op_i); + } + if (i > 0 && LAST_OP(i - 1)) + return ret; + } + + i--; + if (i < 0) + return -1; + + if (ops[i].cc_scratch == magic) + return 0; + ops[i].cc_scratch = magic; + + if (!is_opr_referenced(opr, &ops[i])) + continue; + + if (*op_i >= 0) + return -1; + + *op_i = i; + return 1; + } +} + +// find next instruction that reads opr +// if multiple results are found - fail +// *op_i must be set to -1 by the caller +// returns 1 if found, *op_i is then set to referencer insn +static int find_next_read(int i, int opcnt, + const struct parsed_opr *opr, int magic, int *op_i) +{ + struct parsed_op *po; + int j, ret = 0; + + for (; i < opcnt; i++) + { + if (ops[i].cc_scratch == magic) + return 0; + ops[i].cc_scratch = magic; + + po = &ops[i]; + if ((po->flags & OPF_JMP) && po->op != OP_CALL) { + if (po->btj != NULL) { + // jumptable + for (j = 0; j < po->btj->count; j++) { + check_i(po, po->btj->d[j].bt_i); + ret |= find_next_read(po->btj->d[j].bt_i, opcnt, opr, + magic, op_i); + } + return ret; + } + + if (po->flags & OPF_RMD) + continue; + check_i(po, po->bt_i); + if (po->flags & OPF_CJMP) { + ret = find_next_read(po->bt_i, opcnt, opr, magic, op_i); + if (ret < 0) + return ret; + } + + i = po->bt_i - 1; + continue; + } + + if (!is_opr_read(opr, po)) { + if (is_opr_modified(opr, po)) + // it's overwritten + return 0; + if (po->flags & OPF_TAIL) + return 0; + continue; + } + + if (*op_i >= 0) + return -1; + + *op_i = i; + return 1; + } + + return 0; +} + static int try_resolve_const(int i, const struct parsed_opr *opr, int magic, unsigned int *val) { @@ -3147,6 +3320,7 @@ static struct parsed_proto *process_call(int i, int opcnt) const struct parsed_proto *pp_c; struct parsed_proto *pp; const char *tmpname; + int call_i = -1, ref_i = -1; int adj = 0, multipath = 0; int ret, arg; @@ -3155,7 +3329,7 @@ static struct parsed_proto *process_call(int i, int opcnt) if (pp == NULL) { // indirect call - pp_c = resolve_icall(i, opcnt, &multipath); + pp_c = resolve_icall(i, opcnt, &call_i, &multipath); if (pp_c != NULL) { if (!pp_c->is_func && !pp_c->is_fptr) ferr(po, "call to non-func: %s\n", pp_c->name); @@ -3169,6 +3343,23 @@ static struct parsed_proto *process_call(int i, int opcnt) case OPT_REG: // we resolved this call and no longer need the register po->regmask_src &= ~(1 << po->operand[0].reg); + + if (!multipath && i != call_i && ops[call_i].op == OP_MOV + && ops[call_i].operand[1].type == OPT_LABEL) + { + // no other source users? + ret = resolve_last_ref(i, &po->operand[0], opcnt * 10, + &ref_i); + if (ret == 1 && call_i == ref_i) { + // and nothing uses it after us? + ref_i = -1; + ret = find_next_read(i + 1, opcnt, &po->operand[0], + opcnt * 11, &ref_i); + if (ret != 1) + // then also don't need the source mov + ops[call_i].flags |= OPF_RMD; + } + } break; case OPT_REGMEM: pp->is_fptr = 1; @@ -3813,11 +4004,12 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) int need_tmp64 = 0; int had_decl = 0; int label_pending = 0; - int regmask_save = 0; - int regmask_arg = 0; - int regmask_now = 0; - int regmask_init = 0; - int regmask = 0; + int regmask_save = 0; // regs saved/restored in this func + int regmask_arg = 0; // regs carrying function args (fastcall, etc) + int regmask_now; // temp + int regmask_init = 0; // regs that need zero initialization + int regmask_pp = 0; // regs used in complex push-pop graph + int regmask = 0; // used regs int pfomask = 0; int found = 0; int depth = 0; @@ -3997,7 +4189,7 @@ tailcall: } else if (po->op == OP_PUSH && !(po->flags & OPF_FARG) && !(po->flags & OPF_RSAVE) && po->operand[0].type == OPT_CONST) - scan_for_pop_const(i, opcnt); + scan_for_pop_const(i, opcnt, ®mask_pp); } // pass5: @@ -4383,6 +4575,7 @@ tailcall: } } + // declare normal registers regmask_now = regmask & ~regmask_arg; regmask_now &= ~(1 << xSP); if (regmask_now & 0x00ff) { @@ -4429,6 +4622,16 @@ tailcall: } } + // declare push-pop temporaries + if (regmask_pp) { + for (reg = 0; reg < 8; reg++) { + if (regmask_pp & (1 << reg)) { + fprintf(fout, " u32 pp_%s;\n", regs_r32[reg]); + had_decl = 1; + } + } + } + if (cond_vars) { for (i = 0; i < 8; i++) { if (cond_vars & (1 << i)) { @@ -5368,6 +5571,13 @@ tailcall: fprintf(fout, " s_%s = %s;", buf1, buf1); break; } + else if (po->flags & OPF_PPUSH) { + tmp_op = po->datap; + ferr_assert(po, tmp_op != NULL); + out_dst_opr(buf2, sizeof(buf2), po, &tmp_op->operand[0]); + fprintf(fout, " pp_%s = %s;", buf2, buf1); + break; + } else if (g_func_pp->is_userstack) { fprintf(fout, " *(--esp) = %s;", buf1); break; @@ -5378,15 +5588,20 @@ tailcall: break; case OP_POP: + out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]); if (po->flags & OPF_RSAVE) { - out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]); fprintf(fout, " %s = s_%s;", buf1, buf1); break; } + else if (po->flags & OPF_PPUSH) { + // push/pop graph + ferr_assert(po, po->datap == NULL); + fprintf(fout, " %s = pp_%s;", buf1, buf1); + break; + } else if (po->datap != NULL) { // push/pop pair tmp_op = po->datap; - out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]); fprintf(fout, " %s = %s;", buf1, out_src_opr(buf2, sizeof(buf2), tmp_op, &tmp_op->operand[0], @@ -5394,8 +5609,7 @@ tailcall: break; } else if (g_func_pp->is_userstack) { - fprintf(fout, " %s = *esp++;", - out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0])); + fprintf(fout, " %s = *esp++;", buf1); break; } else @@ -5599,17 +5813,14 @@ static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits, if (po->btj != NULL) { // jumptable for (j = 0; j < po->btj->count; j++) { + check_i(po, po->btj->d[j].bt_i); gen_hdr_dep_pass(po->btj->d[j].bt_i, opcnt, cbits, fp, regmask_save, regmask_dst, regmask_dep, has_ret); } return; } - if (po->bt_i < 0) { - ferr(po, "dead branch\n"); - return; - } - + check_i(po, po->bt_i); if (po->flags & OPF_CJMP) { gen_hdr_dep_pass(po->bt_i, opcnt, cbits, fp, regmask_save, regmask_dst, regmask_dep, has_ret); @@ -5853,7 +6064,7 @@ tailcall: continue; if (po->op == OP_PUSH && po->operand[0].type == OPT_CONST) - scan_for_pop_const(i, opcnt); + scan_for_pop_const(i, opcnt, ®mask_dummy); } // pass4: @@ -6032,17 +6243,8 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, continue; if (fp->pp != NULL) { - // prefer fp for common style, - // only use output_pp if args are complex - for (j = 0; j < fp->pp->argc; j++) { - if (fp->pp->arg[j].fptr != NULL) - break; - } - if (j != fp->pp->argc) { - output_pp(fout, fp->pp, OPP_ALIGN); - fprintf(fout, ";\n"); - continue; - } + // part of seed, output later + continue; } regmask_dep = fp->regmask_dep; @@ -6130,11 +6332,9 @@ static void output_hdr(FILE *fout) for (i = 0; i < hg_var_cnt; i++) { var = &hg_vars[i]; - if (var->pp != NULL && var->pp->is_fptr) { - fprintf(fout, "extern "); - output_pp(fout, var->pp, 0); - fprintf(fout, ";"); - } + if (var->pp != NULL) + // part of seed + continue; else if (var->is_c_str) fprintf(fout, "extern %-8s %s[];", "char", var->name); else @@ -6151,14 +6351,12 @@ static void output_hdr(FILE *fout) // output function prototypes output_hdr_fp(fout, hg_fp, hg_fp_cnt); - // include passthrough - fprintf(fout, "\n// for translate\n"); + // seed passthrough + fprintf(fout, "\n// - seed -\n"); rewind(g_fhdr); - while (fgets(line, sizeof(line), g_fhdr)) { - if (IS_START(line, "//#")) - fwrite(line, 1, strlen(line), fout); - } + while (fgets(line, sizeof(line), g_fhdr)) + fwrite(line, 1, strlen(line), fout); } // read a line, truncating it if it doesn't fit @@ -6741,7 +6939,7 @@ parse_words: do_pending_endp: // do delayed endp processing to collect switch jumptables if (pending_endp) { - if (in_func && !skip_func && !end && wordc >= 2 + if (in_func && !g_skip_func && !end && wordc >= 2 && ((words[0][0] == 'd' && words[0][2] == 0) || (words[1][0] == 'd' && words[1][2] == 0))) { @@ -6796,7 +6994,7 @@ do_pending_endp: continue; } - if (in_func && !skip_func) { + if (in_func && !g_skip_func) { if (g_header_mode) gen_hdr(g_func, pi); else @@ -6807,7 +7005,7 @@ do_pending_endp: in_func = 0; g_ida_func_attr = 0; skip_warned = 0; - skip_func = 0; + g_skip_func = 0; g_func[0] = 0; func_chunks_used = 0; func_chunk_i = -1; @@ -6841,7 +7039,7 @@ do_pending_endp: words[0], g_func); p = words[0]; if (bsearch(&p, rlist, rlist_len, sizeof(rlist[0]), cmpstringp)) - skip_func = 1; + g_skip_func = 1; strcpy(g_func, words[0]); set_label(0, words[0]); in_func = 1; @@ -6860,10 +7058,10 @@ do_pending_endp: && ops[0].op == OP_JMP && ops[0].operand[0].had_ds) { // import jump - skip_func = 1; + g_skip_func = 1; } - if (!skip_func && func_chunks_used) { + if (!g_skip_func && func_chunks_used) { // start processing chunks struct chunk_item *ci, key = { g_func, 0 }; @@ -6922,8 +7120,8 @@ do_pending_endp: continue; } - if (!in_func || skip_func) { - if (!skip_warned && !skip_func && g_labels[pi] != NULL) { + if (!in_func || g_skip_func) { + if (!skip_warned && !g_skip_func && g_labels[pi] != NULL) { if (verbose) anote("skipping from '%s'\n", g_labels[pi]); skip_warned = 1;