X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=tools%2Ftranslate.c;h=99d83e9e18936cbcd751a763e25b735747647abb;hb=f124ee13ba892739bb8164e6be9c8d3942a28744;hp=e2db06d1b3ea13923c80741dc6702b43f81054f2;hpb=c47daea41b46c1f418246d940b6edaa7567be648;p=ia32rtools.git diff --git a/tools/translate.c b/tools/translate.c index e2db06d..99d83e9 100644 --- a/tools/translate.c +++ b/tools/translate.c @@ -224,10 +224,10 @@ struct parsed_op { unsigned char pfo; unsigned char pfo_inv; unsigned char operand_cnt; - unsigned char p_argnum; // arg push: altered before call arg # + unsigned char p_argnum; // arg push: call's saved arg # unsigned char p_arggrp; // arg push: arg group # for above unsigned char p_argpass;// arg push: arg of host func - short p_argnext;// arg push: same arg pushed elsewhere or -1 + short pad; int regmask_src; // all referensed regs int regmask_dst; int pfomask; // flagop: parsed_flag_op that can't be delayed @@ -289,6 +289,7 @@ enum sct_func_attr { SCTFA_RM_REGS = (1 << 2), // don't emit regs (mask) SCTFA_NOWARN = (1 << 3), // don't try to detect problems SCTFA_ARGFRAME = (1 << 4), // copy all args to a struct, in order + SCTFA_UA_FLOAT = (1 << 5), // emit float i/o helpers for alignemnt }; enum x87_const { @@ -310,7 +311,6 @@ enum segment { SEG_GS, }; -// note: limited to 32k due to p_argnext #define MAX_OPS 4096 #define MAX_ARG_GRP 2 @@ -1950,7 +1950,7 @@ static int stack_frame_access(struct parsed_op *po, const char *prefix = ""; const char *bp_arg = NULL; char ofs_reg[16] = { 0, }; - char argname[8]; + char argname[8], buf2[32]; int i, arg_i, arg_s; int unaligned = 0; int stack_ra = 0; @@ -1972,15 +1972,20 @@ static int stack_frame_access(struct parsed_op *po, arg_i = (offset - stack_ra - 4) / 4; if (arg_i < 0 || arg_i >= g_func_pp->argc_stack) { - if (g_func_pp->is_vararg && arg_i == g_func_pp->argc_stack) { - if (is_lea) { + if (g_func_pp->is_vararg && arg_i >= g_func_pp->argc_stack) { + // vararg access - messy and non-portable, + // but works with gcc on both x86 and ARM + if (arg_i == g_func_pp->argc_stack) // should be va_list - if (cast[0] == 0) - cast = "(u32)"; - snprintf(buf, buf_size, "%sap", cast); - } + snprintf(buf2, sizeof(buf2), "*(u32 *)&ap"); + else + snprintf(buf2, sizeof(buf2), "(*(u32 *)&ap + %u)", + (arg_i - g_func_pp->argc_stack) * 4); + + if (is_lea) + snprintf(buf, buf_size, "%s%s", cast, buf2); else - snprintf(buf, buf_size, "%sva_arg(ap, u32)", cast); + snprintf(buf, buf_size, "%s*(u32 *)%s", cast, buf2); return -1; } ferr(po, "offset 0x%x (%s,%d) doesn't map to any arg\n", @@ -2406,6 +2411,20 @@ static char *out_src_opr_u32(char *buf, size_t buf_size, return out_src_opr(buf, buf_size, po, popr, NULL, 0); } +// do we need a helper func to perform a float i/o? +static int float_opr_needs_helper(struct parsed_op *po, + struct parsed_opr *popr) +{ + if (!(g_sct_func_attr & SCTFA_UA_FLOAT)) + return 0; + if (popr->type != OPT_REGMEM) + return 0; + if (is_stack_access(po, popr)) + return 0; + + return 1; +} + static char *out_opr_float(char *buf, size_t buf_size, struct parsed_op *po, struct parsed_opr *popr, int is_src, int need_float_stack) @@ -2459,7 +2478,10 @@ static char *out_opr_float(char *buf, size_t buf_size, break; } out_src_opr(tmp, sizeof(tmp), po, popr, "", 1); - snprintf(buf, buf_size, "*(%s *)(%s)", cast, tmp); + if (is_src && float_opr_needs_helper(po, popr)) + snprintf(buf, buf_size, "%s_load(%s)", cast, tmp); + else + snprintf(buf, buf_size, "*(%s *)(%s)", cast, tmp); break; case OPT_CONST: @@ -5262,6 +5284,8 @@ static void check_fptr_args(int i, int opcnt, struct parsed_proto *pp) const char *s_reg; int pp_cmp_i; int arg, reg; + int bad = 0; + int j; for (arg = 0; arg < pp->argc; arg++) { pp_cmp = NULL; @@ -5278,31 +5302,81 @@ static void check_fptr_args(int i, int opcnt, struct parsed_proto *pp) s_opr.reg = reg; scan_for_call_type(i, &s_opr, i + arg + opcnt * 28, 0, &pp_cmp, &pp_cmp_i, NULL); + if (pp_cmp != NULL && !pp_compatible_func(pp_arg, pp_cmp)) { + bad = 1; + if (pp_cmp_i >= 0) + fnote(&ops[pp_cmp_i], "(referenced here)\n"); + } } else { - po_a = pp->arg[arg].datap; - if (po_a != NULL && po_a->op == OP_PUSH) + for (j = 0; j < pp->arg[arg].push_ref_cnt; j++) { + po_a = pp->arg[arg].push_refs[j]; + if (po_a == NULL || po_a->op != OP_PUSH) + continue; pp_cmp = resolve_func_ptr(po_a - ops, opcnt, 0, &po_a->operand[0], &pp_cmp_i, NULL); - if (pp_cmp_i < 0) - pp_cmp_i = po_a - ops; + if (pp_cmp != NULL && !pp_compatible_func(pp_arg, pp_cmp)) { + bad = 1; + if (pp_cmp_i < 0) + pp_cmp_i = po_a - ops; + if (pp_cmp_i >= 0) + fnote(&ops[pp_cmp_i], "(referenced here)\n"); + } + } } - if (pp_cmp != NULL && !pp_compatible_func(pp_arg, pp_cmp)) { - if (pp_cmp_i >= 0) - fnote(&ops[pp_cmp_i], "(referenced here)\n"); + if (bad) ferr(&ops[i], "incompatible fptr arg %d\n", arg + 1); - } } } +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 pp_insert_stack_args(struct parsed_proto *pp, int count) +{ + int a; + + pp->argc += count; + pp->argc_stack += count; + + for (a = 0; a < pp->argc; a++) + if (pp->arg[a].type.name == NULL) + pp->arg[a].type.name = strdup("int"); +} + +static void pp_add_push_ref(struct parsed_proto *pp, + int arg, struct parsed_op *po) +{ + pp->arg[arg].push_refs = realloc(pp->arg[arg].push_refs, + (pp->arg[arg].push_ref_cnt + 1) + * sizeof(pp->arg[arg].push_refs[0])); + ferr_assert(po, pp->arg[arg].push_refs != NULL); + pp->arg[arg].push_refs[pp->arg[arg].push_ref_cnt++] = po; +} + static void mark_float_arg(struct parsed_op *po, struct parsed_proto *pp, int arg, int *regmask_ffca) { - po->p_argnext = -1; + ferr_assert(po, pp->arg[arg].push_ref_cnt == 0); + pp_add_push_ref(pp, arg, po); + po->p_argnum = arg + 1; - ferr_assert(po, pp->arg[arg].datap == NULL); - pp->arg[arg].datap = po; po->flags |= OPF_DONE | OPF_FARGNR | OPF_FARG; if (regmask_ffca != NULL) *regmask_ffca |= 1 << arg; @@ -5372,9 +5446,9 @@ static int collect_call_args_no_push(int i, struct parsed_proto *pp, for (arg = base_arg; arg < pp->argc; arg++) { ferr_assert(&ops[i], pp->arg[arg].reg == NULL); - po = pp->arg[arg].datap; - if (po == NULL) - ferr(&ops[i], "arg %d/%d not found\n", arg, pp->argc); + if (pp->arg[arg].push_ref_cnt != 1) + ferr(&ops[i], "arg %d/%d not found or bad\n", arg, pp->argc); + po = pp->arg[arg].push_refs[0]; if (po->operand[0].lmod == OPLM_QWORD) arg++; } @@ -5454,8 +5528,7 @@ static int collect_call_args_early(int i, int opcnt, if (ops[j].op == OP_PUSH) { - ops[j].p_argnext = -1; - ferr_assert(&ops[j], pp->arg[arg].datap == NULL); + int ref_handled = 0; k = check_for_stp(j + 1, i); if (k != -1) { @@ -5466,15 +5539,18 @@ static int collect_call_args_early(int i, int opcnt, if (!pp->arg[arg].type.is_float) ferr(&ops[i], "arg %d should be float\n", arg + 1); mark_float_arg(&ops[k], pp, arg, regmask_ffca); + ref_handled = 1; } } - if (pp->arg[arg].datap == NULL) { - pp->arg[arg].datap = &ops[j]; - if (regmask != NULL && ops[j].operand[0].type == OPT_REG) - *regmask |= 1 << ops[j].operand[0].reg; + if (!ref_handled) { + ferr_assert(&ops[j], pp->arg[arg].push_ref_cnt == 0); + pp_add_push_ref(pp, arg, &ops[j]); } + if (regmask != NULL && ops[j].operand[0].type == OPT_REG) + *regmask |= 1 << ops[j].operand[0].reg; + ops[j].flags |= OPF_RMD | OPF_DONE | OPF_FARGNR | OPF_FARG; ops[j].flags &= ~OPF_RSAVE; @@ -5491,38 +5567,50 @@ static int collect_call_args_early(int i, int opcnt, return 0; } -static int sync_argnum(struct parsed_op *po, int argnum) +// ensure all s_a* numbers match for a given func arg in all branches +// returns 1 if any changes were made, 0 if not +static int sync_argnum(struct parsed_proto *pp, int arg, + int *argnum, int *arggrp) { struct parsed_op *po_tmp; + int changed = 0; + int i; // see if other branches don't have higher argnum - for (po_tmp = po; po_tmp != NULL; ) { - if (argnum < po_tmp->p_argnum) - argnum = po_tmp->p_argnum; - // note: p_argnext is active on current collect_call_args only - po_tmp = po_tmp->p_argnext >= 0 ? &ops[po_tmp->p_argnext] : NULL; + for (i = 0; i < pp->arg[arg].push_ref_cnt; i++) { + po_tmp = pp->arg[arg].push_refs[i]; + if (*argnum < po_tmp->p_argnum) + *argnum = po_tmp->p_argnum; + if (*arggrp < po_tmp->p_arggrp) + *arggrp = po_tmp->p_arggrp; } // make all argnums consistent - for (po_tmp = po; po_tmp != NULL; ) { - if (po_tmp->p_argnum != 0) - po_tmp->p_argnum = argnum; - po_tmp = po_tmp->p_argnext >= 0 ? &ops[po_tmp->p_argnext] : NULL; + for (i = 0; i < pp->arg[arg].push_ref_cnt; i++) { + po_tmp = pp->arg[arg].push_refs[i]; + if (po_tmp->p_argnum == 0) + continue; + if (po_tmp->p_argnum != *argnum || po_tmp->p_arggrp != *arggrp) { + po_tmp->p_argnum = *argnum; + po_tmp->p_arggrp = *arggrp; + changed = 1; + } } - return argnum; + return changed; } static int collect_call_args_r(struct parsed_op *po, int i, - struct parsed_proto *pp, int *regmask, int *arg_grp, - int arg, int argnum, int magic, int need_op_saving, int may_reuse) + struct parsed_proto *pp, int *regmask, + int arg, int argnum, int magic, + int skip, int need_op_saving, int may_reuse) { struct parsed_proto *pp_tmp; - struct parsed_op *po_tmp; struct label_ref *lr; int need_to_save_current; int arg_grp_current = 0; int save_args_seen = 0; + int dummy = 0; int ret = 0; int reg; char buf[32]; @@ -5559,8 +5647,8 @@ static int collect_call_args_r(struct parsed_op *po, int i, check_i(&ops[j], lr->i); if ((ops[lr->i].flags & (OPF_JMP|OPF_CJMP)) != OPF_JMP) may_reuse = 1; - ret = collect_call_args_r(po, lr->i, pp, regmask, arg_grp, - arg, argnum, magic, need_op_saving, may_reuse); + ret = collect_call_args_r(po, lr->i, pp, regmask, + arg, argnum, magic, skip, need_op_saving, may_reuse); if (ret < 0) return ret; } @@ -5574,8 +5662,8 @@ static int collect_call_args_r(struct parsed_op *po, int i, continue; } need_op_saving = 1; - ret = collect_call_args_r(po, lr->i, pp, regmask, arg_grp, - arg, argnum, magic, need_op_saving, may_reuse); + ret = collect_call_args_r(po, lr->i, pp, regmask, + arg, argnum, magic, skip, need_op_saving, may_reuse); if (ret < 0) return ret; } @@ -5593,6 +5681,8 @@ static int collect_call_args_r(struct parsed_op *po, int i, if (may_reuse && pp_tmp->argc_stack > 0) ferr(po, "arg collect %d/%d hit '%s' with %d stack args\n", arg, pp->argc, opr_name(&ops[j], 0), pp_tmp->argc_stack); + if (!pp_tmp->is_unresolved) + skip = pp_tmp->argc_stack; } // esp adjust of 0 means we collected it before else if (ops[j].op == OP_ADD && ops[j].operand[0].reg == xSP @@ -5621,19 +5711,19 @@ static int collect_call_args_r(struct parsed_op *po, int i, may_reuse = 1; } + else if (ops[j].op == OP_PUSH && skip > 0) { + // XXX: might want to rm OPF_FARGNR and only use this + skip--; + } else if (ops[j].op == OP_PUSH && !(ops[j].flags & (OPF_FARGNR|OPF_DONE))) { if (pp->is_unresolved && (ops[j].flags & OPF_RMD)) break; - ops[j].p_argnext = -1; - po_tmp = pp->arg[arg].datap; - if (po_tmp != NULL) - ops[j].p_argnext = po_tmp - ops; - pp->arg[arg].datap = &ops[j]; + pp_add_push_ref(pp, arg, &ops[j]); - argnum = sync_argnum(&ops[j], argnum); + sync_argnum(pp, arg, &argnum, &dummy); need_to_save_current = 0; reg = -1; @@ -5715,6 +5805,7 @@ static int collect_call_args_r(struct parsed_op *po, int i, if (pp->arg[arg].is_saved) { ops[j].flags &= ~OPF_RMD; ops[j].p_argnum = argnum; + ops[j].p_arggrp = arg_grp_current; } // tracking reg usage @@ -5746,48 +5837,25 @@ static int collect_call_args_r(struct parsed_op *po, int i, return -1; } - if (arg_grp_current > *arg_grp) - *arg_grp = arg_grp_current; - return arg; } static int collect_call_args(struct parsed_op *po, int i, int opcnt, struct parsed_proto *pp, int *regmask, int magic) { - // arg group is for cases when pushes for - // multiple funcs are going on - struct parsed_op *po_tmp; - int arg_grp = 0; int ret; - int a; - ret = collect_call_args_r(po, i, pp, regmask, &arg_grp, - 0, 1, magic, 0, 0); + ret = collect_call_args_r(po, i, pp, regmask, 0, 1, magic, + 0, 0, 0); if (ret < 0) return ret; - 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"); - } + if (pp->is_unresolved) + pp_insert_stack_args(pp, 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; - po_tmp = po_tmp->p_argnext >= 0 ? &ops[po_tmp->p_argnext] : NULL; - } - } - } + // note: p_argnum, p_arggrp will be propagated in a later pass, + // look for sync_argnum() (p_arggrp is for cases when mixed pushes + // for multiple funcs are going on) if (!g_header_mode) check_fptr_args(i, opcnt, pp); @@ -5997,24 +6065,6 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits, } } -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 output_std_flag_z(FILE *fout, struct parsed_op *po, int *pfomask, const char *dst_opr_text) { @@ -6434,19 +6484,24 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (pp->is_unresolved) { int regmask_stack = 0; - collect_call_args(po, i, opcnt, pp, ®mask, i + opcnt * 2); - // this is pretty rough guess: - // see ecx and edx were pushed (and not their saved versions) - for (arg = 0; arg < pp->argc; arg++) { - if (pp->arg[arg].reg != NULL && !pp->arg[arg].is_saved) - continue; + if ((po->flags & OPF_TAIL) && g_func_pp->is_stdcall) + pp_insert_stack_args(pp, g_func_pp->argc_stack); + else { + collect_call_args(po, i, opcnt, pp, ®mask, i + opcnt * 2); - tmp_op = pp->arg[arg].datap; - if (tmp_op == NULL) - ferr(po, "parsed_op missing for arg%d\n", arg); - if (tmp_op->operand[0].type == OPT_REG) - regmask_stack |= 1 << tmp_op->operand[0].reg; + // this is pretty rough guess: + // see ecx and edx were pushed (and not their saved versions) + for (arg = 0; arg < pp->argc; arg++) { + if (pp->arg[arg].reg != NULL && !pp->arg[arg].is_saved) + continue; + + if (pp->arg[arg].push_ref_cnt == 0) + ferr(po, "parsed_op missing for arg%d\n", arg); + tmp_op = pp->arg[arg].push_refs[0]; + if (tmp_op->operand[0].type == OPT_REG) + regmask_stack |= 1 << tmp_op->operand[0].reg; + } } // quick dumb check for potential reg-args @@ -6589,7 +6644,41 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) } } - // pass8: final adjustments + // pass8: sync all push arg numbers + // some calls share args and not all of them + // (there's only partial intersection) + do { + int changed, argnum, arggrp; + + found = 0; + for (i = 0; i < opcnt; i++) + { + po = &ops[i]; + if ((po->flags & (OPF_RMD|OPF_DONE)) || po->op != OP_CALL) + continue; + + pp = po->pp; + arggrp = 0; + do { + changed = 0; + for (arg = argnum = 0; arg < pp->argc; arg++) { + if (pp->arg[arg].reg != NULL) + continue; + if (pp->arg[arg].is_saved) + changed |= sync_argnum(pp, arg, &argnum, &arggrp); + argnum++; + } + found |= changed; + } + while (changed); + + if (argnum > 32) + ferr(po, "too many args or looping in graph\n"); + } + } + while (found); + + // pass9: final adjustments for (i = 0; i < opcnt; i++) { po = &ops[i]; @@ -7827,9 +7916,12 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) } // stack arg - tmp_op = pp->arg[arg].datap; - if (tmp_op == NULL) + if (pp->arg[arg].push_ref_cnt == 0) ferr(po, "parsed_op missing for arg%d\n", arg); + if (pp->arg[arg].push_ref_cnt > 1) + ferr_assert(po, pp->arg[arg].is_saved); + tmp_op = pp->arg[arg].push_refs[0]; + ferr_assert(po, tmp_op != NULL); if (tmp_op->flags & OPF_VAPUSH) { fprintf(fout, "ap"); @@ -7846,7 +7938,9 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) ferr_assert(po, cast[0] == 0); out_src_opr(buf1, sizeof(buf1), tmp_op, &tmp_op->operand[0], cast, 0); - tmp_op = pp->arg[++arg].datap; + arg++; + ferr_assert(po, pp->arg[arg].push_ref_cnt == 1); + tmp_op = pp->arg[arg].push_refs[0]; ferr_assert(po, tmp_op != NULL); out_src_opr(buf2, sizeof(buf2), tmp_op, &tmp_op->operand[0], cast, 0); @@ -8096,19 +8190,27 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) break; case OP_FST: + dead_dst = 0; if (po->flags & OPF_FARG) { // store to stack as func arg - snprintf(buf1, sizeof(buf1), "fs_%d", po->p_argnum); - dead_dst = 0; + fprintf(fout, " fs_%d = %s;", po->p_argnum, float_st0); + } + else if (po->operand[0].type == OPT_REG + && po->operand[0].reg == xST0) + { + dead_dst = 1; + } + else if (float_opr_needs_helper(po, &po->operand[0])) { + out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], "", 1); + fprintf(fout, " %s_store(%s, %s);", + po->operand[0].lmod == OPLM_QWORD ? "double" : "float", + float_st0, buf1); } else { out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0], need_float_stack); - dead_dst = po->operand[0].type == OPT_REG - && po->operand[0].reg == xST0; - } - if (!dead_dst) fprintf(fout, " %s = %s;", buf1, float_st0); + } if (po->flags & OPF_FSHIFT) { if (need_float_stack) fprintf(fout, " f_stp++;"); @@ -9784,6 +9886,7 @@ int main(int argc, char *argv[]) "rm_regmask", "nowarn", "argframe", + "align_float", }; // parse manual attribute-list comment