translate: float: stack mode and some more ops
authornotaz <notasas@gmail.com>
Sat, 25 Apr 2015 20:40:02 +0000 (23:40 +0300)
committernotaz <notasas@gmail.com>
Sat, 25 Apr 2015 20:40:02 +0000 (23:40 +0300)
tests/Makefile
tests/x87.asm
tests/x87.expect.c
tests/x87_f.asm [new file with mode: 0644]
tests/x87_f.expect.c [new file with mode: 0644]
tests/x87_f.seed.h [new file with mode: 0644]
tests/x87_s.asm [new file with mode: 0644]
tests/x87_s.expect.c [new file with mode: 0644]
tests/x87_s.seed.h [new file with mode: 0644]
tools/translate.c

index 16b404d..81d5d99 100644 (file)
@@ -1,6 +1,6 @@
 
 TESTS = reg_call1 reg_call2 reg_call3 reg_call_tail reg_save \
-       varargs ops x87
+       varargs ops x87 x87_f x87_s
 
 all: $(addsuffix .ok,$(TESTS))
 
index 240730f..e6b40e5 100644 (file)
@@ -17,8 +17,13 @@ arg_0           = dword ptr  8
                 fdiv    st(1), st
                 faddp   st(1), st
                 fld1
-                fdivrp  [ebp+var_18]
+                fdivr   [ebp+var_18]
+                fxch    st(1)
+                fchs
+                fsubrp  st, st
                 fld     st(1)
+                fyl2x
+                fld     st
                 fstp    [ebp+var_18]
                 fst     [ebp+var_20]
                 call    __ftol
index d114f10..a5d0e70 100644 (file)
@@ -9,17 +9,18 @@ int sub_test(int a1, int a2)
   f_st0 = (double)(s32)sf.d[0];  // var_20 fild
   f_st0 /= (double)(s32)a1;  // arg_0
   f_st0 *= *((double *)(u32)&sf.q[1]);  // var_18
-  f_st1 = f_st0;
-  f_st0 = (double)(s32)sf.d[0];  // var_20 fild
+  f_st1 = f_st0;  f_st0 = (double)(s32)sf.d[0];  // var_20 fild
   f_st1 /= f_st0;
   f_st0 = f_st1 + f_st0;
-  f_st1 = f_st0;
-  f_st0 = 1.0;
+  f_st1 = f_st0;  f_st0 = 1.0;
   f_st0 = *((double *)(u32)&sf.q[1]) / f_st0;  // var_18
-  f_st1 = f_st0;
-  f_st0 = f_st1;  // fld
-  *((double *)(u32)&sf.q[1]) = f_st0;
-  f_st0 = f_st1;  // var_18 fst
+  { double t = f_st0; f_st0 = f_st1; f_st1 = t; }  // fxch
+  f_st0 = -f_st0;
+  f_st0 = f_st1;
+  f_st1 = f_st0;  f_st0 = f_st1;  // fld
+  f_st0 = f_st1 * log2(f_st0);
+  f_st1 = f_st0;  // fld st
+  *((double *)(u32)&sf.q[1]) = f_st0;  f_st0 = f_st1;  // var_18 fst
   *((float *)(u32)&sf.d[0]) = f_st0;  // var_20 fst
   eax = (s32)f_st0;  // ftol
   return eax;
diff --git a/tests/x87_f.asm b/tests/x87_f.asm
new file mode 100644 (file)
index 0000000..06a9118
--- /dev/null
@@ -0,0 +1,24 @@
+
+_text           segment para public 'CODE' use32
+
+sub_test        proc near
+
+var_4           = dword ptr -4
+
+                push    ebp
+                mov     ebp, esp
+                sub     esp, 4
+                mov     [ebp+var_4], 4
+                fild    [ebp+var_4]
+                fild    [ebp+var_4]
+                fsqrt
+                fpatan
+                call    __ftol
+                mov     esp, ebp
+                pop     ebp
+                retn
+sub_test        endp
+
+_text           ends
+
+; vim:expandtab
diff --git a/tests/x87_f.expect.c b/tests/x87_f.expect.c
new file mode 100644 (file)
index 0000000..b943c96
--- /dev/null
@@ -0,0 +1,17 @@
+int sub_test()
+{
+  union { u32 d[1]; u8 b[4]; } sf;
+  u32 eax;
+  u32 edx;
+  float f_st0;
+  float f_st1;
+
+  sf.d[0] = 4;  // var_4
+  f_st0 = (float)(s32)sf.d[0];  // var_4 fild
+  f_st1 = f_st0;  f_st0 = (float)(s32)sf.d[0];  // var_4 fild
+  f_st0 = sqrtf(f_st0);
+  f_st0 = atanf(f_st1 / f_st0);
+  eax = (s32)f_st0;  // ftol
+  return eax;
+}
+
diff --git a/tests/x87_f.seed.h b/tests/x87_f.seed.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/x87_s.asm b/tests/x87_s.asm
new file mode 100644 (file)
index 0000000..9b86871
--- /dev/null
@@ -0,0 +1,37 @@
+
+_text           segment para public 'CODE' use32
+
+sub_test        proc near
+
+var_4           = dword ptr -4
+
+                push    ebp
+                mov     ebp, esp
+                sub     esp, 4
+                mov     [ebp+var_4], 4
+                fild    [ebp+var_4]
+                fld     [ebp+var_4]
+                fild    [ebp+var_4]
+                fld1
+                fild    [ebp+var_4]
+                fldz
+                fldln2
+                fild    [ebp+var_4]
+                faddp   st, st(7)
+                fdivp   st(5), st
+                fyl2x
+                fsubp   st(2), st
+                fsubrp  st, st
+                fxch    st(6)
+                fchs
+                fpatan
+                fstp    [ebp+var_4]
+                call    __ftol
+                mov     esp, ebp
+                pop     ebp
+                retn
+sub_test        endp
+
+_text           ends
+
+; vim:expandtab
diff --git a/tests/x87_s.expect.c b/tests/x87_s.expect.c
new file mode 100644 (file)
index 0000000..6b01b42
--- /dev/null
@@ -0,0 +1,30 @@
+int sub_test()
+{
+  union { u32 d[1]; u8 b[4]; double q[1]; } sf;
+  u32 eax;
+  u32 edx;
+  float f_st[8];
+  int f_stp = 0;
+
+  sf.d[0] = 4;  // var_4
+  f_st[--f_stp & 7] = (float)(s32)sf.d[0];  // var_4 fild
+  f_st[--f_stp & 7] = *((float *)(u32)&sf.d[0]);  // var_4 fld
+  f_st[--f_stp & 7] = (float)(s32)sf.d[0];  // var_4 fild
+  f_st[--f_stp & 7] = 1.0;
+  f_st[--f_stp & 7] = (float)(s32)sf.d[0];  // var_4 fild
+  f_st[--f_stp & 7] = 0.0;
+  f_st[--f_stp & 7] = 0.693147180559945;
+  f_st[--f_stp & 7] = (float)(s32)sf.d[0];  // var_4 fild
+  f_stp++;
+  f_st[(f_stp + 5) & 7] /= f_st[f_stp & 7];  f_stp++;
+  f_st[(f_stp + 1) & 7] = f_st[(f_stp + 1) & 7] * log2f(f_st[f_stp & 7]); f_stp++;
+  f_st[(f_stp + 2) & 7] -= f_st[f_stp & 7];  f_stp++;
+  f_stp++;
+  { float t = f_st[f_stp & 7]; f_st[f_stp & 7] = f_st[(f_stp + 6) & 7]; f_st[(f_stp + 6) & 7] = t; }  // fxch
+  f_st[f_stp & 7] = -f_st[f_stp & 7];
+  f_st[(f_stp + 1) & 7] = atanf(f_st[(f_stp + 1) & 7] / f_st[f_stp & 7]); f_stp++;
+  *((float *)(u32)&sf.d[0]) = f_st[f_stp & 7];  f_stp++;  // var_4 fst
+  eax = (s32)f_st[f_stp & 7]; f_stp++;  // ftol
+  return eax;
+}
+
diff --git a/tests/x87_s.seed.h b/tests/x87_s.seed.h
new file mode 100644 (file)
index 0000000..e69de29
index a7f629e..2239106 100644 (file)
@@ -136,11 +136,14 @@ 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
@@ -337,6 +340,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 {
@@ -1029,6 +1035,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,11 +1057,14 @@ 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 },
@@ -1345,6 +1355,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 +1394,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 +2205,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 +2215,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 +2252,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,
@@ -3803,11 +3826,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)
@@ -4663,7 +4686,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,6 +4785,10 @@ 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)
@@ -4770,11 +4796,13 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits,
 
     if (po->flags & OPF_FPUSH) {
       if (regmask_now & mxST1)
-        ferr(po, "TODO: FPUSH on active ST1\n");
-      if (regmask_now & mxST0)
+        regmask_now |= mxSTa; // switch to "full stack" mode
+      if (regmask_now & mxSTa)
         po->flags |= OPF_FSHIFT;
-      mask = mxST0 | mxST1;
-      regmask_now = (regmask_now & ~mask) | ((regmask_now & mxST0) << 1);
+      if (!(regmask_now & mxST7_2)) {
+        regmask_now =
+          (regmask_now & ~mxST1_0) | ((regmask_now & mxST0) << 1);
+      }
     }
 
     // if incomplete register is used, clear it on init to avoid
@@ -4814,16 +4842,18 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits,
 
     // 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 +4984,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 +5002,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;
@@ -5284,6 +5318,9 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   }
 
   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 +5478,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;
+        }
       }
     }
   }
@@ -6350,7 +6394,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 = ");
           }
         }
 
@@ -6571,57 +6619,97 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 
       // 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++;");
+        }
+        else {
+          if (!dead_dst)
+            fprintf(fout, "  %s = f_st0;", buf1);
+          if (po->flags & OPF_FSHIFT)
+            fprintf(fout, "  f_st0 = f_st1;");
         }
-        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");
+        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 +6717,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 +6779,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;