+static const struct parsed_proto *try_recover_pp(
+ struct parsed_op *po, const struct parsed_opr *opr)
+{
+ const struct parsed_proto *pp = NULL;
+
+ // maybe an arg of g_func?
+ if (opr->type == OPT_REGMEM && is_stack_access(po, opr))
+ {
+ char ofs_reg[16] = { 0, };
+ int arg, arg_s, arg_i;
+ int stack_ra = 0;
+ int offset = 0;
+
+ parse_stack_access(po, opr->name, ofs_reg,
+ &offset, &stack_ra, NULL);
+ if (ofs_reg[0] != 0)
+ ferr(po, "offset reg on arg access?\n");
+ if (offset <= stack_ra)
+ ferr(po, "stack var call unhandled yet\n");
+
+ arg_i = (offset - stack_ra - 4) / 4;
+ for (arg = arg_s = 0; arg < g_func_pp->argc; arg++) {
+ if (g_func_pp->arg[arg].reg != NULL)
+ continue;
+ if (arg_s == arg_i)
+ break;
+ arg_s++;
+ }
+ if (arg == g_func_pp->argc)
+ ferr(po, "stack arg %d not in prototype?\n", arg_i);
+
+ pp = g_func_pp->arg[arg].fptr;
+ if (pp == NULL)
+ ferr(po, "icall sa: arg%d is not a fptr?\n", arg + 1);
+ if (pp->argc_reg != 0)
+ ferr(po, "icall sa: reg arg in arg-call unhandled yet\n");
+ }
+ else if (opr->type == OPT_OFFSET || opr->type == OPT_LABEL) {
+ pp = proto_parse(g_fhdr, opr->name);
+ if (pp == NULL)
+ ferr(po, "proto_parse failed for icall from '%s'\n", opr->name);
+ if (pp->argc_reg != 0)
+ ferr(po, "arg-call unhandled yet for '%s'\n", opr->name);
+ }
+
+ return pp;
+}
+
+static void scan_for_call_type(int i, struct parsed_opr *opr,
+ int magic, const struct parsed_proto **pp_found)
+{
+ const struct parsed_proto *pp = NULL;
+ struct parsed_op *po;
+ struct label_ref *lr;
+
+ while (i >= 0) {
+ if (ops[i].cc_scratch == magic)
+ return;
+ ops[i].cc_scratch = magic;
+
+ if (g_labels[i][0] != 0) {
+ lr = &g_label_refs[i];
+ for (; lr != NULL; lr = lr->next)
+ scan_for_call_type(lr->i, opr, magic, pp_found);
+ if (i > 0 && LAST_OP(i - 1))
+ return;
+ }
+ i--;
+
+ if (!(ops[i].flags & OPF_DATA))
+ continue;
+ if (!is_opr_modified(opr, &ops[i]))
+ continue;
+ if (ops[i].op != OP_MOV && ops[i].op != OP_LEA) {
+ // most probably trashed by some processing
+ *pp_found = NULL;
+ return;
+ }
+
+ opr = &ops[i].operand[1];
+ if (opr->type != OPT_REG)
+ break;
+ }
+
+ po = (i >= 0) ? &ops[i] : ops;
+
+ if (i < 0) {
+ // reached the top - can only be an arg-reg
+ if (opr->type != OPT_REG)
+ return;
+
+ for (i = 0; i < g_func_pp->argc; i++) {
+ if (g_func_pp->arg[i].reg == NULL)
+ continue;
+ if (IS(opr->name, g_func_pp->arg[i].reg))
+ break;
+ }
+ if (i == g_func_pp->argc)
+ return;
+ pp = g_func_pp->arg[i].fptr;
+ if (pp == NULL)
+ ferr(po, "icall: arg%d (%s) is not a fptr?\n",
+ i + 1, g_func_pp->arg[i].reg);
+ if (pp->argc_reg != 0)
+ ferr(po, "icall: reg arg in arg-call unhandled yet\n");
+ }
+ else
+ pp = try_recover_pp(po, opr);
+
+ if (*pp_found != NULL && pp != NULL) {
+ if (!IS((*pp_found)->ret_type.name, pp->ret_type.name)
+ || (*pp_found)->is_stdcall != pp->is_stdcall
+ || (*pp_found)->argc != pp->argc
+ || (*pp_found)->argc_reg != pp->argc_reg
+ || (*pp_found)->argc_stack != pp->argc_stack)
+ {
+ ferr(po, "icall: parsed_proto mismatch\n");
+ }
+ }
+ if (pp != NULL)
+ *pp_found = pp;
+}
+
+static const struct parsed_proto *resolve_call(int i, int opcnt)
+{
+ const struct parsed_proto *pp = NULL;
+
+ switch (ops[i].operand[0].type) {
+ case OPT_REGMEM:
+ case OPT_LABEL:
+ case OPT_OFFSET:
+ pp = try_recover_pp(&ops[i], &ops[i].operand[0]);
+ break;
+ default:
+ scan_for_call_type(i, &ops[i].operand[0], i + opcnt * 9, &pp);
+ break;
+ }
+
+ return pp;
+}
+