X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=tools%2Ftranslate.c;h=95e8802525e05c1a0505695faa6c4a5e01f47973;hb=16057ce1aaef8f239faf0209ef7af813d949713c;hp=a55e6d174d6e723dcede52b3539bf1ad80d67864;hpb=4c20744d669982153209773e206c7a1f4ff28689;p=ia32rtools.git diff --git a/tools/translate.c b/tools/translate.c index a55e6d1..95e8802 100644 --- a/tools/translate.c +++ b/tools/translate.c @@ -70,6 +70,8 @@ enum op_op { OP_NOP, OP_PUSH, OP_POP, + OP_PUSHA, + OP_POPA, OP_LEAVE, OP_MOV, OP_LEA, @@ -79,6 +81,7 @@ enum op_op { OP_NOT, OP_XLAT, OP_CDQ, + OP_BSWAP, OP_LODS, OP_STOS, OP_MOVS, @@ -124,6 +127,7 @@ enum op_op { OP_FILD, OP_FLDc, OP_FST, + OP_FIST, OP_FADD, OP_FDIV, OP_FMUL, @@ -136,9 +140,21 @@ enum op_op { OP_FISUB, OP_FIDIVR, OP_FISUBR, + OP_FCOM, + OP_FNSTSW, + OP_FCHS, + OP_FCOS, + OP_FPATAN, + OP_FPTAN, + OP_FSIN, + OP_FSQRT, + OP_FXCH, + OP_FYL2X, // mmx OP_EMMS, // pseudo-ops for lib calls + OPP_ALLSHL, + OPP_ALLSHR, OPP_FTOL, // undefined OP_UD2, @@ -213,6 +229,7 @@ struct parsed_op { // (OPF_CC) - points to one of (OPF_FLAGS) that affects cc op // OP_PUSH - points to OP_POP in complex push/pop graph // OP_POP - points to OP_PUSH in simple push/pop pair +// OP_FCOM - needed_status_word_bits | (is_z_check << 16) struct parsed_equ { char name[64]; @@ -249,6 +266,11 @@ enum ida_func_attr { IDAFA_FPD = (1 << 5), }; +enum sct_func_attr { + SCTFA_CLEAR_SF = (1 << 0), // clear stack frame + SCTFA_CLEAR_REGS = (1 << 1), // clear registers (mask) +}; + enum x87_const { X87_CONST_1 = 1, X87_CONST_2T, @@ -279,6 +301,10 @@ 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_sct_func_attr; +static int g_stack_clear_start; // in dwords +static int g_stack_clear_len; +static int g_regmask_init; static int g_skip_func; static int g_allow_regfunc; static int g_quiet_pp; @@ -319,9 +345,13 @@ enum x86_regs { }; #define mxAX (1 << xAX) +#define mxCX (1 << xCX) #define mxDX (1 << xDX) #define mxST0 (1 << xST0) #define mxST1 (1 << xST1) +#define mxST1_0 (mxST1 | mxST0) +#define mxST7_2 (0xfc << xST0) +#define mxSTa (0xff << xST0) // possible basic comparison types (without inversion) enum parsed_flag_op { @@ -894,6 +924,8 @@ static const struct { { "nop", OP_NOP, 0, 0, 0 }, { "push", OP_PUSH, 1, 1, 0 }, { "pop", OP_POP, 1, 1, OPF_DATA }, + { "pusha",OP_PUSHA, 0, 0, 0 }, + { "popa", OP_POPA, 0, 0, OPF_DATA }, { "leave",OP_LEAVE, 0, 0, OPF_DATA }, { "mov" , OP_MOV, 2, 2, OPF_DATA }, { "lea", OP_LEA, 2, 2, OPF_DATA }, @@ -903,6 +935,7 @@ static const struct { { "not", OP_NOT, 1, 1, OPF_DATA }, { "xlat", OP_XLAT, 0, 0, OPF_DATA }, { "cdq", OP_CDQ, 0, 0, OPF_DATA }, + { "bswap",OP_BSWAP, 1, 1, OPF_DATA }, { "lodsb",OP_LODS, 0, 0, OPF_DATA }, { "lodsw",OP_LODS, 0, 0, OPF_DATA }, { "lodsd",OP_LODS, 0, 0, OPF_DATA }, @@ -1014,9 +1047,12 @@ static const struct { { "fld", OP_FLD, 1, 1, OPF_FPUSH }, { "fild", OP_FILD, 1, 1, OPF_FPUSH }, { "fld1", OP_FLDc, 0, 0, OPF_FPUSH }, + { "fldln2", OP_FLDc, 0, 0, OPF_FPUSH }, { "fldz", OP_FLDc, 0, 0, OPF_FPUSH }, - { "fstp", OP_FST, 1, 1, OPF_FPOP }, { "fst", OP_FST, 1, 1, 0 }, + { "fstp", OP_FST, 1, 1, OPF_FPOP }, + { "fist", OP_FIST, 1, 1, 0 }, + { "fistp", OP_FIST, 1, 1, OPF_FPOP }, { "fadd", OP_FADD, 0, 2, 0 }, { "faddp", OP_FADD, 0, 2, OPF_FPOP }, { "fdiv", OP_FDIV, 0, 2, 0 }, @@ -1035,10 +1071,23 @@ static const struct { { "fisub", OP_FISUB, 1, 1, 0 }, { "fidivr", OP_FIDIVR, 1, 1, 0 }, { "fisubr", OP_FISUBR, 1, 1, 0 }, + { "fcom", OP_FCOM, 0, 1, 0 }, + { "fcomp", OP_FCOM, 0, 1, OPF_FPOP }, + { "fnstsw", OP_FNSTSW, 1, 1, OPF_DATA }, + { "fchs", OP_FCHS, 0, 0, 0 }, + { "fcos", OP_FCOS, 0, 0, 0 }, + { "fpatan", OP_FPATAN, 0, 0, OPF_FPOP }, + { "fptan", OP_FPTAN, 0, 0, OPF_FPUSH }, + { "fsin", OP_FSIN, 0, 0, 0 }, + { "fsqrt", OP_FSQRT, 0, 0, 0 }, + { "fxch", OP_FXCH, 1, 1, 0 }, + { "fyl2x", OP_FYL2X, 0, 0, OPF_FPOP }, // mmx { "emms", OP_EMMS, 0, 0, OPF_DATA }, { "movq", OP_MOV, 2, 2, OPF_DATA }, // pseudo-ops for lib calls + { "_allshl",OPP_ALLSHL }, + { "_allshr",OPP_ALLSHR }, { "_ftol", OPP_FTOL }, // must be last { "ud2", OP_UD2 }, @@ -1137,6 +1186,7 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) case OP_INC: case OP_DEC: case OP_NEG: + case OP_BSWAP: // more below.. op->regmask_src |= op->regmask_dst; break; @@ -1325,6 +1375,8 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) op->regmask_dst |= mxST0; if (IS(words[op_w] + 3, "1")) op->operand[0].val = X87_CONST_1; + else if (IS(words[op_w] + 3, "ln2")) + op->operand[0].val = X87_CONST_LN2; else if (IS(words[op_w] + 3, "z")) op->operand[0].val = X87_CONST_Z; else @@ -1332,6 +1384,7 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) break; case OP_FST: + case OP_FIST: op->regmask_src |= mxST0; break; @@ -1362,10 +1415,29 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) case OP_FISUB: case OP_FIDIVR: case OP_FISUBR: + case OP_FCHS: + case OP_FCOS: + case OP_FSIN: + case OP_FSQRT: + case OP_FXCH: op->regmask_src |= mxST0; op->regmask_dst |= mxST0; break; + case OP_FPATAN: + case OP_FYL2X: + op->regmask_src |= mxST0 | mxST1; + op->regmask_dst |= mxST0; + break; + + case OP_FPTAN: + aerr("TODO\n"); + break; + + case OP_FCOM: + op->regmask_src |= mxST0; + break; + default: break; } @@ -2158,7 +2230,7 @@ static char *out_src_opr_u32(char *buf, size_t buf_size, } static char *out_src_opr_float(char *buf, size_t buf_size, - struct parsed_op *po, struct parsed_opr *popr) + struct parsed_op *po, struct parsed_opr *popr, int need_float_stack) { const char *cast = NULL; char tmp[256]; @@ -2168,7 +2240,15 @@ static char *out_src_opr_float(char *buf, size_t buf_size, if (popr->reg < xST0 || popr->reg > xST7) ferr(po, "bad reg: %d\n", popr->reg); - snprintf(buf, buf_size, "f_st%d", popr->reg - xST0); + if (need_float_stack) { + if (popr->reg == xST0) + snprintf(buf, buf_size, "f_st[f_stp & 7]"); + else + snprintf(buf, buf_size, "f_st[(f_stp + %d) & 7]", + popr->reg - xST0); + } + else + snprintf(buf, buf_size, "f_st%d", popr->reg - xST0); break; case OPT_REGMEM: @@ -2197,10 +2277,10 @@ static char *out_src_opr_float(char *buf, size_t buf_size, } static char *out_dst_opr_float(char *buf, size_t buf_size, - struct parsed_op *po, struct parsed_opr *popr) + struct parsed_op *po, struct parsed_opr *popr, int need_float_stack) { // same? - return out_src_opr_float(buf, buf_size, po, popr); + return out_src_opr_float(buf, buf_size, po, popr, need_float_stack); } static void out_test_for_cc(char *buf, size_t buf_size, @@ -2236,7 +2316,8 @@ static void out_test_for_cc(char *buf, size_t buf_size, } static void out_cmp_for_cc(char *buf, size_t buf_size, - struct parsed_op *po, enum parsed_flag_op pfo, int is_inv) + struct parsed_op *po, enum parsed_flag_op pfo, int is_inv, + int is_neg) { const char *cast, *scast, *cast_use; char buf1[256], buf2[256]; @@ -2270,8 +2351,13 @@ static void out_cmp_for_cc(char *buf, size_t buf_size, out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], cast_use, 0); if (po->op == OP_DEC) snprintf(buf2, sizeof(buf2), "1"); - else - out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], cast_use, 0); + else { + char cast_op2[64]; + snprintf(cast_op2, sizeof(cast_op2) - 1, "%s", cast_use); + if (is_neg) + strcat(cast_op2, "-"); + out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], cast_op2, 0); + } switch (pfo) { case PFO_C: @@ -2339,7 +2425,7 @@ static void out_cmp_test(char *buf, size_t buf_size, po->operand[0].lmod, buf3); } else if (po->op == OP_CMP) { - out_cmp_for_cc(buf, buf_size, po, pfo, is_inv); + out_cmp_for_cc(buf, buf_size, po, pfo, is_inv, 0); } else ferr(po, "%s: unhandled op: %d\n", __func__, po->op); @@ -3451,17 +3537,31 @@ static int get_pp_arg_regmask_src(const struct parsed_proto *pp) static int get_pp_arg_regmask_dst(const struct parsed_proto *pp) { + int regmask = 0; + int i, reg; + + if (pp->has_retreg) { + for (i = 0; i < pp->argc; i++) { + if (pp->arg[i].type.is_retreg) { + reg = char_array_i(regs_r32, + ARRAY_SIZE(regs_r32), pp->arg[i].reg); + ferr_assert(ops, reg >= 0); + regmask |= 1 << reg; + } + } + } + if (strstr(pp->ret_type.name, "int64")) - return (1 << xAX) | (1 << xDX); + return regmask | (1 << xAX) | (1 << xDX); if (IS(pp->ret_type.name, "float") || IS(pp->ret_type.name, "double")) { - return mxST0; + return regmask | mxST0; } if (strcasecmp(pp->ret_type.name, "void") == 0) - return 0; + return regmask; - return mxAX; + return regmask | mxAX; } static void resolve_branches_parse_calls(int opcnt) @@ -3473,7 +3573,9 @@ static void resolve_branches_parse_calls(int opcnt) unsigned int regmask_src; unsigned int regmask_dst; } pseudo_ops[] = { - { "__ftol", OPP_FTOL, OPF_FPOP, mxST0, mxAX | mxDX }, + { "__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 }, }; const struct parsed_proto *pp_c; struct parsed_proto *pp; @@ -3511,6 +3613,8 @@ static void resolve_branches_parse_calls(int opcnt) tmpname = opr_name(po, 0); if (IS_START(tmpname, "loc_")) ferr(po, "call to loc_*\n"); + if (IS(tmpname, "__alloca_probe")) + continue; // convert some calls to pseudo-ops for (l = 0; l < ARRAY_SIZE(pseudo_ops); l++) { @@ -3595,7 +3699,8 @@ tailcall: static void scan_prologue_epilogue(int opcnt) { - int ecx_push = 0, esp_sub = 0; + int ecx_push = 0, esp_sub = 0, pusha = 0; + int sandard_epilogue; int found; int i, j, l; @@ -3609,14 +3714,19 @@ static void scan_prologue_epilogue(int opcnt) ops[1].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; i = 2; - if (ops[2].op == OP_SUB && IS(opr_name(&ops[2], 0), "esp")) { - g_stack_fsz = opr_const(&ops[2], 1); - ops[2].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + if (ops[i].op == OP_PUSHA) { + ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + pusha = 1; + 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; i++; } else { // another way msvc builds stack frame.. - i = 2; while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) { g_stack_fsz += 4; ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; @@ -3650,10 +3760,20 @@ static void scan_prologue_epilogue(int opcnt) j--; } - if ((ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ebp")) - || ops[j].op == OP_LEAVE) + sandard_epilogue = 0; + if (ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ebp")) + { + ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + // the standard epilogue is sometimes even used without a sf + if (ops[j - 1].op == OP_MOV + && IS(opr_name(&ops[j - 1], 0), "esp") + && IS(opr_name(&ops[j - 1], 1), "ebp")) + sandard_epilogue = 1; + } + else if (ops[j].op == OP_LEAVE) { ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + sandard_epilogue = 1; } else if (ops[i].op == OP_CALL && ops[i].pp != NULL && ops[i].pp->is_noreturn) @@ -3666,13 +3786,10 @@ static void scan_prologue_epilogue(int opcnt) else if (!(g_ida_func_attr & IDAFA_NORETURN)) ferr(&ops[j], "'pop ebp' expected\n"); - if (g_stack_fsz != 0) { + if (g_stack_fsz != 0 || sandard_epilogue) { if (ops[j].op == OP_LEAVE) j--; - else if (ops[j].op == OP_POP - && ops[j - 1].op == OP_MOV - && IS(opr_name(&ops[j - 1], 0), "esp") - && IS(opr_name(&ops[j - 1], 1), "ebp")) + else if (sandard_epilogue) // mov esp, ebp { ops[j - 1].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; j -= 2; @@ -3689,6 +3806,13 @@ static void scan_prologue_epilogue(int opcnt) } } + if (pusha) { + if (ops[j].op == OP_POPA) + ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + else + ferr(&ops[j], "popa expected\n"); + } + found = 1; i++; } while (i < opcnt); @@ -3715,6 +3839,20 @@ static void scan_prologue_epilogue(int opcnt) { g_stack_fsz = ops[i].operand[1].val; ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + i++; + esp_sub = 1; + break; + } + else if (ops[i].op == OP_MOV && ops[i].operand[0].reg == xAX + && ops[i].operand[1].type == OPT_CONST + && ops[i + 1].op == OP_CALL + && IS(opr_name(&ops[i + 1], 0), "__alloca_probe")) + { + g_stack_fsz += ops[i].operand[1].val; + ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + i++; + ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + i++; esp_sub = 1; break; } @@ -3757,11 +3895,11 @@ static void scan_prologue_epilogue(int opcnt) { g_sp_frame = 1; - i++; do { for (; i < opcnt; i++) if (ops[i].flags & OPF_TAIL) break; + j = i - 1; if (i == opcnt && (ops[j].flags & OPF_JMP)) { if (ops[j].bt_i != -1 || ops[j].btj != NULL) @@ -3798,7 +3936,16 @@ static void scan_prologue_epilogue(int opcnt) || !IS(opr_name(&ops[j], 0), "esp") || ops[j].operand[1].type != OPT_CONST || ops[j].operand[1].val != g_stack_fsz) + { + if (ops[i].op == OP_CALL && ops[i].pp != NULL + && ops[i].pp->is_noreturn) + { + // noreturn tailcall with no epilogue + i++; + 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 @@ -3912,6 +4059,45 @@ static int resolve_last_ref(int i, const struct parsed_opr *opr, } } +// adjust datap of all reachable 'op' insns when moving back +// returns 1 if at least 1 op was found +// returns -1 if path without an op was found +static int adjust_prev_op(int i, enum op_op op, int magic, void *datap) +{ + 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 |= adjust_prev_op(lr->i, op, magic, datap); + } + 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 (ops[i].op != op) + continue; + + ops[i].datap = datap; + return 1; + } +} + // find next instruction that reads opr // *op_i must be set to -1 by the caller // on return, *op_i is set to first referencer insn @@ -3954,11 +4140,13 @@ static int find_next_read(int i, int opcnt, } if (!is_opr_read(opr, po)) { - if (is_opr_modified(opr, po) - && (po->op == OP_CALL - || ((po->flags & OPF_DATA) - && po->operand[0].lmod == OPLM_DWORD))) + int full_opr = 1; + if (opr->type == OPT_REG && po->operand[0].type == OPT_REG + && opr->reg == po->operand[0].reg && (po->flags & OPF_DATA)) { + full_opr = po->operand[0].lmod >= opr->lmod; + } + if (is_opr_modified(opr, po) && full_opr) { // it's overwritten return ret; } @@ -3977,6 +4165,65 @@ static int find_next_read(int i, int opcnt, return 0; } +// find next instruction that reads opr +// *op_i must be set to -1 by the caller +// on return, *op_i is set to first flag user insn +// returns 1 if exactly 1 flag user is found +static int find_next_flag_use(int i, int opcnt, int magic, int *op_i) +{ + struct parsed_op *po; + int j, ret = 0; + + for (; i < opcnt; i++) + { + if (ops[i].cc_scratch == magic) + return ret; + ops[i].cc_scratch = magic; + + po = &ops[i]; + if (po->op == OP_CALL) + return -1; + if (po->flags & OPF_JMP) { + 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_flag_use(po->btj->d[j].bt_i, opcnt, + magic, op_i); + } + return ret; + } + + if (po->flags & OPF_RMD) + continue; + check_i(po, po->bt_i); + if (po->flags & OPF_CJMP) + goto found; + else + i = po->bt_i - 1; + continue; + } + + if (!(po->flags & OPF_CC)) { + if (po->flags & OPF_FLAGS) + // flags changed + return ret; + if (po->flags & OPF_TAIL) + return ret; + continue; + } + +found: + 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) { @@ -3996,6 +4243,45 @@ static int try_resolve_const(int i, const struct parsed_opr *opr, return -1; } +static int resolve_used_bits(int i, int opcnt, int reg, + int *mask, int *is_z_check) +{ + struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_WORD, reg); + int j = -1, k = -1; + int ret; + + ret = find_next_read(i, opcnt, &opr, i + opcnt * 20, &j); + if (ret != 1) + return -1; + + find_next_read(j + 1, opcnt, &opr, i + opcnt * 20 + 1, &k); + if (k != -1) { + fnote(&ops[j], "(first read)\n"); + ferr(&ops[k], "TODO: bit resolve: multiple readers\n"); + } + + if (ops[j].op != OP_TEST || ops[j].operand[1].type != OPT_CONST) + ferr(&ops[j], "TODO: bit resolve: not a const test\n"); + + ferr_assert(&ops[j], ops[j].operand[0].type == OPT_REG); + ferr_assert(&ops[j], ops[j].operand[0].reg == reg); + + *mask = ops[j].operand[1].val; + if (ops[j].operand[0].lmod == OPLM_BYTE + && ops[j].operand[0].name[1] == 'h') + { + *mask <<= 8; + } + ferr_assert(&ops[j], (*mask & ~0xffff) == 0); + + *is_z_check = 0; + ret = find_next_flag_use(j + 1, opcnt, i + opcnt * 20 + 2, &k); + if (ret == 1) + *is_z_check = ops[k].pfo == PFO_Z; + + return 0; +} + static const struct parsed_proto *resolve_icall(int i, int opcnt, int *pp_i, int *multi_src) { @@ -4272,9 +4558,11 @@ static int collect_call_args_early(struct parsed_op *po, int i, 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 (!g_header_mode) { + 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; @@ -4617,7 +4905,6 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits, int *regmask_init, int regmask_arg) { struct parsed_op *po; - unsigned int mask; int already_saved; int regmask_new; int regmask_op; @@ -4717,20 +5004,15 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits, po->regmask_dst &= ~(1 << xAX); } } + + // not "full stack" mode and have something in stack + if (!(regmask_now & mxST7_2) && (regmask_now & mxST1_0)) + ferr(po, "float stack is not empty on func call\n"); } if (po->flags & OPF_NOREGS) continue; - if (po->flags & OPF_FPUSH) { - if (regmask_now & mxST1) - ferr(po, "TODO: FPUSH on active ST1\n"); - if (regmask_now & mxST0) - po->flags |= OPF_FSHIFT; - mask = mxST0 | mxST1; - regmask_now = (regmask_now & ~mask) | ((regmask_now & mxST0) << 1); - } - // if incomplete register is used, clear it on init to avoid // later use of uninitialized upper part in some situations if ((po->flags & OPF_DATA) && po->operand[0].type == OPT_REG @@ -4750,21 +5032,6 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits, if (g_bp_frame && !(po->flags & OPF_EBP_S)) regmask_new &= ~(1 << xBP); - if (po->op == OP_CALL) { - // allow fastcall calls from anywhere, calee may be also sitting - // in some fastcall table even when it's not using reg args - if (regmask_new & po->regmask_src & (1 << xCX)) { - *regmask_init |= (1 << xCX); - regmask_now |= (1 << xCX); - regmask_new &= ~(1 << xCX); - } - if (regmask_new & po->regmask_src & (1 << xDX)) { - *regmask_init |= (1 << xDX); - regmask_now |= (1 << xDX); - regmask_new &= ~(1 << xDX); - } - } - if (regmask_new != 0) fnote(po, "uninitialized reg mask: %x\n", regmask_new); @@ -4778,23 +5045,46 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits, } } + if (po->flags & OPF_FPUSH) { + if (regmask_now & mxST1) + regmask_now |= mxSTa; // switch to "full stack" mode + if (regmask_now & mxSTa) + po->flags |= OPF_FSHIFT; + if (!(regmask_now & mxST7_2)) { + regmask_now = + (regmask_now & ~mxST1_0) | ((regmask_now & mxST0) << 1); + } + } + regmask_now |= regmask_op; *regmask |= regmask_now; // released regs if (po->flags & OPF_FPOP) { - mask = mxST0 | mxST1; - if (!(regmask_now & mask)) + if ((regmask_now & mxSTa) == 0) ferr(po, "float pop on empty stack?\n"); - if (regmask_now & mxST1) + if (regmask_now & (mxST7_2 | mxST1)) po->flags |= OPF_FSHIFT; - regmask_now = (regmask_now & ~mask) | ((regmask_now & mxST1) >> 1); + if (!(regmask_now & mxST7_2)) { + regmask_now = + (regmask_now & ~mxST1_0) | ((regmask_now & mxST1) >> 1); + } } if (po->flags & OPF_TAIL) { - if (regmask_now & (mxST0 | mxST1)) - ferr(po, "float regs on tail: %x\n", regmask_now); - return; + if (!(regmask_now & mxST7_2)) { + if (get_pp_arg_regmask_dst(g_func_pp) & mxST0) { + if (!(regmask_now & mxST0)) + ferr(po, "no st0 on float return, mask: %x\n", + regmask_now); + } + else if (regmask_now & mxST1_0) + ferr(po, "float regs on tail: %x\n", regmask_now); + } + + // there is support for "conditional tailcall", sort of + if (!(po->flags & OPF_CC)) + return; } } } @@ -4919,12 +5209,18 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) unsigned int uval; int save_arg_vars[MAX_ARG_GRP] = { 0, }; unsigned char cbits[MAX_OPS / 8]; - int cond_vars = 0; + const char *float_type; + const char *float_st0; + const char *float_st1; + int need_float_stack = 0; + int need_float_sw = 0; // status word int need_tmp_var = 0; int need_tmp64 = 0; + int cond_vars = 0; int had_decl = 0; int label_pending = 0; - int regmask_save = 0; // regs saved/restored in this func + int need_double = 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 int regmask_now; // temp @@ -4933,6 +5229,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) int regmask = 0; // used regs int pfomask = 0; int found = 0; + int dead_dst; int no_output; int i, j, l; int arg; @@ -4941,6 +5238,8 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) g_bp_frame = g_sp_frame = g_stack_fsz = 0; g_stack_frame_used = 0; + if (g_sct_func_attr & SCTFA_CLEAR_REGS) + regmask_init = g_regmask_init; g_func_pp = proto_parse(fhdr, funcn, 0); if (g_func_pp == NULL) @@ -4949,17 +5248,6 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) regmask_arg = get_pp_arg_regmask_src(g_func_pp); regmask_ret = get_pp_arg_regmask_dst(g_func_pp); - if (g_func_pp->has_retreg) { - for (arg = 0; arg < g_func_pp->argc; arg++) { - if (g_func_pp->arg[arg].type.is_retreg) { - reg = char_array_i(regs_r32, - ARRAY_SIZE(regs_r32), g_func_pp->arg[arg].reg); - ferr_assert(ops, reg >= 0); - regmask_ret |= 1 << reg; - } - } - } - // pass1: // - resolve all branches // - parse calls with labels @@ -5024,8 +5312,11 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) // - process calls, stage 2 // - handle some push/pop pairs // - scan for STD/CLD, propagate DF + // - try to resolve needed x87 status word bits for (i = 0; i < opcnt; i++) { + int mask, z_check; + po = &ops[i]; if (po->flags & OPF_RMD) continue; @@ -5061,16 +5352,39 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (po->flags & OPF_DONE) continue; - 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, i + opcnt * 12); - } - else if (po->op == OP_POP) + switch (po->op) { + case OP_PUSH: + if (!(po->flags & OPF_FARG) && !(po->flags & OPF_RSAVE) + && po->operand[0].type == OPT_CONST) + { + scan_for_pop_const(i, opcnt, i + opcnt * 12); + } + break; + + case OP_POP: scan_pushes_for_pop(i, opcnt, ®mask_pp); - else if (po->op == OP_STD) { + break; + + case OP_STD: po->flags |= OPF_DF | OPF_RMD | OPF_DONE; scan_propagate_df(i + 1, opcnt); + break; + + case OP_FNSTSW: + need_float_sw = 1; + if (po->operand[0].type != OPT_REG || po->operand[0].reg != xAX) + ferr(po, "TODO: fnstsw to mem\n"); + ret = resolve_used_bits(i + 1, opcnt, xAX, &mask, &z_check); + if (ret != 0) + ferr(po, "fnstsw resolve failed\n"); + ret = adjust_prev_op(i, OP_FCOM, i + opcnt * 21, + (void *)(long)(mask | (z_check << 16))); + if (ret != 1) + ferr(po, "failed to find fcom: %d\n", ret); + break; + + default: + break; } } @@ -5078,13 +5392,14 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) // - find POPs for PUSHes, rm both // - scan for all used registers memset(cbits, 0, sizeof(cbits)); - reg_use_pass(0, opcnt, cbits, 0, ®mask, + reg_use_pass(0, opcnt, cbits, regmask_init, ®mask, 0, ®mask_save, ®mask_init, regmask_arg); // pass7: // - find flag set ops for their users // - do unresolved calls // - declare indirect functions + // - other op specific processing for (i = 0; i < opcnt; i++) { po = &ops[i]; @@ -5149,16 +5464,23 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) cond_vars |= 1 << PFO_C; } - if (po->op == OP_CMPS || po->op == OP_SCAS) { + switch (po->op) { + case OP_CMPS: + case OP_SCAS: cond_vars |= 1 << PFO_Z; - } - else if (po->op == OP_MUL - || (po->op == OP_IMUL && po->operand_cnt == 1)) - { + break; + + case OP_MUL: if (po->operand[0].lmod == OPLM_DWORD) need_tmp64 = 1; - } - else if (po->op == OP_CALL) { + break; + + case OP_IMUL: + if (po->operand_cnt == 1 && po->operand[0].lmod == OPLM_DWORD) + need_tmp64 = 1; + break; + + case OP_CALL: // note: resolved non-reg calls are OPF_DONE already pp = po->pp; ferr_assert(po, pp != NULL); @@ -5205,27 +5527,31 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (pp->argc_stack > 0) pp->is_stdcall = 1; } - } - else if (po->op == OP_MOV && po->operand[0].pp != NULL - && po->operand[1].pp != NULL) - { - // = offset - if ((po->operand[1].pp->is_func || po->operand[1].pp->is_fptr) - && !IS_START(po->operand[1].name, "off_")) + break; + + case OP_MOV: + if (po->operand[0].pp != NULL && po->operand[1].pp != NULL) { - if (!po->operand[0].pp->is_fptr) - ferr(po, "%s not declared as fptr when it should be\n", - po->operand[0].name); - if (pp_cmp_func(po->operand[0].pp, po->operand[1].pp)) { - pp_print(buf1, sizeof(buf1), po->operand[0].pp); - pp_print(buf2, sizeof(buf2), po->operand[1].pp); - fnote(po, "var: %s\n", buf1); - fnote(po, "func: %s\n", buf2); - ferr(po, "^ mismatch\n"); + // = offset + if ((po->operand[1].pp->is_func || po->operand[1].pp->is_fptr) + && !IS_START(po->operand[1].name, "off_")) + { + if (!po->operand[0].pp->is_fptr) + ferr(po, "%s not declared as fptr when it should be\n", + po->operand[0].name); + if (pp_cmp_func(po->operand[0].pp, po->operand[1].pp)) { + pp_print(buf1, sizeof(buf1), po->operand[0].pp); + pp_print(buf2, sizeof(buf2), po->operand[1].pp); + fnote(po, "var: %s\n", buf1); + fnote(po, "func: %s\n", buf2); + ferr(po, "^ mismatch\n"); + } } } - } - else if (po->op == OP_DIV || po->op == OP_IDIV) { + break; + + case OP_DIV: + case OP_IDIV: if (po->operand[0].lmod == OPLM_DWORD) { // 32bit division is common, look for it if (po->op == OP_DIV) @@ -5239,21 +5565,47 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) } else need_tmp_var = 1; - } - else if (po->op == OP_CLD) + break; + + case OP_CLD: po->flags |= OPF_RMD | OPF_DONE; - else if (po->op == OPP_FTOL) { + break; + + case OP_RCL: + case OP_RCR: + case OP_XCHG: + need_tmp_var = 1; + break; + + case OP_FLD: + if (po->operand[0].lmod == OPLM_QWORD) + need_double = 1; + break; + + case OPP_ALLSHL: + case OPP_ALLSHR: + need_tmp64 = 1; + break; + + case OPP_FTOL: { struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xDX); j = -1; find_next_read(i + 1, opcnt, &opr, i + opcnt * 18, &j); if (j == -1) po->flags |= OPF_32BIT; + break; } - if (po->op == OP_RCL || po->op == OP_RCR || po->op == OP_XCHG) - need_tmp_var = 1; + default: + break; + } } + float_type = need_double ? "double" : "float"; + need_float_stack = !!(regmask & mxST7_2); + float_st0 = need_float_stack ? "f_st[f_stp & 7]" : "f_st0"; + float_st1 = need_float_stack ? "f_st[(f_stp + 1) & 7]" : "f_st1"; + // output starts here // define userstack size @@ -5410,18 +5762,30 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) } } // ... x87 - if (regmask_now & 0xff0000) { - for (reg = 16; reg < 24; reg++) { - if (regmask_now & (1 << reg)) { - fprintf(fout, " double f_st%d", reg - 16); - if (regmask_init & (1 << reg)) - fprintf(fout, " = 0"); - fprintf(fout, ";\n"); - had_decl = 1; + if (need_float_stack) { + fprintf(fout, " %s f_st[8];\n", float_type); + fprintf(fout, " int f_stp = 0;\n"); + had_decl = 1; + } + else { + if (regmask_now & 0xff0000) { + for (reg = 16; reg < 24; reg++) { + if (regmask_now & (1 << reg)) { + fprintf(fout, " %s f_st%d", float_type, reg - 16); + if (regmask_init & (1 << reg)) + fprintf(fout, " = 0"); + fprintf(fout, ";\n"); + had_decl = 1; + } } } } + if (need_float_sw) { + fprintf(fout, " u16 f_sw;\n"); + had_decl = 1; + } + if (regmask_save) { for (reg = 0; reg < 8; reg++) { if (regmask_save & (1 << reg)) { @@ -5475,6 +5839,24 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (had_decl) fprintf(fout, "\n"); + // do stack clear, if needed + if (g_sct_func_attr & SCTFA_CLEAR_SF) { + fprintf(fout, " "); + if (g_stack_clear_len != 0) { + if (g_stack_clear_len <= 4) { + for (i = 0; i < g_stack_clear_len; i++) + fprintf(fout, "sf.d[%d] = ", g_stack_clear_start + i); + fprintf(fout, "0;\n"); + } + else { + fprintf(fout, "memset(&sf[%d], 0, %d);\n", + g_stack_clear_start, g_stack_clear_len * 4); + } + } + else + fprintf(fout, "memset(&sf, 0, sizeof(sf));\n"); + } + if (g_func_pp->is_vararg) { if (g_func_pp->argc_stack == 0) ferr(ops, "vararg func without stack args?\n"); @@ -5670,6 +6052,12 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) strcpy(g_comment, "cdq"); break; + case OP_BSWAP: + assert_operand_cnt(1); + out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]); + fprintf(fout, " %s = __builtin_bswap32(%s);", buf1, buf1); + break; + case OP_LODS: if (po->flags & OPF_REP) { assert_operand_cnt(3); @@ -6010,6 +6398,12 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) delayed_flag_op = NULL; break; } + if (pfomask & (1 << PFO_LE)) { + out_cmp_for_cc(buf1, sizeof(buf1), po, PFO_LE, 0, 1); + fprintf(fout, " cond_%s = %s;\n", + parsed_flag_op_names[PFO_LE], buf1); + pfomask &= ~(1 << PFO_LE); + } goto dualop_arith; case OP_SUB: @@ -6022,7 +6416,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (j == PFO_Z || j == PFO_S) continue; - out_cmp_for_cc(buf1, sizeof(buf1), po, j, 0); + out_cmp_for_cc(buf1, sizeof(buf1), po, j, 0, 0); fprintf(fout, " cond_%s = %s;\n", parsed_flag_op_names[j], buf1); pfomask &= ~(1 << j); @@ -6072,7 +6466,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (j == PFO_Z || j == PFO_S || j == PFO_C) continue; - out_cmp_for_cc(buf1, sizeof(buf1), po, j, 0); + out_cmp_for_cc(buf1, sizeof(buf1), po, j, 0, 0); fprintf(fout, " cond_%s = %s;\n", parsed_flag_op_names[j], buf1); pfomask &= ~(1 << j); @@ -6301,7 +6695,11 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) fprintf(fout, "(u32)"); } else if (po->regmask_dst & mxST0) { - fprintf(fout, "f_st0 = "); + ferr_assert(po, po->flags & OPF_FPUSH); + if (need_float_stack) + fprintf(fout, "f_st[--f_stp & 7] = "); + else + fprintf(fout, "f_st0 = "); } } @@ -6441,7 +6839,10 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) g_func_pp->arg[arg].reg, g_func_pp->arg[arg].reg); } - if (!(regmask_ret & (1 << xAX))) { + if (regmask_ret & mxST0) { + fprintf(fout, " return %s;", float_st0); + } + else if (!(regmask_ret & mxAX)) { if (i != opcnt - 1 || label_pending) fprintf(fout, " return;"); } @@ -6520,59 +6921,120 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) no_output = 1; break; + // pseudo ops + case OPP_ALLSHL: + case OPP_ALLSHR: + fprintf(fout, " tmp64 = ((u64)edx << 32) | eax;\n"); + fprintf(fout, " tmp64 = (s64)tmp64 %s= LOBYTE(ecx);\n", + po->op == OPP_ALLSHL ? "<<" : ">>"); + fprintf(fout, " edx = tmp64 >> 32; eax = tmp64;"); + strcat(g_comment, po->op == OPP_ALLSHL + ? " allshl" : " allshr"); + break; + // x87 case OP_FLD: - if (po->flags & OPF_FSHIFT) - fprintf(fout, " f_st1 = f_st0;\n"); - if (po->operand[0].type == OPT_REG - && po->operand[0].reg == xST0) - { - strcat(g_comment, " fld st"); - break; + if (need_float_stack) { + out_src_opr_float(buf1, sizeof(buf1), + po, &po->operand[0], 1); + if (po->regmask_src & mxSTa) { + fprintf(fout, " f_st[(f_stp - 1) & 7] = %s; f_stp--;", + buf1); + } + else + fprintf(fout, " f_st[--f_stp & 7] = %s;", buf1); + } + else { + if (po->flags & OPF_FSHIFT) + fprintf(fout, " f_st1 = f_st0;"); + if (po->operand[0].type == OPT_REG + && po->operand[0].reg == xST0) + { + strcat(g_comment, " fld st"); + break; + } + fprintf(fout, " f_st0 = %s;", + out_src_opr_float(buf1, sizeof(buf1), + po, &po->operand[0], 0)); } - fprintf(fout, " f_st0 = %s;", - out_src_opr_float(buf1, sizeof(buf1), po, &po->operand[0])); strcat(g_comment, " fld"); break; case OP_FILD: - if (po->flags & OPF_FSHIFT) - fprintf(fout, " f_st1 = f_st0;\n"); - fprintf(fout, " f_st0 = (double)%s;", - out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], - lmod_cast(po, po->operand[0].lmod, 1), 0)); + out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], + lmod_cast(po, po->operand[0].lmod, 1), 0); + snprintf(buf2, sizeof(buf2), "(%s)%s", float_type, buf1); + if (need_float_stack) { + fprintf(fout, " f_st[--f_stp & 7] = %s;", buf2); + } + else { + if (po->flags & OPF_FSHIFT) + fprintf(fout, " f_st1 = f_st0;"); + fprintf(fout, " f_st0 = %s;", buf2); + } strcat(g_comment, " fild"); break; case OP_FLDc: - if (po->flags & OPF_FSHIFT) - fprintf(fout, " f_st1 = f_st0;\n"); - fprintf(fout, " f_st0 = "); + if (need_float_stack) + fprintf(fout, " f_st[--f_stp & 7] = "); + else { + if (po->flags & OPF_FSHIFT) + fprintf(fout, " f_st1 = f_st0;"); + fprintf(fout, " f_st0 = "); + } switch (po->operand[0].val) { - case X87_CONST_1: fprintf(fout, "1.0;"); break; - case X87_CONST_Z: fprintf(fout, "0.0;"); break; + case X87_CONST_1: fprintf(fout, "1.0;"); break; + case X87_CONST_LN2: fprintf(fout, "0.693147180559945;"); break; + case X87_CONST_Z: fprintf(fout, "0.0;"); break; default: ferr(po, "TODO\n"); break; } break; case OP_FST: - if ((po->flags & OPF_FPOP) && po->operand[0].type == OPT_REG - && po->operand[0].reg == xST0) - { + dead_dst = po->operand[0].type == OPT_REG + && po->operand[0].reg == xST0; + if (!dead_dst) { + fprintf(fout, " %s = %s;", + out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0], + need_float_stack), float_st0); + } + if (po->flags & OPF_FSHIFT) { + if (need_float_stack) + fprintf(fout, " f_stp++;"); + else + fprintf(fout, " f_st0 = f_st1;"); + } + if (dead_dst && !(po->flags & OPF_FSHIFT)) no_output = 1; - break; + else + strcat(g_comment, " fst"); + break; + + case OP_FIST: + fprintf(fout, " %s = %s%s;", + out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]), + lmod_cast(po, po->operand[0].lmod, 1), float_st0); + if (po->flags & OPF_FSHIFT) { + if (need_float_stack) + fprintf(fout, " f_stp++;"); + else + fprintf(fout, " f_st0 = f_st1;"); } - fprintf(fout, " %s = f_st0;", - out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0])); - if (po->flags & OPF_FSHIFT) - fprintf(fout, "\n f_st0 = f_st1;"); - strcat(g_comment, " fst"); + strcat(g_comment, " fist"); break; case OP_FADD: case OP_FDIV: case OP_FMUL: case OP_FSUB: + out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0], + need_float_stack); + out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1], + need_float_stack); + dead_dst = (po->flags & OPF_FPOP) + && po->operand[0].type == OPT_REG + && po->operand[0].reg == xST0; switch (po->op) { case OP_FADD: j = '+'; break; case OP_FDIV: j = '/'; break; @@ -6580,27 +7042,55 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) case OP_FSUB: j = '-'; break; default: j = 'x'; break; } - if (po->flags & OPF_FSHIFT) { - fprintf(fout, " f_st0 = f_st1 %c f_st0;", j); + if (need_float_stack) { + if (!dead_dst) + fprintf(fout, " %s %c= %s;", buf1, j, buf2); + if (po->flags & OPF_FSHIFT) + fprintf(fout, " f_stp++;"); } else { - fprintf(fout, " %s %c= %s;", - out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0]), - j, - out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1])); + if (po->flags & OPF_FSHIFT) { + // note: assumes only 2 regs handled + if (!dead_dst) + fprintf(fout, " f_st0 = f_st1 %c f_st0;", j); + else + fprintf(fout, " f_st0 = f_st1;"); + } + else if (!dead_dst) + fprintf(fout, " %s %c= %s;", buf1, j, buf2); } + no_output = (dead_dst && !(po->flags & OPF_FSHIFT)); break; case OP_FDIVR: case OP_FSUBR: - if (po->flags & OPF_FSHIFT) - snprintf(buf1, sizeof(buf1), "f_st0"); - else - out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0]); - fprintf(fout, " %s = %s %c %s;", buf1, - out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1]), - po->op == OP_FDIVR ? '/' : '-', - out_src_opr_float(buf3, sizeof(buf3), po, &po->operand[0])); + out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0], + need_float_stack); + out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1], + need_float_stack); + out_src_opr_float(buf3, sizeof(buf3), po, &po->operand[0], + need_float_stack); + dead_dst = (po->flags & OPF_FPOP) + && po->operand[0].type == OPT_REG + && po->operand[0].reg == xST0; + j = po->op == OP_FDIVR ? '/' : '-'; + if (need_float_stack) { + if (!dead_dst) + fprintf(fout, " %s = %s %c %s;", buf1, buf2, j, buf3); + if (po->flags & OPF_FSHIFT) + fprintf(fout, " f_stp++;"); + } + else { + if (po->flags & OPF_FSHIFT) { + if (!dead_dst) + fprintf(fout, " f_st0 = f_st0 %c f_st1;", j); + else + fprintf(fout, " f_st0 = f_st1;"); + } + else if (!dead_dst) + fprintf(fout, " %s = %s %c %s;", buf1, buf2, j, buf3); + } + no_output = (dead_dst && !(po->flags & OPF_FSHIFT)); break; case OP_FIADD: @@ -6614,23 +7104,129 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) case OP_FISUB: j = '-'; break; default: j = 'x'; break; } - fprintf(fout, " f_st0 %c= (double)%s;", j, + fprintf(fout, " %s %c= (%s)%s;", float_st0, + j, float_type, out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], lmod_cast(po, po->operand[0].lmod, 1), 0)); break; case OP_FIDIVR: case OP_FISUBR: - fprintf(fout, " f_st0 = %s %c f_st0;", - out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1]), - po->op == OP_FIDIVR ? '/' : '-'); + fprintf(fout, " %s = %s %c %s;", float_st0, + out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1], + need_float_stack), + po->op == OP_FIDIVR ? '/' : '-', float_st0); + break; + + case OP_FCOM: { + int mask, z_check; + ferr_assert(po, po->datap != NULL); + mask = (long)po->datap & 0xffff; + z_check = ((long)po->datap >> 16) & 1; + out_src_opr_float(buf1, sizeof(buf1), po, &po->operand[0], + need_float_stack); + if (mask == 0x0100) { // C0 -> < + fprintf(fout, " f_sw = %s < %s ? 0x0100 : 0;", + float_st0, buf1); + } + else if (mask == 0x4000) { // C3 -> = + fprintf(fout, " f_sw = %s == %s ? 0x4000 : 0;", + float_st0, buf1); + } + else if (mask == 0x4100) { // C3, C0 + if (z_check) { + fprintf(fout, " f_sw = %s <= %s ? 0x4100 : 0;", + float_st0, buf1); + strcat(g_comment, " z_chk_det"); + } + else { + fprintf(fout, " f_sw = %s == %s ? 0x4000 : " + "(%s < %s ? 0x0100 : 0);", + float_st0, buf1, float_st0, buf1); + } + } + else + ferr(po, "unhandled sw mask: %x\n", mask); + if (po->flags & OPF_FSHIFT) { + if (need_float_stack) + fprintf(fout, " f_stp++;"); + else + fprintf(fout, " f_st0 = f_st1;"); + } + break; + } + + case OP_FNSTSW: + fprintf(fout, " %s = f_sw;", + out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0])); + break; + + case OP_FCHS: + fprintf(fout, " %s = -%s;", float_st0, float_st0); + break; + + case OP_FCOS: + fprintf(fout, " %s = cos%s(%s);", float_st0, + need_double ? "" : "f", float_st0); + break; + + case OP_FPATAN: + if (need_float_stack) { + fprintf(fout, " %s = atan%s(%s / %s);", float_st1, + need_double ? "" : "f", float_st1, float_st0); + fprintf(fout, " f_stp++;"); + } + else { + fprintf(fout, " f_st0 = atan%s(f_st1 / f_st0);", + need_double ? "" : "f"); + } + break; + + case OP_FYL2X: + if (need_float_stack) { + fprintf(fout, " %s = %s * log2%s(%s);", float_st1, + float_st1, need_double ? "" : "f", float_st0); + fprintf(fout, " f_stp++;"); + } + else { + fprintf(fout, " f_st0 = f_st1 * log2%s(f_st0);", + need_double ? "" : "f"); + } + break; + + case OP_FSIN: + fprintf(fout, " %s = sin%s(%s);", float_st0, + need_double ? "" : "f", float_st0); + break; + + case OP_FSQRT: + fprintf(fout, " %s = sqrt%s(%s);", float_st0, + need_double ? "" : "f", float_st0); + break; + + case OP_FXCH: + dead_dst = po->operand[0].type == OPT_REG + && po->operand[0].reg == xST0; + if (!dead_dst) { + out_src_opr_float(buf1, sizeof(buf1), po, &po->operand[0], + need_float_stack); + fprintf(fout, " { %s t = %s; %s = %s; %s = t; }", float_type, + float_st0, float_st0, buf1, buf1); + strcat(g_comment, " fxch"); + } + else + no_output = 1; break; case OPP_FTOL: ferr_assert(po, po->flags & OPF_32BIT); - fprintf(fout, " eax = (s32)f_st0;"); - if (po->flags & OPF_FSHIFT) - fprintf(fout, "\n f_st0 = f_st1;"); + fprintf(fout, " eax = (s32)%s;", float_st0); + if (po->flags & OPF_FSHIFT) { + if (need_float_stack) + fprintf(fout, " f_stp++;"); + else + fprintf(fout, " f_st0 = f_st1;"); + } strcat(g_comment, " ftol"); break; @@ -6753,10 +7349,13 @@ static struct scanned_var { } *hg_vars; static int hg_var_cnt; +static char **hg_refs; +static int hg_ref_cnt; + static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, int count); -struct func_prototype *hg_fp_add(const char *funcn) +static struct func_prototype *hg_fp_add(const char *funcn) { struct func_prototype *fp; @@ -6818,6 +7417,19 @@ static int hg_fp_cmp_id(const void *p1_, const void *p2_) } #endif +static void hg_ref_add(const char *name) +{ + if ((hg_ref_cnt & 0xff) == 0) { + hg_refs = realloc(hg_refs, sizeof(hg_refs[0]) * (hg_ref_cnt + 0x100)); + my_assert_not(hg_refs, NULL); + memset(hg_refs + hg_ref_cnt, 0, sizeof(hg_refs[0]) * 0x100); + } + + hg_refs[hg_ref_cnt] = strdup(name); + my_assert_not(hg_refs[hg_ref_cnt], NULL); + hg_ref_cnt++; +} + // recursive register dep pass // - track saved regs (part 2) // - try to figure out arg-regs @@ -7131,7 +7743,7 @@ static void gen_hdr(const char *funcn, int opcnt) fp->argc_stack--; } - fp->regmask_dep = regmask_dep & ~(1 << xSP); + fp->regmask_dep = regmask_dep & ~((1 << xSP) | mxSTa); fp->has_ret = has_ret; #if 0 printf("// has_ret %d, regmask_dep %x\n", @@ -7172,6 +7784,24 @@ static void hg_fp_resolve_deps(struct func_prototype *fp) } } +// make all thiscall/edx arg functions referenced from .data fastcall +static void do_func_refs_from_data(void) +{ + struct func_prototype *fp, fp_s; + int i; + + for (i = 0; i < hg_ref_cnt; i++) { + strcpy(fp_s.name, hg_refs[i]); + fp = bsearch(&fp_s, hg_fp, hg_fp_cnt, + sizeof(hg_fp[0]), hg_fp_cmp_name); + if (fp == NULL) + continue; + + if (fp->argc_stack != 0 && (fp->regmask_dep & (mxCX | mxDX))) + fp->regmask_dep |= mxCX | mxDX; + } +} + static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, int count) { @@ -7179,7 +7809,7 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, char *p, namebuf[NAMELEN]; const char *name; int regmask_dep; - int argc_stack; + int argc_normal; int j, arg; for (; count > 0; count--, fp++) { @@ -7217,18 +7847,25 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, } regmask_dep = fp->regmask_dep; - argc_stack = fp->argc_stack; + argc_normal = fp->argc_stack; fprintf(fout, "%-5s", fp->pp ? fp->pp->ret_type.name : (fp->has_ret ? "int" : "void")); - if (regmask_dep && (fp->is_stdcall || argc_stack == 0) - && (regmask_dep & ~((1 << xCX) | (1 << xDX))) == 0) + if (regmask_dep && (fp->is_stdcall || fp->argc_stack > 0) + && (regmask_dep & ~mxCX) == 0) + { + fprintf(fout, "/*__thiscall*/ "); + argc_normal++; + regmask_dep = 0; + } + else if (regmask_dep && (fp->is_stdcall || fp->argc_stack == 0) + && (regmask_dep & ~(mxCX | mxDX)) == 0) { fprintf(fout, " __fastcall "); - if (!(regmask_dep & (1 << xDX)) && argc_stack == 0) - argc_stack = 1; + if (!(regmask_dep & (1 << xDX)) && fp->argc_stack == 0) + argc_normal = 1; else - argc_stack += 2; + argc_normal += 2; regmask_dep = 0; } else if (regmask_dep && !fp->is_stdcall) { @@ -7258,7 +7895,7 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, } } - for (j = 0; j < argc_stack; j++) { + for (j = 0; j < argc_normal; j++) { arg++; if (arg != 1) fprintf(fout, ", "); @@ -7310,6 +7947,9 @@ static void output_hdr(FILE *fout) for (i = 0; i < hg_fp_cnt; i++) hg_fp_resolve_deps(&hg_fp[i]); + // adjust functions referenced from data segment + do_func_refs_from_data(); + // note: messes up .proto ptr, don't use //qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_id); @@ -7353,7 +7993,7 @@ static char *next_word_s(char *w, size_t wsize, char *s) s = sskip(s); i = 0; - if (*s == '\'') { + if (*s == '\'' && s[1] != '\r' && s[1] != '\n') { w[0] = s[0]; for (i = 1; i < wsize - 1; i++) { if (s[i] == 0) { @@ -7407,7 +8047,7 @@ static int is_xref_needed(char *p, char **rlist, int rlist_len) return 1; } -static int xrefs_show_need(FILE *fasm, char *p, +static int ida_xrefs_show_need(FILE *fasm, char *p, char **rlist, int rlist_len) { int found_need = 0; @@ -7454,7 +8094,8 @@ static void scan_variables(FILE *fasm, char **rlist, int rlist_len) { struct scanned_var *var; char line[256] = { 0, }; - char words[3][256]; + char words[4][256]; + int no_identifier; char *p = NULL; int wordc; int l; @@ -7493,8 +8134,7 @@ static void scan_variables(FILE *fasm, char **rlist, int rlist_len) asmln++; p = line; - if (my_isblank(*p)) - continue; + no_identifier = my_isblank(*p); p = sskip(p); if (*p == 0 || *p == ';') @@ -7514,13 +8154,19 @@ static void scan_variables(FILE *fasm, char **rlist, int rlist_len) if (wordc < 2) continue; + if (no_identifier) { + if (wordc >= 3 && IS(words[0], "dd") && IS(words[1], "offset")) + hg_ref_add(words[2]); + continue; + } + if (IS_START(words[0], "__IMPORT_DESCRIPTOR_")) { // when this starts, we don't need anything from this section break; } // check refs comment(s) - if (!xrefs_show_need(fasm, p, rlist, rlist_len)) + if (!ida_xrefs_show_need(fasm, p, rlist, rlist_len)) continue; if ((hg_var_cnt & 0xff) == 0) { @@ -7550,8 +8196,11 @@ static void scan_variables(FILE *fasm, char **rlist, int rlist_len) continue; } - if (IS(words[1], "dd")) + if (IS(words[1], "dd")) { var->lmod = OPLM_DWORD; + if (wordc >= 4 && IS(words[2], "offset")) + hg_ref_add(words[3]); + } else if (IS(words[1], "dw")) var->lmod = OPLM_WORD; else if (IS(words[1], "db")) { @@ -7872,6 +8521,46 @@ int main(int argc, char *argv[]) } } } + else if (p[2] == 's' && IS_START(p, "; sctattr:")) + { + static const char *attrs[] = { + "clear_sf", + "clear_regmask", + }; + + // parse manual attribute-list comment + g_sct_func_attr = 0; + p = sskip(p + 10); + + for (; *p != 0; p = sskip(p)) { + for (i = 0; i < ARRAY_SIZE(attrs); i++) { + if (!strncmp(p, attrs[i], strlen(attrs[i]))) { + g_sct_func_attr |= 1 << i; + p += strlen(attrs[i]); + break; + } + } + if (*p == '=') { + j = ret = 0; + if (i == 0) + // clear_sf=start,len (in dwords) + ret = sscanf(p, "=%d,%d%n", &g_stack_clear_start, + &g_stack_clear_len, &j); + else if (i == 1) + // clear_regmask= + ret = sscanf(p, "=%x%n", &g_regmask_init, &j) + 1; + if (ret < 2) { + anote("unparsed attr value: %s\n", p); + break; + } + p += j; + } + else if (i == ARRAY_SIZE(attrs)) { + anote("unparsed sct attr: %s\n", p); + break; + } + } + } else if (p[2] == 'S' && IS_START(p, "; START OF FUNCTION CHUNK FOR ")) { p += 30; @@ -8040,6 +8729,10 @@ do_pending_endp: pending_endp = 0; in_func = 0; g_ida_func_attr = 0; + g_sct_func_attr = 0; + g_stack_clear_start = 0; + g_stack_clear_len = 0; + g_regmask_init = 0; skip_warned = 0; g_skip_func = 0; g_func[0] = 0;