+ for (; i >= 0; i--) {
+ if (ops[i].op == OP_XOR
+ && ops[i].operand[0].lmod == OPLM_DWORD
+ && ops[i].operand[0].reg == ops[i].operand[1].reg
+ && ops[i].operand[0].reg == reg)
+ return i;
+
+ if (ops[i].regmask_dst & (1 << reg))
+ return -1;
+ if (g_labels[i][0] != 0)
+ return -1;
+ }
+
+ return -1;
+}
+
+// scan for positive, constant esp adjust
+static int scan_for_esp_adjust(int i, int opcnt, int *adj)
+{
+ struct parsed_op *po;
+ *adj = 0;
+
+ for (; i < opcnt; i++) {
+ po = &ops[i];
+
+ if (po->op == OP_ADD && po->operand[0].reg == xSP) {
+ if (po->operand[1].type != OPT_CONST)
+ ferr(&ops[i], "non-const esp adjust?\n");
+ *adj += po->operand[1].val;
+ if (*adj & 3)
+ ferr(&ops[i], "unaligned esp adjust: %x\n", *adj);
+ return i;
+ }
+ else if (po->op == OP_PUSH)
+ *adj -= lmod_bytes(po, po->operand[0].lmod);
+ else if (po->op == OP_POP)
+ *adj += lmod_bytes(po, po->operand[0].lmod);
+ else if (po->flags & (OPF_JMP|OPF_TAIL)) {
+ if (po->op != OP_CALL)
+ return -1;
+ if (po->operand[0].type != OPT_LABEL)
+ return -1;
+ // TODO: should only allow combining __cdecl calls..
+ }
+
+ if (g_labels[i][0] != 0)
+ return -1;
+ }
+
+ return -1;
+}
+
+static int collect_call_args(struct parsed_op *po, int i,
+ struct parsed_proto *pp, int *save_arg_vars, int arg,
+ int need_op_saving, int branched)
+{
+ struct parsed_proto *pp_tmp;
+ struct label_ref *lr;
+ int ret = 0;
+ int j;
+
+ if (i < 0)
+ ferr(po, "no refs for '%s'?\n", g_labels[i]);
+
+ for (; arg < pp->argc; arg++)
+ if (pp->arg[arg].reg == NULL)
+ break;
+
+ for (j = i; j >= 0 && arg < pp->argc; )
+ {
+ if (g_labels[j][0] != 0) {
+ branched = 1;
+ lr = &g_label_refs[j];
+ if (lr->next != NULL)
+ need_op_saving = 1;
+ for (; lr->next; lr = lr->next)
+ ret |= collect_call_args(po, lr->i, pp, save_arg_vars,
+ arg, need_op_saving, branched);
+
+ if (j > 0 && LAST_OP(j - 1)) {
+ // follow last branch in reverse
+ j = lr->i;
+ continue;
+ }
+ need_op_saving = 1;
+ ret |= collect_call_args(po, lr->i, pp, save_arg_vars,
+ arg, need_op_saving, branched);
+ }
+ j--;
+
+ if (ops[j].op == OP_CALL)
+ {
+ pp_tmp = ops[j].datap;
+ if (pp_tmp == NULL)
+ ferr(po, "arg collect hit unparsed call\n");
+ if (branched && pp_tmp->argc_stack > 0)
+ ferr(po, "arg collect %d/%d hit '%s' with %d stack args\n",
+ arg, pp->argc, opr_name(&ops[j], 0), pp_tmp->argc_stack);
+ }
+ else if (ops[j].op == OP_ADD && ops[j].operand[0].reg == xSP) {
+ ferr(po, "arg collect %d/%d hit esp adjust\n",
+ arg, pp->argc);
+ }
+ else if (ops[j].op == OP_POP) {
+ ferr(po, "arg collect %d/%d hit pop\n", arg, pp->argc);
+ }
+ else if (LAST_OP(j)) {
+ break;
+ }
+ else if ((ops[j].flags & (OPF_JMP|OPF_CC)) == (OPF_JMP|OPF_CC))
+ {
+ branched = 1;
+ }
+ else if (ops[j].op == OP_PUSH && !(ops[j].flags & OPF_FARG))
+ {
+ pp->arg[arg].datap = &ops[j];
+ if (!need_op_saving) {
+ ret = scan_for_mod(&ops[j], j + 1, i);
+ need_op_saving = (ret >= 0);
+ }
+ if (need_op_saving) {
+ // mark this push as one that needs operand saving
+ ops[j].flags &= ~OPF_RMD;
+ if (ops[j].argnum == 0) {
+ ops[j].argnum = arg + 1;
+ *save_arg_vars |= 1 << arg;
+ }
+ else if (ops[j].argnum < arg + 1)
+ ferr(&ops[j], "argnum conflict (%d<%d) for '%s'\n",
+ ops[j].argnum, arg + 1, pp->name);
+ }
+ else if (ops[j].argnum == 0)
+ ops[j].flags |= OPF_RMD;
+
+ // some PUSHes are reused by calls on multiple branches,
+ // but that can't happen if we didn't branch, so they
+ // can be removed from future searches (handles nested calls)
+ if (!branched)
+ ops[j].flags |= OPF_FARG;
+
+ // next arg
+ for (arg++; arg < pp->argc; arg++)
+ if (pp->arg[arg].reg == NULL)
+ break;
+ }
+ }
+
+ if (arg < pp->argc) {
+ ferr(po, "arg collect failed for '%s': %d/%d\n",
+ pp->name, arg, pp->argc);
+ ret = -1;
+ }
+ return ret;
+}
+
+static void add_label_ref(struct label_ref *lr, int op_i)
+{
+ struct label_ref *lr_new;
+
+ if (lr->i == -1) {
+ lr->i = op_i;
+ return;
+ }
+
+ lr_new = calloc(1, sizeof(*lr_new));
+ lr_new->i = op_i;
+ lr->next = lr_new;
+}
+
+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;
+ const char *tmpname;
+ enum parsed_flag_op pfo;
+ int save_arg_vars = 0;
+ int cmp_result_vars = 0;
+ int need_mul_var = 0;
+ int had_decl = 0;
+ int branched = 0;
+ int label_pending = 0;
+ int regmask_save = 0;
+ int regmask_arg = 0;
+ int regmask = 0;
+ int pfomask = 0;
+ int found = 0;
+ int depth = 0;
+ int no_output;
+ int i, j, l;
+ int dummy;
+ int arg;