OPF_REPZ = (1 << 8), /* rep is repe/repz */
OPF_REPNZ = (1 << 9), /* rep is repne/repnz */
OPF_FARG = (1 << 10), /* push collected as func arg (no reuse) */
- OPF_EBP_S = (1 << 11), /* ebp used as scratch, not BP */
+ OPF_EBP_S = (1 << 11), /* ebp used as scratch here, not BP */
};
enum op_op {
const struct parsed_type *c_type)
{
static const char *dword_types[] = {
- "int", "_DWORD", "UINT_PTR",
- "DWORD", "HANDLE", "HWND", "HMODULE",
+ "int", "_DWORD", "UINT_PTR", "DWORD",
"WPARAM", "LPARAM", "UINT",
+ "HIMC",
};
static const char *word_types[] = {
"uint16_t", "int16_t", "_WORD",
if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
if (po->btj != NULL) {
// jumptable
- for (j = 0; j < po->btj->count - 1; j++) {
+ for (j = 0; j < po->btj->count; j++) {
ret |= scan_for_pop(po->btj->d[j].bt_i, opcnt, reg, magic,
depth, maxdepth, do_flags);
if (ret < 0)
return ret; // dead end
}
- // follow last jumptable entry
- i = po->btj->d[j].bt_i - 1;
- continue;
+ return ret;
}
if (po->bt_i < 0) {
&& po->operand[0].type == OPT_REG
&& IS(po->operand[0].name, reg))
{
- if (po->op == OP_PUSH) {
+ if (po->op == OP_PUSH && !(po->flags & OPF_FARG)) {
depth++;
if (depth > *maxdepth)
*maxdepth = depth;
if (do_flags)
op_set_clear_flag(po, OPF_RSAVE, OPF_RMD);
}
- else if (depth == 0) {
- if (do_flags)
- op_set_clear_flag(po, OPF_RMD, OPF_RSAVE);
- return 1;
- }
- else {
- depth--;
- if (depth < 0) // should not happen
- ferr(po, "fail with depth\n");
- if (do_flags)
- op_set_clear_flag(po, OPF_RSAVE, OPF_RMD);
+ else if (po->op == OP_POP) {
+ if (depth == 0) {
+ if (do_flags)
+ op_set_clear_flag(po, OPF_RMD, OPF_RSAVE);
+ return 1;
+ }
+ else {
+ depth--;
+ if (depth < 0) // should not happen
+ ferr(po, "fail with depth\n");
+ if (do_flags)
+ op_set_clear_flag(po, OPF_RSAVE, OPF_RMD);
+ }
}
}
}
if (ops[i].regmask_dst & (1 << xDX))
return -1;
- if (g_labels[i][0] != 0)
- return -1;
}
return -1;
if (ops[i].regmask_dst & (1 << reg))
return -1;
- if (g_labels[i][0] != 0)
- return -1;
}
return -1;
}
static const struct parsed_proto *try_recover_pp(
- struct parsed_op *po, const struct parsed_opr *opr)
+ struct parsed_op *po, const struct parsed_opr *opr, int *search_instead)
{
const struct parsed_proto *pp = NULL;
char buf[256];
&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");
+ if (offset <= stack_ra) {
+ // search who set the stack var instead
+ if (search_instead != NULL)
+ *search_instead = 1;
+ return NULL;
+ }
arg_i = (offset - stack_ra - 4) / 4;
for (arg = arg_s = 0; arg < g_func_pp->argc; arg++) {
return pp;
}
-static void scan_for_call_type(int i, struct parsed_opr *opr,
+static void scan_for_call_type(int i, const struct parsed_opr *opr,
int magic, const struct parsed_proto **pp_found, int *multi)
{
const struct parsed_proto *pp = NULL;
check_func_pp(po, pp, "icall reg-arg");
}
else
- pp = try_recover_pp(po, opr);
+ pp = try_recover_pp(po, opr, NULL);
if (*pp_found != NULL && pp != NULL && *pp_found != pp) {
if (!IS((*pp_found)->ret_type.name, pp->ret_type.name)
int *multi_src)
{
const struct parsed_proto *pp = NULL;
+ int search_advice = 0;
*multi_src = 0;
case OPT_REGMEM:
case OPT_LABEL:
case OPT_OFFSET:
- pp = try_recover_pp(&ops[i], &ops[i].operand[0]);
- break;
+ pp = try_recover_pp(&ops[i], &ops[i].operand[0], &search_advice);
+ if (!search_advice)
+ break;
+ // fallthrough
default:
scan_for_call_type(i, &ops[i].operand[0], i + opcnt * 9, &pp,
multi_src);
if (!may_reuse)
ops[j].flags |= OPF_FARG;
+ ops[j].flags &= ~OPF_RSAVE;
+
arg++;
if (!pp->is_unresolved) {
// next arg
pp_c = proto_parse(fhdr, tmpname, 0);
if (pp_c == NULL)
ferr(po, "proto_parse failed for call '%s'\n", tmpname);
- if (pp_c->is_fptr && pp_c->argc_reg != 0)
- ferr(po, "fptr call with reg arg\n");
+ if (pp_c->is_fptr)
+ check_func_pp(po, pp_c, "fptr var call");
+ if (pp_c->is_noreturn)
+ po->flags |= OPF_TAIL;
+
pp = proto_clone(pp_c);
my_assert_not(pp, NULL);
po->datap = pp;
regmask |= 1 << xAX;
}
+ // pass4:
+ // - confirm regmask_save, it might have been reduced
+ if (regmask_save != 0)
+ {
+ regmask_save = 0;
+ for (i = 0; i < opcnt; i++) {
+ po = &ops[i];
+ if (po->flags & OPF_RMD)
+ continue;
+
+ if (po->op == OP_PUSH && (po->flags & OPF_RSAVE))
+ regmask_save |= 1 << po->operand[0].reg;
+ }
+ }
+
+
// output LUTs/jumptables
for (i = 0; i < g_func_pd_cnt; i++) {
pd = &g_func_pd[i];
// else already handled as 'return f()'
if (ret) {
- if (!IS(g_func_pp->ret_type.name, "void"))
- ferr(po, "int func -> void func tailcall?\n");
- fprintf(fout, "\n return;");
- strcat(g_comment, "^ tailcall");
+ if (!IS(g_func_pp->ret_type.name, "void")) {
+ if (!pp->is_noreturn)
+ ferr(po, "int func -> void func tailcall?\n");
+ strcat(g_comment, "tailcall noreturn");
+ }
+ else {
+ fprintf(fout, "\n return;");
+ strcat(g_comment, "^ tailcall");
+ }
}
else
strcat(g_comment, "tailcall");
i = 2;
}
else {
+ if (pd == NULL) {
+ if (verbose)
+ anote("skipping alignment byte?\n");
+ continue;
+ }
lmod = lmod_from_directive(words[0]);
if (lmod != pd->lmod)
aerr("lmod change? %d->%d\n", pd->lmod, lmod);