translate: fixes
[ia32rtools.git] / tools / translate.c
index 1fd5e47..f52b1b5 100644 (file)
@@ -55,6 +55,7 @@ enum op_op {
        OP_LEA,
        OP_MOVZX,
        OP_MOVSX,
+       OP_XCHG,
        OP_NOT,
        OP_CDQ,
        OP_STOS,
@@ -454,8 +455,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",
-    "HIMC",
+    "WPARAM", "LPARAM", "UINT", "__int32",
+    "LONG", "HIMC",
   };
   static const char *word_types[] = {
     "uint16_t", "int16_t", "_WORD",
@@ -464,7 +465,7 @@ static int guess_lmod_from_c_type(enum opr_lenmod *lmod,
   static const char *byte_types[] = {
     "uint8_t", "int8_t", "char",
     "unsigned __int8", "__int8", "BYTE", "_BYTE",
-    "_UNKNOWN",
+    "CHAR", "_UNKNOWN",
   };
   const char *n;
   int i;
@@ -726,6 +727,7 @@ static const struct {
   { "lea",  OP_LEA,    2, 2, OPF_DATA },
   { "movzx",OP_MOVZX,  2, 2, OPF_DATA },
   { "movsx",OP_MOVSX,  2, 2, OPF_DATA },
+  { "xchg", OP_XCHG,   2, 2, OPF_DATA },
   { "not",  OP_NOT,    1, 1, OPF_DATA },
   { "cdq",  OP_CDQ,    0, 0, OPF_DATA },
   { "stosb",OP_STOS,   0, 0, OPF_DATA },
@@ -933,6 +935,11 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc)
     op->regmask_dst = op->regmask_src;
     break;
 
+  case OP_XCHG:
+    op->regmask_src |= op->regmask_dst;
+    op->regmask_dst |= op->regmask_src;
+    break;
+
   case OP_JECXZ:
     op->operand_cnt = 1;
     op->regmask_src = 1 << xCX;
@@ -1415,8 +1422,10 @@ static void stack_frame_access(struct parsed_op *po,
       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);
     }
-    if (popr->is_ptr && popr->lmod != OPLM_DWORD)
-      ferr(po, "bp_arg arg%d: non-dword ptr access\n", i + 1);
+    // can't check this because msvc likes to reuse
+    // arg space for scratch..
+    //if (popr->is_ptr && popr->lmod != OPLM_DWORD)
+    //  ferr(po, "bp_arg arg%d: non-dword ptr access\n", i + 1);
   }
   else
   {
@@ -2298,18 +2307,20 @@ static int scan_for_reg_clear(int i, int reg)
 }
 
 // scan for positive, constant esp adjust
-static int scan_for_esp_adjust(int i, int opcnt, int *adj)
+static int scan_for_esp_adjust(int i, int opcnt, int *adj,
+  int *multipath)
 {
   const struct parsed_proto *pp;
   struct parsed_op *po;
   int first_pop = -1;
-  *adj = 0;
+
+  *adj = *multipath = 0;
 
   for (; i < opcnt; i++) {
     po = &ops[i];
 
     if (g_labels[i][0] != 0)
-      break;
+      *multipath = 1;
 
     if (po->op == OP_ADD && po->operand[0].reg == xSP) {
       if (po->operand[1].type != OPT_CONST)
@@ -2319,17 +2330,24 @@ static int scan_for_esp_adjust(int i, int opcnt, int *adj)
         ferr(&ops[i], "unaligned esp adjust: %x\n", *adj);
       return i;
     }
-    else if (po->op == OP_PUSH) {
+    else if (po->op == OP_PUSH && !(po->flags & OPF_RMD)) {
       //if (first_pop == -1)
       //  first_pop = -2; // none
       *adj -= lmod_bytes(po, po->operand[0].lmod);
     }
     else if (po->op == OP_POP && !(po->flags & OPF_RMD)) {
+      // seems like msvc only uses 'pop ecx' for stack realignment..
+      if (po->operand[0].type != OPT_REG || po->operand[0].reg != xCX)
+        break;
       if (first_pop == -1 && *adj >= 0)
         first_pop = i;
       *adj += lmod_bytes(po, po->operand[0].lmod);
     }
     else if (po->flags & (OPF_JMP|OPF_TAIL)) {
+      if (po->op == OP_JMP && po->btj == NULL) {
+        i = po->bt_i - 1;
+        continue;
+      }
       if (po->op != OP_CALL)
         break;
       if (po->operand[0].type != OPT_LABEL)
@@ -2340,10 +2358,7 @@ static int scan_for_esp_adjust(int i, int opcnt, int *adj)
     }
   }
 
-  if (*adj <= 8 && first_pop >= 0 && ops[first_pop].op == OP_POP
-    && ops[first_pop].operand[0].type == OPT_REG
-    && ops[first_pop].operand[0].reg == xCX)
-  {
+  if (first_pop >= 0) {
     // probably 'pop ecx' was used..
     return first_pop;
   }
@@ -2788,6 +2803,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   enum parsed_flag_op pfo;
   int save_arg_vars = 0;
   int cmp_result_vars = 0;
+  int need_tmp_var = 0;
   int need_mul_var = 0;
   int had_decl = 0;
   int label_pending = 0;
@@ -3109,7 +3125,7 @@ tailcall:
           pp = calloc(1, sizeof(*pp));
           my_assert_not(pp, NULL);
           pp->is_fptr = 1;
-          ret = scan_for_esp_adjust(i + 1, opcnt, &j);
+          ret = scan_for_esp_adjust(i + 1, opcnt, &j, &l);
           if (ret < 0) {
             if (!g_allow_regfunc)
               ferr(po, "non-__cdecl indirect call unhandled yet\n");
@@ -3130,7 +3146,7 @@ tailcall:
       // look for and make use of esp adjust
       ret = -1;
       if (!pp->is_stdcall && pp->argc_stack > 0)
-        ret = scan_for_esp_adjust(i + 1, opcnt, &j);
+        ret = scan_for_esp_adjust(i + 1, opcnt, &j, &l);
       if (ret >= 0) {
         if (pp->is_vararg) {
           if (j / 4 < pp->argc_stack)
@@ -3160,7 +3176,7 @@ tailcall:
             ret++;
           }
         }
-        else {
+        else if (!l) {
           // a bit of a hack, but deals with use of
           // single adj for multiple calls
           ops[ret].operand[1].val -= j;
@@ -3297,12 +3313,9 @@ tailcall:
           if (branched || scan_for_mod(tmp_op, setters[j] + 1, i, 0) >= 0)
             pfomask = 1 << pfo;
         }
-        else if (tmp_op->op == OP_SCAS) {
+        else if (tmp_op->op == OP_CMPS || tmp_op->op == OP_SCAS) {
           pfomask = 1 << pfo;
         }
-        else if (tmp_op->op == OP_CMPS) {
-          pfomask = 1 << PFO_Z;
-        }
         else {
           // see if we'll be able to handle based on op result
           if ((tmp_op->op != OP_AND && tmp_op->op != OP_OR
@@ -3330,6 +3343,9 @@ tailcall:
     {
       need_mul_var = 1;
     }
+    else if (po->op == OP_XCHG) {
+      need_tmp_var = 1;
+    }
     else if (po->op == OP_CALL) {
       pp = po->datap;
       if (pp == NULL)
@@ -3534,6 +3550,11 @@ tailcall:
     }
   }
 
+  if (need_tmp_var) {
+    fprintf(fout, "  u32 tmp;\n");
+    had_decl = 1;
+  }
+
   if (need_mul_var) {
     fprintf(fout, "  u64 mul_tmp;\n");
     had_decl = 1;
@@ -3627,6 +3648,27 @@ tailcall:
 
     pfomask = po->pfomask;
 
+    if (po->flags & (OPF_REPZ|OPF_REPNZ)) {
+      // we need initial flags for ecx=0 case..
+      if (i > 0 && ops[i - 1].op == OP_XOR
+        && IS(ops[i - 1].operand[0].name,
+              ops[i - 1].operand[1].name))
+      {
+        fprintf(fout, "  cond_z = ");
+        if (pfomask & (1 << PFO_C))
+          fprintf(fout, "cond_c = ");
+        fprintf(fout, "0;\n");
+      }
+      else if (last_arith_dst != NULL) {
+        out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
+        out_test_for_cc(buf1, sizeof(buf1), po, PFO_Z, 0,
+          last_arith_dst->lmod, buf3);
+        fprintf(fout, "  cond_z = %s;\n", buf1);
+      }
+      else
+        ferr(po, "missing initial ZF\n");
+    }
+
     switch (po->op)
     {
       case OP_MOV:
@@ -3672,6 +3714,19 @@ tailcall:
               buf3, 0));
         break;
 
+      case OP_XCHG:
+        assert_operand_cnt(2);
+        propagate_lmod(po, &po->operand[0], &po->operand[1]);
+        fprintf(fout, "  tmp = %s;",
+          out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], "", 0));
+        fprintf(fout, " %s = %s;",
+          out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+          out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], "", 0));
+        fprintf(fout, " %s = tmp;",
+          out_dst_opr(buf1, sizeof(buf1), po, &po->operand[1]));
+        snprintf(g_comment, sizeof(g_comment), "xchg");
+        break;
+
       case OP_NOT:
         assert_operand_cnt(1);
         out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
@@ -3733,13 +3788,20 @@ tailcall:
         l = (po->flags & OPF_DF) ? '-' : '+';
         if (po->flags & OPF_REP) {
           fprintf(fout,
-            "  for (; ecx != 0; ecx--, edi %c= %d, esi %c= %d)\n",
+            "  for (; ecx != 0; ecx--, edi %c= %d, esi %c= %d) {\n",
             l, j, l, j);
+          if (pfomask & (1 << PFO_C)) {
+            // ugh..
+            fprintf(fout,
+            "    cond_c = %sedi < %sesi;\n", buf1, buf1);
+            pfomask &= ~(1 << PFO_C);
+          }
           fprintf(fout,
             "    if ((cond_z = (%sedi == %sesi)) %s 0)\n",
               buf1, buf1, (po->flags & OPF_REPZ) ? "==" : "!=");
           fprintf(fout,
-            "      break;");
+            "      break;\n"
+            "  }");
           snprintf(g_comment, sizeof(g_comment), "rep%s cmps",
             (po->flags & OPF_REPZ) ? "e" : "ne");
         }