+ 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_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;
+ }
+
+ return -1;
+}
+
+static int collect_call_args_no_push(int i, struct parsed_proto *pp,
+ int *regmask_ffca)
+{
+ struct parsed_op *po;
+ int offset = 0;
+ int base_arg;
+ int j, arg;
+ int ret;
+
+ for (base_arg = 0; base_arg < pp->argc; base_arg++)
+ if (pp->arg[base_arg].reg == NULL)
+ break;
+
+ for (j = i; j > 0; )
+ {
+ ferr_assert(&ops[j], g_labels[j] == NULL);
+ j--;
+
+ po = &ops[j];
+ ferr_assert(po, po->op != OP_PUSH);
+ if (po->op == OP_FST)
+ {
+ if (po->operand[0].type != OPT_REGMEM)
+ continue;
+ ret = parse_stack_esp_offset(po, po->operand[0].name, &offset);
+ if (ret != 0)
+ continue;
+ if (offset < 0 || offset >= pp->argc_stack * 4 || (offset & 3)) {
+ //ferr(po, "offset %d, %d args\n", offset, pp->argc_stack);
+ continue;
+ }
+
+ arg = base_arg + offset / 4;
+ mark_float_arg(po, pp, arg, regmask_ffca);