+/*
+ * ia32rtools
+ * (C) notaz, 2013,2014
+ *
+ * This work is licensed under the terms of 3-clause BSD license.
+ * See COPYING file in the top-level directory.
+ */
+
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
OPF_ATAIL = (1 << 14), /* tail call with reused arg frame */
OPF_32BIT = (1 << 15), /* 32bit division */
OPF_LOCK = (1 << 16), /* op has lock prefix */
+ OPF_VAPUSH = (1 << 17), /* vararg ptr push (as call arg) */
};
enum op_op {
"LONG", "HIMC",
};
static const char *word_types[] = {
- "uint16_t", "int16_t", "_WORD",
+ "uint16_t", "int16_t", "_WORD", "WORD",
"unsigned __int16", "__int16",
};
static const char *byte_types[] = {
"uint8_t", "int8_t", "char",
"unsigned __int8", "__int8", "BYTE", "_BYTE",
"CHAR", "_UNKNOWN",
+ // structures.. deal the same as with _UNKNOWN for now
+ "CRITICAL_SECTION",
};
const char *n;
int i;
return pp;
}
-static int try_resolve_const(int i, const struct parsed_opr *opr,
- int magic, unsigned int *val)
+// find an instruction that changed opr before i op
+// *op_i must be set to -1
+static int resolve_origin(int i, const struct parsed_opr *opr,
+ int magic, int *op_i)
{
struct label_ref *lr;
int ret = 0;
if (g_labels[i][0] != 0) {
lr = &g_label_refs[i];
for (; lr != NULL; lr = lr->next)
- ret |= try_resolve_const(lr->i, opr, magic, val);
+ ret |= resolve_origin(lr->i, opr, magic, op_i);
if (i > 0 && LAST_OP(i - 1))
return ret;
}
continue;
if (!is_opr_modified(opr, &ops[i]))
continue;
+
+ if (*op_i >= 0) {
+ if (*op_i == i)
+ return 1;
+ // XXX: could check if the other op does the same
+ return -1;
+ }
+
+ *op_i = i;
+ return 1;
+ }
+}
+
+static int try_resolve_const(int i, const struct parsed_opr *opr,
+ int magic, unsigned int *val)
+{
+ int s_i = -1;
+ int ret = 0;
+
+ ret = resolve_origin(i, opr, magic, &s_i);
+ if (ret == 1) {
+ i = s_i;
if (ops[i].op != OP_MOV && ops[i].operand[1].type != OPT_CONST)
return -1;
*val = ops[i].operand[1].val;
return 1;
}
+
+ return -1;
}
static int collect_call_args_r(struct parsed_op *po, int i,
struct parsed_proto *pp_tmp;
struct label_ref *lr;
int need_to_save_current;
+ int save_args;
int ret = 0;
- int j;
+ char buf[32];
+ int j, k;
if (i < 0) {
ferr(po, "dead label encountered\n");
pp->arg[arg].datap = &ops[j];
need_to_save_current = 0;
+ save_args = 0;
if (!need_op_saving) {
ret = scan_for_mod(&ops[j], j + 1, i, 1);
need_to_save_current = (ret >= 0);
ops[j].flags &= ~OPF_RMD;
if (ops[j].argnum == 0) {
ops[j].argnum = arg + 1;
- *save_arg_vars |= 1 << arg;
+ save_args |= 1 << arg;
}
else if (ops[j].argnum < arg + 1)
ferr(&ops[j], "argnum conflict (%d<%d) for '%s'\n",
ops[j].flags &= ~OPF_RSAVE;
+ // check for __VALIST
+ if (!pp->is_unresolved && g_func_pp->is_vararg
+ && IS(pp->arg[arg].type.name, "__VALIST"))
+ {
+ snprintf(buf, sizeof(buf), "arg_%X",
+ g_func_pp->argc_stack * 4);
+ k = -1;
+ ret = resolve_origin(j, &ops[j].operand[0], magic + 1, &k);
+ if (ret == 1 && k >= 0 && ops[k].op == OP_LEA
+ && strstr(ops[k].operand[1].name, buf))
+ {
+ ops[k].flags |= OPF_RMD;
+ ops[j].flags |= OPF_RMD | OPF_VAPUSH;
+ save_args &= ~(1 << arg);
+ }
+ }
+
+ *save_arg_vars |= save_args;
+
+ // tracking reg usage
+ if (!(ops[j].flags & OPF_VAPUSH)
+ && ops[j].operand[0].type == OPT_REG)
+ {
+ *regmask |= 1 << ops[j].operand[0].reg;
+ }
+
arg++;
if (!pp->is_unresolved) {
// next arg
break;
}
magic = (magic & 0xffffff) | (arg << 24);
-
- // tracking reg usage
- if (ops[j].operand[0].type == OPT_REG)
- *regmask |= 1 << ops[j].operand[0].reg;
}
}
// indirect call
pp_c = resolve_icall(i, opcnt, &l);
if (pp_c != NULL) {
+ if (!pp_c->is_func && !pp_c->is_fptr)
+ ferr(po, "call to non-func: %s\n", pp_c->name);
pp = proto_clone(pp_c);
my_assert_not(pp, NULL);
if (l)
pfomask = 1 << po->pfo;
}
- if (tmp_op->op == OP_ADD && po->pfo == PFO_C)
- need_tmp64 = 1;
+ if (tmp_op->op == OP_ADD && po->pfo == PFO_C) {
+ propagate_lmod(tmp_op, &tmp_op->operand[0],
+ &tmp_op->operand[1]);
+ if (tmp_op->operand[0].lmod == OPLM_DWORD)
+ need_tmp64 = 1;
+ }
}
if (pfomask) {
tmp_op->pfomask |= pfomask;
else if (po->op == OP_MUL
|| (po->op == OP_IMUL && po->operand_cnt == 1))
{
- need_tmp64 = 1;
+ if (po->operand[0].lmod == OPLM_DWORD)
+ need_tmp64 = 1;
}
else if (po->op == OP_CALL) {
pp = po->pp;
assert_operand_cnt(2);
propagate_lmod(po, &po->operand[0], &po->operand[1]);
if (pfomask & (1 << PFO_C)) {
- fprintf(fout, " tmp64 = (u64)%s + %s;\n",
- out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]),
- out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
- fprintf(fout, " cond_c = tmp64 >> 32;\n");
- fprintf(fout, " %s = (u32)tmp64;",
- out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
- strcat(g_comment, "add64");
+ out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
+ if (po->operand[0].lmod == OPLM_DWORD) {
+ fprintf(fout, " tmp64 = (u64)%s + %s;\n", buf1, buf2);
+ fprintf(fout, " cond_c = tmp64 >> 32;\n");
+ fprintf(fout, " %s = (u32)tmp64;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
+ strcat(g_comment, "add64");
+ }
+ else {
+ fprintf(fout, " cond_c = ((u32)%s + %s) >> %d;\n",
+ buf1, buf2, lmod_bytes(po, po->operand[0].lmod) * 8);
+ fprintf(fout, " %s += %s;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ buf2);
+ }
pfomask &= ~(1 << PFO_C);
output_std_flags(fout, po, &pfomask, buf1);
last_arith_dst = &po->operand[0];
// fallthrough
case OP_MUL:
assert_operand_cnt(1);
- strcpy(buf1, po->op == OP_IMUL ? "(s64)(s32)" : "(u64)");
- fprintf(fout, " tmp64 = %seax * %s%s;\n", buf1, buf1,
- out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]));
- fprintf(fout, " edx = tmp64 >> 32;\n");
- fprintf(fout, " eax = tmp64;");
+ switch (po->operand[0].lmod) {
+ case OPLM_DWORD:
+ strcpy(buf1, po->op == OP_IMUL ? "(s64)(s32)" : "(u64)");
+ fprintf(fout, " tmp64 = %seax * %s%s;\n", buf1, buf1,
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]));
+ fprintf(fout, " edx = tmp64 >> 32;\n");
+ fprintf(fout, " eax = tmp64;");
+ break;
+ case OPLM_BYTE:
+ strcpy(buf1, po->op == OP_IMUL ? "(s16)(s8)" : "(u16)(u8)");
+ fprintf(fout, " LOWORD(eax) = %seax * %s;", buf1,
+ out_src_opr(buf2, sizeof(buf2), po, &po->operand[0],
+ buf1, 0));
+ break;
+ default:
+ ferr(po, "TODO: unhandled mul type\n");
+ break;
+ }
last_arith_dst = NULL;
delayed_flag_op = NULL;
break;
tmp_op = pp->arg[arg].datap;
if (tmp_op == NULL)
ferr(po, "parsed_op missing for arg%d\n", arg);
- if (tmp_op->argnum != 0) {
+
+ if (tmp_op->flags & OPF_VAPUSH) {
+ fprintf(fout, "ap");
+ }
+ else if (tmp_op->argnum != 0) {
fprintf(fout, "%ss_a%d", cast, tmp_op->argnum);
}
else {