X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=tools%2Ftranslate.c;h=6c432dde9aa699e5bb1f5318dd8e531f8e0f76cb;hb=66bdb2b07a1f50833ad79ea71a378dafd79030e6;hp=e9ea443e62361e40a23b53e2dd50236ec67d542a;hpb=25a330eb4fbab19f36ef6bd8e2f6f817e90b870f;p=ia32rtools.git diff --git a/tools/translate.c b/tools/translate.c index e9ea443..6c432dd 100644 --- a/tools/translate.c +++ b/tools/translate.c @@ -2141,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) { @@ -2173,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) @@ -2181,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); @@ -2323,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 @@ -2352,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) { @@ -2441,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) { @@ -2738,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; @@ -2751,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; @@ -2816,24 +2856,176 @@ 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 -static int is_like_tailjmp(int j) +static void add_label_ref(struct label_ref *lr, int op_i) { - if (!(ops[j].flags & OPF_JMP)) - return 0; + struct label_ref *lr_new; - if (ops[j].op == OP_JMP && !ops[j].operand[0].had_ds) - // probably local branch back.. - return 1; - if (ops[j].op == OP_CALL) - // probably noreturn call.. - return 1; + if (lr->i == -1) { + lr->i = op_i; + return; + } - return 0; + lr_new = calloc(1, sizeof(*lr_new)); + lr_new->i = op_i; + lr_new->next = lr->next; + lr->next = lr_new; +} + +static struct parsed_data *try_resolve_jumptab(int i, int opcnt) +{ + struct parsed_op *po = &ops[i]; + struct parsed_data *pd; + char label[NAMELEN], *p; + int len, j, l; + + p = strchr(po->operand[0].name, '['); + if (p == NULL) + return NULL; + + len = p - po->operand[0].name; + strncpy(label, po->operand[0].name, len); + label[len] = 0; + + for (j = 0, pd = NULL; j < g_func_pd_cnt; j++) { + if (IS(g_func_pd[j].label, label)) { + pd = &g_func_pd[j]; + break; + } + } + if (pd == NULL) + //ferr(po, "label '%s' not parsed?\n", label); + return NULL; + + if (pd->type != OPT_OFFSET) + ferr(po, "label '%s' with non-offset data?\n", label); + + // find all labels, link + for (j = 0; j < pd->count; j++) { + for (l = 0; l < opcnt; l++) { + if (g_labels[l] != NULL && IS(g_labels[l], pd->d[j].u.label)) { + add_label_ref(&g_label_refs[l], i); + pd->d[j].bt_i = l; + break; + } + } + } + + return pd; +} + +static void clear_labels(int count) +{ + int i; + + for (i = 0; i < count; i++) { + if (g_labels[i] != NULL) { + free(g_labels[i]); + g_labels[i] = NULL; + } + } +} + +static void resolve_branches_parse_calls(int opcnt) +{ + const struct parsed_proto *pp_c; + struct parsed_proto *pp; + struct parsed_data *pd; + struct parsed_op *po; + const char *tmpname; + int i, l, ret; + + for (i = 0; i < opcnt; i++) + { + po = &ops[i]; + po->bt_i = -1; + po->btj = NULL; + + if (po->op == OP_CALL) { + pp = NULL; + + if (po->operand[0].type == OPT_LABEL) { + tmpname = opr_name(po, 0); + if (IS_START(tmpname, "loc_")) + ferr(po, "call to loc_*\n"); + pp_c = proto_parse(g_fhdr, tmpname, g_header_mode); + if (!g_header_mode && pp_c == NULL) + ferr(po, "proto_parse failed for call '%s'\n", tmpname); + + if (pp_c != NULL) { + pp = proto_clone(pp_c); + my_assert_not(pp, NULL); + } + } + else if (po->datap != NULL) { + pp = calloc(1, sizeof(*pp)); + my_assert_not(pp, NULL); + + ret = parse_protostr(po->datap, pp); + if (ret < 0) + ferr(po, "bad protostr supplied: %s\n", (char *)po->datap); + free(po->datap); + po->datap = NULL; + } + + if (pp != NULL) { + if (pp->is_fptr) + check_func_pp(po, pp, "fptr var call"); + if (pp->is_noreturn) + po->flags |= OPF_TAIL; + } + po->pp = pp; + continue; + } + + if (!(po->flags & OPF_JMP) || po->op == OP_RET) + continue; + + if (po->operand[0].type == OPT_REGMEM) { + pd = try_resolve_jumptab(i, opcnt); + if (pd == NULL) + goto tailcall; + + po->btj = pd; + continue; + } + + for (l = 0; l < opcnt; l++) { + if (g_labels[l] != NULL + && IS(po->operand[0].name, g_labels[l])) + { + if (l == i + 1 && po->op == OP_JMP) { + // yet another alignment type.. + po->flags |= OPF_RMD|OPF_DONE; + break; + } + add_label_ref(&g_label_refs[l], i); + po->bt_i = l; + break; + } + } + + if (po->bt_i != -1 || (po->flags & OPF_RMD)) + continue; + + if (po->operand[0].type == OPT_LABEL) + // assume tail call + goto tailcall; + + ferr(po, "unhandled branch\n"); + +tailcall: + po->op = OP_CALL; + po->flags |= OPF_TAIL; + if (i > 0 && ops[i - 1].op == OP_POP) + po->flags |= OPF_ATAIL; + i--; // reprocess + } } static void scan_prologue_epilogue(int opcnt) @@ -2883,12 +3075,13 @@ static void scan_prologue_epilogue(int opcnt) found = 0; do { for (; i < opcnt; i++) - if (ops[i].op == OP_RET) + if (ops[i].flags & OPF_TAIL) break; j = i - 1; if (i == opcnt && (ops[j].flags & OPF_JMP)) { - if (found && is_like_tailjmp(j)) - break; + if (ops[j].bt_i != -1 || ops[j].btj != NULL) + break; + i--; j--; } @@ -2897,6 +3090,14 @@ static void scan_prologue_epilogue(int opcnt) { ops[j].flags |= OPF_RMD | OPF_DONE; } + else if (ops[i].op == OP_CALL && ops[i].pp != NULL + && ops[i].pp->is_noreturn) + { + // on noreturn, msvc sometimes cleans stack, sometimes not + i++; + found = 1; + continue; + } else if (!(g_ida_func_attr & IDAFA_NORETURN)) ferr(&ops[j], "'pop ebp' expected\n"); @@ -2924,6 +3125,8 @@ static void scan_prologue_epilogue(int opcnt) i++; } while (i < opcnt); + if (!found) + ferr(ops, "missing ebp epilogue\n"); return; } @@ -2989,12 +3192,13 @@ static void scan_prologue_epilogue(int opcnt) i++; do { for (; i < opcnt; i++) - if (ops[i].op == OP_RET) + if (ops[i].flags & OPF_TAIL) break; j = i - 1; if (i == opcnt && (ops[j].flags & OPF_JMP)) { - if (found && is_like_tailjmp(j)) - break; + if (ops[j].bt_i != -1 || ops[j].btj != NULL) + break; + i--; j--; } @@ -3035,16 +3239,20 @@ static void scan_prologue_epilogue(int opcnt) i++; } while (i < opcnt); + + if (!found) + ferr(ops, "missing esp epilogue\n"); } } 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: @@ -3056,7 +3264,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; } @@ -3064,7 +3272,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, @@ -3116,6 +3324,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) { @@ -3173,6 +3485,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; @@ -3181,7 +3494,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); @@ -3195,6 +3508,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; @@ -3589,127 +3919,58 @@ static int collect_call_args(struct parsed_op *po, int i, int save_arg_vars_current = 0; int arg_grp = 0; int ret; - int a; - - ret = collect_call_args_r(po, i, pp, regmask, - &save_arg_vars_current, &arg_grp, 0, magic, 0, 0); - if (ret < 0) - return ret; - - if (arg_grp != 0) { - // propagate arg_grp - for (a = 0; a < pp->argc; a++) { - if (pp->arg[a].reg != NULL) - continue; - - po_tmp = pp->arg[a].datap; - while (po_tmp != NULL) { - po_tmp->p_arggrp = arg_grp; - if (po_tmp->p_argnext > 0) - po_tmp = &ops[po_tmp->p_argnext]; - else - po_tmp = NULL; - } - } - } - save_arg_vars[arg_grp] |= save_arg_vars_current; - - if (pp->is_unresolved) { - pp->argc += ret; - pp->argc_stack += ret; - for (a = 0; a < pp->argc; a++) - if (pp->arg[a].type.name == NULL) - pp->arg[a].type.name = strdup("int"); - } - - return ret; -} - -static void pp_insert_reg_arg(struct parsed_proto *pp, const char *reg) -{ - int i; - - for (i = 0; i < pp->argc; i++) - if (pp->arg[i].reg == NULL) - break; - - if (pp->argc_stack) - memmove(&pp->arg[i + 1], &pp->arg[i], - sizeof(pp->arg[0]) * pp->argc_stack); - memset(&pp->arg[i], 0, sizeof(pp->arg[i])); - pp->arg[i].reg = strdup(reg); - pp->arg[i].type.name = strdup("int"); - pp->argc++; - pp->argc_reg++; -} - -static void add_label_ref(struct label_ref *lr, int op_i) -{ - struct label_ref *lr_new; - - if (lr->i == -1) { - lr->i = op_i; - return; - } - - lr_new = calloc(1, sizeof(*lr_new)); - lr_new->i = op_i; - lr_new->next = lr->next; - lr->next = lr_new; -} - -static struct parsed_data *try_resolve_jumptab(int i, int opcnt) -{ - struct parsed_op *po = &ops[i]; - struct parsed_data *pd; - char label[NAMELEN], *p; - int len, j, l; - - p = strchr(po->operand[0].name, '['); - if (p == NULL) - return NULL; - - len = p - po->operand[0].name; - strncpy(label, po->operand[0].name, len); - label[len] = 0; - - for (j = 0, pd = NULL; j < g_func_pd_cnt; j++) { - if (IS(g_func_pd[j].label, label)) { - pd = &g_func_pd[j]; - break; - } - } - if (pd == NULL) - //ferr(po, "label '%s' not parsed?\n", label); - return NULL; + int a; - if (pd->type != OPT_OFFSET) - ferr(po, "label '%s' with non-offset data?\n", label); + ret = collect_call_args_r(po, i, pp, regmask, + &save_arg_vars_current, &arg_grp, 0, magic, 0, 0); + if (ret < 0) + return ret; - // find all labels, link - for (j = 0; j < pd->count; j++) { - for (l = 0; l < opcnt; l++) { - if (g_labels[l] != NULL && IS(g_labels[l], pd->d[j].u.label)) { - add_label_ref(&g_label_refs[l], i); - pd->d[j].bt_i = l; - break; + if (arg_grp != 0) { + // propagate arg_grp + for (a = 0; a < pp->argc; a++) { + if (pp->arg[a].reg != NULL) + continue; + + po_tmp = pp->arg[a].datap; + while (po_tmp != NULL) { + po_tmp->p_arggrp = arg_grp; + if (po_tmp->p_argnext > 0) + po_tmp = &ops[po_tmp->p_argnext]; + else + po_tmp = NULL; } } } + save_arg_vars[arg_grp] |= save_arg_vars_current; - return pd; + if (pp->is_unresolved) { + pp->argc += ret; + pp->argc_stack += ret; + for (a = 0; a < pp->argc; a++) + if (pp->arg[a].type.name == NULL) + pp->arg[a].type.name = strdup("int"); + } + + return ret; } -static void clear_labels(int count) +static void pp_insert_reg_arg(struct parsed_proto *pp, const char *reg) { int i; - for (i = 0; i < count; i++) { - if (g_labels[i] != NULL) { - free(g_labels[i]); - g_labels[i] = NULL; - } - } + for (i = 0; i < pp->argc; i++) + if (pp->arg[i].reg == NULL) + break; + + if (pp->argc_stack) + memmove(&pp->arg[i + 1], &pp->arg[i], + sizeof(pp->arg[0]) * pp->argc_stack); + memset(&pp->arg[i], 0, sizeof(pp->arg[i])); + pp->arg[i].reg = strdup(reg); + pp->arg[i].type.name = strdup("int"); + pp->argc++; + pp->argc_reg++; } static void output_std_flags(FILE *fout, struct parsed_op *po, @@ -3828,10 +4089,8 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) struct parsed_op *po, *delayed_flag_op = NULL, *tmp_op; struct parsed_opr *last_arith_dst = NULL; char buf1[256], buf2[256], buf3[256], cast[64]; - const struct parsed_proto *pp_c; struct parsed_proto *pp, *pp_tmp; struct parsed_data *pd; - const char *tmpname; unsigned int uval; int save_arg_vars[MAX_ARG_GRP] = { 0, }; int cond_vars = 0; @@ -3864,110 +4123,28 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) regmask_arg = get_pp_arg_regmask(g_func_pp); // pass1: - // - handle ebp/esp frame, remove ops related to it - scan_prologue_epilogue(opcnt); - - // pass2: - // - parse calls with labels // - resolve all branches - for (i = 0; i < opcnt; i++) - { - po = &ops[i]; - po->bt_i = -1; - po->btj = NULL; - - if (po->flags & (OPF_RMD|OPF_DONE)) - continue; - - if (po->op == OP_CALL) { - pp = NULL; - - if (po->operand[0].type == OPT_LABEL) { - tmpname = opr_name(po, 0); - if (IS_START(tmpname, "loc_")) - ferr(po, "call to loc_*\n"); - pp_c = proto_parse(fhdr, tmpname, 0); - if (pp_c == NULL) - ferr(po, "proto_parse failed for call '%s'\n", tmpname); - - pp = proto_clone(pp_c); - my_assert_not(pp, NULL); - } - else if (po->datap != NULL) { - pp = calloc(1, sizeof(*pp)); - my_assert_not(pp, NULL); - - ret = parse_protostr(po->datap, pp); - if (ret < 0) - ferr(po, "bad protostr supplied: %s\n", (char *)po->datap); - free(po->datap); - po->datap = NULL; - } - - if (pp != NULL) { - if (pp->is_fptr) - check_func_pp(po, pp, "fptr var call"); - if (pp->is_noreturn) - po->flags |= OPF_TAIL; - } - po->pp = pp; - continue; - } - - if (!(po->flags & OPF_JMP) || po->op == OP_RET) - continue; - - if (po->operand[0].type == OPT_REGMEM) { - pd = try_resolve_jumptab(i, opcnt); - if (pd == NULL) - goto tailcall; - - po->btj = pd; - continue; - } - - for (l = 0; l < opcnt; l++) { - if (g_labels[l] != NULL - && IS(po->operand[0].name, g_labels[l])) - { - if (l == i + 1 && po->op == OP_JMP) { - // yet another alignment type.. - po->flags |= OPF_RMD|OPF_DONE; - break; - } - add_label_ref(&g_label_refs[l], i); - po->bt_i = l; - break; - } - } - - if (po->bt_i != -1 || (po->flags & OPF_RMD)) - continue; - - if (po->operand[0].type == OPT_LABEL) - // assume tail call - goto tailcall; - - ferr(po, "unhandled branch\n"); + // - parse calls with labels + resolve_branches_parse_calls(opcnt); -tailcall: - po->op = OP_CALL; - po->flags |= OPF_TAIL; - if (i > 0 && ops[i - 1].op == OP_POP) - po->flags |= OPF_ATAIL; - i--; // reprocess - } + // pass2: + // - handle ebp/esp frame, remove ops related to it + scan_prologue_epilogue(opcnt); // pass3: // - remove dead labels - // - process trivial calls for (i = 0; i < opcnt; i++) { if (g_labels[i] != NULL && g_label_refs[i].i == -1) { free(g_labels[i]); g_labels[i] = NULL; } + } + // pass4: + // - process trivial calls + for (i = 0; i < opcnt; i++) + { po = &ops[i]; if (po->flags & (OPF_RMD|OPF_DONE)) continue; @@ -4000,7 +4177,7 @@ tailcall: } } - // pass4: + // pass5: // - process calls // - handle push /pop pairs for (i = 0; i < opcnt; i++) @@ -4027,7 +4204,7 @@ tailcall: scan_for_pop_const(i, opcnt, ®mask_pp); } - // pass5: + // pass6: // - find POPs for PUSHes, rm both // - scan for STD/CLD, propagate DF // - scan for all used registers @@ -4272,7 +4449,7 @@ tailcall: } } - // pass6: + // pass7: // - confirm regmask_save, it might have been reduced if (regmask_save != 0) { @@ -4804,6 +4981,19 @@ tailcall: // arithmetic w/flags case OP_AND: + if (po->operand[1].type == OPT_CONST && !po->operand[1].val) { + // deal with complex dst clear + assert_operand_cnt(2); + fprintf(fout, " %s = %s;", + out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]), + out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], + default_cast_to(buf3, sizeof(buf3), &po->operand[0]), 0)); + output_std_flags(fout, po, &pfomask, buf1); + last_arith_dst = &po->operand[0]; + delayed_flag_op = NULL; + break; + } + // fallthrough case OP_OR: propagate_lmod(po, &po->operand[0], &po->operand[1]); // fallthrough @@ -5648,17 +5838,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); @@ -5767,17 +5954,14 @@ static void gen_hdr(const char *funcn, int opcnt) { int save_arg_vars[MAX_ARG_GRP] = { 0, }; unsigned char cbits[MAX_OPS / 8]; - const struct parsed_proto *pp_c; struct parsed_proto *pp; struct func_prototype *fp; - struct parsed_data *pd; struct parsed_op *po; - const char *tmpname; int regmask_dummy = 0; int regmask_dep; int max_bp_offset = 0; int has_ret; - int i, j, l, ret; + int i, j, ret; if ((hg_fp_cnt & 0xff) == 0) { hg_fp = realloc(hg_fp, sizeof(hg_fp[0]) * (hg_fp_cnt + 0x100)); @@ -5805,89 +5989,37 @@ static void gen_hdr(const char *funcn, int opcnt) g_stack_frame_used = 0; // pass1: + // - resolve all branches + // - parse calls with labels + resolve_branches_parse_calls(opcnt); + + // pass2: // - handle ebp/esp frame, remove ops related to it scan_prologue_epilogue(opcnt); - // pass2: + // pass3: + // - remove dead labels // - collect calls - // - resolve all branches for (i = 0; i < opcnt; i++) { - po = &ops[i]; - po->bt_i = -1; - po->btj = NULL; + if (g_labels[i] != NULL && g_label_refs[i].i == -1) { + free(g_labels[i]); + g_labels[i] = NULL; + } + po = &ops[i]; if (po->flags & (OPF_RMD|OPF_DONE)) continue; if (po->op == OP_CALL) { - tmpname = opr_name(po, 0); - pp = NULL; - if (po->operand[0].type == OPT_LABEL) { - hg_fp_add_dep(fp, tmpname); - - // perhaps a call to already known func? - pp_c = proto_parse(g_fhdr, tmpname, 1); - if (pp_c != NULL) - pp = proto_clone(pp_c); - } - else if (po->datap != NULL) { - pp = calloc(1, sizeof(*pp)); - my_assert_not(pp, NULL); - - ret = parse_protostr(po->datap, pp); - if (ret < 0) - ferr(po, "bad protostr supplied: %s\n", (char *)po->datap); - free(po->datap); - po->datap = NULL; - } - if (pp != NULL && pp->is_noreturn) - po->flags |= OPF_TAIL; - - po->pp = pp; - continue; - } - - if (!(po->flags & OPF_JMP) || po->op == OP_RET) - continue; - - if (po->operand[0].type == OPT_REGMEM) { - pd = try_resolve_jumptab(i, opcnt); - if (pd == NULL) - goto tailcall; - - po->btj = pd; - continue; + if (po->operand[0].type == OPT_LABEL) + hg_fp_add_dep(fp, opr_name(po, 0)); + else if (po->pp != NULL) + hg_fp_add_dep(fp, po->pp->name); } - - for (l = 0; l < opcnt; l++) { - if (g_labels[l] != NULL - && IS(po->operand[0].name, g_labels[l])) - { - add_label_ref(&g_label_refs[l], i); - po->bt_i = l; - break; - } - } - - if (po->bt_i != -1 || (po->flags & OPF_RMD)) - continue; - - if (po->operand[0].type == OPT_LABEL) - // assume tail call - goto tailcall; - - ferr(po, "unhandled branch\n"); - -tailcall: - po->op = OP_CALL; - po->flags |= OPF_TAIL; - if (i > 0 && ops[i - 1].op == OP_POP) - po->flags |= OPF_ATAIL; - i--; // reprocess } - // pass3: + // pass4: // - remove dead labels // - handle push /pop pairs for (i = 0; i < opcnt; i++) @@ -5905,7 +6037,7 @@ tailcall: scan_for_pop_const(i, opcnt, ®mask_dummy); } - // pass4: + // pass5: // - process trivial calls for (i = 0; i < opcnt; i++) { @@ -5938,7 +6070,7 @@ tailcall: } } - // pass5: + // pass6: // - track saved regs (simple) // - process calls for (i = 0; i < opcnt; i++) @@ -5968,7 +6100,7 @@ tailcall: } } - // pass6 + // pass7 memset(cbits, 0, sizeof(cbits)); regmask_dep = 0; has_ret = -1;