translate: more float stuff
[ia32rtools.git] / tools / translate.c
index 324bc26..07f290a 100644 (file)
 #include "my_str.h"
 #include "common.h"
 
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
-#define IS(w, y) !strcmp(w, y)
-#define IS_START(w, y) !strncmp(w, y, strlen(y))
-
 #include "protoparse.h"
 
 static const char *asmfn;
@@ -73,6 +69,7 @@ enum op_flags {
   OPF_FPUSH  = (1 << 22), /* pushes x87 stack */
   OPF_FPOP   = (1 << 23), /* pops x87 stack */
   OPF_FSHIFT = (1 << 24), /* x87 stack shift is actually needed */
+  OPF_FINT   = (1 << 25), /* integer float op arg */
 };
 
 enum op_op {
@@ -117,6 +114,7 @@ enum op_op {
        OP_ADC,
        OP_SBB,
        OP_BSF,
+       OP_BSR,
        OP_INC,
        OP_DEC,
        OP_NEG,
@@ -360,6 +358,7 @@ enum x86_regs {
 #define mxAX     (1 << xAX)
 #define mxCX     (1 << xCX)
 #define mxDX     (1 << xDX)
+#define mxSP     (1 << xSP)
 #define mxST0    (1 << xST0)
 #define mxST1    (1 << xST1)
 #define mxST1_0  (mxST1 | mxST0)
@@ -998,6 +997,7 @@ static const struct {
   { "adc",  OP_ADC,    2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C },
   { "sbb",  OP_SBB,    2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C },
   { "bsf",  OP_BSF,    2, 2, OPF_DATA|OPF_FLAGS },
+  { "bsr",  OP_BSR,    2, 2, OPF_DATA|OPF_FLAGS },
   { "inc",  OP_INC,    1, 1, OPF_DATA|OPF_FLAGS },
   { "dec",  OP_DEC,    1, 1, OPF_DATA|OPF_FLAGS },
   { "neg",  OP_NEG,    1, 1, OPF_DATA|OPF_FLAGS },
@@ -1072,14 +1072,14 @@ static const struct {
   { "setnle", OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_LE, 1 },
   // x87
   { "fld",    OP_FLD,    1, 1, OPF_FPUSH },
-  { "fild",   OP_FILD,   1, 1, OPF_FPUSH },
+  { "fild",   OP_FILD,   1, 1, OPF_FPUSH|OPF_FINT },
   { "fld1",   OP_FLDc,   0, 0, OPF_FPUSH },
   { "fldln2", OP_FLDc,   0, 0, OPF_FPUSH },
   { "fldz",   OP_FLDc,   0, 0, OPF_FPUSH },
   { "fst",    OP_FST,    1, 1, 0 },
   { "fstp",   OP_FST,    1, 1, OPF_FPOP },
-  { "fist",   OP_FIST,   1, 1, 0 },
-  { "fistp",  OP_FIST,   1, 1, OPF_FPOP },
+  { "fist",   OP_FIST,   1, 1, OPF_FINT },
+  { "fistp",  OP_FIST,   1, 1, OPF_FPOP|OPF_FINT },
   { "fadd",   OP_FADD,   0, 2, 0 },
   { "faddp",  OP_FADD,   0, 2, OPF_FPOP },
   { "fdiv",   OP_FDIV,   0, 2, 0 },
@@ -1092,12 +1092,12 @@ static const struct {
   { "fdivrp", OP_FDIVR,  0, 2, OPF_FPOP },
   { "fsubr",  OP_FSUBR,  0, 2, 0 },
   { "fsubrp", OP_FSUBR,  0, 2, OPF_FPOP },
-  { "fiadd",  OP_FIADD,  1, 1, 0 },
-  { "fidiv",  OP_FIDIV,  1, 1, 0 },
-  { "fimul",  OP_FIMUL,  1, 1, 0 },
-  { "fisub",  OP_FISUB,  1, 1, 0 },
-  { "fidivr", OP_FIDIVR, 1, 1, 0 },
-  { "fisubr", OP_FISUBR, 1, 1, 0 },
+  { "fiadd",  OP_FIADD,  1, 1, OPF_FINT },
+  { "fidiv",  OP_FIDIV,  1, 1, OPF_FINT },
+  { "fimul",  OP_FIMUL,  1, 1, OPF_FINT },
+  { "fisub",  OP_FISUB,  1, 1, OPF_FINT },
+  { "fidivr", OP_FIDIVR, 1, 1, OPF_FINT },
+  { "fisubr", OP_FISUBR, 1, 1, OPF_FINT },
   { "fcom",   OP_FCOM,   0, 1, 0 },
   { "fcomp",  OP_FCOM,   0, 1, OPF_FPOP },
   { "fnstsw", OP_FNSTSW, 1, 1, OPF_DATA },
@@ -1117,6 +1117,7 @@ static const struct {
   { "_allshr",OPP_ALLSHR },
   { "_ftol",  OPP_FTOL },
   { "_CIpow", OPP_CIPOW },
+  { "abort",  OPP_ABORT },
   // must be last
   { "ud2",    OP_UD2 },
 };
@@ -1880,7 +1881,8 @@ static int stack_frame_access(struct parsed_op *po,
   int retval = -1;
   int sf_ofs;
 
-  if (po->flags & OPF_EBP_S)
+  if (g_bp_frame && (po->flags & OPF_EBP_S)
+      && !(po->regmask_src & mxSP))
     ferr(po, "stack_frame_access while ebp is scratch\n");
 
   parse_stack_access(po, name, ofs_reg, &offset,
@@ -1981,6 +1983,14 @@ static int stack_frame_access(struct parsed_op *po,
       }
       break;
 
+    case OPLM_QWORD:
+      ferr_assert(po, !(offset & 7));
+      if (cast[0])
+        prefix = cast;
+      snprintf(buf, buf_size, "%s%sa%d",
+        prefix, is_lea ? "&" : "", i + 1);
+      break;
+
     default:
       ferr(po, "bp_arg bad lmod: %d\n", popr->lmod);
     }
@@ -2054,10 +2064,9 @@ static int stack_frame_access(struct parsed_op *po,
       ferr_assert(po, !(sf_ofs & 7));
       ferr_assert(po, ofs_reg[0] == 0);
       // only used for x87 int64/float, float sets is_lea
-      if (is_lea)
-        snprintf(buf, buf_size, "%ssf.q[%d]", prefix, sf_ofs / 8);
-      else
-        snprintf(buf, buf_size, "*(s64 *)&sf.q[%d]", sf_ofs / 8);
+      if (!is_lea && (po->flags & OPF_FINT))
+        prefix = "*(s64 *)&";
+      snprintf(buf, buf_size, "%ssf.q[%d]", prefix, sf_ofs / 8);
       break;
 
     default:
@@ -2294,8 +2303,9 @@ static char *out_src_opr_u32(char *buf, size_t buf_size,
   return out_src_opr(buf, buf_size, po, popr, NULL, 0);
 }
 
-static char *out_src_opr_float(char *buf, size_t buf_size,
-  struct parsed_op *po, struct parsed_opr *popr, int need_float_stack)
+static char *out_opr_float(char *buf, size_t buf_size,
+  struct parsed_op *po, struct parsed_opr *popr, int is_src,
+  int need_float_stack)
 {
   const char *cast = NULL;
   char tmp[256];
@@ -2317,6 +2327,12 @@ static char *out_src_opr_float(char *buf, size_t buf_size,
     break;
 
   case OPT_REGMEM:
+    if (popr->lmod == OPLM_QWORD && is_stack_access(po, popr)) {
+      stack_frame_access(po, popr, buf, buf_size,
+        popr->name, "", is_src, 0);
+      break;
+    }
+    // fallthrough
   case OPT_LABEL:
   case OPT_OFFSET:
     switch (popr->lmod) {
@@ -2341,11 +2357,16 @@ static char *out_src_opr_float(char *buf, size_t buf_size,
   return buf;
 }
 
+static char *out_src_opr_float(char *buf, size_t buf_size,
+  struct parsed_op *po, struct parsed_opr *popr, int need_float_stack)
+{
+  return out_opr_float(buf, buf_size, po, popr, 1, need_float_stack);
+}
+
 static char *out_dst_opr_float(char *buf, size_t buf_size,
   struct parsed_op *po, struct parsed_opr *popr, int need_float_stack)
 {
-  // same?
-  return out_src_opr_float(buf, buf_size, po, popr, need_float_stack);
+  return out_opr_float(buf, buf_size, po, popr, 0, need_float_stack);
 }
 
 static void out_test_for_cc(char *buf, size_t buf_size,
@@ -2380,6 +2401,11 @@ static void out_test_for_cc(char *buf, size_t buf_size,
     snprintf(buf, buf_size, "(%d)", !!is_inv);
     break;
 
+  case PFO_P: // PF==1
+    snprintf(buf, buf_size, "(%sdo_parity(%s))",
+      is_inv ? "!" : "", expr);
+    break;
+
   default:
     ferr(po, "%s: unhandled parsed_flag_op: %d\n", __func__, pfo);
   }
@@ -2694,8 +2720,9 @@ static int scan_for_rsave_pop_reg(int i, int magic, int reg, int set_flags)
       return -1;
   }
 
-  // nothing interesting on this path
-  return 0;
+  // nothing interesting on this path,
+  // still return ret for something recursive calls could find
+  return ret;
 }
 
 static void find_reachable_exits(int i, int opcnt, int magic,
@@ -2746,7 +2773,8 @@ static int scan_for_pop_ret(int i, int opcnt, int reg, int set_flags)
 {
   static int exits[MAX_EXITS];
   static int exit_count;
-  int j, ret;
+  int found = 0;
+  int e, j, ret;
 
   if (!set_flags) {
     exit_count = 0;
@@ -2756,13 +2784,23 @@ static int scan_for_pop_ret(int i, int opcnt, int reg, int set_flags)
   }
 
   for (j = 0; j < exit_count; j++) {
-    ret = scan_for_rsave_pop_reg(exits[j], i + opcnt * 16 + set_flags,
+    e = exits[j];
+    ret = scan_for_rsave_pop_reg(e, i + opcnt * 16 + set_flags,
             reg, set_flags);
-    if (ret == -1)
-      return -1;
+    if (ret != -1) {
+      found |= ret;
+      continue;
+    }
+    if (ops[e].op == OP_CALL && ops[e].pp != NULL
+      && ops[e].pp->is_noreturn)
+    {
+      // assume stack cleanup was skipped
+      continue;
+    }
+    return -1;
   }
 
-  return 1;
+  return found;
 }
 
 // scan for one or more pop of push <const>
@@ -3526,7 +3564,7 @@ static void scan_for_call_type(int i, const struct parsed_opr *opr,
   if (*pp_found != NULL && pp != NULL && *pp_found != pp) {
     if (!IS((*pp_found)->ret_type.name, pp->ret_type.name)
       || (*pp_found)->is_stdcall != pp->is_stdcall
-      || (*pp_found)->is_fptr != pp->is_fptr
+      //|| (*pp_found)->is_fptr != pp->is_fptr
       || (*pp_found)->argc != pp->argc
       || (*pp_found)->argc_reg != pp->argc_reg
       || (*pp_found)->argc_stack != pp->argc_stack)
@@ -3677,6 +3715,8 @@ static void resolve_branches_parse_calls(int opcnt)
     { "__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 },
+    // more precise? Wine gets away with just __ftol handler
+    { "__ftol2",  OPP_FTOL,   OPF_FPOP, mxST0, mxAX | mxDX },
     { "__CIpow",  OPP_CIPOW,  OPF_FPOP, mxST0|mxST1, mxST0 },
   };
   const struct parsed_proto *pp_c;
@@ -3684,6 +3724,7 @@ static void resolve_branches_parse_calls(int opcnt)
   struct parsed_data *pd;
   struct parsed_op *po;
   const char *tmpname;
+  enum op_op prev_op;
   int i, l;
   int ret;
 
@@ -3747,8 +3788,10 @@ static void resolve_branches_parse_calls(int opcnt)
       if (pp != NULL) {
         if (pp->is_fptr)
           check_func_pp(po, pp, "fptr var call");
-        if (pp->is_noreturn)
+        if (pp->is_noreturn) {
           po->flags |= OPF_TAIL;
+          po->flags &= ~OPF_ATAIL; // most likely...
+        }
       }
       po->pp = pp;
       continue;
@@ -3793,13 +3836,19 @@ static void resolve_branches_parse_calls(int opcnt)
 tailcall:
     po->op = OP_CALL;
     po->flags |= OPF_TAIL;
-    if (i > 0 && ops[i - 1].op == OP_POP)
+    prev_op = i > 0 ? ops[i - 1].op : OP_UD2;
+    if (prev_op == OP_POP)
+      po->flags |= OPF_ATAIL;
+    if (g_stack_fsz + g_bp_frame == 0 && prev_op != OP_PUSH
+      && (g_func_pp == NULL || g_func_pp->argc_stack > 0))
+    {
       po->flags |= OPF_ATAIL;
+    }
     i--; // reprocess
   }
 }
 
-static void scan_prologue_epilogue(int opcnt)
+static void scan_prologue_epilogue(int opcnt, int *stack_align)
 {
   int ecx_push = 0, esp_sub = 0, pusha = 0;
   int sandard_epilogue;
@@ -3822,6 +3871,19 @@ static void scan_prologue_epilogue(int opcnt)
       i++;
     }
 
+    if (ops[i].op == OP_AND && ops[i].operand[0].reg == xSP
+        && ops[i].operand[1].type == OPT_CONST)
+    {
+      l = ops[i].operand[1].val;
+      j = ffs(l) - 1;
+      if (j == -1 || (l >> j) != -1)
+        ferr(&ops[i], "unhandled esp align: %x\n", l);
+      if (stack_align != NULL)
+        *stack_align = 1 << j;
+      ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+      i++;
+    }
+
     if (ops[i].op == OP_SUB && IS(opr_name(&ops[i], 0), "esp")) {
       g_stack_fsz = opr_const(&ops[i], 1);
       ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
@@ -4022,13 +4084,22 @@ static void scan_prologue_epilogue(int opcnt)
             l += ops[j].operand[1].val / 4 - 1;
           }
           else
-            ferr(&ops[j], "'pop ecx' expected\n");
+            break;
 
           ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
           j--;
         }
-        if (l != ecx_push)
+        if (l != ecx_push) {
+          if (i < opcnt && ops[i].op == OP_CALL
+            && ops[i].pp != NULL && ops[i].pp->is_noreturn)
+          {
+            // noreturn tailcall with no epilogue
+            i++;
+            found = 1;
+            continue;
+          }
           ferr(&ops[j], "epilogue scan failed\n");
+        }
 
         found = 1;
       }
@@ -4036,21 +4107,25 @@ static void scan_prologue_epilogue(int opcnt)
       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)
+            || ops[j].operand[1].type != OPT_CONST)
         {
-          if (ops[i].op == OP_CALL && ops[i].pp != NULL
-            && ops[i].pp->is_noreturn)
+          if (i < opcnt && ops[i].op == OP_CALL
+            && ops[i].pp != NULL && ops[i].pp->is_noreturn)
           {
             // noreturn tailcall with no epilogue
             i++;
+            found = 1;
             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
+        if (ops[j].operand[1].val < g_stack_fsz)
+          ferr(&ops[j], "esp adj is too low (need %d)\n", g_stack_fsz);
+
+        ops[j].operand[1].val -= g_stack_fsz; // for stack arg scanner
+        if (ops[j].operand[1].val == 0)
+          ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
         found = 1;
       }
 
@@ -4642,6 +4717,9 @@ static struct parsed_proto *process_call(int i, int opcnt)
         ferr(po, "too many args for '%s'\n", tmpname);
     }
     if (pp->argc_stack > adj / 4) {
+      if (pp->is_noreturn)
+        // assume no stack adjust was emited
+        goto out;
       fnote(po, "(this call)\n");
       ferr(&ops[ret], "stack tracking failed for '%s': %x %x\n",
         tmpname, pp->argc_stack * 4, adj);
@@ -4654,6 +4732,7 @@ static struct parsed_proto *process_call(int i, int opcnt)
     ferr(po, "missing esp_adjust for vararg func '%s'\n",
       pp->name);
 
+out:
   return pp;
 }
 
@@ -4684,8 +4763,10 @@ static int collect_call_args_no_push(int i, struct parsed_proto *pp,
       ret = parse_stack_esp_offset(po, po->operand[0].name, &offset);
       if (ret != 0)
         continue;
-      if (offset < 0 || offset >= pp->argc_stack * 4 || (offset & 3))
-        ferr(po, "bad offset %d (%d args)\n", offset, pp->argc_stack);
+      if (offset < 0 || offset >= pp->argc_stack * 4 || (offset & 3)) {
+        //ferr(po, "offset %d, %d args\n", offset, pp->argc_stack);
+        continue;
+      }
 
       arg = base_arg + offset / 4;
       po->p_argnext = -1;
@@ -4904,8 +4985,8 @@ static int collect_call_args_r(struct parsed_op *po, int i,
 
       pp_tmp = ops[j].pp;
       if (pp_tmp == NULL)
-        ferr(po, "arg collect hit unparsed call '%s'\n",
-          ops[j].operand[0].name);
+        ferr(po, "arg collect %d/%d hit unparsed call '%s'\n",
+          arg, pp->argc, ops[j].operand[0].name);
       if (may_reuse && pp_tmp->argc_stack > 0)
         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);
@@ -5317,7 +5398,7 @@ static void pp_insert_reg_arg(struct parsed_proto *pp, const char *reg)
   pp->argc_reg++;
 }
 
-static void output_std_flags(FILE *fout, struct parsed_op *po,
+static void output_std_flag_z(FILE *fout, struct parsed_op *po,
   int *pfomask, const char *dst_opr_text)
 {
   if (*pfomask & (1 << PFO_Z)) {
@@ -5325,6 +5406,11 @@ static void output_std_flags(FILE *fout, struct parsed_op *po,
       lmod_cast_u(po, po->operand[0].lmod), dst_opr_text);
     *pfomask &= ~(1 << PFO_Z);
   }
+}
+
+static void output_std_flag_s(FILE *fout, struct parsed_op *po,
+  int *pfomask, const char *dst_opr_text)
+{
   if (*pfomask & (1 << PFO_S)) {
     fprintf(fout, "\n  cond_s = (%s%s < 0);",
       lmod_cast_s(po, po->operand[0].lmod), dst_opr_text);
@@ -5332,6 +5418,13 @@ static void output_std_flags(FILE *fout, struct parsed_op *po,
   }
 }
 
+static void output_std_flags(FILE *fout, struct parsed_op *po,
+  int *pfomask, const char *dst_opr_text)
+{
+  output_std_flag_z(fout, po, pfomask, dst_opr_text);
+  output_std_flag_s(fout, po, pfomask, dst_opr_text);
+}
+
 enum {
   OPP_FORCE_NORETURN = (1 << 0),
   OPP_SIMPLE_ARGS    = (1 << 1),
@@ -5388,6 +5481,9 @@ static void output_pp(FILE *fout, const struct parsed_proto *pp,
       if (!pp->is_fptr)
         fprintf(fout, " a%d", i + 1);
     }
+
+    if (pp->arg[i].type.is_64bit)
+      i++;
   }
   if (pp->is_vararg) {
     if (i > 0)
@@ -5431,6 +5527,8 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   int had_decl = 0;
   int label_pending = 0;
   int need_double = 0;
+  int stack_align = 0;
+  int stack_fsz_adj = 0;
   int regmask_save = 0; // used regs saved/restored in this func
   int regmask_arg;      // regs from this function args (fastcall, etc)
   int regmask_ret;      // regs needed on ret
@@ -5467,7 +5565,21 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 
   // pass2:
   // - handle ebp/esp frame, remove ops related to it
-  scan_prologue_epilogue(opcnt);
+  scan_prologue_epilogue(opcnt, &stack_align);
+
+  // handle a case where sf size is unalignment, but is
+  // placed in a way that elements are still aligned
+  if (g_stack_fsz & 4) {
+    for (i = 0; i < g_eqcnt; i++) {
+      if (g_eqs[i].lmod != OPLM_QWORD)
+        continue;
+      if (!(g_eqs[i].offset & 4)) {
+        g_stack_fsz += 4;
+        stack_fsz_adj = 4;
+      }
+      break;
+    }
+  }
 
   // pass3:
   // - remove dead labels
@@ -5910,6 +6022,9 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 
   // declare stack frame, va_arg
   if (g_stack_fsz) {
+    if (stack_fsz_adj)
+      fprintf(fout, "  // stack_fsz_adj %d\n", stack_fsz_adj);
+
     fprintf(fout, "  union { u32 d[%d];", (g_stack_fsz + 3) / 4);
     if (g_func_lmods & (1 << OPLM_WORD))
       fprintf(fout, " u16 w[%d];", (g_stack_fsz + 1) / 2);
@@ -5917,6 +6032,11 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       fprintf(fout, " u8 b[%d];", g_stack_fsz);
     if (g_func_lmods & (1 << OPLM_QWORD))
       fprintf(fout, " double q[%d];", (g_stack_fsz + 7) / 8);
+
+    if (stack_align > 8)
+      ferr(ops, "unhandled stack align of %d\n", stack_align);
+    else if (stack_align == 8)
+      fprintf(fout, " u64 align;");
     fprintf(fout, " } sf;\n");
     had_decl = 1;
   }
@@ -6657,15 +6777,20 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         break;
 
       case OP_BSF:
+      case OP_BSR:
+        // on SKL, if src is 0, dst is left unchanged
         assert_operand_cnt(2);
+        out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
         out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
-        fprintf(fout, "  %s = %s ? __builtin_ffs(%s) - 1 : 0;",
-          out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
-          buf2, buf2);
-        output_std_flags(fout, po, &pfomask, buf1);
+        output_std_flag_z(fout, po, &pfomask, buf2);
+        if (po->op == OP_BSF)
+          snprintf(buf3, sizeof(buf3), "__builtin_ffs(%s) - 1", buf2);
+        else
+          snprintf(buf3, sizeof(buf3), "31 - __builtin_clz(%s)", buf2);
+        fprintf(fout, "  if (%s) %s = %s;", buf2, buf1, buf3);
         last_arith_dst = &po->operand[0];
         delayed_flag_op = NULL;
-        strcat(g_comment, " bsf");
+        strcat(g_comment, po->op == OP_BSF ? " bsf" : " bsr");
         break;
 
       case OP_DEC:
@@ -6920,10 +7045,13 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
           pp->has_structarg ? "_sa" : "");
 
         if (po->flags & OPF_ATAIL) {
-          if (pp->argc_stack != g_func_pp->argc_stack
-            || (pp->argc_stack > 0
-                && pp->is_stdcall != g_func_pp->is_stdcall))
-            ferr(po, "incompatible tailcall\n");
+          int check_compat =
+            g_func_pp->is_stdcall && g_func_pp->argc_stack > 0;
+          check_compat |= pp->argc_stack > 0;
+          if (check_compat
+           && (pp->argc_stack != g_func_pp->argc_stack
+               || pp->is_stdcall != g_func_pp->is_stdcall))
+            ferr(po, "incompatible arg-reuse tailcall\n");
           if (g_func_pp->has_retreg)
             ferr(po, "TODO: retreg+tailcall\n");
 
@@ -7355,7 +7483,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         z_check = ((long)po->datap >> 16) & 1;
         out_src_opr_float(buf1, sizeof(buf1), po, &po->operand[0],
           need_float_stack);
-        if (mask == 0x0100) { // C0 -> <
+        if (mask == 0x0100 || mask == 0x0500) { // C0 -> <
           fprintf(fout, "  f_sw = %s < %s ? 0x0100 : 0;",
             float_st0, buf1);
         }
@@ -7528,7 +7656,8 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         last_arith_dst = NULL;
     }
 
-    label_pending = 0;
+    if (!no_output)
+      label_pending = 0;
   }
 
   if (g_stack_fsz && !g_stack_frame_used)
@@ -7850,7 +7979,7 @@ static void gen_hdr(const char *funcn, int opcnt)
 
   // pass2:
   // - handle ebp/esp frame, remove ops related to it
-  scan_prologue_epilogue(opcnt);
+  scan_prologue_epilogue(opcnt, NULL);
 
   // pass3:
   // - remove dead labels