+ }
+ }
+
+ if (po->bt_i != -1 || (po->flags & OPF_RMD))
+ continue;
+
+ if (po->operand[0].type == OPT_LABEL)
+ // assume tail call
+ goto tailcall;
+
+ ferr(po, "unhandled branch\n");
+
+tailcall:
+ po->op = OP_CALL;
+ po->flags |= OPF_TAIL;
+ if (i > 0 && ops[i - 1].op == OP_POP)
+ po->flags |= OPF_ATAIL;
+ i--; // reprocess
+ }
+
+ // pass3:
+ // - remove dead labels
+ // - process trivial calls
+ for (i = 0; i < opcnt; i++)
+ {
+ if (g_labels[i] != NULL && g_label_refs[i].i == -1) {
+ free(g_labels[i]);
+ g_labels[i] = NULL;
+ }
+
+ po = &ops[i];
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ 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)
+ patch_esp_adjust(&ops[j], pp->argc_stack * 4);
+ else
+ ops[j].flags |= OPF_DONE;
+ }
+
+ if (strstr(pp->ret_type.name, "int64"))
+ need_tmp64 = 1;
+
+ po->flags |= OPF_DONE;
+ }
+ }
+ }
+
+ // pass4:
+ // - process calls
+ // - handle push <const>/pop pairs
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ continue;
+
+ if (po->op == OP_CALL && !(po->flags & OPF_DONE))
+ {
+ pp = process_call(i, opcnt);
+
+ if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) {
+ // since we know the args, collect them
+ collect_call_args(po, i, pp, ®mask, save_arg_vars,
+ i + opcnt * 2);
+ }
+
+ if (strstr(pp->ret_type.name, "int64"))
+ need_tmp64 = 1;
+ }
+ else 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);
+ }
+
+ // 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++)
+ {
+ po = &ops[i];
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ continue;
+
+ if (po->op == OP_PUSH && (po->flags & OPF_RSAVE)) {
+ reg = po->operand[0].reg;
+ if (!(regmask & (1 << reg)))
+ // not a reg save after all, rerun scan_for_pop
+ po->flags &= ~OPF_RSAVE;
+ else
+ regmask_save |= 1 << reg;
+ }
+
+ if (po->op == OP_PUSH && !(po->flags & OPF_FARG)
+ && !(po->flags & OPF_RSAVE) && !g_func_pp->is_userstack)
+ {
+ if (po->operand[0].type == OPT_REG)
+ {
+ reg = po->operand[0].reg;
+ if (reg < 0)
+ ferr(po, "reg not set for push?\n");
+
+ depth = 0;
+ ret = scan_for_pop(i + 1, opcnt,
+ po->operand[0].name, i + opcnt * 3, 0, &depth, 0);
+ if (ret == 1) {
+ if (depth > 1)
+ ferr(po, "too much depth: %d\n", depth);
+
+ po->flags |= OPF_RMD;
+ scan_for_pop(i + 1, opcnt, po->operand[0].name,
+ i + opcnt * 4, 0, &depth, 1);
+ continue;
+ }
+ ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, 0);
+ if (ret == 0) {
+ arg = OPF_RMD;
+ if (regmask & (1 << reg)) {
+ if (regmask_save & (1 << reg))
+ ferr(po, "%s already saved?\n", po->operand[0].name);
+ arg = OPF_RSAVE;
+ }
+ po->flags |= arg;
+ scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, arg);
+ continue;
+ }
+ }
+ }
+
+ if (po->op == OP_STD) {
+ po->flags |= OPF_DF | OPF_RMD | OPF_DONE;
+ scan_propagate_df(i + 1, opcnt);
+ }
+
+ regmask_now = po->regmask_src | po->regmask_dst;
+ if (regmask_now & (1 << xBP)) {
+ if (g_bp_frame && !(po->flags & OPF_EBP_S)) {
+ if (po->regmask_dst & (1 << xBP))
+ // compiler decided to drop bp frame and use ebp as scratch
+ scan_fwd_set_flags(i + 1, opcnt, i + opcnt * 5, OPF_EBP_S);
+ else
+ regmask_now &= ~(1 << xBP);
+ }
+ }
+
+ regmask |= regmask_now;
+
+ if (po->flags & OPF_CC)
+ {
+ int setters[16], cnt = 0, branched = 0;
+
+ ret = scan_for_flag_set(i, i + opcnt * 6,
+ &branched, setters, &cnt);
+ if (ret < 0 || cnt <= 0)
+ ferr(po, "unable to trace flag setter(s)\n");
+ if (cnt > ARRAY_SIZE(setters))
+ ferr(po, "too many flag setters\n");
+
+ for (j = 0; j < cnt; j++)
+ {
+ tmp_op = &ops[setters[j]]; // flag setter
+ pfomask = 0;
+
+ // to get nicer code, we try to delay test and cmp;
+ // if we can't because of operand modification, or if we
+ // have arith op, or branch, make it calculate flags explicitly
+ if (tmp_op->op == OP_TEST || tmp_op->op == OP_CMP)
+ {
+ if (branched || scan_for_mod(tmp_op, setters[j] + 1, i, 0) >= 0)
+ pfomask = 1 << po->pfo;
+ }
+ else if (tmp_op->op == OP_CMPS || tmp_op->op == OP_SCAS) {
+ pfomask = 1 << po->pfo;
+ }
+ else {
+ // see if we'll be able to handle based on op result
+ if ((tmp_op->op != OP_AND && tmp_op->op != OP_OR
+ && po->pfo != PFO_Z && po->pfo != PFO_S
+ && po->pfo != PFO_P)
+ || branched
+ || scan_for_mod_opr0(tmp_op, setters[j] + 1, i) >= 0)
+ {
+ pfomask = 1 << po->pfo;
+ }
+
+ if (tmp_op->op == OP_ADD && po->pfo == PFO_C) {
+ propagate_lmod(tmp_op, &tmp_op->operand[0],
+ &tmp_op->operand[1]);
+ if (tmp_op->operand[0].lmod == OPLM_DWORD)
+ need_tmp64 = 1;
+ }
+ }
+ if (pfomask) {
+ tmp_op->pfomask |= pfomask;
+ cond_vars |= pfomask;
+ }
+ // note: may overwrite, currently not a problem
+ po->datap = tmp_op;
+ }
+
+ if (po->op == OP_RCL || po->op == OP_RCR
+ || po->op == OP_ADC || po->op == OP_SBB)
+ cond_vars |= 1 << PFO_C;
+ }
+
+ if (po->op == OP_CMPS || po->op == OP_SCAS) {
+ cond_vars |= 1 << PFO_Z;
+ }
+ else if (po->op == OP_MUL
+ || (po->op == OP_IMUL && po->operand_cnt == 1))
+ {
+ if (po->operand[0].lmod == OPLM_DWORD)
+ 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");
+
+ if (pp->is_unresolved) {
+ int regmask_stack = 0;
+ collect_call_args(po, i, pp, ®mask, save_arg_vars,
+ 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)
+ continue;
+
+ tmp_op = pp->arg[arg].datap;
+ if (tmp_op == NULL)
+ ferr(po, "parsed_op missing for arg%d\n", arg);
+ if (tmp_op->p_argnum == 0 && tmp_op->operand[0].type == OPT_REG)
+ regmask_stack |= 1 << tmp_op->operand[0].reg;
+ }
+
+ if (!((regmask_stack & (1 << xCX))
+ && (regmask_stack & (1 << xDX))))
+ {
+ if (pp->argc_stack != 0
+ || ((regmask | regmask_arg) & ((1 << xCX)|(1 << xDX))))
+ {
+ pp_insert_reg_arg(pp, "ecx");
+ pp->is_fastcall = 1;
+ regmask_init |= 1 << xCX;
+ regmask |= 1 << xCX;
+ }
+ if (pp->argc_stack != 0
+ || ((regmask | regmask_arg) & (1 << xDX)))
+ {
+ pp_insert_reg_arg(pp, "edx");
+ regmask_init |= 1 << xDX;
+ regmask |= 1 << xDX;
+ }
+ }
+
+ // note: __cdecl doesn't fall into is_unresolved category
+ if (pp->argc_stack > 0)
+ pp->is_stdcall = 1;
+ }
+
+ for (arg = 0; arg < pp->argc; arg++) {
+ if (pp->arg[arg].reg != NULL) {
+ reg = char_array_i(regs_r32,
+ ARRAY_SIZE(regs_r32), pp->arg[arg].reg);
+ if (reg < 0)
+ ferr(ops, "arg '%s' is not a reg?\n", pp->arg[arg].reg);
+ if (!(regmask & (1 << reg))) {
+ regmask_init |= 1 << reg;
+ regmask |= 1 << reg;
+ }
+ }
+ }
+ }
+ else if (po->op == OP_MOV && po->operand[0].pp != NULL
+ && po->operand[1].pp != NULL)
+ {
+ // <var> = offset <something>
+ 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_RET && !IS(g_func_pp->ret_type.name, "void"))
+ regmask |= 1 << xAX;
+ else if (po->op == OP_DIV || po->op == OP_IDIV) {
+ // 32bit division is common, look for it
+ if (po->op == OP_DIV)
+ ret = scan_for_reg_clear(i, xDX);
+ else
+ ret = scan_for_cdq_edx(i);
+ if (ret >= 0)
+ po->flags |= OPF_32BIT;
+ else
+ need_tmp64 = 1;
+ }
+ else if (po->op == OP_CLD)
+ po->flags |= OPF_RMD | OPF_DONE;
+
+ if (po->op == OP_RCL || po->op == OP_RCR || po->op == OP_XCHG) {
+ need_tmp_var = 1;
+ }
+ }
+
+ // pass6:
+ // - confirm regmask_save, it might have been reduced
+ if (regmask_save != 0)
+ {
+ regmask_save = 0;
+ for (i = 0; i < opcnt; i++) {
+ po = &ops[i];
+ if (po->flags & OPF_RMD)
+ continue;
+
+ if (po->op == OP_PUSH && (po->flags & OPF_RSAVE))
+ regmask_save |= 1 << po->operand[0].reg;
+ }
+ }
+
+ // output starts here
+
+ // define userstack size
+ if (g_func_pp->is_userstack) {
+ fprintf(fout, "#ifndef US_SZ_%s\n", g_func_pp->name);
+ fprintf(fout, "#define US_SZ_%s USERSTACK_SIZE\n", g_func_pp->name);
+ fprintf(fout, "#endif\n");
+ }
+
+ // the function itself
+ ferr_assert(ops, !g_func_pp->is_fptr);
+ output_pp(fout, g_func_pp,
+ (g_ida_func_attr & IDAFA_NORETURN) ? OPP_FORCE_NORETURN : 0);
+ fprintf(fout, "\n{\n");
+
+ // declare indirect functions
+ for (i = 0; i < opcnt; i++) {
+ po = &ops[i];
+ if (po->flags & OPF_RMD)
+ continue;
+
+ if (po->op == OP_CALL) {
+ pp = po->pp;
+ if (pp == NULL)
+ ferr(po, "NULL pp\n");
+
+ if (pp->is_fptr && !(pp->name[0] != 0 && pp->is_arg)) {
+ if (pp->name[0] != 0) {
+ memmove(pp->name + 2, pp->name, strlen(pp->name) + 1);
+ memcpy(pp->name, "i_", 2);
+
+ // might be declared already
+ found = 0;
+ for (j = 0; j < i; j++) {
+ if (ops[j].op == OP_CALL && (pp_tmp = ops[j].pp)) {
+ if (pp_tmp->is_fptr && IS(pp->name, pp_tmp->name)) {
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (found)
+ continue;
+ }
+ else
+ snprintf(pp->name, sizeof(pp->name), "icall%d", i);
+
+ fprintf(fout, " ");
+ output_pp(fout, pp, OPP_SIMPLE_ARGS);
+ fprintf(fout, ";\n");
+ }
+ }
+ }
+
+ // output LUTs/jumptables
+ for (i = 0; i < g_func_pd_cnt; i++) {
+ pd = &g_func_pd[i];
+ fprintf(fout, " static const ");
+ if (pd->type == OPT_OFFSET) {
+ fprintf(fout, "void *jt_%s[] =\n { ", pd->label);
+
+ for (j = 0; j < pd->count; j++) {
+ if (j > 0)
+ fprintf(fout, ", ");
+ fprintf(fout, "&&%s", pd->d[j].u.label);
+ }
+ }
+ else {
+ fprintf(fout, "%s %s[] =\n { ",
+ lmod_type_u(ops, pd->lmod), pd->label);
+
+ for (j = 0; j < pd->count; j++) {
+ if (j > 0)
+ fprintf(fout, ", ");
+ fprintf(fout, "%u", pd->d[j].u.val);
+ }
+ }
+ fprintf(fout, " };\n");
+ had_decl = 1;
+ }
+
+ // declare stack frame, va_arg
+ if (g_stack_fsz) {
+ fprintf(fout, " union { u32 d[%d]; u16 w[%d]; u8 b[%d]; } sf;\n",
+ (g_stack_fsz + 3) / 4, (g_stack_fsz + 1) / 2, g_stack_fsz);
+ had_decl = 1;
+ }
+
+ if (g_func_pp->is_userstack) {
+ fprintf(fout, " u32 fake_sf[US_SZ_%s / 4];\n", g_func_pp->name);
+ fprintf(fout, " u32 *esp = &fake_sf[sizeof(fake_sf) / 4];\n");
+ had_decl = 1;
+ }
+
+ if (g_func_pp->is_vararg) {
+ fprintf(fout, " va_list ap;\n");
+ had_decl = 1;
+ }
+
+ // declare arg-registers
+ for (i = 0; i < g_func_pp->argc; i++) {
+ if (g_func_pp->arg[i].reg != NULL) {
+ reg = char_array_i(regs_r32,
+ ARRAY_SIZE(regs_r32), g_func_pp->arg[i].reg);
+ if (regmask & (1 << reg)) {
+ if (g_func_pp->arg[i].type.is_retreg)
+ fprintf(fout, " u32 %s = *r_%s;\n",
+ g_func_pp->arg[i].reg, g_func_pp->arg[i].reg);
+ else
+ fprintf(fout, " u32 %s = (u32)a%d;\n",
+ g_func_pp->arg[i].reg, i + 1);
+ }
+ else {
+ if (g_func_pp->arg[i].type.is_retreg)
+ ferr(ops, "retreg '%s' is unused?\n",
+ g_func_pp->arg[i].reg);
+ fprintf(fout, " // %s = a%d; // unused\n",
+ g_func_pp->arg[i].reg, i + 1);
+ }
+ had_decl = 1;
+ }
+ }
+
+ regmask_now = regmask & ~regmask_arg;
+ regmask_now &= ~(1 << xSP);
+ if (regmask_now & 0x00ff) {
+ for (reg = 0; reg < 8; reg++) {
+ if (regmask_now & (1 << reg)) {
+ fprintf(fout, " u32 %s", regs_r32[reg]);
+ if (regmask_init & (1 << reg))
+ fprintf(fout, " = 0");
+ fprintf(fout, ";\n");
+ had_decl = 1;
+ }
+ }
+ }
+ if (regmask_now & 0xff00) {
+ for (reg = 8; reg < 16; reg++) {
+ if (regmask_now & (1 << reg)) {
+ fprintf(fout, " mmxr %s", regs_r32[reg]);
+ if (regmask_init & (1 << reg))
+ fprintf(fout, " = { 0, }");
+ fprintf(fout, ";\n");
+ had_decl = 1;
+ }
+ }
+ }
+
+ if (regmask_save) {
+ for (reg = 0; reg < 8; reg++) {
+ if (regmask_save & (1 << reg)) {
+ fprintf(fout, " u32 s_%s;\n", regs_r32[reg]);
+ had_decl = 1;
+ }
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(save_arg_vars); i++) {
+ if (save_arg_vars[i] == 0)
+ continue;
+ for (reg = 0; reg < 32; reg++) {
+ if (save_arg_vars[i] & (1 << reg)) {
+ fprintf(fout, " u32 %s;\n",
+ saved_arg_name(buf1, sizeof(buf1), i, reg + 1));
+ had_decl = 1;
+ }
+ }
+ }
+
+ if (cond_vars) {
+ for (i = 0; i < 8; i++) {
+ if (cond_vars & (1 << i)) {
+ fprintf(fout, " u32 cond_%s;\n", parsed_flag_op_names[i]);
+ had_decl = 1;
+ }
+ }
+ }
+
+ if (need_tmp_var) {
+ fprintf(fout, " u32 tmp;\n");
+ had_decl = 1;
+ }
+
+ if (need_tmp64) {
+ fprintf(fout, " u64 tmp64;\n");
+ had_decl = 1;
+ }
+
+ if (had_decl)
+ fprintf(fout, "\n");
+
+ if (g_func_pp->is_vararg) {
+ if (g_func_pp->argc_stack == 0)
+ ferr(ops, "vararg func without stack args?\n");
+ fprintf(fout, " va_start(ap, a%d);\n", g_func_pp->argc);
+ }
+
+ // output ops
+ for (i = 0; i < opcnt; i++)
+ {
+ if (g_labels[i] != NULL) {
+ fprintf(fout, "\n%s:\n", g_labels[i]);
+ label_pending = 1;
+
+ delayed_flag_op = NULL;
+ last_arith_dst = NULL;
+ }
+
+ po = &ops[i];
+ if (po->flags & OPF_RMD)
+ continue;
+
+ no_output = 0;
+
+ #define assert_operand_cnt(n_) \
+ if (po->operand_cnt != n_) \
+ ferr(po, "operand_cnt is %d/%d\n", po->operand_cnt, n_)
+
+ // conditional/flag using op?
+ if (po->flags & OPF_CC)
+ {
+ int is_delayed = 0;
+
+ tmp_op = po->datap;
+
+ // we go through all this trouble to avoid using parsed_flag_op,
+ // which makes generated code much nicer
+ if (delayed_flag_op != NULL)
+ {
+ out_cmp_test(buf1, sizeof(buf1), delayed_flag_op,
+ po->pfo, po->pfo_inv);
+ is_delayed = 1;
+ }
+ else if (last_arith_dst != NULL
+ && (po->pfo == PFO_Z || po->pfo == PFO_S || po->pfo == PFO_P
+ || (tmp_op && (tmp_op->op == OP_AND || tmp_op->op == OP_OR))
+ ))
+ {
+ out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
+ out_test_for_cc(buf1, sizeof(buf1), po, po->pfo, po->pfo_inv,
+ last_arith_dst->lmod, buf3);
+ is_delayed = 1;
+ }
+ else if (tmp_op != NULL) {
+ // use preprocessed flag calc results
+ if (!(tmp_op->pfomask & (1 << po->pfo)))
+ ferr(po, "not prepared for pfo %d\n", po->pfo);
+
+ // note: pfo_inv was not yet applied
+ snprintf(buf1, sizeof(buf1), "(%scond_%s)",
+ po->pfo_inv ? "!" : "", parsed_flag_op_names[po->pfo]);
+ }
+ else {
+ ferr(po, "all methods of finding comparison failed\n");
+ }
+
+ if (po->flags & OPF_JMP) {
+ fprintf(fout, " if %s", buf1);
+ }
+ else if (po->op == OP_RCL || po->op == OP_RCR
+ || po->op == OP_ADC || po->op == OP_SBB)
+ {
+ if (is_delayed)
+ fprintf(fout, " cond_%s = %s;\n",
+ parsed_flag_op_names[po->pfo], buf1);
+ }
+ else if (po->flags & OPF_DATA) { // SETcc
+ out_dst_opr(buf2, sizeof(buf2), po, &po->operand[0]);
+ fprintf(fout, " %s = %s;", buf2, buf1);
+ }
+ else {
+ ferr(po, "unhandled conditional op\n");
+ }
+ }
+
+ pfomask = po->pfomask;
+
+ if (po->flags & (OPF_REPZ|OPF_REPNZ)) {
+ struct parsed_opr opr = {0,};
+ opr.type = OPT_REG;
+ opr.reg = xCX;
+ opr.lmod = OPLM_DWORD;
+ ret = try_resolve_const(i, &opr, opcnt * 7 + i, &uval);
+
+ if (ret != 1 || uval == 0) {
+ // we need initial flags for ecx=0 case..
+ if (i > 0 && ops[i - 1].op == OP_XOR
+ && IS(ops[i - 1].operand[0].name,
+ ops[i - 1].operand[1].name))
+ {
+ fprintf(fout, " cond_z = ");
+ if (pfomask & (1 << PFO_C))
+ fprintf(fout, "cond_c = ");
+ fprintf(fout, "0;\n");
+ }
+ else if (last_arith_dst != NULL) {
+ out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
+ out_test_for_cc(buf1, sizeof(buf1), po, PFO_Z, 0,
+ last_arith_dst->lmod, buf3);
+ fprintf(fout, " cond_z = %s;\n", buf1);
+ }
+ else
+ ferr(po, "missing initial ZF\n");
+ }
+ }
+
+ switch (po->op)
+ {
+ case OP_MOV:
+ assert_operand_cnt(2);
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ default_cast_to(buf3, sizeof(buf3), &po->operand[0]);
+ fprintf(fout, " %s = %s;", buf1,
+ out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
+ buf3, 0));
+ break;
+
+ case OP_LEA:
+ assert_operand_cnt(2);
+ po->operand[1].lmod = OPLM_DWORD; // always
+ fprintf(fout, " %s = %s;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
+ NULL, 1));
+ break;
+
+ case OP_MOVZX:
+ assert_operand_cnt(2);
+ fprintf(fout, " %s = %s;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ break;
+
+ case OP_MOVSX:
+ assert_operand_cnt(2);
+ switch (po->operand[1].lmod) {
+ case OPLM_BYTE:
+ strcpy(buf3, "(s8)");
+ break;
+ case OPLM_WORD:
+ strcpy(buf3, "(s16)");
+ break;
+ default:
+ ferr(po, "invalid src lmod: %d\n", po->operand[1].lmod);
+ }
+ fprintf(fout, " %s = %s;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
+ buf3, 0));
+ break;
+
+ case OP_XCHG:
+ assert_operand_cnt(2);
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ fprintf(fout, " tmp = %s;",
+ out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], "", 0));
+ fprintf(fout, " %s = %s;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
+ default_cast_to(buf3, sizeof(buf3), &po->operand[0]), 0));
+ fprintf(fout, " %s = %stmp;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[1]),
+ default_cast_to(buf3, sizeof(buf3), &po->operand[1]));
+ snprintf(g_comment, sizeof(g_comment), "xchg");
+ break;
+
+ case OP_NOT:
+ assert_operand_cnt(1);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ fprintf(fout, " %s = ~%s;", buf1, buf1);
+ break;
+
+ case OP_CDQ:
+ assert_operand_cnt(2);
+ fprintf(fout, " %s = (s32)%s >> 31;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ strcpy(g_comment, "cdq");
+ break;
+
+ case OP_LODS:
+ assert_operand_cnt(3);
+ if (po->flags & OPF_REP) {
+ // hmh..
+ ferr(po, "TODO\n");
+ }
+ else {
+ fprintf(fout, " eax = %sesi; esi %c= %d;",
+ lmod_cast_u_ptr(po, po->operand[0].lmod),
+ (po->flags & OPF_DF) ? '-' : '+',
+ lmod_bytes(po, po->operand[0].lmod));
+ strcpy(g_comment, "lods");
+ }
+ break;
+
+ case OP_STOS:
+ assert_operand_cnt(3);
+ if (po->flags & OPF_REP) {
+ fprintf(fout, " for (; ecx != 0; ecx--, edi %c= %d)\n",
+ (po->flags & OPF_DF) ? '-' : '+',
+ lmod_bytes(po, po->operand[0].lmod));
+ fprintf(fout, " %sedi = eax;",
+ lmod_cast_u_ptr(po, po->operand[0].lmod));
+ strcpy(g_comment, "rep stos");
+ }
+ else {
+ fprintf(fout, " %sedi = eax; edi %c= %d;",
+ lmod_cast_u_ptr(po, po->operand[0].lmod),
+ (po->flags & OPF_DF) ? '-' : '+',
+ lmod_bytes(po, po->operand[0].lmod));
+ strcpy(g_comment, "stos");
+ }
+ break;
+
+ case OP_MOVS:
+ assert_operand_cnt(3);
+ j = lmod_bytes(po, po->operand[0].lmod);
+ strcpy(buf1, lmod_cast_u_ptr(po, po->operand[0].lmod));
+ l = (po->flags & OPF_DF) ? '-' : '+';
+ if (po->flags & OPF_REP) {
+ fprintf(fout,
+ " for (; ecx != 0; ecx--, edi %c= %d, esi %c= %d)\n",
+ l, j, l, j);
+ fprintf(fout,
+ " %sedi = %sesi;", buf1, buf1);
+ strcpy(g_comment, "rep movs");
+ }
+ else {
+ fprintf(fout, " %sedi = %sesi; edi %c= %d; esi %c= %d;",
+ buf1, buf1, l, j, l, j);
+ strcpy(g_comment, "movs");
+ }
+ break;
+
+ case OP_CMPS:
+ // repe ~ repeat while ZF=1
+ assert_operand_cnt(3);
+ j = lmod_bytes(po, po->operand[0].lmod);
+ strcpy(buf1, lmod_cast_u_ptr(po, po->operand[0].lmod));
+ l = (po->flags & OPF_DF) ? '-' : '+';
+ if (po->flags & OPF_REP) {
+ fprintf(fout,
+ " for (; ecx != 0; ecx--) {\n");
+ if (pfomask & (1 << PFO_C)) {
+ // ugh..
+ fprintf(fout,
+ " cond_c = %sesi < %sedi;\n", buf1, buf1);
+ pfomask &= ~(1 << PFO_C);
+ }
+ fprintf(fout,
+ " cond_z = (%sesi == %sedi); esi %c= %d, edi %c= %d;\n",
+ buf1, buf1, l, j, l, j);
+ fprintf(fout,
+ " if (cond_z %s 0) break;\n",
+ (po->flags & OPF_REPZ) ? "==" : "!=");
+ fprintf(fout,
+ " }");
+ snprintf(g_comment, sizeof(g_comment), "rep%s cmps",
+ (po->flags & OPF_REPZ) ? "e" : "ne");
+ }
+ else {
+ fprintf(fout,
+ " cond_z = (%sesi == %sedi); esi %c= %d; edi %c= %d;",
+ buf1, buf1, l, j, l, j);
+ strcpy(g_comment, "cmps");
+ }
+ pfomask &= ~(1 << PFO_Z);
+ last_arith_dst = NULL;
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_SCAS:
+ // only does ZF (for now)
+ // repe ~ repeat while ZF=1
+ assert_operand_cnt(3);
+ j = lmod_bytes(po, po->operand[0].lmod);
+ l = (po->flags & OPF_DF) ? '-' : '+';
+ if (po->flags & OPF_REP) {
+ fprintf(fout,
+ " for (; ecx != 0; ecx--) {\n");
+ fprintf(fout,
+ " cond_z = (%seax == %sedi); edi %c= %d;\n",
+ lmod_cast_u(po, po->operand[0].lmod),
+ lmod_cast_u_ptr(po, po->operand[0].lmod), l, j);
+ fprintf(fout,
+ " if (cond_z %s 0) break;\n",
+ (po->flags & OPF_REPZ) ? "==" : "!=");
+ fprintf(fout,
+ " }");
+ snprintf(g_comment, sizeof(g_comment), "rep%s scas",
+ (po->flags & OPF_REPZ) ? "e" : "ne");
+ }
+ else {
+ fprintf(fout, " cond_z = (%seax == %sedi); edi %c= %d;",
+ lmod_cast_u(po, po->operand[0].lmod),
+ lmod_cast_u_ptr(po, po->operand[0].lmod), l, j);
+ strcpy(g_comment, "scas");
+ }
+ pfomask &= ~(1 << PFO_Z);
+ last_arith_dst = NULL;
+ delayed_flag_op = NULL;
+ break;
+
+ // arithmetic w/flags
+ case OP_AND:
+ case OP_OR:
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ // fallthrough
+ dualop_arith:
+ assert_operand_cnt(2);
+ fprintf(fout, " %s %s= %s;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ op_to_c(po),
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_SHL:
+ case OP_SHR:
+ assert_operand_cnt(2);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ if (pfomask & (1 << PFO_C)) {
+ if (po->operand[1].type == OPT_CONST) {
+ l = lmod_bytes(po, po->operand[0].lmod) * 8;
+ j = po->operand[1].val;
+ j %= l;
+ if (j != 0) {
+ if (po->op == OP_SHL)
+ j = l - j;
+ else
+ j -= 1;
+ fprintf(fout, " cond_c = (%s >> %d) & 1;\n",
+ buf1, j);
+ }
+ else
+ ferr(po, "zero shift?\n");
+ }
+ else
+ ferr(po, "TODO\n");
+ pfomask &= ~(1 << PFO_C);
+ }
+ fprintf(fout, " %s %s= %s;", buf1, op_to_c(po),
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_SAR:
+ assert_operand_cnt(2);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ fprintf(fout, " %s = %s%s >> %s;", buf1,
+ lmod_cast_s(po, po->operand[0].lmod), buf1,
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_SHRD:
+ assert_operand_cnt(3);
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ l = lmod_bytes(po, po->operand[0].lmod) * 8;
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
+ out_src_opr_u32(buf3, sizeof(buf3), po, &po->operand[2]);
+ fprintf(fout, " %s >>= %s; %s |= %s << (%d - %s);",
+ buf1, buf3, buf1, buf2, l, buf3);
+ strcpy(g_comment, "shrd");
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_ROL:
+ case OP_ROR:
+ assert_operand_cnt(2);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ if (po->operand[1].type == OPT_CONST) {
+ j = po->operand[1].val;
+ j %= lmod_bytes(po, po->operand[0].lmod) * 8;
+ fprintf(fout, po->op == OP_ROL ?
+ " %s = (%s << %d) | (%s >> %d);" :
+ " %s = (%s >> %d) | (%s << %d);",
+ buf1, buf1, j, buf1,
+ lmod_bytes(po, po->operand[0].lmod) * 8 - j);
+ }
+ else
+ ferr(po, "TODO\n");
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_RCL:
+ case OP_RCR:
+ assert_operand_cnt(2);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ l = lmod_bytes(po, po->operand[0].lmod) * 8;
+ if (po->operand[1].type == OPT_CONST) {
+ j = po->operand[1].val % l;
+ if (j == 0)
+ ferr(po, "zero rotate\n");
+ fprintf(fout, " tmp = (%s >> %d) & 1;\n",
+ buf1, (po->op == OP_RCL) ? (l - j) : (j - 1));
+ if (po->op == OP_RCL) {
+ fprintf(fout,
+ " %s = (%s << %d) | (cond_c << %d)",
+ buf1, buf1, j, j - 1);
+ if (j != 1)
+ fprintf(fout, " | (%s >> %d)", buf1, l + 1 - j);
+ }
+ else {
+ fprintf(fout,
+ " %s = (%s >> %d) | (cond_c << %d)",
+ buf1, buf1, j, l - j);
+ if (j != 1)
+ fprintf(fout, " | (%s << %d)", buf1, l + 1 - j);
+ }
+ fprintf(fout, ";\n");
+ fprintf(fout, " cond_c = tmp;");
+ }
+ else
+ ferr(po, "TODO\n");
+ strcpy(g_comment, (po->op == OP_RCL) ? "rcl" : "rcr");
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_XOR:
+ assert_operand_cnt(2);
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ if (IS(opr_name(po, 0), opr_name(po, 1))) {
+ // special case for XOR
+ if (pfomask & (1 << PFO_BE)) { // weird, but it happens..
+ fprintf(fout, " cond_be = 1;\n");
+ pfomask &= ~(1 << PFO_BE);
+ }
+ fprintf(fout, " %s = 0;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+ }
+ goto dualop_arith;
+
+ case OP_ADD:
+ assert_operand_cnt(2);
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ if (pfomask & (1 << PFO_C)) {
+ out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
+ if (po->operand[0].lmod == OPLM_DWORD) {
+ fprintf(fout, " tmp64 = (u64)%s + %s;\n", buf1, buf2);
+ fprintf(fout, " cond_c = tmp64 >> 32;\n");
+ fprintf(fout, " %s = (u32)tmp64;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
+ strcat(g_comment, "add64");
+ }
+ else {
+ fprintf(fout, " cond_c = ((u32)%s + %s) >> %d;\n",
+ buf1, buf2, lmod_bytes(po, po->operand[0].lmod) * 8);
+ fprintf(fout, " %s += %s;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ buf2);
+ }
+ pfomask &= ~(1 << PFO_C);
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+ }
+ goto dualop_arith;
+
+ case OP_SUB:
+ assert_operand_cnt(2);
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ if (pfomask & ~((1 << PFO_Z) | (1 << PFO_S))) {
+ for (j = 0; j <= PFO_LE; j++) {
+ if (!(pfomask & (1 << j)))
+ continue;
+ if (j == PFO_Z || j == PFO_S)
+ continue;
+
+ out_cmp_for_cc(buf1, sizeof(buf1), po, j, 0);
+ fprintf(fout, " cond_%s = %s;\n",
+ parsed_flag_op_names[j], buf1);
+ pfomask &= ~(1 << j);
+ }
+ }
+ goto dualop_arith;
+
+ case OP_ADC:
+ case OP_SBB:
+ assert_operand_cnt(2);
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ if (po->op == OP_SBB
+ && IS(po->operand[0].name, po->operand[1].name))
+ {
+ // avoid use of unitialized var
+ fprintf(fout, " %s = -cond_c;", buf1);
+ // carry remains what it was
+ pfomask &= ~(1 << PFO_C);
+ }
+ else {
+ fprintf(fout, " %s %s= %s + cond_c;", buf1, op_to_c(po),
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ }
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_BSF:
+ assert_operand_cnt(2);
+ 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);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ strcat(g_comment, "bsf");
+ break;
+
+ case OP_DEC:
+ if (pfomask & ~(PFOB_S | PFOB_S | PFOB_C)) {
+ for (j = 0; j <= PFO_LE; j++) {
+ if (!(pfomask & (1 << j)))
+ continue;
+ if (j == PFO_Z || j == PFO_S || j == PFO_C)
+ continue;
+
+ out_cmp_for_cc(buf1, sizeof(buf1), po, j, 0);
+ fprintf(fout, " cond_%s = %s;\n",
+ parsed_flag_op_names[j], buf1);
+ pfomask &= ~(1 << j);
+ }
+ }
+ // fallthrough
+
+ case OP_INC:
+ if (pfomask & (1 << PFO_C))
+ // carry is unaffected by inc/dec.. wtf?
+ ferr(po, "carry propagation needed\n");
+
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ if (po->operand[0].type == OPT_REG) {
+ strcpy(buf2, po->op == OP_INC ? "++" : "--");
+ fprintf(fout, " %s%s;", buf1, buf2);
+ }
+ else {
+ strcpy(buf2, po->op == OP_INC ? "+" : "-");
+ fprintf(fout, " %s %s= 1;", buf1, buf2);
+ }
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_NEG:
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]);
+ fprintf(fout, " %s = -%s%s;", buf1,
+ lmod_cast_s(po, po->operand[0].lmod), buf2);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ if (pfomask & (1 << PFO_C)) {
+ fprintf(fout, "\n cond_c = (%s != 0);", buf1);
+ pfomask &= ~(1 << PFO_C);
+ }
+ break;
+
+ case OP_IMUL:
+ if (po->operand_cnt == 2) {
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ goto dualop_arith;
+ }
+ if (po->operand_cnt == 3)
+ ferr(po, "TODO imul3\n");
+ // fallthrough
+ case OP_MUL:
+ assert_operand_cnt(1);
+ switch (po->operand[0].lmod) {
+ case OPLM_DWORD:
+ strcpy(buf1, po->op == OP_IMUL ? "(s64)(s32)" : "(u64)");
+ fprintf(fout, " tmp64 = %seax * %s%s;\n", buf1, buf1,
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]));
+ fprintf(fout, " edx = tmp64 >> 32;\n");
+ fprintf(fout, " eax = tmp64;");
+ break;
+ case OPLM_BYTE:
+ strcpy(buf1, po->op == OP_IMUL ? "(s16)(s8)" : "(u16)(u8)");
+ fprintf(fout, " LOWORD(eax) = %seax * %s;", buf1,
+ out_src_opr(buf2, sizeof(buf2), po, &po->operand[0],
+ buf1, 0));
+ break;
+ default:
+ ferr(po, "TODO: unhandled mul type\n");
+ break;
+ }
+ last_arith_dst = NULL;
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_DIV:
+ case OP_IDIV:
+ assert_operand_cnt(1);
+ if (po->operand[0].lmod != OPLM_DWORD)
+ ferr(po, "unhandled lmod %d\n", po->operand[0].lmod);
+
+ out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
+ strcpy(buf2, lmod_cast(po, po->operand[0].lmod,
+ po->op == OP_IDIV));
+ switch (po->operand[0].lmod) {
+ case OPLM_DWORD:
+ if (po->flags & OPF_32BIT)
+ snprintf(buf3, sizeof(buf3), "%seax", buf2);
+ else {
+ fprintf(fout, " tmp64 = ((u64)edx << 32) | eax;\n");
+ snprintf(buf3, sizeof(buf3), "%stmp64",
+ (po->op == OP_IDIV) ? "(s64)" : "");
+ }
+ if (po->operand[0].type == OPT_REG
+ && po->operand[0].reg == xDX)
+ {
+ fprintf(fout, " eax = %s / %s%s;", buf3, buf2, buf1);
+ fprintf(fout, " edx = %s %% %s%s;\n", buf3, buf2, buf1);
+ }
+ else {
+ fprintf(fout, " edx = %s %% %s%s;\n", buf3, buf2, buf1);
+ fprintf(fout, " eax = %s / %s%s;", buf3, buf2, buf1);
+ }
+ break;
+ default:
+ ferr(po, "unhandled division type\n");
+ }
+ last_arith_dst = NULL;
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_TEST:
+ case OP_CMP:
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ if (pfomask != 0) {
+ for (j = 0; j < 8; j++) {
+ if (pfomask & (1 << j)) {
+ out_cmp_test(buf1, sizeof(buf1), po, j, 0);
+ fprintf(fout, " cond_%s = %s;",
+ parsed_flag_op_names[j], buf1);
+ }
+ }
+ pfomask = 0;
+ }
+ else
+ no_output = 1;
+ last_arith_dst = NULL;
+ delayed_flag_op = po;
+ break;
+
+ case OP_SCC:
+ // SETcc - should already be handled
+ break;
+
+ // note: we reuse OP_Jcc for SETcc, only flags differ
+ case OP_JCC:
+ fprintf(fout, "\n goto %s;", po->operand[0].name);
+ break;
+
+ case OP_JECXZ:
+ fprintf(fout, " if (ecx == 0)\n");
+ fprintf(fout, " goto %s;", po->operand[0].name);
+ strcat(g_comment, "jecxz");
+ break;
+
+ case OP_JMP:
+ assert_operand_cnt(1);
+ last_arith_dst = NULL;
+ delayed_flag_op = NULL;
+
+ if (po->operand[0].type == OPT_REGMEM) {
+ ret = sscanf(po->operand[0].name, "%[^[][%[^*]*4]",
+ buf1, buf2);
+ if (ret != 2)
+ ferr(po, "parse failure for jmp '%s'\n",
+ po->operand[0].name);
+ fprintf(fout, " goto *jt_%s[%s];", buf1, buf2);
+ break;
+ }
+ else if (po->operand[0].type != OPT_LABEL)
+ ferr(po, "unhandled jmp type\n");
+
+ fprintf(fout, " goto %s;", po->operand[0].name);
+ break;
+
+ case OP_CALL:
+ assert_operand_cnt(1);
+ pp = po->pp;
+ my_assert_not(pp, NULL);
+
+ strcpy(buf3, " ");
+ if (po->flags & OPF_CC) {
+ // we treat conditional branch to another func
+ // (yes such code exists..) as conditional tailcall
+ strcat(buf3, " ");
+ fprintf(fout, " {\n");
+ }
+
+ if (pp->is_fptr && !pp->is_arg) {
+ fprintf(fout, "%s%s = %s;\n", buf3, pp->name,
+ out_src_opr(buf1, sizeof(buf1), po, &po->operand[0],
+ "(void *)", 0));
+ if (pp->is_unresolved)
+ fprintf(fout, "%sunresolved_call(\"%s:%d\", %s);\n",
+ buf3, asmfn, po->asmln, pp->name);
+ }
+
+ fprintf(fout, "%s", buf3);
+ if (strstr(pp->ret_type.name, "int64")) {
+ if (po->flags & OPF_TAIL)
+ ferr(po, "int64 and tail?\n");
+ fprintf(fout, "tmp64 = ");
+ }
+ else if (!IS(pp->ret_type.name, "void")) {
+ if (po->flags & OPF_TAIL) {
+ if (!IS(g_func_pp->ret_type.name, "void")) {
+ fprintf(fout, "return ");
+ if (g_func_pp->ret_type.is_ptr != pp->ret_type.is_ptr)
+ fprintf(fout, "(%s)", g_func_pp->ret_type.name);
+ }
+ }
+ else if (regmask & (1 << xAX)) {
+ fprintf(fout, "eax = ");
+ if (pp->ret_type.is_ptr)
+ fprintf(fout, "(u32)");
+ }
+ }
+
+ if (pp->name[0] == 0)
+ ferr(po, "missing pp->name\n");
+ fprintf(fout, "%s%s(", pp->name,
+ 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");
+ if (g_func_pp->has_retreg)
+ ferr(po, "TODO: retreg+tailcall\n");
+
+ for (arg = j = 0; arg < pp->argc; arg++) {
+ if (arg > 0)
+ fprintf(fout, ", ");
+
+ cast[0] = 0;
+ if (pp->arg[arg].type.is_ptr)
+ snprintf(cast, sizeof(cast), "(%s)",
+ pp->arg[arg].type.name);
+
+ if (pp->arg[arg].reg != NULL) {
+ fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
+ continue;
+ }
+ // stack arg
+ for (; j < g_func_pp->argc; j++)
+ if (g_func_pp->arg[j].reg == NULL)
+ break;
+ fprintf(fout, "%sa%d", cast, j + 1);
+ j++;
+ }
+ }
+ else {
+ for (arg = 0; arg < pp->argc; arg++) {
+ if (arg > 0)
+ fprintf(fout, ", ");
+
+ cast[0] = 0;
+ if (pp->arg[arg].type.is_ptr)
+ snprintf(cast, sizeof(cast), "(%s)",
+ pp->arg[arg].type.name);
+
+ if (pp->arg[arg].reg != NULL) {
+ if (pp->arg[arg].type.is_retreg)
+ fprintf(fout, "&%s", pp->arg[arg].reg);
+ else
+ fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
+ continue;
+ }
+
+ // stack arg
+ tmp_op = pp->arg[arg].datap;
+ if (tmp_op == NULL)
+ ferr(po, "parsed_op missing for arg%d\n", arg);
+
+ if (tmp_op->flags & OPF_VAPUSH) {
+ fprintf(fout, "ap");
+ }
+ else if (tmp_op->p_argpass != 0) {
+ fprintf(fout, "a%d", tmp_op->p_argpass);
+ }
+ else if (tmp_op->p_argnum != 0) {
+ fprintf(fout, "%s%s", cast,
+ saved_arg_name(buf1, sizeof(buf1),
+ tmp_op->p_arggrp, tmp_op->p_argnum));
+ }
+ else {
+ fprintf(fout, "%s",
+ out_src_opr(buf1, sizeof(buf1),
+ tmp_op, &tmp_op->operand[0], cast, 0));
+ }
+ }
+ }
+ fprintf(fout, ");");
+
+ if (strstr(pp->ret_type.name, "int64")) {
+ fprintf(fout, "\n");
+ fprintf(fout, "%sedx = tmp64 >> 32;\n", buf3);
+ fprintf(fout, "%seax = tmp64;", buf3);
+ }
+
+ if (pp->is_unresolved) {
+ snprintf(buf2, sizeof(buf2), " unresolved %dreg",
+ pp->argc_reg);
+ strcat(g_comment, buf2);
+ }
+
+ if (po->flags & OPF_TAIL) {
+ ret = 0;
+ if (i == opcnt - 1 || pp->is_noreturn)
+ ret = 0;
+ else if (IS(pp->ret_type.name, "void"))
+ ret = 1;
+ else if (IS(g_func_pp->ret_type.name, "void"))
+ ret = 1;
+ // else already handled as 'return f()'
+
+ if (ret) {
+ if (!IS(g_func_pp->ret_type.name, "void")) {
+ ferr(po, "int func -> void func tailcall?\n");
+ }
+ else {
+ fprintf(fout, "\n%sreturn;", buf3);
+ strcat(g_comment, " ^ tailcall");
+ }
+ }
+ else
+ strcat(g_comment, " tailcall");
+ }
+ if (pp->is_noreturn)
+ strcat(g_comment, " noreturn");
+ if ((po->flags & OPF_ATAIL) && pp->argc_stack > 0)
+ strcat(g_comment, " argframe");
+ if (po->flags & OPF_CC)
+ strcat(g_comment, " cond");
+
+ if (po->flags & OPF_CC)
+ fprintf(fout, "\n }");
+
+ delayed_flag_op = NULL;
+ last_arith_dst = NULL;
+ break;
+
+ case OP_RET:
+ if (g_func_pp->is_vararg)
+ fprintf(fout, " va_end(ap);\n");
+ if (g_func_pp->has_retreg) {
+ for (arg = 0; arg < g_func_pp->argc; arg++)
+ if (g_func_pp->arg[arg].type.is_retreg)
+ fprintf(fout, " *r_%s = %s;\n",
+ g_func_pp->arg[arg].reg, g_func_pp->arg[arg].reg);
+ }
+
+ if (IS(g_func_pp->ret_type.name, "void")) {
+ if (i != opcnt - 1 || label_pending)
+ fprintf(fout, " return;");
+ }
+ else if (g_func_pp->ret_type.is_ptr) {
+ fprintf(fout, " return (%s)eax;",
+ g_func_pp->ret_type.name);
+ }
+ else if (IS(g_func_pp->ret_type.name, "__int64"))
+ fprintf(fout, " return ((u64)edx << 32) | eax;");
+ else
+ fprintf(fout, " return eax;");
+
+ last_arith_dst = NULL;
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_PUSH:
+ out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
+ if (po->p_argnum != 0) {
+ // special case - saved func arg
+ fprintf(fout, " %s = %s;",
+ saved_arg_name(buf2, sizeof(buf2),
+ po->p_arggrp, po->p_argnum), buf1);
+ break;
+ }
+ else if (po->flags & OPF_RSAVE) {
+ fprintf(fout, " s_%s = %s;", buf1, buf1);
+ break;
+ }
+ else if (g_func_pp->is_userstack) {
+ fprintf(fout, " *(--esp) = %s;", buf1);
+ break;
+ }
+ if (!(g_ida_func_attr & IDAFA_NORETURN))
+ ferr(po, "stray push encountered\n");
+ no_output = 1;
+ break;
+
+ case OP_POP:
+ if (po->flags & OPF_RSAVE) {
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ fprintf(fout, " %s = s_%s;", buf1, buf1);
+ break;
+ }
+ else if (po->datap != NULL) {
+ // push/pop pair
+ tmp_op = po->datap;
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ fprintf(fout, " %s = %s;", buf1,
+ out_src_opr(buf2, sizeof(buf2),
+ tmp_op, &tmp_op->operand[0],
+ default_cast_to(buf3, sizeof(buf3), &po->operand[0]), 0));
+ break;
+ }
+ else if (g_func_pp->is_userstack) {
+ fprintf(fout, " %s = *esp++;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
+ break;
+ }
+ else
+ ferr(po, "stray pop encountered\n");
+ break;
+
+ case OP_NOP:
+ no_output = 1;
+ break;
+
+ // mmx
+ case OP_EMMS:
+ strcpy(g_comment, "(emms)");
+ break;
+
+ default:
+ no_output = 1;
+ ferr(po, "unhandled op type %d, flags %x\n",
+ po->op, po->flags);
+ break;
+ }
+
+ if (g_comment[0] != 0) {
+ char *p = g_comment;
+ while (my_isblank(*p))
+ p++;
+ fprintf(fout, " // %s", p);
+ g_comment[0] = 0;
+ no_output = 0;
+ }
+ if (!no_output)
+ fprintf(fout, "\n");
+
+ // some sanity checking
+ if (po->flags & OPF_REP) {
+ if (po->op != OP_STOS && po->op != OP_MOVS
+ && po->op != OP_CMPS && po->op != OP_SCAS)
+ ferr(po, "unexpected rep\n");
+ if (!(po->flags & (OPF_REPZ|OPF_REPNZ))
+ && (po->op == OP_CMPS || po->op == OP_SCAS))
+ ferr(po, "cmps/scas with plain rep\n");
+ }
+ if ((po->flags & (OPF_REPZ|OPF_REPNZ))
+ && po->op != OP_CMPS && po->op != OP_SCAS)
+ ferr(po, "unexpected repz/repnz\n");
+
+ if (pfomask != 0)
+ ferr(po, "missed flag calc, pfomask=%x\n", pfomask);
+
+ // see is delayed flag stuff is still valid
+ if (delayed_flag_op != NULL && delayed_flag_op != po) {
+ if (is_any_opr_modified(delayed_flag_op, po, 0))
+ delayed_flag_op = NULL;
+ }
+
+ if (last_arith_dst != NULL && last_arith_dst != &po->operand[0]) {
+ if (is_opr_modified(last_arith_dst, po))
+ last_arith_dst = NULL;
+ }
+
+ label_pending = 0;
+ }
+
+ if (g_stack_fsz && !g_stack_frame_used)
+ fprintf(fout, " (void)sf;\n");
+
+ fprintf(fout, "}\n\n");
+
+ gen_x_cleanup(opcnt);
+}
+
+static void gen_x_cleanup(int opcnt)
+{
+ int i;
+
+ for (i = 0; i < opcnt; i++) {
+ struct label_ref *lr, *lr_del;
+
+ lr = g_label_refs[i].next;
+ while (lr != NULL) {
+ lr_del = lr;
+ lr = lr->next;
+ free(lr_del);
+ }
+ g_label_refs[i].i = -1;
+ g_label_refs[i].next = NULL;
+
+ if (ops[i].op == OP_CALL) {
+ if (ops[i].pp)
+ proto_release(ops[i].pp);
+ }
+ }
+ g_func_pp = NULL;
+}
+
+struct func_proto_dep;
+
+struct func_prototype {
+ char name[NAMELEN];
+ int id;
+ int argc_stack;
+ int regmask_dep;
+ int has_ret:3; // -1, 0, 1: unresolved, no, yes
+ unsigned int dep_resolved:1;
+ unsigned int is_stdcall:1;
+ struct func_proto_dep *dep_func;
+ int dep_func_cnt;
+ const struct parsed_proto *pp; // seed pp, if any
+};
+
+struct func_proto_dep {
+ char *name;
+ struct func_prototype *proto;
+ int regmask_live; // .. at the time of call
+ unsigned int ret_dep:1; // return from this is caller's return
+};
+
+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;
+ unsigned int is_c_str:1;
+ const struct parsed_proto *pp; // seed pp, if any
+} *hg_vars;
+static int hg_var_cnt;
+
+static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
+ int count);
+
+static struct func_proto_dep *hg_fp_find_dep(struct func_prototype *fp,
+ const char *name)
+{
+ int i;
+
+ for (i = 0; i < fp->dep_func_cnt; i++)
+ if (IS(fp->dep_func[i].name, name))
+ return &fp->dep_func[i];
+
+ return NULL;
+}
+
+static void hg_fp_add_dep(struct func_prototype *fp, const char *name)
+{
+ // is it a dupe?
+ if (hg_fp_find_dep(fp, name))
+ return;
+
+ if ((fp->dep_func_cnt & 0xff) == 0) {
+ fp->dep_func = realloc(fp->dep_func,
+ sizeof(fp->dep_func[0]) * (fp->dep_func_cnt + 0x100));
+ my_assert_not(fp->dep_func, NULL);
+ memset(&fp->dep_func[fp->dep_func_cnt], 0,
+ sizeof(fp->dep_func[0]) * 0x100);
+ }
+ fp->dep_func[fp->dep_func_cnt].name = strdup(name);
+ fp->dep_func_cnt++;
+}
+
+static int hg_fp_cmp_name(const void *p1_, const void *p2_)
+{
+ const struct func_prototype *p1 = p1_, *p2 = p2_;
+ return strcmp(p1->name, p2->name);
+}
+
+#if 0
+static int hg_fp_cmp_id(const void *p1_, const void *p2_)
+{
+ const struct func_prototype *p1 = p1_, *p2 = p2_;
+ return p1->id - p2->id;
+}
+#endif
+
+// recursive register dep pass
+// - track saved regs (part 2)
+// - try to figure out arg-regs
+// - calculate reg deps
+static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits,
+ struct func_prototype *fp, int regmask_save, int regmask_dst,
+ int *regmask_dep, int *has_ret)
+{
+ struct func_proto_dep *dep;
+ struct parsed_op *po;
+ int from_caller = 0;
+ int depth;
+ int j, l;
+ int reg;
+ int ret;
+
+ for (; i < opcnt; i++)
+ {
+ if (cbits[i >> 3] & (1 << (i & 7)))
+ return;
+ cbits[i >> 3] |= (1 << (i & 7));
+
+ po = &ops[i];
+
+ if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
+ if (po->btj != NULL) {
+ // jumptable
+ for (j = 0; j < po->btj->count; j++) {
+ gen_hdr_dep_pass(po->btj->d[j].bt_i, opcnt, cbits, fp,
+ regmask_save, regmask_dst, regmask_dep, has_ret);
+ }
+ return;
+ }
+
+ if (po->bt_i < 0) {
+ ferr(po, "dead branch\n");
+ return;
+ }
+
+ if (po->flags & OPF_CJMP) {
+ gen_hdr_dep_pass(po->bt_i, opcnt, cbits, fp,
+ regmask_save, regmask_dst, regmask_dep, has_ret);
+ }
+ else {
+ i = po->bt_i - 1;
+ }
+ continue;
+ }
+
+ if (po->flags & OPF_FARG)
+ /* (just calculate register deps) */;
+ else if (po->op == OP_PUSH && po->operand[0].type == OPT_REG)
+ {
+ reg = po->operand[0].reg;
+ if (reg < 0)
+ ferr(po, "reg not set for push?\n");
+
+ if (po->flags & OPF_RSAVE) {
+ regmask_save |= 1 << reg;
+ continue;
+ }
+ if (po->flags & OPF_DONE)
+ continue;
+
+ depth = 0;
+ ret = scan_for_pop(i + 1, opcnt,
+ po->operand[0].name, i + opcnt * 2, 0, &depth, 0);
+ if (ret == 1) {
+ regmask_save |= 1 << reg;
+ po->flags |= OPF_RMD;
+ scan_for_pop(i + 1, opcnt,
+ po->operand[0].name, i + opcnt * 3, 0, &depth, 1);
+ continue;
+ }
+ }
+ else if (po->flags & OPF_RMD)
+ continue;
+ else if (po->op == OP_CALL) {
+ po->regmask_dst |= 1 << xAX;
+
+ dep = hg_fp_find_dep(fp, po->operand[0].name);
+ if (dep != NULL)
+ dep->regmask_live = regmask_save | regmask_dst;
+ }
+ else if (po->op == OP_RET) {
+ if (po->operand_cnt > 0) {
+ fp->is_stdcall = 1;
+ if (fp->argc_stack >= 0
+ && fp->argc_stack != po->operand[0].val / 4)
+ ferr(po, "ret mismatch? (%d)\n", fp->argc_stack * 4);
+ fp->argc_stack = po->operand[0].val / 4;
+ }
+ }
+
+ if (*has_ret != 0 && (po->flags & OPF_TAIL)) {
+ if (po->op == OP_CALL) {
+ j = i;
+ ret = 1;
+ }
+ else {
+ struct parsed_opr opr = { 0, };
+ opr.type = OPT_REG;
+ opr.reg = xAX;
+ j = -1;
+ from_caller = 0;
+ ret = resolve_origin(i, &opr, i + opcnt * 4, &j, &from_caller);
+ }
+
+ if (ret == -1 && from_caller) {
+ // unresolved eax - probably void func
+ *has_ret = 0;
+ }
+ else {
+ if (ops[j].op == OP_CALL) {
+ dep = hg_fp_find_dep(fp, po->operand[0].name);
+ if (dep != NULL)
+ dep->ret_dep = 1;
+ else
+ *has_ret = 1;
+ }
+ else
+ *has_ret = 1;
+ }
+ }
+
+ l = regmask_save | regmask_dst;
+ if (g_bp_frame && !(po->flags & OPF_EBP_S))
+ l |= 1 << xBP;
+
+ l = po->regmask_src & ~l;
+#if 0
+ if (l)
+ fnote(po, "dep |= %04x, dst %04x, save %04x (f %x)\n",
+ l, regmask_dst, regmask_save, po->flags);
+#endif
+ *regmask_dep |= l;
+ regmask_dst |= po->regmask_dst;
+
+ if (po->flags & OPF_TAIL)
+ return;
+ }
+}
+
+static void gen_hdr(const char *funcn, int opcnt)
+{
+ int save_arg_vars[MAX_ARG_GRP] = { 0, };
+ unsigned char cbits[MAX_OPS / 8];
+ const struct parsed_proto *pp_c;
+ struct parsed_proto *pp;
+ struct func_prototype *fp;
+ struct parsed_data *pd;
+ struct parsed_op *po;
+ const char *tmpname;
+ int regmask_dummy = 0;
+ int regmask_dep;
+ int max_bp_offset = 0;
+ int has_ret;
+ int i, j, l, ret;
+
+ if ((hg_fp_cnt & 0xff) == 0) {
+ hg_fp = realloc(hg_fp, sizeof(hg_fp[0]) * (hg_fp_cnt + 0x100));
+ my_assert_not(hg_fp, NULL);
+ memset(hg_fp + hg_fp_cnt, 0, sizeof(hg_fp[0]) * 0x100);
+ }
+
+ fp = &hg_fp[hg_fp_cnt];
+ snprintf(fp->name, sizeof(fp->name), "%s", funcn);
+ fp->id = hg_fp_cnt;
+ fp->argc_stack = -1;
+ hg_fp_cnt++;
+
+ // perhaps already in seed header?
+ fp->pp = proto_parse(g_fhdr, funcn, 1);
+ if (fp->pp != NULL) {
+ fp->argc_stack = fp->pp->argc_stack;
+ fp->is_stdcall = fp->pp->is_stdcall;
+ fp->regmask_dep = get_pp_arg_regmask(fp->pp);
+ fp->has_ret = !IS(fp->pp->ret_type.name, "void");
+ return;
+ }
+
+ g_bp_frame = g_sp_frame = g_stack_fsz = 0;
+ g_stack_frame_used = 0;
+
+ // pass1:
+ // - handle ebp/esp frame, remove ops related to it
+ scan_prologue_epilogue(opcnt);
+
+ // pass2:
+ // - collect calls
+ // - resolve all branches
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ po->bt_i = -1;
+ po->btj = NULL;
+
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ continue;
+
+ if (po->op == OP_CALL) {
+ tmpname = opr_name(po, 0);
+ pp = NULL;
+ if (po->operand[0].type == OPT_LABEL) {
+ hg_fp_add_dep(fp, tmpname);
+
+ // perhaps a call to already known func?
+ pp_c = proto_parse(g_fhdr, tmpname, 1);
+ if (pp_c != NULL)
+ pp = proto_clone(pp_c);
+ }
+ else if (po->datap != NULL) {
+ pp = calloc(1, sizeof(*pp));
+ my_assert_not(pp, NULL);
+
+ ret = parse_protostr(po->datap, pp);
+ if (ret < 0)
+ ferr(po, "bad protostr supplied: %s\n", (char *)po->datap);
+ free(po->datap);
+ po->datap = NULL;
+ }
+ if (pp != NULL && pp->is_noreturn)
+ po->flags |= OPF_TAIL;
+
+ po->pp = pp;
+ continue;
+ }
+
+ if (!(po->flags & OPF_JMP) || po->op == OP_RET)
+ continue;
+
+ if (po->operand[0].type == OPT_REGMEM) {
+ pd = try_resolve_jumptab(i, opcnt);
+ if (pd == NULL)
+ goto tailcall;
+
+ po->btj = pd;
+ continue;
+ }
+
+ for (l = 0; l < opcnt; l++) {
+ if (g_labels[l] != NULL
+ && IS(po->operand[0].name, g_labels[l]))
+ {
+ add_label_ref(&g_label_refs[l], i);
+ po->bt_i = l;
+ break;
+ }
+ }
+
+ if (po->bt_i != -1 || (po->flags & OPF_RMD))
+ continue;
+
+ if (po->operand[0].type == OPT_LABEL)
+ // assume tail call
+ goto tailcall;
+
+ ferr(po, "unhandled branch\n");
+
+tailcall:
+ po->op = OP_CALL;
+ po->flags |= OPF_TAIL;
+ if (i > 0 && ops[i - 1].op == OP_POP)
+ po->flags |= OPF_ATAIL;
+ i--; // reprocess
+ }
+
+ // pass3:
+ // - remove dead labels
+ // - handle push <const>/pop pairs
+ for (i = 0; i < opcnt; i++)
+ {
+ if (g_labels[i] != NULL && g_label_refs[i].i == -1) {
+ free(g_labels[i]);
+ g_labels[i] = NULL;
+ }
+
+ po = &ops[i];
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ continue;
+
+ if (po->op == OP_PUSH && po->operand[0].type == OPT_CONST)
+ scan_for_pop_const(i, opcnt);
+ }
+
+ // pass4:
+ // - process trivial calls
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ 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_dummy) != 0)
+ pp = NULL;
+ }
+
+ if (pp != NULL) {
+ if (j >= 0) {
+ // commit esp adjust
+ ops[j].flags |= OPF_RMD;
+ if (ops[j].op != OP_POP)
+ patch_esp_adjust(&ops[j], pp->argc_stack * 4);
+ else
+ ops[j].flags |= OPF_DONE;
+ }
+
+ po->flags |= OPF_DONE;
+ }
+ }
+ }
+
+ // pass5:
+ // - track saved regs (simple)
+ // - process calls
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ continue;
+
+ if (po->op == OP_PUSH && po->operand[0].type == OPT_REG)
+ {
+ ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, 0);
+ if (ret == 0) {
+ // regmask_save |= 1 << po->operand[0].reg; // do it later
+ po->flags |= OPF_RSAVE | OPF_RMD | OPF_DONE;
+ scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, OPF_RMD);
+ }
+ }
+ else if (po->op == OP_CALL && !(po->flags & OPF_DONE))
+ {
+ pp = process_call(i, opcnt);
+
+ if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) {
+ // since we know the args, collect them
+ ret = collect_call_args(po, i, pp, ®mask_dummy, save_arg_vars,
+ i + opcnt * 1);
+ }
+ }
+ }
+
+ // pass6
+ memset(cbits, 0, sizeof(cbits));
+ regmask_dep = 0;
+ has_ret = -1;
+
+ gen_hdr_dep_pass(0, opcnt, cbits, fp, 0, 0, ®mask_dep, &has_ret);
+
+ // find unreachable code - must be fixed in IDA
+ for (i = 0; i < opcnt; i++)
+ {
+ if (cbits[i >> 3] & (1 << (i & 7)))
+ continue;
+
+ if (ops[i].op != OP_NOP)
+ ferr(&ops[i], "unreachable code\n");
+ }
+
+ if (has_ret == -1 && (regmask_dep & (1 << xAX)))
+ has_ret = 1;
+
+ for (i = 0; i < g_eqcnt; i++) {
+ if (g_eqs[i].offset > max_bp_offset && g_eqs[i].offset < 4*32)
+ max_bp_offset = g_eqs[i].offset;
+ }
+
+ if (fp->argc_stack < 0) {
+ max_bp_offset = (max_bp_offset + 3) & ~3;
+ fp->argc_stack = max_bp_offset / 4;
+ if ((g_ida_func_attr & IDAFA_BP_FRAME) && fp->argc_stack > 0)
+ fp->argc_stack--;
+ }
+
+ fp->regmask_dep = regmask_dep & ~(1 << xSP);
+ fp->has_ret = has_ret;
+#if 0
+ printf("// has_ret %d, regmask_dep %x\n",
+ fp->has_ret, fp->regmask_dep);
+ output_hdr_fp(stdout, fp, 1);
+ if (IS(funcn, "sub_100073FD")) exit(1);
+#endif
+
+ gen_x_cleanup(opcnt);
+}
+
+static void hg_fp_resolve_deps(struct func_prototype *fp)
+{
+ struct func_prototype fp_s;
+ int dep;
+ int i;
+
+ // this thing is recursive, so mark first..
+ fp->dep_resolved = 1;
+
+ for (i = 0; i < fp->dep_func_cnt; i++) {
+ strcpy(fp_s.name, fp->dep_func[i].name);
+ fp->dep_func[i].proto = bsearch(&fp_s, hg_fp, hg_fp_cnt,
+ sizeof(hg_fp[0]), hg_fp_cmp_name);
+ if (fp->dep_func[i].proto != NULL) {
+ if (!fp->dep_func[i].proto->dep_resolved)
+ hg_fp_resolve_deps(fp->dep_func[i].proto);
+
+ dep = ~fp->dep_func[i].regmask_live
+ & fp->dep_func[i].proto->regmask_dep;
+ fp->regmask_dep |= dep;
+ // printf("dep %s %s |= %x\n", fp->name,
+ // fp->dep_func[i].name, dep);
+
+ if (fp->has_ret == -1)
+ fp->has_ret = fp->dep_func[i].proto->has_ret;
+ }
+ }
+}
+
+static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
+ int count)
+{
+ const struct parsed_proto *pp;
+ char *p, namebuf[NAMELEN];
+ const char *name;
+ int regmask_dep;
+ int argc_stack;
+ int j, arg;
+
+ for (; count > 0; count--, fp++) {
+ if (fp->has_ret == -1)
+ fprintf(fout, "// ret unresolved\n");
+#if 0
+ fprintf(fout, "// dep:");
+ for (j = 0; j < fp->dep_func_cnt; j++) {
+ fprintf(fout, " %s/", fp->dep_func[j].name);
+ if (fp->dep_func[j].proto != NULL)
+ fprintf(fout, "%04x/%d", fp->dep_func[j].proto->regmask_dep,
+ fp->dep_func[j].proto->has_ret);
+ }
+ fprintf(fout, "\n");
+#endif
+
+ p = strchr(fp->name, '@');
+ if (p != NULL) {
+ memcpy(namebuf, fp->name, p - fp->name);
+ namebuf[p - fp->name] = 0;
+ name = namebuf;
+ }
+ else
+ name = fp->name;
+ if (name[0] == '_')
+ name++;
+
+ pp = proto_parse(g_fhdr, name, 1);
+ if (pp != NULL && pp->is_include)
+ continue;
+
+ if (fp->pp != NULL) {
+ // prefer fp for common style,
+ // only use output_pp if args are complex
+ for (j = 0; j < fp->pp->argc; j++) {
+ if (fp->pp->arg[j].fptr != NULL)
+ break;
+ }
+ if (j != fp->pp->argc) {
+ output_pp(fout, fp->pp, OPP_ALIGN);
+ fprintf(fout, ";\n");
+ continue;
+ }
+ }
+
+ regmask_dep = fp->regmask_dep;
+ argc_stack = 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)
+ {
+ fprintf(fout, " __fastcall ");
+ if (!(regmask_dep & (1 << xDX)) && argc_stack == 0)
+ argc_stack = 1;
+ else
+ argc_stack += 2;
+ regmask_dep = 0;
+ }
+ else if (regmask_dep && !fp->is_stdcall) {
+ fprintf(fout, "/*__usercall*/ ");
+ }
+ else if (regmask_dep) {
+ fprintf(fout, "/*__userpurge*/ ");
+ }
+ else if (fp->is_stdcall)
+ fprintf(fout, " __stdcall ");
+ else
+ fprintf(fout, " __cdecl ");