|| ((ops[_i].flags & (OPF_JMP|OPF_CJMP|OPF_RMD)) == OPF_JMP \
&& ops[_i].op != OP_CALL))
+#define check_i(po, i) \
+ if ((i) < 0) \
+ ferr(po, "bad " #i ": %d\n", i)
+
static int scan_for_pop(int i, int opcnt, const char *reg,
int magic, int depth, int *maxdepth, int do_flags)
{
if (po->btj != NULL) {
// jumptable
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, reg, magic,
depth, maxdepth, do_flags);
if (ret < 0)
return ret;
}
- if (po->bt_i < 0) {
- ferr(po, "dead branch\n");
- return -1;
- }
-
+ check_i(po, po->bt_i);
if (po->flags & OPF_CJMP) {
ret |= scan_for_pop(po->bt_i, opcnt, reg, magic,
depth, maxdepth, do_flags);
if (po->flags & OPF_JMP) {
if (po->btj != NULL) {
// jumptable
- for (j = 0; j < po->btj->count; j++)
+ for (j = 0; j < po->btj->count; j++) {
+ check_i(po, po->btj->d[j].bt_i);
scan_propagate_df(po->btj->d[j].bt_i, opcnt);
+ }
return;
}
- if (po->bt_i < 0) {
- ferr(po, "dead branch\n");
- return;
- }
-
+ check_i(po, po->bt_i);
if (po->flags & OPF_CJMP)
scan_propagate_df(po->bt_i, opcnt);
else
ferr(po, "missing DF clear?\n");
}
+// is operand 'opr' referenced by parsed_op 'po'?
+static int is_opr_referenced(const struct parsed_opr *opr,
+ const struct parsed_op *po)
+{
+ int i, mask;
+
+ if (opr->type == OPT_REG) {
+ mask = po->regmask_dst | po->regmask_src;
+ if (po->op == OP_CALL)
+ mask |= (1 << xAX) | (1 << xCX) | (1 << xDX);
+ if ((1 << opr->reg) & mask)
+ return 1;
+ else
+ return 0;
+ }
+
+ for (i = 0; i < po->operand_cnt; i++)
+ if (IS(po->operand[0].name, opr->name))
+ return 1;
+
+ return 0;
+}
+
+// is operand 'opr' read by parsed_op 'po'?
+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)
+ return 1;
+ else
+ return 0;
+ }
+
+ // yes I'm lazy
+ return 0;
+}
+
// is operand 'opr' modified by parsed_op 'po'?
static int is_opr_modified(const struct parsed_opr *opr,
const struct parsed_op *po)
{
int mask;
- if ((po->flags & OPF_RMD) || !(po->flags & OPF_DATA))
+ if (!(po->flags & OPF_DATA))
return 0;
if (opr->type == OPT_REG) {
return -1;
}
-#define check_i(po, i) \
- if ((i) < 0) \
- ferr(po, "bad " #i ": %d\n", i)
-
static int scan_for_flag_set(int i, int magic, int *branched,
int *setters, int *setter_cnt)
{
}
static void scan_for_call_type(int i, const struct parsed_opr *opr,
- int magic, const struct parsed_proto **pp_found, int *multi)
+ int magic, const struct parsed_proto **pp_found, int *pp_i,
+ int *multi)
{
const struct parsed_proto *pp = NULL;
struct parsed_op *po;
lr = &g_label_refs[i];
for (; lr != NULL; lr = lr->next) {
check_i(&ops[i], lr->i);
- scan_for_call_type(lr->i, opr, magic, pp_found, multi);
+ scan_for_call_type(lr->i, opr, magic, pp_found, pp_i, multi);
}
if (i > 0 && LAST_OP(i - 1))
return;
}
*multi = 1;
}
- if (pp != NULL)
+ if (pp != NULL) {
*pp_found = pp;
+ *pp_i = po - ops;
+ }
}
// early check for tail call or branch back
}
static const struct parsed_proto *resolve_icall(int i, int opcnt,
- int *multi_src)
+ int *pp_i, int *multi_src)
{
const struct parsed_proto *pp = NULL;
int search_advice = 0;
*multi_src = 0;
+ *pp_i = -1;
switch (ops[i].operand[0].type) {
case OPT_REGMEM:
// fallthrough
default:
scan_for_call_type(i, &ops[i].operand[0], i + opcnt * 9, &pp,
- multi_src);
+ pp_i, multi_src);
break;
}
}
// find an instruction that changed opr before i op
-// *op_i must be set to -1 by caller
+// *op_i must be set to -1 by the caller
// *entry is set to 1 if one source is determined to be the caller
// returns 1 if found, *op_i is then set to origin
static int resolve_origin(int i, const struct parsed_opr *opr,
}
}
+// find an instruction that previously referenced 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
+static int resolve_last_ref(int i, const struct parsed_opr *opr,
+ int magic, int *op_i)
+{
+ struct label_ref *lr;
+ int ret = 0;
+
+ if (ops[i].cc_scratch == magic)
+ return 0;
+ ops[i].cc_scratch = magic;
+
+ while (1) {
+ if (g_labels[i] != NULL) {
+ lr = &g_label_refs[i];
+ for (; lr != NULL; lr = lr->next) {
+ check_i(&ops[i], lr->i);
+ ret |= resolve_last_ref(lr->i, opr, magic, op_i);
+ }
+ if (i > 0 && LAST_OP(i - 1))
+ return ret;
+ }
+
+ i--;
+ if (i < 0)
+ return -1;
+
+ if (ops[i].cc_scratch == magic)
+ return 0;
+ ops[i].cc_scratch = magic;
+
+ if (!is_opr_referenced(opr, &ops[i]))
+ continue;
+
+ if (*op_i >= 0)
+ return -1;
+
+ *op_i = i;
+ return 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
+static int find_next_read(int i, int opcnt,
+ const struct parsed_opr *opr, int magic, int *op_i)
+{
+ struct parsed_op *po;
+ int j, ret = 0;
+
+ for (; i < opcnt; i++)
+ {
+ if (ops[i].cc_scratch == magic)
+ return 0;
+ ops[i].cc_scratch = magic;
+
+ po = &ops[i];
+ if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
+ if (po->btj != NULL) {
+ // jumptable
+ for (j = 0; j < po->btj->count; j++) {
+ check_i(po, po->btj->d[j].bt_i);
+ ret |= find_next_read(po->btj->d[j].bt_i, opcnt, opr,
+ magic, op_i);
+ }
+ return ret;
+ }
+
+ if (po->flags & OPF_RMD)
+ continue;
+ check_i(po, po->bt_i);
+ if (po->flags & OPF_CJMP) {
+ ret = find_next_read(po->bt_i, opcnt, opr, magic, op_i);
+ if (ret < 0)
+ return ret;
+ }
+
+ i = po->bt_i - 1;
+ continue;
+ }
+
+ if (!is_opr_read(opr, po)) {
+ if (is_opr_modified(opr, po))
+ // it's overwritten
+ return 0;
+ if (po->flags & OPF_TAIL)
+ return 0;
+ continue;
+ }
+
+ if (*op_i >= 0)
+ return -1;
+
+ *op_i = i;
+ return 1;
+ }
+
+ return 0;
+}
+
static int try_resolve_const(int i, const struct parsed_opr *opr,
int magic, unsigned int *val)
{
const struct parsed_proto *pp_c;
struct parsed_proto *pp;
const char *tmpname;
+ int call_i = -1, ref_i = -1;
int adj = 0, multipath = 0;
int ret, arg;
if (pp == NULL)
{
// indirect call
- pp_c = resolve_icall(i, opcnt, &multipath);
+ pp_c = resolve_icall(i, opcnt, &call_i, &multipath);
if (pp_c != NULL) {
if (!pp_c->is_func && !pp_c->is_fptr)
ferr(po, "call to non-func: %s\n", pp_c->name);
case OPT_REG:
// we resolved this call and no longer need the register
po->regmask_src &= ~(1 << po->operand[0].reg);
+
+ if (!multipath && i != call_i && ops[call_i].op == OP_MOV
+ && ops[call_i].operand[1].type == OPT_LABEL)
+ {
+ // no other source users?
+ ret = resolve_last_ref(i, &po->operand[0], opcnt * 10,
+ &ref_i);
+ 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],
+ opcnt * 11, &ref_i);
+ if (ret != 1)
+ // then also don't need the source mov
+ ops[call_i].flags |= OPF_RMD;
+ }
+ }
break;
case OPT_REGMEM:
pp->is_fptr = 1;
if (po->btj != NULL) {
// jumptable
for (j = 0; j < po->btj->count; j++) {
+ check_i(po, po->btj->d[j].bt_i);
gen_hdr_dep_pass(po->btj->d[j].bt_i, opcnt, cbits, fp,
regmask_save, regmask_dst, regmask_dep, has_ret);
}
return;
}
- if (po->bt_i < 0) {
- ferr(po, "dead branch\n");
- return;
- }
-
+ check_i(po, po->bt_i);
if (po->flags & OPF_CJMP) {
gen_hdr_dep_pass(po->bt_i, opcnt, cbits, fp,
regmask_save, regmask_dst, regmask_dep, has_ret);