translate: more float ops
[ia32rtools.git] / tools / translate.c
index 6714aa2..a7f629e 100644 (file)
@@ -136,6 +136,11 @@ enum op_op {
   OP_FISUB,
   OP_FIDIVR,
   OP_FISUBR,
+  OP_FCOS,
+  OP_FPATAN,
+  OP_FPTAN,
+  OP_FSIN,
+  OP_FSQRT,
   // mmx
   OP_EMMS,
   // pseudo-ops for lib calls
@@ -250,7 +255,8 @@ enum ida_func_attr {
 };
 
 enum sct_func_attr {
-  SCTFA_CLEAR_SF = (1 << 0), // clear stack frame
+  SCTFA_CLEAR_SF   = (1 << 0), // clear stack frame
+  SCTFA_CLEAR_REGS = (1 << 1), // clear registers (mask)
 };
 
 enum x87_const {
@@ -286,6 +292,7 @@ static int g_ida_func_attr;
 static int g_sct_func_attr;
 static int g_stack_clear_start; // in dwords
 static int g_stack_clear_len;
+static int g_regmask_init;
 static int g_skip_func;
 static int g_allow_regfunc;
 static int g_quiet_pp;
@@ -326,6 +333,7 @@ enum x86_regs {
 };
 
 #define mxAX     (1 << xAX)
+#define mxCX     (1 << xCX)
 #define mxDX     (1 << xDX)
 #define mxST0    (1 << xST0)
 #define mxST1    (1 << xST1)
@@ -1042,6 +1050,11 @@ static const struct {
   { "fisub",  OP_FISUB,  1, 1, 0 },
   { "fidivr", OP_FIDIVR, 1, 1, 0 },
   { "fisubr", OP_FISUBR, 1, 1, 0 },
+  { "fcos",   OP_FCOS,   0, 0, 0 },
+  { "fpatan", OP_FPATAN, 0, 0, OPF_FPOP },
+  { "fptan",  OP_FPTAN,  0, 0, OPF_FPUSH },
+  { "fsin",   OP_FSIN,   0, 0, 0 },
+  { "fsqrt",  OP_FSQRT,  0, 0, 0 },
   // mmx
   { "emms",   OP_EMMS,   0, 0, OPF_DATA },
   { "movq",   OP_MOV,    2, 2, OPF_DATA },
@@ -1369,10 +1382,22 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc)
   case OP_FISUB:
   case OP_FIDIVR:
   case OP_FISUBR:
+  case OP_FCOS:
+  case OP_FSIN:
+  case OP_FSQRT:
     op->regmask_src |= mxST0;
     op->regmask_dst |= mxST0;
     break;
 
+  case OP_FPATAN:
+    op->regmask_src |= mxST0 | mxST1;
+    op->regmask_dst |= mxST0;
+    break;
+
+  case OP_FPTAN:
+    aerr("TODO\n");
+    break;
+
   default:
     break;
   }
@@ -3458,17 +3483,31 @@ static int get_pp_arg_regmask_src(const struct parsed_proto *pp)
 
 static int get_pp_arg_regmask_dst(const struct parsed_proto *pp)
 {
+  int regmask = 0;
+  int i, reg;
+
+  if (pp->has_retreg) {
+    for (i = 0; i < pp->argc; i++) {
+      if (pp->arg[i].type.is_retreg) {
+        reg = char_array_i(regs_r32,
+                ARRAY_SIZE(regs_r32), pp->arg[i].reg);
+        ferr_assert(ops, reg >= 0);
+        regmask |= 1 << reg;
+      }
+    }
+  }
+
   if (strstr(pp->ret_type.name, "int64"))
-    return (1 << xAX) | (1 << xDX);
+    return regmask | (1 << xAX) | (1 << xDX);
   if (IS(pp->ret_type.name, "float")
    || IS(pp->ret_type.name, "double"))
   {
-    return mxST0;
+    return regmask | mxST0;
   }
   if (strcasecmp(pp->ret_type.name, "void") == 0)
-    return 0;
+    return regmask;
 
-  return mxAX;
+  return regmask | mxAX;
 }
 
 static void resolve_branches_parse_calls(int opcnt)
@@ -4757,21 +4796,6 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits,
     if (g_bp_frame && !(po->flags & OPF_EBP_S))
       regmask_new &= ~(1 << xBP);
 
-    if (po->op == OP_CALL) {
-      // allow fastcall calls from anywhere, calee may be also sitting
-      // in some fastcall table even when it's not using reg args
-      if (regmask_new & po->regmask_src & (1 << xCX)) {
-        *regmask_init |= (1 << xCX);
-        regmask_now |= (1 << xCX);
-        regmask_new &= ~(1 << xCX);
-      }
-      if (regmask_new & po->regmask_src & (1 << xDX)) {
-        *regmask_init |= (1 << xDX);
-        regmask_now |= (1 << xDX);
-        regmask_new &= ~(1 << xDX);
-      }
-    }
-
     if (regmask_new != 0)
       fnote(po, "uninitialized reg mask: %x\n", regmask_new);
 
@@ -4801,7 +4825,10 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits,
     if (po->flags & OPF_TAIL) {
       if (regmask_now & (mxST0 | mxST1))
         ferr(po, "float regs on tail: %x\n", regmask_now);
-      return;
+
+      // there is support for "conditional tailcall", sort of
+      if (!(po->flags & OPF_CC))
+        return;
     }
   }
 }
@@ -4926,11 +4953,13 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   unsigned int uval;
   int save_arg_vars[MAX_ARG_GRP] = { 0, };
   unsigned char cbits[MAX_OPS / 8];
+  const char *float_type;
   int cond_vars = 0;
   int need_tmp_var = 0;
   int need_tmp64 = 0;
   int had_decl = 0;
   int label_pending = 0;
+  int need_double = 0;
   int regmask_save = 0; // regs saved/restored in this func
   int regmask_arg;      // regs from this function args (fastcall, etc)
   int regmask_ret;      // regs needed on ret
@@ -4948,6 +4977,8 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 
   g_bp_frame = g_sp_frame = g_stack_fsz = 0;
   g_stack_frame_used = 0;
+  if (g_sct_func_attr & SCTFA_CLEAR_REGS)
+    regmask_init = g_regmask_init;
 
   g_func_pp = proto_parse(fhdr, funcn, 0);
   if (g_func_pp == NULL)
@@ -4956,17 +4987,6 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   regmask_arg = get_pp_arg_regmask_src(g_func_pp);
   regmask_ret = get_pp_arg_regmask_dst(g_func_pp);
 
-  if (g_func_pp->has_retreg) {
-    for (arg = 0; arg < g_func_pp->argc; arg++) {
-      if (g_func_pp->arg[arg].type.is_retreg) {
-        reg = char_array_i(regs_r32,
-                ARRAY_SIZE(regs_r32), g_func_pp->arg[arg].reg);
-        ferr_assert(ops, reg >= 0);
-        regmask_ret |= 1 << reg;
-      }
-    }
-  }
-
   // pass1:
   // - resolve all branches
   // - parse calls with labels
@@ -5085,7 +5105,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   // - find POPs for PUSHes, rm both
   // - scan for all used registers
   memset(cbits, 0, sizeof(cbits));
-  reg_use_pass(0, opcnt, cbits, 0, &regmask,
+  reg_use_pass(0, opcnt, cbits, regmask_init, &regmask,
     0, &regmask_save, &regmask_init, regmask_arg);
 
   // pass7:
@@ -5256,11 +5276,15 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       if (j == -1)
         po->flags |= OPF_32BIT;
     }
+    else if (po->op == OP_FLD && po->operand[0].lmod == OPLM_QWORD)
+      need_double = 1;
 
     if (po->op == OP_RCL || po->op == OP_RCR || po->op == OP_XCHG)
       need_tmp_var = 1;
   }
 
+  float_type = need_double ? "double" : "float";
+
   // output starts here
 
   // define userstack size
@@ -5420,7 +5444,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   if (regmask_now & 0xff0000) {
     for (reg = 16; reg < 24; reg++) {
       if (regmask_now & (1 << reg)) {
-        fprintf(fout, "  double f_st%d", reg - 16);
+        fprintf(fout, "  %s f_st%d", float_type, reg - 16);
         if (regmask_init & (1 << reg))
           fprintf(fout, " = 0");
         fprintf(fout, ";\n");
@@ -6563,7 +6587,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       case OP_FILD:
         if (po->flags & OPF_FSHIFT)
           fprintf(fout, "  f_st1 = f_st0;\n");
-        fprintf(fout, "  f_st0 = (double)%s;",
+        fprintf(fout, "  f_st0 = (%s)%s;", float_type,
           out_src_opr(buf1, sizeof(buf1), po, &po->operand[0],
             lmod_cast(po, po->operand[0].lmod, 1), 0));
         strcat(g_comment, " fild");
@@ -6639,7 +6663,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         case OP_FISUB: j = '-'; break;
         default: j = 'x'; break;
         }
-        fprintf(fout, "  f_st0 %c= (double)%s;", j,
+        fprintf(fout, "  f_st0 %c= (%s)%s;", j, float_type,
           out_src_opr(buf1, sizeof(buf1), po, &po->operand[0],
             lmod_cast(po, po->operand[0].lmod, 1), 0));
         break;
@@ -6651,6 +6675,26 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
           po->op == OP_FIDIVR ? '/' : '-');
         break;
 
+      case OP_FCOS:
+        fprintf(fout, "  f_st0 = cos%s(f_st0);",
+          need_double ? "" : "f");
+        break;
+
+      case OP_FPATAN:
+        fprintf(fout, "  f_st0 = atan%s(f_st1 / f_st0);",
+          need_double ? "" : "f");
+        break;
+
+      case OP_FSIN:
+        fprintf(fout, "  f_st0 = sin%s(f_st0);",
+          need_double ? "" : "f");
+        break;
+
+      case OP_FSQRT:
+        fprintf(fout, "  f_st0 = sqrt%s(f_st0);",
+          need_double ? "" : "f");
+        break;
+
       case OPP_FTOL:
         ferr_assert(po, po->flags & OPF_32BIT);
         fprintf(fout, "  eax = (s32)f_st0;");
@@ -6778,10 +6822,13 @@ static struct scanned_var {
 } *hg_vars;
 static int hg_var_cnt;
 
+static char **hg_refs;
+static int hg_ref_cnt;
+
 static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
   int count);
 
-struct func_prototype *hg_fp_add(const char *funcn)
+static struct func_prototype *hg_fp_add(const char *funcn)
 {
   struct func_prototype *fp;
 
@@ -6843,6 +6890,19 @@ static int hg_fp_cmp_id(const void *p1_, const void *p2_)
 }
 #endif
 
+static void hg_ref_add(const char *name)
+{
+  if ((hg_ref_cnt & 0xff) == 0) {
+    hg_refs = realloc(hg_refs, sizeof(hg_refs[0]) * (hg_ref_cnt + 0x100));
+    my_assert_not(hg_refs, NULL);
+    memset(hg_refs + hg_ref_cnt, 0, sizeof(hg_refs[0]) * 0x100);
+  }
+
+  hg_refs[hg_ref_cnt] = strdup(name);
+  my_assert_not(hg_refs[hg_ref_cnt], NULL);
+  hg_ref_cnt++;
+}
+
 // recursive register dep pass
 // - track saved regs (part 2)
 // - try to figure out arg-regs
@@ -7197,6 +7257,24 @@ static void hg_fp_resolve_deps(struct func_prototype *fp)
   }
 }
 
+// make all thiscall/edx arg functions referenced from .data fastcall
+static void do_func_refs_from_data(void)
+{
+  struct func_prototype *fp, fp_s;
+  int i;
+
+  for (i = 0; i < hg_ref_cnt; i++) {
+    strcpy(fp_s.name, hg_refs[i]);
+    fp = bsearch(&fp_s, hg_fp, hg_fp_cnt,
+      sizeof(hg_fp[0]), hg_fp_cmp_name);
+    if (fp == NULL)
+      continue;
+
+    if (fp->argc_stack != 0 && (fp->regmask_dep & (mxCX | mxDX)))
+      fp->regmask_dep |= mxCX | mxDX;
+  }
+}
+
 static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
   int count)
 {
@@ -7204,7 +7282,7 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
   char *p, namebuf[NAMELEN];
   const char *name;
   int regmask_dep;
-  int argc_stack;
+  int argc_normal;
   int j, arg;
 
   for (; count > 0; count--, fp++) {
@@ -7242,18 +7320,25 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
     }
 
     regmask_dep = fp->regmask_dep;
-    argc_stack = fp->argc_stack;
+    argc_normal = fp->argc_stack;
 
     fprintf(fout, "%-5s", fp->pp ? fp->pp->ret_type.name :
       (fp->has_ret ? "int" : "void"));
-    if (regmask_dep && (fp->is_stdcall || argc_stack == 0)
-      && (regmask_dep & ~((1 << xCX) | (1 << xDX))) == 0)
+    if (regmask_dep && (fp->is_stdcall || fp->argc_stack > 0)
+      && (regmask_dep & ~mxCX) == 0)
+    {
+      fprintf(fout, "/*__thiscall*/  ");
+      argc_normal++;
+      regmask_dep = 0;
+    }
+    else if (regmask_dep && (fp->is_stdcall || fp->argc_stack == 0)
+      && (regmask_dep & ~(mxCX | mxDX)) == 0)
     {
       fprintf(fout, "  __fastcall    ");
-      if (!(regmask_dep & (1 << xDX)) && argc_stack == 0)
-        argc_stack = 1;
+      if (!(regmask_dep & (1 << xDX)) && fp->argc_stack == 0)
+        argc_normal = 1;
       else
-        argc_stack += 2;
+        argc_normal += 2;
       regmask_dep = 0;
     }
     else if (regmask_dep && !fp->is_stdcall) {
@@ -7283,7 +7368,7 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
       }
     }
 
-    for (j = 0; j < argc_stack; j++) {
+    for (j = 0; j < argc_normal; j++) {
       arg++;
       if (arg != 1)
         fprintf(fout, ", ");
@@ -7335,6 +7420,9 @@ static void output_hdr(FILE *fout)
   for (i = 0; i < hg_fp_cnt; i++)
     hg_fp_resolve_deps(&hg_fp[i]);
 
+  // adjust functions referenced from data segment
+  do_func_refs_from_data();
+
   // note: messes up .proto ptr, don't use
   //qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_id);
 
@@ -7378,7 +7466,7 @@ static char *next_word_s(char *w, size_t wsize, char *s)
   s = sskip(s);
 
   i = 0;
-  if (*s == '\'') {
+  if (*s == '\'' && s[1] != '\r' && s[1] != '\n') {
     w[0] = s[0];
     for (i = 1; i < wsize - 1; i++) {
       if (s[i] == 0) {
@@ -7432,7 +7520,7 @@ static int is_xref_needed(char *p, char **rlist, int rlist_len)
   return 1;
 }
 
-static int xrefs_show_need(FILE *fasm, char *p,
+static int ida_xrefs_show_need(FILE *fasm, char *p,
   char **rlist, int rlist_len)
 {
   int found_need = 0;
@@ -7479,7 +7567,8 @@ static void scan_variables(FILE *fasm, char **rlist, int rlist_len)
 {
   struct scanned_var *var;
   char line[256] = { 0, };
-  char words[3][256];
+  char words[4][256];
+  int no_identifier;
   char *p = NULL;
   int wordc;
   int l;
@@ -7518,8 +7607,7 @@ static void scan_variables(FILE *fasm, char **rlist, int rlist_len)
       asmln++;
 
       p = line;
-      if (my_isblank(*p))
-        continue;
+      no_identifier = my_isblank(*p);
 
       p = sskip(p);
       if (*p == 0 || *p == ';')
@@ -7539,13 +7627,19 @@ static void scan_variables(FILE *fasm, char **rlist, int rlist_len)
       if (wordc < 2)
         continue;
 
+      if (no_identifier) {
+        if (wordc >= 3 && IS(words[0], "dd") && IS(words[1], "offset"))
+          hg_ref_add(words[2]);
+        continue;
+      }
+
       if (IS_START(words[0], "__IMPORT_DESCRIPTOR_")) {
         // when this starts, we don't need anything from this section
         break;
       }
 
       // check refs comment(s)
-      if (!xrefs_show_need(fasm, p, rlist, rlist_len))
+      if (!ida_xrefs_show_need(fasm, p, rlist, rlist_len))
         continue;
 
       if ((hg_var_cnt & 0xff) == 0) {
@@ -7575,8 +7669,11 @@ static void scan_variables(FILE *fasm, char **rlist, int rlist_len)
         continue;
       }
 
-      if      (IS(words[1], "dd"))
+      if      (IS(words[1], "dd")) {
         var->lmod = OPLM_DWORD;
+        if (wordc >= 4 && IS(words[2], "offset"))
+          hg_ref_add(words[3]);
+      }
       else if (IS(words[1], "dw"))
         var->lmod = OPLM_WORD;
       else if (IS(words[1], "db")) {
@@ -7901,6 +7998,7 @@ int main(int argc, char *argv[])
       {
         static const char *attrs[] = {
           "clear_sf",
+          "clear_regmask",
         };
 
         // parse manual attribute-list comment
@@ -7915,12 +8013,17 @@ int main(int argc, char *argv[])
               break;
             }
           }
-          if (i == 0 && *p == '=') {
-            // clear_sf=start,len (in dwords)
-            ret = sscanf(p, "=%d,%d%n", &g_stack_clear_start,
-                    &g_stack_clear_len, &j);
+          if (*p == '=') {
+            j = ret = 0;
+            if (i == 0)
+              // clear_sf=start,len (in dwords)
+              ret = sscanf(p, "=%d,%d%n", &g_stack_clear_start,
+                      &g_stack_clear_len, &j);
+            else if (i == 1)
+              // clear_regmask=<mask>
+              ret = sscanf(p, "=%x%n", &g_regmask_init, &j) + 1;
             if (ret < 2) {
-              anote("unparsed clear_sf attr value: %s\n", p);
+              anote("unparsed attr value: %s\n", p);
               break;
             }
             p += j;
@@ -8102,6 +8205,7 @@ do_pending_endp:
       g_sct_func_attr = 0;
       g_stack_clear_start = 0;
       g_stack_clear_len = 0;
+      g_regmask_init = 0;
       skip_warned = 0;
       g_skip_func = 0;
       g_func[0] = 0;