translate: more sub flags and shrd
[ia32rtools.git] / tools / translate.c
index 778df52..b64b065 100644 (file)
@@ -86,6 +86,7 @@ enum op_op {
        OP_SHL,
        OP_SHR,
        OP_SAR,
+       OP_SHRD,
        OP_ROL,
        OP_ROR,
        OP_RCL,
@@ -275,6 +276,22 @@ static void printf_number(char *buf, size_t buf_size,
   snprintf(buf, buf_size, number < 10 ? "%lu" : "0x%02lx", number);
 }
 
+static int check_segment_prefix(const char *s)
+{
+  if (s[0] == 0 || s[1] != 's' || s[2] != ':')
+    return 0;
+
+  switch (s[0]) {
+  case 'c': return 1;
+  case 'd': return 2;
+  case 's': return 3;
+  case 'e': return 4;
+  case 'f': return 5;
+  case 'g': return 6;
+  default:  return 0;
+  }
+}
+
 static int parse_reg(enum opr_lenmod *reg_lmod, const char *s)
 {
   int reg;
@@ -326,8 +343,8 @@ static int parse_indmode(char *name, int *regmask, int need_c_cvt)
       s++;
     *d = 0;
 
-    // skip 'ds:' prefix
-    if (IS_START(s, "ds:"))
+    // skip '?s:' prefixes
+    if (check_segment_prefix(s))
       s += 3;
 
     s = next_idt(w, sizeof(w), s);
@@ -373,7 +390,8 @@ static int is_reg_in_str(const char *s)
   return 0;
 }
 
-static const char *parse_stack_el(const char *name, char *extra_reg)
+static const char *parse_stack_el(const char *name, char *extra_reg,
+  int early_try)
 {
   const char *p, *p2, *s;
   char *endp = NULL;
@@ -381,31 +399,34 @@ static const char *parse_stack_el(const char *name, char *extra_reg)
   long val;
   int len;
 
-  p = name;
-  if (IS_START(p + 3, "+ebp+") && is_reg_in_str(p)) {
-    p += 4;
-    if (extra_reg != NULL) {
-      strncpy(extra_reg, name, 3);
-      extra_reg[4] = 0;
+  if (g_bp_frame || early_try)
+  {
+    p = name;
+    if (IS_START(p + 3, "+ebp+") && is_reg_in_str(p)) {
+      p += 4;
+      if (extra_reg != NULL) {
+        strncpy(extra_reg, name, 3);
+        extra_reg[4] = 0;
+      }
     }
-  }
 
-  if (IS_START(p, "ebp+")) {
-    p += 4;
+    if (IS_START(p, "ebp+")) {
+      p += 4;
 
-    p2 = strchr(p, '+');
-    if (p2 != NULL && is_reg_in_str(p)) {
-      if (extra_reg != NULL) {
-        strncpy(extra_reg, p, p2 - p);
-        extra_reg[p2 - p] = 0;
+      p2 = strchr(p, '+');
+      if (p2 != NULL && is_reg_in_str(p)) {
+        if (extra_reg != NULL) {
+          strncpy(extra_reg, p, p2 - p);
+          extra_reg[p2 - p] = 0;
+        }
+        p = p2 + 1;
       }
-      p = p2 + 1;
-    }
 
-    if (!('0' <= *p && *p <= '9'))
-      return p;
+      if (!('0' <= *p && *p <= '9'))
+        return p;
 
-    return NULL;
+      return NULL;
+    }
   }
 
   if (!IS_START(name, "esp+"))
@@ -474,7 +495,8 @@ static int guess_lmod_from_c_type(enum opr_lenmod *lmod,
   static const char *dword_types[] = {
     "int", "_DWORD", "UINT_PTR", "DWORD",
     "WPARAM", "LPARAM", "UINT", "__int32",
-    "LONG", "HIMC", "BOOL",
+    "LONG", "HIMC", "BOOL", "size_t",
+    "float",
   };
   static const char *word_types[] = {
     "uint16_t", "int16_t", "_WORD", "WORD",
@@ -610,7 +632,10 @@ static int parse_operand(struct parsed_opr *opr,
 
     if (label != NULL) {
       opr->type = OPT_LABEL;
-      if (IS_START(label, "ds:")) {
+      ret = check_segment_prefix(label);
+      if (ret != 0) {
+        if (ret >= 5)
+          aerr("fs/gs used\n");
         opr->had_ds = 1;
         label += 3;
       }
@@ -656,7 +681,10 @@ static int parse_operand(struct parsed_opr *opr,
   if (wordc_in != 1)
     aerr("parse_operand 1 word expected\n");
 
-  if (IS_START(words[w], "ds:")) {
+  ret = check_segment_prefix(words[w]);
+  if (ret != 0) {
+    if (ret >= 5)
+      aerr("fs/gs used\n");
     opr->had_ds = 1;
     memmove(words[w], words[w] + 3, strlen(words[w]) - 2);
   }
@@ -669,10 +697,11 @@ static int parse_operand(struct parsed_opr *opr,
       aerr("[] parse failure\n");
 
     parse_indmode(opr->name, regmask_indirect, 1);
-    if (opr->lmod == OPLM_UNSPEC && parse_stack_el(opr->name, NULL)) {
+    if (opr->lmod == OPLM_UNSPEC && parse_stack_el(opr->name, NULL, 1))
+    {
       // might be an equ
       struct parsed_equ *eq =
-        equ_find(NULL, parse_stack_el(opr->name, NULL), &i);
+        equ_find(NULL, parse_stack_el(opr->name, NULL, 1), &i);
       if (eq)
         opr->lmod = eq->lmod;
     }
@@ -800,6 +829,7 @@ static const struct {
   { "shr",  OP_SHR,    2, 2, OPF_DATA|OPF_FLAGS },
   { "sal",  OP_SHL,    2, 2, OPF_DATA|OPF_FLAGS },
   { "sar",  OP_SAR,    2, 2, OPF_DATA|OPF_FLAGS },
+  { "shrd", OP_SHRD,   3, 3, OPF_DATA|OPF_FLAGS },
   { "rol",  OP_ROL,    2, 2, OPF_DATA|OPF_FLAGS },
   { "ror",  OP_ROR,    2, 2, OPF_DATA|OPF_FLAGS },
   { "rcl",  OP_RCL,    2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C },
@@ -1037,6 +1067,11 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc)
       op->operand[1].lmod = OPLM_BYTE;
     break;
 
+  case OP_SHRD:
+    if (op->operand[2].lmod == OPLM_UNSPEC)
+      op->operand[2].lmod = OPLM_BYTE;
+    break;
+
   case OP_PUSH:
     if (op->operand[0].lmod == OPLM_UNSPEC
         && (op->operand[0].type == OPT_CONST
@@ -1316,7 +1351,7 @@ static struct parsed_equ *equ_find(struct parsed_op *po, const char *name,
 static int is_stack_access(struct parsed_op *po,
   const struct parsed_opr *popr)
 {
-  return (parse_stack_el(popr->name, NULL)
+  return (parse_stack_el(popr->name, NULL, 0)
     || (g_bp_frame && !(po->flags & OPF_EBP_S)
         && IS_START(popr->name, "ebp")));
 }
@@ -1347,7 +1382,7 @@ static void parse_stack_access(struct parsed_op *po,
       ferr(po, "ebp- parse of '%s' failed\n", name);
   }
   else {
-    bp_arg = parse_stack_el(name, ofs_reg);
+    bp_arg = parse_stack_el(name, ofs_reg, 0);
     snprintf(g_comment, sizeof(g_comment), "%s", bp_arg);
     eq = equ_find(po, bp_arg, &offset);
     if (eq == NULL)
@@ -1496,7 +1531,8 @@ static int stack_frame_access(struct parsed_op *po,
     // common problem
     guess_lmod_from_c_type(&tmp_lmod, &g_func_pp->arg[i].type);
     if (tmp_lmod != OPLM_DWORD
-      && (unaligned || (!is_src && tmp_lmod < popr->lmod)))
+      && (unaligned || (!is_src && lmod_bytes(po, tmp_lmod)
+                         < lmod_bytes(po, popr->lmod) + (offset & 3))))
     {
       ferr(po, "bp_arg arg%d/w offset %d and type '%s' is too small\n",
         i + 1, offset, g_func_pp->arg[i].type.name);
@@ -2742,12 +2778,16 @@ static int collect_call_args_r(struct parsed_op *po, int i,
         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);
     }
-    else if (ops[j].op == OP_ADD && ops[j].operand[0].reg == xSP) {
+    // esp adjust of 0 means we collected it before
+    else if (ops[j].op == OP_ADD && ops[j].operand[0].reg == xSP
+      && (ops[j].operand[1].type != OPT_CONST
+          || ops[j].operand[1].val != 0))
+    {
       if (pp->is_unresolved)
         break;
 
-      ferr(po, "arg collect %d/%d hit esp adjust\n",
-        arg, pp->argc);
+      ferr(po, "arg collect %d/%d hit esp adjust of %d\n",
+        arg, pp->argc, ops[j].operand[1].val);
     }
     else if (ops[j].op == OP_POP) {
       if (pp->is_unresolved)
@@ -2785,9 +2825,12 @@ static int collect_call_args_r(struct parsed_op *po, int i,
           ops[j].p_argnum = arg + 1;
           save_args |= 1 << arg;
         }
-        else if (ops[j].p_argnum < arg + 1)
-          ferr(&ops[j], "p_argnum conflict (%d<%d) for '%s'\n",
-            ops[j].p_argnum, arg + 1, pp->name);
+        else if (ops[j].p_argnum < arg + 1) {
+          // XXX: might kill valid var..
+          //*save_arg_vars &= ~(1 << (ops[j].p_argnum - 1));
+          ops[j].p_argnum = arg + 1;
+          save_args |= 1 << arg;
+        }
       }
       else if (ops[j].p_argnum == 0)
         ops[j].flags |= OPF_RMD;
@@ -3095,22 +3138,33 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
     } while (i < opcnt);
   }
   else {
-    for (i = 0; i < opcnt; i++) {
+    int ecx_push = 0, esp_sub = 0;
+
+    i = 0;
+    while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
+      ops[i].flags |= OPF_RMD;
+      g_stack_fsz += 4;
+      ecx_push++;
+      i++;
+    }
+
+    for (; i < opcnt; i++) {
       if (ops[i].op == OP_PUSH || (ops[i].flags & (OPF_JMP|OPF_TAIL)))
         break;
       if (ops[i].op == OP_SUB && ops[i].operand[0].reg == xSP
         && ops[i].operand[1].type == OPT_CONST)
       {
-        g_sp_frame = 1;
+        g_stack_fsz = ops[i].operand[1].val;
+        ops[i].flags |= OPF_RMD;
+        esp_sub = 1;
         break;
       }
     }
 
     found = 0;
-    if (g_sp_frame)
+    if (ecx_push || esp_sub)
     {
-      g_stack_fsz = ops[i].operand[1].val;
-      ops[i].flags |= OPF_RMD;
+      g_sp_frame = 1;
 
       i++;
       do {
@@ -3124,14 +3178,31 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
           j--;
         }
 
-        if (ops[j].op != OP_ADD
-            || !IS(opr_name(&ops[j], 0), "esp")
-            || ops[j].operand[1].type != OPT_CONST
-            || ops[j].operand[1].val != g_stack_fsz)
-          ferr(&ops[j], "'add esp' expected\n");
-        ops[j].flags |= OPF_RMD;
+        if (ecx_push > 0) {
+          for (l = 0; l < ecx_push; l++) {
+            if (ops[j].op != OP_POP
+              || !IS(opr_name(&ops[j], 0), "ecx"))
+            {
+              ferr(&ops[j], "'pop ecx' expected\n");
+            }
+            ops[j].flags |= OPF_RMD;
+            j--;
+          }
+
+          found = 1;
+        }
+
+        if (esp_sub) {
+          if (ops[j].op != OP_ADD
+              || !IS(opr_name(&ops[j], 0), "esp")
+              || ops[j].operand[1].type != OPT_CONST
+              || ops[j].operand[1].val != g_stack_fsz)
+            ferr(&ops[j], "'add esp' expected\n");
+          ops[j].flags |= OPF_RMD;
+
+          found = 1;
+        }
 
-        found = 1;
         i++;
       } while (i < opcnt);
     }
@@ -4234,6 +4305,21 @@ tailcall:
         delayed_flag_op = NULL;
         break;
 
+      case OP_SHRD:
+        assert_operand_cnt(3);
+        propagate_lmod(po, &po->operand[0], &po->operand[1]);
+        l = lmod_bytes(po, po->operand[0].lmod) * 8;
+        out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+        out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
+        out_src_opr_u32(buf3, sizeof(buf3), po, &po->operand[2]);
+        fprintf(fout, "  %s >>= %s; %s |= %s << (%d - %s);",
+          buf1, buf3, buf1, buf2, l, buf3);
+        strcpy(g_comment, "shrd");
+        output_std_flags(fout, po, &pfomask, buf1);
+        last_arith_dst = &po->operand[0];
+        delayed_flag_op = NULL;
+        break;
+
       case OP_ROL:
       case OP_ROR:
         assert_operand_cnt(2);
@@ -4338,11 +4424,18 @@ tailcall:
       case OP_SUB:
         assert_operand_cnt(2);
         propagate_lmod(po, &po->operand[0], &po->operand[1]);
-        if (pfomask & (1 << PFO_C)) {
-          fprintf(fout, "  cond_c = %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]));
-          pfomask &= ~(1 << PFO_C);
+        if (pfomask & ~((1 << PFO_Z) | (1 << PFO_S))) {
+          for (j = 0; j <= PFO_LE; j++) {
+            if (!(pfomask & (1 << j)))
+              continue;
+            if (j == PFO_Z || j == PFO_S)
+              continue;
+
+            out_cmp_for_cc(buf1, sizeof(buf1), po, j, 0);
+            fprintf(fout, "  cond_%s = %s;\n",
+              parsed_flag_op_names[j], buf1);
+            pfomask &= ~(1 << j);
+          }
         }
         goto dualop_arith;