X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=tools%2Ftranslate.c;h=d51d64c1784265f9a32e3723778cce18585666b4;hb=f9327ad4c893cce131ab39f91743d69ebac662d1;hp=e8b1ba9a91bc97be2da42f47a48d711f068fcf8d;hpb=0ea6430ca5707ae1e6ddd43d5e01dbc0f6979bf4;p=ia32rtools.git diff --git a/tools/translate.c b/tools/translate.c index e8b1ba9..d51d64c 100644 --- a/tools/translate.c +++ b/tools/translate.c @@ -19,15 +19,12 @@ #include #include #include +#include #include "my_assert.h" #include "my_str.h" #include "common.h" -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) -#define IS(w, y) !strcmp(w, y) -#define IS_START(w, y) !strncmp(w, y, strlen(y)) - #include "protoparse.h" static const char *asmfn; @@ -116,6 +113,7 @@ enum op_op { OP_ADC, OP_SBB, OP_BSF, + OP_BSR, OP_INC, OP_DEC, OP_NEG, @@ -359,6 +357,7 @@ enum x86_regs { #define mxAX (1 << xAX) #define mxCX (1 << xCX) #define mxDX (1 << xDX) +#define mxSP (1 << xSP) #define mxST0 (1 << xST0) #define mxST1 (1 << xST1) #define mxST1_0 (mxST1 | mxST0) @@ -491,7 +490,7 @@ static int parse_indmode(char *name, int *regmask, int need_c_cvt) } if ('0' <= w[0] && w[0] <= '9') { - number = parse_number(w); + number = parse_number(w, 0); printf_number(d, sizeof(cvtbuf) - (d - cvtbuf), number); continue; } @@ -587,8 +586,9 @@ static const char *parse_stack_el(const char *name, char *extra_reg, if (len < sizeof(buf) - 1) { strncpy(buf, s, len); buf[len] = 0; + errno = 0; val = strtol(buf, &endp, 16); - if (val == 0 || *endp != 0) { + if (val == 0 || *endp != 0 || errno != 0) { aerr("%s num parse fail for '%s'\n", __func__, buf); return NULL; } @@ -874,7 +874,7 @@ static int parse_operand(struct parsed_opr *opr, else if (('0' <= words[w][0] && words[w][0] <= '9') || words[w][0] == '-') { - number = parse_number(words[w]); + number = parse_number(words[w], 0); opr->type = OPT_CONST; opr->val = number; printf_number(opr->name, sizeof(opr->name), number); @@ -996,6 +996,7 @@ static const struct { { "adc", OP_ADC, 2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C }, { "sbb", OP_SBB, 2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C }, { "bsf", OP_BSF, 2, 2, OPF_DATA|OPF_FLAGS }, + { "bsr", OP_BSR, 2, 2, OPF_DATA|OPF_FLAGS }, { "inc", OP_INC, 1, 1, OPF_DATA|OPF_FLAGS }, { "dec", OP_DEC, 1, 1, OPF_DATA|OPF_FLAGS }, { "neg", OP_NEG, 1, 1, OPF_DATA|OPF_FLAGS }, @@ -1750,8 +1751,9 @@ static struct parsed_equ *equ_find(struct parsed_op *po, const char *name, if (namelen <= 0) ferr(po, "equ parse failed for '%s'\n", name); + errno = 0; *extra_offs = strtol(p, &endp, 16); - if (*endp != 0) + if (*endp != 0 || errno != 0) ferr(po, "equ parse failed for '%s'\n", name); } @@ -1795,10 +1797,11 @@ static void parse_stack_access(struct parsed_op *po, p = name + 4; if (IS_START(p, "0x")) p += 2; + errno = 0; offset = strtoul(p, &endp, 16); if (name[3] == '-') offset = -offset; - if (*endp != 0) + if (*endp != 0 || errno != 0) ferr(po, "ebp- parse of '%s' failed\n", name); } else { @@ -1843,8 +1846,9 @@ static int parse_stack_esp_offset(struct parsed_op *po, // just plain offset? if (!IS_START(name, "esp+")) return -1; + errno = 0; offset = strtol(name + 4, &endp, 0); - if (endp == NULL || *endp != 0) + if (endp == NULL || *endp != 0 || errno != 0) return -1; *offset_out = offset; return 0; @@ -1875,7 +1879,8 @@ static int stack_frame_access(struct parsed_op *po, int retval = -1; int sf_ofs; - if (po->flags & OPF_EBP_S) + if (g_bp_frame && (po->flags & OPF_EBP_S) + && !(po->regmask_src & mxSP)) ferr(po, "stack_frame_access while ebp is scratch\n"); parse_stack_access(po, name, ofs_reg, &offset, @@ -2689,8 +2694,9 @@ static int scan_for_rsave_pop_reg(int i, int magic, int reg, int set_flags) return -1; } - // nothing interesting on this path - return 0; + // nothing interesting on this path, + // still return ret for something recursive calls could find + return ret; } static void find_reachable_exits(int i, int opcnt, int magic, @@ -2741,7 +2747,8 @@ static int scan_for_pop_ret(int i, int opcnt, int reg, int set_flags) { static int exits[MAX_EXITS]; static int exit_count; - int j, ret; + int found = 0; + int e, j, ret; if (!set_flags) { exit_count = 0; @@ -2751,13 +2758,23 @@ static int scan_for_pop_ret(int i, int opcnt, int reg, int set_flags) } for (j = 0; j < exit_count; j++) { - ret = scan_for_rsave_pop_reg(exits[j], i + opcnt * 16 + set_flags, + e = exits[j]; + ret = scan_for_rsave_pop_reg(e, i + opcnt * 16 + set_flags, reg, set_flags); - if (ret == -1) - return -1; + if (ret != -1) { + found |= ret; + continue; + } + if (ops[e].op == OP_CALL && ops[e].pp != NULL + && ops[e].pp->is_noreturn) + { + // assume stack cleanup was skipped + continue; + } + return -1; } - return 1; + return found; } // scan for one or more pop of push @@ -3521,7 +3538,7 @@ static void scan_for_call_type(int i, const struct parsed_opr *opr, if (*pp_found != NULL && pp != NULL && *pp_found != pp) { if (!IS((*pp_found)->ret_type.name, pp->ret_type.name) || (*pp_found)->is_stdcall != pp->is_stdcall - || (*pp_found)->is_fptr != pp->is_fptr + //|| (*pp_found)->is_fptr != pp->is_fptr || (*pp_found)->argc != pp->argc || (*pp_found)->argc_reg != pp->argc_reg || (*pp_found)->argc_stack != pp->argc_stack) @@ -3672,6 +3689,8 @@ static void resolve_branches_parse_calls(int opcnt) { "__allshl", OPP_ALLSHL, OPF_DATA, mxAX|mxDX|mxCX, mxAX|mxDX }, { "__allshr", OPP_ALLSHR, OPF_DATA, mxAX|mxDX|mxCX, mxAX|mxDX }, { "__ftol", OPP_FTOL, OPF_FPOP, mxST0, mxAX | mxDX }, + // more precise? Wine gets away with just __ftol handler + { "__ftol2", OPP_FTOL, OPF_FPOP, mxST0, mxAX | mxDX }, { "__CIpow", OPP_CIPOW, OPF_FPOP, mxST0|mxST1, mxST0 }, }; const struct parsed_proto *pp_c; @@ -3679,6 +3698,7 @@ static void resolve_branches_parse_calls(int opcnt) struct parsed_data *pd; struct parsed_op *po; const char *tmpname; + enum op_op prev_op; int i, l; int ret; @@ -3742,8 +3762,10 @@ static void resolve_branches_parse_calls(int opcnt) if (pp != NULL) { if (pp->is_fptr) check_func_pp(po, pp, "fptr var call"); - if (pp->is_noreturn) + if (pp->is_noreturn) { po->flags |= OPF_TAIL; + po->flags &= ~OPF_ATAIL; // most likely... + } } po->pp = pp; continue; @@ -3788,13 +3810,19 @@ static void resolve_branches_parse_calls(int opcnt) tailcall: po->op = OP_CALL; po->flags |= OPF_TAIL; - if (i > 0 && ops[i - 1].op == OP_POP) + prev_op = i > 0 ? ops[i - 1].op : OP_UD2; + if (prev_op == OP_POP) + po->flags |= OPF_ATAIL; + if (g_stack_fsz + g_bp_frame == 0 && prev_op != OP_PUSH + && (g_func_pp == NULL || g_func_pp->argc_stack > 0)) + { po->flags |= OPF_ATAIL; + } i--; // reprocess } } -static void scan_prologue_epilogue(int opcnt) +static void scan_prologue_epilogue(int opcnt, int *stack_align) { int ecx_push = 0, esp_sub = 0, pusha = 0; int sandard_epilogue; @@ -3817,6 +3845,19 @@ static void scan_prologue_epilogue(int opcnt) i++; } + if (ops[i].op == OP_AND && ops[i].operand[0].reg == xSP + && ops[i].operand[1].type == OPT_CONST) + { + l = ops[i].operand[1].val; + j = ffs(l) - 1; + if (j == -1 || (l >> j) != -1) + ferr(&ops[i], "unhandled esp align: %x\n", l); + if (stack_align != NULL) + *stack_align = 1 << j; + ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + i++; + } + if (ops[i].op == OP_SUB && IS(opr_name(&ops[i], 0), "esp")) { g_stack_fsz = opr_const(&ops[i], 1); ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; @@ -4017,13 +4058,22 @@ static void scan_prologue_epilogue(int opcnt) l += ops[j].operand[1].val / 4 - 1; } else - ferr(&ops[j], "'pop ecx' expected\n"); + break; ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; j--; } - if (l != ecx_push) + if (l != ecx_push) { + if (i < opcnt && ops[i].op == OP_CALL + && ops[i].pp != NULL && ops[i].pp->is_noreturn) + { + // noreturn tailcall with no epilogue + i++; + found = 1; + continue; + } ferr(&ops[j], "epilogue scan failed\n"); + } found = 1; } @@ -4031,21 +4081,25 @@ static void scan_prologue_epilogue(int opcnt) if (esp_sub) { if (ops[j].op != OP_ADD || !IS(opr_name(&ops[j], 0), "esp") - || ops[j].operand[1].type != OPT_CONST - || ops[j].operand[1].val != g_stack_fsz) + || ops[j].operand[1].type != OPT_CONST) { - if (ops[i].op == OP_CALL && ops[i].pp != NULL - && ops[i].pp->is_noreturn) + if (i < opcnt && ops[i].op == OP_CALL + && ops[i].pp != NULL && ops[i].pp->is_noreturn) { // noreturn tailcall with no epilogue i++; + found = 1; continue; } ferr(&ops[j], "'add esp' expected\n"); } - ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; - ops[j].operand[1].val = 0; // hack for stack arg scanner + if (ops[j].operand[1].val < g_stack_fsz) + ferr(&ops[j], "esp adj is too low (need %d)\n", g_stack_fsz); + + ops[j].operand[1].val -= g_stack_fsz; // for stack arg scanner + if (ops[j].operand[1].val == 0) + ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; found = 1; } @@ -4637,6 +4691,9 @@ static struct parsed_proto *process_call(int i, int opcnt) ferr(po, "too many args for '%s'\n", tmpname); } if (pp->argc_stack > adj / 4) { + if (pp->is_noreturn) + // assume no stack adjust was emited + goto out; fnote(po, "(this call)\n"); ferr(&ops[ret], "stack tracking failed for '%s': %x %x\n", tmpname, pp->argc_stack * 4, adj); @@ -4649,6 +4706,7 @@ static struct parsed_proto *process_call(int i, int opcnt) ferr(po, "missing esp_adjust for vararg func '%s'\n", pp->name); +out: return pp; } @@ -4679,8 +4737,10 @@ static int collect_call_args_no_push(int i, struct parsed_proto *pp, ret = parse_stack_esp_offset(po, po->operand[0].name, &offset); if (ret != 0) continue; - if (offset < 0 || offset >= pp->argc_stack * 4 || (offset & 3)) - ferr(po, "bad offset %d (%d args)\n", offset, pp->argc_stack); + if (offset < 0 || offset >= pp->argc_stack * 4 || (offset & 3)) { + //ferr(po, "offset %d, %d args\n", offset, pp->argc_stack); + continue; + } arg = base_arg + offset / 4; po->p_argnext = -1; @@ -4899,8 +4959,8 @@ static int collect_call_args_r(struct parsed_op *po, int i, pp_tmp = ops[j].pp; if (pp_tmp == NULL) - ferr(po, "arg collect hit unparsed call '%s'\n", - ops[j].operand[0].name); + ferr(po, "arg collect %d/%d hit unparsed call '%s'\n", + arg, pp->argc, ops[j].operand[0].name); 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); @@ -5312,7 +5372,7 @@ static void pp_insert_reg_arg(struct parsed_proto *pp, const char *reg) pp->argc_reg++; } -static void output_std_flags(FILE *fout, struct parsed_op *po, +static void output_std_flag_z(FILE *fout, struct parsed_op *po, int *pfomask, const char *dst_opr_text) { if (*pfomask & (1 << PFO_Z)) { @@ -5320,6 +5380,11 @@ static void output_std_flags(FILE *fout, struct parsed_op *po, lmod_cast_u(po, po->operand[0].lmod), dst_opr_text); *pfomask &= ~(1 << PFO_Z); } +} + +static void output_std_flag_s(FILE *fout, struct parsed_op *po, + int *pfomask, const char *dst_opr_text) +{ if (*pfomask & (1 << PFO_S)) { fprintf(fout, "\n cond_s = (%s%s < 0);", lmod_cast_s(po, po->operand[0].lmod), dst_opr_text); @@ -5327,6 +5392,13 @@ static void output_std_flags(FILE *fout, struct parsed_op *po, } } +static void output_std_flags(FILE *fout, struct parsed_op *po, + int *pfomask, const char *dst_opr_text) +{ + output_std_flag_z(fout, po, pfomask, dst_opr_text); + output_std_flag_s(fout, po, pfomask, dst_opr_text); +} + enum { OPP_FORCE_NORETURN = (1 << 0), OPP_SIMPLE_ARGS = (1 << 1), @@ -5426,6 +5498,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) int had_decl = 0; int label_pending = 0; int need_double = 0; + int stack_align = 0; int regmask_save = 0; // used regs saved/restored in this func int regmask_arg; // regs from this function args (fastcall, etc) int regmask_ret; // regs needed on ret @@ -5462,7 +5535,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) // pass2: // - handle ebp/esp frame, remove ops related to it - scan_prologue_epilogue(opcnt); + scan_prologue_epilogue(opcnt, &stack_align); // pass3: // - remove dead labels @@ -5912,6 +5985,10 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) fprintf(fout, " u8 b[%d];", g_stack_fsz); if (g_func_lmods & (1 << OPLM_QWORD)) fprintf(fout, " double q[%d];", (g_stack_fsz + 7) / 8); + if (stack_align > 8) + ferr(ops, "unhandled stack align of %d\n", stack_align); + else if (stack_align == 8) + fprintf(fout, " u64 align;"); fprintf(fout, " } sf;\n"); had_decl = 1; } @@ -6652,15 +6729,20 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) break; case OP_BSF: + case OP_BSR: + // on SKL, if src is 0, dst is left unchanged assert_operand_cnt(2); + out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]); out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]); - fprintf(fout, " %s = %s ? __builtin_ffs(%s) - 1 : 0;", - out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]), - buf2, buf2); - output_std_flags(fout, po, &pfomask, buf1); + output_std_flag_z(fout, po, &pfomask, buf2); + if (po->op == OP_BSF) + snprintf(buf3, sizeof(buf3), "__builtin_ffs(%s) - 1", buf2); + else + snprintf(buf3, sizeof(buf3), "31 - __builtin_clz(%s)", buf2); + fprintf(fout, " if (%s) %s = %s;", buf2, buf1, buf3); last_arith_dst = &po->operand[0]; delayed_flag_op = NULL; - strcat(g_comment, " bsf"); + strcat(g_comment, po->op == OP_BSF ? " bsf" : " bsr"); break; case OP_DEC: @@ -6915,10 +6997,13 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) pp->has_structarg ? "_sa" : ""); if (po->flags & OPF_ATAIL) { - if (pp->argc_stack != g_func_pp->argc_stack - || (pp->argc_stack > 0 - && pp->is_stdcall != g_func_pp->is_stdcall)) - ferr(po, "incompatible tailcall\n"); + int check_compat = + g_func_pp->is_stdcall && g_func_pp->argc_stack > 0; + check_compat |= pp->argc_stack > 0; + if (check_compat + && (pp->argc_stack != g_func_pp->argc_stack + || pp->is_stdcall != g_func_pp->is_stdcall)) + ferr(po, "incompatible arg-reuse tailcall\n"); if (g_func_pp->has_retreg) ferr(po, "TODO: retreg+tailcall\n"); @@ -7845,7 +7930,7 @@ static void gen_hdr(const char *funcn, int opcnt) // pass2: // - handle ebp/esp frame, remove ops related to it - scan_prologue_epilogue(opcnt); + scan_prologue_epilogue(opcnt, NULL); // pass3: // - remove dead labels @@ -8991,7 +9076,7 @@ do_pending_endp: if (pd->type == OPT_OFFSET) pd->d[pd->count].u.label = strdup(words[i]); else - pd->d[pd->count].u.val = parse_number(words[i]); + pd->d[pd->count].u.val = parse_number(words[i], 0); pd->d[pd->count].bt_i = -1; pd->count++; } @@ -9170,7 +9255,7 @@ do_pending_endp: else aerr("bad lmod: '%s'\n", words[2]); - g_eqs[g_eqcnt].offset = parse_number(words[4]); + g_eqs[g_eqcnt].offset = parse_number(words[4], 0); g_eqcnt++; continue; }