+ if (ops[j].p_arggrp > arg_grp_current) {
+ save_args_seen = 0;
+ arg_grp_current = ops[j].p_arggrp;
+ }
+ if (ops[j].p_argnum > 0)
+ save_args_seen |= 1 << (ops[j].p_argnum - 1);
+ }
+
+ if (arg < pp->argc) {
+ ferr(po, "arg collect failed for '%s': %d/%d\n",
+ pp->name, arg, pp->argc);
+ return -1;
+ }
+
+ if (arg_grp_current > *arg_grp)
+ *arg_grp = arg_grp_current;
+
+ return arg;
+}
+
+static int collect_call_args(struct parsed_op *po, int i,
+ struct parsed_proto *pp, int *regmask, int *save_arg_vars,
+ int magic)
+{
+ // arg group is for cases when pushes for
+ // multiple funcs are going on
+ struct parsed_op *po_tmp;
+ int save_arg_vars_current = 0;
+ int arg_grp = 0;
+ int ret;
+ int a;
+
+ ret = collect_call_args_r(po, i, pp, regmask,
+ &save_arg_vars_current, &arg_grp, 0, magic, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ if (arg_grp != 0) {
+ // propagate arg_grp
+ for (a = 0; a < pp->argc; a++) {
+ if (pp->arg[a].reg != NULL)
+ continue;
+
+ po_tmp = pp->arg[a].datap;
+ while (po_tmp != NULL) {
+ po_tmp->p_arggrp = arg_grp;
+ if (po_tmp->p_argnext > 0)
+ po_tmp = &ops[po_tmp->p_argnext];
+ else
+ po_tmp = NULL;
+ }
+ }
+ }
+ save_arg_vars[arg_grp] |= save_arg_vars_current;
+
+ if (pp->is_unresolved) {
+ pp->argc += ret;
+ pp->argc_stack += ret;
+ for (a = 0; a < pp->argc; a++)
+ if (pp->arg[a].type.name == NULL)
+ pp->arg[a].type.name = strdup("int");
+ }
+
+ return ret;
+}
+
+static void reg_use_pass(int i, int opcnt, unsigned char *cbits,
+ int regmask_now, int *regmask,
+ int regmask_save_now, int *regmask_save,
+ int *regmask_init, int regmask_arg)
+{
+ struct parsed_op *po;
+ int already_saved;
+ int regmask_new;
+ int regmask_op;
+ int flags_set;
+ int ret, reg;
+ int j;
+
+ for (; i < opcnt; i++)
+ {
+ po = &ops[i];
+ if (cbits[i >> 3] & (1 << (i & 7)))
+ return;
+ cbits[i >> 3] |= (1 << (i & 7));
+
+ if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ continue;
+ if (po->btj != NULL) {
+ for (j = 0; j < po->btj->count; j++) {
+ check_i(po, po->btj->d[j].bt_i);
+ reg_use_pass(po->btj->d[j].bt_i, opcnt, cbits,
+ regmask_now, regmask, regmask_save_now, regmask_save,
+ regmask_init, regmask_arg);
+ }
+ return;
+ }
+
+ check_i(po, po->bt_i);
+ if (po->flags & OPF_CJMP)
+ reg_use_pass(po->bt_i, opcnt, cbits,
+ regmask_now, regmask, regmask_save_now, regmask_save,
+ regmask_init, regmask_arg);
+ else
+ i = po->bt_i - 1;
+ continue;
+ }
+
+ if (po->op == OP_PUSH && !(po->flags & (OPF_FARG|OPF_DONE))
+ && !g_func_pp->is_userstack
+ && po->operand[0].type == OPT_REG)
+ {
+ reg = po->operand[0].reg;
+ ferr_assert(po, reg >= 0);
+
+ already_saved = 0;
+ flags_set = OPF_RSAVE | OPF_RMD | OPF_DONE;
+ if (regmask_now & (1 << reg)) {
+ already_saved = regmask_save_now & (1 << reg);
+ flags_set = OPF_RSAVE | OPF_DONE;
+ }
+
+ ret = scan_for_pop(i + 1, opcnt, i + opcnt * 3, reg, 0, 0);
+ if (ret == 1) {
+ scan_for_pop(i + 1, opcnt, i + opcnt * 4, reg, 0, flags_set);
+ }
+ else {
+ ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, 0);
+ if (ret == 1) {
+ scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg,
+ flags_set);
+ }
+ }
+ if (ret == 1) {
+ ferr_assert(po, !already_saved);
+ po->flags |= flags_set;
+
+ if (regmask_now & (1 << reg)) {
+ regmask_save_now |= (1 << reg);
+ *regmask_save |= regmask_save_now;
+ }
+ continue;
+ }
+ }
+ else if (po->op == OP_POP && (po->flags & OPF_RSAVE)) {
+ reg = po->operand[0].reg;
+ ferr_assert(po, reg >= 0);
+
+ if (regmask_save_now & (1 << reg))
+ regmask_save_now &= ~(1 << reg);
+ else
+ regmask_now &= ~(1 << reg);
+ continue;
+ }
+ else if (po->op == OP_CALL) {
+ if ((po->regmask_dst & (1 << xAX))
+ && !(po->regmask_dst & (1 << xDX)))
+ {
+ if (po->flags & OPF_TAIL)
+ // don't need eax, will do "return f();" or "f(); return;"
+ po->regmask_dst &= ~(1 << xAX);
+ else {
+ struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xAX);
+ j = -1;
+ find_next_read(i + 1, opcnt, &opr, i + opcnt * 17, &j);
+ if (j == -1)
+ // not used
+ po->regmask_dst &= ~(1 << xAX);
+ }
+ }
+ }
+
+ if (po->flags & OPF_NOREGS)
+ continue;