OPF_VAPUSH = (1 << 18), /* vararg ptr push (as call arg) */
OPF_DONE = (1 << 19), /* already fully handled by analysis */
OPF_PPUSH = (1 << 20), /* part of complex push-pop graph */
+ OPF_NOREGS = (1 << 21), /* don't track regs of this op */
};
enum op_op {
#define MAX_OPERANDS 3
#define NAMELEN 112
+#define OPR_INIT(type_, lmod_, reg_) \
+ { type_, lmod_, reg_, }
+
struct parsed_opr {
enum opr_type type;
enum opr_lenmod lmod;
+ int reg;
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_lt:1; // type override is larger than C
unsigned int had_ds:1; // had ds: prefix
const struct parsed_proto *pp; // for OPT_LABEL
- int reg;
unsigned int val;
char name[NAMELEN];
};
break;
case OP_IMUL:
+ if (op->operand_cnt == 2) {
+ if (op->operand[0].type != OPT_REG)
+ aerr("reg expected\n");
+ op->regmask_src |= 1 << op->operand[0].reg;
+ }
if (op->operand_cnt != 1)
break;
// fallthrough
&& op->operand[0].reg == op->operand[1].reg
&& IS(op->operand[0].name, op->operand[1].name)) // ! ah, al..
{
- op->flags |= OPF_RMD | OPF_DONE;
+ op->flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
op->regmask_src = op->regmask_dst = 0;
}
break;
char buf[16];
snprintf(buf, sizeof(buf), "%s+0", op->operand[0].name);
if (IS(buf, op->operand[1].name))
- op->flags |= OPF_RMD | OPF_DONE;
+ op->flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
}
break;
}
}
-static void op_set_clear_flag(struct parsed_op *po,
- enum op_flags flag_set, enum op_flags flag_clear)
-{
- po->flags |= flag_set;
- po->flags &= ~flag_clear;
-}
-
// last op in stream - unconditional branch or ret
#define LAST_OP(_i) ((ops[_i].flags & OPF_TAIL) \
|| ((ops[_i].flags & (OPF_JMP|OPF_CJMP|OPF_RMD)) == OPF_JMP \
// note: this skips over calls and rm'd stuff assuming they're handled
// so it's intended to use at one of final passes
static int scan_for_pop(int i, int opcnt, int magic, int reg,
- int depth, int *maxdepth, int do_flags)
+ int depth, int flags_set)
{
- const struct parsed_proto *pp;
struct parsed_op *po;
int relevant;
int ret = 0;
for (; i < opcnt; i++) {
po = &ops[i];
if (po->cc_scratch == magic)
- break; // already checked
+ return ret; // already checked
po->cc_scratch = magic;
if (po->flags & OPF_TAIL) {
if (po->op == OP_CALL) {
- pp = proto_parse(g_fhdr, po->operand[0].name, g_quiet_pp);
- if (pp != NULL && pp->is_noreturn)
- // no stack cleanup for noreturn
- return ret;
+ if (po->pp != NULL && po->pp->is_noreturn)
+ // assume no stack cleanup for noreturn
+ return 1;
}
return -1; // deadend
}
for (j = 0; j < po->btj->count; j++) {
check_i(po, po->btj->d[j].bt_i);
ret |= scan_for_pop(po->btj->d[j].bt_i, opcnt, magic, reg,
- depth, maxdepth, do_flags);
+ depth, flags_set);
if (ret < 0)
return ret; // dead end
}
check_i(po, po->bt_i);
if (po->flags & OPF_CJMP) {
ret |= scan_for_pop(po->bt_i, opcnt, magic, reg,
- depth, maxdepth, do_flags);
+ depth, flags_set);
if (ret < 0)
return ret; // dead end
}
if (po->op == OP_PUSH) {
depth++;
- if (relevant) {
- if (depth > *maxdepth)
- *maxdepth = depth;
- if (do_flags)
- op_set_clear_flag(po, OPF_RSAVE, OPF_RMD);
- }
}
else if (po->op == OP_POP) {
- if (depth == 0) {
- if (relevant && do_flags)
- op_set_clear_flag(po, OPF_RMD, OPF_RSAVE);
+ if (relevant && depth == 0) {
+ po->flags |= flags_set;
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);
- }
+ depth--;
}
}
- return ret;
+ return -1;
}
// scan for 'reg' pop backwards starting from i
return -1;
}
- return 0;
+ return 1;
}
// scan for one or more pop of push <const>
return;
}
+ if (po->flags & OPF_RMD)
+ continue;
check_i(po, po->bt_i);
if (po->flags & OPF_CJMP)
scan_propagate_df(po->bt_i, opcnt);
static int is_opr_read(const struct parsed_opr *opr,
const struct parsed_op *po)
{
- int mask;
-
if (opr->type == OPT_REG) {
- mask = po->regmask_src;
- if (po->op == OP_CALL)
- // assume worst case
- mask |= (1 << xAX) | (1 << xCX) | (1 << xDX);
- if ((1 << opr->reg) & mask)
+ if (po->regmask_src & (1 << opr->reg))
return 1;
else
return 0;
{
int mask;
- if (!(po->flags & OPF_DATA))
- return 0;
-
if (opr->type == OPT_REG) {
if (po->op == OP_CALL) {
- mask = (1 << xAX) | (1 << xCX) | (1 << xDX);
- if ((1 << opr->reg) & mask)
+ mask = po->regmask_dst;
+ mask |= (1 << xAX) | (1 << xCX) | (1 << xDX); // ?
+ if (mask & (1 << opr->reg))
return 1;
else
return 0;
}
- if (po->operand[0].type == OPT_REG) {
- if (po->regmask_dst & (1 << opr->reg))
- return 1;
- else
- return 0;
- }
+ if (po->regmask_dst & (1 << opr->reg))
+ return 1;
+ else
+ return 0;
}
return IS(po->operand[0].name, opr->name);
if (do_update && *adj >= 0) {
po->flags |= OPF_RMD;
if (!*is_multipath)
- po->flags |= OPF_DONE;
+ po->flags |= OPF_DONE | OPF_NOREGS;
}
*adj += lmod_bytes(po, po->operand[0].lmod);
}
}
+static int get_pp_arg_regmask_src(const struct parsed_proto *pp)
+{
+ int regmask = 0;
+ int i, reg;
+
+ for (i = 0; i < pp->argc; i++) {
+ if (pp->arg[i].reg != NULL) {
+ reg = char_array_i(regs_r32,
+ ARRAY_SIZE(regs_r32), pp->arg[i].reg);
+ if (reg < 0)
+ ferr(ops, "arg '%s' of func '%s' is not a reg?\n",
+ pp->arg[i].reg, pp->name);
+ regmask |= 1 << reg;
+ }
+ }
+
+ return regmask;
+}
+
+static int get_pp_arg_regmask_dst(const struct parsed_proto *pp)
+{
+ if (strstr(pp->ret_type.name, "int64"))
+ return (1 << xAX) | (1 << xDX);
+ if (strcasecmp(pp->ret_type.name, "void") == 0)
+ return 0;
+
+ return (1 << xAX);
+}
+
static void resolve_branches_parse_calls(int opcnt)
{
const struct parsed_proto *pp_c;
struct parsed_data *pd;
struct parsed_op *po;
const char *tmpname;
- int i, l, ret;
+ int i, l;
+ int ret;
for (i = 0; i < opcnt; i++)
{
&& IS(opr_name(&ops[1], 1), "esp"))
{
g_bp_frame = 1;
- ops[0].flags |= OPF_RMD | OPF_DONE;
- ops[1].flags |= OPF_RMD | OPF_DONE;
+ ops[0].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+ ops[1].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
i = 2;
if (ops[2].op == OP_SUB && IS(opr_name(&ops[2], 0), "esp")) {
g_stack_fsz = opr_const(&ops[2], 1);
- ops[2].flags |= OPF_RMD | OPF_DONE;
+ ops[2].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
i++;
}
else {
i = 2;
while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
g_stack_fsz += 4;
- ops[i].flags |= OPF_RMD | OPF_DONE;
+ ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
ecx_push++;
i++;
}
&& IS(opr_name(&ops[i + 1], 0), "__alloca_probe"))
{
g_stack_fsz += ops[i].operand[1].val;
- ops[i].flags |= OPF_RMD | OPF_DONE;
+ ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
i++;
- ops[i].flags |= OPF_RMD | OPF_DONE;
+ ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
i++;
}
}
if ((ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ebp"))
|| ops[j].op == OP_LEAVE)
{
- ops[j].flags |= OPF_RMD | OPF_DONE;
+ ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
}
else if (ops[i].op == OP_CALL && ops[i].pp != NULL
&& ops[i].pp->is_noreturn)
&& IS(opr_name(&ops[j - 1], 0), "esp")
&& IS(opr_name(&ops[j - 1], 1), "ebp"))
{
- ops[j - 1].flags |= OPF_RMD | OPF_DONE;
+ ops[j - 1].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
j -= 2;
}
else if (!(g_ida_func_attr & IDAFA_NORETURN))
// non-bp frame
i = 0;
while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
- ops[i].flags |= OPF_RMD | OPF_DONE;
+ ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
g_stack_fsz += 4;
ecx_push++;
i++;
&& ops[i].operand[1].type == OPT_CONST)
{
g_stack_fsz = ops[i].operand[1].val;
- ops[i].flags |= OPF_RMD | OPF_DONE;
+ ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
esp_sub = 1;
break;
}
while (i > 0 && j > 0) {
i--;
if (ops[i].op == OP_PUSH) {
- ops[i].flags &= ~(OPF_RMD | OPF_DONE);
+ ops[i].flags &= ~(OPF_RMD | OPF_DONE | OPF_NOREGS);
j--;
}
}
else
ferr(&ops[j], "'pop ecx' expected\n");
- ops[j].flags |= OPF_RMD | OPF_DONE;
+ ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
j--;
}
if (l != ecx_push)
|| ops[j].operand[1].val != g_stack_fsz)
ferr(&ops[j], "'add esp' expected\n");
- ops[j].flags |= OPF_RMD | OPF_DONE;
+ ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
ops[j].operand[1].val = 0; // hack for stack arg scanner
found = 1;
}
}
// find next instruction that reads opr
-// if multiple results are found - fail
// *op_i must be set to -1 by the caller
-// returns 1 if found, *op_i is then set to referencer insn
+// on return, *op_i is set to first referencer insn
+// returns 1 if exactly 1 referencer is found
static int find_next_read(int i, int opcnt,
const struct parsed_opr *opr, int magic, int *op_i)
{
for (; i < opcnt; i++)
{
if (ops[i].cc_scratch == magic)
- return 0;
+ return ret;
ops[i].cc_scratch = magic;
po = &ops[i];
continue;
check_i(po, po->bt_i);
if (po->flags & OPF_CJMP) {
- ret = find_next_read(po->bt_i, opcnt, opr, magic, op_i);
+ ret |= find_next_read(po->bt_i, opcnt, opr, magic, op_i);
if (ret < 0)
return ret;
}
-
- i = po->bt_i - 1;
+ else
+ i = po->bt_i - 1;
continue;
}
if (!is_opr_read(opr, po)) {
if (is_opr_modified(opr, po))
// it's overwritten
- return 0;
+ return ret;
if (po->flags & OPF_TAIL)
- return 0;
+ return ret;
continue;
}
if (ret == 1 && call_i == ref_i) {
// and nothing uses it after us?
ref_i = -1;
- ret = find_next_read(i + 1, opcnt, &po->operand[0],
- i + opcnt * 11, &ref_i);
- if (ret != 1)
+ find_next_read(i + 1, opcnt, &po->operand[0],
+ i + opcnt * 11, &ref_i);
+ if (ref_i == -1)
// then also don't need the source mov
- ops[call_i].flags |= OPF_RMD;
+ ops[call_i].flags |= OPF_RMD | OPF_NOREGS;
}
}
break;
if (!g_func_pp->is_vararg
|| strstr(ops[k].operand[1].name, buf))
{
- ops[k].flags |= OPF_RMD | OPF_DONE;
- ops[j].flags |= OPF_RMD | OPF_VAPUSH;
+ ops[k].flags |= OPF_RMD | OPF_NOREGS | OPF_DONE;
+ ops[j].flags |= OPF_RMD | OPF_NOREGS | OPF_VAPUSH;
save_args &= ~(1 << arg);
reg = -1;
}
return ret;
}
+static void reg_use_pass(int i, int opcnt, unsigned char *cbits,
+ int regmask_now, int *regmask,
+ int regmask_save_now, int *regmask_save,
+ int *regmask_init, int regmask_arg)
+{
+ struct parsed_op *po;
+ int already_saved;
+ int regmask_new;
+ int regmask_op;
+ int flags_set;
+ int ret, reg;
+ int j;
+
+ for (; i < opcnt; i++)
+ {
+ po = &ops[i];
+ if (cbits[i >> 3] & (1 << (i & 7)))
+ return;
+ cbits[i >> 3] |= (1 << (i & 7));
+
+ if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ continue;
+ if (po->btj != NULL) {
+ for (j = 0; j < po->btj->count; j++) {
+ check_i(po, po->btj->d[j].bt_i);
+ reg_use_pass(po->btj->d[j].bt_i, opcnt, cbits,
+ regmask_now, regmask, regmask_save_now, regmask_save,
+ regmask_init, regmask_arg);
+ }
+ return;
+ }
+
+ check_i(po, po->bt_i);
+ if (po->flags & OPF_CJMP)
+ reg_use_pass(po->bt_i, opcnt, cbits,
+ regmask_now, regmask, regmask_save_now, regmask_save,
+ regmask_init, regmask_arg);
+ else
+ i = po->bt_i - 1;
+ continue;
+ }
+
+ if (po->op == OP_PUSH && !(po->flags & (OPF_FARG|OPF_DONE))
+ && !g_func_pp->is_userstack
+ && po->operand[0].type == OPT_REG)
+ {
+ reg = po->operand[0].reg;
+ ferr_assert(po, reg >= 0);
+
+ already_saved = 0;
+ flags_set = OPF_RSAVE | OPF_RMD | OPF_DONE;
+ if (regmask_now & (1 << reg)) {
+ already_saved = regmask_save_now & (1 << reg);
+ flags_set = OPF_RSAVE | OPF_DONE;
+ }
+
+ ret = scan_for_pop(i + 1, opcnt, i + opcnt * 3, reg, 0, 0);
+ if (ret == 1) {
+ scan_for_pop(i + 1, opcnt, i + opcnt * 4, reg, 0, flags_set);
+ }
+ else {
+ ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, 0);
+ if (ret == 1) {
+ scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg,
+ flags_set);
+ }
+ }
+ if (ret == 1) {
+ ferr_assert(po, !already_saved);
+ po->flags |= flags_set;
+
+ if (regmask_now & (1 << reg)) {
+ regmask_save_now |= (1 << reg);
+ *regmask_save |= regmask_save_now;
+ }
+ continue;
+ }
+ }
+ else if (po->op == OP_POP && (po->flags & OPF_RSAVE)) {
+ reg = po->operand[0].reg;
+ ferr_assert(po, reg >= 0);
+
+ if (regmask_save_now & (1 << reg))
+ regmask_save_now &= ~(1 << reg);
+ else
+ regmask_now &= ~(1 << reg);
+ continue;
+ }
+ else if (po->op == OP_CALL) {
+ if ((po->regmask_dst & (1 << xAX))
+ && !(po->regmask_dst & (1 << xDX)))
+ {
+ if (po->flags & OPF_TAIL)
+ // don't need eax, will do "return f();" or "f(); return;"
+ po->regmask_dst &= ~(1 << xAX);
+ else {
+ struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xAX);
+ j = -1;
+ find_next_read(i + 1, opcnt, &opr, i + opcnt * 17, &j);
+ if (j == -1)
+ // not used
+ po->regmask_dst &= ~(1 << xAX);
+ }
+ }
+ }
+
+ if (po->flags & OPF_NOREGS)
+ continue;
+
+ regmask_op = po->regmask_src | po->regmask_dst;
+
+ regmask_new = po->regmask_src & ~regmask_now & ~regmask_arg;
+ regmask_new &= ~(1 << xSP);
+ if (g_bp_frame && !(po->flags & OPF_EBP_S))
+ regmask_new &= ~(1 << xBP);
+
+ if (po->op == OP_CALL) {
+ // allow fastcall calls from anywhere, calee may be also sitting
+ // in some fastcall table even when it's not using reg args
+ if (regmask_new & po->regmask_src & (1 << xCX)) {
+ *regmask_init |= (1 << xCX);
+ regmask_now |= (1 << xCX);
+ regmask_new &= ~(1 << xCX);
+ }
+ if (regmask_new & po->regmask_src & (1 << xDX)) {
+ *regmask_init |= (1 << xDX);
+ regmask_now |= (1 << xDX);
+ regmask_new &= ~(1 << xDX);
+ }
+ }
+
+ if (regmask_new != 0)
+ fnote(po, "uninitialized reg mask: %x\n", regmask_new);
+
+ if (regmask_op & (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 + 1, opcnt, i + opcnt * 5, OPF_EBP_S);
+ else
+ regmask_op &= ~(1 << xBP);
+ }
+ }
+
+ regmask_now |= regmask_op;
+ *regmask |= regmask_now;
+
+ if (po->flags & OPF_TAIL)
+ return;
+ }
+}
+
static void pp_insert_reg_arg(struct parsed_proto *pp, const char *reg)
{
int i;
fprintf(fout, ")");
}
-static int get_pp_arg_regmask(const struct parsed_proto *pp)
-{
- int regmask = 0;
- int i, reg;
-
- for (i = 0; i < pp->argc; i++) {
- if (pp->arg[i].reg != NULL) {
- reg = char_array_i(regs_r32,
- ARRAY_SIZE(regs_r32), pp->arg[i].reg);
- if (reg < 0)
- ferr(ops, "arg '%s' of func '%s' is not a reg?\n",
- pp->arg[i].reg, pp->name);
- regmask |= 1 << reg;
- }
- }
-
- return regmask;
-}
-
static char *saved_arg_name(char *buf, size_t buf_size, int grp, int num)
{
char buf1[16];
struct parsed_data *pd;
unsigned int uval;
int save_arg_vars[MAX_ARG_GRP] = { 0, };
+ unsigned char cbits[MAX_OPS / 8];
int cond_vars = 0;
int need_tmp_var = 0;
int need_tmp64 = 0;
int had_decl = 0;
int label_pending = 0;
int regmask_save = 0; // regs saved/restored in this func
- int regmask_arg = 0; // regs carrying function args (fastcall, etc)
+ int regmask_arg; // regs from this function args (fastcall, etc)
+ int regmask_ret; // regs needed on ret
int regmask_now; // temp
int regmask_init = 0; // regs that need zero initialization
int regmask_pp = 0; // regs used in complex push-pop graph
int regmask = 0; // used regs
int pfomask = 0;
int found = 0;
- int depth = 0;
int no_output;
int i, j, l;
int arg;
if (g_func_pp == NULL)
ferr(ops, "proto_parse failed for '%s'\n", funcn);
- regmask_arg = get_pp_arg_regmask(g_func_pp);
+ regmask_arg = get_pp_arg_regmask_src(g_func_pp);
+ regmask_ret = get_pp_arg_regmask_dst(g_func_pp);
+
+ if (g_func_pp->has_retreg) {
+ for (arg = 0; arg < g_func_pp->argc; arg++) {
+ if (g_func_pp->arg[arg].type.is_retreg) {
+ reg = char_array_i(regs_r32,
+ ARRAY_SIZE(regs_r32), g_func_pp->arg[arg].reg);
+ ferr_assert(ops, reg >= 0);
+ regmask_ret |= 1 << reg;
+ }
+ }
+ }
// pass1:
// - resolve all branches
// pass3:
// - remove dead labels
+ // - set regs needed at ret
for (i = 0; i < opcnt; i++)
{
if (g_labels[i] != NULL && g_label_refs[i].i == -1) {
free(g_labels[i]);
g_labels[i] = NULL;
}
+
+ if (ops[i].op == OP_RET)
+ ops[i].regmask_src |= regmask_ret;
}
// pass4:
patch_esp_adjust(&ops[j], pp->argc_stack * 4);
else {
for (l = 0; l < pp->argc_stack; l++)
- ops[j + l].flags |= OPF_DONE | OPF_RMD;
+ ops[j + l].flags |= OPF_DONE | OPF_RMD | OPF_NOREGS;
}
}
}
// pass5:
- // - process calls
- // - handle push <const>/pop pairs
+ // - process calls, stage 2
+ // - handle some push/pop pairs
+ // - scan for STD/CLD, propagate DF
for (i = 0; i < opcnt; i++)
{
po = &ops[i];
- if (po->flags & (OPF_RMD|OPF_DONE))
+ if (po->flags & OPF_RMD)
continue;
- if (po->op == OP_CALL && !(po->flags & OPF_DONE))
+ if (po->op == OP_CALL)
{
- pp = process_call(i, opcnt);
+ if (!(po->flags & OPF_DONE)) {
+ pp = process_call(i, opcnt);
- if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) {
- // since we know the args, collect them
- collect_call_args(po, i, pp, ®mask, save_arg_vars,
- i + opcnt * 2);
+ if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) {
+ // since we know the args, collect them
+ collect_call_args(po, i, pp, ®mask, save_arg_vars,
+ i + opcnt * 2);
+ }
+ // for unresolved, collect after other passes
}
+ pp = po->pp;
+ ferr_assert(po, pp != NULL);
+
+ po->regmask_src |= get_pp_arg_regmask_src(pp);
+ po->regmask_dst |= get_pp_arg_regmask_dst(pp);
+
if (strstr(pp->ret_type.name, "int64"))
need_tmp64 = 1;
+
+ continue;
}
- else if (po->op == OP_PUSH && !(po->flags & OPF_FARG)
+
+ if (po->flags & OPF_DONE)
+ continue;
+
+ if (po->op == OP_PUSH && !(po->flags & OPF_FARG)
&& !(po->flags & OPF_RSAVE) && po->operand[0].type == OPT_CONST)
{
scan_for_pop_const(i, opcnt, i + opcnt * 12);
}
else if (po->op == OP_POP)
scan_pushes_for_pop(i, opcnt, ®mask_pp);
+ else if (po->op == OP_STD) {
+ po->flags |= OPF_DF | OPF_RMD | OPF_DONE;
+ scan_propagate_df(i + 1, opcnt);
+ }
}
// pass6:
// - find POPs for PUSHes, rm both
- // - scan for STD/CLD, propagate DF
// - scan for all used registers
+ memset(cbits, 0, sizeof(cbits));
+ reg_use_pass(0, opcnt, cbits, 0, ®mask,
+ 0, ®mask_save, ®mask_init, regmask_arg);
+
+ // pass7:
// - find flag set ops for their users
- // - do unreselved calls
+ // - do unresolved calls
// - declare indirect functions
for (i = 0; i < opcnt; i++)
{
if (po->flags & (OPF_RMD|OPF_DONE))
continue;
- if (po->op == OP_PUSH && !(po->flags & OPF_FARG)
- && !(po->flags & OPF_RSAVE) && !g_func_pp->is_userstack)
- {
- if (po->operand[0].type == OPT_REG)
- {
- reg = po->operand[0].reg;
- if (reg < 0)
- ferr(po, "reg not set for push?\n");
-
- // FIXME: OPF_RSAVE, regmask_save
- depth = 0;
- ret = scan_for_pop(i + 1, opcnt, i + opcnt * 3,
- po->operand[0].reg, 0, &depth, 0);
- if (ret == 1) {
- if (depth > 0)
- ferr(po, "too much depth: %d\n", depth);
-
- po->flags |= OPF_RMD;
- scan_for_pop(i + 1, opcnt, i + opcnt * 4,
- po->operand[0].reg, 0, &depth, 1);
- continue;
- }
- ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, 0);
- if (ret == 0) {
- arg = OPF_RMD;
- if (regmask & (1 << reg)) {
- if (regmask_save & (1 << reg))
- ferr(po, "%s already saved?\n", po->operand[0].name);
- // arg = OPF_RSAVE; // FIXME
- }
- po->flags |= arg;
- scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, arg);
- continue;
- }
- }
- }
-
- if (po->op == OP_STD) {
- po->flags |= OPF_DF | OPF_RMD | OPF_DONE;
- scan_propagate_df(i + 1, opcnt);
- }
-
- 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 + 1, 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;
else if (po->op == OP_CALL) {
// note: resolved non-reg calls are OPF_DONE already
pp = po->pp;
- if (pp == NULL)
- ferr(po, "NULL pp\n");
+ ferr_assert(po, pp != NULL);
if (pp->is_unresolved) {
int regmask_stack = 0;
if (pp->argc_stack > 0)
pp->is_stdcall = 1;
}
-
- for (arg = 0; arg < pp->argc; arg++) {
- if (pp->arg[arg].reg != NULL) {
- reg = char_array_i(regs_r32,
- ARRAY_SIZE(regs_r32), pp->arg[arg].reg);
- if (reg < 0)
- ferr(ops, "arg '%s' is not a reg?\n", pp->arg[arg].reg);
- if (!(regmask & (1 << reg))) {
- regmask_init |= 1 << reg;
- regmask |= 1 << reg;
- }
- }
- }
}
else if (po->op == OP_MOV && po->operand[0].pp != NULL
&& po->operand[1].pp != NULL)
}
}
}
- else if (po->op == OP_RET && !IS(g_func_pp->ret_type.name, "void"))
- regmask |= 1 << xAX;
else if (po->op == OP_DIV || po->op == OP_IDIV) {
// 32bit division is common, look for it
if (po->op == OP_DIV)
}
}
- // pass7:
- // - 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 starts here
// define userstack size
pfomask = po->pfomask;
if (po->flags & (OPF_REPZ|OPF_REPNZ)) {
- struct parsed_opr opr = {0,};
- opr.type = OPT_REG;
- opr.reg = xCX;
- opr.lmod = OPLM_DWORD;
+ struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xCX);
ret = try_resolve_const(i, &opr, opcnt * 7 + i, &uval);
if (ret != 1 || uval == 0) {
}
else if (!IS(pp->ret_type.name, "void")) {
if (po->flags & OPF_TAIL) {
- if (!IS(g_func_pp->ret_type.name, "void")) {
+ if (regmask_ret & (1 << xAX)) {
fprintf(fout, "return ");
if (g_func_pp->ret_type.is_ptr != pp->ret_type.is_ptr)
fprintf(fout, "(%s)", g_func_pp->ret_type.name);
}
}
- else if (regmask & (1 << xAX)) {
+ else if (po->regmask_dst & (1 << xAX)) {
fprintf(fout, "eax = ");
if (pp->ret_type.is_ptr)
fprintf(fout, "(u32)");
ret = 0;
else if (IS(pp->ret_type.name, "void"))
ret = 1;
- else if (IS(g_func_pp->ret_type.name, "void"))
+ else if (!(regmask_ret & (1 << xAX)))
ret = 1;
// else already handled as 'return f()'
if (ret) {
- if (!IS(g_func_pp->ret_type.name, "void")) {
+ if (regmask_ret & (1 << xAX)) {
ferr(po, "int func -> void func tailcall?\n");
}
else {
g_func_pp->arg[arg].reg, g_func_pp->arg[arg].reg);
}
- if (IS(g_func_pp->ret_type.name, "void")) {
+ if (!(regmask_ret & (1 << xAX))) {
if (i != opcnt - 1 || label_pending)
fprintf(fout, " return;");
}
struct func_proto_dep *dep;
struct parsed_op *po;
int from_caller = 0;
- int depth;
int j, l;
int reg;
int ret;
else if (po->op == OP_PUSH && po->operand[0].type == OPT_REG)
{
reg = po->operand[0].reg;
- if (reg < 0)
- ferr(po, "reg not set for push?\n");
+ ferr_assert(po, reg >= 0);
if (po->flags & OPF_RSAVE) {
regmask_save |= 1 << reg;
if (po->flags & OPF_DONE)
continue;
- depth = 0;
- ret = scan_for_pop(i + 1, opcnt, i + opcnt * 2,
- po->operand[0].reg, 0, &depth, 0);
+ ret = scan_for_pop(i + 1, opcnt, i + opcnt * 2, reg, 0, 0);
if (ret == 1) {
regmask_save |= 1 << reg;
po->flags |= OPF_RMD;
- scan_for_pop(i + 1, opcnt, i + opcnt * 3,
- po->operand[0].reg, 0, &depth, 1);
+ scan_for_pop(i + 1, opcnt, i + opcnt * 3, reg, 0, OPF_RMD);
continue;
}
}
ret = 1;
}
else {
- struct parsed_opr opr = { 0, };
- opr.type = OPT_REG;
- opr.reg = xAX;
+ struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xAX);
j = -1;
from_caller = 0;
ret = resolve_origin(i, &opr, i + opcnt * 4, &j, &from_caller);
patch_esp_adjust(&ops[j], pp->argc_stack * 4);
else {
for (l = 0; l < pp->argc_stack; l++)
- ops[j + l].flags |= OPF_DONE | OPF_RMD;
+ ops[j + l].flags |= OPF_DONE | OPF_RMD | OPF_NOREGS;
}
}
&& po->operand[0].reg != xCX)
{
ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, 0);
- if (ret == 0) {
+ if (ret == 1) {
// regmask_save |= 1 << po->operand[0].reg; // do it later
po->flags |= OPF_RSAVE | OPF_RMD | OPF_DONE;
scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, OPF_RMD);
fp->pp = &pp_cache[i];
fp->argc_stack = fp->pp->argc_stack;
fp->is_stdcall = fp->pp->is_stdcall;
- fp->regmask_dep = get_pp_arg_regmask(fp->pp);
+ fp->regmask_dep = get_pp_arg_regmask_src(fp->pp);
fp->has_ret = !IS(fp->pp->ret_type.name, "void");
}
unsigned long addr = strtoul(p, NULL, 16);
unsigned long f_addr = strtoul(g_func + 4, NULL, 16);
if (addr > f_addr && !scanned_ahead) {
- anote("scan_ahead caused by '%s', addr %lx\n",
- g_func, addr);
+ //anote("scan_ahead caused by '%s', addr %lx\n",
+ // g_func, addr);
scan_ahead(fasm);
scanned_ahead = 1;
func_chunks_sorted = 0;