+ // look for and make use of esp adjust
+ multipath = 0;
+ ret = -1;
+ if (!pp->is_stdcall && pp->argc_stack > 0) {
+ int adj_expect = pp->is_vararg ? -1 : pp->argc_stack * 4;
+ ret = scan_for_esp_adjust(i + 1, opcnt,
+ adj_expect, &adj, &multipath, 0);
+ }
+ if (ret >= 0) {
+ if (pp->is_vararg) {
+ if (adj / 4 < pp->argc_stack) {
+ fnote(po, "(this call)\n");
+ ferr(&ops[ret], "esp adjust is too small: %x < %x\n",
+ adj, pp->argc_stack * 4);
+ }
+ // modify pp to make it have varargs as normal args
+ arg = pp->argc;
+ pp->argc += adj / 4 - pp->argc_stack;
+ for (; arg < pp->argc; arg++) {
+ pp->arg[arg].type.name = strdup("int");
+ pp->argc_stack++;
+ }
+ if (pp->argc > ARRAY_SIZE(pp->arg))
+ ferr(po, "too many args for '%s'\n", tmpname);
+ }
+ if (pp->argc_stack > adj / 4) {
+ if (pp->is_noreturn)
+ // assume no stack adjust was emited
+ goto out;
+ fnote(po, "(this call)\n");
+ ferr(&ops[ret], "stack tracking failed for '%s': %x %x\n",
+ tmpname, pp->argc_stack * 4, adj);
+ }
+
+ scan_for_esp_adjust(i + 1, opcnt,
+ pp->argc_stack * 4, &adj, &multipath, 1);
+ }
+ else if (pp->is_vararg)
+ ferr(po, "missing esp_adjust for vararg func '%s'\n",
+ pp->name);
+
+out:
+ return pp;
+}
+
+static void check_fptr_args(int i, int opcnt, struct parsed_proto *pp)
+{
+ struct parsed_opr s_opr = OPR_INIT(OPT_REG, OPLM_DWORD, 0);
+ const struct parsed_proto *pp_arg, *pp_cmp;
+ const struct parsed_op *po_a;
+ const char *s_reg;
+ int pp_cmp_i;
+ int arg, reg;
+ int bad = 0;
+ int j;
+
+ for (arg = 0; arg < pp->argc; arg++) {
+ pp_cmp = NULL;
+ pp_cmp_i = -1;
+
+ pp_arg = pp->arg[arg].pp;
+ if (pp_arg == NULL || !pp_arg->is_func)
+ continue;
+
+ s_reg = pp->arg[arg].reg;
+ if (s_reg != NULL) {
+ reg = char_array_i(regs_r32, ARRAY_SIZE(regs_r32), s_reg);
+ ferr_assert(&ops[i], reg >= 0);
+ s_opr.reg = reg;
+ scan_for_call_type(i, &s_opr, i + arg + opcnt * 28, 0,
+ &pp_cmp, &pp_cmp_i, NULL);
+ if (pp_cmp != NULL && !pp_compatible_func(pp_arg, pp_cmp)) {
+ bad = 1;
+ if (pp_cmp_i >= 0)
+ fnote(&ops[pp_cmp_i], "(referenced here)\n");
+ }
+ }
+ else {
+ for (j = 0; j < pp->arg[arg].push_ref_cnt; j++) {
+ po_a = pp->arg[arg].push_refs[j];
+ if (po_a == NULL || po_a->op != OP_PUSH)
+ continue;
+ pp_cmp = resolve_func_ptr(po_a - ops, opcnt, 0,
+ &po_a->operand[0], &pp_cmp_i, NULL);
+ if (pp_cmp != NULL && !pp_compatible_func(pp_arg, pp_cmp)) {
+ bad = 1;
+ if (pp_cmp_i < 0)
+ pp_cmp_i = po_a - ops;
+ if (pp_cmp_i >= 0)
+ fnote(&ops[pp_cmp_i], "(referenced here)\n");
+ }
+ }
+ }
+
+ if (bad)
+ ferr(&ops[i], "incompatible fptr arg %d\n", arg + 1);
+ }
+}
+
+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 pp_insert_stack_args(struct parsed_proto *pp, int count)
+{
+ int a;
+
+ pp->argc += count;
+ pp->argc_stack += count;
+
+ for (a = 0; a < pp->argc; a++)
+ if (pp->arg[a].type.name == NULL)
+ pp->arg[a].type.name = strdup("int");
+}
+
+static void pp_add_push_ref(struct parsed_proto *pp,
+ int arg, struct parsed_op *po)
+{
+ pp->arg[arg].push_refs = realloc(pp->arg[arg].push_refs,
+ (pp->arg[arg].push_ref_cnt + 1)
+ * sizeof(pp->arg[arg].push_refs[0]));
+ ferr_assert(po, pp->arg[arg].push_refs != NULL);
+ pp->arg[arg].push_refs[pp->arg[arg].push_ref_cnt++] = po;
+}
+
+static void mark_float_arg(struct parsed_op *po,
+ struct parsed_proto *pp, int arg, int *regmask_ffca)
+{
+ ferr_assert(po, pp->arg[arg].push_ref_cnt == 0);
+ pp_add_push_ref(pp, arg, po);
+
+ po->p_argnum = arg + 1;
+ po->flags |= OPF_DONE | OPF_FARGNR | OPF_FARG;
+ if (regmask_ffca != NULL)
+ *regmask_ffca |= 1 << arg;
+}
+
+static int check_for_stp(int i, int i_to)
+{
+ struct parsed_op *po;
+
+ for (; i < i_to; i++) {
+ po = &ops[i];
+ if (po->op == OP_FST)
+ return i;
+ if (g_labels[i] != NULL || (po->flags & OPF_JMP))
+ return -1;
+ if (po->op == OP_CALL || po->op == OP_PUSH || po->op == OP_POP)
+ return -1;
+ if (po->op == OP_ADD && po->operand[0].reg == xSP)
+ return -1;