X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=tools%2Ftranslate.c;h=81c555be627a981d2bb4aa4d5bdcdb948250a96f;hb=266771390e5ccfaa1f8153de5b878f1c7226b2a0;hp=c464758483246ff44704e4202a5be489bea2c0ae;hpb=9af2d373a75efbac33d111d4a820cbcb300e0efd;p=ia32rtools.git diff --git a/tools/translate.c b/tools/translate.c index c464758..81c555b 100644 --- a/tools/translate.c +++ b/tools/translate.c @@ -56,6 +56,7 @@ enum op_flags { OPF_32BIT = (1 << 16), /* 32bit division */ 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 */ }; enum op_op { @@ -247,6 +248,11 @@ static int g_header_mode; printf("%s:%d: note: [%s] '%s': " fmt, asmfn, (op_)->asmln, g_func, \ dump_op(op_), ##__VA_ARGS__) +#define ferr_assert(op_, cond) do { \ + if (!(cond)) ferr(op_, "assertion '%s' failed on ln :%d\n", #cond, \ + __LINE__); \ +} while (0) + const char *regs_r32[] = { "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp", // not r32, but list here for easy parsing and printing @@ -2543,6 +2549,8 @@ static int scan_for_esp_adjust(int i, int opcnt, *adj -= lmod_bytes(po, po->operand[0].lmod); } else if (po->op == OP_POP && !(po->flags & OPF_RMD)) { + if (po->datap != NULL) // in push/pop pair? + break; // seems like msvc only uses 'pop ecx' for stack realignment.. if (po->operand[0].type != OPT_REG || po->operand[0].reg != xCX) break; @@ -3067,13 +3075,45 @@ static int try_resolve_const(int i, const struct parsed_opr *opr, return -1; } +static struct parsed_proto *process_call_early(int i, int opcnt, + int *adj_i) +{ + struct parsed_op *po = &ops[i]; + struct parsed_proto *pp; + int multipath = 0; + int adj = 0; + int ret; + + pp = po->pp; + if (pp == NULL || pp->is_vararg || pp->argc_reg != 0) + // leave for later + return NULL; + + // look for and make use of esp adjust + *adj_i = ret = -1; + if (!pp->is_stdcall && pp->argc_stack > 0) + ret = scan_for_esp_adjust(i + 1, opcnt, + pp->argc_stack * 4, &adj, &multipath); + if (ret >= 0) { + if (pp->argc_stack > adj / 4) + return NULL; + if (multipath) + return NULL; + if (ops[ret].op == OP_POP && adj != 4) + return NULL; + } + + *adj_i = ret; + return pp; +} + static struct parsed_proto *process_call(int i, int opcnt) { struct parsed_op *po = &ops[i]; const struct parsed_proto *pp_c; struct parsed_proto *pp; const char *tmpname; - int j = 0, l = 0; + int adj = 0, multipath = 0; int ret, arg; tmpname = opr_name(po, 0); @@ -3081,13 +3121,13 @@ static struct parsed_proto *process_call(int i, int opcnt) if (pp == NULL) { // indirect call - pp_c = resolve_icall(i, opcnt, &l); + pp_c = resolve_icall(i, opcnt, &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); pp = proto_clone(pp_c); my_assert_not(pp, NULL); - if (l) + if (multipath) // not resolved just to single func pp->is_fptr = 1; @@ -3108,18 +3148,18 @@ static struct parsed_proto *process_call(int i, int opcnt) my_assert_not(pp, NULL); pp->is_fptr = 1; - ret = scan_for_esp_adjust(i + 1, opcnt, ~0, &j, &l); - if (ret < 0 || j < 0) { + ret = scan_for_esp_adjust(i + 1, opcnt, ~0, &adj, &multipath); + if (ret < 0 || adj < 0) { if (!g_allow_regfunc) ferr(po, "non-__cdecl indirect call unhandled yet\n"); pp->is_unresolved = 1; - j = 0; + adj = 0; } - j /= 4; - if (j > ARRAY_SIZE(pp->arg)) - ferr(po, "esp adjust too large: %d\n", j); + adj /= 4; + if (adj > ARRAY_SIZE(pp->arg)) + ferr(po, "esp adjust too large: %d\n", adj); pp->ret_type.name = strdup("int"); - pp->argc = pp->argc_stack = j; + pp->argc = pp->argc_stack = adj; for (arg = 0; arg < pp->argc; arg++) pp->arg[arg].type.name = strdup("int"); } @@ -3130,15 +3170,17 @@ static struct parsed_proto *process_call(int i, int opcnt) ret = -1; if (!pp->is_stdcall && pp->argc_stack > 0) ret = scan_for_esp_adjust(i + 1, opcnt, - pp->argc_stack * 4, &j, &l); + pp->argc_stack * 4, &adj, &multipath); if (ret >= 0) { if (pp->is_vararg) { - if (j / 4 < pp->argc_stack) - ferr(po, "esp adjust is too small: %x < %x\n", - j, pp->argc_stack * 4); + if (adj / 4 < pp->argc_stack) { + fnote(po, "(this call)\n"); + ferr(&ops[ret], "esp adjust is too small: %x < %x\n", + adj, pp->argc_stack * 4); + } // modify pp to make it have varargs as normal args arg = pp->argc; - pp->argc += j / 4 - pp->argc_stack; + pp->argc += adj / 4 - pp->argc_stack; for (; arg < pp->argc; arg++) { pp->arg[arg].type.name = strdup("int"); pp->argc_stack++; @@ -3146,28 +3188,28 @@ static struct parsed_proto *process_call(int i, int opcnt) if (pp->argc > ARRAY_SIZE(pp->arg)) ferr(po, "too many args for '%s'\n", tmpname); } - if (pp->argc_stack > j / 4) { + if (pp->argc_stack > adj / 4) { fnote(po, "(this call)\n"); ferr(&ops[ret], "stack tracking failed for '%s': %x %x\n", - tmpname, pp->argc_stack * 4, j); + tmpname, pp->argc_stack * 4, adj); } ops[ret].flags |= OPF_RMD; if (ops[ret].op == OP_POP) { - if (j > 4) { + if (adj > 4) { // deal with multi-pop stack adjust - j = pp->argc_stack; - while (ops[ret].op == OP_POP && j > 0 && ret < opcnt) { + adj = pp->argc_stack; + while (ops[ret].op == OP_POP && adj > 0 && ret < opcnt) { ops[ret].flags |= OPF_RMD; - j--; + adj--; ret++; } } } - else if (!l) { + else if (!multipath) { // a bit of a hack, but deals with use of // single adj for multiple calls - ops[ret].operand[1].val -= j; + ops[ret].operand[1].val -= pp->argc_stack * 4; } } else if (pp->is_vararg) @@ -3177,6 +3219,82 @@ static struct parsed_proto *process_call(int i, int opcnt) return pp; } +static int collect_call_args_early(struct parsed_op *po, int i, + struct parsed_proto *pp, int *regmask) +{ + int arg, ret; + int j; + + for (arg = 0; arg < pp->argc; arg++) + if (pp->arg[arg].reg == NULL) + break; + + // first see if it can be easily done + for (j = i; j > 0 && arg < pp->argc; ) + { + if (g_labels[j] != NULL) + return -1; + j--; + + if (ops[j].op == OP_CALL) + return -1; + else if (ops[j].op == OP_ADD && ops[j].operand[0].reg == xSP) + return -1; + else if (ops[j].op == OP_POP) + return -1; + else if (ops[j].flags & OPF_CJMP) + return -1; + else if (ops[j].op == OP_PUSH) { + if (ops[j].flags & (OPF_FARG|OPF_FARGNR)) + return -1; + ret = scan_for_mod(&ops[j], j + 1, i, 1); + if (ret >= 0) + return -1; + + if (pp->arg[arg].type.is_va_list) + return -1; + + // next arg + for (arg++; arg < pp->argc; arg++) + if (pp->arg[arg].reg == NULL) + break; + } + } + + if (arg < pp->argc) + return -1; + + // now do it + for (arg = 0; arg < pp->argc; arg++) + if (pp->arg[arg].reg == NULL) + break; + + for (j = i; j > 0 && arg < pp->argc; ) + { + j--; + + if (ops[j].op == OP_PUSH) + { + ops[j].p_argnext = -1; + ferr_assert(&ops[j], pp->arg[arg].datap == NULL); + pp->arg[arg].datap = &ops[j]; + + if (ops[j].operand[0].type == OPT_REG) + *regmask |= 1 << ops[j].operand[0].reg; + + ops[j].flags |= OPF_RMD | OPF_FARGNR | OPF_FARG; + ops[j].flags &= ~OPF_RSAVE; + + // next arg + for (arg++; arg < pp->argc; arg++) + if (pp->arg[arg].reg == NULL) + break; + } + } + + return 0; +} + static int collect_call_args_r(struct parsed_op *po, int i, struct parsed_proto *pp, int *regmask, int *save_arg_vars, int *arg_grp, int arg, int magic, int need_op_saving, int may_reuse) @@ -3742,7 +3860,7 @@ tailcall: // pass3: // - remove dead labels - // - process calls + // - process trivial calls for (i = 0; i < opcnt; i++) { if (g_labels[i] != NULL && g_label_refs[i].i == -1) { @@ -3755,6 +3873,42 @@ tailcall: continue; if (po->op == OP_CALL) + { + pp = process_call_early(i, opcnt, &j); + if (pp != NULL) { + if (!(po->flags & OPF_ATAIL)) + // since we know the args, try to collect them + if (collect_call_args_early(po, i, pp, ®mask) != 0) + pp = NULL; + } + + if (pp != NULL) { + if (j >= 0) { + // commit esp adjust + ops[j].flags |= OPF_RMD; + if (ops[j].op != OP_POP) { + ferr_assert(&ops[j], ops[j].op == OP_ADD); + ops[j].operand[1].val -= pp->argc_stack * 4; + } + } + + if (strstr(pp->ret_type.name, "int64")) + need_tmp64 = 1; + + po->flags |= OPF_DONE; + } + } + } + + // pass4: + // - process calls + for (i = 0; i < opcnt; i++) + { + po = &ops[i]; + if (po->flags & OPF_RMD) + continue; + + if (po->op == OP_CALL && !(po->flags & OPF_DONE)) { pp = process_call(i, opcnt); @@ -3769,14 +3923,15 @@ tailcall: } } - // pass4: + // pass5: // - find POPs for PUSHes, rm both // - scan for STD/CLD, propagate DF // - scan for all used registers // - find flag set ops for their users // - do unreselved calls // - declare indirect functions - for (i = 0; i < opcnt; i++) { + for (i = 0; i < opcnt; i++) + { po = &ops[i]; if (po->flags & OPF_RMD) continue; @@ -3915,6 +4070,7 @@ tailcall: need_tmp64 = 1; } else if (po->op == OP_CALL) { + // note: resolved non-reg calls are OPF_DONE already pp = po->pp; if (pp == NULL) ferr(po, "NULL pp\n"); @@ -4015,7 +4171,7 @@ tailcall: } } - // pass4: + // pass6: // - confirm regmask_save, it might have been reduced if (regmask_save != 0) { @@ -5328,6 +5484,13 @@ struct func_proto_dep { static struct func_prototype *hg_fp; static int hg_fp_cnt; +static struct scanned_var { + char name[NAMELEN]; + enum opr_lenmod lmod; + unsigned int is_seeded:1; +} *hg_vars; +static int hg_var_cnt; + static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, int count); @@ -5376,6 +5539,7 @@ static int hg_fp_cmp_id(const void *p1_, const void *p2_) static void gen_hdr(const char *funcn, int opcnt) { + int save_arg_vars[MAX_ARG_GRP] = { 0, }; const struct parsed_proto *pp_c; struct parsed_proto *pp; struct func_prototype *fp; @@ -5383,6 +5547,7 @@ static void gen_hdr(const char *funcn, int opcnt) struct parsed_data *pd; struct parsed_op *po; const char *tmpname; + int regmask_dummy = 0; int regmask_save = 0; int regmask_dst = 0; int regmask_dep = 0; @@ -5501,7 +5666,7 @@ tailcall: // pass3: // - remove dead labels - // - process calls + // - process trivial calls // - handle push /pop pairs for (i = 0; i < opcnt; i++) { @@ -5514,22 +5679,55 @@ tailcall: if (po->flags & OPF_RMD) continue; - if (po->op == OP_CALL) { + if (po->op == OP_CALL) + { + pp = process_call_early(i, opcnt, &j); + if (pp != NULL) { + if (!(po->flags & OPF_ATAIL)) + // since we know the args, try to collect them + if (collect_call_args_early(po, i, pp, ®mask_dummy) != 0) + pp = NULL; + } + + if (pp != NULL) { + if (j >= 0) { + // commit esp adjust + ops[j].flags |= OPF_RMD; + if (ops[j].op != OP_POP) { + ferr_assert(&ops[j], ops[j].op == OP_ADD); + ops[j].operand[1].val -= pp->argc_stack * 4; + } + } + + po->flags |= OPF_DONE; + } + } + else if (po->op == OP_PUSH && po->operand[0].type == OPT_CONST) { + scan_for_pop_const(i, opcnt); + } + } + + // pass4: + // - process calls + for (i = 0; i < opcnt; i++) + { + po = &ops[i]; + if (po->flags & OPF_RMD) + continue; + + if (po->op == OP_CALL && !(po->flags & OPF_DONE)) + { pp = process_call(i, opcnt); if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) { - int regmask_dummy = 0, save_arg_vars[MAX_ARG_GRP] = { 0, }; // since we know the args, collect them collect_call_args(po, i, pp, ®mask_dummy, save_arg_vars, i + opcnt * 2); } } - else if (po->op == OP_PUSH && po->operand[0].type == OPT_CONST) { - scan_for_pop_const(i, opcnt); - } } - // pass4: + // pass5: // - track saved regs // - try to figure out arg-regs for (i = 0; i < opcnt; i++) @@ -5643,6 +5841,7 @@ tailcall: fp->regmask_dep = regmask_dep & ~(1 << xSP); fp->has_ret = has_ret; + // output_hdr_fp(stdout, fp, 1); gen_x_cleanup(opcnt); } @@ -5755,6 +5954,14 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, static void output_hdr(FILE *fout) { + static const char *lmod_c_names[] = { + [OPLM_UNSPEC] = "???", + [OPLM_BYTE] = "uint8_t", + [OPLM_WORD] = "uint16_t", + [OPLM_DWORD] = "uint32_t", + [OPLM_QWORD] = "uint64_t", + }; + const struct scanned_var *var; int i; // resolve deps @@ -5765,25 +5972,46 @@ static void output_hdr(FILE *fout) // note: messes up .proto ptr, don't use //qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_id); + // output variables + for (i = 0; i < hg_var_cnt; i++) { + var = &hg_vars[i]; + + fprintf(fout, "extern %-8s %s;", + lmod_c_names[var->lmod], var->name); + + if (var->is_seeded) + fprintf(fout, " // seeded"); + fprintf(fout, "\n"); + } + + fprintf(fout, "\n"); + + // output function prototypes output_hdr_fp(fout, hg_fp, hg_fp_cnt); } -static void set_label(int i, const char *name) +// read a line, truncating it if it doesn't fit +static char *my_fgets(char *s, size_t size, FILE *stream) { - const char *p; - int len; + char *ret, *ret2; + char buf[64]; + int p; - len = strlen(name); - p = strchr(name, ':'); - if (p != NULL) - len = p - name; + p = size - 2; + if (p >= 0) + s[p] = 0; - if (g_labels[i] != NULL && !IS_START(g_labels[i], "algn_")) - aerr("dupe label '%s' vs '%s'?\n", name, g_labels[i]); - g_labels[i] = realloc(g_labels[i], len + 1); - my_assert_not(g_labels[i], NULL); - memcpy(g_labels[i], name, len); - g_labels[i][len] = 0; + ret = fgets(s, size, stream); + if (ret != NULL && p >= 0 && s[p] != 0 && s[p] != '\n') { + p = sizeof(buf) - 2; + do { + buf[p] = 0; + ret2 = fgets(buf, sizeof(buf), stream); + } + while (ret2 != NULL && buf[p] != 0 && buf[p] != '\n'); + } + + return ret; } // '=' needs special treatment.. @@ -5806,6 +6034,131 @@ static char *next_word_s(char *w, size_t wsize, char *s) return s + i; } +static void scan_variables(FILE *fasm) +{ + const struct parsed_proto *pp_c; + struct scanned_var *var; + char line[256] = { 0, }; + char words[2][256]; + char *p = NULL; + int wordc; + + while (!feof(fasm)) + { + // skip to next data section + while (my_fgets(line, sizeof(line), fasm)) + { + asmln++; + + p = sskip(line); + if (*p == 0 || *p == ';') + continue; + + p = sskip(next_word_s(words[0], sizeof(words[0]), p)); + if (*p == 0 || *p == ';') + continue; + + if (*p != 's' || !IS_START(p, "segment para public")) + continue; + + break; + } + + if (p == NULL || !IS_START(p, "segment para public")) + break; + p = sskip(p + 19); + + if (!IS_START(p, "'DATA'")) + continue; + + // now process it + while (my_fgets(line, sizeof(line), fasm)) + { + asmln++; + + p = line; + if (my_isblank(*p)) + continue; + + p = sskip(p); + if (*p == 0 || *p == ';') + continue; + + for (wordc = 0; wordc < ARRAY_SIZE(words); wordc++) { + words[wordc][0] = 0; + p = sskip(next_word_s(words[wordc], sizeof(words[0]), p)); + if (*p == 0 || *p == ';') { + wordc++; + break; + } + } + + if (wordc == 2 && IS(words[1], "ends")) + break; + + if ((hg_var_cnt & 0xff) == 0) { + hg_vars = realloc(hg_vars, sizeof(hg_vars[0]) + * (hg_var_cnt + 0x100)); + my_assert_not(hg_vars, NULL); + memset(hg_vars + hg_var_cnt, 0, sizeof(hg_vars[0]) * 0x100); + } + + var = &hg_vars[hg_var_cnt++]; + snprintf(var->name, sizeof(var->name), "%s", words[0]); + + // maybe already in seed header? + pp_c = proto_parse(g_fhdr, var->name, 1); + if (pp_c != NULL) { + if (pp_c->is_func) + aerr("func?\n"); + else if (pp_c->is_fptr) { + var->lmod = OPLM_DWORD; + //var->is_ptr = 1; + } + else if (!guess_lmod_from_c_type(&var->lmod, &pp_c->type)) + aerr("unhandled C type '%s' for '%s'\n", + pp_c->type.name, var->name); + + var->is_seeded = 1; + continue; + } + + if (IS(words[1], "dd")) + var->lmod = OPLM_DWORD; + else if (IS(words[1], "dw")) + var->lmod = OPLM_WORD; + else if (IS(words[1], "db")) + var->lmod = OPLM_BYTE; + else if (IS(words[1], "dq")) + var->lmod = OPLM_QWORD; + //else if (IS(words[1], "dt")) + else + aerr("type '%s' not known\n", words[1]); + } + } + + rewind(fasm); + asmln = 0; +} + +static void set_label(int i, const char *name) +{ + const char *p; + int len; + + len = strlen(name); + p = strchr(name, ':'); + if (p != NULL) + len = p - name; + + if (g_labels[i] != NULL && !IS_START(g_labels[i], "algn_")) + aerr("dupe label '%s' vs '%s'?\n", name, g_labels[i]); + g_labels[i] = realloc(g_labels[i], len + 1); + my_assert_not(g_labels[i], NULL); + memcpy(g_labels[i], name, len); + g_labels[i][len] = 0; +} + struct chunk_item { char *name; long fptr; @@ -5854,7 +6207,7 @@ static void scan_ahead(FILE *fasm) oldpos = ftell(fasm); oldasmln = asmln; - while (fgets(line, sizeof(line), fasm)) + while (my_fgets(line, sizeof(line), fasm)) { wordc = 0; asmln++; @@ -5982,7 +6335,7 @@ int main(int argc, char *argv[]) frlist = fopen(argv[arg], "r"); my_assert_not(frlist, NULL); - while (fgets(line, sizeof(line), frlist)) { + while (my_fgets(line, sizeof(line), frlist)) { p = sskip(line); if (*p == 0 || *p == ';') continue; @@ -6031,7 +6384,10 @@ int main(int argc, char *argv[]) g_label_refs[i].next = NULL; } - while (fgets(line, sizeof(line), fasm)) + if (g_header_mode) + scan_variables(fasm); + + while (my_fgets(line, sizeof(line), fasm)) { wordc = 0; asmln++; @@ -6348,7 +6704,7 @@ do_pending_endp: } // scan for next text segment - while (fgets(line, sizeof(line), fasm)) { + while (my_fgets(line, sizeof(line), fasm)) { asmln++; p = sskip(line); if (*p == 0 || *p == ';')