+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)
+{
+ 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 = 0; 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) {
+ 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);
+
+ if (j > 0 && ((ops[j - 1].flags & OPF_TAIL)
+ || (ops[j - 1].flags & (OPF_JMP|OPF_CC)) == OPF_JMP))
+ {
+ // 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);
+ }
+ 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 (pp_tmp->argc_stack > 0)
+ ferr(po, "arg collect hit '%s' with %d stack args\n",
+ opr_name(&ops[j], 0), pp_tmp->argc_stack);
+ }
+ else if ((ops[j].flags & OPF_TAIL)
+ || (ops[j].flags & (OPF_JMP|OPF_CC)) == OPF_JMP)
+ {
+ break;
+ }
+ else if (ops[j].op == OP_PUSH)
+ {
+ 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;
+ ops[j].argmask |= 1 << arg;
+ *save_arg_vars |= 1 << arg;
+ }
+ else
+ ops[j].flags |= OPF_RMD;
+
+ // 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;
+}
+