+ if (po->flags & OPF_NOREGS)
+ continue;
+
+ // 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
+ && po->operand[0].lmod != OPLM_DWORD)
+ {
+ reg = po->operand[0].reg;
+ ferr_assert(po, reg >= 0);
+
+ if (!(regmask_now & (1 << reg)))
+ *regmask_init |= 1 << reg;
+ }
+
+ regmask_op = po->regmask_src | po->regmask_dst;
+
+ regmask_new = po->regmask_src & ~regmask_now & ~regmask_arg;
+ regmask_new &= ~(1 << xSP);
+ 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);
+
+ if (regmask_op & (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_op &= ~(1 << xBP);
+ }
+ }
+
+ regmask_now |= regmask_op;
+ *regmask |= regmask_now;
+
+ if (po->flags & OPF_TAIL)
+ return;
+ }
+}
+
+static void pp_insert_reg_arg(struct parsed_proto *pp, const char *reg)
+{
+ int i;
+
+ for (i = 0; i < pp->argc; i++)
+ if (pp->arg[i].reg == NULL)
+ break;
+
+ if (pp->argc_stack)
+ memmove(&pp->arg[i + 1], &pp->arg[i],
+ sizeof(pp->arg[0]) * pp->argc_stack);
+ memset(&pp->arg[i], 0, sizeof(pp->arg[i]));
+ pp->arg[i].reg = strdup(reg);
+ pp->arg[i].type.name = strdup("int");
+ pp->argc++;
+ pp->argc_reg++;
+}
+
+static void output_std_flags(FILE *fout, struct parsed_op *po,
+ int *pfomask, const char *dst_opr_text)
+{
+ if (*pfomask & (1 << PFO_Z)) {
+ fprintf(fout, "\n cond_z = (%s%s == 0);",
+ lmod_cast_u(po, po->operand[0].lmod), dst_opr_text);
+ *pfomask &= ~(1 << PFO_Z);
+ }
+ if (*pfomask & (1 << PFO_S)) {
+ fprintf(fout, "\n cond_s = (%s%s < 0);",
+ lmod_cast_s(po, po->operand[0].lmod), dst_opr_text);
+ *pfomask &= ~(1 << PFO_S);
+ }
+}
+
+enum {
+ OPP_FORCE_NORETURN = (1 << 0),
+ OPP_SIMPLE_ARGS = (1 << 1),
+ OPP_ALIGN = (1 << 2),
+};
+
+static void output_pp_attrs(FILE *fout, const struct parsed_proto *pp,
+ int flags)
+{
+ const char *cconv = "";
+
+ if (pp->is_fastcall)
+ cconv = "__fastcall ";
+ else if (pp->is_stdcall && pp->argc_reg == 0)
+ cconv = "__stdcall ";
+
+ fprintf(fout, (flags & OPP_ALIGN) ? "%-16s" : "%s", cconv);
+
+ if (pp->is_noreturn || (flags & OPP_FORCE_NORETURN))
+ fprintf(fout, "noreturn ");
+}
+
+static void output_pp(FILE *fout, const struct parsed_proto *pp,
+ int flags)
+{
+ int i;
+
+ fprintf(fout, (flags & OPP_ALIGN) ? "%-5s" : "%s ",
+ pp->ret_type.name);
+ if (pp->is_fptr)
+ fprintf(fout, "(");
+ output_pp_attrs(fout, pp, flags);
+ if (pp->is_fptr)
+ fprintf(fout, "*");
+ fprintf(fout, "%s", pp->name);
+ if (pp->is_fptr)
+ fprintf(fout, ")");
+
+ fprintf(fout, "(");
+ for (i = 0; i < pp->argc; i++) {
+ if (i > 0)
+ fprintf(fout, ", ");
+ if (pp->arg[i].fptr != NULL && !(flags & OPP_SIMPLE_ARGS)) {
+ // func pointer
+ output_pp(fout, pp->arg[i].fptr, 0);
+ }
+ else if (pp->arg[i].type.is_retreg) {
+ fprintf(fout, "u32 *r_%s", pp->arg[i].reg);
+ }
+ else {
+ fprintf(fout, "%s", pp->arg[i].type.name);
+ if (!pp->is_fptr)
+ fprintf(fout, " a%d", i + 1);
+ }
+ }
+ if (pp->is_vararg) {
+ if (i > 0)
+ fprintf(fout, ", ");
+ fprintf(fout, "...");
+ }
+ fprintf(fout, ")");
+}
+
+static char *saved_arg_name(char *buf, size_t buf_size, int grp, int num)
+{
+ char buf1[16];
+
+ buf1[0] = 0;
+ if (grp > 0)
+ snprintf(buf1, sizeof(buf1), "%d", grp);
+ snprintf(buf, buf_size, "s%s_a%d", buf1, num);
+
+ return buf;
+}
+
+static void gen_x_cleanup(int opcnt);
+
+static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
+{
+ struct parsed_op *po, *delayed_flag_op = NULL, *tmp_op;
+ struct parsed_opr *last_arith_dst = NULL;
+ char buf1[256], buf2[256], buf3[256], cast[64];
+ struct parsed_proto *pp, *pp_tmp;
+ struct parsed_data *pd;
+ unsigned int uval;
+ int save_arg_vars[MAX_ARG_GRP] = { 0, };
+ unsigned char cbits[MAX_OPS / 8];
+ int cond_vars = 0;
+ int need_tmp_var = 0;
+ int need_tmp64 = 0;
+ int had_decl = 0;
+ int label_pending = 0;
+ int regmask_save = 0; // 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
+ int regmask_init = 0; // regs that need zero initialization
+ int regmask_pp = 0; // regs used in complex push-pop graph
+ int regmask = 0; // used regs
+ int pfomask = 0;
+ int found = 0;
+ int no_output;
+ int i, j, l;
+ int arg;
+ int reg;
+ int ret;
+
+ g_bp_frame = g_sp_frame = g_stack_fsz = 0;
+ g_stack_frame_used = 0;
+
+ g_func_pp = proto_parse(fhdr, funcn, 0);
+ if (g_func_pp == NULL)
+ ferr(ops, "proto_parse failed for '%s'\n", funcn);
+
+ 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
+ resolve_branches_parse_calls(opcnt);
+
+ // pass2:
+ // - handle ebp/esp frame, remove ops related to it
+ scan_prologue_epilogue(opcnt);
+
+ // pass3:
+ // - remove dead labels
+ // - set regs needed at ret
+ 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;
+ }
+
+ if (ops[i].op == OP_RET)
+ ops[i].regmask_src |= regmask_ret;
+ }
+
+ // 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) != 0)
+ pp = NULL;
+ }
+
+ if (pp != NULL) {
+ if (j >= 0) {
+ // commit esp adjust
+ if (ops[j].op != OP_POP)
+ patch_esp_adjust(&ops[j], pp->argc_stack * 4);
+ else {
+ for (l = 0; l < pp->argc_stack; l++)
+ ops[j + l].flags |= OPF_DONE | OPF_RMD | OPF_NOREGS;
+ }
+ }
+
+ if (strstr(pp->ret_type.name, "int64"))
+ need_tmp64 = 1;
+
+ po->flags |= OPF_DONE;
+ }
+ }
+ }
+
+ // pass5:
+ // - process calls, stage 2
+ // - handle some push/pop pairs
+ // - scan for STD/CLD, propagate DF
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ if (po->flags & OPF_RMD)
+ continue;
+
+ if (po->op == OP_CALL)
+ {
+ if (!(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);
+ }
+ // for unresolved, collect after other passes
+ }
+
+ pp = po->pp;
+ ferr_assert(po, pp != NULL);
+
+ po->regmask_src |= get_pp_arg_regmask_src(pp);
+ po->regmask_dst |= get_pp_arg_regmask_dst(pp);
+
+ if (strstr(pp->ret_type.name, "int64"))
+ need_tmp64 = 1;
+
+ continue;
+ }
+
+ 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)
+ scan_pushes_for_pop(i, opcnt, ®mask_pp);
+ else if (po->op == OP_STD) {
+ po->flags |= OPF_DF | OPF_RMD | OPF_DONE;
+ scan_propagate_df(i + 1, opcnt);
+ }
+ }
+
+ // pass6:
+ // - find POPs for PUSHes, rm both
+ // - scan for all used registers
+ memset(cbits, 0, sizeof(cbits));
+ reg_use_pass(0, opcnt, cbits, 0, ®mask,
+ 0, ®mask_save, ®mask_init, regmask_arg);
+
+ // pass7:
+ // - find flag set ops for their users
+ // - do unresolved calls
+ // - declare indirect functions
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ continue;
+
+ 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;
+ ferr_assert(po, pp != NULL);
+
+ 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;
+ }
+ }
+ 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_DIV || po->op == OP_IDIV) {
+ if (po->operand[0].lmod == OPLM_DWORD) {
+ // 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
+ need_tmp_var = 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;
+ }
+
+ // 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;
+ }
+ }
+
+ // declare normal registers
+ 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;
+ }
+ }
+ }
+
+ // declare push-pop temporaries
+ if (regmask_pp) {
+ for (reg = 0; reg < 8; reg++) {
+ if (regmask_pp & (1 << reg)) {
+ fprintf(fout, " u32 pp_%s;\n", regs_r32[reg]);
+ 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 = OPR_INIT(OPT_REG, OPLM_DWORD, xCX);
+ 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_XLAT:
+ assert_operand_cnt(2);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
+ fprintf(fout, " %s = *(u8 *)(%s + %s);", buf1, buf2, buf1);
+ strcpy(g_comment, "xlat");
+ 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:
+ if (po->flags & OPF_REP) {
+ assert_operand_cnt(3);
+ // hmh..
+ ferr(po, "TODO\n");
+ }
+ else {
+ assert_operand_cnt(2);
+ fprintf(fout, " %s = %sesi; esi %c= %d;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[1]),
+ lmod_cast_u_ptr(po, po->operand[1].lmod),
+ (po->flags & OPF_DF) ? '-' : '+',
+ lmod_bytes(po, po->operand[1].lmod));
+ strcpy(g_comment, "lods");
+ }
+ break;
+
+ case OP_STOS:
+ if (po->flags & OPF_REP) {
+ assert_operand_cnt(3);
+ fprintf(fout, " for (; ecx != 0; ecx--, edi %c= %d)\n",
+ (po->flags & OPF_DF) ? '-' : '+',
+ lmod_bytes(po, po->operand[1].lmod));
+ fprintf(fout, " %sedi = eax;",
+ lmod_cast_u_ptr(po, po->operand[1].lmod));
+ strcpy(g_comment, "rep stos");
+ }
+ else {
+ assert_operand_cnt(2);
+ fprintf(fout, " %sedi = eax; edi %c= %d;",
+ lmod_cast_u_ptr(po, po->operand[1].lmod),
+ (po->flags & OPF_DF) ? '-' : '+',
+ lmod_bytes(po, po->operand[1].lmod));
+ strcpy(g_comment, "stos");
+ }
+ break;
+
+ case OP_MOVS:
+ 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) {
+ assert_operand_cnt(3);
+ 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 {
+ assert_operand_cnt(2);
+ 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
+ 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) {
+ assert_operand_cnt(3);
+ 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 {
+ 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,
+ " for (; ecx != 0; ecx--) {\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,
+ " 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
+ 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);
+ 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 & (1 << xAX)) {
+ 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 (po->regmask_dst & (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 (!(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 & (1 << xAX))) {
+ 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;");