more stuff storm needs
[ia32rtools.git] / tools / translate.c
index e45ee00..7493761 100644 (file)
@@ -43,6 +43,8 @@ enum op_flags {
   OPF_FARG   = (1 << 11), /* push collected as func arg (no reuse) */
   OPF_EBP_S  = (1 << 12), /* ebp used as scratch here, not BP */
   OPF_DF     = (1 << 13), /* DF flag set */
+  OPF_ATAIL  = (1 << 14), /* tail call with reused arg frame */
+  OPF_32BIT  = (1 << 15), /* 32bit division */
 };
 
 enum op_op {
@@ -75,6 +77,8 @@ enum op_op {
        OP_SAR,
        OP_ROL,
        OP_ROR,
+       OP_RCL,
+       OP_RCR,
        OP_ADC,
        OP_SBB,
        OP_INC,
@@ -455,8 +459,8 @@ static int guess_lmod_from_c_type(enum opr_lenmod *lmod,
 {
   static const char *dword_types[] = {
     "int", "_DWORD", "UINT_PTR", "DWORD",
-    "WPARAM", "LPARAM", "UINT",
-    "HIMC",
+    "WPARAM", "LPARAM", "UINT", "__int32",
+    "LONG", "HIMC",
   };
   static const char *word_types[] = {
     "uint16_t", "int16_t", "_WORD",
@@ -465,7 +469,7 @@ static int guess_lmod_from_c_type(enum opr_lenmod *lmod,
   static const char *byte_types[] = {
     "uint8_t", "int8_t", "char",
     "unsigned __int8", "__int8", "BYTE", "_BYTE",
-    "_UNKNOWN",
+    "CHAR", "_UNKNOWN",
   };
   const char *n;
   int i;
@@ -755,6 +759,8 @@ static const struct {
   { "sar",  OP_SAR,    2, 2, OPF_DATA|OPF_FLAGS },
   { "rol",  OP_ROL,    2, 2, OPF_DATA|OPF_FLAGS },
   { "ror",  OP_ROR,    2, 2, OPF_DATA|OPF_FLAGS },
+  { "rcl",  OP_RCL,    2, 2, OPF_DATA|OPF_FLAGS|OPF_CC },
+  { "rcr",  OP_RCR,    2, 2, OPF_DATA|OPF_FLAGS|OPF_CC },
   { "adc",  OP_ADC,    2, 2, OPF_DATA|OPF_FLAGS|OPF_CC },
   { "sbb",  OP_SBB,    2, 2, OPF_DATA|OPF_FLAGS|OPF_CC },
   { "inc",  OP_INC,    1, 1, OPF_DATA|OPF_FLAGS },
@@ -1422,8 +1428,10 @@ static void stack_frame_access(struct parsed_op *po,
       ferr(po, "bp_arg arg%d/w offset %d and type '%s' is too small\n",
         i + 1, offset, g_func_pp->arg[i].type.name);
     }
-    if (popr->is_ptr && popr->lmod != OPLM_DWORD)
-      ferr(po, "bp_arg arg%d: non-dword ptr access\n", i + 1);
+    // can't check this because msvc likes to reuse
+    // arg space for scratch..
+    //if (popr->is_ptr && popr->lmod != OPLM_DWORD)
+    //  ferr(po, "bp_arg arg%d: non-dword ptr access\n", i + 1);
   }
   else
   {
@@ -1731,6 +1739,8 @@ static enum parsed_flag_op split_cond(struct parsed_op *po,
     *is_inv = 1;
     return PFO_LE;
 
+  case OP_RCL:
+  case OP_RCR:
   case OP_ADC:
   case OP_SBB:
     return PFO_C;
@@ -2305,18 +2315,20 @@ 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, int *adj)
+static int scan_for_esp_adjust(int i, int opcnt, int *adj,
+  int *multipath)
 {
   const struct parsed_proto *pp;
   struct parsed_op *po;
   int first_pop = -1;
-  *adj = 0;
+
+  *adj = *multipath = 0;
 
   for (; i < opcnt; i++) {
     po = &ops[i];
 
     if (g_labels[i][0] != 0)
-      break;
+      *multipath = 1;
 
     if (po->op == OP_ADD && po->operand[0].reg == xSP) {
       if (po->operand[1].type != OPT_CONST)
@@ -2340,6 +2352,10 @@ static int scan_for_esp_adjust(int i, int opcnt, int *adj)
       *adj += lmod_bytes(po, po->operand[0].lmod);
     }
     else if (po->flags & (OPF_JMP|OPF_TAIL)) {
+      if (po->op == OP_JMP && po->btj == NULL) {
+        i = po->bt_i - 1;
+        continue;
+      }
       if (po->op != OP_CALL)
         break;
       if (po->operand[0].type != OPT_LABEL)
@@ -2794,9 +2810,9 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   const char *tmpname;
   enum parsed_flag_op pfo;
   int save_arg_vars = 0;
-  int cmp_result_vars = 0;
+  int cond_vars = 0;
   int need_tmp_var = 0;
-  int need_mul_var = 0;
+  int need_tmp64 = 0;
   int had_decl = 0;
   int label_pending = 0;
   int regmask_save = 0;
@@ -2823,7 +2839,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 
   fprintf(fout, "%s ", g_func_pp->ret_type.name);
   output_pp_attrs(fout, g_func_pp, g_ida_func_attr & IDAFA_NORETURN);
-  fprintf(fout, "%s(", funcn);
+  fprintf(fout, "%s(", g_func_pp->name);
 
   for (i = 0; i < g_func_pp->argc; i++) {
     if (i > 0)
@@ -2909,34 +2925,44 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       for (; i < opcnt; i++)
         if (ops[i].op == OP_RET)
           break;
-      if (i == opcnt && (ops[i - 1].flags & OPF_JMP) && found)
-        break;
+      j = i - 1;
+      if (i == opcnt && (ops[j].flags & OPF_JMP)) {
+        if (found) {
+          if (ops[j].op == OP_JMP && !ops[j].operand[0].had_ds)
+            // probably local branch back..
+            break;
+          if (ops[j].op == OP_CALL)
+            // probably noreturn call..
+            break;
+        }
+        j--;
+      }
 
-      if ((ops[i - 1].op == OP_POP && IS(opr_name(&ops[i - 1], 0), "ebp"))
-        || ops[i - 1].op == OP_LEAVE)
+      if ((ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ebp"))
+          || ops[j].op == OP_LEAVE)
       {
-        ops[i - 1].flags |= OPF_RMD;
+        ops[j].flags |= OPF_RMD;
       }
       else if (!(g_ida_func_attr & IDAFA_NORETURN))
-        ferr(&ops[i - 1], "'pop ebp' expected\n");
+        ferr(&ops[j], "'pop ebp' expected\n");
 
       if (g_stack_fsz != 0) {
-        if (ops[i - 2].op == OP_MOV
-            && IS(opr_name(&ops[i - 2], 0), "esp")
-            && IS(opr_name(&ops[i - 2], 1), "ebp"))
+        if (ops[j - 1].op == OP_MOV
+            && IS(opr_name(&ops[j - 1], 0), "esp")
+            && IS(opr_name(&ops[j - 1], 1), "ebp"))
         {
-          ops[i - 2].flags |= OPF_RMD;
+          ops[j - 1].flags |= OPF_RMD;
         }
-        else if (ops[i - 1].op != OP_LEAVE
+        else if (ops[j].op != OP_LEAVE
           && !(g_ida_func_attr & IDAFA_NORETURN))
         {
-          ferr(&ops[i - 2], "esp restore expected\n");
+          ferr(&ops[j - 1], "esp restore expected\n");
         }
 
-        if (ecx_push && ops[i - 3].op == OP_POP
-          && IS(opr_name(&ops[i - 3], 0), "ecx"))
+        if (ecx_push && ops[j - 2].op == OP_POP
+          && IS(opr_name(&ops[j - 2], 0), "ecx"))
         {
-          ferr(&ops[i - 3], "unexpected ecx pop\n");
+          ferr(&ops[j - 2], "unexpected ecx pop\n");
         }
       }
 
@@ -3083,6 +3109,8 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 tailcall:
     po->op = OP_CALL;
     po->flags |= OPF_TAIL;
+    if (i > 0 && ops[i - 1].op == OP_POP)
+      po->flags |= OPF_ATAIL;
     i--; // reprocess
   }
 
@@ -3117,7 +3145,7 @@ tailcall:
           pp = calloc(1, sizeof(*pp));
           my_assert_not(pp, NULL);
           pp->is_fptr = 1;
-          ret = scan_for_esp_adjust(i + 1, opcnt, &j);
+          ret = scan_for_esp_adjust(i + 1, opcnt, &j, &l);
           if (ret < 0) {
             if (!g_allow_regfunc)
               ferr(po, "non-__cdecl indirect call unhandled yet\n");
@@ -3138,7 +3166,7 @@ tailcall:
       // look for and make use of esp adjust
       ret = -1;
       if (!pp->is_stdcall && pp->argc_stack > 0)
-        ret = scan_for_esp_adjust(i + 1, opcnt, &j);
+        ret = scan_for_esp_adjust(i + 1, opcnt, &j, &l);
       if (ret >= 0) {
         if (pp->is_vararg) {
           if (j / 4 < pp->argc_stack)
@@ -3168,7 +3196,7 @@ tailcall:
             ret++;
           }
         }
-        else {
+        else if (!l) {
           // a bit of a hack, but deals with use of
           // single adj for multiple calls
           ops[ret].operand[1].val -= j;
@@ -3178,14 +3206,14 @@ tailcall:
         ferr(po, "missing esp_adjust for vararg func '%s'\n",
           pp->name);
 
-      if (!pp->is_unresolved) {
+      if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) {
         // since we know the args, collect them
         collect_call_args(po, i, pp, &regmask, &save_arg_vars,
           i + opcnt * 2);
       }
 
       if (strstr(pp->ret_type.name, "int64"))
-        need_mul_var = 1;
+        need_tmp64 = 1;
     }
   }
 
@@ -3305,12 +3333,9 @@ tailcall:
           if (branched || scan_for_mod(tmp_op, setters[j] + 1, i, 0) >= 0)
             pfomask = 1 << pfo;
         }
-        else if (tmp_op->op == OP_SCAS) {
+        else if (tmp_op->op == OP_CMPS || tmp_op->op == OP_SCAS) {
           pfomask = 1 << pfo;
         }
-        else if (tmp_op->op == OP_CMPS) {
-          pfomask = 1 << PFO_Z;
-        }
         else {
           // see if we'll be able to handle based on op result
           if ((tmp_op->op != OP_AND && tmp_op->op != OP_OR
@@ -3318,28 +3343,29 @@ tailcall:
               || branched
               || scan_for_mod_opr0(tmp_op, setters[j] + 1, i) >= 0)
             pfomask = 1 << pfo;
+
+          if (tmp_op->op == OP_ADD && pfo == PFO_C)
+            need_tmp64 = 1;
         }
         if (pfomask) {
           tmp_op->pfomask |= pfomask;
-          cmp_result_vars |= pfomask;
+          cond_vars |= pfomask;
         }
         // note: may overwrite, currently not a problem
         po->datap = tmp_op;
       }
 
-      if (po->op == OP_ADC || po->op == OP_SBB)
-        cmp_result_vars |= 1 << PFO_C;
+      if (po->op == OP_RCL || po->op == OP_RCR
+       || po->op == OP_ADC || po->op == OP_SBB)
+        cond_vars |= 1 << PFO_C;
     }
     else if (po->op == OP_CMPS || po->op == OP_SCAS) {
-      cmp_result_vars |= 1 << PFO_Z;
+      cond_vars |= 1 << PFO_Z;
     }
     else if (po->op == OP_MUL
       || (po->op == OP_IMUL && po->operand_cnt == 1))
     {
-      need_mul_var = 1;
-    }
-    else if (po->op == OP_XCHG) {
-      need_tmp_var = 1;
+      need_tmp64 = 1;
     }
     else if (po->op == OP_CALL) {
       pp = po->datap;
@@ -3436,6 +3462,21 @@ tailcall:
     }
     else if (po->op == OP_RET && !IS(g_func_pp->ret_type.name, "void"))
       regmask |= 1 << xAX;
+    else if (po->op == OP_DIV || po->op == OP_IDIV) {
+      // 32bit division is common, look for it
+      if (po->op == OP_DIV)
+        ret = scan_for_reg_clear(i, xDX);
+      else
+        ret = scan_for_cdq_edx(i);
+      if (ret >= 0)
+        po->flags |= OPF_32BIT;
+      else
+        need_tmp64 = 1;
+    }
+
+    if (po->op == OP_RCL || po->op == OP_RCR || po->op == OP_XCHG) {
+      need_tmp_var = 1;
+    }
   }
 
   // pass4:
@@ -3536,9 +3577,9 @@ tailcall:
     }
   }
 
-  if (cmp_result_vars) {
+  if (cond_vars) {
     for (i = 0; i < 8; i++) {
-      if (cmp_result_vars & (1 << i)) {
+      if (cond_vars & (1 << i)) {
         fprintf(fout, "  u32 cond_%s;\n", parsed_flag_op_names[i]);
         had_decl = 1;
       }
@@ -3550,8 +3591,8 @@ tailcall:
     had_decl = 1;
   }
 
-  if (need_mul_var) {
-    fprintf(fout, "  u64 mul_tmp;\n");
+  if (need_tmp64) {
+    fprintf(fout, "  u64 tmp64;\n");
     had_decl = 1;
   }
 
@@ -3627,7 +3668,9 @@ tailcall:
       if (po->flags & OPF_JMP) {
         fprintf(fout, "  if %s\n", buf1);
       }
-      else if (po->op == OP_ADC || po->op == OP_SBB) {
+      else if (po->op == OP_RCL || po->op == OP_RCR
+               || po->op == OP_ADC || po->op == OP_SBB)
+      {
         if (is_delayed)
           fprintf(fout, "  cond_%s = %s;\n",
             parsed_flag_op_names[pfo], buf1);
@@ -3643,6 +3686,27 @@ tailcall:
 
     pfomask = po->pfomask;
 
+    if (po->flags & (OPF_REPZ|OPF_REPNZ)) {
+      // 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:
@@ -3762,13 +3826,20 @@ tailcall:
         l = (po->flags & OPF_DF) ? '-' : '+';
         if (po->flags & OPF_REP) {
           fprintf(fout,
-            "  for (; ecx != 0; ecx--, edi %c= %d, esi %c= %d)\n",
+            "  for (; ecx != 0; ecx--, edi %c= %d, esi %c= %d) {\n",
             l, j, l, j);
+          if (pfomask & (1 << PFO_C)) {
+            // ugh..
+            fprintf(fout,
+            "    cond_c = %sedi < %sesi;\n", buf1, buf1);
+            pfomask &= ~(1 << PFO_C);
+          }
           fprintf(fout,
             "    if ((cond_z = (%sedi == %sesi)) %s 0)\n",
               buf1, buf1, (po->flags & OPF_REPZ) ? "==" : "!=");
           fprintf(fout,
-            "      break;");
+            "      break;\n"
+            "  }");
           snprintf(g_comment, sizeof(g_comment), "rep%s cmps",
             (po->flags & OPF_REPZ) ? "e" : "ne");
         }
@@ -3814,8 +3885,6 @@ tailcall:
         break;
 
       // arithmetic w/flags
-      case OP_ADD:
-      case OP_SUB:
       case OP_AND:
       case OP_OR:
         propagate_lmod(po, &po->operand[0], &po->operand[1]);
@@ -3845,8 +3914,8 @@ tailcall:
                 j = l - j;
               else
                 j -= 1;
-              fprintf(fout, "  cond_c = (%s & 0x%02x) ? 1 : 0;\n",
-                buf1, 1 << j);
+              fprintf(fout, "  cond_c = (%s >> %d) & 1;\n",
+                buf1, j);
             }
             else
               ferr(po, "zero shift?\n");
@@ -3893,11 +3962,51 @@ tailcall:
         delayed_flag_op = NULL;
         break;
 
+      case OP_RCL:
+      case OP_RCR:
+        assert_operand_cnt(2);
+        out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+        l = lmod_bytes(po, po->operand[0].lmod) * 8;
+        if (po->operand[1].type == OPT_CONST) {
+          j = po->operand[1].val % l;
+          if (j == 0)
+            ferr(po, "zero rotate\n");
+          fprintf(fout, "  tmp = (%s >> %d) & 1;\n",
+            buf1, (po->op == OP_RCL) ? (l - j) : (j - 1));
+          if (po->op == OP_RCL) {
+            fprintf(fout,
+              "  %s = (%s << %d) | (cond_c << %d)",
+              buf1, buf1, j, j - 1);
+            if (j != 1)
+              fprintf(fout, " | (%s >> %d)", buf1, l + 1 - j);
+          }
+          else {
+            fprintf(fout,
+              "  %s = (%s >> %d) | (cond_c << %d)",
+              buf1, buf1, j, l - j);
+            if (j != 1)
+              fprintf(fout, " | (%s << %d)", buf1, l + 1 - j);
+          }
+          fprintf(fout, ";\n");
+          fprintf(fout, "  cond_c = tmp;");
+        }
+        else
+          ferr(po, "TODO\n");
+        strcpy(g_comment, (po->op == OP_RCL) ? "rcl" : "rcr");
+        output_std_flags(fout, po, &pfomask, buf1);
+        last_arith_dst = &po->operand[0];
+        delayed_flag_op = NULL;
+        break;
+
       case OP_XOR:
         assert_operand_cnt(2);
         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);
+          }
           fprintf(fout, "  %s = 0;",
             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
           last_arith_dst = &po->operand[0];
@@ -3906,6 +4015,36 @@ tailcall:
         }
         goto dualop_arith;
 
+      case OP_ADD:
+        assert_operand_cnt(2);
+        propagate_lmod(po, &po->operand[0], &po->operand[1]);
+        if (pfomask & (1 << PFO_C)) {
+          fprintf(fout, "  tmp64 = (u64)%s + %s;\n",
+            out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]),
+            out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+          fprintf(fout, "  cond_c = tmp64 >> 32;\n");
+          fprintf(fout, "  %s = (u32)tmp64;",
+            out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
+          strcat(g_comment, "add64");
+          pfomask &= ~(1 << PFO_C);
+          output_std_flags(fout, po, &pfomask, buf1);
+          last_arith_dst = &po->operand[0];
+          delayed_flag_op = NULL;
+          break;
+        }
+        goto dualop_arith;
+
+      case OP_SUB:
+        assert_operand_cnt(2);
+        propagate_lmod(po, &po->operand[0], &po->operand[1]);
+        if (pfomask & (1 << PFO_C)) {
+          fprintf(fout, "  cond_c = %s < %s;\n",
+            out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]),
+            out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+          pfomask &= ~(1 << PFO_C);
+        }
+        goto dualop_arith;
+
       case OP_ADC:
       case OP_SBB:
         assert_operand_cnt(2);
@@ -3968,10 +4107,10 @@ tailcall:
       case OP_MUL:
         assert_operand_cnt(1);
         strcpy(buf1, po->op == OP_IMUL ? "(s64)(s32)" : "(u64)");
-        fprintf(fout, "  mul_tmp = %seax * %s%s;\n", buf1, buf1,
+        fprintf(fout, "  tmp64 = %seax * %s%s;\n", buf1, buf1,
           out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]));
-        fprintf(fout, "  edx = mul_tmp >> 32;\n");
-        fprintf(fout, "  eax = mul_tmp;");
+        fprintf(fout, "  edx = tmp64 >> 32;\n");
+        fprintf(fout, "  eax = tmp64;");
         last_arith_dst = NULL;
         delayed_flag_op = NULL;
         break;
@@ -3982,20 +4121,32 @@ tailcall:
         if (po->operand[0].lmod != OPLM_DWORD)
           ferr(po, "unhandled lmod %d\n", po->operand[0].lmod);
 
-        // 32bit division is common, look for it
-        if (po->op == OP_DIV)
-          ret = scan_for_reg_clear(i, xDX);
-        else
-          ret = scan_for_cdq_edx(i);
-        if (ret >= 0) {
-          out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
-          strcpy(buf2, lmod_cast(po, po->operand[0].lmod,
-            po->op == OP_IDIV));
-          fprintf(fout, "  edx = %seax %% %s%s;\n", buf2, buf2, buf1);
-          fprintf(fout, "  eax = %seax / %s%s;", buf2, buf2, buf1);
+        out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
+        strcpy(buf2, lmod_cast(po, po->operand[0].lmod,
+          po->op == OP_IDIV));
+        switch (po->operand[0].lmod) {
+        case OPLM_DWORD:
+          if (po->flags & OPF_32BIT)
+            snprintf(buf3, sizeof(buf3), "%seax", buf2);
+          else {
+            fprintf(fout, "  tmp64 = ((u64)edx << 32) | eax;\n");
+            snprintf(buf3, sizeof(buf3), "%stmp64",
+              (po->op == OP_IDIV) ? "(s64)" : "");
+          }
+          if (po->operand[0].type == OPT_REG
+            && po->operand[0].reg == xDX)
+          {
+            fprintf(fout, "  eax = %s / %s%s;", buf3, buf2, buf1);
+            fprintf(fout, "  edx = %s %% %s%s;\n", buf3, buf2, buf1);
+          }
+          else {
+            fprintf(fout, "  edx = %s %% %s%s;\n", buf3, buf2, buf1);
+            fprintf(fout, "  eax = %s / %s%s;", buf3, buf2, buf1);
+          }
+          break;
+        default:
+          ferr(po, "unhandled division type\n");
         }
-        else
-          ferr(po, "TODO 64bit divident\n");
         last_arith_dst = NULL;
         delayed_flag_op = NULL;
         break;
@@ -4066,7 +4217,7 @@ tailcall:
         if (strstr(pp->ret_type.name, "int64")) {
           if (po->flags & OPF_TAIL)
             ferr(po, "int64 and tail?\n");
-          fprintf(fout, "mul_tmp = ");
+          fprintf(fout, "tmp64 = ");
         }
         else if (!IS(pp->ret_type.name, "void")) {
           if (po->flags & OPF_TAIL) {
@@ -4088,38 +4239,68 @@ tailcall:
         fprintf(fout, "%s%s(", pp->name,
           pp->has_structarg ? "_sa" : "");
 
-        for (arg = 0; arg < pp->argc; arg++) {
-          if (arg > 0)
-            fprintf(fout, ", ");
+        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");
 
-          cast[0] = 0;
-          if (pp->arg[arg].type.is_ptr)
-            snprintf(cast, sizeof(cast), "(%s)", pp->arg[arg].type.name);
+          for (arg = j = 0; arg < pp->argc; arg++) {
+            if (arg > 0)
+              fprintf(fout, ", ");
 
-          if (pp->arg[arg].reg != NULL) {
-            fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
-            continue;
-          }
+            cast[0] = 0;
+            if (pp->arg[arg].type.is_ptr)
+              snprintf(cast, sizeof(cast), "(%s)",
+                pp->arg[arg].type.name);
 
-          // stack arg
-          tmp_op = pp->arg[arg].datap;
-          if (tmp_op == NULL)
-            ferr(po, "parsed_op missing for arg%d\n", arg);
-          if (tmp_op->argnum != 0) {
-            fprintf(fout, "%ss_a%d", cast, tmp_op->argnum);
+            if (pp->arg[arg].reg != NULL) {
+              fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
+              continue;
+            }
+            // stack arg
+            for (; j < g_func_pp->argc; j++)
+              if (g_func_pp->arg[j].reg == NULL)
+                break;
+            fprintf(fout, "%sa%d", cast, j + 1);
+            j++;
           }
-          else {
-            fprintf(fout, "%s",
-              out_src_opr(buf1, sizeof(buf1),
-                tmp_op, &tmp_op->operand[0], cast, 0));
+        }
+        else {
+          for (arg = 0; arg < pp->argc; arg++) {
+            if (arg > 0)
+              fprintf(fout, ", ");
+
+            cast[0] = 0;
+            if (pp->arg[arg].type.is_ptr)
+              snprintf(cast, sizeof(cast), "(%s)",
+                pp->arg[arg].type.name);
+
+            if (pp->arg[arg].reg != NULL) {
+              fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
+              continue;
+            }
+
+            // stack arg
+            tmp_op = pp->arg[arg].datap;
+            if (tmp_op == NULL)
+              ferr(po, "parsed_op missing for arg%d\n", arg);
+            if (tmp_op->argnum != 0) {
+              fprintf(fout, "%ss_a%d", cast, tmp_op->argnum);
+            }
+            else {
+              fprintf(fout, "%s",
+                out_src_opr(buf1, sizeof(buf1),
+                  tmp_op, &tmp_op->operand[0], cast, 0));
+            }
           }
         }
         fprintf(fout, ");");
 
         if (strstr(pp->ret_type.name, "int64")) {
           fprintf(fout, "\n");
-          fprintf(fout, "  edx = mul_tmp >> 32;\n");
-          fprintf(fout, "  eax = mul_tmp;");
+          fprintf(fout, "  edx = tmp64 >> 32;\n");
+          fprintf(fout, "  eax = tmp64;");
         }
 
         if (pp->is_unresolved) {
@@ -4152,6 +4333,8 @@ tailcall:
         }
         if (pp->is_noreturn)
           strcat(g_comment, " noreturn");
+        if ((po->flags & OPF_ATAIL) && pp->argc_stack > 0)
+          strcat(g_comment, " argframe");
         delayed_flag_op = NULL;
         last_arith_dst = NULL;
         break;
@@ -4168,6 +4351,8 @@ tailcall:
           fprintf(fout, "  return (%s)eax;",
             g_func_pp->ret_type.name);
         }
+        else if (IS(g_func_pp->ret_type.name, "__int64"))
+          fprintf(fout, "  return ((u64)edx << 32) | eax;");
         else
           fprintf(fout, "  return eax;");