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 */
};
enum op_op {
enum opr_lenmod lmod;
unsigned int is_ptr:1; // pointer in C
unsigned int is_array:1; // array in C
+ unsigned int type_from_var:1; // .. in header, sometimes wrong
unsigned int size_mismatch:1; // type override differs from C
unsigned int size_lt:1; // type override is larger than C
int reg;
const struct parsed_type *c_type)
{
static const char *dword_types[] = {
- "int", "_DWORD", "DWORD", "HANDLE", "HWND", "HMODULE",
+ "int", "_DWORD", "UINT_PTR",
+ "DWORD", "HANDLE", "HWND", "HMODULE",
"WPARAM", "LPARAM", "UINT",
};
static const char *word_types[] = {
static const char *byte_types[] = {
"uint8_t", "int8_t", "char",
"unsigned __int8", "__int8", "BYTE",
+ "_UNKNOWN",
};
const char *n;
int i;
anote("unhandled C type '%s' for '%s'\n",
pp->type.name, opr->name);
- if (opr->lmod == OPLM_UNSPEC)
+ if (opr->lmod == OPLM_UNSPEC) {
opr->lmod = tmplmod;
+ opr->type_from_var = 1;
+ }
else if (opr->lmod != tmplmod) {
opr->size_mismatch = 1;
if (tmplmod < opr->lmod)
{ "idiv", OP_IDIV, 1, 1, OPF_DATA|OPF_FLAGS },
{ "test", OP_TEST, 2, 2, OPF_FLAGS },
{ "cmp", OP_CMP, 2, 2, OPF_FLAGS },
- { "retn", OP_RET, 0, 1, OPF_JMP|OPF_TAIL },
+ { "retn", OP_RET, 0, 1, OPF_TAIL },
{ "call", OP_CALL, 1, 1, OPF_JMP|OPF_DATA|OPF_FLAGS },
{ "jmp", OP_JMP, 1, 1, OPF_JMP },
{ "jo", OP_JO, 1, 1, OPF_JMP|OPF_CC }, // 70 OF=1
const char *p;
char *endp = NULL;
int i, arg_i, arg_s;
+ int unaligned = 0;
int stack_ra = 0;
int offset = 0;
int sf_ofs;
int lim;
- if (!IS_START(name, "ebp-")) {
+ if (po->flags & OPF_EBP_S)
+ ferr(po, "stack_frame_access while ebp is scratch\n");
+
+ if (IS_START(name, "ebp-")
+ || (IS_START(name, "ebp+") && '0' <= name[4] && name[4] <= '9'))
+ {
+ p = name + 4;
+ if (IS_START(p, "0x"))
+ p += 2;
+ offset = strtoul(p, &endp, 16);
+ if (name[3] == '-')
+ offset = -offset;
+ if (*endp != 0)
+ ferr(po, "ebp- parse of '%s' failed\n", name);
+ }
+ else {
bp_arg = parse_stack_el(name, ofs_reg);
snprintf(g_comment, sizeof(g_comment), "%s", bp_arg);
eq = equ_find(po, bp_arg, &offset);
ferr(po, "detected but missing eq\n");
offset += eq->offset;
}
- else {
- p = name + 4;
- if (IS_START(p, "0x"))
- p += 2;
- offset = -strtoul(p, &endp, 16);
- if (*endp != 0)
- ferr(po, "ebp- parse of '%s' failed\n", name);
- }
if (!strncmp(name, "ebp", 3))
stack_ra = 4;
case OPLM_WORD:
if (is_lea)
ferr(po, "lea/word to arg?\n");
- if (offset & 1)
- ferr(po, "unaligned arg access\n");
- if (is_src && (offset & 2) == 0)
+ if (offset & 1) {
+ unaligned = 1;
+ if (!is_src) {
+ if (offset & 2)
+ ferr(po, "problematic arg store\n");
+ snprintf(buf, buf_size, "*(u16 *)((char *)&a%d + 1)", i + 1);
+ }
+ else
+ ferr(po, "unaligned arg word load\n");
+ }
+ else if (is_src && (offset & 2) == 0)
snprintf(buf, buf_size, "(u16)a%d", i + 1);
else
snprintf(buf, buf_size, "%sWORD(a%d)",
prefix = cast;
else if (is_src)
prefix = "(u32)";
+
if (offset & 3) {
- snprintf(g_comment, sizeof(g_comment), "%s unaligned", bp_arg);
+ unaligned = 1;
if (is_lea)
snprintf(buf, buf_size, "(u32)&a%d + %d",
i + 1, offset & 3);
- else
+ else if (!is_src)
+ ferr(po, "unaligned arg store\n");
+ else {
+ // mov edx, [ebp+arg_4+2]; movsx ecx, dx
snprintf(buf, buf_size, "%s(a%d >> %d)",
prefix, i + 1, (offset & 3) * 8);
+ }
}
else {
snprintf(buf, buf_size, "%s%sa%d",
ferr(po, "bp_arg bad lmod: %d\n", popr->lmod);
}
+ if (unaligned)
+ snprintf(g_comment, sizeof(g_comment), "%s unaligned", bp_arg);
+
// common problem
guess_lmod_from_c_type(&tmp_lmod, &g_func_pp->arg[i].type);
- if ((offset & 3) && tmp_lmod != OPLM_DWORD)
+ if (unaligned && tmp_lmod != OPLM_DWORD)
ferr(po, "bp_arg arg/w offset %d and type '%s'\n",
offset, g_func_pp->arg[i].type.name);
}
case OPT_REGMEM:
if (parse_stack_el(popr->name, NULL)
- || (g_bp_frame && IS_START(popr->name, "ebp-")))
+ || (g_bp_frame && !(po->flags & OPF_EBP_S)
+ && IS_START(popr->name, "ebp")))
{
stack_frame_access(po, popr, buf, buf_size,
popr->name, cast, 1, is_lea);
case OPT_REGMEM:
if (parse_stack_el(popr->name, NULL)
- || (g_bp_frame && IS_START(popr->name, "ebp-")))
+ || (g_bp_frame && !(po->flags & OPF_EBP_S)
+ && IS_START(popr->name, "ebp")))
{
stack_frame_access(po, popr, buf, buf_size,
popr->name, "", 0, 0);
popr1->lmod = popr2->lmod;
else if (popr2->lmod == OPLM_UNSPEC)
popr2->lmod = popr1->lmod;
- else if (popr1->lmod != popr2->lmod)
- ferr(po, "conflicting lmods: %d vs %d\n", popr1->lmod, popr2->lmod);
+ else if (popr1->lmod != popr2->lmod) {
+ if (popr1->type_from_var) {
+ popr1->size_mismatch = 1;
+ if (popr1->lmod < popr2->lmod)
+ popr1->size_lt = 1;
+ popr1->lmod = popr2->lmod;
+ }
+ else if (popr2->type_from_var) {
+ popr2->size_mismatch = 1;
+ if (popr2->lmod < popr1->lmod)
+ popr2->size_lt = 1;
+ popr2->lmod = popr1->lmod;
+ }
+ else
+ ferr(po, "conflicting lmods: %d vs %d\n",
+ popr1->lmod, popr2->lmod);
+ }
}
static const char *op_to_c(struct parsed_op *po)
return -1;
}
-static int scan_for_flag_set(int i, int *branched, int *setters,
- int *setter_cnt)
+static int scan_for_flag_set(int i, int magic, int *branched,
+ int *setters, int *setter_cnt)
{
+ struct label_ref *lr;
int ret;
while (i >= 0) {
+ if (ops[i].cc_scratch == magic) {
+ ferr(&ops[i], "%s looped\n", __func__);
+ return -1;
+ }
+ ops[i].cc_scratch = magic;
+
if (g_labels[i][0] != 0) {
*branched = 1;
- if (g_label_refs[i].next != NULL)
- return -1;
+
+ lr = &g_label_refs[i];
+ for (; lr->next; lr = lr->next) {
+ ret = scan_for_flag_set(lr->i, magic,
+ branched, setters, setter_cnt);
+ if (ret < 0)
+ return ret;
+ }
+
if (i > 0 && LAST_OP(i - 1)) {
i = g_label_refs[i].i;
continue;
}
- ret = scan_for_flag_set(g_label_refs[i].i, branched,
- setters, setter_cnt);
+ ret = scan_for_flag_set(lr->i, magic,
+ branched, setters, setter_cnt);
if (ret < 0)
return ret;
}
return -1;
}
+static void scan_fwd_set_flags(int i, int opcnt, int magic, int flags)
+{
+ struct parsed_op *po;
+ int j;
+
+ if (i < 0)
+ ferr(ops, "%s: followed bad branch?\n", __func__);
+
+ for (; i < opcnt; i++) {
+ po = &ops[i];
+ if (po->cc_scratch == magic)
+ return;
+ po->cc_scratch = magic;
+ po->flags |= flags;
+
+ if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
+ if (po->btj != NULL) {
+ // jumptable
+ for (j = 0; j < po->btj->count; j++)
+ scan_fwd_set_flags(po->btj->d[j].bt_i, opcnt, magic, flags);
+ return;
+ }
+
+ scan_fwd_set_flags(po->bt_i, opcnt, magic, flags);
+ if (!(po->flags & OPF_CC))
+ return;
+ }
+ if (po->flags & OPF_TAIL)
+ return;
+ }
+}
+
static int collect_call_args(struct parsed_op *po, int i,
struct parsed_proto *pp, int *save_arg_vars, int arg,
- int need_op_saving, int may_reuse)
+ int magic, int need_op_saving, int may_reuse)
{
struct parsed_proto *pp_tmp;
struct label_ref *lr;
int ret = 0;
int j;
- if (i < 0)
+ if (i < 0) {
ferr(po, "no refs for '%s'?\n", g_labels[i]);
+ return -1;
+ }
for (; arg < pp->argc; arg++)
if (pp->arg[arg].reg == NULL)
break;
+ magic = (magic & 0xffffff) | (arg << 24);
for (j = i; j >= 0 && arg < pp->argc; )
{
+ if (((ops[j].cc_scratch ^ magic) & 0xffffff) == 0) {
+ if (ops[j].cc_scratch != magic) {
+ ferr(&ops[j], "arg collect hit same path with diff args for %s\n",
+ pp->name);
+ return -1;
+ }
+ // ok: have already been here
+ return 0;
+ }
+ ops[j].cc_scratch = magic;
+
if (g_labels[j][0] != 0) {
lr = &g_label_refs[j];
if (lr->next != NULL)
for (; lr->next; lr = lr->next) {
if ((ops[lr->i].flags & (OPF_JMP|OPF_CC)) != OPF_JMP)
may_reuse = 1;
- ret |= collect_call_args(po, lr->i, pp, save_arg_vars,
- arg, need_op_saving, may_reuse);
+ ret = collect_call_args(po, lr->i, pp, save_arg_vars,
+ arg, magic, need_op_saving, may_reuse);
+ if (ret < 0)
+ return ret;
}
if ((ops[lr->i].flags & (OPF_JMP|OPF_CC)) != OPF_JMP)
continue;
}
need_op_saving = 1;
- ret |= collect_call_args(po, lr->i, pp, save_arg_vars,
- arg, need_op_saving, may_reuse);
+ ret = collect_call_args(po, lr->i, pp, save_arg_vars,
+ arg, magic, need_op_saving, may_reuse);
+ if (ret < 0)
+ return ret;
}
j--;
for (arg++; arg < pp->argc; arg++)
if (pp->arg[arg].reg == NULL)
break;
+ magic = (magic & 0xffffff) | (arg << 24);
}
}
lr->next = lr_new;
}
+static void output_std_flags(FILE *fout, struct parsed_op *po,
+ int *pfomask, const char *dst_opr_text)
+{
+ if (*pfomask & (1 << PFO_Z)) {
+ fprintf(fout, "\n cond_z = (%s%s == 0);",
+ lmod_cast_u(po, po->operand[0].lmod), dst_opr_text);
+ *pfomask &= ~(1 << PFO_Z);
+ }
+ if (*pfomask & (1 << PFO_S)) {
+ fprintf(fout, "\n cond_s = (%s%s < 0);",
+ lmod_cast_s(po, po->operand[0].lmod), dst_opr_text);
+ *pfomask &= ~(1 << PFO_S);
+ }
+}
+
static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
{
struct parsed_op *po, *delayed_flag_op = NULL, *tmp_op;
int save_arg_vars = 0;
int cmp_result_vars = 0;
int need_mul_var = 0;
+ int have_func_ret = 0;
int had_decl = 0;
int label_pending = 0;
int regmask_save = 0;
int regmask_arg = 0;
+ int regmask_now = 0;
int regmask = 0;
int pfomask = 0;
int found = 0;
}
}
- collect_call_args(po, i, pp, &save_arg_vars, 0, 0, 0);
+ collect_call_args(po, i, pp, &save_arg_vars,
+ 0, i + opcnt * 2, 0, 0);
if (strstr(pp->ret_type.name, "int64"))
need_mul_var = 1;
+ if (!(po->flags & OPF_TAIL) && !IS(pp->ret_type.name, "void"))
+ have_func_ret = 1;
po->datap = pp;
}
}
depth = 0;
ret = scan_for_pop(i + 1, opcnt,
- po->operand[0].name, i + opcnt, 0, &depth, 0);
+ po->operand[0].name, i + opcnt * 3, 0, &depth, 0);
if (ret == 1) {
if (depth > 1)
ferr(po, "too much depth: %d\n", depth);
po->flags |= OPF_RMD;
scan_for_pop(i + 1, opcnt, po->operand[0].name,
- i + opcnt * 2, 0, &depth, 1);
+ i + opcnt * 4, 0, &depth, 1);
continue;
}
ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, 0);
}
}
- regmask |= po->regmask_src | po->regmask_dst;
+ regmask_now = po->regmask_src | po->regmask_dst;
+ if (regmask_now & (1 << xBP)) {
+ if (g_bp_frame && !(po->flags & OPF_EBP_S)) {
+ if (po->regmask_dst & (1 << xBP))
+ // compiler decided to drop bp frame and use ebp as scratch
+ scan_fwd_set_flags(i, opcnt, i + opcnt * 5, OPF_EBP_S);
+ else
+ regmask_now &= ~(1 << xBP);
+ }
+ }
+
+ regmask |= regmask_now;
if (po->flags & OPF_CC)
{
int setters[16], cnt = 0, branched = 0;
- ret = scan_for_flag_set(i, &branched, setters, &cnt);
+ ret = scan_for_flag_set(i, i + opcnt * 6,
+ &branched, setters, &cnt);
if (ret < 0 || cnt <= 0)
ferr(po, "unable to trace flag setter(s)\n");
if (cnt > ARRAY_SIZE(setters))
pfomask = 1 << PFO_Z;
}
else {
- if ((pfo != PFO_Z && pfo != PFO_S && pfo != PFO_P)
+ // see if we'll be able to handle based on op result
+ if ((tmp_op->op != OP_AND && tmp_op->op != OP_OR
+ && pfo != PFO_Z && pfo != PFO_S && pfo != PFO_P)
+ || branched
|| scan_for_mod_opr0(tmp_op, setters[j] + 1, i) >= 0)
pfomask = 1 << pfo;
}
if (pfomask) {
tmp_op->pfomask |= pfomask;
cmp_result_vars |= pfomask;
- // note: may overwrite, currently not a problem
- po->datap = tmp_op;
}
+ // note: may overwrite, currently not a problem
+ po->datap = tmp_op;
}
if (po->op == OP_ADC || po->op == OP_SBB)
}
// declare other regs - special case for eax
- if (!((regmask | regmask_arg) & 1)
+ if (!((regmask | regmask_arg) & 1) && have_func_ret
&& !IS(g_func_pp->ret_type.name, "void"))
{
fprintf(fout, " u32 eax = 0;\n");
regmask &= ~regmask_arg;
regmask &= ~(1 << xSP);
- if (g_bp_frame)
- regmask &= ~(1 << xBP);
if (regmask) {
for (reg = 0; reg < 8; reg++) {
if (regmask & (1 << reg)) {
int is_inv = 0;
pfo = split_cond(po, po->op, &is_inv);
+ tmp_op = po->datap;
// we go through all this trouble to avoid using parsed_flag_op,
// which makes generated code much nicer
is_delayed = 1;
}
else if (last_arith_dst != NULL
- && (pfo == PFO_Z || pfo == PFO_S || pfo == PFO_P))
+ && (pfo == PFO_Z || pfo == PFO_S || pfo == PFO_P
+ || (tmp_op && (tmp_op->op == OP_AND || tmp_op->op == OP_OR))
+ ))
{
out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
out_test_for_cc(buf1, sizeof(buf1), po, pfo, is_inv,
last_arith_dst->lmod, buf3);
is_delayed = 1;
}
- else if (po->datap != NULL) {
+ else if (tmp_op != NULL) {
// use preprocessed flag calc results
- tmp_op = po->datap;
- if (!tmp_op || !(tmp_op->pfomask & (1 << pfo)))
+ if (!(tmp_op->pfomask & (1 << pfo)))
ferr(po, "not prepared for pfo %d\n", pfo);
// note: is_inv was not yet applied
case OP_OR:
propagate_lmod(po, &po->operand[0], &po->operand[1]);
// fallthrough
- case OP_SHL:
- case OP_SHR:
dualop_arith:
assert_operand_cnt(2);
fprintf(fout, " %s %s= %s;",
out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
op_to_c(po),
out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
- if (pfomask & (1 << PFO_Z)) {
- fprintf(fout, "\n cond_z = (%s == 0);", buf1);
- pfomask &= ~(1 << PFO_Z);
- }
- if (pfomask & (1 << PFO_S)) {
- fprintf(fout, "\n cond_s = ((s32)%s < 0);", buf1);
- pfomask &= ~(1 << PFO_S);
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_SHL:
+ case OP_SHR:
+ assert_operand_cnt(2);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ if (pfomask & (1 << PFO_C)) {
+ if (po->operand[1].type == OPT_CONST) {
+ l = lmod_bytes(po, po->operand[0].lmod) * 8;
+ j = po->operand[1].val;
+ j %= l;
+ if (j != 0) {
+ if (po->op == OP_SHL)
+ j = l - j;
+ else
+ j -= 1;
+ fprintf(fout, " cond_c = (%s & 0x%02x) ? 1 : 0;\n",
+ buf1, 1 << j);
+ }
+ else
+ ferr(po, "zero shift?\n");
+ }
+ else
+ ferr(po, "TODO\n");
+ pfomask &= ~(1 << PFO_C);
}
+ fprintf(fout, " %s %s= %s;", buf1, op_to_c(po),
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ output_std_flags(fout, po, &pfomask, buf1);
last_arith_dst = &po->operand[0];
delayed_flag_op = NULL;
break;
fprintf(fout, " %s = %s%s >> %s;", buf1,
lmod_cast_s(po, po->operand[0].lmod), buf1,
out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ output_std_flags(fout, po, &pfomask, buf1);
last_arith_dst = &po->operand[0];
delayed_flag_op = NULL;
break;
}
else
ferr(po, "TODO\n");
+ output_std_flags(fout, po, &pfomask, buf1);
last_arith_dst = &po->operand[0];
delayed_flag_op = NULL;
break;