translate: support more dereference types
[ia32rtools.git] / tools / translate.c
index 95e8802..591090d 100644 (file)
@@ -511,12 +511,12 @@ static int is_reg_in_str(const char *s)
 }
 
 static const char *parse_stack_el(const char *name, char *extra_reg,
-  int early_try)
+  int *base_val, int early_try)
 {
   const char *p, *p2, *s;
   char *endp = NULL;
   char buf[32];
-  long val;
+  long val = -1;
   int len;
 
   if (g_bp_frame || early_try)
@@ -589,6 +589,8 @@ static const char *parse_stack_el(const char *name, char *extra_reg,
   if ('0' <= *p && *p <= '9')
     return NULL;
 
+  if (base_val != NULL)
+    *base_val = val;
   return p;
 }
 
@@ -823,11 +825,12 @@ static int parse_operand(struct parsed_opr *opr,
       aerr("[] parse failure\n");
 
     parse_indmode(opr->name, regmask_indirect, 1);
-    if (opr->lmod == OPLM_UNSPEC && parse_stack_el(opr->name, NULL, 1))
+    if (opr->lmod == OPLM_UNSPEC
+      && parse_stack_el(opr->name, NULL, NULL, 1))
     {
       // might be an equ
       struct parsed_equ *eq =
-        equ_find(NULL, parse_stack_el(opr->name, NULL, 1), &i);
+        equ_find(NULL, parse_stack_el(opr->name, NULL, NULL, 1), &i);
       if (eq)
         opr->lmod = eq->lmod;
 
@@ -1747,7 +1750,7 @@ static struct parsed_equ *equ_find(struct parsed_op *po, const char *name,
 static int is_stack_access(struct parsed_op *po,
   const struct parsed_opr *popr)
 {
-  return (parse_stack_el(popr->name, NULL, 0)
+  return (parse_stack_el(popr->name, NULL, NULL, 0)
     || (g_bp_frame && !(po->flags & OPF_EBP_S)
         && IS_START(popr->name, "ebp")));
 }
@@ -1778,8 +1781,7 @@ static void parse_stack_access(struct parsed_op *po,
       ferr(po, "ebp- parse of '%s' failed\n", name);
   }
   else {
-    bp_arg = parse_stack_el(name, ofs_reg, 0);
-    snprintf(g_comment, sizeof(g_comment), "%s", bp_arg);
+    bp_arg = parse_stack_el(name, ofs_reg, NULL, 0);
     eq = equ_find(po, bp_arg, &offset);
     if (eq == NULL)
       ferr(po, "detected but missing eq\n");
@@ -1797,11 +1799,46 @@ static void parse_stack_access(struct parsed_op *po,
   }
 
   *offset_out = offset;
-  *stack_ra_out = stack_ra;
+  if (stack_ra_out)
+    *stack_ra_out = stack_ra;
   if (bp_arg_out)
     *bp_arg_out = bp_arg;
 }
 
+static int parse_stack_esp_offset(struct parsed_op *po,
+  const char *name, int *offset_out)
+{
+  char ofs_reg[16] = { 0, };
+  struct parsed_equ *eq;
+  const char *bp_arg;
+  char *endp = NULL;
+  int base_val = 0;
+  int offset = 0;
+
+  if (strstr(name, "esp") == NULL)
+    return -1;
+  bp_arg = parse_stack_el(name, ofs_reg, &base_val, 0);
+  if (bp_arg == NULL) {
+    // just plain offset?
+    if (!IS_START(name, "esp+"))
+      return -1;
+    offset = strtol(name + 4, &endp, 0);
+    if (endp == NULL || *endp != 0)
+      return -1;
+    *offset_out = offset;
+    return 0;
+  }
+
+  if (ofs_reg[0] != 0)
+    return -1;
+  eq = equ_find(po, bp_arg, &offset);
+  if (eq == NULL)
+    ferr(po, "detected but missing eq\n");
+  offset += eq->offset;
+  *offset_out = base_val + offset;
+  return 0;
+}
+
 static int stack_frame_access(struct parsed_op *po,
   struct parsed_opr *popr, char *buf, size_t buf_size,
   const char *name, const char *cast, int is_src, int is_lea)
@@ -1824,6 +1861,8 @@ static int stack_frame_access(struct parsed_op *po,
   parse_stack_access(po, name, ofs_reg, &offset,
     &stack_ra, &bp_arg, is_lea);
 
+  snprintf(g_comment, sizeof(g_comment), "%s", bp_arg);
+
   if (offset > stack_ra)
   {
     arg_i = (offset - stack_ra - 4) / 4;
@@ -1922,7 +1961,7 @@ static int stack_frame_access(struct parsed_op *po,
     }
 
     if (unaligned)
-      snprintf(g_comment, sizeof(g_comment), "%s unaligned", bp_arg);
+      strcat(g_comment, " unaligned");
 
     // common problem
     guess_lmod_from_c_type(&tmp_lmod, &g_func_pp->arg[i].type);
@@ -2500,7 +2539,7 @@ static const char *op_to_c(struct parsed_op *po)
 // note: this skips over calls and rm'd stuff assuming they're handled
 // so it's intended to use at one of final passes
 static int scan_for_pop(int i, int opcnt, int magic, int reg,
-  int depth, int flags_set)
+  int depth, int seen_noreturn, int flags_set)
 {
   struct parsed_op *po;
   int relevant;
@@ -2516,10 +2555,12 @@ static int scan_for_pop(int i, int opcnt, int magic, int reg,
     if (po->flags & OPF_TAIL) {
       if (po->op == OP_CALL) {
         if (po->pp != NULL && po->pp->is_noreturn)
-          // assume no stack cleanup for noreturn
-          return 1;
+          seen_noreturn = 1;
+        else
+          return -1;
       }
-      return -1; // deadend
+      else
+        return -1; // deadend
     }
 
     if (po->flags & (OPF_RMD|OPF_DONE|OPF_FARG))
@@ -2531,7 +2572,7 @@ static int scan_for_pop(int i, int opcnt, int magic, int reg,
         for (j = 0; j < po->btj->count; j++) {
           check_i(po, po->btj->d[j].bt_i);
           ret |= scan_for_pop(po->btj->d[j].bt_i, opcnt, magic, reg,
-                   depth, flags_set);
+                   depth, seen_noreturn, flags_set);
           if (ret < 0)
             return ret; // dead end
         }
@@ -2541,7 +2582,7 @@ static int scan_for_pop(int i, int opcnt, int magic, int reg,
       check_i(po, po->bt_i);
       if (po->flags & OPF_CJMP) {
         ret |= scan_for_pop(po->bt_i, opcnt, magic, reg,
-                 depth, flags_set);
+                 depth, seen_noreturn, flags_set);
         if (ret < 0)
           return ret; // dead end
       }
@@ -2570,7 +2611,8 @@ static int scan_for_pop(int i, int opcnt, int magic, int reg,
     }
   }
 
-  return -1;
+  // for noreturn, assume msvc skipped stack cleanup
+  return seen_noreturn ? 1 : -1;
 }
 
 // scan for 'reg' pop backwards starting from i
@@ -3057,8 +3099,11 @@ static int scan_for_mod_opr0(struct parsed_op *po_test,
   return -1;
 }
 
-static int scan_for_flag_set(int i, int magic, int *branched,
-  int *setters, int *setter_cnt)
+static int try_resolve_const(int i, const struct parsed_opr *opr,
+  int magic, unsigned int *val);
+
+static int scan_for_flag_set(int i, int opcnt, int magic,
+  int *branched, int *setters, int *setter_cnt)
 {
   struct label_ref *lr;
   int ret;
@@ -3077,7 +3122,7 @@ static int scan_for_flag_set(int i, int magic, int *branched,
       lr = &g_label_refs[i];
       for (; lr->next; lr = lr->next) {
         check_i(&ops[i], lr->i);
-        ret = scan_for_flag_set(lr->i, magic,
+        ret = scan_for_flag_set(lr->i, opcnt, magic,
                 branched, setters, setter_cnt);
         if (ret < 0)
           return ret;
@@ -3088,7 +3133,7 @@ static int scan_for_flag_set(int i, int magic, int *branched,
         i = lr->i;
         continue;
       }
-      ret = scan_for_flag_set(lr->i, magic,
+      ret = scan_for_flag_set(lr->i, opcnt, magic,
               branched, setters, setter_cnt);
       if (ret < 0)
         return ret;
@@ -3098,6 +3143,20 @@ static int scan_for_flag_set(int i, int magic, int *branched,
     if (ops[i].flags & OPF_FLAGS) {
       setters[*setter_cnt] = i;
       (*setter_cnt)++;
+
+      if (ops[i].flags & OPF_REP) {
+        struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xCX);
+        unsigned int uval;
+
+        ret = try_resolve_const(i, &opr, i + opcnt * 7, &uval);
+        if (ret != 1 || uval == 0) {
+          // can't treat it as full setter because of ecx=0 case,
+          // also disallow delayed compare
+          *branched = 1;
+          continue;
+        }
+      }
+
       return 0;
     }
 
@@ -3297,7 +3356,8 @@ static void scan_fwd_set_flags(int i, int opcnt, int magic, int flags)
 }
 
 static const struct parsed_proto *try_recover_pp(
-  struct parsed_op *po, const struct parsed_opr *opr, int *search_instead)
+  struct parsed_op *po, const struct parsed_opr *opr,
+  int is_call, int *search_instead)
 {
   const struct parsed_proto *pp = NULL;
   char buf[256];
@@ -3336,10 +3396,12 @@ static const struct parsed_proto *try_recover_pp(
     if (arg == g_func_pp->argc)
       ferr(po, "stack arg %d not in prototype?\n", arg_i);
 
-    pp = g_func_pp->arg[arg].fptr;
-    if (pp == NULL)
-      ferr(po, "icall sa: arg%d is not a fptr?\n", arg + 1);
-    check_func_pp(po, pp, "icall arg");
+    pp = g_func_pp->arg[arg].pp;
+    if (is_call) {
+      if (pp == NULL)
+        ferr(po, "icall arg: arg%d has no pp\n", arg + 1);
+      check_func_pp(po, pp, "icall arg");
+    }
   }
   else if (opr->type == OPT_REGMEM && strchr(opr->name + 1, '[')) {
     // label[index]
@@ -3420,14 +3482,14 @@ static void scan_for_call_type(int i, const struct parsed_opr *opr,
     }
     if (i == g_func_pp->argc)
       return;
-    pp = g_func_pp->arg[i].fptr;
+    pp = g_func_pp->arg[i].pp;
     if (pp == NULL)
       ferr(po, "icall: arg%d (%s) is not a fptr?\n",
         i + 1, g_func_pp->arg[i].reg);
     check_func_pp(po, pp, "icall reg-arg");
   }
   else
-    pp = try_recover_pp(po, opr, NULL);
+    pp = try_recover_pp(po, opr, 1, NULL);
 
   if (*pp_found != NULL && pp != NULL && *pp_found != pp) {
     if (!IS((*pp_found)->ret_type.name, pp->ret_type.name)
@@ -3564,6 +3626,13 @@ static int get_pp_arg_regmask_dst(const struct parsed_proto *pp)
   return regmask | mxAX;
 }
 
+static int are_ops_same(struct parsed_op *po1, struct parsed_op *po2)
+{
+  return po1->op == po2->op && po1->operand_cnt == po2->operand_cnt
+    && memcmp(po1->operand, po2->operand,
+              sizeof(po1->operand[0]) * po1->operand_cnt) == 0;
+}
+
 static void resolve_branches_parse_calls(int opcnt)
 {
   static const struct {
@@ -3971,10 +4040,6 @@ static int resolve_origin(int i, const struct parsed_opr *opr,
   struct label_ref *lr;
   int ret = 0;
 
-  if (ops[i].cc_scratch == magic)
-    return 0;
-  ops[i].cc_scratch = magic;
-
   while (1) {
     if (g_labels[i] != NULL) {
       lr = &g_label_refs[i];
@@ -4003,10 +4068,9 @@ static int resolve_origin(int i, const struct parsed_opr *opr,
       continue;
 
     if (*op_i >= 0) {
-      if (*op_i == i)
+      if (*op_i == i || are_ops_same(&ops[*op_i], &ops[i]))
         return ret | 1;
 
-      // XXX: could check if the other op does the same
       return -1;
     }
 
@@ -4025,10 +4089,6 @@ static int resolve_last_ref(int i, const struct parsed_opr *opr,
   struct label_ref *lr;
   int ret = 0;
 
-  if (ops[i].cc_scratch == magic)
-    return 0;
-  ops[i].cc_scratch = magic;
-
   while (1) {
     if (g_labels[i] != NULL) {
       lr = &g_label_refs[i];
@@ -4282,16 +4342,103 @@ static int resolve_used_bits(int i, int opcnt, int reg,
   return 0;
 }
 
+static const struct parsed_proto *resolve_deref(int i, int magic,
+  struct parsed_opr *opr, int level)
+{
+  struct parsed_opr opr_s = OPR_INIT(OPT_REG, OPLM_DWORD, 0);
+  const struct parsed_proto *pp = NULL;
+  int from_caller = 0;
+  char s_reg[4];
+  int offset = 0;
+  int len = 0;
+  int j = -1;
+  int k = -1;
+  int reg;
+  int ret;
+
+  ret = sscanf(opr->name, "%3s+%x%n", s_reg, &offset, &len);
+  if (ret != 2 || len != strlen(opr->name)) {
+    ret = sscanf(opr->name, "%3s%n", s_reg, &len);
+    if (ret != 1 || len != strlen(opr->name))
+      return NULL;
+  }
+
+  reg = char_array_i(regs_r32, ARRAY_SIZE(regs_r32), s_reg);
+  if (reg < 0)
+    return NULL;
+
+  opr_s.reg = reg;
+  ret = resolve_origin(i, &opr_s, i + magic, &j, NULL);
+  if (ret != 1)
+    return NULL;
+
+  if (ops[j].op == OP_MOV && ops[j].operand[1].type == OPT_REGMEM
+    && strlen(ops[j].operand[1].name) == 3
+    && ops[j].operand[0].lmod == OPLM_DWORD
+    && ops[j].pp == NULL // no hint
+    && level == 0)
+  {
+    // allow one simple dereference (com/directx)
+    reg = char_array_i(regs_r32, ARRAY_SIZE(regs_r32),
+            ops[j].operand[1].name);
+    if (reg < 0)
+      return NULL;
+    opr_s.reg = reg;
+    ret = resolve_origin(j, &opr_s, j + magic, &k, NULL);
+    if (ret != 1)
+      return NULL;
+    j = k;
+  }
+  if (ops[j].op != OP_MOV || ops[j].operand[0].lmod != OPLM_DWORD)
+    return NULL;
+
+  if (ops[j].pp != NULL) {
+    // type hint in asm
+    pp = ops[j].pp;
+  }
+  else if (ops[j].operand[1].type == OPT_REGMEM) {
+    pp = try_recover_pp(&ops[j], &ops[j].operand[1], 0, NULL);
+    if (pp == NULL) {
+      // maybe structure ptr in structure
+      pp = resolve_deref(j, magic, &ops[j].operand[1], level + 1);
+    }
+  }
+  else if (ops[j].operand[1].type == OPT_LABEL)
+    pp = proto_parse(g_fhdr, ops[j].operand[1].name, g_quiet_pp);
+  else if (ops[j].operand[1].type == OPT_REG) {
+    // maybe arg reg?
+    k = -1;
+    ret = resolve_origin(j, &ops[j].operand[1], i + magic,
+            &k, &from_caller);
+    if (ret != 1 && from_caller && k == -1 && g_func_pp != NULL) {
+      for (k = 0; k < g_func_pp->argc; k++) {
+        if (g_func_pp->arg[k].reg == NULL)
+          continue;
+        if (IS(g_func_pp->arg[k].reg, ops[j].operand[1].name)) {
+          pp = g_func_pp->arg[k].pp;
+          break;
+        }
+      }
+    }
+  }
+
+  if (pp == NULL)
+    return NULL;
+  if (pp->is_func || pp->is_fptr || !pp->type.is_struct) {
+    if (offset != 0)
+      ferr(&ops[j], "expected struct, got '%s %s'\n",
+           pp->type.name, pp->name);
+    return NULL;
+  }
+
+  return proto_lookup_struct(g_fhdr, pp->type.name, offset);
+}
+
 static const struct parsed_proto *resolve_icall(int i, int opcnt,
   int *pp_i, int *multi_src)
 {
   const struct parsed_proto *pp = NULL;
   int search_advice = 0;
-  int offset = -1;
-  char name[256];
-  char s_reg[4];
-  int reg, len;
-  int ret;
 
   *multi_src = 0;
   *pp_i = -1;
@@ -4299,67 +4446,14 @@ static const struct parsed_proto *resolve_icall(int i, int opcnt,
   switch (ops[i].operand[0].type) {
   case OPT_REGMEM:
     // try to resolve struct member calls
-    ret = sscanf(ops[i].operand[0].name, "%3s+%x%n",
-            s_reg, &offset, &len);
-    if (ret == 2 && len == strlen(ops[i].operand[0].name))
-    {
-      reg = char_array_i(regs_r32, ARRAY_SIZE(regs_r32), s_reg);
-      if (reg >= 0) {
-        struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, reg);
-        int j = -1;
-        ret = resolve_origin(i, &opr, i + opcnt * 19, &j, NULL);
-        if (ret != 1)
-          break;
-        if (ops[j].op == OP_MOV && ops[j].operand[1].type == OPT_REGMEM
-          && ops[j].operand[0].lmod == OPLM_DWORD
-          && ops[j].pp == NULL) // no hint
-        {
-          // allow one simple dereference (directx)
-          reg = char_array_i(regs_r32, ARRAY_SIZE(regs_r32),
-                  ops[j].operand[1].name);
-          if (reg < 0)
-            break;
-          struct parsed_opr opr2 = OPR_INIT(OPT_REG, OPLM_DWORD, reg);
-          int k = -1;
-          ret = resolve_origin(j, &opr2, j + opcnt * 19, &k, NULL);
-          if (ret != 1)
-            break;
-          j = k;
-        }
-        if (ops[j].op != OP_MOV)
-          break;
-        if (ops[j].operand[0].lmod != OPLM_DWORD)
-          break;
-        if (ops[j].pp != NULL) {
-          // type hint in asm
-          pp = ops[j].pp;
-        }
-        else if (ops[j].operand[1].type == OPT_REGMEM) {
-          // allow 'hello[ecx]' - assume array of same type items
-          ret = sscanf(ops[j].operand[1].name, "%[^[][e%2s]",
-                  name, s_reg);
-          if (ret != 2)
-            break;
-          pp = proto_parse(g_fhdr, name, g_quiet_pp);
-        }
-        else if (ops[j].operand[1].type == OPT_LABEL)
-          pp = proto_parse(g_fhdr, ops[j].operand[1].name, g_quiet_pp);
-        else
-          break;
-        if (pp == NULL)
-          break;
-        if (pp->is_func || pp->is_fptr || !pp->type.is_struct) {
-          pp = NULL;
-          break;
-        }
-        pp = proto_lookup_struct(g_fhdr, pp->type.name, offset);
-      }
+    pp = resolve_deref(i, i + opcnt * 19, &ops[i].operand[0], 0);
+    if (pp != NULL)
       break;
-    }
     // fallthrough
   case OPT_LABEL:
   case OPT_OFFSET:
-    pp = try_recover_pp(&ops[i], &ops[i].operand[0], &search_advice);
+    pp = try_recover_pp(&ops[i], &ops[i].operand[0],
+           1, &search_advice);
     if (!search_advice)
       break;
     // fallthrough
@@ -4530,9 +4624,69 @@ static struct parsed_proto *process_call(int i, int opcnt)
   return pp;
 }
 
-static int collect_call_args_early(struct parsed_op *po, int i,
-  struct parsed_proto *pp, int *regmask)
+static int collect_call_args_no_push(int i, struct parsed_proto *pp,
+  int *regmask_ffca)
+{
+  struct parsed_op *po;
+  int offset = 0;
+  int base_arg;
+  int j, arg;
+  int ret;
+
+  for (base_arg = 0; base_arg < pp->argc; base_arg++)
+    if (pp->arg[base_arg].reg == NULL)
+      break;
+
+  for (j = i; j > 0; )
+  {
+    ferr_assert(&ops[j], g_labels[j] == NULL);
+    j--;
+
+    po = &ops[j];
+    ferr_assert(po, po->op != OP_PUSH);
+    if (po->op == OP_FST)
+    {
+      if (po->operand[0].type != OPT_REGMEM)
+        continue;
+      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);
+
+      arg = base_arg + offset / 4;
+      po->p_argnext = -1;
+      po->p_argnum = arg + 1;
+      ferr_assert(po, pp->arg[arg].datap == NULL);
+      pp->arg[arg].datap = po;
+      po->flags |= OPF_DONE | OPF_FARGNR | OPF_FARG;
+      if (regmask_ffca != NULL)
+        *regmask_ffca |= 1 << arg;
+    }
+    else if (po->op == OP_SUB && po->operand[0].reg == xSP
+      && po->operand[1].type == OPT_CONST)
+    {
+      po->flags |= OPF_RMD | OPF_DONE | OPF_FARGNR | OPF_FARG;
+      break;
+    }
+  }
+
+  for (arg = base_arg; arg < pp->argc; arg++) {
+    ferr_assert(&ops[i], pp->arg[arg].reg == NULL);
+    po = pp->arg[arg].datap;
+    if (po == NULL)
+      ferr(&ops[i], "arg %d/%d not found\n", arg, pp->argc);
+    if (po->operand[0].lmod == OPLM_QWORD)
+      arg++;
+  }
+
+  return 0;
+}
+
+static int collect_call_args_early(int i, struct parsed_proto *pp,
+  int *regmask, int *regmask_ffca)
 {
+  struct parsed_op *po;
   int arg, ret;
   int j;
 
@@ -4547,19 +4701,20 @@ static int collect_call_args_early(struct parsed_op *po, int i,
       return -1;
     j--;
 
-    if (ops[j].op == OP_CALL)
+    po = &ops[j];
+    if (po->op == OP_CALL)
       return -1;
-    else if (ops[j].op == OP_ADD && ops[j].operand[0].reg == xSP)
+    else if (po->op == OP_ADD && po->operand[0].reg == xSP)
       return -1;
-    else if (ops[j].op == OP_POP)
+    else if (po->op == OP_POP)
       return -1;
-    else if (ops[j].flags & OPF_CJMP)
+    else if (po->flags & OPF_CJMP)
       return -1;
-    else if (ops[j].op == OP_PUSH) {
-      if (ops[j].flags & (OPF_FARG|OPF_FARGNR))
+    else if (po->op == OP_PUSH) {
+      if (po->flags & (OPF_FARG|OPF_FARGNR))
         return -1;
       if (!g_header_mode) {
-        ret = scan_for_mod(&ops[j], j + 1, i, 1);
+        ret = scan_for_mod(po, j + 1, i, 1);
         if (ret >= 0)
           return -1;
       }
@@ -4572,6 +4727,17 @@ static int collect_call_args_early(struct parsed_op *po, int i,
         if (pp->arg[arg].reg == NULL)
           break;
     }
+    else if (po->op == OP_SUB && po->operand[0].reg == xSP
+      && po->operand[1].type == OPT_CONST)
+    {
+      if (po->flags & (OPF_RMD|OPF_DONE))
+        return -1;
+      if (po->operand[1].val != pp->argc_stack * 4)
+        ferr(po, "unexpected esp adjust: %d\n",
+             po->operand[1].val * 4);
+      ferr_assert(po, pp->argc - arg == pp->argc_stack);
+      return collect_call_args_no_push(i, pp, regmask_ffca);
+    }
   }
 
   if (arg < pp->argc)
@@ -4592,7 +4758,7 @@ static int collect_call_args_early(struct parsed_op *po, int i,
       ferr_assert(&ops[j], pp->arg[arg].datap == NULL);
       pp->arg[arg].datap = &ops[j];
 
-      if (ops[j].operand[0].type == OPT_REG)
+      if (regmask != NULL && ops[j].operand[0].type == OPT_REG)
         *regmask |= 1 << ops[j].operand[0].reg;
 
       ops[j].flags |= OPF_RMD | OPF_DONE | OPF_FARGNR | OPF_FARG;
@@ -4956,9 +5122,10 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits,
         flags_set = OPF_RSAVE | OPF_DONE;
       }
 
-      ret = scan_for_pop(i + 1, opcnt, i + opcnt * 3, reg, 0, 0);
+      ret = scan_for_pop(i + 1, opcnt, i + opcnt * 3, reg, 0, 0, 0);
       if (ret == 1) {
-        scan_for_pop(i + 1, opcnt, i + opcnt * 4, reg, 0, flags_set);
+        scan_for_pop(i + 1, opcnt, i + opcnt * 4,
+          reg, 0, 0, flags_set);
       }
       else {
         ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, 0);
@@ -5164,9 +5331,11 @@ static void output_pp(FILE *fout, const struct parsed_proto *pp,
   for (i = 0; i < pp->argc; i++) {
     if (i > 0)
       fprintf(fout, ", ");
-    if (pp->arg[i].fptr != NULL && !(flags & OPP_SIMPLE_ARGS)) {
+    if (pp->arg[i].pp != NULL && pp->arg[i].pp->is_func
+      && !(flags & OPP_SIMPLE_ARGS))
+    {
       // func pointer
-      output_pp(fout, pp->arg[i].fptr, 0);
+      output_pp(fout, pp->arg[i].pp, 0);
     }
     else if (pp->arg[i].type.is_retreg) {
       fprintf(fout, "u32 *r_%s", pp->arg[i].reg);
@@ -5206,7 +5375,6 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   char buf1[256], buf2[256], buf3[256], cast[64];
   struct parsed_proto *pp, *pp_tmp;
   struct parsed_data *pd;
-  unsigned int uval;
   int save_arg_vars[MAX_ARG_GRP] = { 0, };
   unsigned char cbits[MAX_OPS / 8];
   const char *float_type;
@@ -5226,6 +5394,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   int regmask_now;      // temp
   int regmask_init = 0; // regs that need zero initialization
   int regmask_pp = 0;   // regs used in complex push-pop graph
+  int regmask_ffca = 0; // float function call args
   int regmask = 0;      // used regs
   int pfomask = 0;
   int found = 0;
@@ -5283,10 +5452,12 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
     {
       pp = process_call_early(i, opcnt, &j);
       if (pp != NULL) {
-        if (!(po->flags & OPF_ATAIL))
+        if (!(po->flags & OPF_ATAIL)) {
           // since we know the args, try to collect them
-          if (collect_call_args_early(po, i, pp, &regmask) != 0)
+          ret = collect_call_args_early(i, pp, &regmask, &regmask_ffca);
+          if (ret != 0)
             pp = NULL;
+        }
       }
 
       if (pp != NULL) {
@@ -5410,7 +5581,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
     {
       int setters[16], cnt = 0, branched = 0;
 
-      ret = scan_for_flag_set(i, i + opcnt * 6,
+      ret = scan_for_flag_set(i, opcnt, i + opcnt * 6,
               &branched, setters, &cnt);
       if (ret < 0 || cnt <= 0)
         ferr(po, "unable to trace flag setter(s)\n");
@@ -5807,6 +5978,15 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
     }
   }
 
+  if (regmask_ffca) {
+    for (reg = 0; reg < 32; reg++) {
+      if (regmask_ffca & (1 << reg)) {
+        fprintf(fout, "  %s fs_%d;\n", float_type, reg + 1);
+        had_decl = 1;
+      }
+    }
+  }
+
   // declare push-pop temporaries
   if (regmask_pp) {
     for (reg = 0; reg < 8; reg++) {
@@ -5943,32 +6123,6 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 
     pfomask = po->pfomask;
 
-    if (po->flags & (OPF_REPZ|OPF_REPNZ)) {
-      struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xCX);
-      ret = try_resolve_const(i, &opr, opcnt * 7 + i, &uval);
-
-      if (ret != 1 || uval == 0) {
-        // we need initial flags for ecx=0 case..
-        if (i > 0 && ops[i - 1].op == OP_XOR
-          && IS(ops[i - 1].operand[0].name,
-                ops[i - 1].operand[1].name))
-        {
-          fprintf(fout, "  cond_z = ");
-          if (pfomask & (1 << PFO_C))
-            fprintf(fout, "cond_c = ");
-          fprintf(fout, "0;\n");
-        }
-        else if (last_arith_dst != NULL) {
-          out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
-          out_test_for_cc(buf1, sizeof(buf1), po, PFO_Z, 0,
-            last_arith_dst->lmod, buf3);
-          fprintf(fout, "  cond_z = %s;\n", buf1);
-        }
-        else
-          ferr(po, "missing initial ZF\n");
-      }
-    }
-
     switch (po->op)
     {
       case OP_MOV:
@@ -6124,7 +6278,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         if (po->flags & OPF_REP) {
           assert_operand_cnt(3);
           fprintf(fout,
-            "  for (; ecx != 0; ecx--) {\n");
+            "  while (ecx != 0) {\n");
           if (pfomask & (1 << PFO_C)) {
             // ugh..
             fprintf(fout,
@@ -6135,6 +6289,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
             "    cond_z = (%sesi == %sedi); esi %c= %d, edi %c= %d;\n",
               buf1, buf1, l, j, l, j);
           fprintf(fout,
+            "    ecx--;\n"
             "    if (cond_z %s 0) break;\n",
               (po->flags & OPF_REPZ) ? "==" : "!=");
           fprintf(fout,
@@ -6162,12 +6317,13 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         if (po->flags & OPF_REP) {
           assert_operand_cnt(3);
           fprintf(fout,
-            "  for (; ecx != 0; ecx--) {\n");
+            "  while (ecx != 0) {\n");
           fprintf(fout,
             "    cond_z = (%seax == %sedi); edi %c= %d;\n",
               lmod_cast_u(po, po->operand[1].lmod),
               lmod_cast_u_ptr(po, po->operand[1].lmod), l, j);
           fprintf(fout,
+            "    ecx--;\n"
             "    if (cond_z %s 0) break;\n",
               (po->flags & OPF_REPZ) ? "==" : "!=");
           fprintf(fout,
@@ -6360,9 +6516,13 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         propagate_lmod(po, &po->operand[0], &po->operand[1]);
         if (IS(opr_name(po, 0), opr_name(po, 1))) {
           // special case for XOR
-          if (pfomask & (1 << PFO_BE)) { // weird, but it happens..
-            fprintf(fout, "  cond_be = 1;\n");
-            pfomask &= ~(1 << PFO_BE);
+          int z = PFOB_O | PFOB_C | PFOB_S | (1 << PFO_L);
+          for (j = 0; j <= PFO_LE; j++) {
+            if (pfomask & (1 << j)) {
+              fprintf(fout, "  cond_%s = %d;\n",
+                parsed_flag_op_names[j], (1 << j) & z ? 0 : 1);
+              pfomask &= ~(1 << j);
+            }
           }
           fprintf(fout, "  %s = 0;",
             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
@@ -6763,6 +6923,11 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
             if (tmp_op->flags & OPF_VAPUSH) {
               fprintf(fout, "ap");
             }
+            else if (tmp_op->op == OP_FST) {
+              fprintf(fout, "fs_%d", tmp_op->p_argnum);
+              if (tmp_op->operand[0].lmod == OPLM_QWORD)
+                arg++;
+            }
             else if (tmp_op->p_argpass != 0) {
               fprintf(fout, "a%d", tmp_op->p_argpass);
             }
@@ -6992,13 +7157,19 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         break;
 
       case OP_FST:
-        dead_dst = po->operand[0].type == OPT_REG
-          && po->operand[0].reg == xST0;
-        if (!dead_dst) {
-          fprintf(fout, "  %s = %s;",
-            out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0],
-              need_float_stack), float_st0);
+        if (po->flags & OPF_FARG) {
+          // store to stack as func arg
+          snprintf(buf1, sizeof(buf1), "fs_%d", po->p_argnum);
+          dead_dst = 0;
+        }
+        else {
+          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 (!dead_dst)
+          fprintf(fout, "  %s = %s;", buf1, float_st0);
         if (po->flags & OPF_FSHIFT) {
           if (need_float_stack)
             fprintf(fout, "  f_stp++;");
@@ -7492,11 +7663,11 @@ static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits,
       if (po->flags & OPF_DONE)
         continue;
 
-      ret = scan_for_pop(i + 1, opcnt, i + opcnt * 2, reg, 0, 0);
+      ret = scan_for_pop(i + 1, opcnt, i + opcnt * 2, reg, 0, 0, 0);
       if (ret == 1) {
         regmask_save |= 1 << reg;
         po->flags |= OPF_RMD;
-        scan_for_pop(i + 1, opcnt, i + opcnt * 3, reg, 0, OPF_RMD);
+        scan_for_pop(i + 1, opcnt, i + opcnt * 3, reg, 0, 0, OPF_RMD);
         continue;
       }
     }
@@ -7656,7 +7827,7 @@ static void gen_hdr(const char *funcn, int opcnt)
       if (pp != NULL) {
         if (!(po->flags & OPF_ATAIL))
           // since we know the args, try to collect them
-          if (collect_call_args_early(po, i, pp, &regmask_dummy) != 0)
+          if (collect_call_args_early(i, pp, NULL, NULL) != 0)
             pp = NULL;
       }