translate: many improvements for header gen
[ia32rtools.git] / tools / translate.c
index 81c555b..e43da7c 100644 (file)
@@ -37,7 +37,7 @@ static FILE *g_fhdr;
 #include "masm_tools.h"
 
 enum op_flags {
-  OPF_RMD    = (1 << 0), /* removed or optimized out */
+  OPF_RMD    = (1 << 0), /* removed from code generation */
   OPF_DATA   = (1 << 1), /* data processing - writes to dst opr */
   OPF_FLAGS  = (1 << 2), /* sets flags */
   OPF_JMP    = (1 << 3), /* branch, call */
@@ -1174,7 +1174,7 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc)
      && op->operand[0].reg == op->operand[1].reg
      && IS(op->operand[0].name, op->operand[1].name)) // ! ah, al..
     {
-      op->flags |= OPF_RMD;
+      op->flags |= OPF_RMD | OPF_DONE;
       op->regmask_src = op->regmask_dst = 0;
     }
     break;
@@ -1186,7 +1186,7 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc)
       char buf[16];
       snprintf(buf, sizeof(buf), "%s+0", op->operand[0].name);
       if (IS(buf, op->operand[1].name))
-        op->flags |= OPF_RMD;
+        op->flags |= OPF_RMD | OPF_DONE;
     }
     break;
 
@@ -1720,7 +1720,7 @@ static void check_func_pp(struct parsed_op *po,
   // fptrs must use 32bit args, callsite might have no information and
   // lack a cast to smaller types, which results in incorrectly masked
   // args passed (callee may assume masked args, it does on ARM)
-  if (!pp->is_oslib) {
+  if (!pp->is_osinc) {
     for (i = 0; i < pp->argc; i++) {
       ret = guess_lmod_from_c_type(&tmp_lmod, &pp->arg[i].type);
       if (ret && tmp_lmod != OPLM_DWORD)
@@ -2161,7 +2161,7 @@ static int scan_for_pop(int i, int opcnt, const char *reg,
       return -1; // deadend
     }
 
-    if ((po->flags & OPF_RMD)
+    if ((po->flags & (OPF_RMD|OPF_DONE))
         || (po->op == OP_PUSH && po->p_argnum != 0)) // arg push
       continue;
 
@@ -2237,12 +2237,13 @@ static int scan_for_pop_ret(int i, int opcnt, const char *reg,
       continue;
 
     for (j = i - 1; j >= 0; j--) {
-      if (ops[j].flags & OPF_RMD)
+      if (ops[j].flags & (OPF_RMD|OPF_DONE))
         continue;
       if (ops[j].flags & OPF_JMP)
         return -1;
 
-      if (ops[j].op == OP_POP && ops[j].operand[0].type == OPT_REG
+      if (ops[j].op == OP_POP && ops[j].datap == NULL
+          && ops[j].operand[0].type == OPT_REG
           && IS(ops[j].operand[0].name, reg))
       {
         found = 1;
@@ -2258,7 +2259,6 @@ static int scan_for_pop_ret(int i, int opcnt, const char *reg,
   return found ? 0 : -1;
 }
 
-// XXX: merge with scan_for_pop?
 static void scan_for_pop_const(int i, int opcnt)
 {
   int j;
@@ -2270,9 +2270,10 @@ static void scan_for_pop_const(int i, int opcnt)
       break;
     }
 
-    if (!(ops[j].flags & OPF_RMD) && ops[j].op == OP_POP)
+    if (ops[j].op == OP_POP && !(ops[j].flags & (OPF_RMD|OPF_DONE)))
     {
-      ops[i].flags |= OPF_RMD;
+      ops[i].flags |= OPF_RMD | OPF_DONE;
+      ops[j].flags |= OPF_DONE;
       ops[j].datap = &ops[i];
       break;
     }
@@ -2317,7 +2318,7 @@ static void scan_propagate_df(int i, int opcnt)
       break;
 
     if (po->op == OP_CLD) {
-      po->flags |= OPF_RMD;
+      po->flags |= OPF_RMD | OPF_DONE;
       return;
     }
   }
@@ -2522,7 +2523,7 @@ static int scan_for_reg_clear(int i, int reg)
 
 // scan for positive, constant esp adjust
 static int scan_for_esp_adjust(int i, int opcnt,
-  unsigned int adj_expect, int *adj, int *multipath)
+  int adj_expect, int *adj, int *multipath)
 {
   struct parsed_op *po;
   int first_pop = -1;
@@ -2530,11 +2531,13 @@ static int scan_for_esp_adjust(int i, int opcnt,
   *adj = *multipath = 0;
 
   for (; i < opcnt && *adj < adj_expect; i++) {
-    po = &ops[i];
-
     if (g_labels[i] != NULL)
       *multipath = 1;
 
+    po = &ops[i];
+    if (po->flags & OPF_DONE)
+      continue;
+
     if (po->op == OP_ADD && po->operand[0].reg == xSP) {
       if (po->operand[1].type != OPT_CONST)
         ferr(&ops[i], "non-const esp adjust?\n");
@@ -2543,23 +2546,25 @@ static int scan_for_esp_adjust(int i, int opcnt,
         ferr(&ops[i], "unaligned esp adjust: %x\n", *adj);
       return i;
     }
-    else if (po->op == OP_PUSH && !(po->flags & OPF_RMD)) {
+    else if (po->op == OP_PUSH) {
       //if (first_pop == -1)
       //  first_pop = -2; // none
       *adj -= lmod_bytes(po, po->operand[0].lmod);
     }
-    else if (po->op == OP_POP && !(po->flags & OPF_RMD)) {
-      if (po->datap != NULL) // in push/pop pair?
-        break;
-      // seems like msvc only uses 'pop ecx' for stack realignment..
-      if (po->operand[0].type != OPT_REG || po->operand[0].reg != xCX)
-        break;
-      if (first_pop == -1 && *adj >= 0)
-        first_pop = i;
+    else if (po->op == OP_POP) {
+      if (!(po->flags & OPF_DONE)) {
+        // seems like msvc only uses 'pop ecx' for stack realignment..
+        if (po->operand[0].type != OPT_REG || po->operand[0].reg != xCX)
+          break;
+        if (first_pop == -1 && *adj >= 0)
+          first_pop = i;
+      }
       *adj += lmod_bytes(po, po->operand[0].lmod);
     }
     else if (po->flags & (OPF_JMP|OPF_TAIL)) {
       if (po->op == OP_JMP && po->btj == NULL) {
+        if (po->bt_i <= i)
+          break;
         i = po->bt_i - 1;
         continue;
       }
@@ -2569,6 +2574,7 @@ static int scan_for_esp_adjust(int i, int opcnt,
         break;
       if (po->pp != NULL && po->pp->is_stdcall)
         break;
+      // assume it's another cdecl call
     }
   }
 
@@ -2788,13 +2794,13 @@ static void scan_prologue_epilogue(int opcnt)
       && IS(opr_name(&ops[1], 1), "esp"))
   {
     g_bp_frame = 1;
-    ops[0].flags |= OPF_RMD;
-    ops[1].flags |= OPF_RMD;
+    ops[0].flags |= OPF_RMD | OPF_DONE;
+    ops[1].flags |= OPF_RMD | OPF_DONE;
     i = 2;
 
     if (ops[2].op == OP_SUB && IS(opr_name(&ops[2], 0), "esp")) {
       g_stack_fsz = opr_const(&ops[2], 1);
-      ops[2].flags |= OPF_RMD;
+      ops[2].flags |= OPF_RMD | OPF_DONE;
       i++;
     }
     else {
@@ -2802,7 +2808,7 @@ static void scan_prologue_epilogue(int opcnt)
       i = 2;
       while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
         g_stack_fsz += 4;
-        ops[i].flags |= OPF_RMD;
+        ops[i].flags |= OPF_RMD | OPF_DONE;
         ecx_push++;
         i++;
       }
@@ -2813,9 +2819,9 @@ static void scan_prologue_epilogue(int opcnt)
           && IS(opr_name(&ops[i + 1], 0), "__alloca_probe"))
       {
         g_stack_fsz += ops[i].operand[1].val;
-        ops[i].flags |= OPF_RMD;
+        ops[i].flags |= OPF_RMD | OPF_DONE;
         i++;
-        ops[i].flags |= OPF_RMD;
+        ops[i].flags |= OPF_RMD | OPF_DONE;
         i++;
       }
     }
@@ -2835,7 +2841,7 @@ static void scan_prologue_epilogue(int opcnt)
       if ((ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ebp"))
           || ops[j].op == OP_LEAVE)
       {
-        ops[j].flags |= OPF_RMD;
+        ops[j].flags |= OPF_RMD | OPF_DONE;
       }
       else if (!(g_ida_func_attr & IDAFA_NORETURN))
         ferr(&ops[j], "'pop ebp' expected\n");
@@ -2845,7 +2851,7 @@ static void scan_prologue_epilogue(int opcnt)
             && IS(opr_name(&ops[j - 1], 0), "esp")
             && IS(opr_name(&ops[j - 1], 1), "ebp"))
         {
-          ops[j - 1].flags |= OPF_RMD;
+          ops[j - 1].flags |= OPF_RMD | OPF_DONE;
         }
         else if (ops[j].op != OP_LEAVE
           && !(g_ida_func_attr & IDAFA_NORETURN))
@@ -2870,7 +2876,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;
+    ops[i].flags |= OPF_RMD | OPF_DONE;
     g_stack_fsz += 4;
     ecx_push++;
     i++;
@@ -2883,7 +2889,7 @@ static void scan_prologue_epilogue(int opcnt)
       && ops[i].operand[1].type == OPT_CONST)
     {
       g_stack_fsz = ops[i].operand[1].val;
-      ops[i].flags |= OPF_RMD;
+      ops[i].flags |= OPF_RMD | OPF_DONE;
       esp_sub = 1;
       break;
     }
@@ -2902,7 +2908,7 @@ static void scan_prologue_epilogue(int opcnt)
       while (i > 0 && j > 0) {
         i--;
         if (ops[i].op == OP_PUSH) {
-          ops[i].flags &= ~OPF_RMD;
+          ops[i].flags &= ~(OPF_RMD | OPF_DONE);
           j--;
         }
       }
@@ -2947,12 +2953,12 @@ static void scan_prologue_epilogue(int opcnt)
                    && ops[j].operand[1].type == OPT_CONST)
           {
             /* add esp, N */
-            ecx_push -= ops[j].operand[1].val / 4 - 1;
+            l += ops[j].operand[1].val / 4 - 1;
           }
           else
             ferr(&ops[j], "'pop ecx' expected\n");
 
-          ops[j].flags |= OPF_RMD;
+          ops[j].flags |= OPF_RMD | OPF_DONE;
           j--;
         }
         if (l != ecx_push)
@@ -2968,7 +2974,7 @@ static void scan_prologue_epilogue(int opcnt)
             || ops[j].operand[1].val != g_stack_fsz)
           ferr(&ops[j], "'add esp' expected\n");
 
-        ops[j].flags |= OPF_RMD;
+        ops[j].flags |= OPF_RMD | OPF_DONE;
         ops[j].operand[1].val = 0; // hack for stack arg scanner
         found = 1;
       }
@@ -3107,6 +3113,21 @@ static struct parsed_proto *process_call_early(int i, int opcnt,
   return pp;
 }
 
+static void patch_esp_adjust(struct parsed_op *po, int adj)
+{
+  ferr_assert(po, po->op == OP_ADD);
+  ferr_assert(po, IS(opr_name(po, 0), "esp"));
+  ferr_assert(po, po->operand[1].type == OPT_CONST);
+
+  // this is a bit of a hack, but deals with use of
+  // single adj for multiple calls
+  po->operand[1].val -= adj;
+  po->flags |= OPF_RMD;
+  if (po->operand[1].val == 0)
+    po->flags |= OPF_DONE;
+  ferr_assert(po, (int)po->operand[1].val >= 0);
+}
+
 static struct parsed_proto *process_call(int i, int opcnt)
 {
   struct parsed_op *po = &ops[i];
@@ -3148,7 +3169,7 @@ static struct parsed_proto *process_call(int i, int opcnt)
       my_assert_not(pp, NULL);
 
       pp->is_fptr = 1;
-      ret = scan_for_esp_adjust(i + 1, opcnt, ~0, &adj, &multipath);
+      ret = scan_for_esp_adjust(i + 1, opcnt, 32*4, &adj, &multipath);
       if (ret < 0 || adj < 0) {
         if (!g_allow_regfunc)
           ferr(po, "non-__cdecl indirect call unhandled yet\n");
@@ -3167,6 +3188,7 @@ static struct parsed_proto *process_call(int i, int opcnt)
   }
 
   // look for and make use of esp adjust
+  multipath = 0;
   ret = -1;
   if (!pp->is_stdcall && pp->argc_stack > 0)
     ret = scan_for_esp_adjust(i + 1, opcnt,
@@ -3200,17 +3222,14 @@ static struct parsed_proto *process_call(int i, int opcnt)
         // deal with multi-pop stack adjust
         adj = pp->argc_stack;
         while (ops[ret].op == OP_POP && adj > 0 && ret < opcnt) {
-          ops[ret].flags |= OPF_RMD;
+          ops[ret].flags |= OPF_RMD | OPF_DONE;
           adj--;
           ret++;
         }
       }
     }
-    else if (!multipath) {
-      // a bit of a hack, but deals with use of
-      // single adj for multiple calls
-      ops[ret].operand[1].val -= pp->argc_stack * 4;
-    }
+    else if (!multipath)
+      patch_esp_adjust(&ops[ret], pp->argc_stack * 4);
   }
   else if (pp->is_vararg)
     ferr(po, "missing esp_adjust for vararg func '%s'\n",
@@ -3282,7 +3301,7 @@ static int collect_call_args_early(struct parsed_op *po, int i,
       if (ops[j].operand[0].type == OPT_REG)
         *regmask |= 1 << ops[j].operand[0].reg;
 
-      ops[j].flags |= OPF_RMD | OPF_FARGNR | OPF_FARG;
+      ops[j].flags |= OPF_RMD | OPF_DONE | OPF_FARGNR | OPF_FARG;
       ops[j].flags &= ~OPF_RSAVE;
 
       // next arg
@@ -3388,8 +3407,7 @@ static int collect_call_args_r(struct parsed_op *po, int i,
       ferr(po, "arg collect %d/%d hit esp adjust of %d\n",
         arg, pp->argc, ops[j].operand[1].val);
     }
-    else if (ops[j].op == OP_POP && !(ops[j].flags & OPF_RMD)
-      && ops[j].datap == NULL)
+    else if (ops[j].op == OP_POP && !(ops[j].flags & OPF_DONE))
     {
       if (pp->is_unresolved)
         break;
@@ -3403,7 +3421,8 @@ static int collect_call_args_r(struct parsed_op *po, int i,
 
       may_reuse = 1;
     }
-    else if (ops[j].op == OP_PUSH && !(ops[j].flags & OPF_FARGNR))
+    else if (ops[j].op == OP_PUSH
+      && !(ops[j].flags & (OPF_FARGNR|OPF_DONE)))
     {
       if (pp->is_unresolved && (ops[j].flags & OPF_RMD))
         break;
@@ -3471,7 +3490,7 @@ static int collect_call_args_r(struct parsed_op *po, int i,
             if (!g_func_pp->is_vararg
               || strstr(ops[k].operand[1].name, buf))
             {
-              ops[k].flags |= OPF_RMD;
+              ops[k].flags |= OPF_RMD | OPF_DONE;
               ops[j].flags |= OPF_RMD | OPF_VAPUSH;
               save_args &= ~(1 << arg);
               reg = -1;
@@ -3486,7 +3505,7 @@ static int collect_call_args_r(struct parsed_op *po, int i,
             ret = stack_frame_access(&ops[k], &ops[k].operand[1],
               buf, sizeof(buf), ops[k].operand[1].name, "", 1, 0);
             if (ret >= 0) {
-              ops[k].flags |= OPF_RMD;
+              ops[k].flags |= OPF_RMD | OPF_DONE;
               ops[j].flags |= OPF_RMD;
               ops[j].p_argpass = ret + 1;
               save_args &= ~(1 << arg);
@@ -3776,7 +3795,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
     po->bt_i = -1;
     po->btj = NULL;
 
-    if (po->flags & OPF_RMD)
+    if (po->flags & (OPF_RMD|OPF_DONE))
       continue;
 
     if (po->op == OP_CALL) {
@@ -3832,7 +3851,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       {
         if (l == i + 1 && po->op == OP_JMP) {
           // yet another alignment type..
-          po->flags |= OPF_RMD;
+          po->flags |= OPF_RMD|OPF_DONE;
           break;
         }
         add_label_ref(&g_label_refs[l], i);
@@ -3869,7 +3888,7 @@ tailcall:
     }
 
     po = &ops[i];
-    if (po->flags & OPF_RMD)
+    if (po->flags & (OPF_RMD|OPF_DONE))
       continue;
 
     if (po->op == OP_CALL)
@@ -3886,10 +3905,10 @@ tailcall:
         if (j >= 0) {
           // commit esp adjust
           ops[j].flags |= OPF_RMD;
-          if (ops[j].op != OP_POP) {
-            ferr_assert(&ops[j], ops[j].op == OP_ADD);
-            ops[j].operand[1].val -= pp->argc_stack * 4;
-          }
+          if (ops[j].op != OP_POP)
+            patch_esp_adjust(&ops[j], pp->argc_stack * 4);
+          else
+            ops[j].flags |= OPF_DONE;
         }
 
         if (strstr(pp->ret_type.name, "int64"))
@@ -3905,7 +3924,7 @@ tailcall:
   for (i = 0; i < opcnt; i++)
   {
     po = &ops[i];
-    if (po->flags & OPF_RMD)
+    if (po->flags & (OPF_RMD|OPF_DONE))
       continue;
 
     if (po->op == OP_CALL && !(po->flags & OPF_DONE))
@@ -3933,7 +3952,7 @@ tailcall:
   for (i = 0; i < opcnt; i++)
   {
     po = &ops[i];
-    if (po->flags & OPF_RMD)
+    if (po->flags & (OPF_RMD|OPF_DONE))
       continue;
 
     if (po->op == OP_PUSH && (po->flags & OPF_RSAVE)) {
@@ -3985,7 +4004,7 @@ tailcall:
     }
 
     if (po->op == OP_STD) {
-      po->flags |= OPF_DF | OPF_RMD;
+      po->flags |= OPF_DF | OPF_RMD | OPF_DONE;
       scan_propagate_df(i + 1, opcnt);
     }
 
@@ -4164,7 +4183,7 @@ tailcall:
         need_tmp64 = 1;
     }
     else if (po->op == OP_CLD)
-      po->flags |= OPF_RMD;
+      po->flags |= OPF_RMD | OPF_DONE;
 
     if (po->op == OP_RCL || po->op == OP_RCR || po->op == OP_XCHG) {
       need_tmp_var = 1;
@@ -5467,11 +5486,12 @@ struct func_prototype {
   int id;
   int argc_stack;
   int regmask_dep;
-  int has_ret:3;                // -1, 0, 1: unresolved, no, yes
+  int has_ret:3;                 // -1, 0, 1: unresolved, no, yes
   unsigned int dep_resolved:1;
   unsigned int is_stdcall:1;
   struct func_proto_dep *dep_func;
   int dep_func_cnt;
+  const struct parsed_proto *pp; // seed pp, if any
 };
 
 struct func_proto_dep {
@@ -5488,6 +5508,7 @@ static struct scanned_var {
   char name[NAMELEN];
   enum opr_lenmod lmod;
   unsigned int is_seeded:1;
+  unsigned int is_c_str:1;
 } *hg_vars;
 static int hg_var_cnt;
 
@@ -5537,25 +5558,164 @@ static int hg_fp_cmp_id(const void *p1_, const void *p2_)
 }
 #endif
 
+// recursive register dep pass
+// - track saved regs (part 2)
+// - try to figure out arg-regs
+// - calculate reg deps
+static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits,
+  struct func_prototype *fp, int regmask_save, int regmask_dst,
+  int *regmask_dep, int *has_ret)
+{
+  struct func_proto_dep *dep;
+  struct parsed_op *po;
+  int from_caller = 0;
+  int depth;
+  int j, l;
+  int reg;
+  int ret;
+
+  for (; i < opcnt; i++)
+  {
+    if (cbits[i >> 3] & (1 << (i & 7)))
+      return;
+    cbits[i >> 3] |= (1 << (i & 7));
+
+    po = &ops[i];
+
+    if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
+      if (po->btj != NULL) {
+        // jumptable
+        for (j = 0; j < po->btj->count; j++) {
+          gen_hdr_dep_pass(po->btj->d[j].bt_i, opcnt, cbits, fp,
+            regmask_save, regmask_dst, regmask_dep, has_ret);
+        }
+        return;
+      }
+
+      if (po->bt_i < 0) {
+        ferr(po, "dead branch\n");
+        return;
+      }
+
+      if (po->flags & OPF_CJMP) {
+        gen_hdr_dep_pass(po->bt_i, opcnt, cbits, fp,
+          regmask_save, regmask_dst, regmask_dep, has_ret);
+      }
+      else {
+        i = po->bt_i - 1;
+      }
+      continue;
+    }
+
+    if (po->flags & OPF_FARG)
+      /* (just calculate register deps) */;
+    else if (po->op == OP_PUSH && po->operand[0].type == OPT_REG)
+    {
+      reg = po->operand[0].reg;
+      if (reg < 0)
+        ferr(po, "reg not set for push?\n");
+
+      if (po->flags & OPF_RSAVE) {
+        regmask_save |= 1 << reg;
+        continue;
+      }
+      if (po->flags & OPF_DONE)
+        continue;
+
+      depth = 0;
+      ret = scan_for_pop(i + 1, opcnt,
+              po->operand[0].name, i + opcnt * 2, 0, &depth, 0);
+      if (ret == 1) {
+        regmask_save |= 1 << reg;
+        po->flags |= OPF_RMD;
+        scan_for_pop(i + 1, opcnt,
+          po->operand[0].name, i + opcnt * 3, 0, &depth, 1);
+        continue;
+      }
+    }
+    else if (po->flags & OPF_RMD)
+      continue;
+    else if (po->op == OP_CALL) {
+      po->regmask_dst |= 1 << xAX;
+
+      dep = hg_fp_find_dep(fp, po->operand[0].name);
+      if (dep != NULL)
+        dep->regmask_live = regmask_save | regmask_dst;
+    }
+    else if (po->op == OP_RET) {
+      if (po->operand_cnt > 0) {
+        fp->is_stdcall = 1;
+        if (fp->argc_stack >= 0
+            && fp->argc_stack != po->operand[0].val / 4)
+          ferr(po, "ret mismatch? (%d)\n", fp->argc_stack * 4);
+        fp->argc_stack = po->operand[0].val / 4;
+      }
+    }
+
+    if (*has_ret != 0 && (po->flags & OPF_TAIL)) {
+      if (po->op == OP_CALL) {
+        j = i;
+        ret = 1;
+      }
+      else {
+        struct parsed_opr opr = { 0, };
+        opr.type = OPT_REG;
+        opr.reg = xAX;
+        j = -1;
+        from_caller = 0;
+        ret = resolve_origin(i, &opr, i + opcnt * 4, &j, &from_caller);
+      }
+
+      if (ret == -1 && from_caller) {
+        // unresolved eax - probably void func
+        *has_ret = 0;
+      }
+      else {
+        if (ops[j].op == OP_CALL) {
+          dep = hg_fp_find_dep(fp, po->operand[0].name);
+          if (dep != NULL)
+            dep->ret_dep = 1;
+          else
+            *has_ret = 1;
+        }
+        else
+          *has_ret = 1;
+      }
+    }
+
+    l = regmask_save | regmask_dst;
+    if (g_bp_frame && !(po->flags & OPF_EBP_S))
+      l |= 1 << xBP;
+
+    l = po->regmask_src & ~l;
+#if 0
+    if (l)
+      fnote(po, "dep |= %04x, dst %04x, save %04x (f %x)\n",
+        l, regmask_dst, regmask_save, po->flags);
+#endif
+    *regmask_dep |= l;
+    regmask_dst |= po->regmask_dst;
+
+    if (po->flags & OPF_TAIL)
+      return;
+  }
+}
+
 static void gen_hdr(const char *funcn, int opcnt)
 {
   int save_arg_vars[MAX_ARG_GRP] = { 0, };
+  unsigned char cbits[MAX_OPS / 8];
   const struct parsed_proto *pp_c;
   struct parsed_proto *pp;
   struct func_prototype *fp;
-  struct func_proto_dep *dep;
   struct parsed_data *pd;
   struct parsed_op *po;
   const char *tmpname;
   int regmask_dummy = 0;
-  int regmask_save = 0;
-  int regmask_dst = 0;
-  int regmask_dep = 0;
+  int regmask_dep;
   int max_bp_offset = 0;
-  int has_ret = -1;
-  int from_caller = 0;
+  int has_ret;
   int i, j, l, ret;
-  int depth, reg;
 
   if ((hg_fp_cnt & 0xff) == 0) {
     hg_fp = realloc(hg_fp, sizeof(hg_fp[0]) * (hg_fp_cnt + 0x100));
@@ -5570,11 +5730,12 @@ static void gen_hdr(const char *funcn, int opcnt)
   hg_fp_cnt++;
 
   // perhaps already in seed header?
-  pp_c = proto_parse(g_fhdr, funcn, 1);
-  if (pp_c != NULL) {
-    fp->argc_stack = pp_c->argc_stack;
-    fp->regmask_dep = get_pp_arg_regmask(pp_c);
-    fp->has_ret = !IS(pp_c->ret_type.name, "void");
+  fp->pp = proto_parse(g_fhdr, funcn, 1);
+  if (fp->pp != NULL) {
+    fp->argc_stack = fp->pp->argc_stack;
+    fp->is_stdcall = fp->pp->is_stdcall;
+    fp->regmask_dep = get_pp_arg_regmask(fp->pp);
+    fp->has_ret = !IS(fp->pp->ret_type.name, "void");
     return;
   }
 
@@ -5594,7 +5755,7 @@ static void gen_hdr(const char *funcn, int opcnt)
     po->bt_i = -1;
     po->btj = NULL;
 
-    if (po->flags & OPF_RMD)
+    if (po->flags & (OPF_RMD|OPF_DONE))
       continue;
 
     if (po->op == OP_CALL) {
@@ -5666,7 +5827,6 @@ tailcall:
 
   // pass3:
   // - remove dead labels
-  // - process trivial calls
   // - handle push <const>/pop pairs
   for (i = 0; i < opcnt; i++)
   {
@@ -5676,7 +5836,19 @@ tailcall:
     }
 
     po = &ops[i];
-    if (po->flags & OPF_RMD)
+    if (po->flags & (OPF_RMD|OPF_DONE))
+      continue;
+
+    if (po->op == OP_PUSH && po->operand[0].type == OPT_CONST)
+      scan_for_pop_const(i, opcnt);
+  }
+
+  // pass4:
+  // - process trivial calls
+  for (i = 0; i < opcnt; i++)
+  {
+    po = &ops[i];
+    if (po->flags & (OPF_RMD|OPF_DONE))
       continue;
 
     if (po->op == OP_CALL)
@@ -5693,135 +5865,62 @@ tailcall:
         if (j >= 0) {
           // commit esp adjust
           ops[j].flags |= OPF_RMD;
-          if (ops[j].op != OP_POP) {
-            ferr_assert(&ops[j], ops[j].op == OP_ADD);
-            ops[j].operand[1].val -= pp->argc_stack * 4;
-          }
+          if (ops[j].op != OP_POP)
+            patch_esp_adjust(&ops[j], pp->argc_stack * 4);
+          else
+            ops[j].flags |= OPF_DONE;
         }
 
         po->flags |= OPF_DONE;
       }
     }
-    else if (po->op == OP_PUSH && po->operand[0].type == OPT_CONST) {
-      scan_for_pop_const(i, opcnt);
-    }
   }
 
-  // pass4:
+  // pass5:
+  // - track saved regs (simple)
   // - process calls
   for (i = 0; i < opcnt; i++)
   {
     po = &ops[i];
-    if (po->flags & OPF_RMD)
+    if (po->flags & (OPF_RMD|OPF_DONE))
       continue;
 
-    if (po->op == OP_CALL && !(po->flags & OPF_DONE))
+    if (po->op == OP_PUSH && po->operand[0].type == OPT_REG)
     {
-      pp = process_call(i, opcnt);
-
-      if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) {
-        // since we know the args, collect them
-        collect_call_args(po, i, pp, &regmask_dummy, save_arg_vars,
-          i + opcnt * 2);
-      }
-    }
-  }
-
-  // pass5:
-  // - track saved regs
-  // - try to figure out arg-regs
-  for (i = 0; i < opcnt; i++)
-  {
-    po = &ops[i];
-
-    if (po->flags & OPF_FARG)
-      /* (just calculate register deps) */;
-    else if (po->flags & OPF_RMD)
-      continue;
-    else if (po->op == OP_PUSH && po->operand[0].type == OPT_REG)
-    {
-      reg = po->operand[0].reg;
-      if (reg < 0)
-        ferr(po, "reg not set for push?\n");
-
-      depth = 0;
-      ret = scan_for_pop(i + 1, opcnt,
-              po->operand[0].name, i + opcnt * 1, 0, &depth, 0);
-      if (ret == 1) {
-        regmask_save |= 1 << reg;
-        po->flags |= OPF_RMD;
-        scan_for_pop(i + 1, opcnt,
-          po->operand[0].name, i + opcnt * 2, 0, &depth, 1);
-        continue;
-      }
       ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, 0);
       if (ret == 0) {
-        regmask_save |= 1 << reg;
-        po->flags |= OPF_RMD;
+        // regmask_save |= 1 << po->operand[0].reg; // do it later
+        po->flags |= OPF_RSAVE | OPF_RMD | OPF_DONE;
         scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, OPF_RMD);
-        continue;
       }
     }
-    else if (po->op == OP_CALL) {
-      po->regmask_dst |= 1 << xAX;
+    else if (po->op == OP_CALL && !(po->flags & OPF_DONE))
+    {
+      pp = process_call(i, opcnt);
 
-      dep = hg_fp_find_dep(fp, po->operand[0].name);
-      if (dep != NULL)
-        dep->regmask_live = regmask_save | regmask_dst;
-    }
-    else if (po->op == OP_RET) {
-      if (po->operand_cnt > 0) {
-        fp->is_stdcall = 1;
-        if (fp->argc_stack >= 0
-            && fp->argc_stack != po->operand[0].val / 4)
-          ferr(po, "ret mismatch? (%d)\n", fp->argc_stack * 4);
-        fp->argc_stack = po->operand[0].val / 4;
+      if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) {
+        // since we know the args, collect them
+        ret = collect_call_args(po, i, pp, &regmask_dummy, save_arg_vars,
+          i + opcnt * 1);
       }
     }
+  }
 
-    if (has_ret != 0 && (po->flags & OPF_TAIL)) {
-      if (po->op == OP_CALL) {
-        j = i;
-        ret = 1;
-      }
-      else {
-        struct parsed_opr opr = { 0, };
-        opr.type = OPT_REG;
-        opr.reg = xAX;
-        j = -1;
-        from_caller = 0;
-        ret = resolve_origin(i, &opr, i + opcnt * 3, &j, &from_caller);
-      }
+  // pass6
+  memset(cbits, 0, sizeof(cbits));
+  regmask_dep = 0;
+  has_ret = -1;
 
-      if (ret == -1 && from_caller) {
-        // unresolved eax - probably void func
-        has_ret = 0;
-      }
-      else {
-        if (ops[j].op == OP_CALL) {
-          dep = hg_fp_find_dep(fp, po->operand[0].name);
-          if (dep != NULL)
-            dep->ret_dep = 1;
-          else
-            has_ret = 1;
-        }
-        else
-          has_ret = 1;
-      }
-    }
+  gen_hdr_dep_pass(0, opcnt, cbits, fp, 0, 0, &regmask_dep, &has_ret);
 
-    l = regmask_save | regmask_dst;
-    if (g_bp_frame && !(po->flags & OPF_EBP_S))
-      l |= 1 << xBP;
+  // find unreachable code - must be fixed in IDA
+  for (i = 0; i < opcnt; i++)
+  {
+    if (cbits[i >> 3] & (1 << (i & 7)))
+      continue;
 
-    l = po->regmask_src & ~l;
-#if 0
-    if (l)
-      fnote(po, "dep |= %04x, dst %04x, save %04x\n", l,
-        regmask_dst, regmask_save);
-#endif
-    regmask_dep |= l;
-    regmask_dst |= po->regmask_dst;
+    if (ops[i].op != OP_NOP)
+      ferr(&ops[i], "unreachable code\n");
   }
 
   if (has_ret == -1 && (regmask_dep & (1 << xAX)))
@@ -5841,7 +5940,12 @@ tailcall:
 
   fp->regmask_dep = regmask_dep & ~(1 << xSP);
   fp->has_ret = has_ret;
-  // output_hdr_fp(stdout, fp, 1);
+#if 0
+  printf("// has_ret %d, regmask_dep %x\n",
+    fp->has_ret, fp->regmask_dep);
+  output_hdr_fp(stdout, fp, 1);
+  if (IS(funcn, "sub_100073FD")) exit(1);
+#endif
 
   gen_x_cleanup(opcnt);
 }
@@ -5849,6 +5953,7 @@ tailcall:
 static void hg_fp_resolve_deps(struct func_prototype *fp)
 {
   struct func_prototype fp_s;
+  int dep;
   int i;
 
   // this thing is recursive, so mark first..
@@ -5862,8 +5967,11 @@ static void hg_fp_resolve_deps(struct func_prototype *fp)
       if (!fp->dep_func[i].proto->dep_resolved)
         hg_fp_resolve_deps(fp->dep_func[i].proto);
 
-      fp->regmask_dep |= ~fp->dep_func[i].regmask_live
-                       & fp->dep_func[i].proto->regmask_dep;
+      dep = ~fp->dep_func[i].regmask_live
+           & fp->dep_func[i].proto->regmask_dep;
+      fp->regmask_dep |= dep;
+      // printf("dep %s %s |= %x\n", fp->name,
+      //   fp->dep_func[i].name, dep);
 
       if (fp->has_ret == -1)
         fp->has_ret = fp->dep_func[i].proto->has_ret;
@@ -5874,8 +5982,9 @@ static void hg_fp_resolve_deps(struct func_prototype *fp)
 static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
   int count)
 {
-  char *p, buf[NAMELEN];
-  const char *cp;
+  const struct parsed_proto *pp;
+  char *p, namebuf[NAMELEN];
+  const char *name;
   int regmask_dep;
   int argc_stack;
   int j, arg;
@@ -5894,10 +6003,26 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
     fprintf(fout, "\n");
 #endif
 
+    p = strchr(fp->name, '@');
+    if (p != NULL) {
+      memcpy(namebuf, fp->name, p - fp->name);
+      namebuf[p - fp->name] = 0;
+      name = namebuf;
+    }
+    else
+      name = fp->name;
+    if (name[0] == '_')
+      name++;
+
+    pp = proto_parse(g_fhdr, name, 1);
+    if (pp != NULL && pp->is_include)
+      continue;
+
     regmask_dep = fp->regmask_dep;
     argc_stack = fp->argc_stack;
 
-    fprintf(fout, fp->has_ret ? "int  " : "void ");
+    fprintf(fout, "%-5s", fp->pp ? fp->pp->ret_type.name :
+      (fp->has_ret ? "int" : "void"));
     if (regmask_dep && (fp->is_stdcall || argc_stack == 0)
       && (regmask_dep & ~((1 << xCX) | (1 << xDX))) == 0)
     {
@@ -5919,17 +6044,7 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
     else
       fprintf(fout, "  __cdecl       ");
 
-    p = strchr(fp->name, '@');
-    if (p != NULL) {
-      memcpy(buf, fp->name, p - fp->name);
-      buf[p - fp->name] = 0;
-      cp = buf;
-    }
-    else
-      cp = fp->name;
-    if (cp[0] == '_')
-      cp++;
-    fprintf(fout, "%s(", cp);
+    fprintf(fout, "%s(", name);
 
     arg = 0;
     for (j = 0; j < xSP; j++) {
@@ -5937,7 +6052,11 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
         arg++;
         if (arg != 1)
           fprintf(fout, ", ");
-        fprintf(fout, "int a%d/*<%s>*/", arg, regs_r32[j]);
+        if (fp->pp != NULL)
+          fprintf(fout, "%s", fp->pp->arg[arg - 1].type.name);
+        else
+          fprintf(fout, "int");
+        fprintf(fout, " a%d/*<%s>*/", arg, regs_r32[j]);
       }
     }
 
@@ -5945,7 +6064,14 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
       arg++;
       if (arg != 1)
         fprintf(fout, ", ");
-      fprintf(fout, "int a%d", arg);
+      if (fp->pp != NULL) {
+        fprintf(fout, "%s", fp->pp->arg[arg - 1].type.name);
+        if (!fp->pp->arg[arg - 1].type.is_ptr)
+          fprintf(fout, " ");
+      }
+      else
+        fprintf(fout, "int ");
+      fprintf(fout, "a%d", arg);
     }
 
     fprintf(fout, ");\n");
@@ -5976,8 +6102,11 @@ static void output_hdr(FILE *fout)
   for (i = 0; i < hg_var_cnt; i++) {
     var = &hg_vars[i];
 
-    fprintf(fout, "extern %-8s %s;",
-      lmod_c_names[var->lmod], var->name);
+    if (var->is_c_str)
+      fprintf(fout, "extern %-8s %s[];", "char", var->name);
+    else
+      fprintf(fout, "extern %-8s %s;",
+        lmod_c_names[var->lmod], var->name);
 
     if (var->is_seeded)
       fprintf(fout, " // seeded");
@@ -6014,24 +6143,39 @@ static char *my_fgets(char *s, size_t size, FILE *stream)
   return ret;
 }
 
-// '=' needs special treatment..
+// '=' needs special treatment
+// also ' quote
 static char *next_word_s(char *w, size_t wsize, char *s)
 {
-       size_t i;
+  size_t i;
 
-       s = sskip(s);
+  s = sskip(s);
 
-       for (i = 0; i < wsize - 1; i++) {
-               if (s[i] == 0 || my_isblank(s[i]) || (s[i] == '=' && i > 0))
-                       break;
-               w[i] = s[i];
-       }
-       w[i] = 0;
+  i = 0;
+  if (*s == '\'') {
+    w[0] = s[0];
+    for (i = 1; i < wsize - 1; i++) {
+      if (s[i] == 0) {
+        printf("warning: missing closing quote: \"%s\"\n", s);
+        break;
+      }
+      if (s[i] == '\'')
+        break;
+      w[i] = s[i];
+    }
+  }
+
+  for (; i < wsize - 1; i++) {
+    if (s[i] == 0 || my_isblank(s[i]) || (s[i] == '=' && i > 0))
+      break;
+    w[i] = s[i];
+  }
+  w[i] = 0;
 
-       if (s[i] != 0 && !my_isblank(s[i]) && s[i] != '=')
-               printf("warning: '%s' truncated\n", w);
+  if (s[i] != 0 && !my_isblank(s[i]) && s[i] != '=')
+    printf("warning: '%s' truncated\n", w);
 
-       return s + i;
+  return s + i;
 }
 
 static void scan_variables(FILE *fasm)
@@ -6039,9 +6183,10 @@ static void scan_variables(FILE *fasm)
   const struct parsed_proto *pp_c;
   struct scanned_var *var;
   char line[256] = { 0, };
-  char words[2][256];
+  char words[3][256];
   char *p = NULL;
   int wordc;
+  int l;
 
   while (!feof(fasm))
   {
@@ -6095,6 +6240,8 @@ static void scan_variables(FILE *fasm)
 
       if (wordc == 2 && IS(words[1], "ends"))
         break;
+      if (wordc < 2)
+        continue;
 
       if ((hg_var_cnt & 0xff) == 0) {
         hg_vars = realloc(hg_vars, sizeof(hg_vars[0])
@@ -6127,8 +6274,13 @@ static void scan_variables(FILE *fasm)
         var->lmod = OPLM_DWORD;
       else if (IS(words[1], "dw"))
         var->lmod = OPLM_WORD;
-      else if (IS(words[1], "db"))
+      else if (IS(words[1], "db")) {
         var->lmod = OPLM_BYTE;
+        if (wordc >= 3 && (l = strlen(words[2])) > 4) {
+          if (words[2][0] == '\'' && IS(words[2] + l - 2, ",0"))
+            var->is_c_str = 1;
+        }
+      }
       else if (IS(words[1], "dq"))
         var->lmod = OPLM_QWORD;
       //else if (IS(words[1], "dt"))