translate: more float
[ia32rtools.git] / tools / translate.c
index eec360f..b57c812 100644 (file)
@@ -68,7 +68,9 @@ enum op_flags {
   OPF_NOREGS = (1 << 21), /* don't track regs of this op */
   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_FPOPP  = (1 << 24), /* pops x87 stack twice */
+  OPF_FSHIFT = (1 << 25), /* x87 stack shift is actually needed */
+  OPF_FINT   = (1 << 26), /* integer float op arg */
 };
 
 enum op_op {
@@ -113,6 +115,7 @@ enum op_op {
        OP_ADC,
        OP_SBB,
        OP_BSF,
+       OP_BSR,
        OP_INC,
        OP_DEC,
        OP_NEG,
@@ -203,7 +206,7 @@ struct parsed_opr {
   unsigned int type_from_var:1; // .. in header, sometimes wrong
   unsigned int size_mismatch:1; // type override differs from C
   unsigned int size_lt:1;  // type override is larger than C
-  unsigned int had_ds:1;   // had ds: prefix
+  unsigned int segment:7;  // had segment override (enum segment)
   const struct parsed_proto *pp; // for OPT_LABEL
   unsigned int val;
   char name[NAMELEN];
@@ -289,6 +292,15 @@ enum x87_const {
   X87_CONST_Z,
 };
 
+enum segment {
+  SEG_CS = 1,
+  SEG_DS,
+  SEG_SS,
+  SEG_ES,
+  SEG_FS,
+  SEG_GS,
+};
+
 // note: limited to 32k due to p_argnext
 #define MAX_OPS     4096
 #define MAX_ARG_GRP 2
@@ -308,6 +320,8 @@ static int g_bp_frame;
 static int g_sp_frame;
 static int g_stack_frame_used;
 static int g_stack_fsz;
+static int g_seh_found;
+static int g_seh_size;
 static int g_ida_func_attr;
 static int g_sct_func_attr;
 static int g_stack_clear_start; // in dwords
@@ -356,6 +370,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)
@@ -407,12 +422,12 @@ static int check_segment_prefix(const char *s)
     return 0;
 
   switch (s[0]) {
-  case 'c': return 1;
-  case 'd': return 2;
-  case 's': return 3;
-  case 'e': return 4;
-  case 'f': return 5;
-  case 'g': return 6;
+  case 'c': return SEG_CS;
+  case 'd': return SEG_DS;
+  case 's': return SEG_SS;
+  case 'e': return SEG_ES;
+  case 'f': return SEG_FS;
+  case 'g': return SEG_GS;
   default:  return 0;
   }
 }
@@ -780,9 +795,7 @@ static int parse_operand(struct parsed_opr *opr,
       opr->type = OPT_LABEL;
       ret = check_segment_prefix(label);
       if (ret != 0) {
-        if (ret >= 5)
-          aerr("fs/gs used\n");
-        opr->had_ds = 1;
+        opr->segment = ret;
         label += 3;
       }
       strcpy(opr->name, label);
@@ -831,10 +844,10 @@ static int parse_operand(struct parsed_opr *opr,
 
   ret = check_segment_prefix(words[w]);
   if (ret != 0) {
-    if (ret >= 5)
-      aerr("fs/gs used\n");
-    opr->had_ds = 1;
+    opr->segment = ret;
     memmove(words[w], words[w] + 3, strlen(words[w]) - 2);
+    if (ret == SEG_FS && IS(words[w], "0"))
+      g_seh_found = 1;
   }
   strcpy(opr->name, words[w]);
 
@@ -994,6 +1007,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 },
@@ -1068,14 +1082,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 },
@@ -1088,14 +1102,18 @@ 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 },
+  { "fcompp", OP_FCOM,   0, 0, OPF_FPOPP },
+  { "fucom",  OP_FCOM,   0, 1, 0 },
+  { "fucomp", OP_FCOM,   0, 1, OPF_FPOP },
+  { "fucompp",OP_FCOM,   0, 0, OPF_FPOPP },
   { "fnstsw", OP_FNSTSW, 1, 1, OPF_DATA },
   { "fchs",   OP_FCHS,   0, 0, 0 },
   { "fcos",   OP_FCOS,   0, 0, 0 },
@@ -1113,6 +1131,7 @@ static const struct {
   { "_allshr",OPP_ALLSHR },
   { "_ftol",  OPP_FTOL },
   { "_CIpow", OPP_CIPOW },
+  { "abort",  OPP_ABORT },
   // must be last
   { "ud2",    OP_UD2 },
 };
@@ -1460,6 +1479,13 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc)
 
   case OP_FCOM:
     op->regmask_src |= mxST0;
+    if (op->operand_cnt == 0) {
+      op->operand_cnt = 1;
+      op->operand[0].type = OPT_REG;
+      op->operand[0].lmod = OPLM_QWORD;
+      op->operand[0].reg = xST1;
+      op->regmask_src |= mxST1;
+    }
     break;
 
   default:
@@ -1876,7 +1902,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,
@@ -1977,6 +2004,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);
     }
@@ -2050,10 +2085,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:
@@ -2109,6 +2143,14 @@ static const char *check_label_read_ref(struct parsed_op *po,
   return pp->name;
 }
 
+static void check_opr(struct parsed_op *po, struct parsed_opr *popr)
+{
+  if (popr->segment == SEG_FS)
+    ferr(po, "fs: used\n");
+  if (popr->segment == SEG_GS)
+    ferr(po, "gs: used\n");
+}
+
 static char *out_src_opr(char *buf, size_t buf_size,
   struct parsed_op *po, struct parsed_opr *popr, const char *cast,
   int is_lea)
@@ -2119,6 +2161,8 @@ static char *out_src_opr(char *buf, size_t buf_size,
   char *p;
   int ret;
 
+  check_opr(po, popr);
+
   if (cast == NULL)
     cast = "";
 
@@ -2233,6 +2277,8 @@ static char *out_src_opr(char *buf, size_t buf_size,
 static char *out_dst_opr(char *buf, size_t buf_size,
        struct parsed_op *po, struct parsed_opr *popr)
 {
+  check_opr(po, popr);
+
   switch (popr->type) {
   case OPT_REG:
     switch (popr->lmod) {
@@ -2290,8 +2336,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];
@@ -2313,6 +2360,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) {
@@ -2337,11 +2390,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,
@@ -2376,6 +2434,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);
   }
@@ -3534,7 +3597,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)
@@ -3685,6 +3748,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;
@@ -3726,6 +3791,13 @@ static void resolve_branches_parse_calls(int opcnt)
           ferr(po, "call to loc_*\n");
         if (IS(tmpname, "__alloca_probe"))
           continue;
+        if (IS(tmpname, "__SEH_prolog")) {
+          ferr_assert(po, g_seh_found == 0);
+          g_seh_found = 2;
+          continue;
+        }
+        if (IS(tmpname, "__SEH_epilog"))
+          continue;
 
         // convert some calls to pseudo-ops
         for (l = 0; l < ARRAY_SIZE(pseudo_ops); l++) {
@@ -3816,13 +3888,186 @@ tailcall:
   }
 }
 
-static void scan_prologue_epilogue(int opcnt)
+static int resolve_origin(int i, const struct parsed_opr *opr,
+  int magic, int *op_i, int *is_caller);
+
+static void eliminate_seh_writes(int opcnt)
+{
+  const struct parsed_opr *opr;
+  char ofs_reg[16];
+  int offset;
+  int i;
+
+  // assume all sf writes above g_seh_size to be seh related
+  // (probably unsafe but oh well)
+  for (i = 0; i < opcnt; i++) {
+    if (ops[i].op != OP_MOV)
+      continue;
+    opr = &ops[i].operand[0];
+    if (opr->type != OPT_REGMEM)
+      continue;
+    if (!is_stack_access(&ops[i], opr))
+      continue;
+
+    offset = 0;
+    parse_stack_access(&ops[i], opr->name, ofs_reg, &offset,
+      NULL, NULL, 0);
+    if (offset < 0 && offset >= -g_seh_size)
+      ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+  }
+}
+
+static void eliminate_seh(int opcnt)
+{
+  int i, j, k, ret;
+
+  for (i = 0; i < opcnt; i++) {
+    if (ops[i].op != OP_MOV)
+      continue;
+    if (ops[i].operand[0].segment != SEG_FS)
+      continue;
+    if (!IS(opr_name(&ops[i], 0), "0"))
+      continue;
+
+    ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+    if (ops[i].operand[1].reg == xSP) {
+      for (j = i - 1; j >= 0; j--) {
+        if (ops[j].op != OP_PUSH)
+          continue;
+        ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+        g_seh_size += 4;
+        if (ops[j].operand[0].val == ~0)
+          break;
+        if (ops[j].operand[0].type == OPT_REG) {
+          k = -1;
+          ret = resolve_origin(j, &ops[j].operand[0],
+                  j + opcnt * 22, &k, NULL);
+          if (ret == 1)
+            ops[k].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+        }
+      }
+      if (j < 0)
+        ferr(ops, "missing seh terminator\n");
+    }
+    else {
+      k = -1;
+      ret = resolve_origin(i, &ops[i].operand[1],
+              i + opcnt * 23, &k, NULL);
+      if (ret == 1)
+        ops[k].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+    }
+  }
+
+  eliminate_seh_writes(opcnt);
+}
+
+static void eliminate_seh_calls(int opcnt)
+{
+  int epilog_found = 0;
+  int i;
+
+  g_bp_frame = 1;
+  g_seh_size = 0x10;
+
+  i = 0;
+  ferr_assert(&ops[i], ops[i].op == OP_PUSH
+               && ops[i].operand[0].type == OPT_CONST);
+  g_stack_fsz = g_seh_size + ops[i].operand[0].val;
+  ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+
+  i++;
+  ferr_assert(&ops[i], ops[i].op == OP_PUSH
+               && ops[i].operand[0].type == OPT_OFFSET);
+  ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+
+  i++;
+  ferr_assert(&ops[i], ops[i].op == OP_CALL
+               && IS(opr_name(&ops[i], 0), "__SEH_prolog"));
+  ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+
+  for (i++; i < opcnt; i++) {
+    if (ops[i].op != OP_CALL)
+      continue;
+    if (!IS(opr_name(&ops[i], 0), "__SEH_epilog"))
+      continue;
+
+    ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+    epilog_found = 1;
+  }
+  ferr_assert(ops, epilog_found);
+
+  eliminate_seh_writes(opcnt);
+}
+
+static int scan_prologue(int i, int opcnt, int *ecx_push, int *esp_sub)
+{
+  int j;
+
+  for (; i < opcnt; i++)
+    if (!(ops[i].flags & OPF_DONE))
+      break;
+
+  while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
+    ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+    g_stack_fsz += 4;
+    (*ecx_push)++;
+    i++;
+  }
+
+  for (; i < opcnt; i++) {
+    if (i > 0 && g_labels[i] != NULL)
+      break;
+    if (ops[i].op == OP_PUSH || (ops[i].flags & (OPF_JMP|OPF_TAIL)))
+      break;
+    if (ops[i].op == OP_SUB && ops[i].operand[0].reg == xSP
+      && ops[i].operand[1].type == OPT_CONST)
+    {
+      g_stack_fsz += opr_const(&ops[i], 1);
+      ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+      i++;
+      *esp_sub = 1;
+      break;
+    }
+    if (ops[i].op == OP_MOV && ops[i].operand[0].reg == xAX
+        && ops[i].operand[1].type == OPT_CONST)
+    {
+      for (j = i + 1; j < opcnt; j++)
+        if (!(ops[j].flags & OPF_DONE))
+          break;
+      if (ops[j].op == OP_CALL
+        && IS(opr_name(&ops[j], 0), "__alloca_probe"))
+      {
+        g_stack_fsz += opr_const(&ops[i], 1);
+        ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+        ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+        i = j + 1;
+        *esp_sub = 1;
+      }
+      break;
+    }
+  }
+
+  return i;
+}
+
+static void scan_prologue_epilogue(int opcnt, int *stack_align)
 {
   int ecx_push = 0, esp_sub = 0, pusha = 0;
   int sandard_epilogue;
   int found;
   int i, j, l;
 
+  if (g_seh_found == 2) {
+    eliminate_seh_calls(opcnt);
+    return;
+  }
+  if (g_seh_found) {
+    eliminate_seh(opcnt);
+    // ida treats seh as part of sf
+    g_stack_fsz = g_seh_size;
+    esp_sub = 1;
+  }
+
   if (ops[0].op == OP_PUSH && IS(opr_name(&ops[0], 0), "ebp")
       && ops[1].op == OP_MOV
       && IS(opr_name(&ops[1], 0), "ebp")
@@ -3831,7 +4076,10 @@ static void scan_prologue_epilogue(int opcnt)
     g_bp_frame = 1;
     ops[0].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
     ops[1].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
-    i = 2;
+
+    for (i = 2; i < opcnt; i++)
+      if (!(ops[i].flags & OPF_DONE))
+        break;
 
     if (ops[i].op == OP_PUSHA) {
       ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
@@ -3839,32 +4087,20 @@ static void scan_prologue_epilogue(int opcnt)
       i++;
     }
 
-    if (ops[i].op == OP_SUB && IS(opr_name(&ops[i], 0), "esp")) {
-      g_stack_fsz = opr_const(&ops[i], 1);
+    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++;
     }
-    else {
-      // another way msvc builds stack frame..
-      while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
-        g_stack_fsz += 4;
-        ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
-        ecx_push++;
-        i++;
-      }
-      // and another way..
-      if (i == 2 && ops[i].op == OP_MOV && ops[i].operand[0].reg == xAX
-          && ops[i].operand[1].type == OPT_CONST
-          && ops[i + 1].op == OP_CALL
-          && IS(opr_name(&ops[i + 1], 0), "__alloca_probe"))
-      {
-        g_stack_fsz += ops[i].operand[1].val;
-        ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
-        i++;
-        ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
-        i++;
-      }
-    }
+
+    i = scan_prologue(i, opcnt, &ecx_push, &esp_sub);
 
     found = 0;
     do {
@@ -3942,40 +4178,7 @@ static void scan_prologue_epilogue(int opcnt)
   }
 
   // non-bp frame
-  i = 0;
-  while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
-    ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
-    g_stack_fsz += 4;
-    ecx_push++;
-    i++;
-  }
-
-  for (; i < opcnt; i++) {
-    if (ops[i].op == OP_PUSH || (ops[i].flags & (OPF_JMP|OPF_TAIL)))
-      break;
-    if (ops[i].op == OP_SUB && ops[i].operand[0].reg == xSP
-      && ops[i].operand[1].type == OPT_CONST)
-    {
-      g_stack_fsz = ops[i].operand[1].val;
-      ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
-      i++;
-      esp_sub = 1;
-      break;
-    }
-    else if (ops[i].op == OP_MOV && ops[i].operand[0].reg == xAX
-          && ops[i].operand[1].type == OPT_CONST
-          && ops[i + 1].op == OP_CALL
-          && IS(opr_name(&ops[i + 1], 0), "__alloca_probe"))
-    {
-      g_stack_fsz += ops[i].operand[1].val;
-      ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
-      i++;
-      ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
-      i++;
-      esp_sub = 1;
-      break;
-    }
-  }
+  i = scan_prologue(0, opcnt, &ecx_push, &esp_sub);
 
   if (ecx_push && !esp_sub) {
     // could actually be args for a call..
@@ -3998,7 +4201,8 @@ static void scan_prologue_epilogue(int opcnt)
         ferr(&ops[i], "unhandled prologue\n");
 
       // recheck
-      i = g_stack_fsz = ecx_push = 0;
+      i = ecx_push = 0;
+      g_stack_fsz = g_seh_size;
       while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
         if (!(ops[i].flags & OPF_RMD))
           break;
@@ -4026,9 +4230,20 @@ static void scan_prologue_epilogue(int opcnt)
         i--;
         j--;
       }
+      else if (i < opcnt && (ops[i].flags & OPF_ATAIL)) {
+        // skip arg updates for arg-reuse tailcall
+        for (; j >= 0; j--) {
+          if (ops[j].op != OP_MOV)
+            break;
+          if (ops[j].operand[0].type != OPT_REGMEM)
+            break;
+          if (strstr(ops[j].operand[0].name, "arg_") == NULL)
+            break;
+        }
+      }
 
-      if (ecx_push > 0) {
-        for (l = 0; l < ecx_push; l++) {
+      if (ecx_push > 0 && !esp_sub) {
+        for (l = 0; l < ecx_push && j >= 0; l++) {
           if (ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ecx"))
             /* pop ecx */;
           else if (ops[j].op == OP_ADD
@@ -5306,7 +5521,15 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits,
     *regmask |= regmask_now;
 
     // released regs
-    if (po->flags & OPF_FPOP) {
+    if (po->flags & OPF_FPOPP) {
+      if ((regmask_now & mxSTa) == 0)
+        ferr(po, "float pop on empty stack?\n");
+      if (regmask_now & mxST7_2)
+        po->flags |= OPF_FSHIFT;
+      if (!(regmask_now & mxST7_2))
+        regmask_now &= ~mxST1_0;
+    }
+    else if (po->flags & OPF_FPOP) {
       if ((regmask_now & mxSTa) == 0)
         ferr(po, "float pop on empty stack?\n");
       if (regmask_now & (mxST7_2 | mxST1))
@@ -5353,7 +5576,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)) {
@@ -5361,6 +5584,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);
@@ -5368,6 +5596,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),
@@ -5424,6 +5659,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)
@@ -5467,6 +5705,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
@@ -5486,6 +5726,7 @@ 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;
+  g_seh_size = 0;
   if (g_sct_func_attr & SCTFA_CLEAR_REGS)
     regmask_init = g_regmask_init;
 
@@ -5503,7 +5744,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
@@ -5855,7 +6110,8 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       save_arg_vars[po->p_arggrp] |= 1 << (po->p_argnum - 1);
 
     // correct for "full stack" mode late enable
-    if ((po->flags & (OPF_PPUSH|OPF_FPOP)) && need_float_stack)
+    if ((po->flags & (OPF_PPUSH|OPF_FPOP|OPF_FPOPP))
+        && need_float_stack)
       po->flags |= OPF_FSHIFT;
   }
 
@@ -5865,6 +6121,9 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 
   // output starts here
 
+  if (g_seh_found)
+    fprintf(fout, "// had SEH\n");
+
   // define userstack size
   if (g_func_pp->is_userstack) {
     fprintf(fout, "#ifndef US_SZ_%s\n", g_func_pp->name);
@@ -5946,6 +6205,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);
@@ -5953,6 +6215,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;
   }
@@ -6693,15 +6960,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:
@@ -6915,7 +7187,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
           fprintf(fout, "%s%s = %s;\n", buf3, pp->name,
             out_src_opr(buf1, sizeof(buf1), po, &po->operand[0],
               "(void *)", 0));
-          if (pp->is_unresolved)
+          if (pp->is_unresolved || IS_START(pp->name, "i_guess"))
             fprintf(fout, "%sunresolved_call(\"%s:%d\", %s);\n",
               buf3, asmfn, po->asmln, pp->name);
         }
@@ -7394,11 +7666,11 @@ 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);
         }
-        else if (mask == 0x4000) { // C3 -> =
+        else if (mask == 0x4000 || mask == 0x4400) { // C3 -> =
           fprintf(fout, "  f_sw = %s == %s ? 0x4000 : 0;",
             float_st0, buf1);
         }
@@ -7417,10 +7689,16 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         else
           ferr(po, "unhandled sw mask: %x\n", mask);
         if (po->flags & OPF_FSHIFT) {
-          if (need_float_stack)
-            fprintf(fout, " f_stp++;");
-          else
+          if (need_float_stack) {
+            if (po->flags & OPF_FPOPP)
+              fprintf(fout, " f_stp += 2;");
+            else
+              fprintf(fout, " f_stp++;");
+          }
+          else {
+            ferr_assert(po, !(po->flags & OPF_FPOPP));
             fprintf(fout, " f_st0 = f_st1;");
+          }
         }
         break;
       }
@@ -7567,7 +7845,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)
@@ -7881,6 +8160,7 @@ static void gen_hdr(const char *funcn, int opcnt)
 
   g_bp_frame = g_sp_frame = g_stack_fsz = 0;
   g_stack_frame_used = 0;
+  g_seh_size = 0;
 
   // pass1:
   // - resolve all branches
@@ -7889,7 +8169,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
@@ -9059,6 +9339,7 @@ do_pending_endp:
       skip_warned = 0;
       g_skip_func = 0;
       g_func[0] = 0;
+      g_seh_found = 0;
       func_chunks_used = 0;
       func_chunk_i = -1;
       if (pi != 0) {
@@ -9110,7 +9391,7 @@ do_pending_endp:
         aerr("endp '%s' while skipping code\n", words[0]);
 
       if ((g_ida_func_attr & IDAFA_THUNK) && pi == 1
-        && ops[0].op == OP_JMP && ops[0].operand[0].had_ds)
+        && ops[0].op == OP_JMP && ops[0].operand[0].segment)
       {
         // import jump
         g_skip_func = 1;