+ else {
+ assert_operand_cnt(2);
+ 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
+ j = lmod_bytes(po, po->operand[1].lmod);
+ l = (po->flags & OPF_DF) ? '-' : '+';
+ if (po->flags & OPF_REP) {
+ assert_operand_cnt(3);
+ fprintf(fout,
+ " while (ecx != 0) {\n");
+ fprintf(fout,
+ " cond_z = (%seax == %sedi); edi %c= %d;\n",
+ lmod_cast_u(po, po->operand[1].lmod),
+ lmod_cast_u_ptr(po, po->operand[1].lmod), l, j);
+ fprintf(fout,
+ " ecx--;\n"
+ " 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 {
+ assert_operand_cnt(2);
+ fprintf(fout, " cond_z = (%seax == %sedi); edi %c= %d;",
+ lmod_cast_u(po, po->operand[1].lmod),
+ lmod_cast_u_ptr(po, po->operand[1].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:
+ if (po->operand[1].type == OPT_CONST && !po->operand[1].val)
+ goto dualop_arith_const;
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ goto dualop_arith;
+
+ case OP_OR:
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ if (po->operand[1].type == OPT_CONST) {
+ j = lmod_bytes(po, po->operand[0].lmod);
+ if (((1ull << j * 8) - 1) == po->operand[1].val)
+ goto dualop_arith_const;
+ }
+ goto dualop_arith;
+
+ 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;
+
+ dualop_arith_const:
+ // and 0, or ~0 used instead mov
+ assert_operand_cnt(2);
+ 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));
+ 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]));
+ if (po->operand[1].type != OPT_CONST)
+ fprintf(fout, " & 0x1f");
+ fprintf(fout, ";");
+ 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_SHLD:
+ 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_src_opr_u32(buf3, sizeof(buf3), po, &po->operand[2]);
+ if (po->operand[2].type != OPT_CONST) {
+ // no handling for "undefined" case, hopefully not needed
+ snprintf(buf2, sizeof(buf2), "(%s & 0x1f)", buf3);
+ strcpy(buf3, buf2);
+ }
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ if (po->op == OP_SHLD) {
+ fprintf(fout, " %s <<= %s; %s |= %s >> (%d - %s);",
+ buf1, buf3, buf1, buf2, l, buf3);
+ strcpy(g_comment, "shld");
+ }
+ else {
+ 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
+ int z = PFOB_O | PFOB_C | PFOB_S | (1 << PFO_L);
+ for (j = 0; j <= PFO_LE; j++) {
+ if (pfomask & (1 << j)) {
+ fprintf(fout, " cond_%s = %d;\n",
+ parsed_flag_op_names[j], (1 << j) & z ? 0 : 1);
+ pfomask &= ~(1 << j);
+ }
+ }
+ 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;
+ }
+ 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:
+ 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, 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, 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 & PFOB_C) {
+ fprintf(fout, "\n cond_c = (%s != 0);", buf1);
+ pfomask &= ~PFOB_C;
+ }
+ output_std_flags(fout, po, &pfomask, buf1);
+ 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);
+ out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
+ strcpy(cast, 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(buf2, sizeof(buf2), "%seax", cast);
+ else {
+ fprintf(fout, " tmp64 = ((u64)edx << 32) | eax;\n");
+ snprintf(buf2, sizeof(buf2), "%stmp64",
+ (po->op == OP_IDIV) ? "(s64)" : "");
+ }
+ if (po->operand[0].type == OPT_REG
+ && po->operand[0].reg == xDX)
+ {
+ fprintf(fout, " eax = %s / %s%s;\n", buf2, cast, buf1);
+ fprintf(fout, " edx = %s %% %s%s;", buf2, cast, buf1);
+ }
+ else {
+ fprintf(fout, " edx = %s %% %s%s;\n", buf2, cast, buf1);
+ fprintf(fout, " eax = %s / %s%s;", buf2, cast, buf1);
+ }
+ break;
+ case OPLM_WORD:
+ fprintf(fout, " tmp = (edx << 16) | (eax & 0xffff);\n");
+ snprintf(buf2, sizeof(buf2), "%stmp",
+ (po->op == OP_IDIV) ? "(s32)" : "");
+ if (po->operand[0].type == OPT_REG
+ && po->operand[0].reg == xDX)
+ {
+ fprintf(fout, " LOWORD(eax) = %s / %s%s;\n",
+ buf2, cast, buf1);
+ fprintf(fout, " LOWORD(edx) = %s %% %s%s;",
+ buf2, cast, buf1);
+ }
+ else {
+ fprintf(fout, " LOWORD(edx) = %s %% %s%s;\n",
+ buf2, cast, buf1);
+ fprintf(fout, " LOWORD(eax) = %s / %s%s;",
+ buf2, cast, buf1);
+ }
+ strcat(g_comment, " div16");
+ break;
+ default:
+ ferr(po, "unhandled div lmod %d\n", po->operand[0].lmod);
+ }
+ 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_LOOP:
+ fprintf(fout, " if (--ecx != 0)\n");
+ fprintf(fout, " goto %s;", po->operand[0].name);
+ strcat(g_comment, " loop");
+ 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 (regmask_ret & mxAX) {
+ 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_ret & mxST0)
+ ferr(po, "float tailcall\n");
+ }
+ else if (po->regmask_dst & mxAX) {
+ fprintf(fout, "eax = ");
+ if (pp->ret_type.is_ptr)
+ fprintf(fout, "(u32)");
+ }
+ else if (po->regmask_dst & mxST0) {
+ ferr_assert(po, po->flags & OPF_FPUSH);
+ if (need_float_stack)
+ fprintf(fout, "f_st[--f_stp & 7] = ");
+ else
+ fprintf(fout, "f_st0 = ");
+ }
+ }
+
+ 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 if (IS(pp->arg[arg].reg, "ebp")
+ && g_bp_frame && !(po->flags & OPF_EBP_S))
+ {
+ // rare special case
+ fprintf(fout, "%s(u32)&sf.b[sizeof(sf)]", cast);
+ strcat(g_comment, " bp_ref");
+ }
+ 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->op == OP_FST) {
+ fprintf(fout, "fs_%d", tmp_op->p_argnum);
+ if (tmp_op->operand[0].lmod == OPLM_QWORD)
+ arg++;
+ }
+ else if (tmp_op->p_argpass != 0) {
+ fprintf(fout, "a%d", tmp_op->p_argpass);
+ }
+ else if (pp->arg[arg].is_saved) {
+ ferr_assert(po, 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 (!(regmask_ret & (1 << xAX)))
+ ret = 1;
+ // else already handled as 'return f()'
+
+ if (ret) {
+ fprintf(fout, "\n%sreturn;", buf3);
+ strcat(g_comment, " ^ tailcall");
+ }
+ else
+ strcat(g_comment, " tailcall");
+
+ if ((regmask_ret & (1 << xAX))
+ && IS(pp->ret_type.name, "void") && !pp->is_noreturn)
+ {
+ ferr(po, "int func -> void func tailcall?\n");
+ }
+ }
+ 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 (regmask_ret & mxST0) {
+ fprintf(fout, " return %s;", float_st0);
+ }
+ else if (!(regmask_ret & mxAX)) {
+ 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 (po->flags & OPF_PPUSH) {
+ tmp_op = po->datap;
+ ferr_assert(po, tmp_op != NULL);
+ out_dst_opr(buf2, sizeof(buf2), po, &tmp_op->operand[0]);
+ fprintf(fout, " pp_%s = %s;", buf2, 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:
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ if (po->flags & OPF_RSAVE) {
+ fprintf(fout, " %s = s_%s;", buf1, buf1);
+ break;
+ }
+ else if (po->flags & OPF_PPUSH) {
+ // push/pop graph / non-const
+ ferr_assert(po, po->datap == NULL);
+ fprintf(fout, " %s = pp_%s;", buf1, buf1);
+ break;
+ }
+ else if (po->datap != NULL) {
+ // push/pop pair
+ tmp_op = po->datap;
+ 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++;", buf1);
+ break;
+ }
+ else
+ ferr(po, "stray pop encountered\n");
+ break;
+
+ case OP_NOP:
+ 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 (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));
+ }
+ strcat(g_comment, " fld");
+ break;
+
+ case OP_FILD:
+ 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 (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_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_FARG) {
+ // store to stack as func arg
+ snprintf(buf1, sizeof(buf1), "fs_%d", po->p_argnum);
+ dead_dst = 0;
+ }
+ else {
+ out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0],
+ need_float_stack);
+ dead_dst = po->operand[0].type == OPT_REG
+ && po->operand[0].reg == xST0;
+ }
+ if (!dead_dst)
+ fprintf(fout, " %s = %s;", buf1, float_st0);
+ if (po->flags & OPF_FSHIFT) {
+ if (need_float_stack)
+ fprintf(fout, " f_stp++;");
+ else
+ fprintf(fout, " f_st0 = f_st1;");
+ }
+ if (dead_dst && !(po->flags & OPF_FSHIFT))
+ no_output = 1;
+ 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;");
+ }
+ 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;
+ case OP_FMUL: j = '*'; break;
+ case OP_FSUB: j = '-'; break;
+ default: j = 'x'; break;
+ }
+ 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 {
+ 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:
+ 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:
+ case OP_FIDIV:
+ case OP_FIMUL:
+ case OP_FISUB:
+ switch (po->op) {
+ case OP_FIADD: j = '+'; break;
+ case OP_FIDIV: j = '/'; break;
+ case OP_FIMUL: j = '*'; break;
+ case OP_FISUB: j = '-'; break;
+ default: j = 'x'; break;
+ }
+ 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, " %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");
+ }
+ strcat(g_comment, " fyl2x");
+ 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)%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;
+
+ case OPP_CIPOW:
+ if (need_float_stack) {
+ fprintf(fout, " %s = pow%s(%s, %s);", float_st1,
+ need_double ? "" : "f", float_st1, float_st0);
+ fprintf(fout, " f_stp++;");
+ }
+ else {
+ fprintf(fout, " f_st0 = pow%s(f_st1, f_st0);",
+ need_double ? "" : "f");
+ }
+ strcat(g_comment, " CIpow");
+ break;
+
+ case OPP_ABORT:
+ fprintf(fout, " do_skip_code_abort();");
+ break;
+
+ // mmx
+ case OP_EMMS:
+ fprintf(fout, " do_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 char **hg_refs;
+static int hg_ref_cnt;
+
+static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
+ int count);
+
+static struct func_prototype *hg_fp_add(const char *funcn)
+{
+ struct func_prototype *fp;
+
+ 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++;
+
+ return fp;
+}
+
+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
+
+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
+// - 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 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->flags & OPF_RMD)
+ continue;
+
+ if (po->btj != NULL) {
+ // jumptable
+ for (j = 0; j < po->btj->count; j++) {
+ check_i(po, po->btj->d[j].bt_i);
+ gen_hdr_dep_pass(po->btj->d[j].bt_i, opcnt, cbits, fp,
+ regmask_save, regmask_dst, regmask_dep, has_ret);
+ }
+ return;
+ }
+
+ check_i(po, po->bt_i);
+ 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;
+ ferr_assert(po, reg >= 0);
+
+ if (po->flags & OPF_RSAVE) {
+ regmask_save |= 1 << reg;
+ continue;
+ }
+ if (po->flags & OPF_DONE)
+ continue;
+
+ ret = scan_for_pop(i + 1, opcnt, i + opcnt * 2, reg, 0, 0, 0);
+ if (ret == 1) {
+ regmask_save |= 1 << reg;
+ po->flags |= OPF_RMD;
+ scan_for_pop(i + 1, opcnt, i + opcnt * 3, reg, 0, 0, OPF_RMD);
+ 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;
+ if (g_bp_frame && !(po->flags & OPF_EBP_S))
+ dep->regmask_live |= 1 << xBP;
+ }
+ }
+ 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 is 0, there is uninitialized eax path,
+ // which means it's most likely void func
+ if (*has_ret != 0 && (po->flags & OPF_TAIL)) {
+ if (po->op == OP_CALL) {
+ j = i;
+ ret = 1;
+ }
+ else {
+ struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, 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 (j >= 0 && ops[j].op == OP_CALL) {
+ dep = hg_fp_find_dep(fp, ops[j].operand[0].name);
+ if (dep != NULL)
+ dep->ret_dep = 1;
+ else
+ *has_ret = 1;