+ // - process calls
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ if (po->flags & OPF_RMD)
+ continue;
+
+ if (po->op == OP_CALL)
+ {
+ pp = calloc(1, sizeof(*pp));
+ my_assert_not(pp, NULL);
+ tmpname = opr_name(po, 0);
+ if (po->operand[0].type != OPT_LABEL)
+ {
+ ret = scan_for_esp_adjust(i + 1, opcnt, &j);
+ if (ret < 0)
+ ferr(po, "non-__cdecl indirect call unhandled yet\n");
+ j /= 4;
+ if (j > ARRAY_SIZE(pp->arg))
+ ferr(po, "esp adjust too large?\n");
+ pp->ret_type = strdup("int");
+ pp->argc = pp->argc_stack = j;
+ for (arg = 0; arg < pp->argc; arg++)
+ pp->arg[arg].type = strdup("int");
+ }
+ else {
+ ret = proto_parse(fhdr, tmpname, pp);
+ if (ret)
+ ferr(po, "proto_parse failed for call '%s'\n", tmpname);
+ }
+
+ // look for and make use of esp adjust
+ ret = -1;
+ if (!pp->is_stdcall)
+ ret = scan_for_esp_adjust(i + 1, opcnt, &j);
+ if (ret >= 0) {
+ if (pp->is_vararg) {
+ if (j / 4 < pp->argc_stack)
+ ferr(po, "esp adjust is too small: %x < %x\n",
+ j, pp->argc_stack * 4);
+ // modify pp to make it have varargs as normal args
+ arg = pp->argc;
+ pp->argc += j / 4 - pp->argc_stack;
+ for (; arg < pp->argc; arg++) {
+ pp->arg[arg].type = strdup("int");
+ pp->argc_stack++;
+ }
+ if (pp->argc > ARRAY_SIZE(pp->arg))
+ ferr(po, "too many args\n");
+ }
+ if (pp->argc_stack != j / 4)
+ ferr(po, "stack tracking failed: %x %x\n",
+ pp->argc_stack * 4, j);
+
+ ops[ret].flags |= OPF_RMD;
+ // a bit of a hack, but deals with use of
+ // single adj for multiple calls
+ ops[ret].operand[1].val -= j;
+ }
+ else if (pp->is_vararg)
+ ferr(po, "missing esp_adjust for vararg func '%s'\n",
+ pp->name);
+
+ // can't call functions with non-__cdecl callbacks yet
+ for (arg = 0; arg < pp->argc; arg++) {
+ if (pp->arg[arg].fptr != NULL) {
+ pp_tmp = pp->arg[arg].fptr;
+ if (pp_tmp->is_stdcall || pp_tmp->argc != pp_tmp->argc_stack)
+ ferr(po, "'%s' has a non-__cdecl callback\n", tmpname);
+ }
+ }
+
+ // collect all stack args
+ 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) {
+ if (j > 0 && ((ops[j - 1].flags & OPF_TAIL)
+ || (ops[j - 1].flags & (OPF_JMP|OPF_CC)) == OPF_JMP))
+ {
+ // follow the branch in reverse
+ if (g_label_refs[j].i == -1)
+ ferr(po, "no refs for '%s'?\n", g_labels[j]);
+ if (g_label_refs[j].next != NULL)
+ ferr(po, "unhandled multiple refs to '%s'\n", g_labels[j]);
+ j = g_label_refs[j].i + 1;
+ continue;
+ }
+ break;
+ }
+ 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];
+ ret = scan_for_mod(&ops[j], j + 1, i);
+ if (ret >= 0) {
+ // 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",
+ tmpname, arg, pp->argc);
+ po->datap = pp;
+ }
+ }
+
+ // pass4: