improved type handling and cast generation
authornotaz <notasas@gmail.com>
Wed, 25 Dec 2013 02:35:36 +0000 (04:35 +0200)
committernotaz <notasas@gmail.com>
Wed, 25 Dec 2013 02:35:36 +0000 (04:35 +0200)
tools/cmpmrg_text.c
tools/protoparse.h
tools/translate.c

index 5a0d296..987443e 100644 (file)
@@ -287,11 +287,11 @@ static int handle_pad(uint8_t *d_obj, uint8_t *d_exe, int maxlen)
 {
        static const uint8_t p7[7] = { 0x8d, 0xa4, 0x24, 0x00, 0x00, 0x00, 0x00 };
        static const uint8_t p6[6] = { 0x8d, 0x9b, 0x00, 0x00, 0x00, 0x00 };
-       static const uint8_t p5[5] = { 0x05, 0x00, 0x00, 0x00, 0x00 };
-       static const uint8_t p4[4] = { 0x8d, 0x64, 0x24, 0x00 };
-       static const uint8_t p3[3] = { 0x8d, 0x49, 0x00 };
-       static const uint8_t p2[2] = { 0x8b, 0xff };
-       static const uint8_t p1[1] = { 0x90 };
+       static const uint8_t p5[5] = { 0x05, 0x00, 0x00, 0x00, 0x00 }; // add eax, 0
+       static const uint8_t p4[4] = { 0x8d, 0x64, 0x24, 0x00 }; // lea
+       static const uint8_t p3[3] = { 0x8d, 0x49, 0x00 }; // lea ecx, [ecx]
+       static const uint8_t p2[2] = { 0x8b, 0xff }; // mov edi, edi
+       static const uint8_t p1[1] = { 0x90 }; // nop
        int len;
        int i;
 
@@ -494,14 +494,14 @@ int main(int argc, char *argv[])
 
        f_obj = fopen(argv[1], "r+b");
        if (f_obj == NULL) {
-               fprintf(stderr, "%s", argv[1]);
+               fprintf(stderr, "%s", argv[1]);
                perror("");
                return 1;
        }
 
        f_exe = fopen(argv[2], "r");
        if (f_exe == NULL) {
-               fprintf(stderr, "%s", argv[2]);
+               fprintf(stderr, "%s", argv[2]);
                perror("");
                return 1;
        }
index 3a51438..c7d41dc 100644 (file)
@@ -1,16 +1,25 @@
 
 struct parsed_proto;
 
+struct parsed_type {
+       char *name;
+       unsigned int is_array:1;
+       unsigned int is_ptr:1;
+};
+
 struct parsed_proto_arg {
        char *reg;
-       char *type;
+       struct parsed_type type;
        struct parsed_proto *fptr;
        void *datap;
 };
 
 struct parsed_proto {
        char name[256];
-       char *ret_type;
+       union {
+               struct parsed_type ret_type;
+               struct parsed_type type;
+       };
        struct parsed_proto_arg arg[16];
        int argc;
        int argc_stack;
@@ -19,7 +28,6 @@ struct parsed_proto {
        unsigned int is_stdcall:1;
        unsigned int is_vararg:1;
        unsigned int is_fptr:1;
-       unsigned int is_array:1;
 };
 
 static const char *hdrfn;
@@ -108,37 +116,31 @@ static const char *known_type_mod[] = {
        "const",
        "signed",
        "unsigned",
+       "struct",
+       "enum",
 };
 
-static const char *known_types[] = {
-       "void *",
-       "char *",
-       "FILE *",
-       "int *",
-       "__int8 *",
-       "__int16 *",
-       "char",
-       "__int8",
-       "__int16",
-       "__int32",
-       "int",
-       "bool",
-       "void",
-       "BYTE",
-       "WORD",
-       "BOOL",
-       "DWORD",
-       "_DWORD",
-       "HMODULE",
+static const char *known_ptr_types[] = {
        "HANDLE",
+       "HMODULE",
+       "HINSTANCE",
        "HWND",
-       "LANGID",
-       "LPCSTR",
-       "size_t",
+       "HDC",
+       "HGDIOBJ",
+       "PLONG",
+       "PDWORD",
+       "PVOID",
+       "PCVOID",
+};
+
+static const char *ignored_keywords[] = {
+       "extern",
+       "WINBASEAPI",
+       "WINUSERAPI",
 };
 
 // returns ptr to char after type ends
-static const char *typecmp(const char *n, const char *t)
+static int typecmp(const char *n, const char *t)
 {
        for (; *t != 0; n++, t++) {
                while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
@@ -146,20 +148,17 @@ static const char *typecmp(const char *n, const char *t)
                while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
                        t++;
                if (*n != *t)
-                       return NULL;
+                       return *n - *t;
        }
 
-       return n;
+       return 0;
 }
 
-static char *check_type(const char *name, int *len_out)
+static const char *skip_type_mod(const char *n)
 {
-       const char *n = name, *n1;
        int len;
        int i;
 
-       *len_out = 0;
-
        for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) {
                len = strlen(known_type_mod[i]);
                if (strncmp(n, known_type_mod[i], len) != 0)
@@ -171,16 +170,47 @@ static char *check_type(const char *name, int *len_out)
                i = 0;
        }
 
-       for (i = 0; i < ARRAY_SIZE(known_types); i++) {
-               n1 = typecmp(n, known_types[i]);
-               if (n1 == NULL)
+       return n;
+}
+
+static int check_type(const char *name, struct parsed_type *type)
+{
+       const char *n, *n1;
+       int ret = -1;
+       int i;
+
+       n = skip_type_mod(name);
+
+       for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
+               if (typecmp(n, known_ptr_types[i]))
                        continue;
 
-               *len_out = n1 - name;
-               return strndup(name, *len_out);
+               type->is_ptr = 1;
+               break;
        }
 
-       return NULL;
+       if (n[0] == 'L' && n[1] == 'P')
+               type->is_ptr = 1;
+
+       // assume single word
+       while (!my_isblank(*n) && !my_issep(*n))
+               n++;
+
+       while (1) {
+               n1 = n;
+               while (my_isblank(*n))
+                       n++;
+               if (*n == '*') {
+                       type->is_ptr = 1;
+                       n++;
+                       continue;
+               }
+               break;
+       }
+
+       ret = n1 - name;
+       type->name = strndup(name, ret);
+       return ret;
 }
 
 /* args are always expanded to 32bit */
@@ -208,11 +238,10 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp)
        char regparm[16];
        char buf[256];
        char cconv[32];
-       char *kt;
        int xarg = 0;
        char *p, *p1;
+       int i, l;
        int ret;
-       int i;
 
        p = sskip(protostr);
        if (p[0] == '/' && p[1] == '/') {
@@ -227,18 +256,18 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp)
                        p1[0] = p1[1] = ' ';
        }
 
-       if (strncmp(p, "extern ", 7) == 0)
-               p = sskip(p + 7);
-       if (strncmp(p, "WINBASEAPI ", 11) == 0)
-               p = sskip(p + 11);
+       for (i = 0; i < ARRAY_SIZE(ignored_keywords); i++) {
+               l = strlen(ignored_keywords[i]);
+               if (!strncmp(p, ignored_keywords[i], l) && my_isblank(p[l]))
+                       p = sskip(p + l + 1);
+       }
 
-       kt = check_type(p, &ret);
-       if (kt == NULL) {
+       ret = check_type(p, &pp->ret_type);
+       if (ret <= 0) {
                printf("%s:%d:%zd: unhandled return in '%s'\n",
                        hdrfn, hdrfline, (p - protostr) + 1, protostr);
                return -1;
        }
-       pp->ret_type = kt;
        p = sskip(p + ret);
 
        if (!strchr(p, ')')) {
@@ -254,7 +283,7 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp)
                p1 = strchr(p, ']');
                if (p1 != NULL) {
                        p = p1 + 1;
-                       pp->is_array = 1;
+                       pp->ret_type.is_array = 1;
                }
                return p - protostr;
        }
@@ -371,13 +400,12 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp)
                xarg++;
 
                p1 = p;
-               kt = check_type(p, &ret);
-               if (kt == NULL) {
+               ret = check_type(p, &arg->type);
+               if (ret <= 0) {
                        printf("%s:%d:%zd: unhandled type for arg%d\n",
                                hdrfn, hdrfline, (p - protostr) + 1, xarg);
                        return -1;
                }
-               arg->type = kt;
                p = sskip(p + ret);
 
                if (*p == '(') {
@@ -390,7 +418,8 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp)
                                return -1;
                        }
                        // we'll treat it as void * for non-calls
-                       arg->type = "void *";
+                       arg->type.name = "void *";
+                       arg->type.is_ptr = 1;
 
                        p = p1 + ret;
                }
@@ -471,11 +500,11 @@ static void proto_release(struct parsed_proto *pp)
        for (i = 0; i < pp->argc; i++) {
                if (pp->arg[i].reg != NULL)
                        free(pp->arg[i].reg);
-               if (pp->arg[i].type != NULL)
-                       free(pp->arg[i].type);
+               if (pp->arg[i].type.name != NULL)
+                       free(pp->arg[i].type.name);
                if (pp->arg[i].fptr != NULL)
                        free(pp->arg[i].fptr);
        }
-       if (pp->ret_type != NULL)
-               free(pp->ret_type);
+       if (pp->ret_type.name != NULL)
+               free(pp->ret_type.name);
 }
index 8c52603..7da8be5 100644 (file)
@@ -457,11 +457,9 @@ static int guess_lmod_from_name(struct parsed_opr *opr)
   return 0;
 }
 
-static int guess_lmod_from_c_type(struct parsed_opr *opr, const char *c_type)
+static int guess_lmod_from_c_type(enum opr_lenmod *lmod,
+  const struct parsed_type *c_type)
 {
-  static const char *ptr_types[] = {
-    "LPCSTR",
-  };
   static const char *dword_types[] = {
     "int", "_DWORD", "DWORD", "HANDLE", "HWND", "HMODULE",
   };
@@ -471,44 +469,37 @@ static int guess_lmod_from_c_type(struct parsed_opr *opr, const char *c_type)
   static const char *byte_types[] = {
     "char", "__int8", "unsigned __int8", "BYTE",
   };
+  const char *n;
   int i;
 
-  if (strchr(c_type, '*')) {
-    opr->lmod = OPLM_DWORD;
-    opr->is_ptr = 1;
+  if (c_type->is_ptr) {
+    *lmod = OPLM_DWORD;
     return 1;
   }
 
-  for (i = 0; i < ARRAY_SIZE(dword_types); i++) {
-    if (IS(c_type, dword_types[i])) {
-      opr->lmod = OPLM_DWORD;
-      return 1;
-    }
-  }
+  n = skip_type_mod(c_type->name);
 
-  for (i = 0; i < ARRAY_SIZE(ptr_types); i++) {
-    if (IS(c_type, ptr_types[i])) {
-      opr->lmod = OPLM_DWORD;
-      opr->is_ptr = 1;
+  for (i = 0; i < ARRAY_SIZE(dword_types); i++) {
+    if (IS(n, dword_types[i])) {
+      *lmod = OPLM_DWORD;
       return 1;
     }
   }
 
   for (i = 0; i < ARRAY_SIZE(word_types); i++) {
-    if (IS(c_type, word_types[i])) {
-      opr->lmod = OPLM_WORD;
+    if (IS(n, word_types[i])) {
+      *lmod = OPLM_WORD;
       return 1;
     }
   }
 
   for (i = 0; i < ARRAY_SIZE(byte_types); i++) {
-    if (IS(c_type, byte_types[i])) {
-      opr->lmod = OPLM_BYTE;
+    if (IS(n, byte_types[i])) {
+      *lmod = OPLM_BYTE;
       return 1;
     }
   }
 
-  anote("unhandled C type '%s' for '%s'\n", c_type, opr->name);
   return 0;
 }
 
@@ -678,9 +669,12 @@ static int parse_operand(struct parsed_opr *opr,
       opr->lmod = OPLM_DWORD;
       opr->is_ptr = 1;
     }
-    else if (opr->lmod == OPLM_UNSPEC)
-      guess_lmod_from_c_type(opr, pp.ret_type);
-    opr->is_array = pp.is_array;
+    else if (opr->lmod == OPLM_UNSPEC) {
+      if (!guess_lmod_from_c_type(&opr->lmod, &pp.type))
+        anote("unhandled C type '%s' for '%s'\n", pp.type.name, opr->name);
+    }
+    opr->is_ptr = pp.type.is_ptr;
+    opr->is_array = pp.type.is_array;
   }
   proto_release(&pp);
 
@@ -954,6 +948,15 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc)
       op->operand[0].lmod = OPLM_DWORD;
     break;
 
+  // alignment
+  case OP_MOV:
+    if (op->operand[0].type == OPT_REG && op->operand[1].type == OPT_REG
+     && op->operand[0].reg == xDI && op->operand[1].reg == xDI)
+    {
+      op->flags |= OPF_RMD;
+    }
+    break;
+
   default:
     break;
   }
@@ -1142,8 +1145,9 @@ static struct parsed_equ *equ_find(struct parsed_op *po, const char *name,
 
 static void stack_frame_access(struct parsed_op *po,
   enum opr_lenmod lmod, char *buf, size_t buf_size,
-  const char *name, int is_src, int is_lea)
+  const char *name, const char *cast, int is_src, int is_lea)
 {
+  enum opr_lenmod tmp_lmod = OPLM_UNSPEC;
   const char *prefix = "";
   char ofs_reg[16] = { 0, };
   struct parsed_equ *eq;
@@ -1209,7 +1213,11 @@ static void stack_frame_access(struct parsed_op *po,
     case OPLM_DWORD:
       if (offset & 3)
         ferr(po, "unaligned arg access\n");
-      snprintf(buf, buf_size, "%sa%d", is_src ? "(u32)" : "", i + 1);
+      if (cast[0])
+        prefix = cast;
+      else if (is_src)
+        prefix = "(u32)";
+      snprintf(buf, buf_size, "%sa%d", prefix, i + 1);
       break;
 
     default:
@@ -1217,13 +1225,10 @@ static void stack_frame_access(struct parsed_op *po,
     }
 
     // common problem
-    if ((offset & 3)
-        && (strstr(g_func_pp.arg[i].type, "int8")
-         || strstr(g_func_pp.arg[i].type, "int16")))
-    {
+    guess_lmod_from_c_type(&tmp_lmod, &g_func_pp.arg[i].type);
+    if ((offset & 3) && tmp_lmod != OPLM_DWORD)
       ferr(po, "bp_arg arg/w offset %d and type '%s'\n",
-        offset, g_func_pp.arg[i].type);
-    }
+        offset, g_func_pp.arg[i].type.name);
   }
   else {
     if (g_stack_fsz == 0)
@@ -1235,6 +1240,8 @@ static void stack_frame_access(struct parsed_op *po,
 
     if (is_lea)
       prefix = "(u32)&";
+    else
+      prefix = cast;
 
     switch (lmod)
     {
@@ -1275,13 +1282,23 @@ static void stack_frame_access(struct parsed_op *po,
   }
 }
 
+static void check_label_read_ref(struct parsed_op *po, const char *name)
+{
+  if (IS_START(name, "sub_"))
+    ferr(po, "func reference?\n");
+}
+
 static char *out_src_opr(char *buf, size_t buf_size,
-       struct parsed_op *po, struct parsed_opr *popr, int is_lea)
+       struct parsed_op *po, struct parsed_opr *popr, const char *cast,
+  int is_lea)
 {
   char tmp1[256], tmp2[256];
   char expr[256];
   int ret;
 
+  if (cast == NULL)
+    cast = "";
+
   switch (popr->type) {
   case OPT_REG:
     if (is_lea)
@@ -1289,7 +1306,7 @@ static char *out_src_opr(char *buf, size_t buf_size,
 
     switch (popr->lmod) {
     case OPLM_DWORD:
-      snprintf(buf, buf_size, "%s", opr_reg_p(po, popr));
+      snprintf(buf, buf_size, "%s%s", cast, opr_reg_p(po, popr));
       break;
     case OPLM_WORD:
       snprintf(buf, buf_size, "(u16)%s", opr_reg_p(po, popr));
@@ -1308,7 +1325,7 @@ static char *out_src_opr(char *buf, size_t buf_size,
   case OPT_REGMEM:
     if (parse_stack_el(popr->name, NULL)) {
       stack_frame_access(po, popr->lmod, buf, buf_size,
-        popr->name, 1, is_lea);
+        popr->name, cast, 1, is_lea);
       break;
     }
 
@@ -1327,31 +1344,38 @@ static char *out_src_opr(char *buf, size_t buf_size,
       break;
     }
 
-    snprintf(buf, buf_size, "%s(%s)",
-      lmod_cast_u_ptr(po, popr->lmod), expr);
+    if (cast[0] == 0)
+      cast = lmod_cast_u_ptr(po, popr->lmod);
+    snprintf(buf, buf_size, "%s(%s)", cast, expr);
     break;
 
   case OPT_LABEL:
+    check_label_read_ref(po, popr->name);
+    if (cast[0] == 0 && popr->is_ptr)
+      cast = "(u32)";
     if (is_lea)
       snprintf(buf, buf_size, "(u32)&%s", popr->name);
     else
-      snprintf(buf, buf_size, "%s%s%s",
-        popr->is_ptr ? "(u32)" : "",
-        popr->name,
+      snprintf(buf, buf_size, "%s%s%s", cast, popr->name,
         popr->is_array ? "[0]" : "");
     break;
 
   case OPT_OFFSET:
+    check_label_read_ref(po, popr->name);
+    if (cast[0] == 0)
+      cast = "(u32)";
     if (is_lea)
       ferr(po, "lea an offset?\n");
-    snprintf(buf, buf_size, "(u32)&%s", popr->name);
+    snprintf(buf, buf_size, "%s&%s", cast, popr->name);
     break;
 
   case OPT_CONST:
     if (is_lea)
       ferr(po, "lea from const?\n");
 
-    printf_number(buf, buf_size, popr->val);
+    snprintf(buf, buf_size, "%s", cast);
+    ret = strlen(buf);
+    printf_number(buf + ret, buf_size - ret, popr->val);
     break;
 
   default:
@@ -1389,11 +1413,11 @@ static char *out_dst_opr(char *buf, size_t buf_size,
   case OPT_REGMEM:
     if (parse_stack_el(popr->name, NULL)) {
       stack_frame_access(po, popr->lmod, buf, buf_size,
-        popr->name, 0, 0);
+        popr->name, "", 0, 0);
       break;
     }
 
-    return out_src_opr(buf, buf_size, po, popr, 0);
+    return out_src_opr(buf, buf_size, po, popr, NULL, 0);
 
   case OPT_LABEL:
     snprintf(buf, buf_size, "%s%s", popr->name,
@@ -1407,6 +1431,12 @@ static char *out_dst_opr(char *buf, size_t buf_size,
   return buf;
 }
 
+static char *out_src_opr_u32(char *buf, size_t buf_size,
+       struct parsed_op *po, struct parsed_opr *popr)
+{
+  return out_src_opr(buf, buf_size, po, popr, NULL, 0);
+}
+
 static enum parsed_flag_op split_cond(struct parsed_op *po,
   enum op_op op, int *is_inv)
 {
@@ -1552,19 +1582,19 @@ static void out_cmp_test(char *buf, size_t buf_size,
 
   if (po->op == OP_TEST) {
     if (IS(opr_name(po, 0), opr_name(po, 1))) {
-      out_src_opr(buf3, sizeof(buf3), po, &po->operand[0], 0);
+      out_src_opr_u32(buf3, sizeof(buf3), po, &po->operand[0]);
     }
     else {
-      out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], 0);
-      out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], 0);
+      out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
+      out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
       snprintf(buf3, sizeof(buf3), "(%s & %s)", buf1, buf2);
     }
     out_test_for_cc(buf, buf_size, po, pfo, is_inv,
       po->operand[0].lmod, buf3);
   }
   else if (po->op == OP_CMP) {
-    out_src_opr(buf2, sizeof(buf2), po, &po->operand[0], 0);
-    out_src_opr(buf3, sizeof(buf3), po, &po->operand[1], 0);
+    out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]);
+    out_src_opr_u32(buf3, sizeof(buf3), po, &po->operand[1]);
     out_cmp_for_cc(buf, buf_size, po, pfo, is_inv,
       po->operand[0].lmod, buf2, buf3);
   }
@@ -1895,7 +1925,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 {
   struct parsed_op *po, *delayed_flag_op = NULL, *tmp_op;
   struct parsed_opr *last_arith_dst = NULL;
-  char buf1[256], buf2[256], buf3[256];
+  char buf1[256], buf2[256], buf3[256], cast[64];
   struct parsed_proto *pp, *pp_tmp;
   struct parsed_data *pd;
   const char *tmpname;
@@ -1904,6 +1934,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   int cmp_result_vars = 0;
   int need_mul_var = 0;
   int had_decl = 0;
+  int label_pending = 0;
   int regmask_save = 0;
   int regmask_arg = 0;
   int regmask = 0;
@@ -1923,11 +1954,11 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   if (ret)
     ferr(ops, "proto_parse failed for '%s'\n", funcn);
 
-  fprintf(fout, "%s %s(", g_func_pp.ret_type, funcn);
+  fprintf(fout, "%s %s(", g_func_pp.ret_type.name, funcn);
   for (i = 0; i < g_func_pp.argc; i++) {
     if (i > 0)
       fprintf(fout, ", ");
-    fprintf(fout, "%s a%d", g_func_pp.arg[i].type, i + 1);
+    fprintf(fout, "%s a%d", g_func_pp.arg[i].type.name, i + 1);
   }
   fprintf(fout, ")\n{\n");
 
@@ -2126,10 +2157,10 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         j /= 4;
         if (j > ARRAY_SIZE(pp->arg))
           ferr(po, "esp adjust too large?\n");
-        pp->ret_type = strdup("int");
+        pp->ret_type.name = strdup("int");
         pp->argc = pp->argc_stack = j;
         for (arg = 0; arg < pp->argc; arg++)
-          pp->arg[arg].type = strdup("int");
+          pp->arg[arg].type.name = strdup("int");
       }
       else {
         ret = proto_parse(fhdr, tmpname, pp);
@@ -2150,7 +2181,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
           arg = pp->argc;
           pp->argc += j / 4 - pp->argc_stack;
           for (; arg < pp->argc; arg++) {
-            pp->arg[arg].type = strdup("int");
+            pp->arg[arg].type.name = strdup("int");
             pp->argc_stack++;
           }
           if (pp->argc > ARRAY_SIZE(pp->arg))
@@ -2331,11 +2362,11 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
     else if (po->op == OP_CALL && po->operand[0].type != OPT_LABEL) {
       pp = po->datap;
       my_assert_not(pp, NULL);
-      fprintf(fout, "  %s (*icall%d)(", pp->ret_type, i);
+      fprintf(fout, "  %s (*icall%d)(", pp->ret_type.name, i);
       for (j = 0; j < pp->argc; j++) {
         if (j > 0)
           fprintf(fout, ", ");
-        fprintf(fout, "%s a%d", pp->arg[j].type, j + 1);
+        fprintf(fout, "%s a%d", pp->arg[j].type.name, j + 1);
       }
       fprintf(fout, ");\n");
     }
@@ -2388,7 +2419,9 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   }
 
   // declare other regs - special case for eax
-  if (!((regmask | regmask_arg) & 1) && !IS(g_func_pp.ret_type, "void")) {
+  if (!((regmask | regmask_arg) & 1)
+   && !IS(g_func_pp.ret_type.name, "void"))
+  {
     fprintf(fout, "  u32 eax = 0;\n");
     had_decl = 1;
   }
@@ -2444,8 +2477,10 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   // output ops
   for (i = 0; i < opcnt; i++)
   {
-    if (g_labels[i][0] != 0 && g_label_refs[i].i != -1)
+    if (g_labels[i][0] != 0 && g_label_refs[i].i != -1) {
       fprintf(fout, "\n%s:\n", g_labels[i]);
+      label_pending = 1;
+    }
 
     po = &ops[i];
     if (po->flags & OPF_RMD)
@@ -2475,7 +2510,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       else if (last_arith_dst != NULL
         && (pfo == PFO_Z || pfo == PFO_S || pfo == PFO_P))
       {
-        out_src_opr(buf3, sizeof(buf3), po, last_arith_dst, 0);
+        out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
         out_test_for_cc(buf1, sizeof(buf1), po, pfo, is_inv,
           last_arith_dst->lmod, buf3);
         is_delayed = 1;
@@ -2518,10 +2553,10 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       case OP_MOV:
         assert_operand_cnt(2);
         propagate_lmod(po, &po->operand[0], &po->operand[1]);
-        fprintf(fout, "  %s = %s%s;",
+        fprintf(fout, "  %s = %s;",
             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
-            po->operand[0].is_ptr ? "(void *)" : "",
-            out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], 0));
+            out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
+              po->operand[0].is_ptr ? "(void *)" : "", 0));
         break;
 
       case OP_LEA:
@@ -2529,14 +2564,15 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         po->operand[1].lmod = OPLM_DWORD; // always
         fprintf(fout, "  %s = %s;",
             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
-            out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], 1));
+            out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
+              NULL, 1));
         break;
 
       case OP_MOVZX:
         assert_operand_cnt(2);
         fprintf(fout, "  %s = %s;",
             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
-            out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], 0));
+            out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
         break;
 
       case OP_MOVSX:
@@ -2554,7 +2590,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         fprintf(fout, "  %s = %s%s;",
             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
             buf3,
-            out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], 0));
+            out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
         break;
 
       case OP_NOT:
@@ -2567,7 +2603,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         assert_operand_cnt(2);
         fprintf(fout, "  %s = (s32)%s >> 31;",
             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
-            out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], 0));
+            out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
         strcpy(g_comment, "cdq");
         break;
 
@@ -2652,7 +2688,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         fprintf(fout, "  %s %s= %s;",
             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
             op_to_c(po),
-            out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], 0));
+            out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
         last_arith_dst = &po->operand[0];
         delayed_flag_op = NULL;
         break;
@@ -2662,7 +2698,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
         fprintf(fout, "  %s = %s%s >> %s;", buf1,
           lmod_cast_s(po, po->operand[0].lmod), buf1,
-          out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], 0));
+          out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
         last_arith_dst = &po->operand[0];
         delayed_flag_op = NULL;
         break;
@@ -2706,7 +2742,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         fprintf(fout, "  %s %s= %s + cond_c;",
             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
             op_to_c(po),
-            out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], 0));
+            out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
         last_arith_dst = &po->operand[0];
         delayed_flag_op = NULL;
         break;
@@ -2728,7 +2764,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 
       case OP_NEG:
         out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
-        out_src_opr(buf2, sizeof(buf2), po, &po->operand[0], 0);
+        out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]);
         fprintf(fout, "  %s = -%s%s;", buf1,
           lmod_cast_s(po, po->operand[0].lmod), buf2);
         last_arith_dst = &po->operand[0];
@@ -2749,7 +2785,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         assert_operand_cnt(1);
         strcpy(buf1, po->op == OP_IMUL ? "(s64)(s32)" : "(u64)");
         fprintf(fout, "  mul_tmp = %seax * %s%s;\n", buf1, buf1,
-          out_src_opr(buf2, sizeof(buf2), po, &po->operand[0], 0));
+          out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]));
         fprintf(fout, "  edx = mul_tmp >> 32;\n");
         fprintf(fout, "  eax = mul_tmp;");
         last_arith_dst = NULL;
@@ -2768,7 +2804,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         else
           ret = scan_for_cdq_edx(i - 1);
         if (ret >= 0) {
-          out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], 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);
@@ -2831,15 +2867,15 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 
         if (po->operand[0].type != OPT_LABEL)
           fprintf(fout, "  icall%d = (void *)%s;\n", i,
-            out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], 0));
+            out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]));
 
         fprintf(fout, "  ");
-        if (!IS(pp->ret_type, "void")) {
+        if (!IS(pp->ret_type.name, "void")) {
           if (po->flags & OPF_TAIL)
             fprintf(fout, "return ");
           else
             fprintf(fout, "eax = ");
-          if (strchr(pp->ret_type, '*'))
+          if (pp->ret_type.is_ptr)
             fprintf(fout, "(u32)");
         }
 
@@ -2856,11 +2892,12 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
           if (arg > 0)
             fprintf(fout, ", ");
 
-          if (strchr(pp->arg[arg].type, '*'))
-            fprintf(fout, "(%s)", pp->arg[arg].type);
+          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", pp->arg[arg].reg);
+            fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
             continue;
           }
 
@@ -2869,31 +2906,36 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
           if (tmp_op == NULL)
             ferr(po, "parsed_op missing for arg%d\n", arg);
           if (tmp_op->argmask) {
-            fprintf(fout, "s_a%d", arg + 1);
+            fprintf(fout, "%ss_a%d", cast, arg + 1);
           }
           else {
             fprintf(fout, "%s",
               out_src_opr(buf1, sizeof(buf1),
-                tmp_op, &tmp_op->operand[0], 0));
+                tmp_op, &tmp_op->operand[0], cast, 0));
           }
         }
         fprintf(fout, ");");
 
         if (po->flags & OPF_TAIL) {
           strcpy(g_comment, "tailcall");
-          if (IS(pp->ret_type, "void"))
+          if (IS(pp->ret_type.name, "void")) {
             fprintf(fout, "\n  return;");
+            strcpy(g_comment, "^ tailcall");
+          }
         }
         delayed_flag_op = NULL;
         last_arith_dst = NULL;
         break;
 
       case OP_RET:
-        if (IS(g_func_pp.ret_type, "void"))
-          fprintf(fout, "  return;");
-        else if (strchr(g_func_pp.ret_type, '*'))
+        if (IS(g_func_pp.ret_type.name, "void")) {
+          if (i != opcnt - 1 || label_pending)
+            fprintf(fout, "  return;");
+        }
+        else if (g_func_pp.ret_type.is_ptr) {
           fprintf(fout, "  return (%s)eax;",
-            g_func_pp.ret_type);
+            g_func_pp.ret_type.name);
+        }
         else
           fprintf(fout, "  return eax;");
         break;
@@ -2901,7 +2943,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       case OP_PUSH:
         if (po->argmask) {
           // special case - saved func arg
-          out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], 0);
+          out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
           for (j = 0; j < 32; j++) {
             if (po->argmask & (1 << j))
               fprintf(fout, "  s_a%d = %s;", j + 1, buf1);
@@ -2909,7 +2951,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
           break;
         }
         else if (po->flags & OPF_RSAVE) {
-          out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], 0);
+          out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
           fprintf(fout, "  s_%s = %s;", buf1, buf1);
           break;
         }
@@ -2918,7 +2960,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 
       case OP_POP:
         if (po->flags & OPF_RSAVE) {
-          out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], 0);
+          out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
           fprintf(fout, "  %s = s_%s;", buf1, buf1);
           break;
         }
@@ -2963,6 +3005,8 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       if (is_opr_modified(last_arith_dst, po))
         last_arith_dst = NULL;
     }
+
+    label_pending = 0;
   }
 
   fprintf(fout, "}\n\n");
@@ -3188,8 +3232,8 @@ parse_words:
     }
 
     // alow asm patches in comments
-    if (*p == ';' && IS_START(p, "; sctpatch: ")) {
-      p = sskip(p + 12);
+    if (*p == ';' && IS_START(p, "; sctpatch:")) {
+      p = sskip(p + 11);
       if (*p == 0 || *p == ';')
         continue;
       goto parse_words; // lame