}
}
-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);
}
}
}
// scan back for cdq, if anything modifies edx, fail
static int scan_for_cdq_edx(int i)
{
- for (; i >= 0; i--) {
+ while (i >= 0) {
+ if (g_labels[i][0] != 0) {
+ if (g_label_refs[i].next != NULL)
+ return -1;
+ if (i > 0 && LAST_OP(i - 1)) {
+ i = g_label_refs[i].i;
+ continue;
+ }
+ return -1;
+ }
+ i--;
+
if (ops[i].op == OP_CDQ)
return i;
static int scan_for_reg_clear(int i, int reg)
{
- for (; i >= 0; i--) {
+ while (i >= 0) {
+ if (g_labels[i][0] != 0) {
+ if (g_label_refs[i].next != NULL)
+ return -1;
+ if (i > 0 && LAST_OP(i - 1)) {
+ i = g_label_refs[i].i;
+ continue;
+ }
+ return -1;
+ }
+ i--;
+
if (ops[i].op == OP_XOR
&& ops[i].operand[0].lmod == OPLM_DWORD
&& ops[i].operand[0].reg == ops[i].operand[1].reg
{
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);
- 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,
// 32bit division is common, look for it
if (po->op == OP_DIV)
- ret = scan_for_reg_clear(i - 1, xDX);
+ ret = scan_for_reg_clear(i, xDX);
else
- ret = scan_for_cdq_edx(i - 1);
+ ret = scan_for_cdq_edx(i);
if (ret >= 0) {
out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
strcpy(buf2, lmod_cast(po, po->operand[0].lmod,
int asmln;
};
+static struct chunk_item *func_chunks;
+static int func_chunk_cnt;
+static int func_chunk_alloc;
+
+static void add_func_chunk(FILE *fasm, const char *name, int line)
+{
+ if (func_chunk_cnt >= func_chunk_alloc) {
+ func_chunk_alloc *= 2;
+ func_chunks = realloc(func_chunks,
+ func_chunk_alloc * sizeof(func_chunks[0]));
+ my_assert_not(func_chunks, NULL);
+ }
+ func_chunks[func_chunk_cnt].fptr = ftell(fasm);
+ func_chunks[func_chunk_cnt].name = strdup(name);
+ func_chunks[func_chunk_cnt].asmln = line;
+ func_chunk_cnt++;
+}
+
static int cmp_chunks(const void *p1, const void *p2)
{
const struct chunk_item *c1 = p1, *c2 = p2;
return strcmp(*(char * const *)p1, *(char * const *)p2);
}
+static void scan_ahead(FILE *fasm)
+{
+ char words[2][256];
+ char line[256];
+ long oldpos;
+ int oldasmln;
+ int wordc;
+ char *p;
+ int i;
+
+ oldpos = ftell(fasm);
+ oldasmln = asmln;
+
+ while (fgets(line, sizeof(line), fasm))
+ {
+ wordc = 0;
+ asmln++;
+
+ p = sskip(line);
+ if (*p == 0)
+ continue;
+
+ if (*p == ';')
+ {
+ // get rid of random tabs
+ for (i = 0; line[i] != 0; i++)
+ if (line[i] == '\t')
+ line[i] = ' ';
+
+ if (p[2] == 'S' && IS_START(p, "; START OF FUNCTION CHUNK FOR "))
+ {
+ p += 30;
+ next_word(words[0], sizeof(words[0]), p);
+ if (words[0][0] == 0)
+ aerr("missing name for func chunk?\n");
+
+ add_func_chunk(fasm, words[0], asmln);
+ }
+ continue;
+ } // *p == ';'
+
+ for (wordc = 0; wordc < ARRAY_SIZE(words); wordc++) {
+ words[wordc][0] = 0;
+ p = sskip(next_word_s(words[wordc], sizeof(words[0]), p));
+ if (*p == 0 || *p == ';') {
+ wordc++;
+ break;
+ }
+ }
+
+ if (wordc == 2 && IS(words[1], "ends"))
+ break;
+ }
+
+ fseek(fasm, oldpos, SEEK_SET);
+ asmln = oldasmln;
+}
+
int main(int argc, char *argv[])
{
FILE *fout, *fasm, *frlist;
char **rlist = NULL;
int rlist_len = 0;
int rlist_alloc = 0;
- struct chunk_item *func_chunks;
int func_chunks_used = 0;
int func_chunks_sorted = 0;
- int func_chunk_cnt = 0;
- int func_chunk_alloc;
int func_chunk_i = -1;
long func_chunk_ret = 0;
int func_chunk_ret_ln = 0;
+ int scanned_ahead = 0;
char line[256];
char words[16][256];
enum opr_lenmod lmod;
p += 30;
next_word(words[0], sizeof(words[0]), p);
if (words[0][0] == 0)
- aerr("missing nam for func chunk?\n");
- if (func_chunk_cnt >= func_chunk_alloc) {
- func_chunk_alloc *= 2;
- func_chunks = realloc(func_chunks,
- func_chunk_alloc * sizeof(func_chunks[0]));
- my_assert_not(func_chunks, NULL);
+ aerr("missing name for func chunk?\n");
+
+ if (!scanned_ahead) {
+ add_func_chunk(fasm, words[0], asmln);
+ func_chunks_sorted = 0;
}
- func_chunks[func_chunk_cnt].fptr = ftell(fasm);
- func_chunks[func_chunk_cnt].name = strdup(words[0]);
- func_chunks[func_chunk_cnt].asmln = asmln;
- func_chunk_cnt++;
- func_chunks_sorted = 0;
}
else if (p[2] == 'E' && IS_START(p, "; END OF FUNCTION CHUNK"))
{
if (IS_START(g_func, "sub_")) {
unsigned long addr = strtoul(p, NULL, 16);
unsigned long f_addr = strtoul(g_func + 4, NULL, 16);
- if (addr > f_addr)
- aerr("need a chunk %lX that is after %s\n", addr, g_func);
+ if (addr > f_addr && !scanned_ahead) {
+ anote("scan_ahead caused by '%s', f_addr %lx\n",
+ g_func, f_addr);
+ scan_ahead(fasm);
+ scanned_ahead = 1;
+ func_chunks_sorted = 0;
+ }
}
}
continue;
parse_words:
memset(words, 0, sizeof(words));
- for (wordc = 0; wordc < 16; wordc++) {
+ for (wordc = 0; wordc < ARRAY_SIZE(words); wordc++) {
p = sskip(next_word_s(words[wordc], sizeof(words[0]), p));
if (*p == 0 || *p == ';') {
wordc++;