translate: bswap, allsh* and some tweaks
[ia32rtools.git] / tools / translate.c
index a7f629e..1ae3cf7 100644 (file)
@@ -79,6 +79,7 @@ enum op_op {
        OP_NOT,
        OP_XLAT,
        OP_CDQ,
+       OP_BSWAP,
        OP_LODS,
        OP_STOS,
        OP_MOVS,
@@ -136,14 +137,19 @@ enum op_op {
   OP_FISUB,
   OP_FIDIVR,
   OP_FISUBR,
+  OP_FCHS,
   OP_FCOS,
   OP_FPATAN,
   OP_FPTAN,
   OP_FSIN,
   OP_FSQRT,
+  OP_FXCH,
+  OP_FYL2X,
   // mmx
   OP_EMMS,
   // pseudo-ops for lib calls
+  OPP_ALLSHL,
+  OPP_ALLSHR,
   OPP_FTOL,
   // undefined
   OP_UD2,
@@ -337,6 +343,9 @@ enum x86_regs {
 #define mxDX     (1 << xDX)
 #define mxST0    (1 << xST0)
 #define mxST1    (1 << xST1)
+#define mxST1_0  (mxST1 | mxST0)
+#define mxST7_2  (0xfc << xST0)
+#define mxSTa    (0xff << xST0)
 
 // possible basic comparison types (without inversion)
 enum parsed_flag_op {
@@ -918,6 +927,7 @@ static const struct {
   { "not",  OP_NOT,    1, 1, OPF_DATA },
   { "xlat", OP_XLAT,   0, 0, OPF_DATA },
   { "cdq",  OP_CDQ,    0, 0, OPF_DATA },
+  { "bswap",OP_BSWAP,  1, 1, OPF_DATA },
   { "lodsb",OP_LODS,   0, 0, OPF_DATA },
   { "lodsw",OP_LODS,   0, 0, OPF_DATA },
   { "lodsd",OP_LODS,   0, 0, OPF_DATA },
@@ -1029,6 +1039,7 @@ static const struct {
   { "fld",    OP_FLD,    1, 1, OPF_FPUSH },
   { "fild",   OP_FILD,   1, 1, OPF_FPUSH },
   { "fld1",   OP_FLDc,   0, 0, OPF_FPUSH },
+  { "fldln2", OP_FLDc,   0, 0, OPF_FPUSH },
   { "fldz",   OP_FLDc,   0, 0, OPF_FPUSH },
   { "fstp",   OP_FST,    1, 1, OPF_FPOP },
   { "fst",    OP_FST,    1, 1, 0 },
@@ -1050,15 +1061,20 @@ static const struct {
   { "fisub",  OP_FISUB,  1, 1, 0 },
   { "fidivr", OP_FIDIVR, 1, 1, 0 },
   { "fisubr", OP_FISUBR, 1, 1, 0 },
+  { "fchs",   OP_FCHS,   0, 0, 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 },
+  { "fxch",   OP_FXCH,   1, 1, 0 },
+  { "fyl2x",  OP_FYL2X,  0, 0, OPF_FPOP },
   // mmx
   { "emms",   OP_EMMS,   0, 0, OPF_DATA },
   { "movq",   OP_MOV,    2, 2, OPF_DATA },
   // pseudo-ops for lib calls
+  { "_allshl",OPP_ALLSHL },
+  { "_allshr",OPP_ALLSHR },
   { "_ftol",  OPP_FTOL },
   // must be last
   { "ud2",    OP_UD2 },
@@ -1157,6 +1173,7 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc)
   case OP_INC:
   case OP_DEC:
   case OP_NEG:
+  case OP_BSWAP:
   // more below..
     op->regmask_src |= op->regmask_dst;
     break;
@@ -1345,6 +1362,8 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc)
     op->regmask_dst |= mxST0;
     if      (IS(words[op_w] + 3, "1"))
       op->operand[0].val = X87_CONST_1;
+    else if (IS(words[op_w] + 3, "ln2"))
+      op->operand[0].val = X87_CONST_LN2;
     else if (IS(words[op_w] + 3, "z"))
       op->operand[0].val = X87_CONST_Z;
     else
@@ -1382,14 +1401,17 @@ 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_FCHS:
   case OP_FCOS:
   case OP_FSIN:
   case OP_FSQRT:
+  case OP_FXCH:
     op->regmask_src |= mxST0;
     op->regmask_dst |= mxST0;
     break;
 
   case OP_FPATAN:
+  case OP_FYL2X:
     op->regmask_src |= mxST0 | mxST1;
     op->regmask_dst |= mxST0;
     break;
@@ -2190,7 +2212,7 @@ static char *out_src_opr_u32(char *buf, size_t buf_size,
 }
 
 static char *out_src_opr_float(char *buf, size_t buf_size,
-  struct parsed_op *po, struct parsed_opr *popr)
+  struct parsed_op *po, struct parsed_opr *popr, int need_float_stack)
 {
   const char *cast = NULL;
   char tmp[256];
@@ -2200,7 +2222,15 @@ static char *out_src_opr_float(char *buf, size_t buf_size,
     if (popr->reg < xST0 || popr->reg > xST7)
       ferr(po, "bad reg: %d\n", popr->reg);
 
-    snprintf(buf, buf_size, "f_st%d", popr->reg - xST0);
+    if (need_float_stack) {
+      if (popr->reg == xST0)
+        snprintf(buf, buf_size, "f_st[f_stp & 7]");
+      else
+        snprintf(buf, buf_size, "f_st[(f_stp + %d) & 7]",
+          popr->reg - xST0);
+    }
+    else
+      snprintf(buf, buf_size, "f_st%d", popr->reg - xST0);
     break;
 
   case OPT_REGMEM:
@@ -2229,10 +2259,10 @@ static char *out_src_opr_float(char *buf, size_t buf_size,
 }
 
 static char *out_dst_opr_float(char *buf, size_t buf_size,
-  struct parsed_op *po, struct parsed_opr *popr)
+  struct parsed_op *po, struct parsed_opr *popr, int need_float_stack)
 {
   // same?
-  return out_src_opr_float(buf, buf_size, po, popr);
+  return out_src_opr_float(buf, buf_size, po, popr, need_float_stack);
 }
 
 static void out_test_for_cc(char *buf, size_t buf_size,
@@ -3519,7 +3549,9 @@ static void resolve_branches_parse_calls(int opcnt)
     unsigned int regmask_src;
     unsigned int regmask_dst;
   } pseudo_ops[] = {
-    { "__ftol", OPP_FTOL, OPF_FPOP, mxST0, mxAX | mxDX },
+    { "__allshl", OPP_ALLSHL, OPF_DATA, mxAX|mxDX|mxCX, mxAX|mxDX },
+    { "__allshr", OPP_ALLSHR, OPF_DATA, mxAX|mxDX|mxCX, mxAX|mxDX },
+    { "__ftol",   OPP_FTOL,   OPF_FPOP, mxST0, mxAX | mxDX },
   };
   const struct parsed_proto *pp_c;
   struct parsed_proto *pp;
@@ -3803,11 +3835,11 @@ static void scan_prologue_epilogue(int opcnt)
   {
     g_sp_frame = 1;
 
-    i++;
     do {
       for (; i < opcnt; i++)
         if (ops[i].flags & OPF_TAIL)
           break;
+
       j = i - 1;
       if (i == opcnt && (ops[j].flags & OPF_JMP)) {
         if (ops[j].bt_i != -1 || ops[j].btj != NULL)
@@ -3844,7 +3876,16 @@ static void scan_prologue_epilogue(int opcnt)
             || !IS(opr_name(&ops[j], 0), "esp")
             || ops[j].operand[1].type != OPT_CONST
             || ops[j].operand[1].val != g_stack_fsz)
+        {
+          if (ops[i].op == OP_CALL && ops[i].pp != NULL
+            && ops[i].pp->is_noreturn)
+          {
+            // noreturn tailcall with no epilogue
+            i++;
+            continue;
+          }
           ferr(&ops[j], "'add esp' expected\n");
+        }
 
         ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
         ops[j].operand[1].val = 0; // hack for stack arg scanner
@@ -4318,9 +4359,11 @@ static int collect_call_args_early(struct parsed_op *po, int i,
     else if (ops[j].op == OP_PUSH) {
       if (ops[j].flags & (OPF_FARG|OPF_FARGNR))
         return -1;
-      ret = scan_for_mod(&ops[j], j + 1, i, 1);
-      if (ret >= 0)
-        return -1;
+      if (!g_header_mode) {
+        ret = scan_for_mod(&ops[j], j + 1, i, 1);
+        if (ret >= 0)
+          return -1;
+      }
 
       if (pp->arg[arg].type.is_va_list)
         return -1;
@@ -4663,7 +4706,6 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits,
   int *regmask_init, int regmask_arg)
 {
   struct parsed_op *po;
-  unsigned int mask;
   int already_saved;
   int regmask_new;
   int regmask_op;
@@ -4763,20 +4805,15 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits,
             po->regmask_dst &= ~(1 << xAX);
         }
       }
+
+      // not "full stack" mode and have something in stack
+      if (!(regmask_now & mxST7_2) && (regmask_now & mxST1_0))
+        ferr(po, "float stack is not empty on func call\n");
     }
 
     if (po->flags & OPF_NOREGS)
       continue;
 
-    if (po->flags & OPF_FPUSH) {
-      if (regmask_now & mxST1)
-        ferr(po, "TODO: FPUSH on active ST1\n");
-      if (regmask_now & mxST0)
-        po->flags |= OPF_FSHIFT;
-      mask = mxST0 | mxST1;
-      regmask_now = (regmask_now & ~mask) | ((regmask_now & mxST0) << 1);
-    }
-
     // if incomplete register is used, clear it on init to avoid
     // later use of uninitialized upper part in some situations
     if ((po->flags & OPF_DATA) && po->operand[0].type == OPT_REG
@@ -4809,21 +4846,34 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits,
       }
     }
 
+    if (po->flags & OPF_FPUSH) {
+      if (regmask_now & mxST1)
+        regmask_now |= mxSTa; // switch to "full stack" mode
+      if (regmask_now & mxSTa)
+        po->flags |= OPF_FSHIFT;
+      if (!(regmask_now & mxST7_2)) {
+        regmask_now =
+          (regmask_now & ~mxST1_0) | ((regmask_now & mxST0) << 1);
+      }
+    }
+
     regmask_now |= regmask_op;
     *regmask |= regmask_now;
 
     // released regs
     if (po->flags & OPF_FPOP) {
-      mask = mxST0 | mxST1;
-      if (!(regmask_now & mask))
+      if ((regmask_now & mxSTa) == 0)
         ferr(po, "float pop on empty stack?\n");
-      if (regmask_now & mxST1)
+      if (regmask_now & (mxST7_2 | mxST1))
         po->flags |= OPF_FSHIFT;
-      regmask_now = (regmask_now & ~mask) | ((regmask_now & mxST1) >> 1);
+      if (!(regmask_now & mxST7_2)) {
+        regmask_now =
+          (regmask_now & ~mxST1_0) | ((regmask_now & mxST1) >> 1);
+      }
     }
 
     if (po->flags & OPF_TAIL) {
-      if (regmask_now & (mxST0 | mxST1))
+      if (!(regmask_now & mxST7_2) && (regmask_now & mxST1_0))
         ferr(po, "float regs on tail: %x\n", regmask_now);
 
       // there is support for "conditional tailcall", sort of
@@ -4954,9 +5004,12 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   int save_arg_vars[MAX_ARG_GRP] = { 0, };
   unsigned char cbits[MAX_OPS / 8];
   const char *float_type;
-  int cond_vars = 0;
+  const char *float_st0;
+  const char *float_st1;
+  int need_float_stack = 0;
   int need_tmp_var = 0;
   int need_tmp64 = 0;
+  int cond_vars = 0;
   int had_decl = 0;
   int label_pending = 0;
   int need_double = 0;
@@ -4969,6 +5022,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   int regmask = 0;      // used regs
   int pfomask = 0;
   int found = 0;
+  int dead_dst;
   int no_output;
   int i, j, l;
   int arg;
@@ -5176,16 +5230,23 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         cond_vars |= 1 << PFO_C;
     }
 
-    if (po->op == OP_CMPS || po->op == OP_SCAS) {
+    switch (po->op) {
+    case OP_CMPS:
+    case OP_SCAS:
       cond_vars |= 1 << PFO_Z;
-    }
-    else if (po->op == OP_MUL
-      || (po->op == OP_IMUL && po->operand_cnt == 1))
-    {
+      break;
+
+    case OP_MUL:
       if (po->operand[0].lmod == OPLM_DWORD)
         need_tmp64 = 1;
-    }
-    else if (po->op == OP_CALL) {
+      break;
+
+    case OP_IMUL:
+      if (po->operand_cnt == 1 && po->operand[0].lmod == OPLM_DWORD)
+        need_tmp64 = 1;
+      break;
+
+    case OP_CALL:
       // note: resolved non-reg calls are OPF_DONE already
       pp = po->pp;
       ferr_assert(po, pp != NULL);
@@ -5232,27 +5293,31 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         if (pp->argc_stack > 0)
           pp->is_stdcall = 1;
       }
-    }
-    else if (po->op == OP_MOV && po->operand[0].pp != NULL
-      && po->operand[1].pp != NULL)
-    {
-      // <var> = offset <something>
-      if ((po->operand[1].pp->is_func || po->operand[1].pp->is_fptr)
-        && !IS_START(po->operand[1].name, "off_"))
+      break;
+
+    case OP_MOV:
+      if (po->operand[0].pp != NULL && po->operand[1].pp != NULL)
       {
-        if (!po->operand[0].pp->is_fptr)
-          ferr(po, "%s not declared as fptr when it should be\n",
-            po->operand[0].name);
-        if (pp_cmp_func(po->operand[0].pp, po->operand[1].pp)) {
-          pp_print(buf1, sizeof(buf1), po->operand[0].pp);
-          pp_print(buf2, sizeof(buf2), po->operand[1].pp);
-          fnote(po, "var:  %s\n", buf1);
-          fnote(po, "func: %s\n", buf2);
-          ferr(po, "^ mismatch\n");
+        // <var> = offset <something>
+        if ((po->operand[1].pp->is_func || po->operand[1].pp->is_fptr)
+          && !IS_START(po->operand[1].name, "off_"))
+        {
+          if (!po->operand[0].pp->is_fptr)
+            ferr(po, "%s not declared as fptr when it should be\n",
+              po->operand[0].name);
+          if (pp_cmp_func(po->operand[0].pp, po->operand[1].pp)) {
+            pp_print(buf1, sizeof(buf1), po->operand[0].pp);
+            pp_print(buf2, sizeof(buf2), po->operand[1].pp);
+            fnote(po, "var:  %s\n", buf1);
+            fnote(po, "func: %s\n", buf2);
+            ferr(po, "^ mismatch\n");
+          }
         }
       }
-    }
-    else if (po->op == OP_DIV || po->op == OP_IDIV) {
+      break;
+
+    case OP_DIV:
+    case OP_IDIV:
       if (po->operand[0].lmod == OPLM_DWORD) {
         // 32bit division is common, look for it
         if (po->op == OP_DIV)
@@ -5266,24 +5331,46 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       }
       else
         need_tmp_var = 1;
-    }
-    else if (po->op == OP_CLD)
+      break;
+
+    case OP_CLD:
       po->flags |= OPF_RMD | OPF_DONE;
-    else if (po->op == OPP_FTOL) {
+      break;
+
+    case OP_RCL:
+    case OP_RCR:
+    case OP_XCHG:
+      need_tmp_var = 1;
+      break;
+
+    case OP_FLD:
+      if (po->operand[0].lmod == OPLM_QWORD)
+        need_double = 1;
+      break;
+
+    case OPP_ALLSHL:
+    case OPP_ALLSHR:
+      need_tmp64 = 1;
+      break;
+
+    case OPP_FTOL: {
       struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xDX);
       j = -1;
       find_next_read(i + 1, opcnt, &opr, i + opcnt * 18, &j);
       if (j == -1)
         po->flags |= OPF_32BIT;
+      break;
     }
-    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;
+    default:
+      break;
+    }
   }
 
   float_type = need_double ? "double" : "float";
+  need_float_stack = !!(regmask & mxST7_2);
+  float_st0 = need_float_stack ? "f_st[f_stp & 7]" : "f_st0";
+  float_st1 = need_float_stack ? "f_st[(f_stp + 1) & 7]" : "f_st1";
 
   // output starts here
 
@@ -5441,14 +5528,21 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
     }
   }
   // ... x87
-  if (regmask_now & 0xff0000) {
-    for (reg = 16; reg < 24; reg++) {
-      if (regmask_now & (1 << reg)) {
-        fprintf(fout, "  %s f_st%d", float_type, reg - 16);
-        if (regmask_init & (1 << reg))
-          fprintf(fout, " = 0");
-        fprintf(fout, ";\n");
-        had_decl = 1;
+  if (need_float_stack) {
+    fprintf(fout, "  %s f_st[8];\n", float_type);
+    fprintf(fout, "  int f_stp = 0;\n");
+    had_decl = 1;
+  }
+  else {
+    if (regmask_now & 0xff0000) {
+      for (reg = 16; reg < 24; reg++) {
+        if (regmask_now & (1 << reg)) {
+          fprintf(fout, "  %s f_st%d", float_type, reg - 16);
+          if (regmask_init & (1 << reg))
+            fprintf(fout, " = 0");
+          fprintf(fout, ";\n");
+          had_decl = 1;
+        }
       }
     }
   }
@@ -5719,6 +5813,12 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         strcpy(g_comment, "cdq");
         break;
 
+      case OP_BSWAP:
+        assert_operand_cnt(1);
+        out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+        fprintf(fout, "  %s = __builtin_bswap32(%s);", buf1, buf1);
+        break;
+
       case OP_LODS:
         if (po->flags & OPF_REP) {
           assert_operand_cnt(3);
@@ -6350,7 +6450,11 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
               fprintf(fout, "(u32)");
           }
           else if (po->regmask_dst & mxST0) {
-            fprintf(fout, "f_st0 = ");
+            ferr_assert(po, po->flags & OPF_FPUSH);
+            if (need_float_stack)
+              fprintf(fout, "f_st[--f_stp & 7] = ");
+            else
+              fprintf(fout, "f_st0 = ");
           }
         }
 
@@ -6569,59 +6673,110 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         no_output = 1;
         break;
 
+      // pseudo ops
+      case OPP_ALLSHL:
+      case OPP_ALLSHR:
+        fprintf(fout, "  tmp64 = ((u64)edx << 32) | eax;\n");
+        fprintf(fout, "  tmp64 = (s64)tmp64 %s= LOBYTE(ecx);\n",
+          po->op == OPP_ALLSHL ? "<<" : ">>");
+        fprintf(fout, "  edx = tmp64 >> 32; eax = tmp64;");
+        strcat(g_comment, po->op == OPP_ALLSHL
+          ? " allshl" : " allshr");
+        break;
+
       // x87
       case OP_FLD:
-        if (po->flags & OPF_FSHIFT)
-          fprintf(fout, "  f_st1 = f_st0;\n");
-        if (po->operand[0].type == OPT_REG
-          && po->operand[0].reg == xST0)
-        {
-          strcat(g_comment, " fld st");
-          break;
+        if (need_float_stack) {
+          out_src_opr_float(buf1, sizeof(buf1),
+            po, &po->operand[0], 1);
+          if (po->regmask_src & mxSTa) {
+            fprintf(fout, "  f_st[(f_stp - 1) & 7] = %s; f_stp--;",
+              buf1);
+          }
+          else
+            fprintf(fout, "  f_st[--f_stp & 7] = %s;", buf1);
+        }
+        else {
+          if (po->flags & OPF_FSHIFT)
+            fprintf(fout, "  f_st1 = f_st0;");
+          if (po->operand[0].type == OPT_REG
+            && po->operand[0].reg == xST0)
+          {
+            strcat(g_comment, " fld st");
+            break;
+          }
+          fprintf(fout, "  f_st0 = %s;",
+            out_src_opr_float(buf1, sizeof(buf1),
+              po, &po->operand[0], 0));
         }
-        fprintf(fout, "  f_st0 = %s;",
-          out_src_opr_float(buf1, sizeof(buf1), po, &po->operand[0]));
         strcat(g_comment, " fld");
         break;
 
       case OP_FILD:
-        if (po->flags & OPF_FSHIFT)
-          fprintf(fout, "  f_st1 = f_st0;\n");
-        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));
+        out_src_opr(buf1, sizeof(buf1), po, &po->operand[0],
+          lmod_cast(po, po->operand[0].lmod, 1), 0);
+        snprintf(buf2, sizeof(buf2), "(%s)%s", float_type, buf1);
+        if (need_float_stack) {
+          fprintf(fout, "  f_st[--f_stp & 7] = %s;", buf2);
+        }
+        else {
+          if (po->flags & OPF_FSHIFT)
+            fprintf(fout, "  f_st1 = f_st0;");
+          fprintf(fout, "  f_st0 = %s;", buf2);
+        }
         strcat(g_comment, " fild");
         break;
 
       case OP_FLDc:
-        if (po->flags & OPF_FSHIFT)
-          fprintf(fout, "  f_st1 = f_st0;\n");
-        fprintf(fout, "  f_st0 = ");
+        if (need_float_stack)
+          fprintf(fout, "  f_st[--f_stp & 7] = ");
+        else {
+          if (po->flags & OPF_FSHIFT)
+            fprintf(fout, "  f_st1 = f_st0;");
+          fprintf(fout, "  f_st0 = ");
+        }
         switch (po->operand[0].val) {
-        case X87_CONST_1: fprintf(fout, "1.0;"); break;
-        case X87_CONST_Z: fprintf(fout, "0.0;"); break;
+        case X87_CONST_1:   fprintf(fout, "1.0;"); break;
+        case X87_CONST_LN2: fprintf(fout, "0.693147180559945;"); break;
+        case X87_CONST_Z:   fprintf(fout, "0.0;"); break;
         default: ferr(po, "TODO\n"); break;
         }
         break;
 
       case OP_FST:
-        if ((po->flags & OPF_FPOP) && po->operand[0].type == OPT_REG
-          && po->operand[0].reg == xST0)
-        {
-          no_output = 1;
-          break;
+        out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0],
+          need_float_stack);
+        dead_dst = po->operand[0].type == OPT_REG
+          && po->operand[0].reg == xST0;
+        if (need_float_stack) {
+          if (!dead_dst)
+            fprintf(fout, "  %s = f_st[f_stp & 7];", buf1);
+          if (po->flags & OPF_FSHIFT)
+            fprintf(fout, "  f_stp++;");
         }
-        fprintf(fout, "  %s = f_st0;",
-          out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0]));
-        if (po->flags & OPF_FSHIFT)
-          fprintf(fout, "\n  f_st0 = f_st1;");
-        strcat(g_comment, " fst");
+        else {
+          if (!dead_dst)
+            fprintf(fout, "  %s = f_st0;", buf1);
+          if (po->flags & OPF_FSHIFT)
+            fprintf(fout, "  f_st0 = f_st1;");
+        }
+        if (dead_dst && !(po->flags & OPF_FSHIFT))
+          no_output = 1;
+        else
+          strcat(g_comment, " fst");
         break;
 
       case OP_FADD:
       case OP_FDIV:
       case OP_FMUL:
       case OP_FSUB:
+        out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0],
+          need_float_stack);
+        out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1],
+          need_float_stack);
+        dead_dst = (po->flags & OPF_FPOP)
+          && po->operand[0].type == OPT_REG
+          && po->operand[0].reg == xST0;
         switch (po->op) {
         case OP_FADD: j = '+'; break;
         case OP_FDIV: j = '/'; break;
@@ -6629,27 +6784,55 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         case OP_FSUB: j = '-'; break;
         default: j = 'x'; break;
         }
-        if (po->flags & OPF_FSHIFT) {
-          fprintf(fout, "  f_st0 = f_st1 %c f_st0;", j);
+        if (need_float_stack) {
+          if (!dead_dst)
+            fprintf(fout, "  %s %c= %s;", buf1, j, buf2);
+          if (po->flags & OPF_FSHIFT)
+            fprintf(fout, "  f_stp++;");
         }
         else {
-          fprintf(fout, "  %s %c= %s;",
-           out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0]),
-           j,
-           out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1]));
+          if (po->flags & OPF_FSHIFT) {
+            // note: assumes only 2 regs handled
+            if (!dead_dst)
+              fprintf(fout, "  f_st0 = f_st1 %c f_st0;", j);
+            else
+              fprintf(fout, "  f_st0 = f_st1;");
+          }
+          else if (!dead_dst)
+            fprintf(fout, "  %s %c= %s;", buf1, j, buf2);
         }
+        no_output = (dead_dst && !(po->flags & OPF_FSHIFT));
         break;
 
       case OP_FDIVR:
       case OP_FSUBR:
-        if (po->flags & OPF_FSHIFT)
-          snprintf(buf1, sizeof(buf1), "f_st0");
-        else
-          out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0]);
-        fprintf(fout, "  %s = %s %c %s;", buf1,
-          out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1]),
-          po->op == OP_FDIVR ? '/' : '-',
-          out_src_opr_float(buf3, sizeof(buf3), po, &po->operand[0]));
+        out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0],
+          need_float_stack);
+        out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1],
+          need_float_stack);
+        out_src_opr_float(buf3, sizeof(buf3), po, &po->operand[0],
+          need_float_stack);
+        dead_dst = (po->flags & OPF_FPOP)
+          && po->operand[0].type == OPT_REG
+          && po->operand[0].reg == xST0;
+        j = po->op == OP_FDIVR ? '/' : '-';
+        if (need_float_stack) {
+          if (!dead_dst)
+            fprintf(fout, "  %s = %s %c %s;", buf1, buf2, j, buf3);
+          if (po->flags & OPF_FSHIFT)
+            fprintf(fout, "  f_stp++;");
+        }
+        else {
+          if (po->flags & OPF_FSHIFT) {
+            if (!dead_dst)
+              fprintf(fout, "  f_st0 = f_st0 %c f_st1;", j);
+            else
+              fprintf(fout, "  f_st0 = f_st1;");
+          }
+          else if (!dead_dst)
+            fprintf(fout, "  %s = %s %c %s;", buf1, buf2, j, buf3);
+        }
+        no_output = (dead_dst && !(po->flags & OPF_FSHIFT));
         break;
 
       case OP_FIADD:
@@ -6663,43 +6846,86 @@ 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= (%s)%s;", j, float_type,
+        fprintf(fout, "  %s %c= (%s)%s;", float_st0,
+          j, float_type,
           out_src_opr(buf1, sizeof(buf1), po, &po->operand[0],
             lmod_cast(po, po->operand[0].lmod, 1), 0));
         break;
 
       case OP_FIDIVR:
       case OP_FISUBR:
-        fprintf(fout, "  f_st0 = %s %c f_st0;",
-          out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1]),
-          po->op == OP_FIDIVR ? '/' : '-');
+        fprintf(fout, "  %s = %s %c %s;", float_st0,
+          out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1],
+            need_float_stack),
+          po->op == OP_FIDIVR ? '/' : '-', float_st0);
+        break;
+
+      case OP_FCHS:
+        fprintf(fout, "  %s = -%s;", float_st0, float_st0);
         break;
 
       case OP_FCOS:
-        fprintf(fout, "  f_st0 = cos%s(f_st0);",
-          need_double ? "" : "f");
+        fprintf(fout, "  %s = cos%s(%s);", float_st0,
+          need_double ? "" : "f", float_st0);
         break;
 
       case OP_FPATAN:
-        fprintf(fout, "  f_st0 = atan%s(f_st1 / f_st0);",
-          need_double ? "" : "f");
+        if (need_float_stack) {
+          fprintf(fout, "  %s = atan%s(%s / %s);", float_st1,
+            need_double ? "" : "f", float_st1, float_st0);
+          fprintf(fout, " f_stp++;");
+        }
+        else {
+          fprintf(fout, "  f_st0 = atan%s(f_st1 / f_st0);",
+            need_double ? "" : "f");
+        }
+        break;
+
+      case OP_FYL2X:
+        if (need_float_stack) {
+          fprintf(fout, "  %s = %s * log2%s(%s);", float_st1,
+            float_st1, need_double ? "" : "f", float_st0);
+          fprintf(fout, " f_stp++;");
+        }
+        else {
+          fprintf(fout, "  f_st0 = f_st1 * log2%s(f_st0);",
+            need_double ? "" : "f");
+        }
         break;
 
       case OP_FSIN:
-        fprintf(fout, "  f_st0 = sin%s(f_st0);",
-          need_double ? "" : "f");
+        fprintf(fout, "  %s = sin%s(%s);", float_st0,
+          need_double ? "" : "f", float_st0);
         break;
 
       case OP_FSQRT:
-        fprintf(fout, "  f_st0 = sqrt%s(f_st0);",
-          need_double ? "" : "f");
+        fprintf(fout, "  %s = sqrt%s(%s);", float_st0,
+          need_double ? "" : "f", float_st0);
+        break;
+
+      case OP_FXCH:
+        dead_dst = po->operand[0].type == OPT_REG
+          && po->operand[0].reg == xST0;
+        if (!dead_dst) {
+          out_src_opr_float(buf1, sizeof(buf1), po, &po->operand[0],
+            need_float_stack);
+          fprintf(fout, "  { %s t = %s; %s = %s; %s = t; }", float_type,
+            float_st0, float_st0, buf1, buf1);
+          strcat(g_comment, " fxch");
+        }
+        else
+          no_output = 1;
         break;
 
       case OPP_FTOL:
         ferr_assert(po, po->flags & OPF_32BIT);
-        fprintf(fout, "  eax = (s32)f_st0;");
-        if (po->flags & OPF_FSHIFT)
-          fprintf(fout, "\n  f_st0 = f_st1;");
+        fprintf(fout, "  eax = (s32)%s;", float_st0);
+        if (po->flags & OPF_FSHIFT) {
+          if (need_float_stack)
+            fprintf(fout, " f_stp++;");
+          else
+            fprintf(fout, " f_st0 = f_st1;");
+        }
         strcat(g_comment, " ftol");
         break;
 
@@ -7216,7 +7442,7 @@ static void gen_hdr(const char *funcn, int opcnt)
       fp->argc_stack--;
   }
 
-  fp->regmask_dep = regmask_dep & ~(1 << xSP);
+  fp->regmask_dep = regmask_dep & ~((1 << xSP) | mxSTa);
   fp->has_ret = has_ret;
 #if 0
   printf("// has_ret %d, regmask_dep %x\n",