const struct parsed_proto *pp)
{
int sarg_ofs = 1; // stack offset to args, in DWORDs
+ int saved_regs = 0;
int argc_repush;
int stack_args;
+ int ret64;
int i;
argc_repush = pp->argc;
stack_args = argc_repush - pp->argc_reg;
}
- fprintf(f, "# %s\n", pp->is_stdcall ? "__stdcall" : "__cdecl");
- fprintf(f, ".global %s\n", sym);
+ ret64 = strstr(pp->ret_type.name, "int64") != NULL;
+
+ fprintf(f, "# %s", pp->is_stdcall ? "__stdcall" : "__cdecl");
+ if (ret64)
+ fprintf(f, " ret64");
+ fprintf(f, "\n.global %s\n", sym);
fprintf(f, "%s:\n", sym);
if (pp->argc_reg == 0) {
return;
}
- // scratch reg, must not be eax or edx (for ret edx:eax)
- fprintf(f, "\tpushl %%ebx\n");
+ // at least sc sub_47B150 needs edx to be preserved
+ // int64 returns use edx:eax - no edx save
+ // we use ecx also as scratch
+ fprintf(f, "\tpushl %%ecx\n");
+ saved_regs++;
sarg_ofs++;
+ if (!ret64) {
+ fprintf(f, "\tpushl %%edx\n");
+ saved_regs++;
+ sarg_ofs++;
+ }
// construct arg stack
for (i = argc_repush - 1; i >= 0; i--) {
if (pp->arg[i].reg == NULL) {
- fprintf(f, "\tmovl %d(%%esp), %%ebx\n",
+ fprintf(f, "\tmovl %d(%%esp), %%ecx\n",
(sarg_ofs + stack_args - 1) * 4);
- fprintf(f, "\tpushl %%ebx\n");
+ fprintf(f, "\tpushl %%ecx\n");
stack_args--;
}
else {
- if (IS(pp->arg[i].reg, "ebx"))
- // must reload original ebx
- fprintf(f, "\tmovl %d(%%esp), %%ebx\n",
+ if (IS(pp->arg[i].reg, "ecx"))
+ // must reload original ecx
+ fprintf(f, "\tmovl %d(%%esp), %%ecx\n",
(sarg_ofs - 2) * 4);
fprintf(f, "\tpushl %%%s\n", pp->arg[i].reg);
// no worries about calling conventions - always __cdecl
fprintf(f, "\n\tcall _%s\n\n", sym);
- if (sarg_ofs > 2)
- fprintf(f, "\tadd $%d,%%esp\n", (sarg_ofs - 2) * 4);
+ if (sarg_ofs > saved_regs + 1)
+ fprintf(f, "\tadd $%d,%%esp\n",
+ (sarg_ofs - (saved_regs + 1)) * 4);
- fprintf(f, "\tpopl %%ebx\n");
+ if (!ret64)
+ fprintf(f, "\tpopl %%edx\n");
+ fprintf(f, "\tpopl %%ecx\n");
if (pp->is_stdcall && pp->argc_stack)
fprintf(f, "\tret $%d\n\n", pp->argc_stack * 4);
}
}
-static void set_flag_no_dup(struct parsed_op *po, enum op_flags flag,
- enum op_flags flag_check)
+static void op_set_clear_flag(struct parsed_op *po,
+ enum op_flags flag_set, enum op_flags flag_clear)
{
- if (po->flags & flag)
- ferr(po, "flag %x already set\n", flag);
- if (po->flags & flag_check)
- ferr(po, "flag_check %x already set\n", flag_check);
-
- po->flags |= flag;
+ po->flags |= flag_set;
+ po->flags &= ~flag_clear;
}
// last op in stream - unconditional branch or ret
if (depth > *maxdepth)
*maxdepth = depth;
if (do_flags)
- set_flag_no_dup(po, OPF_RSAVE, OPF_RMD);
+ op_set_clear_flag(po, OPF_RSAVE, OPF_RMD);
}
else if (depth == 0) {
if (do_flags)
- set_flag_no_dup(po, OPF_RMD, OPF_RSAVE);
+ op_set_clear_flag(po, OPF_RMD, OPF_RSAVE);
return 1;
}
else {
if (depth < 0) // should not happen
ferr(po, "fail with depth\n");
if (do_flags)
- set_flag_no_dup(po, OPF_RSAVE, OPF_RMD);
+ op_set_clear_flag(po, OPF_RSAVE, OPF_RMD);
}
}
}
{
pp_tmp = ops[j].datap;
if (pp_tmp == NULL)
- ferr(po, "arg collect hit unparsed call\n");
+ ferr(po, "arg collect hit unparsed call '%s'\n",
+ ops[j].operand[0].name);
if (may_reuse && pp_tmp->argc_stack > 0)
ferr(po, "arg collect %d/%d hit '%s' with %d stack args\n",
arg, pp->argc, opr_name(&ops[j], 0), pp_tmp->argc_stack);
}
// pass2:
+ // - parse calls with labels
// - resolve all branches
for (i = 0; i < opcnt; i++)
{
po->bt_i = -1;
po->btj = NULL;
- if ((po->flags & OPF_RMD) || !(po->flags & OPF_JMP)
- || po->op == OP_CALL || po->op == OP_RET)
+ if (po->flags & OPF_RMD)
+ continue;
+
+ if (po->op == OP_CALL) {
+ if (po->operand[0].type == OPT_LABEL) {
+ tmpname = opr_name(po, 0);
+ pp_c = proto_parse(fhdr, tmpname);
+ 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");
+ pp = proto_clone(pp_c);
+ my_assert_not(pp, NULL);
+ po->datap = pp;
+ }
+ continue;
+ }
+
+ if (!(po->flags & OPF_JMP) || po->op == OP_RET)
continue;
if (po->operand[0].type == OPT_REGMEM) {
tailcall:
po->op = OP_CALL;
po->flags |= OPF_TAIL;
+ i--; // reprocess
}
// pass3:
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)
+ pp = po->datap;
+ if (pp == NULL)
{
+ // indirect call
+ pp = calloc(1, sizeof(*pp));
+ my_assert_not(pp, NULL);
ret = scan_for_esp_adjust(i + 1, opcnt, &j);
if (ret < 0)
ferr(po, "non-__cdecl indirect call unhandled yet\n");
pp->argc = pp->argc_stack = j;
for (arg = 0; arg < pp->argc; arg++)
pp->arg[arg].type.name = strdup("int");
- }
- else {
- pp_c = proto_parse(fhdr, tmpname);
- 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");
- pp = proto_clone(pp_c);
+ po->datap = pp;
}
// look for and make use of esp adjust
need_mul_var = 1;
if (!(po->flags & OPF_TAIL) && !IS(pp->ret_type.name, "void"))
have_func_ret = 1;
- po->datap = pp;
}
}
if (po->flags & OPF_RMD)
continue;
+ if (po->op == OP_PUSH && (po->flags & OPF_RSAVE)) {
+ reg = po->operand[0].reg;
+ if (!(regmask & (1 << reg)))
+ // not a reg save after all, rerun scan_for_pop
+ po->flags &= ~OPF_RSAVE;
+ else
+ regmask_save |= 1 << reg;
+ }
+
if (po->op == OP_PUSH
&& po->argnum == 0 && !(po->flags & OPF_RSAVE)
&& po->operand[0].type == OPT_REG)
if (ret == 1) {
if (depth > 1)
ferr(po, "too much depth: %d\n", depth);
- if (depth > 0)
- regmask_save |= 1 << reg;
po->flags |= OPF_RMD;
scan_for_pop(i + 1, opcnt, po->operand[0].name,