more 32bit fixes
[ia32rtools.git] / tools / translate.c
CommitLineData
7637b6cc 1/*
2 * ia32rtools
2c10ea1f 3 * (C) notaz, 2013-2015
7637b6cc 4 *
5 * This work is licensed under the terms of 3-clause BSD license.
6 * See COPYING file in the top-level directory.
ea43585b 7 *
8 * recognized asm hint comments:
9 * sctattr - function attributes (see code)
10 * sctend - force end of function/chunk
11 * sctpatch: <p> - replace current asm line with <p>
12 * sctproto: <p> - prototype of ref'd function or struct
13 * sctref - variable is referenced, make global
14 * sctskip_start - start of skipped code chunk (inclusive)
15 * sctskip_end - end of skipped code chunk (inclusive)
7637b6cc 16 */
17
33c35af6 18#define _GNU_SOURCE
c36e914d 19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
e27467d0 22#include <errno.h>
c36e914d 23
24#include "my_assert.h"
25#include "my_str.h"
9ea60b8d 26#include "common.h"
c36e914d 27
28#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
29#define IS(w, y) !strcmp(w, y)
39b168b8 30#define IS_START(w, y) !strncmp(w, y, strlen(y))
c36e914d 31
32#include "protoparse.h"
33
06c5d854 34static const char *asmfn;
c36e914d 35static int asmln;
06c5d854 36static FILE *g_fhdr;
c36e914d 37
940e8e66 38#define anote(fmt, ...) \
39 printf("%s:%d: note: " fmt, asmfn, asmln, ##__VA_ARGS__)
c36e914d 40#define awarn(fmt, ...) \
940e8e66 41 printf("%s:%d: warning: " fmt, asmfn, asmln, ##__VA_ARGS__)
c36e914d 42#define aerr(fmt, ...) do { \
940e8e66 43 printf("%s:%d: error: " fmt, asmfn, asmln, ##__VA_ARGS__); \
33c35af6 44 fcloseall(); \
c36e914d 45 exit(1); \
46} while (0)
47
054f95b2 48#include "masm_tools.h"
49
69a3cdfc 50enum op_flags {
5e49b270 51 OPF_RMD = (1 << 0), /* removed from code generation */
87bf6cec 52 OPF_DATA = (1 << 1), /* data processing - writes to dst opr */
53 OPF_FLAGS = (1 << 2), /* sets flags */
5c024ef7 54 OPF_JMP = (1 << 3), /* branch, call */
04abc5d6 55 OPF_CJMP = (1 << 4), /* cond. branch (cc or jecxz/loop) */
5c024ef7 56 OPF_CC = (1 << 5), /* uses flags */
57 OPF_TAIL = (1 << 6), /* ret or tail call */
58 OPF_RSAVE = (1 << 7), /* push/pop is local reg save/load */
59 OPF_REP = (1 << 8), /* prefixed by rep */
60 OPF_REPZ = (1 << 9), /* rep is repe/repz */
61 OPF_REPNZ = (1 << 10), /* rep is repne/repnz */
9af2d373 62 OPF_FARG = (1 << 11), /* push collected as func arg */
63 OPF_FARGNR = (1 << 12), /* push collected as func arg (no reuse) */
64 OPF_EBP_S = (1 << 13), /* ebp used as scratch here, not BP */
65 OPF_DF = (1 << 14), /* DF flag set */
66 OPF_ATAIL = (1 << 15), /* tail call with reused arg frame */
67 OPF_32BIT = (1 << 16), /* 32bit division */
68 OPF_LOCK = (1 << 17), /* op has lock prefix */
69 OPF_VAPUSH = (1 << 18), /* vararg ptr push (as call arg) */
26677139 70 OPF_DONE = (1 << 19), /* already fully handled by analysis */
25a330eb 71 OPF_PPUSH = (1 << 20), /* part of complex push-pop graph */
b2bd20c0 72 OPF_NOREGS = (1 << 21), /* don't track regs of this op */
d4a985bd 73 OPF_FPUSH = (1 << 22), /* pushes x87 stack */
74 OPF_FPOP = (1 << 23), /* pops x87 stack */
75 OPF_FSHIFT = (1 << 24), /* x87 stack shift is actually needed */
c36e914d 76};
77
78enum op_op {
79 OP_INVAL,
33c35af6 80 OP_NOP,
c36e914d 81 OP_PUSH,
82 OP_POP,
16057ce1 83 OP_PUSHA,
84 OP_POPA,
591721d7 85 OP_LEAVE,
c36e914d 86 OP_MOV,
850c9265 87 OP_LEA,
88 OP_MOVZX,
89 OP_MOVSX,
108e9fe3 90 OP_XCHG,
850c9265 91 OP_NOT,
04abc5d6 92 OP_XLAT,
5101a5f9 93 OP_CDQ,
622eb2ef 94 OP_BSWAP,
092f64e1 95 OP_LODS,
33c35af6 96 OP_STOS,
d4e3b5db 97 OP_MOVS,
7ba45c34 98 OP_CMPS,
591721d7 99 OP_SCAS,
100 OP_STD,
101 OP_CLD,
c36e914d 102 OP_RET,
103 OP_ADD,
91977a1c 104 OP_SUB,
850c9265 105 OP_AND,
106 OP_OR,
107 OP_XOR,
108 OP_SHL,
109 OP_SHR,
110 OP_SAR,
04abc5d6 111 OP_SHLD,
3b2f4044 112 OP_SHRD,
d4e3b5db 113 OP_ROL,
114 OP_ROR,
cb090db0 115 OP_RCL,
116 OP_RCR,
69a3cdfc 117 OP_ADC,
850c9265 118 OP_SBB,
1f84f6b3 119 OP_BSF,
850c9265 120 OP_INC,
121 OP_DEC,
5101a5f9 122 OP_NEG,
850c9265 123 OP_MUL,
124 OP_IMUL,
5101a5f9 125 OP_DIV,
126 OP_IDIV,
c36e914d 127 OP_TEST,
128 OP_CMP,
129 OP_CALL,
130 OP_JMP,
5c024ef7 131 OP_JECXZ,
04abc5d6 132 OP_LOOP,
092f64e1 133 OP_JCC,
134 OP_SCC,
d4a985bd 135 // x87
136 OP_FLD,
137 OP_FILD,
138 OP_FLDc,
139 OP_FST,
16057ce1 140 OP_FIST,
d4a985bd 141 OP_FADD,
142 OP_FDIV,
143 OP_FMUL,
144 OP_FSUB,
145 OP_FDIVR,
146 OP_FSUBR,
147 OP_FIADD,
148 OP_FIDIV,
149 OP_FIMUL,
150 OP_FISUB,
151 OP_FIDIVR,
152 OP_FISUBR,
16057ce1 153 OP_FCOM,
154 OP_FNSTSW,
fe18df39 155 OP_FCHS,
497a6d6b 156 OP_FCOS,
157 OP_FPATAN,
158 OP_FPTAN,
159 OP_FSIN,
160 OP_FSQRT,
fe18df39 161 OP_FXCH,
162 OP_FYL2X,
d4a985bd 163 // mmx
164 OP_EMMS,
165 // pseudo-ops for lib calls
622eb2ef 166 OPP_ALLSHL,
167 OPP_ALLSHR,
d4a985bd 168 OPP_FTOL,
8c83cc48 169 OPP_CIPOW,
11437ea1 170 OPP_ABORT,
d4a985bd 171 // undefined
172 OP_UD2,
c36e914d 173};
174
175enum opr_type {
87bf6cec 176 OPT_UNSPEC,
177 OPT_REG,
178 OPT_REGMEM,
179 OPT_LABEL,
850c9265 180 OPT_OFFSET,
87bf6cec 181 OPT_CONST,
c36e914d 182};
183
2b43685d 184// must be sorted (larger len must be further in enum)
c36e914d 185enum opr_lenmod {
91977a1c 186 OPLM_UNSPEC,
187 OPLM_BYTE,
188 OPLM_WORD,
189 OPLM_DWORD,
90307a99 190 OPLM_QWORD,
c36e914d 191};
192
e83ea7ed 193#define MAX_EXITS 128
194
850c9265 195#define MAX_OPERANDS 3
92d715b6 196#define NAMELEN 112
c36e914d 197
b2bd20c0 198#define OPR_INIT(type_, lmod_, reg_) \
199 { type_, lmod_, reg_, }
200
c36e914d 201struct parsed_opr {
91977a1c 202 enum opr_type type;
203 enum opr_lenmod lmod;
b2bd20c0 204 int reg;
7ba45c34 205 unsigned int is_ptr:1; // pointer in C
206 unsigned int is_array:1; // array in C
a3684be1 207 unsigned int type_from_var:1; // .. in header, sometimes wrong
2b43685d 208 unsigned int size_mismatch:1; // type override differs from C
209 unsigned int size_lt:1; // type override is larger than C
ddaf8bd7 210 unsigned int had_ds:1; // had ds: prefix
c7ed83dd 211 const struct parsed_proto *pp; // for OPT_LABEL
91977a1c 212 unsigned int val;
92d715b6 213 char name[NAMELEN];
c36e914d 214};
215
216struct parsed_op {
91977a1c 217 enum op_op op;
218 struct parsed_opr operand[MAX_OPERANDS];
69a3cdfc 219 unsigned int flags;
092f64e1 220 unsigned char pfo;
221 unsigned char pfo_inv;
222 unsigned char operand_cnt;
3a5101d7 223 unsigned char p_argnum; // arg push: altered before call arg #
224 unsigned char p_arggrp; // arg push: arg group # for above
225 unsigned char p_argpass;// arg push: arg of host func
226 short p_argnext;// arg push: same arg pushed elsewhere or -1
69a3cdfc 227 int regmask_src; // all referensed regs
228 int regmask_dst;
940e8e66 229 int pfomask; // flagop: parsed_flag_op that can't be delayed
940e8e66 230 int cc_scratch; // scratch storage during analysis
4c45fa73 231 int bt_i; // branch target for branches
232 struct parsed_data *btj;// branch targets for jumptables
c7ed83dd 233 struct parsed_proto *pp;// parsed_proto for OP_CALL
91977a1c 234 void *datap;
8eb12e72 235 int asmln;
91977a1c 236};
237
69a3cdfc 238// datap:
865f1aca 239// on start: function/data type hint (sctproto)
240// after analysis:
2c10ea1f 241// (OPF_CC) - points to one of (OPF_FLAGS) that affects cc op
25a330eb 242// OP_PUSH - points to OP_POP in complex push/pop graph
243// OP_POP - points to OP_PUSH in simple push/pop pair
16057ce1 244// OP_FCOM - needed_status_word_bits | (is_z_check << 16)
69a3cdfc 245
91977a1c 246struct parsed_equ {
247 char name[64];
248 enum opr_lenmod lmod;
249 int offset;
c36e914d 250};
251
4c45fa73 252struct parsed_data {
253 char label[256];
254 enum opr_type type;
255 enum opr_lenmod lmod;
256 int count;
257 int count_alloc;
258 struct {
259 union {
260 char *label;
63df67be 261 unsigned int val;
4c45fa73 262 } u;
263 int bt_i;
264 } *d;
265};
266
267struct label_ref {
268 int i;
269 struct label_ref *next;
270};
271
1bafb621 272enum ida_func_attr {
273 IDAFA_BP_FRAME = (1 << 0),
274 IDAFA_LIB_FUNC = (1 << 1),
275 IDAFA_STATIC = (1 << 2),
276 IDAFA_NORETURN = (1 << 3),
277 IDAFA_THUNK = (1 << 4),
278 IDAFA_FPD = (1 << 5),
279};
280
7e08c224 281enum sct_func_attr {
226e8df1 282 SCTFA_CLEAR_SF = (1 << 0), // clear stack frame
283 SCTFA_CLEAR_REGS = (1 << 1), // clear registers (mask)
7e08c224 284};
285
d4a985bd 286enum x87_const {
287 X87_CONST_1 = 1,
288 X87_CONST_2T,
289 X87_CONST_2E,
290 X87_CONST_PI,
291 X87_CONST_LG2,
292 X87_CONST_LN2,
293 X87_CONST_Z,
294};
295
3a5101d7 296// note: limited to 32k due to p_argnext
297#define MAX_OPS 4096
298#define MAX_ARG_GRP 2
c36e914d 299
300static struct parsed_op ops[MAX_OPS];
91977a1c 301static struct parsed_equ *g_eqs;
302static int g_eqcnt;
d7857c3a 303static char *g_labels[MAX_OPS];
4c45fa73 304static struct label_ref g_label_refs[MAX_OPS];
bd96f656 305static const struct parsed_proto *g_func_pp;
4c45fa73 306static struct parsed_data *g_func_pd;
307static int g_func_pd_cnt;
d4a985bd 308static int g_func_lmods;
91977a1c 309static char g_func[256];
310static char g_comment[256];
311static int g_bp_frame;
1bafb621 312static int g_sp_frame;
a2c1d768 313static int g_stack_frame_used;
1bafb621 314static int g_stack_fsz;
4c45fa73 315static int g_ida_func_attr;
7e08c224 316static int g_sct_func_attr;
317static int g_stack_clear_start; // in dwords
318static int g_stack_clear_len;
226e8df1 319static int g_regmask_init;
30c8c549 320static int g_skip_func;
89ff3147 321static int g_allow_regfunc;
8c83cc48 322static int g_allow_user_icall;
92d715b6 323static int g_quiet_pp;
9af2d373 324static int g_header_mode;
92d715b6 325
91977a1c 326#define ferr(op_, fmt, ...) do { \
bfacdc83 327 printf("%s:%d: error %u: [%s] '%s': " fmt, asmfn, (op_)->asmln, \
328 __LINE__, g_func, dump_op(op_), ##__VA_ARGS__); \
33c35af6 329 fcloseall(); \
91977a1c 330 exit(1); \
331} while (0)
de50b98b 332#define fnote(op_, fmt, ...) \
8eb12e72 333 printf("%s:%d: note: [%s] '%s': " fmt, asmfn, (op_)->asmln, g_func, \
de50b98b 334 dump_op(op_), ##__VA_ARGS__)
91977a1c 335
26677139 336#define ferr_assert(op_, cond) do { \
2b70f6d3 337 if (!(cond)) ferr(op_, "assertion '%s' failed\n", #cond); \
26677139 338} while (0)
339
90307a99 340const char *regs_r32[] = {
341 "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp",
342 // not r32, but list here for easy parsing and printing
343 "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
d4a985bd 344 "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)"
90307a99 345};
91977a1c 346const char *regs_r16[] = { "ax", "bx", "cx", "dx", "si", "di", "bp", "sp" };
347const char *regs_r8l[] = { "al", "bl", "cl", "dl" };
348const char *regs_r8h[] = { "ah", "bh", "ch", "dh" };
349
d4a985bd 350enum x86_regs {
351 xUNSPEC = -1,
352 xAX, xBX, xCX, xDX,
353 xSI, xDI, xBP, xSP,
354 xMM0, xMM1, xMM2, xMM3, // mmx
355 xMM4, xMM5, xMM6, xMM7,
356 xST0, xST1, xST2, xST3, // x87
357 xST4, xST5, xST6, xST7,
358};
359
360#define mxAX (1 << xAX)
226e8df1 361#define mxCX (1 << xCX)
d4a985bd 362#define mxDX (1 << xDX)
363#define mxST0 (1 << xST0)
364#define mxST1 (1 << xST1)
fe18df39 365#define mxST1_0 (mxST1 | mxST0)
366#define mxST7_2 (0xfc << xST0)
367#define mxSTa (0xff << xST0)
91977a1c 368
69a3cdfc 369// possible basic comparison types (without inversion)
370enum parsed_flag_op {
371 PFO_O, // 0 OF=1
372 PFO_C, // 2 CF=1
373 PFO_Z, // 4 ZF=1
374 PFO_BE, // 6 CF=1||ZF=1
375 PFO_S, // 8 SF=1
376 PFO_P, // a PF=1
377 PFO_L, // c SF!=OF
378 PFO_LE, // e ZF=1||SF!=OF
379};
380
90307a99 381#define PFOB_O (1 << PFO_O)
382#define PFOB_C (1 << PFO_C)
383#define PFOB_Z (1 << PFO_Z)
384#define PFOB_S (1 << PFO_S)
385
69a3cdfc 386static const char *parsed_flag_op_names[] = {
387 "o", "c", "z", "be", "s", "p", "l", "le"
388};
389
91977a1c 390static int char_array_i(const char *array[], size_t len, const char *s)
391{
392 int i;
c36e914d 393
91977a1c 394 for (i = 0; i < len; i++)
395 if (IS(s, array[i]))
396 return i;
c36e914d 397
91977a1c 398 return -1;
399}
400
63df67be 401static void printf_number(char *buf, size_t buf_size,
402 unsigned long number)
91977a1c 403{
5101a5f9 404 // output in C-friendly form
405 snprintf(buf, buf_size, number < 10 ? "%lu" : "0x%02lx", number);
406}
91977a1c 407
fdd5548a 408static int check_segment_prefix(const char *s)
409{
410 if (s[0] == 0 || s[1] != 's' || s[2] != ':')
411 return 0;
412
413 switch (s[0]) {
414 case 'c': return 1;
415 case 'd': return 2;
416 case 's': return 3;
417 case 'e': return 4;
418 case 'f': return 5;
419 case 'g': return 6;
420 default: return 0;
421 }
422}
423
5101a5f9 424static int parse_reg(enum opr_lenmod *reg_lmod, const char *s)
425{
426 int reg;
91977a1c 427
5101a5f9 428 reg = char_array_i(regs_r32, ARRAY_SIZE(regs_r32), s);
90307a99 429 if (reg >= 8) {
430 *reg_lmod = OPLM_QWORD;
431 return reg;
432 }
5101a5f9 433 if (reg >= 0) {
434 *reg_lmod = OPLM_DWORD;
435 return reg;
91977a1c 436 }
5101a5f9 437 reg = char_array_i(regs_r16, ARRAY_SIZE(regs_r16), s);
438 if (reg >= 0) {
439 *reg_lmod = OPLM_WORD;
440 return reg;
441 }
442 reg = char_array_i(regs_r8h, ARRAY_SIZE(regs_r8h), s);
443 if (reg >= 0) {
444 *reg_lmod = OPLM_BYTE;
445 return reg;
446 }
447 reg = char_array_i(regs_r8l, ARRAY_SIZE(regs_r8l), s);
448 if (reg >= 0) {
449 *reg_lmod = OPLM_BYTE;
450 return reg;
850c9265 451 }
452
453 return -1;
91977a1c 454}
455
5101a5f9 456static int parse_indmode(char *name, int *regmask, int need_c_cvt)
457{
458 enum opr_lenmod lmod;
459 char cvtbuf[256];
460 char *d = cvtbuf;
461 char *s = name;
462 char w[64];
463 long number;
464 int reg;
465 int c = 0;
466
467 *d = 0;
468
469 while (*s != 0) {
470 d += strlen(d);
471 while (my_isblank(*s))
472 s++;
473 for (; my_issep(*s); d++, s++)
474 *d = *s;
475 while (my_isblank(*s))
476 s++;
477 *d = 0;
478
fdd5548a 479 // skip '?s:' prefixes
480 if (check_segment_prefix(s))
87bf6cec 481 s += 3;
482
5101a5f9 483 s = next_idt(w, sizeof(w), s);
484 if (w[0] == 0)
485 break;
486 c++;
487
488 reg = parse_reg(&lmod, w);
489 if (reg >= 0) {
490 *regmask |= 1 << reg;
491 goto pass;
492 }
493
494 if ('0' <= w[0] && w[0] <= '9') {
e27467d0 495 number = parse_number(w, 0);
5101a5f9 496 printf_number(d, sizeof(cvtbuf) - (d - cvtbuf), number);
497 continue;
498 }
499
500 // probably some label/identifier - pass
501
502pass:
503 snprintf(d, sizeof(cvtbuf) - (d - cvtbuf), "%s", w);
504 }
505
506 if (need_c_cvt)
507 strcpy(name, cvtbuf);
508
509 return c;
510}
511
4c45fa73 512static int is_reg_in_str(const char *s)
513{
514 int i;
515
516 if (strlen(s) < 3 || (s[3] && !my_issep(s[3]) && !my_isblank(s[3])))
517 return 0;
518
519 for (i = 0; i < ARRAY_SIZE(regs_r32); i++)
520 if (!strncmp(s, regs_r32[i], 3))
521 return 1;
522
523 return 0;
524}
525
09bc6fd5 526static const char *parse_stack_el(const char *name, char *extra_reg,
30620174 527 int *base_val, int early_try)
d4e3b5db 528{
4c45fa73 529 const char *p, *p2, *s;
1bafb621 530 char *endp = NULL;
d4e3b5db 531 char buf[32];
30620174 532 long val = -1;
d4e3b5db 533 int len;
534
09bc6fd5 535 if (g_bp_frame || early_try)
536 {
537 p = name;
538 if (IS_START(p + 3, "+ebp+") && is_reg_in_str(p)) {
539 p += 4;
540 if (extra_reg != NULL) {
541 strncpy(extra_reg, name, 3);
542 extra_reg[4] = 0;
543 }
4c45fa73 544 }
4c45fa73 545
09bc6fd5 546 if (IS_START(p, "ebp+")) {
547 p += 4;
4c45fa73 548
09bc6fd5 549 p2 = strchr(p, '+');
550 if (p2 != NULL && is_reg_in_str(p)) {
551 if (extra_reg != NULL) {
552 strncpy(extra_reg, p, p2 - p);
553 extra_reg[p2 - p] = 0;
554 }
555 p = p2 + 1;
4c45fa73 556 }
4c45fa73 557
09bc6fd5 558 if (!('0' <= *p && *p <= '9'))
559 return p;
4c45fa73 560
09bc6fd5 561 return NULL;
562 }
4c45fa73 563 }
564
39b168b8 565 if (!IS_START(name, "esp+"))
d4e3b5db 566 return NULL;
567
037f4971 568 s = name + 4;
569 p = strchr(s, '+');
d4e3b5db 570 if (p) {
037f4971 571 if (is_reg_in_str(s)) {
572 if (extra_reg != NULL) {
573 strncpy(extra_reg, s, p - s);
574 extra_reg[p - s] = 0;
575 }
576 s = p + 1;
577 p = strchr(s, '+');
578 if (p == NULL)
579 aerr("%s IDA stackvar not set?\n", __func__);
580 }
1bafb621 581 if (!('0' <= *s && *s <= '9')) {
037f4971 582 aerr("%s IDA stackvar offset not set?\n", __func__);
d4e3b5db 583 return NULL;
1bafb621 584 }
585 if (s[0] == '0' && s[1] == 'x')
586 s += 2;
587 len = p - s;
d4e3b5db 588 if (len < sizeof(buf) - 1) {
1bafb621 589 strncpy(buf, s, len);
d4e3b5db 590 buf[len] = 0;
e27467d0 591 errno = 0;
1bafb621 592 val = strtol(buf, &endp, 16);
e27467d0 593 if (val == 0 || *endp != 0 || errno != 0) {
1bafb621 594 aerr("%s num parse fail for '%s'\n", __func__, buf);
595 return NULL;
596 }
d4e3b5db 597 }
598 p++;
599 }
600 else
601 p = name + 4;
602
603 if ('0' <= *p && *p <= '9')
604 return NULL;
605
30620174 606 if (base_val != NULL)
607 *base_val = val;
d4e3b5db 608 return p;
609}
610
850c9265 611static int guess_lmod_from_name(struct parsed_opr *opr)
612{
04abc5d6 613 if (IS_START(opr->name, "dword_") || IS_START(opr->name, "off_")) {
850c9265 614 opr->lmod = OPLM_DWORD;
615 return 1;
616 }
04abc5d6 617 if (IS_START(opr->name, "word_")) {
850c9265 618 opr->lmod = OPLM_WORD;
619 return 1;
620 }
04abc5d6 621 if (IS_START(opr->name, "byte_")) {
850c9265 622 opr->lmod = OPLM_BYTE;
623 return 1;
624 }
04abc5d6 625 if (IS_START(opr->name, "qword_")) {
90307a99 626 opr->lmod = OPLM_QWORD;
627 return 1;
628 }
850c9265 629 return 0;
630}
631
3ebea2cf 632static int guess_lmod_from_c_type(enum opr_lenmod *lmod,
633 const struct parsed_type *c_type)
06c5d854 634{
11437ea1 635 static const char *qword_types[] = {
636 "uint64_t", "int64_t", "__int64",
637 };
06c5d854 638 static const char *dword_types[] = {
c0de9015 639 "uint32_t", "int", "_DWORD", "UINT_PTR", "DWORD",
4741fdfe 640 "WPARAM", "LPARAM", "UINT", "__int32",
09bc6fd5 641 "LONG", "HIMC", "BOOL", "size_t",
feb0ee5d 642 "float",
06c5d854 643 };
644 static const char *word_types[] = {
fe18709a 645 "uint16_t", "int16_t", "_WORD", "WORD",
2b43685d 646 "unsigned __int16", "__int16",
06c5d854 647 };
648 static const char *byte_types[] = {
2b43685d 649 "uint8_t", "int8_t", "char",
89ff3147 650 "unsigned __int8", "__int8", "BYTE", "_BYTE",
4741fdfe 651 "CHAR", "_UNKNOWN",
b4878d2b 652 // structures.. deal the same as with _UNKNOWN for now
653 "CRITICAL_SECTION",
06c5d854 654 };
3ebea2cf 655 const char *n;
06c5d854 656 int i;
657
3ebea2cf 658 if (c_type->is_ptr) {
659 *lmod = OPLM_DWORD;
06c5d854 660 return 1;
661 }
662
3ebea2cf 663 n = skip_type_mod(c_type->name);
06c5d854 664
3ebea2cf 665 for (i = 0; i < ARRAY_SIZE(dword_types); i++) {
666 if (IS(n, dword_types[i])) {
667 *lmod = OPLM_DWORD;
06c5d854 668 return 1;
669 }
670 }
671
672 for (i = 0; i < ARRAY_SIZE(word_types); i++) {
3ebea2cf 673 if (IS(n, word_types[i])) {
674 *lmod = OPLM_WORD;
06c5d854 675 return 1;
676 }
677 }
678
679 for (i = 0; i < ARRAY_SIZE(byte_types); i++) {
3ebea2cf 680 if (IS(n, byte_types[i])) {
681 *lmod = OPLM_BYTE;
06c5d854 682 return 1;
683 }
684 }
685
11437ea1 686 for (i = 0; i < ARRAY_SIZE(qword_types); i++) {
687 if (IS(n, qword_types[i])) {
688 *lmod = OPLM_QWORD;
689 return 1;
690 }
691 }
692
06c5d854 693 return 0;
694}
695
c7ed83dd 696static char *default_cast_to(char *buf, size_t buf_size,
697 struct parsed_opr *opr)
698{
699 buf[0] = 0;
700
865f1aca 701 if (!opr->is_ptr || strchr(opr->name, '['))
c7ed83dd 702 return buf;
703 if (opr->pp == NULL || opr->pp->type.name == NULL
704 || opr->pp->is_fptr)
705 {
706 snprintf(buf, buf_size, "%s", "(void *)");
707 return buf;
708 }
709
710 snprintf(buf, buf_size, "(%s)", opr->pp->type.name);
711 return buf;
712}
713
4c45fa73 714static enum opr_type lmod_from_directive(const char *d)
715{
716 if (IS(d, "dd"))
717 return OPLM_DWORD;
718 else if (IS(d, "dw"))
719 return OPLM_WORD;
720 else if (IS(d, "db"))
721 return OPLM_BYTE;
722
723 aerr("unhandled directive: '%s'\n", d);
724 return OPLM_UNSPEC;
725}
726
5101a5f9 727static void setup_reg_opr(struct parsed_opr *opr, int reg, enum opr_lenmod lmod,
728 int *regmask)
729{
730 opr->type = OPT_REG;
731 opr->reg = reg;
732 opr->lmod = lmod;
733 *regmask |= 1 << reg;
734}
735
39b168b8 736static struct parsed_equ *equ_find(struct parsed_op *po, const char *name,
737 int *extra_offs);
87bf6cec 738
69a3cdfc 739static int parse_operand(struct parsed_opr *opr,
740 int *regmask, int *regmask_indirect,
1bafb621 741 char words[16][256], int wordc, int w, unsigned int op_flags)
c36e914d 742{
92d715b6 743 const struct parsed_proto *pp = NULL;
850c9265 744 enum opr_lenmod tmplmod;
63df67be 745 unsigned long number;
89ff3147 746 char buf[256];
850c9265 747 int ret, len;
1bafb621 748 int wordc_in;
89ff3147 749 char *p;
850c9265 750 int i;
c36e914d 751
1bafb621 752 if (w >= wordc)
753 aerr("parse_operand w %d, wordc %d\n", w, wordc);
c36e914d 754
1bafb621 755 opr->reg = xUNSPEC;
91977a1c 756
1bafb621 757 for (i = w; i < wordc; i++) {
758 len = strlen(words[i]);
759 if (words[i][len - 1] == ',') {
760 words[i][len - 1] = 0;
761 wordc = i + 1;
762 break;
763 }
764 }
c36e914d 765
1bafb621 766 wordc_in = wordc - w;
c36e914d 767
1bafb621 768 if ((op_flags & OPF_JMP) && wordc_in > 0
769 && !('0' <= words[w][0] && words[w][0] <= '9'))
770 {
771 const char *label = NULL;
772
773 if (wordc_in == 3 && !strncmp(words[w], "near", 4)
774 && IS(words[w + 1], "ptr"))
775 label = words[w + 2];
776 else if (wordc_in == 2 && IS(words[w], "short"))
777 label = words[w + 1];
778 else if (wordc_in == 1
779 && strchr(words[w], '[') == NULL
780 && parse_reg(&tmplmod, words[w]) < 0)
781 label = words[w];
782
783 if (label != NULL) {
784 opr->type = OPT_LABEL;
fdd5548a 785 ret = check_segment_prefix(label);
786 if (ret != 0) {
787 if (ret >= 5)
788 aerr("fs/gs used\n");
ddaf8bd7 789 opr->had_ds = 1;
39b168b8 790 label += 3;
ddaf8bd7 791 }
1bafb621 792 strcpy(opr->name, label);
793 return wordc;
794 }
795 }
c36e914d 796
1bafb621 797 if (wordc_in >= 3) {
798 if (IS(words[w + 1], "ptr")) {
799 if (IS(words[w], "dword"))
800 opr->lmod = OPLM_DWORD;
801 else if (IS(words[w], "word"))
802 opr->lmod = OPLM_WORD;
803 else if (IS(words[w], "byte"))
804 opr->lmod = OPLM_BYTE;
90307a99 805 else if (IS(words[w], "qword"))
806 opr->lmod = OPLM_QWORD;
1bafb621 807 else
808 aerr("type parsing failed\n");
809 w += 2;
810 wordc_in = wordc - w;
811 }
812 }
813
39b168b8 814 if (wordc_in == 2) {
815 if (IS(words[w], "offset")) {
816 opr->type = OPT_OFFSET;
27ebfaed 817 opr->lmod = OPLM_DWORD;
39b168b8 818 strcpy(opr->name, words[w + 1]);
27ebfaed 819 pp = proto_parse(g_fhdr, opr->name, 1);
820 goto do_label;
39b168b8 821 }
822 if (IS(words[w], "(offset")) {
89ff3147 823 p = strchr(words[w + 1], ')');
39b168b8 824 if (p == NULL)
825 aerr("parse of bracketed offset failed\n");
826 *p = 0;
827 opr->type = OPT_OFFSET;
828 strcpy(opr->name, words[w + 1]);
829 return wordc;
830 }
1bafb621 831 }
c36e914d 832
1bafb621 833 if (wordc_in != 1)
850c9265 834 aerr("parse_operand 1 word expected\n");
c36e914d 835
fdd5548a 836 ret = check_segment_prefix(words[w]);
837 if (ret != 0) {
838 if (ret >= 5)
839 aerr("fs/gs used\n");
ddaf8bd7 840 opr->had_ds = 1;
89ff3147 841 memmove(words[w], words[w] + 3, strlen(words[w]) - 2);
ddaf8bd7 842 }
89ff3147 843 strcpy(opr->name, words[w]);
c36e914d 844
850c9265 845 if (words[w][0] == '[') {
846 opr->type = OPT_REGMEM;
847 ret = sscanf(words[w], "[%[^]]]", opr->name);
848 if (ret != 1)
849 aerr("[] parse failure\n");
87bf6cec 850
5101a5f9 851 parse_indmode(opr->name, regmask_indirect, 1);
30620174 852 if (opr->lmod == OPLM_UNSPEC
853 && parse_stack_el(opr->name, NULL, NULL, 1))
09bc6fd5 854 {
87bf6cec 855 // might be an equ
d4e3b5db 856 struct parsed_equ *eq =
30620174 857 equ_find(NULL, parse_stack_el(opr->name, NULL, NULL, 1), &i);
87bf6cec 858 if (eq)
859 opr->lmod = eq->lmod;
d4a985bd 860
861 // might be unaligned access
862 g_func_lmods |= 1 << OPLM_BYTE;
87bf6cec 863 }
850c9265 864 return wordc;
865 }
866 else if (strchr(words[w], '[')) {
867 // label[reg] form
89ff3147 868 p = strchr(words[w], '[');
850c9265 869 opr->type = OPT_REGMEM;
89ff3147 870 parse_indmode(p, regmask_indirect, 0);
871 strncpy(buf, words[w], p - words[w]);
872 buf[p - words[w]] = 0;
873 pp = proto_parse(g_fhdr, buf, 1);
874 goto do_label;
850c9265 875 }
876 else if (('0' <= words[w][0] && words[w][0] <= '9')
877 || words[w][0] == '-')
878 {
e27467d0 879 number = parse_number(words[w], 0);
91977a1c 880 opr->type = OPT_CONST;
5101a5f9 881 opr->val = number;
882 printf_number(opr->name, sizeof(opr->name), number);
91977a1c 883 return wordc;
850c9265 884 }
c36e914d 885
5101a5f9 886 ret = parse_reg(&tmplmod, opr->name);
887 if (ret >= 0) {
888 setup_reg_opr(opr, ret, tmplmod, regmask);
850c9265 889 return wordc;
890 }
891
892 // most likely var in data segment
893 opr->type = OPT_LABEL;
92d715b6 894 pp = proto_parse(g_fhdr, opr->name, g_quiet_pp);
89ff3147 895
896do_label:
bd96f656 897 if (pp != NULL) {
1cd4a663 898 if (pp->is_fptr || pp->is_func) {
06c5d854 899 opr->lmod = OPLM_DWORD;
900 opr->is_ptr = 1;
901 }
2b43685d 902 else {
840257f6 903 tmplmod = OPLM_UNSPEC;
bd96f656 904 if (!guess_lmod_from_c_type(&tmplmod, &pp->type))
2b43685d 905 anote("unhandled C type '%s' for '%s'\n",
bd96f656 906 pp->type.name, opr->name);
2b43685d 907
a3684be1 908 if (opr->lmod == OPLM_UNSPEC) {
2b43685d 909 opr->lmod = tmplmod;
a3684be1 910 opr->type_from_var = 1;
911 }
2b43685d 912 else if (opr->lmod != tmplmod) {
913 opr->size_mismatch = 1;
914 if (tmplmod < opr->lmod)
915 opr->size_lt = 1;
916 }
a652aa9f 917 opr->is_ptr = pp->type.is_ptr;
3ebea2cf 918 }
bd96f656 919 opr->is_array = pp->type.is_array;
06c5d854 920 }
c7ed83dd 921 opr->pp = pp;
06c5d854 922
850c9265 923 if (opr->lmod == OPLM_UNSPEC)
924 guess_lmod_from_name(opr);
850c9265 925 return wordc;
c36e914d 926}
927
33c35af6 928static const struct {
929 const char *name;
930 unsigned int flags;
931} pref_table[] = {
932 { "rep", OPF_REP },
7ba45c34 933 { "repe", OPF_REP|OPF_REPZ },
934 { "repz", OPF_REP|OPF_REPZ },
935 { "repne", OPF_REP|OPF_REPNZ },
936 { "repnz", OPF_REP|OPF_REPNZ },
037f4971 937 { "lock", OPF_LOCK }, // ignored for now..
33c35af6 938};
939
5c024ef7 940#define OPF_CJMP_CC (OPF_JMP|OPF_CJMP|OPF_CC)
941
c36e914d 942static const struct {
850c9265 943 const char *name;
944 enum op_op op;
092f64e1 945 unsigned short minopr;
946 unsigned short maxopr;
69a3cdfc 947 unsigned int flags;
092f64e1 948 unsigned char pfo;
949 unsigned char pfo_inv;
c36e914d 950} op_table[] = {
33c35af6 951 { "nop", OP_NOP, 0, 0, 0 },
69a3cdfc 952 { "push", OP_PUSH, 1, 1, 0 },
953 { "pop", OP_POP, 1, 1, OPF_DATA },
16057ce1 954 { "pusha",OP_PUSHA, 0, 0, 0 },
955 { "popa", OP_POPA, 0, 0, OPF_DATA },
591721d7 956 { "leave",OP_LEAVE, 0, 0, OPF_DATA },
69a3cdfc 957 { "mov" , OP_MOV, 2, 2, OPF_DATA },
958 { "lea", OP_LEA, 2, 2, OPF_DATA },
959 { "movzx",OP_MOVZX, 2, 2, OPF_DATA },
960 { "movsx",OP_MOVSX, 2, 2, OPF_DATA },
108e9fe3 961 { "xchg", OP_XCHG, 2, 2, OPF_DATA },
69a3cdfc 962 { "not", OP_NOT, 1, 1, OPF_DATA },
04abc5d6 963 { "xlat", OP_XLAT, 0, 0, OPF_DATA },
5101a5f9 964 { "cdq", OP_CDQ, 0, 0, OPF_DATA },
622eb2ef 965 { "bswap",OP_BSWAP, 1, 1, OPF_DATA },
092f64e1 966 { "lodsb",OP_LODS, 0, 0, OPF_DATA },
967 { "lodsw",OP_LODS, 0, 0, OPF_DATA },
968 { "lodsd",OP_LODS, 0, 0, OPF_DATA },
33c35af6 969 { "stosb",OP_STOS, 0, 0, OPF_DATA },
970 { "stosw",OP_STOS, 0, 0, OPF_DATA },
971 { "stosd",OP_STOS, 0, 0, OPF_DATA },
d4e3b5db 972 { "movsb",OP_MOVS, 0, 0, OPF_DATA },
973 { "movsw",OP_MOVS, 0, 0, OPF_DATA },
974 { "movsd",OP_MOVS, 0, 0, OPF_DATA },
7ba45c34 975 { "cmpsb",OP_CMPS, 0, 0, OPF_DATA|OPF_FLAGS },
976 { "cmpsw",OP_CMPS, 0, 0, OPF_DATA|OPF_FLAGS },
977 { "cmpsd",OP_CMPS, 0, 0, OPF_DATA|OPF_FLAGS },
591721d7 978 { "scasb",OP_SCAS, 0, 0, OPF_DATA|OPF_FLAGS },
979 { "scasw",OP_SCAS, 0, 0, OPF_DATA|OPF_FLAGS },
980 { "scasd",OP_SCAS, 0, 0, OPF_DATA|OPF_FLAGS },
981 { "std", OP_STD, 0, 0, OPF_DATA }, // special flag
982 { "cld", OP_CLD, 0, 0, OPF_DATA },
69a3cdfc 983 { "add", OP_ADD, 2, 2, OPF_DATA|OPF_FLAGS },
984 { "sub", OP_SUB, 2, 2, OPF_DATA|OPF_FLAGS },
985 { "and", OP_AND, 2, 2, OPF_DATA|OPF_FLAGS },
986 { "or", OP_OR, 2, 2, OPF_DATA|OPF_FLAGS },
987 { "xor", OP_XOR, 2, 2, OPF_DATA|OPF_FLAGS },
988 { "shl", OP_SHL, 2, 2, OPF_DATA|OPF_FLAGS },
989 { "shr", OP_SHR, 2, 2, OPF_DATA|OPF_FLAGS },
990 { "sal", OP_SHL, 2, 2, OPF_DATA|OPF_FLAGS },
991 { "sar", OP_SAR, 2, 2, OPF_DATA|OPF_FLAGS },
04abc5d6 992 { "shld", OP_SHLD, 3, 3, OPF_DATA|OPF_FLAGS },
3b2f4044 993 { "shrd", OP_SHRD, 3, 3, OPF_DATA|OPF_FLAGS },
d4e3b5db 994 { "rol", OP_ROL, 2, 2, OPF_DATA|OPF_FLAGS },
995 { "ror", OP_ROR, 2, 2, OPF_DATA|OPF_FLAGS },
092f64e1 996 { "rcl", OP_RCL, 2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C },
997 { "rcr", OP_RCR, 2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C },
998 { "adc", OP_ADC, 2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C },
999 { "sbb", OP_SBB, 2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C },
1f84f6b3 1000 { "bsf", OP_BSF, 2, 2, OPF_DATA|OPF_FLAGS },
69a3cdfc 1001 { "inc", OP_INC, 1, 1, OPF_DATA|OPF_FLAGS },
1002 { "dec", OP_DEC, 1, 1, OPF_DATA|OPF_FLAGS },
5101a5f9 1003 { "neg", OP_NEG, 1, 1, OPF_DATA|OPF_FLAGS },
1004 { "mul", OP_MUL, 1, 1, OPF_DATA|OPF_FLAGS },
69a3cdfc 1005 { "imul", OP_IMUL, 1, 3, OPF_DATA|OPF_FLAGS },
5101a5f9 1006 { "div", OP_DIV, 1, 1, OPF_DATA|OPF_FLAGS },
1007 { "idiv", OP_IDIV, 1, 1, OPF_DATA|OPF_FLAGS },
69a3cdfc 1008 { "test", OP_TEST, 2, 2, OPF_FLAGS },
1009 { "cmp", OP_CMP, 2, 2, OPF_FLAGS },
a3684be1 1010 { "retn", OP_RET, 0, 1, OPF_TAIL },
de50b98b 1011 { "call", OP_CALL, 1, 1, OPF_JMP|OPF_DATA|OPF_FLAGS },
69a3cdfc 1012 { "jmp", OP_JMP, 1, 1, OPF_JMP },
5c024ef7 1013 { "jecxz",OP_JECXZ, 1, 1, OPF_JMP|OPF_CJMP },
04abc5d6 1014 { "loop", OP_LOOP, 1, 1, OPF_JMP|OPF_CJMP|OPF_DATA },
092f64e1 1015 { "jo", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_O, 0 }, // 70 OF=1
1016 { "jno", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_O, 1 }, // 71 OF=0
1017 { "jc", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_C, 0 }, // 72 CF=1
1018 { "jb", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_C, 0 }, // 72
1019 { "jnc", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_C, 1 }, // 73 CF=0
1020 { "jnb", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_C, 1 }, // 73
1021 { "jae", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_C, 1 }, // 73
1022 { "jz", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_Z, 0 }, // 74 ZF=1
1023 { "je", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_Z, 0 }, // 74
1024 { "jnz", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_Z, 1 }, // 75 ZF=0
1025 { "jne", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_Z, 1 }, // 75
1026 { "jbe", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_BE, 0 }, // 76 CF=1||ZF=1
1027 { "jna", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_BE, 0 }, // 76
1028 { "ja", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_BE, 1 }, // 77 CF=0&&ZF=0
1029 { "jnbe", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_BE, 1 }, // 77
1030 { "js", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_S, 0 }, // 78 SF=1
1031 { "jns", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_S, 1 }, // 79 SF=0
1032 { "jp", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_P, 0 }, // 7a PF=1
1033 { "jpe", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_P, 0 }, // 7a
1034 { "jnp", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_P, 1 }, // 7b PF=0
1035 { "jpo", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_P, 1 }, // 7b
1036 { "jl", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_L, 0 }, // 7c SF!=OF
1037 { "jnge", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_L, 0 }, // 7c
1038 { "jge", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_L, 1 }, // 7d SF=OF
1039 { "jnl", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_L, 1 }, // 7d
1040 { "jle", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_LE, 0 }, // 7e ZF=1||SF!=OF
1041 { "jng", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_LE, 0 }, // 7e
1042 { "jg", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_LE, 1 }, // 7f ZF=0&&SF=OF
1043 { "jnle", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_LE, 1 }, // 7f
1044 { "seto", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_O, 0 },
1045 { "setno", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_O, 1 },
1046 { "setc", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_C, 0 },
1047 { "setb", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_C, 0 },
1048 { "setnc", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_C, 1 },
1049 { "setae", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_C, 1 },
1050 { "setnb", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_C, 1 },
1051 { "setz", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_Z, 0 },
1052 { "sete", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_Z, 0 },
1053 { "setnz", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_Z, 1 },
1054 { "setne", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_Z, 1 },
1055 { "setbe", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_BE, 0 },
1056 { "setna", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_BE, 0 },
1057 { "seta", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_BE, 1 },
1058 { "setnbe", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_BE, 1 },
1059 { "sets", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_S, 0 },
1060 { "setns", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_S, 1 },
1061 { "setp", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_P, 0 },
1062 { "setpe", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_P, 0 },
1063 { "setnp", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_P, 1 },
1064 { "setpo", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_P, 1 },
1065 { "setl", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_L, 0 },
1066 { "setnge", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_L, 0 },
1067 { "setge", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_L, 1 },
1068 { "setnl", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_L, 1 },
1069 { "setle", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_LE, 0 },
1070 { "setng", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_LE, 0 },
1071 { "setg", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_LE, 1 },
1072 { "setnle", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_LE, 1 },
90307a99 1073 // x87
d4a985bd 1074 { "fld", OP_FLD, 1, 1, OPF_FPUSH },
1075 { "fild", OP_FILD, 1, 1, OPF_FPUSH },
1076 { "fld1", OP_FLDc, 0, 0, OPF_FPUSH },
fe18df39 1077 { "fldln2", OP_FLDc, 0, 0, OPF_FPUSH },
d4a985bd 1078 { "fldz", OP_FLDc, 0, 0, OPF_FPUSH },
d4a985bd 1079 { "fst", OP_FST, 1, 1, 0 },
16057ce1 1080 { "fstp", OP_FST, 1, 1, OPF_FPOP },
1081 { "fist", OP_FIST, 1, 1, 0 },
1082 { "fistp", OP_FIST, 1, 1, OPF_FPOP },
d4a985bd 1083 { "fadd", OP_FADD, 0, 2, 0 },
1084 { "faddp", OP_FADD, 0, 2, OPF_FPOP },
1085 { "fdiv", OP_FDIV, 0, 2, 0 },
1086 { "fdivp", OP_FDIV, 0, 2, OPF_FPOP },
1087 { "fmul", OP_FMUL, 0, 2, 0 },
1088 { "fmulp", OP_FMUL, 0, 2, OPF_FPOP },
1089 { "fsub", OP_FSUB, 0, 2, 0 },
1090 { "fsubp", OP_FSUB, 0, 2, OPF_FPOP },
1091 { "fdivr", OP_FDIVR, 0, 2, 0 },
1092 { "fdivrp", OP_FDIVR, 0, 2, OPF_FPOP },
1093 { "fsubr", OP_FSUBR, 0, 2, 0 },
1094 { "fsubrp", OP_FSUBR, 0, 2, OPF_FPOP },
1095 { "fiadd", OP_FIADD, 1, 1, 0 },
1096 { "fidiv", OP_FIDIV, 1, 1, 0 },
1097 { "fimul", OP_FIMUL, 1, 1, 0 },
1098 { "fisub", OP_FISUB, 1, 1, 0 },
1099 { "fidivr", OP_FIDIVR, 1, 1, 0 },
1100 { "fisubr", OP_FISUBR, 1, 1, 0 },
16057ce1 1101 { "fcom", OP_FCOM, 0, 1, 0 },
1102 { "fcomp", OP_FCOM, 0, 1, OPF_FPOP },
1103 { "fnstsw", OP_FNSTSW, 1, 1, OPF_DATA },
fe18df39 1104 { "fchs", OP_FCHS, 0, 0, 0 },
497a6d6b 1105 { "fcos", OP_FCOS, 0, 0, 0 },
1106 { "fpatan", OP_FPATAN, 0, 0, OPF_FPOP },
1107 { "fptan", OP_FPTAN, 0, 0, OPF_FPUSH },
1108 { "fsin", OP_FSIN, 0, 0, 0 },
1109 { "fsqrt", OP_FSQRT, 0, 0, 0 },
fe18df39 1110 { "fxch", OP_FXCH, 1, 1, 0 },
1111 { "fyl2x", OP_FYL2X, 0, 0, OPF_FPOP },
90307a99 1112 // mmx
d4a985bd 1113 { "emms", OP_EMMS, 0, 0, OPF_DATA },
1114 { "movq", OP_MOV, 2, 2, OPF_DATA },
1115 // pseudo-ops for lib calls
622eb2ef 1116 { "_allshl",OPP_ALLSHL },
1117 { "_allshr",OPP_ALLSHR },
d4a985bd 1118 { "_ftol", OPP_FTOL },
8c83cc48 1119 { "_CIpow", OPP_CIPOW },
2c10ea1f 1120 // must be last
1121 { "ud2", OP_UD2 },
69a3cdfc 1122};
c36e914d 1123
1124static void parse_op(struct parsed_op *op, char words[16][256], int wordc)
1125{
bfa4a6ee 1126 enum opr_lenmod lmod = OPLM_UNSPEC;
33c35af6 1127 int prefix_flags = 0;
69a3cdfc 1128 int regmask_ind;
1129 int regmask;
33c35af6 1130 int op_w = 0;
91977a1c 1131 int opr = 0;
33c35af6 1132 int w = 0;
acd03176 1133 int i, j;
c36e914d 1134
33c35af6 1135 for (i = 0; i < ARRAY_SIZE(pref_table); i++) {
1136 if (IS(words[w], pref_table[i].name)) {
1137 prefix_flags = pref_table[i].flags;
1138 break;
1139 }
1140 }
1141
1142 if (prefix_flags) {
1143 if (wordc <= 1)
1144 aerr("lone prefix: '%s'\n", words[0]);
1145 w++;
1146 }
1147
1148 op_w = w;
91977a1c 1149 for (i = 0; i < ARRAY_SIZE(op_table); i++) {
33c35af6 1150 if (IS(words[w], op_table[i].name))
69a3cdfc 1151 break;
1152 }
c36e914d 1153
2c10ea1f 1154 if (i == ARRAY_SIZE(op_table)) {
30c8c549 1155 if (!g_skip_func)
1156 aerr("unhandled op: '%s'\n", words[0]);
2c10ea1f 1157 i--; // OP_UD2
1158 }
33c35af6 1159 w++;
c36e914d 1160
69a3cdfc 1161 op->op = op_table[i].op;
33c35af6 1162 op->flags = op_table[i].flags | prefix_flags;
092f64e1 1163 op->pfo = op_table[i].pfo;
1164 op->pfo_inv = op_table[i].pfo_inv;
69a3cdfc 1165 op->regmask_src = op->regmask_dst = 0;
8eb12e72 1166 op->asmln = asmln;
69a3cdfc 1167
2c10ea1f 1168 if (op->op == OP_UD2)
1169 return;
1170
92d715b6 1171 for (opr = 0; opr < op_table[i].maxopr; opr++) {
1172 if (opr >= op_table[i].minopr && w >= wordc)
1173 break;
1174
69a3cdfc 1175 regmask = regmask_ind = 0;
1176 w = parse_operand(&op->operand[opr], &regmask, &regmask_ind,
1177 words, wordc, w, op->flags);
1178
1179 if (opr == 0 && (op->flags & OPF_DATA))
1180 op->regmask_dst = regmask;
92d715b6 1181 else
1182 op->regmask_src |= regmask;
1183 op->regmask_src |= regmask_ind;
d4a985bd 1184
1185 if (op->operand[opr].lmod != OPLM_UNSPEC)
1186 g_func_lmods |= 1 << op->operand[opr].lmod;
91977a1c 1187 }
c36e914d 1188
91977a1c 1189 if (w < wordc)
1190 aerr("parse_op %s incomplete: %d/%d\n",
1191 words[0], w, wordc);
5101a5f9 1192
1193 // special cases
1194 op->operand_cnt = opr;
1195 if (!strncmp(op_table[i].name, "set", 3))
1196 op->operand[0].lmod = OPLM_BYTE;
1197
5101a5f9 1198 switch (op->op) {
92d715b6 1199 // first operand is not dst
1200 case OP_CMP:
1201 case OP_TEST:
1202 op->regmask_src |= op->regmask_dst;
1203 op->regmask_dst = 0;
1204 break;
1205
1206 // first operand is src too
1207 case OP_NOT:
1208 case OP_ADD:
1209 case OP_AND:
1210 case OP_OR:
1211 case OP_RCL:
1212 case OP_RCR:
1213 case OP_ADC:
1214 case OP_INC:
1215 case OP_DEC:
1216 case OP_NEG:
622eb2ef 1217 case OP_BSWAP:
92d715b6 1218 // more below..
1219 op->regmask_src |= op->regmask_dst;
1220 break;
1221
1222 // special
1223 case OP_XCHG:
1224 op->regmask_src |= op->regmask_dst;
1225 op->regmask_dst |= op->regmask_src;
1226 goto check_align;
1227
1228 case OP_SUB:
1229 case OP_SBB:
1230 case OP_XOR:
1231 if (op->operand[0].type == OPT_REG && op->operand[1].type == OPT_REG
1232 && op->operand[0].lmod == op->operand[1].lmod
1233 && op->operand[0].reg == op->operand[1].reg
1234 && IS(op->operand[0].name, op->operand[1].name)) // ! ah, al..
1235 {
1236 op->regmask_src = 0;
1237 }
1238 else
1239 op->regmask_src |= op->regmask_dst;
1240 break;
1241
1242 // ops with implicit argumets
04abc5d6 1243 case OP_XLAT:
1244 op->operand_cnt = 2;
1245 setup_reg_opr(&op->operand[0], xAX, OPLM_BYTE, &op->regmask_src);
1246 op->regmask_dst = op->regmask_src;
3947cf24 1247 setup_reg_opr(&op->operand[1], xBX, OPLM_DWORD, &op->regmask_src);
04abc5d6 1248 break;
1249
5101a5f9 1250 case OP_CDQ:
1251 op->operand_cnt = 2;
1252 setup_reg_opr(&op->operand[0], xDX, OPLM_DWORD, &op->regmask_dst);
1253 setup_reg_opr(&op->operand[1], xAX, OPLM_DWORD, &op->regmask_src);
1254 break;
1255
092f64e1 1256 case OP_LODS:
33c35af6 1257 case OP_STOS:
591721d7 1258 case OP_SCAS:
591721d7 1259 if (words[op_w][4] == 'b')
33c35af6 1260 lmod = OPLM_BYTE;
591721d7 1261 else if (words[op_w][4] == 'w')
33c35af6 1262 lmod = OPLM_WORD;
591721d7 1263 else if (words[op_w][4] == 'd')
33c35af6 1264 lmod = OPLM_DWORD;
acd03176 1265 j = 0;
3947cf24 1266 op->regmask_src = 0;
acd03176 1267 setup_reg_opr(&op->operand[j++], op->op == OP_LODS ? xSI : xDI,
3947cf24 1268 OPLM_DWORD, &op->regmask_src);
d4e3b5db 1269 op->regmask_dst = op->regmask_src;
3947cf24 1270 setup_reg_opr(&op->operand[j++], xAX, lmod,
092f64e1 1271 op->op == OP_LODS ? &op->regmask_dst : &op->regmask_src);
3947cf24 1272 if (op->flags & OPF_REP) {
1273 setup_reg_opr(&op->operand[j++], xCX, OPLM_DWORD, &op->regmask_src);
1274 op->regmask_dst |= 1 << xCX;
1275 }
acd03176 1276 op->operand_cnt = j;
33c35af6 1277 break;
1278
d4e3b5db 1279 case OP_MOVS:
7ba45c34 1280 case OP_CMPS:
7ba45c34 1281 if (words[op_w][4] == 'b')
d4e3b5db 1282 lmod = OPLM_BYTE;
7ba45c34 1283 else if (words[op_w][4] == 'w')
d4e3b5db 1284 lmod = OPLM_WORD;
7ba45c34 1285 else if (words[op_w][4] == 'd')
d4e3b5db 1286 lmod = OPLM_DWORD;
acd03176 1287 j = 0;
3947cf24 1288 op->regmask_src = 0;
1289 // note: lmod is not correct, don't have where to place it
acd03176 1290 setup_reg_opr(&op->operand[j++], xDI, lmod, &op->regmask_src);
1291 setup_reg_opr(&op->operand[j++], xSI, OPLM_DWORD, &op->regmask_src);
1292 if (op->flags & OPF_REP)
1293 setup_reg_opr(&op->operand[j++], xCX, OPLM_DWORD, &op->regmask_src);
1294 op->operand_cnt = j;
d4e3b5db 1295 op->regmask_dst = op->regmask_src;
1296 break;
1297
04abc5d6 1298 case OP_LOOP:
1299 op->regmask_dst = 1 << xCX;
1300 // fallthrough
5c024ef7 1301 case OP_JECXZ:
04abc5d6 1302 op->operand_cnt = 2;
5c024ef7 1303 op->regmask_src = 1 << xCX;
04abc5d6 1304 op->operand[1].type = OPT_REG;
1305 op->operand[1].reg = xCX;
1306 op->operand[1].lmod = OPLM_DWORD;
5c024ef7 1307 break;
1308
5101a5f9 1309 case OP_IMUL:
b2bd20c0 1310 if (op->operand_cnt == 2) {
1311 if (op->operand[0].type != OPT_REG)
1312 aerr("reg expected\n");
1313 op->regmask_src |= 1 << op->operand[0].reg;
1314 }
5101a5f9 1315 if (op->operand_cnt != 1)
1316 break;
1317 // fallthrough
1318 case OP_MUL:
1319 // singleop mul
92d715b6 1320 op->regmask_src |= op->regmask_dst;
5101a5f9 1321 op->regmask_dst = (1 << xDX) | (1 << xAX);
5101a5f9 1322 if (op->operand[0].lmod == OPLM_UNSPEC)
1323 op->operand[0].lmod = OPLM_DWORD;
1324 break;
1325
1326 case OP_DIV:
1327 case OP_IDIV:
1328 // we could set up operands for edx:eax, but there is no real need to
1329 // (see is_opr_modified())
92d715b6 1330 op->regmask_src |= op->regmask_dst;
1331 op->regmask_dst = (1 << xDX) | (1 << xAX);
5101a5f9 1332 if (op->operand[0].lmod == OPLM_UNSPEC)
1333 op->operand[0].lmod = OPLM_DWORD;
1334 break;
1335
1336 case OP_SHL:
1337 case OP_SHR:
1338 case OP_SAR:
d4e3b5db 1339 case OP_ROL:
1340 case OP_ROR:
92d715b6 1341 op->regmask_src |= op->regmask_dst;
5101a5f9 1342 if (op->operand[1].lmod == OPLM_UNSPEC)
1343 op->operand[1].lmod = OPLM_BYTE;
1344 break;
1345
acd03176 1346 case OP_SHLD:
3b2f4044 1347 case OP_SHRD:
92d715b6 1348 op->regmask_src |= op->regmask_dst;
3b2f4044 1349 if (op->operand[2].lmod == OPLM_UNSPEC)
1350 op->operand[2].lmod = OPLM_BYTE;
1351 break;
1352
7ba45c34 1353 case OP_PUSH:
92d715b6 1354 op->regmask_src |= op->regmask_dst;
1355 op->regmask_dst = 0;
7ba45c34 1356 if (op->operand[0].lmod == OPLM_UNSPEC
1357 && (op->operand[0].type == OPT_CONST
1358 || op->operand[0].type == OPT_OFFSET
1359 || op->operand[0].type == OPT_LABEL))
1360 op->operand[0].lmod = OPLM_DWORD;
1361 break;
1362
3ebea2cf 1363 // alignment
1364 case OP_MOV:
092f64e1 1365 check_align:
3ebea2cf 1366 if (op->operand[0].type == OPT_REG && op->operand[1].type == OPT_REG
092f64e1 1367 && op->operand[0].lmod == op->operand[1].lmod
1368 && op->operand[0].reg == op->operand[1].reg
92d715b6 1369 && IS(op->operand[0].name, op->operand[1].name)) // ! ah, al..
3ebea2cf 1370 {
b2bd20c0 1371 op->flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
092f64e1 1372 op->regmask_src = op->regmask_dst = 0;
3ebea2cf 1373 }
1374 break;
1375
de50b98b 1376 case OP_LEA:
1377 if (op->operand[0].type == OPT_REG
1378 && op->operand[1].type == OPT_REGMEM)
1379 {
1380 char buf[16];
1381 snprintf(buf, sizeof(buf), "%s+0", op->operand[0].name);
1382 if (IS(buf, op->operand[1].name))
b2bd20c0 1383 op->flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
de50b98b 1384 }
1385 break;
1386
89ff3147 1387 case OP_CALL:
1388 // trashed regs must be explicitly detected later
1389 op->regmask_dst = 0;
1390 break;
1391
92d715b6 1392 case OP_LEAVE:
1393 op->regmask_dst = (1 << xBP) | (1 << xSP);
1394 op->regmask_src = 1 << xBP;
1395 break;
1396
d4a985bd 1397 case OP_FLD:
1398 case OP_FILD:
1399 op->regmask_dst |= mxST0;
1400 break;
1401
1402 case OP_FLDc:
1403 op->regmask_dst |= mxST0;
1404 if (IS(words[op_w] + 3, "1"))
1405 op->operand[0].val = X87_CONST_1;
fe18df39 1406 else if (IS(words[op_w] + 3, "ln2"))
1407 op->operand[0].val = X87_CONST_LN2;
d4a985bd 1408 else if (IS(words[op_w] + 3, "z"))
1409 op->operand[0].val = X87_CONST_Z;
1410 else
1411 aerr("TODO\n");
1412 break;
1413
1414 case OP_FST:
16057ce1 1415 case OP_FIST:
d4a985bd 1416 op->regmask_src |= mxST0;
1417 break;
1418
1419 case OP_FADD:
1420 case OP_FDIV:
1421 case OP_FMUL:
1422 case OP_FSUB:
1423 case OP_FDIVR:
1424 case OP_FSUBR:
1425 op->regmask_src |= mxST0;
1426 if (op->operand_cnt == 2)
1427 op->regmask_src |= op->regmask_dst;
1428 else if (op->operand_cnt == 1) {
1429 memcpy(&op->operand[1], &op->operand[0], sizeof(op->operand[1]));
1430 op->operand[0].type = OPT_REG;
1431 op->operand[0].lmod = OPLM_QWORD;
1432 op->operand[0].reg = xST0;
1433 op->regmask_dst |= mxST0;
1434 }
1435 else
1436 // IDA doesn't use this
1437 aerr("no operands?\n");
1438 break;
1439
1440 case OP_FIADD:
1441 case OP_FIDIV:
1442 case OP_FIMUL:
1443 case OP_FISUB:
1444 case OP_FIDIVR:
1445 case OP_FISUBR:
fe18df39 1446 case OP_FCHS:
497a6d6b 1447 case OP_FCOS:
1448 case OP_FSIN:
1449 case OP_FSQRT:
fe18df39 1450 case OP_FXCH:
d4a985bd 1451 op->regmask_src |= mxST0;
1452 op->regmask_dst |= mxST0;
1453 break;
1454
497a6d6b 1455 case OP_FPATAN:
fe18df39 1456 case OP_FYL2X:
497a6d6b 1457 op->regmask_src |= mxST0 | mxST1;
1458 op->regmask_dst |= mxST0;
1459 break;
1460
1461 case OP_FPTAN:
1462 aerr("TODO\n");
1463 break;
1464
16057ce1 1465 case OP_FCOM:
1466 op->regmask_src |= mxST0;
1467 break;
1468
5101a5f9 1469 default:
1470 break;
1471 }
2b70f6d3 1472
1473 if (op->operand[0].type == OPT_REG
2b70f6d3 1474 && op->operand[1].type == OPT_CONST)
1475 {
acd03176 1476 struct parsed_opr *op1 = &op->operand[1];
1477 if ((op->op == OP_AND && op1->val == 0)
1478 || (op->op == OP_OR
1479 && (op1->val == ~0
1480 || (op->operand[0].lmod == OPLM_WORD && op1->val == 0xffff)
1481 || (op->operand[0].lmod == OPLM_BYTE && op1->val == 0xff))))
2b70f6d3 1482 {
1483 op->regmask_src = 0;
1484 }
1485 }
c36e914d 1486}
1487
092f64e1 1488static const char *op_name(struct parsed_op *po)
850c9265 1489{
092f64e1 1490 static char buf[16];
1491 char *p;
850c9265 1492 int i;
1493
092f64e1 1494 if (po->op == OP_JCC || po->op == OP_SCC) {
1495 p = buf;
1496 *p++ = (po->op == OP_JCC) ? 'j' : 's';
1497 if (po->pfo_inv)
1498 *p++ = 'n';
1499 strcpy(p, parsed_flag_op_names[po->pfo]);
1500 return buf;
1501 }
1502
850c9265 1503 for (i = 0; i < ARRAY_SIZE(op_table); i++)
092f64e1 1504 if (op_table[i].op == po->op)
850c9265 1505 return op_table[i].name;
1506
1507 return "???";
1508}
1509
1510// debug
1511static const char *dump_op(struct parsed_op *po)
1512{
1513 static char out[128];
1514 char *p = out;
1515 int i;
1516
4c45fa73 1517 if (po == NULL)
1518 return "???";
1519
092f64e1 1520 snprintf(out, sizeof(out), "%s", op_name(po));
850c9265 1521 for (i = 0; i < po->operand_cnt; i++) {
1522 p += strlen(p);
1523 if (i > 0)
1524 *p++ = ',';
1525 snprintf(p, sizeof(out) - (p - out),
1526 po->operand[i].type == OPT_REGMEM ? " [%s]" : " %s",
1527 po->operand[i].name);
1528 }
1529
1530 return out;
1531}
1532
4c45fa73 1533static const char *lmod_type_u(struct parsed_op *po,
1534 enum opr_lenmod lmod)
1535{
1536 switch (lmod) {
90307a99 1537 case OPLM_QWORD:
1538 return "u64";
4c45fa73 1539 case OPLM_DWORD:
1540 return "u32";
1541 case OPLM_WORD:
1542 return "u16";
1543 case OPLM_BYTE:
1544 return "u8";
1545 default:
1546 ferr(po, "invalid lmod: %d\n", lmod);
1547 return "(_invalid_)";
1548 }
1549}
1550
d4e3b5db 1551static const char *lmod_cast_u(struct parsed_op *po,
1552 enum opr_lenmod lmod)
1553{
1554 switch (lmod) {
90307a99 1555 case OPLM_QWORD:
1556 return "";
d4e3b5db 1557 case OPLM_DWORD:
1558 return "";
1559 case OPLM_WORD:
1560 return "(u16)";
1561 case OPLM_BYTE:
1562 return "(u8)";
1563 default:
1564 ferr(po, "invalid lmod: %d\n", lmod);
1565 return "(_invalid_)";
1566 }
1567}
1568
1569static const char *lmod_cast_u_ptr(struct parsed_op *po,
1570 enum opr_lenmod lmod)
1571{
1572 switch (lmod) {
90307a99 1573 case OPLM_QWORD:
1574 return "*(u64 *)";
d4e3b5db 1575 case OPLM_DWORD:
1576 return "*(u32 *)";
1577 case OPLM_WORD:
1578 return "*(u16 *)";
1579 case OPLM_BYTE:
1580 return "*(u8 *)";
1581 default:
1582 ferr(po, "invalid lmod: %d\n", lmod);
1583 return "(_invalid_)";
1584 }
1585}
1586
1587static const char *lmod_cast_s(struct parsed_op *po,
1588 enum opr_lenmod lmod)
1589{
1590 switch (lmod) {
90307a99 1591 case OPLM_QWORD:
1592 return "(s64)";
d4e3b5db 1593 case OPLM_DWORD:
1594 return "(s32)";
1595 case OPLM_WORD:
1596 return "(s16)";
1597 case OPLM_BYTE:
1598 return "(s8)";
1599 default:
1600 ferr(po, "%s: invalid lmod: %d\n", __func__, lmod);
1601 return "(_invalid_)";
1602 }
1603}
1604
1605static const char *lmod_cast(struct parsed_op *po,
1606 enum opr_lenmod lmod, int is_signed)
1607{
1608 return is_signed ?
1609 lmod_cast_s(po, lmod) :
1610 lmod_cast_u(po, lmod);
1611}
1612
1613static int lmod_bytes(struct parsed_op *po, enum opr_lenmod lmod)
1614{
1615 switch (lmod) {
90307a99 1616 case OPLM_QWORD:
1617 return 8;
d4e3b5db 1618 case OPLM_DWORD:
1619 return 4;
1620 case OPLM_WORD:
1621 return 2;
1622 case OPLM_BYTE:
1623 return 1;
1624 default:
1625 ferr(po, "%s: invalid lmod: %d\n", __func__, lmod);
1626 return 0;
1627 }
1628}
1629
91977a1c 1630static const char *opr_name(struct parsed_op *po, int opr_num)
c36e914d 1631{
91977a1c 1632 if (opr_num >= po->operand_cnt)
1633 ferr(po, "opr OOR: %d/%d\n", opr_num, po->operand_cnt);
1634 return po->operand[opr_num].name;
c36e914d 1635}
1636
91977a1c 1637static unsigned int opr_const(struct parsed_op *po, int opr_num)
c36e914d 1638{
91977a1c 1639 if (opr_num >= po->operand_cnt)
1640 ferr(po, "opr OOR: %d/%d\n", opr_num, po->operand_cnt);
1641 if (po->operand[opr_num].type != OPT_CONST)
1642 ferr(po, "opr %d: const expected\n", opr_num);
1643 return po->operand[opr_num].val;
1644}
c36e914d 1645
91977a1c 1646static const char *opr_reg_p(struct parsed_op *po, struct parsed_opr *popr)
1647{
90307a99 1648 if ((unsigned int)popr->reg >= ARRAY_SIZE(regs_r32))
91977a1c 1649 ferr(po, "invalid reg: %d\n", popr->reg);
1650 return regs_r32[popr->reg];
1651}
c36e914d 1652
e7f5bc39 1653static int check_simple_cast(const char *cast, int *bits, int *is_signed)
1654{
1655 if (IS_START(cast, "(s8)") || IS_START(cast, "(u8)"))
1656 *bits = 8;
1657 else if (IS_START(cast, "(s16)") || IS_START(cast, "(u16)"))
1658 *bits = 16;
1659 else if (IS_START(cast, "(s32)") || IS_START(cast, "(u32)"))
1660 *bits = 32;
1661 else if (IS_START(cast, "(s64)") || IS_START(cast, "(u64)"))
1662 *bits = 64;
1663 else
1664 return -1;
1665
1666 *is_signed = cast[1] == 's' ? 1 : 0;
1667 return 0;
1668}
1669
1670static int check_deref_cast(const char *cast, int *bits)
1671{
1672 if (IS_START(cast, "*(u8 *)"))
1673 *bits = 8;
1674 else if (IS_START(cast, "*(u16 *)"))
1675 *bits = 16;
1676 else if (IS_START(cast, "*(u32 *)"))
1677 *bits = 32;
1678 else if (IS_START(cast, "*(u64 *)"))
1679 *bits = 64;
1680 else
1681 return -1;
1682
1683 return 0;
1684}
1685
a2c1d768 1686// cast1 is the "final" cast
1687static const char *simplify_cast(const char *cast1, const char *cast2)
1688{
1689 static char buf[256];
e7f5bc39 1690 int bits1, bits2;
1691 int s1, s2;
a2c1d768 1692
1693 if (cast1[0] == 0)
1694 return cast2;
1695 if (cast2[0] == 0)
1696 return cast1;
1697 if (IS(cast1, cast2))
1698 return cast1;
e7f5bc39 1699
1700 if (check_simple_cast(cast1, &bits1, &s1) == 0
1701 && check_simple_cast(cast2, &bits2, &s2) == 0)
1702 {
1703 if (bits1 <= bits2)
1704 return cast1;
1705 }
1706 if (check_simple_cast(cast1, &bits1, &s1) == 0
1707 && check_deref_cast(cast2, &bits2) == 0)
1708 {
1709 if (bits1 == bits2) {
1710 snprintf(buf, sizeof(buf), "*(%c%d *)", s1 ? 's' : 'u', bits1);
1711 return buf;
1712 }
1713 }
1714
1cd4a663 1715 if (strchr(cast1, '*') && IS_START(cast2, "(u32)"))
1716 return cast1;
a2c1d768 1717
1718 snprintf(buf, sizeof(buf), "%s%s", cast1, cast2);
1719 return buf;
1720}
1721
1722static const char *simplify_cast_num(const char *cast, unsigned int val)
1723{
1724 if (IS(cast, "(u8)") && val < 0x100)
1725 return "";
1726 if (IS(cast, "(s8)") && val < 0x80)
1727 return "";
1728 if (IS(cast, "(u16)") && val < 0x10000)
1729 return "";
1730 if (IS(cast, "(s16)") && val < 0x8000)
1731 return "";
1732 if (IS(cast, "(s32)") && val < 0x80000000)
1733 return "";
1734
1735 return cast;
1736}
1737
39b168b8 1738static struct parsed_equ *equ_find(struct parsed_op *po, const char *name,
1739 int *extra_offs)
91977a1c 1740{
39b168b8 1741 const char *p;
1742 char *endp;
1743 int namelen;
850c9265 1744 int i;
1745
39b168b8 1746 *extra_offs = 0;
1747 namelen = strlen(name);
1748
c8fb3694 1749 p = strpbrk(name, "+-");
39b168b8 1750 if (p != NULL) {
1751 namelen = p - name;
1752 if (namelen <= 0)
1753 ferr(po, "equ parse failed for '%s'\n", name);
1754
e27467d0 1755 errno = 0;
39b168b8 1756 *extra_offs = strtol(p, &endp, 16);
e27467d0 1757 if (*endp != 0 || errno != 0)
39b168b8 1758 ferr(po, "equ parse failed for '%s'\n", name);
1759 }
1760
850c9265 1761 for (i = 0; i < g_eqcnt; i++)
39b168b8 1762 if (strncmp(g_eqs[i].name, name, namelen) == 0
1763 && g_eqs[i].name[namelen] == 0)
850c9265 1764 break;
87bf6cec 1765 if (i >= g_eqcnt) {
1766 if (po != NULL)
1767 ferr(po, "unresolved equ name: '%s'\n", name);
1768 return NULL;
1769 }
850c9265 1770
1771 return &g_eqs[i];
1772}
1773
1cd4a663 1774static int is_stack_access(struct parsed_op *po,
1775 const struct parsed_opr *popr)
850c9265 1776{
30620174 1777 return (parse_stack_el(popr->name, NULL, NULL, 0)
1cd4a663 1778 || (g_bp_frame && !(po->flags & OPF_EBP_S)
1779 && IS_START(popr->name, "ebp")));
1780}
1781
1782static void parse_stack_access(struct parsed_op *po,
1783 const char *name, char *ofs_reg, int *offset_out,
037f4971 1784 int *stack_ra_out, const char **bp_arg_out, int is_lea)
1cd4a663 1785{
1786 const char *bp_arg = "";
1787 const char *p = NULL;
91977a1c 1788 struct parsed_equ *eq;
4f12f671 1789 char *endp = NULL;
d4e3b5db 1790 int stack_ra = 0;
39b168b8 1791 int offset = 0;
91977a1c 1792
1cd4a663 1793 ofs_reg[0] = 0;
a3684be1 1794
1795 if (IS_START(name, "ebp-")
1796 || (IS_START(name, "ebp+") && '0' <= name[4] && name[4] <= '9'))
1797 {
1798 p = name + 4;
1799 if (IS_START(p, "0x"))
1800 p += 2;
e27467d0 1801 errno = 0;
a3684be1 1802 offset = strtoul(p, &endp, 16);
1803 if (name[3] == '-')
1804 offset = -offset;
e27467d0 1805 if (*endp != 0 || errno != 0)
a3684be1 1806 ferr(po, "ebp- parse of '%s' failed\n", name);
1807 }
1808 else {
30620174 1809 bp_arg = parse_stack_el(name, ofs_reg, NULL, 0);
4f12f671 1810 eq = equ_find(po, bp_arg, &offset);
1811 if (eq == NULL)
1812 ferr(po, "detected but missing eq\n");
1813 offset += eq->offset;
1814 }
39b168b8 1815
d4e3b5db 1816 if (!strncmp(name, "ebp", 3))
1817 stack_ra = 4;
1818
037f4971 1819 // yes it sometimes LEAs ra for compares..
1820 if (!is_lea && ofs_reg[0] == 0
1821 && stack_ra <= offset && offset < stack_ra + 4)
1822 {
39b168b8 1823 ferr(po, "reference to ra? %d %d\n", offset, stack_ra);
037f4971 1824 }
d4e3b5db 1825
1cd4a663 1826 *offset_out = offset;
30620174 1827 if (stack_ra_out)
1828 *stack_ra_out = stack_ra;
1cd4a663 1829 if (bp_arg_out)
1830 *bp_arg_out = bp_arg;
1831}
1832
30620174 1833static int parse_stack_esp_offset(struct parsed_op *po,
1834 const char *name, int *offset_out)
1835{
1836 char ofs_reg[16] = { 0, };
1837 struct parsed_equ *eq;
1838 const char *bp_arg;
1839 char *endp = NULL;
1840 int base_val = 0;
1841 int offset = 0;
1842
1843 if (strstr(name, "esp") == NULL)
1844 return -1;
1845 bp_arg = parse_stack_el(name, ofs_reg, &base_val, 0);
1846 if (bp_arg == NULL) {
1847 // just plain offset?
1848 if (!IS_START(name, "esp+"))
1849 return -1;
e27467d0 1850 errno = 0;
30620174 1851 offset = strtol(name + 4, &endp, 0);
e27467d0 1852 if (endp == NULL || *endp != 0 || errno != 0)
30620174 1853 return -1;
1854 *offset_out = offset;
1855 return 0;
1856 }
1857
1858 if (ofs_reg[0] != 0)
1859 return -1;
1860 eq = equ_find(po, bp_arg, &offset);
1861 if (eq == NULL)
1862 ferr(po, "detected but missing eq\n");
1863 offset += eq->offset;
1864 *offset_out = base_val + offset;
1865 return 0;
1866}
1867
5f70a34f 1868static int stack_frame_access(struct parsed_op *po,
1cd4a663 1869 struct parsed_opr *popr, char *buf, size_t buf_size,
1870 const char *name, const char *cast, int is_src, int is_lea)
1871{
1872 enum opr_lenmod tmp_lmod = OPLM_UNSPEC;
1873 const char *prefix = "";
1874 const char *bp_arg = NULL;
1875 char ofs_reg[16] = { 0, };
1876 int i, arg_i, arg_s;
1877 int unaligned = 0;
1878 int stack_ra = 0;
1879 int offset = 0;
5f70a34f 1880 int retval = -1;
1cd4a663 1881 int sf_ofs;
1cd4a663 1882
1883 if (po->flags & OPF_EBP_S)
1884 ferr(po, "stack_frame_access while ebp is scratch\n");
1885
037f4971 1886 parse_stack_access(po, name, ofs_reg, &offset,
1887 &stack_ra, &bp_arg, is_lea);
1cd4a663 1888
93b5bd18 1889 snprintf(g_comment, sizeof(g_comment), "%s", bp_arg);
1890
4f12f671 1891 if (offset > stack_ra)
1892 {
39b168b8 1893 arg_i = (offset - stack_ra - 4) / 4;
bd96f656 1894 if (arg_i < 0 || arg_i >= g_func_pp->argc_stack)
4f12f671 1895 {
bd96f656 1896 if (g_func_pp->is_vararg
1897 && arg_i == g_func_pp->argc_stack && is_lea)
1898 {
4f12f671 1899 // should be va_list
1900 if (cast[0] == 0)
1901 cast = "(u32)";
1902 snprintf(buf, buf_size, "%sap", cast);
5f70a34f 1903 return -1;
4f12f671 1904 }
1905 ferr(po, "offset %d (%s,%d) doesn't map to any arg\n",
1906 offset, bp_arg, arg_i);
1907 }
4c45fa73 1908 if (ofs_reg[0] != 0)
7ba45c34 1909 ferr(po, "offset reg on arg access?\n");
91977a1c 1910
bd96f656 1911 for (i = arg_s = 0; i < g_func_pp->argc; i++) {
1912 if (g_func_pp->arg[i].reg != NULL)
91977a1c 1913 continue;
1914 if (arg_s == arg_i)
1915 break;
1916 arg_s++;
1917 }
bd96f656 1918 if (i == g_func_pp->argc)
91977a1c 1919 ferr(po, "arg %d not in prototype?\n", arg_i);
850c9265 1920
bd96f656 1921 popr->is_ptr = g_func_pp->arg[i].type.is_ptr;
5f70a34f 1922 retval = i;
de50b98b 1923
1924 switch (popr->lmod)
7ba45c34 1925 {
1926 case OPLM_BYTE:
4f12f671 1927 if (is_lea)
1928 ferr(po, "lea/byte to arg?\n");
7ba45c34 1929 if (is_src && (offset & 3) == 0)
a2c1d768 1930 snprintf(buf, buf_size, "%sa%d",
1931 simplify_cast(cast, "(u8)"), i + 1);
7ba45c34 1932 else
a2c1d768 1933 snprintf(buf, buf_size, "%sBYTE%d(a%d)",
1934 cast, offset & 3, i + 1);
7ba45c34 1935 break;
1936
1937 case OPLM_WORD:
4f12f671 1938 if (is_lea)
1939 ferr(po, "lea/word to arg?\n");
a3684be1 1940 if (offset & 1) {
1941 unaligned = 1;
1942 if (!is_src) {
1943 if (offset & 2)
1944 ferr(po, "problematic arg store\n");
a2c1d768 1945 snprintf(buf, buf_size, "%s((char *)&a%d + 1)",
1946 simplify_cast(cast, "*(u16 *)"), i + 1);
a3684be1 1947 }
1948 else
1949 ferr(po, "unaligned arg word load\n");
1950 }
1951 else if (is_src && (offset & 2) == 0)
a2c1d768 1952 snprintf(buf, buf_size, "%sa%d",
1953 simplify_cast(cast, "(u16)"), i + 1);
7ba45c34 1954 else
a2c1d768 1955 snprintf(buf, buf_size, "%s%sWORD(a%d)",
1956 cast, (offset & 2) ? "HI" : "LO", i + 1);
7ba45c34 1957 break;
1958
1959 case OPLM_DWORD:
3ebea2cf 1960 if (cast[0])
1961 prefix = cast;
1962 else if (is_src)
1963 prefix = "(u32)";
a3684be1 1964
de50b98b 1965 if (offset & 3) {
a3684be1 1966 unaligned = 1;
2b43685d 1967 if (is_lea)
1968 snprintf(buf, buf_size, "(u32)&a%d + %d",
1969 i + 1, offset & 3);
a3684be1 1970 else if (!is_src)
1971 ferr(po, "unaligned arg store\n");
1972 else {
1973 // mov edx, [ebp+arg_4+2]; movsx ecx, dx
2b43685d 1974 snprintf(buf, buf_size, "%s(a%d >> %d)",
1975 prefix, i + 1, (offset & 3) * 8);
a3684be1 1976 }
de50b98b 1977 }
1978 else {
1979 snprintf(buf, buf_size, "%s%sa%d",
1980 prefix, is_lea ? "&" : "", i + 1);
1981 }
7ba45c34 1982 break;
1983
1984 default:
de50b98b 1985 ferr(po, "bp_arg bad lmod: %d\n", popr->lmod);
7ba45c34 1986 }
1987
a3684be1 1988 if (unaligned)
93b5bd18 1989 strcat(g_comment, " unaligned");
a3684be1 1990
7ba45c34 1991 // common problem
bd96f656 1992 guess_lmod_from_c_type(&tmp_lmod, &g_func_pp->arg[i].type);
a2c1d768 1993 if (tmp_lmod != OPLM_DWORD
de041e5b 1994 && (unaligned || (!is_src && lmod_bytes(po, tmp_lmod)
1995 < lmod_bytes(po, popr->lmod) + (offset & 3))))
a2c1d768 1996 {
1997 ferr(po, "bp_arg arg%d/w offset %d and type '%s' is too small\n",
1998 i + 1, offset, g_func_pp->arg[i].type.name);
1999 }
4741fdfe 2000 // can't check this because msvc likes to reuse
2001 // arg space for scratch..
2002 //if (popr->is_ptr && popr->lmod != OPLM_DWORD)
2003 // ferr(po, "bp_arg arg%d: non-dword ptr access\n", i + 1);
91977a1c 2004 }
4f12f671 2005 else
2006 {
1bafb621 2007 if (g_stack_fsz == 0)
2008 ferr(po, "stack var access without stackframe\n");
a2c1d768 2009 g_stack_frame_used = 1;
850c9265 2010
39b168b8 2011 sf_ofs = g_stack_fsz + offset;
c8fb3694 2012 if (ofs_reg[0] == 0 && (offset > 0 || sf_ofs < 0))
39b168b8 2013 ferr(po, "bp_stack offset %d/%d\n", offset, g_stack_fsz);
850c9265 2014
2015 if (is_lea)
33c35af6 2016 prefix = "(u32)&";
3ebea2cf 2017 else
2018 prefix = cast;
850c9265 2019
de50b98b 2020 switch (popr->lmod)
850c9265 2021 {
2022 case OPLM_BYTE:
7ba45c34 2023 snprintf(buf, buf_size, "%ssf.b[%d%s%s]",
2024 prefix, sf_ofs, ofs_reg[0] ? "+" : "", ofs_reg);
850c9265 2025 break;
7ba45c34 2026
850c9265 2027 case OPLM_WORD:
7ba45c34 2028 if ((sf_ofs & 1) || ofs_reg[0] != 0) {
2029 // known unaligned or possibly unaligned
2030 strcat(g_comment, " unaligned");
2031 if (prefix[0] == 0)
2032 prefix = "*(u16 *)&";
2033 snprintf(buf, buf_size, "%ssf.b[%d%s%s]",
2034 prefix, sf_ofs, ofs_reg[0] ? "+" : "", ofs_reg);
2035 break;
2036 }
2037 snprintf(buf, buf_size, "%ssf.w[%d]", prefix, sf_ofs / 2);
850c9265 2038 break;
7ba45c34 2039
850c9265 2040 case OPLM_DWORD:
7ba45c34 2041 if ((sf_ofs & 3) || ofs_reg[0] != 0) {
2042 // known unaligned or possibly unaligned
2043 strcat(g_comment, " unaligned");
2044 if (prefix[0] == 0)
2045 prefix = "*(u32 *)&";
2046 snprintf(buf, buf_size, "%ssf.b[%d%s%s]",
2047 prefix, sf_ofs, ofs_reg[0] ? "+" : "", ofs_reg);
2048 break;
2049 }
2050 snprintf(buf, buf_size, "%ssf.d[%d]", prefix, sf_ofs / 4);
850c9265 2051 break;
7ba45c34 2052
d4a985bd 2053 case OPLM_QWORD:
2054 ferr_assert(po, !(sf_ofs & 7));
2055 ferr_assert(po, ofs_reg[0] == 0);
8c83cc48 2056 // only used for x87 int64/float, float sets is_lea
2057 if (is_lea)
2058 snprintf(buf, buf_size, "%ssf.q[%d]", prefix, sf_ofs / 8);
2059 else
2060 snprintf(buf, buf_size, "*(s64 *)&sf.q[%d]", sf_ofs / 8);
d4a985bd 2061 break;
2062
850c9265 2063 default:
de50b98b 2064 ferr(po, "bp_stack bad lmod: %d\n", popr->lmod);
850c9265 2065 }
91977a1c 2066 }
5f70a34f 2067
2068 return retval;
91977a1c 2069}
c36e914d 2070
89ff3147 2071static void check_func_pp(struct parsed_op *po,
2072 const struct parsed_proto *pp, const char *pfx)
2073{
179b79a9 2074 enum opr_lenmod tmp_lmod;
b74c31e3 2075 char buf[256];
179b79a9 2076 int ret, i;
b74c31e3 2077
89ff3147 2078 if (pp->argc_reg != 0) {
8c83cc48 2079 if (!g_allow_user_icall && !pp->is_fastcall) {
b74c31e3 2080 pp_print(buf, sizeof(buf), pp);
2081 ferr(po, "%s: unexpected reg arg in icall: %s\n", pfx, buf);
2082 }
89ff3147 2083 if (pp->argc_stack > 0 && pp->argc_reg != 2)
2084 ferr(po, "%s: %d reg arg(s) with %d stack arg(s)\n",
2085 pfx, pp->argc_reg, pp->argc_stack);
2086 }
179b79a9 2087
2088 // fptrs must use 32bit args, callsite might have no information and
2089 // lack a cast to smaller types, which results in incorrectly masked
2090 // args passed (callee may assume masked args, it does on ARM)
61e29183 2091 if (!pp->is_osinc) {
179b79a9 2092 for (i = 0; i < pp->argc; i++) {
2093 ret = guess_lmod_from_c_type(&tmp_lmod, &pp->arg[i].type);
2094 if (ret && tmp_lmod != OPLM_DWORD)
2095 ferr(po, "reference to %s with arg%d '%s'\n", pp->name,
2096 i + 1, pp->arg[i].type.name);
2097 }
2098 }
89ff3147 2099}
2100
7aca4698 2101static const char *check_label_read_ref(struct parsed_op *po,
2102 const char *name)
3ebea2cf 2103{
840257f6 2104 const struct parsed_proto *pp;
2105
36595fd2 2106 pp = proto_parse(g_fhdr, name, 0);
840257f6 2107 if (pp == NULL)
2108 ferr(po, "proto_parse failed for ref '%s'\n", name);
2109
89ff3147 2110 if (pp->is_func)
2111 check_func_pp(po, pp, "ref");
7aca4698 2112
2113 return pp->name;
3ebea2cf 2114}
2115
91977a1c 2116static char *out_src_opr(char *buf, size_t buf_size,
591721d7 2117 struct parsed_op *po, struct parsed_opr *popr, const char *cast,
3ebea2cf 2118 int is_lea)
91977a1c 2119{
850c9265 2120 char tmp1[256], tmp2[256];
2121 char expr[256];
7aca4698 2122 const char *name;
a2c1d768 2123 char *p;
850c9265 2124 int ret;
2125
3ebea2cf 2126 if (cast == NULL)
2127 cast = "";
2128
91977a1c 2129 switch (popr->type) {
2130 case OPT_REG:
850c9265 2131 if (is_lea)
2132 ferr(po, "lea from reg?\n");
2133
91977a1c 2134 switch (popr->lmod) {
90307a99 2135 case OPLM_QWORD:
2136 snprintf(buf, buf_size, "%s%s.q", cast, opr_reg_p(po, popr));
2137 break;
91977a1c 2138 case OPLM_DWORD:
3ebea2cf 2139 snprintf(buf, buf_size, "%s%s", cast, opr_reg_p(po, popr));
91977a1c 2140 break;
850c9265 2141 case OPLM_WORD:
a2c1d768 2142 snprintf(buf, buf_size, "%s%s",
2143 simplify_cast(cast, "(u16)"), opr_reg_p(po, popr));
850c9265 2144 break;
2145 case OPLM_BYTE:
5101a5f9 2146 if (popr->name[1] == 'h') // XXX..
a2c1d768 2147 snprintf(buf, buf_size, "%s(%s >> 8)",
2148 simplify_cast(cast, "(u8)"), opr_reg_p(po, popr));
5101a5f9 2149 else
a2c1d768 2150 snprintf(buf, buf_size, "%s%s",
2151 simplify_cast(cast, "(u8)"), opr_reg_p(po, popr));
850c9265 2152 break;
91977a1c 2153 default:
2154 ferr(po, "invalid src lmod: %d\n", popr->lmod);
2155 }
2156 break;
850c9265 2157
91977a1c 2158 case OPT_REGMEM:
1cd4a663 2159 if (is_stack_access(po, popr)) {
de50b98b 2160 stack_frame_access(po, popr, buf, buf_size,
3ebea2cf 2161 popr->name, cast, 1, is_lea);
91977a1c 2162 break;
2163 }
850c9265 2164
2165 strcpy(expr, popr->name);
2166 if (strchr(expr, '[')) {
2167 // special case: '[' can only be left for label[reg] form
2168 ret = sscanf(expr, "%[^[][%[^]]]", tmp1, tmp2);
2169 if (ret != 2)
2170 ferr(po, "parse failure for '%s'\n", expr);
a2c1d768 2171 if (tmp1[0] == '(') {
2172 // (off_4FFF50+3)[eax]
2173 p = strchr(tmp1 + 1, ')');
2174 if (p == NULL || p[1] != 0)
2175 ferr(po, "parse failure (2) for '%s'\n", expr);
2176 *p = 0;
2177 memmove(tmp1, tmp1 + 1, strlen(tmp1));
2178 }
33c35af6 2179 snprintf(expr, sizeof(expr), "(u32)&%s + %s", tmp1, tmp2);
850c9265 2180 }
2181
2182 // XXX: do we need more parsing?
2183 if (is_lea) {
2184 snprintf(buf, buf_size, "%s", expr);
2185 break;
2186 }
2187
a2c1d768 2188 snprintf(buf, buf_size, "%s(%s)",
2189 simplify_cast(cast, lmod_cast_u_ptr(po, popr->lmod)), expr);
91977a1c 2190 break;
850c9265 2191
91977a1c 2192 case OPT_LABEL:
7aca4698 2193 name = check_label_read_ref(po, popr->name);
3ebea2cf 2194 if (cast[0] == 0 && popr->is_ptr)
2195 cast = "(u32)";
2b43685d 2196
850c9265 2197 if (is_lea)
7aca4698 2198 snprintf(buf, buf_size, "(u32)&%s", name);
2b43685d 2199 else if (popr->size_lt)
2200 snprintf(buf, buf_size, "%s%s%s%s", cast,
2201 lmod_cast_u_ptr(po, popr->lmod),
7aca4698 2202 popr->is_array ? "" : "&", name);
850c9265 2203 else
7aca4698 2204 snprintf(buf, buf_size, "%s%s%s", cast, name,
7ba45c34 2205 popr->is_array ? "[0]" : "");
850c9265 2206 break;
2207
2208 case OPT_OFFSET:
7aca4698 2209 name = check_label_read_ref(po, popr->name);
3ebea2cf 2210 if (cast[0] == 0)
2211 cast = "(u32)";
850c9265 2212 if (is_lea)
2213 ferr(po, "lea an offset?\n");
7aca4698 2214 snprintf(buf, buf_size, "%s&%s", cast, name);
91977a1c 2215 break;
850c9265 2216
91977a1c 2217 case OPT_CONST:
850c9265 2218 if (is_lea)
2219 ferr(po, "lea from const?\n");
2220
a2c1d768 2221 printf_number(tmp1, sizeof(tmp1), popr->val);
ddaf8bd7 2222 if (popr->val == 0 && strchr(cast, '*'))
2223 snprintf(buf, buf_size, "NULL");
2224 else
2225 snprintf(buf, buf_size, "%s%s",
2226 simplify_cast_num(cast, popr->val), tmp1);
91977a1c 2227 break;
850c9265 2228
91977a1c 2229 default:
2230 ferr(po, "invalid src type: %d\n", popr->type);
2231 }
2232
2233 return buf;
2234}
c36e914d 2235
de50b98b 2236// note: may set is_ptr (we find that out late for ebp frame..)
91977a1c 2237static char *out_dst_opr(char *buf, size_t buf_size,
2238 struct parsed_op *po, struct parsed_opr *popr)
2239{
2240 switch (popr->type) {
2241 case OPT_REG:
2242 switch (popr->lmod) {
90307a99 2243 case OPLM_QWORD:
2244 snprintf(buf, buf_size, "%s.q", opr_reg_p(po, popr));
2245 break;
91977a1c 2246 case OPLM_DWORD:
2247 snprintf(buf, buf_size, "%s", opr_reg_p(po, popr));
2248 break;
850c9265 2249 case OPLM_WORD:
2250 // ugh..
2251 snprintf(buf, buf_size, "LOWORD(%s)", opr_reg_p(po, popr));
2252 break;
2253 case OPLM_BYTE:
2254 // ugh..
5101a5f9 2255 if (popr->name[1] == 'h') // XXX..
2256 snprintf(buf, buf_size, "BYTE1(%s)", opr_reg_p(po, popr));
2257 else
2258 snprintf(buf, buf_size, "LOBYTE(%s)", opr_reg_p(po, popr));
850c9265 2259 break;
91977a1c 2260 default:
2261 ferr(po, "invalid dst lmod: %d\n", popr->lmod);
2262 }
2263 break;
850c9265 2264
2265 case OPT_REGMEM:
1cd4a663 2266 if (is_stack_access(po, popr)) {
de50b98b 2267 stack_frame_access(po, popr, buf, buf_size,
3ebea2cf 2268 popr->name, "", 0, 0);
850c9265 2269 break;
2270 }
2271
3ebea2cf 2272 return out_src_opr(buf, buf_size, po, popr, NULL, 0);
850c9265 2273
bfa4a6ee 2274 case OPT_LABEL:
2b43685d 2275 if (popr->size_mismatch)
2276 snprintf(buf, buf_size, "%s%s%s",
2277 lmod_cast_u_ptr(po, popr->lmod),
2278 popr->is_array ? "" : "&", popr->name);
2279 else
2280 snprintf(buf, buf_size, "%s%s", popr->name,
2281 popr->is_array ? "[0]" : "");
bfa4a6ee 2282 break;
2283
91977a1c 2284 default:
2285 ferr(po, "invalid dst type: %d\n", popr->type);
2286 }
2287
2288 return buf;
2289}
c36e914d 2290
3ebea2cf 2291static char *out_src_opr_u32(char *buf, size_t buf_size,
2292 struct parsed_op *po, struct parsed_opr *popr)
2293{
2294 return out_src_opr(buf, buf_size, po, popr, NULL, 0);
2295}
2296
d4a985bd 2297static char *out_src_opr_float(char *buf, size_t buf_size,
fe18df39 2298 struct parsed_op *po, struct parsed_opr *popr, int need_float_stack)
d4a985bd 2299{
2300 const char *cast = NULL;
2301 char tmp[256];
2302
2303 switch (popr->type) {
2304 case OPT_REG:
2305 if (popr->reg < xST0 || popr->reg > xST7)
2306 ferr(po, "bad reg: %d\n", popr->reg);
2307
fe18df39 2308 if (need_float_stack) {
2309 if (popr->reg == xST0)
2310 snprintf(buf, buf_size, "f_st[f_stp & 7]");
2311 else
2312 snprintf(buf, buf_size, "f_st[(f_stp + %d) & 7]",
2313 popr->reg - xST0);
2314 }
2315 else
2316 snprintf(buf, buf_size, "f_st%d", popr->reg - xST0);
d4a985bd 2317 break;
2318
2319 case OPT_REGMEM:
2320 case OPT_LABEL:
2321 case OPT_OFFSET:
2322 switch (popr->lmod) {
2323 case OPLM_QWORD:
2324 cast = "double";
2325 break;
2326 case OPLM_DWORD:
2327 cast = "float";
2328 break;
2329 default:
2330 ferr(po, "unhandled lmod: %d\n", popr->lmod);
2331 break;
2332 }
2333 out_src_opr(tmp, sizeof(tmp), po, popr, "", 1);
11437ea1 2334 snprintf(buf, buf_size, "*(%s *)(%s)", cast, tmp);
d4a985bd 2335 break;
2336
2337 default:
2338 ferr(po, "invalid float type: %d\n", popr->type);
2339 }
2340
2341 return buf;
2342}
2343
2344static char *out_dst_opr_float(char *buf, size_t buf_size,
fe18df39 2345 struct parsed_op *po, struct parsed_opr *popr, int need_float_stack)
d4a985bd 2346{
2347 // same?
fe18df39 2348 return out_src_opr_float(buf, buf_size, po, popr, need_float_stack);
d4a985bd 2349}
2350
91977a1c 2351static void out_test_for_cc(char *buf, size_t buf_size,
940e8e66 2352 struct parsed_op *po, enum parsed_flag_op pfo, int is_inv,
69a3cdfc 2353 enum opr_lenmod lmod, const char *expr)
91977a1c 2354{
69a3cdfc 2355 const char *cast, *scast;
91977a1c 2356
69a3cdfc 2357 cast = lmod_cast_u(po, lmod);
2358 scast = lmod_cast_s(po, lmod);
2359
2360 switch (pfo) {
2361 case PFO_Z:
8c83cc48 2362 case PFO_BE: // CF==1||ZF==1; CF=0
850c9265 2363 snprintf(buf, buf_size, "(%s%s %s 0)",
940e8e66 2364 cast, expr, is_inv ? "!=" : "==");
91977a1c 2365 break;
850c9265 2366
5101a5f9 2367 case PFO_S:
2368 case PFO_L: // SF!=OF; OF=0
2369 snprintf(buf, buf_size, "(%s%s %s 0)",
940e8e66 2370 scast, expr, is_inv ? ">=" : "<");
5101a5f9 2371 break;
2372
8c83cc48 2373 case PFO_LE: // ZF==1||SF!=OF; OF=0
69a3cdfc 2374 snprintf(buf, buf_size, "(%s%s %s 0)",
940e8e66 2375 scast, expr, is_inv ? ">" : "<=");
850c9265 2376 break;
2377
8c83cc48 2378 case PFO_C: // CF=0
2379 case PFO_O: // OF=0
2380 snprintf(buf, buf_size, "(%d)", !!is_inv);
2381 break;
2382
91977a1c 2383 default:
69a3cdfc 2384 ferr(po, "%s: unhandled parsed_flag_op: %d\n", __func__, pfo);
91977a1c 2385 }
2386}
c36e914d 2387
850c9265 2388static void out_cmp_for_cc(char *buf, size_t buf_size,
16057ce1 2389 struct parsed_op *po, enum parsed_flag_op pfo, int is_inv,
2390 int is_neg)
850c9265 2391{
a2c1d768 2392 const char *cast, *scast, *cast_use;
2393 char buf1[256], buf2[256];
2394 enum opr_lenmod lmod;
2395
90307a99 2396 if (po->op != OP_DEC && po->operand[0].lmod != po->operand[1].lmod)
a2c1d768 2397 ferr(po, "%s: lmod mismatch: %d %d\n", __func__,
2398 po->operand[0].lmod, po->operand[1].lmod);
2399 lmod = po->operand[0].lmod;
850c9265 2400
69a3cdfc 2401 cast = lmod_cast_u(po, lmod);
2402 scast = lmod_cast_s(po, lmod);
850c9265 2403
a2c1d768 2404 switch (pfo) {
2405 case PFO_C:
2406 case PFO_Z:
2407 case PFO_BE: // !a
2408 cast_use = cast;
2409 break;
2410
2411 case PFO_S:
2412 case PFO_L: // !ge
2413 case PFO_LE:
2414 cast_use = scast;
2415 break;
2416
2417 default:
2418 ferr(po, "%s: unhandled parsed_flag_op: %d\n", __func__, pfo);
2419 }
2420
2421 out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], cast_use, 0);
90307a99 2422 if (po->op == OP_DEC)
2423 snprintf(buf2, sizeof(buf2), "1");
16057ce1 2424 else {
2425 char cast_op2[64];
2426 snprintf(cast_op2, sizeof(cast_op2) - 1, "%s", cast_use);
2427 if (is_neg)
2428 strcat(cast_op2, "-");
2429 out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], cast_op2, 0);
2430 }
a2c1d768 2431
69a3cdfc 2432 switch (pfo) {
5101a5f9 2433 case PFO_C:
2434 // note: must be unsigned compare
a2c1d768 2435 snprintf(buf, buf_size, "(%s %s %s)",
2436 buf1, is_inv ? ">=" : "<", buf2);
5101a5f9 2437 break;
2438
69a3cdfc 2439 case PFO_Z:
a2c1d768 2440 snprintf(buf, buf_size, "(%s %s %s)",
2441 buf1, is_inv ? "!=" : "==", buf2);
850c9265 2442 break;
2443
5101a5f9 2444 case PFO_BE: // !a
850c9265 2445 // note: must be unsigned compare
a2c1d768 2446 snprintf(buf, buf_size, "(%s %s %s)",
2447 buf1, is_inv ? ">" : "<=", buf2);
2448
2449 // annoying case
2450 if (is_inv && lmod == OPLM_BYTE
2451 && po->operand[1].type == OPT_CONST
2452 && po->operand[1].val == 0xff)
2453 {
2454 snprintf(g_comment, sizeof(g_comment), "if %s", buf);
2455 snprintf(buf, buf_size, "(0)");
2456 }
5101a5f9 2457 break;
2458
2459 // note: must be signed compare
2460 case PFO_S:
2461 snprintf(buf, buf_size, "(%s(%s - %s) %s 0)",
a2c1d768 2462 scast, buf1, buf2, is_inv ? ">=" : "<");
850c9265 2463 break;
2464
5101a5f9 2465 case PFO_L: // !ge
a2c1d768 2466 snprintf(buf, buf_size, "(%s %s %s)",
2467 buf1, is_inv ? ">=" : "<", buf2);
850c9265 2468 break;
2469
90307a99 2470 case PFO_LE: // !g
a2c1d768 2471 snprintf(buf, buf_size, "(%s %s %s)",
2472 buf1, is_inv ? ">" : "<=", buf2);
5101a5f9 2473 break;
2474
850c9265 2475 default:
a2c1d768 2476 break;
850c9265 2477 }
2478}
2479
69a3cdfc 2480static void out_cmp_test(char *buf, size_t buf_size,
940e8e66 2481 struct parsed_op *po, enum parsed_flag_op pfo, int is_inv)
69a3cdfc 2482{
2483 char buf1[256], buf2[256], buf3[256];
2484
2485 if (po->op == OP_TEST) {
2486 if (IS(opr_name(po, 0), opr_name(po, 1))) {
3ebea2cf 2487 out_src_opr_u32(buf3, sizeof(buf3), po, &po->operand[0]);
69a3cdfc 2488 }
2489 else {
3ebea2cf 2490 out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
2491 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
69a3cdfc 2492 snprintf(buf3, sizeof(buf3), "(%s & %s)", buf1, buf2);
2493 }
940e8e66 2494 out_test_for_cc(buf, buf_size, po, pfo, is_inv,
69a3cdfc 2495 po->operand[0].lmod, buf3);
2496 }
2497 else if (po->op == OP_CMP) {
16057ce1 2498 out_cmp_for_cc(buf, buf_size, po, pfo, is_inv, 0);
69a3cdfc 2499 }
2500 else
2501 ferr(po, "%s: unhandled op: %d\n", __func__, po->op);
2502}
2503
850c9265 2504static void propagate_lmod(struct parsed_op *po, struct parsed_opr *popr1,
91977a1c 2505 struct parsed_opr *popr2)
2506{
2507 if (popr1->lmod == OPLM_UNSPEC && popr2->lmod == OPLM_UNSPEC)
2508 ferr(po, "missing lmod for both operands\n");
2509
2510 if (popr1->lmod == OPLM_UNSPEC)
2511 popr1->lmod = popr2->lmod;
2512 else if (popr2->lmod == OPLM_UNSPEC)
2513 popr2->lmod = popr1->lmod;
a3684be1 2514 else if (popr1->lmod != popr2->lmod) {
2515 if (popr1->type_from_var) {
2516 popr1->size_mismatch = 1;
2517 if (popr1->lmod < popr2->lmod)
2518 popr1->size_lt = 1;
2519 popr1->lmod = popr2->lmod;
2520 }
2521 else if (popr2->type_from_var) {
2522 popr2->size_mismatch = 1;
2523 if (popr2->lmod < popr1->lmod)
2524 popr2->size_lt = 1;
2525 popr2->lmod = popr1->lmod;
2526 }
2527 else
2528 ferr(po, "conflicting lmods: %d vs %d\n",
2529 popr1->lmod, popr2->lmod);
2530 }
91977a1c 2531}
c36e914d 2532
850c9265 2533static const char *op_to_c(struct parsed_op *po)
2534{
2535 switch (po->op)
2536 {
2537 case OP_ADD:
5101a5f9 2538 case OP_ADC:
850c9265 2539 return "+";
2540 case OP_SUB:
5101a5f9 2541 case OP_SBB:
850c9265 2542 return "-";
2543 case OP_AND:
2544 return "&";
2545 case OP_OR:
2546 return "|";
2547 case OP_XOR:
2548 return "^";
2549 case OP_SHL:
2550 return "<<";
2551 case OP_SHR:
2552 return ">>";
2553 case OP_MUL:
2554 case OP_IMUL:
2555 return "*";
2556 default:
2557 ferr(po, "op_to_c was supplied with %d\n", po->op);
2558 }
2559}
2560
de50b98b 2561// last op in stream - unconditional branch or ret
2562#define LAST_OP(_i) ((ops[_i].flags & OPF_TAIL) \
092f64e1 2563 || ((ops[_i].flags & (OPF_JMP|OPF_CJMP|OPF_RMD)) == OPF_JMP \
037f4971 2564 && ops[_i].op != OP_CALL))
de50b98b 2565
db63af51 2566#define check_i(po, i) \
2567 if ((i) < 0) \
2568 ferr(po, "bad " #i ": %d\n", i)
2569
e83ea7ed 2570// note: this skips over calls and rm'd stuff assuming they're handled
2571// so it's intended to use at one of final passes
2572static int scan_for_pop(int i, int opcnt, int magic, int reg,
93b5bd18 2573 int depth, int seen_noreturn, int flags_set)
850c9265 2574{
87bf6cec 2575 struct parsed_op *po;
e83ea7ed 2576 int relevant;
87bf6cec 2577 int ret = 0;
4c45fa73 2578 int j;
87bf6cec 2579
850c9265 2580 for (; i < opcnt; i++) {
87bf6cec 2581 po = &ops[i];
2582 if (po->cc_scratch == magic)
b2bd20c0 2583 return ret; // already checked
87bf6cec 2584 po->cc_scratch = magic;
2585
89ff3147 2586 if (po->flags & OPF_TAIL) {
2587 if (po->op == OP_CALL) {
b2bd20c0 2588 if (po->pp != NULL && po->pp->is_noreturn)
93b5bd18 2589 seen_noreturn = 1;
2590 else
0ea6430c 2591 goto out;
89ff3147 2592 }
93b5bd18 2593 else
0ea6430c 2594 goto out;
89ff3147 2595 }
87bf6cec 2596
e83ea7ed 2597 if (po->flags & (OPF_RMD|OPF_DONE|OPF_FARG))
850c9265 2598 continue;
2599
87bf6cec 2600 if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
4c45fa73 2601 if (po->btj != NULL) {
2602 // jumptable
da87ae38 2603 for (j = 0; j < po->btj->count; j++) {
db63af51 2604 check_i(po, po->btj->d[j].bt_i);
e83ea7ed 2605 ret |= scan_for_pop(po->btj->d[j].bt_i, opcnt, magic, reg,
93b5bd18 2606 depth, seen_noreturn, flags_set);
4c45fa73 2607 if (ret < 0)
2608 return ret; // dead end
2609 }
da87ae38 2610 return ret;
4c45fa73 2611 }
2612
db63af51 2613 check_i(po, po->bt_i);
5c024ef7 2614 if (po->flags & OPF_CJMP) {
e83ea7ed 2615 ret |= scan_for_pop(po->bt_i, opcnt, magic, reg,
93b5bd18 2616 depth, seen_noreturn, flags_set);
87bf6cec 2617 if (ret < 0)
2618 return ret; // dead end
2619 }
2620 else {
2621 i = po->bt_i - 1;
2622 }
2623 continue;
2624 }
2625
e83ea7ed 2626 relevant = 0;
d4e3b5db 2627 if ((po->op == OP_POP || po->op == OP_PUSH)
e83ea7ed 2628 && po->operand[0].type == OPT_REG && po->operand[0].reg == reg)
87bf6cec 2629 {
e83ea7ed 2630 relevant = 1;
2631 }
2632
2633 if (po->op == OP_PUSH) {
2634 depth++;
e83ea7ed 2635 }
2636 else if (po->op == OP_POP) {
b2bd20c0 2637 if (relevant && depth == 0) {
2638 po->flags |= flags_set;
e83ea7ed 2639 return 1;
2640 }
b2bd20c0 2641 depth--;
87bf6cec 2642 }
850c9265 2643 }
2644
0ea6430c 2645out:
93b5bd18 2646 // for noreturn, assume msvc skipped stack cleanup
2647 return seen_noreturn ? 1 : -1;
850c9265 2648}
2649
e83ea7ed 2650// scan for 'reg' pop backwards starting from i
2651// intended to use for register restore search, so other reg
2652// references are considered an error
2653static int scan_for_rsave_pop_reg(int i, int magic, int reg, int set_flags)
850c9265 2654{
e83ea7ed 2655 struct parsed_op *po;
2656 struct label_ref *lr;
2657 int ret = 0;
2658
2659 ops[i].cc_scratch = magic;
2660
2661 while (1)
2662 {
2663 if (g_labels[i] != NULL) {
2664 lr = &g_label_refs[i];
2665 for (; lr != NULL; lr = lr->next) {
2666 check_i(&ops[i], lr->i);
2667 ret |= scan_for_rsave_pop_reg(lr->i, magic, reg, set_flags);
2668 if (ret < 0)
2669 return ret;
2670 }
2671 if (i > 0 && LAST_OP(i - 1))
2672 return ret;
2673 }
2674
2675 i--;
2676 if (i < 0)
2677 break;
2678
2679 if (ops[i].cc_scratch == magic)
2680 return ret;
2681 ops[i].cc_scratch = magic;
2682
2683 po = &ops[i];
2684 if (po->op == OP_POP && po->operand[0].reg == reg) {
2685 if (po->flags & (OPF_RMD|OPF_DONE))
2686 return -1;
2687
2688 po->flags |= set_flags;
2689 return 1;
2690 }
2691
2692 // this also covers the case where we reach corresponding push
2693 if ((po->regmask_dst | po->regmask_src) & (1 << reg))
2694 return -1;
2695 }
2696
2697 // nothing interesting on this path
2698 return 0;
2699}
2700
2701static void find_reachable_exits(int i, int opcnt, int magic,
2702 int *exits, int *exit_count)
2703{
2704 struct parsed_op *po;
850c9265 2705 int j;
2706
e83ea7ed 2707 for (; i < opcnt; i++)
2708 {
2709 po = &ops[i];
2710 if (po->cc_scratch == magic)
2711 return;
2712 po->cc_scratch = magic;
2713
2714 if (po->flags & OPF_TAIL) {
2715 ferr_assert(po, *exit_count < MAX_EXITS);
2716 exits[*exit_count] = i;
2717 (*exit_count)++;
2718 return;
2719 }
850c9265 2720
e83ea7ed 2721 if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
2722 if (po->flags & OPF_RMD)
69a3cdfc 2723 continue;
850c9265 2724
e83ea7ed 2725 if (po->btj != NULL) {
2726 for (j = 0; j < po->btj->count; j++) {
2727 check_i(po, po->btj->d[j].bt_i);
2728 find_reachable_exits(po->btj->d[j].bt_i, opcnt, magic,
2729 exits, exit_count);
2730 }
2731 return;
850c9265 2732 }
2733
e83ea7ed 2734 check_i(po, po->bt_i);
2735 if (po->flags & OPF_CJMP)
2736 find_reachable_exits(po->bt_i, opcnt, magic, exits, exit_count);
2737 else
2738 i = po->bt_i - 1;
2739 continue;
850c9265 2740 }
2741 }
e83ea7ed 2742}
2743
2744// scan for 'reg' pop backwards starting from exits (all paths)
2745static int scan_for_pop_ret(int i, int opcnt, int reg, int set_flags)
2746{
2747 static int exits[MAX_EXITS];
2748 static int exit_count;
2749 int j, ret;
850c9265 2750
e83ea7ed 2751 if (!set_flags) {
2752 exit_count = 0;
2753 find_reachable_exits(i, opcnt, i + opcnt * 15, exits,
2754 &exit_count);
2755 ferr_assert(&ops[i], exit_count > 0);
2756 }
2757
2758 for (j = 0; j < exit_count; j++) {
2759 ret = scan_for_rsave_pop_reg(exits[j], i + opcnt * 16 + set_flags,
2760 reg, set_flags);
2761 if (ret == -1)
2762 return -1;
2763 }
2764
b2bd20c0 2765 return 1;
850c9265 2766}
2767
e83ea7ed 2768// scan for one or more pop of push <const>
2769static int scan_for_pop_const_r(int i, int opcnt, int magic,
2770 int push_i, int is_probe)
9af2d373 2771{
25a330eb 2772 struct parsed_op *po;
e83ea7ed 2773 struct label_ref *lr;
2774 int ret = 0;
9af2d373 2775 int j;
2776
e83ea7ed 2777 for (; i < opcnt; i++)
2778 {
2779 po = &ops[i];
2780 if (po->cc_scratch == magic)
2781 return ret; // already checked
2782 po->cc_scratch = magic;
2783
2784 if (po->flags & OPF_JMP) {
2785 if (po->flags & OPF_RMD)
2786 continue;
2787 if (po->op == OP_CALL)
2788 return -1;
2789
2790 if (po->btj != NULL) {
2791 for (j = 0; j < po->btj->count; j++) {
2792 check_i(po, po->btj->d[j].bt_i);
2793 ret |= scan_for_pop_const_r(po->btj->d[j].bt_i, opcnt, magic,
2794 push_i, is_probe);
2795 if (ret < 0)
2796 return ret;
2797 }
2798 return ret;
2799 }
25a330eb 2800
e83ea7ed 2801 check_i(po, po->bt_i);
2802 if (po->flags & OPF_CJMP) {
2803 ret |= scan_for_pop_const_r(po->bt_i, opcnt, magic, push_i,
2804 is_probe);
2805 if (ret < 0)
2806 return ret;
2807 }
2808 else {
2809 i = po->bt_i - 1;
2810 }
25a330eb 2811 continue;
2812 }
2813
e83ea7ed 2814 if ((po->flags & (OPF_TAIL|OPF_RSAVE)) || po->op == OP_PUSH)
2815 return -1;
2816
2817 if (g_labels[i] != NULL) {
2818 // all refs must be visited
2819 lr = &g_label_refs[i];
2820 for (; lr != NULL; lr = lr->next) {
2821 check_i(po, lr->i);
2822 if (ops[lr->i].cc_scratch != magic)
2823 return -1;
2824 }
2825 if (i > 0 && !LAST_OP(i - 1) && ops[i - 1].cc_scratch != magic)
2826 return -1;
2827 }
2828
2829 if (po->op == OP_POP)
9af2d373 2830 {
e83ea7ed 2831 if (po->flags & (OPF_RMD|OPF_DONE))
2832 return -1;
2833
2834 if (!is_probe) {
2835 po->flags |= OPF_DONE;
2836 po->datap = &ops[push_i];
2837 }
2838 return 1;
9af2d373 2839 }
e83ea7ed 2840 }
9af2d373 2841
e83ea7ed 2842 return -1;
2843}
25a330eb 2844
e83ea7ed 2845static void scan_for_pop_const(int i, int opcnt, int magic)
2846{
2847 int ret;
2848
2849 ret = scan_for_pop_const_r(i + 1, opcnt, magic, i, 1);
2850 if (ret == 1) {
2851 ops[i].flags |= OPF_RMD | OPF_DONE;
2852 scan_for_pop_const_r(i + 1, opcnt, magic + 1, i, 0);
2853 }
2854}
2855
2856// check if all branch targets within a marked path are also marked
2857// note: the path checked must not be empty or end with a branch
2858static int check_path_branches(int opcnt, int magic)
2859{
2860 struct parsed_op *po;
2861 int i, j;
2862
2863 for (i = 0; i < opcnt; i++) {
2864 po = &ops[i];
2865 if (po->cc_scratch != magic)
2866 continue;
2867
2868 if (po->flags & OPF_JMP) {
2869 if ((po->flags & OPF_RMD) || po->op == OP_CALL)
2870 continue;
2871
2872 if (po->btj != NULL) {
2873 for (j = 0; j < po->btj->count; j++) {
2874 check_i(po, po->btj->d[j].bt_i);
2875 if (ops[po->btj->d[j].bt_i].cc_scratch != magic)
2876 return 0;
2877 }
25a330eb 2878 }
e83ea7ed 2879
2880 check_i(po, po->bt_i);
2881 if (ops[po->bt_i].cc_scratch != magic)
2882 return 0;
2883 if ((po->flags & OPF_CJMP) && ops[i + 1].cc_scratch != magic)
2884 return 0;
2885 }
2886 }
2887
2888 return 1;
2889}
2890
2891// scan for multiple pushes for given pop
2892static int scan_pushes_for_pop_r(int i, int magic, int pop_i,
2893 int is_probe)
2894{
2895 int reg = ops[pop_i].operand[0].reg;
2896 struct parsed_op *po;
2897 struct label_ref *lr;
2898 int ret = 0;
2899
2900 ops[i].cc_scratch = magic;
2901
2902 while (1)
2903 {
2904 if (g_labels[i] != NULL) {
2905 lr = &g_label_refs[i];
2906 for (; lr != NULL; lr = lr->next) {
2907 check_i(&ops[i], lr->i);
2908 ret |= scan_pushes_for_pop_r(lr->i, magic, pop_i, is_probe);
2909 if (ret < 0)
2910 return ret;
25a330eb 2911 }
e83ea7ed 2912 if (i > 0 && LAST_OP(i - 1))
2913 return ret;
2914 }
2915
2916 i--;
2917 if (i < 0)
9af2d373 2918 break;
e83ea7ed 2919
2920 if (ops[i].cc_scratch == magic)
2921 return ret;
2922 ops[i].cc_scratch = magic;
2923
2924 po = &ops[i];
2925 if (po->op == OP_CALL)
2926 return -1;
2927 if ((po->flags & (OPF_TAIL|OPF_RSAVE)) || po->op == OP_POP)
2928 return -1;
2929
2930 if (po->op == OP_PUSH)
2931 {
2932 if (po->datap != NULL)
2933 return -1;
2934 if (po->operand[0].type == OPT_REG && po->operand[0].reg == reg)
2935 // leave this case for reg save/restore handlers
2936 return -1;
2937
2938 if (!is_probe) {
2939 po->flags |= OPF_PPUSH | OPF_DONE;
2940 po->datap = &ops[pop_i];
2941 }
2942 return 1;
2943 }
2944 }
2945
2946 return -1;
2947}
2948
2949static void scan_pushes_for_pop(int i, int opcnt, int *regmask_pp)
2950{
2951 int magic = i + opcnt * 14;
2952 int ret;
2953
2954 ret = scan_pushes_for_pop_r(i, magic, i, 1);
2955 if (ret == 1) {
2956 ret = check_path_branches(opcnt, magic);
2957 if (ret == 1) {
2958 ops[i].flags |= OPF_PPUSH | OPF_DONE;
2959 *regmask_pp |= 1 << ops[i].operand[0].reg;
2960 scan_pushes_for_pop_r(i, magic + 1, i, 0);
9af2d373 2961 }
2962 }
2963}
2964
591721d7 2965static void scan_propagate_df(int i, int opcnt)
2966{
2967 struct parsed_op *po = &ops[i];
2968 int j;
2969
2970 for (; i < opcnt; i++) {
2971 po = &ops[i];
2972 if (po->flags & OPF_DF)
2973 return; // already resolved
2974 po->flags |= OPF_DF;
2975
2976 if (po->op == OP_CALL)
2977 ferr(po, "call with DF set?\n");
2978
2979 if (po->flags & OPF_JMP) {
2980 if (po->btj != NULL) {
2981 // jumptable
db63af51 2982 for (j = 0; j < po->btj->count; j++) {
2983 check_i(po, po->btj->d[j].bt_i);
591721d7 2984 scan_propagate_df(po->btj->d[j].bt_i, opcnt);
db63af51 2985 }
591721d7 2986 return;
2987 }
2988
b2bd20c0 2989 if (po->flags & OPF_RMD)
2990 continue;
db63af51 2991 check_i(po, po->bt_i);
5c024ef7 2992 if (po->flags & OPF_CJMP)
591721d7 2993 scan_propagate_df(po->bt_i, opcnt);
2994 else
2995 i = po->bt_i - 1;
2996 continue;
2997 }
2998
2999 if (po->flags & OPF_TAIL)
3000 break;
3001
3002 if (po->op == OP_CLD) {
5e49b270 3003 po->flags |= OPF_RMD | OPF_DONE;
591721d7 3004 return;
3005 }
3006 }
3007
3008 ferr(po, "missing DF clear?\n");
3009}
3010
db63af51 3011// is operand 'opr' referenced by parsed_op 'po'?
3012static int is_opr_referenced(const struct parsed_opr *opr,
3013 const struct parsed_op *po)
3014{
3015 int i, mask;
3016
3017 if (opr->type == OPT_REG) {
3018 mask = po->regmask_dst | po->regmask_src;
3019 if (po->op == OP_CALL)
3020 mask |= (1 << xAX) | (1 << xCX) | (1 << xDX);
3021 if ((1 << opr->reg) & mask)
3022 return 1;
3023 else
3024 return 0;
3025 }
3026
3027 for (i = 0; i < po->operand_cnt; i++)
3028 if (IS(po->operand[0].name, opr->name))
3029 return 1;
3030
3031 return 0;
3032}
3033
3034// is operand 'opr' read by parsed_op 'po'?
3035static int is_opr_read(const struct parsed_opr *opr,
3036 const struct parsed_op *po)
3037{
db63af51 3038 if (opr->type == OPT_REG) {
b2bd20c0 3039 if (po->regmask_src & (1 << opr->reg))
db63af51 3040 return 1;
3041 else
3042 return 0;
3043 }
3044
3045 // yes I'm lazy
3046 return 0;
3047}
3048
1cd4a663 3049// is operand 'opr' modified by parsed_op 'po'?
5101a5f9 3050static int is_opr_modified(const struct parsed_opr *opr,
69a3cdfc 3051 const struct parsed_op *po)
3052{
89ff3147 3053 int mask;
3054
89ff3147 3055 if (opr->type == OPT_REG) {
3056 if (po->op == OP_CALL) {
b2bd20c0 3057 mask = po->regmask_dst;
3058 mask |= (1 << xAX) | (1 << xCX) | (1 << xDX); // ?
3059 if (mask & (1 << opr->reg))
89ff3147 3060 return 1;
3061 else
3062 return 0;
3063 }
3064
b2bd20c0 3065 if (po->regmask_dst & (1 << opr->reg))
3066 return 1;
3067 else
3068 return 0;
69a3cdfc 3069 }
3070
3071 return IS(po->operand[0].name, opr->name);
3072}
3073
5101a5f9 3074// is any operand of parsed_op 'po_test' modified by parsed_op 'po'?
3075static int is_any_opr_modified(const struct parsed_op *po_test,
89ff3147 3076 const struct parsed_op *po, int c_mode)
5101a5f9 3077{
89ff3147 3078 int mask;
5101a5f9 3079 int i;
3080
3081 if ((po->flags & OPF_RMD) || !(po->flags & OPF_DATA))
3082 return 0;
3083
de50b98b 3084 if (po_test->operand_cnt == 1 && po_test->operand[0].type == OPT_CONST)
3085 return 0;
3086
3087 if ((po_test->regmask_src | po_test->regmask_dst) & po->regmask_dst)
3088 return 1;
3089
3090 // in reality, it can wreck any register, but in decompiled C
2b43685d 3091 // version it can only overwrite eax or edx:eax
89ff3147 3092 mask = (1 << xAX) | (1 << xDX);
3093 if (!c_mode)
3094 mask |= 1 << xCX;
3095
de50b98b 3096 if (po->op == OP_CALL
89ff3147 3097 && ((po_test->regmask_src | po_test->regmask_dst) & mask))
5101a5f9 3098 return 1;
3099
3100 for (i = 0; i < po_test->operand_cnt; i++)
3101 if (IS(po_test->operand[i].name, po->operand[0].name))
3102 return 1;
3103
3104 return 0;
3105}
3106
940e8e66 3107// scan for any po_test operand modification in range given
89ff3147 3108static int scan_for_mod(struct parsed_op *po_test, int i, int opcnt,
3109 int c_mode)
69a3cdfc 3110{
2b43685d 3111 if (po_test->operand_cnt == 1 && po_test->operand[0].type == OPT_CONST)
3112 return -1;
3113
69a3cdfc 3114 for (; i < opcnt; i++) {
89ff3147 3115 if (is_any_opr_modified(po_test, &ops[i], c_mode))
69a3cdfc 3116 return i;
3117 }
3118
3119 return -1;
3120}
3121
940e8e66 3122// scan for po_test operand[0] modification in range given
3123static int scan_for_mod_opr0(struct parsed_op *po_test,
3124 int i, int opcnt)
3125{
3126 for (; i < opcnt; i++) {
3127 if (is_opr_modified(&po_test->operand[0], &ops[i]))
3128 return i;
3129 }
3130
3131 return -1;
3132}
3133
7f20f633 3134static int try_resolve_const(int i, const struct parsed_opr *opr,
3135 int magic, unsigned int *val);
3136
3137static int scan_for_flag_set(int i, int opcnt, int magic,
3138 int *branched, int *setters, int *setter_cnt)
69a3cdfc 3139{
04f8a628 3140 struct label_ref *lr;
2b43685d 3141 int ret;
de50b98b 3142
3143 while (i >= 0) {
04f8a628 3144 if (ops[i].cc_scratch == magic) {
04abc5d6 3145 // is this a problem?
3146 //ferr(&ops[i], "%s looped\n", __func__);
3147 return 0;
04f8a628 3148 }
3149 ops[i].cc_scratch = magic;
3150
d7857c3a 3151 if (g_labels[i] != NULL) {
2b43685d 3152 *branched = 1;
04f8a628 3153
3154 lr = &g_label_refs[i];
3155 for (; lr->next; lr = lr->next) {
92d715b6 3156 check_i(&ops[i], lr->i);
7f20f633 3157 ret = scan_for_flag_set(lr->i, opcnt, magic,
04f8a628 3158 branched, setters, setter_cnt);
3159 if (ret < 0)
3160 return ret;
3161 }
3162
92d715b6 3163 check_i(&ops[i], lr->i);
de50b98b 3164 if (i > 0 && LAST_OP(i - 1)) {
94d447fb 3165 i = lr->i;
de50b98b 3166 continue;
3167 }
7f20f633 3168 ret = scan_for_flag_set(lr->i, opcnt, magic,
04f8a628 3169 branched, setters, setter_cnt);
2b43685d 3170 if (ret < 0)
3171 return ret;
de50b98b 3172 }
3173 i--;
3174
2b43685d 3175 if (ops[i].flags & OPF_FLAGS) {
3176 setters[*setter_cnt] = i;
3177 (*setter_cnt)++;
7f20f633 3178
3179 if (ops[i].flags & OPF_REP) {
3180 struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xCX);
3181 unsigned int uval;
3182
3183 ret = try_resolve_const(i, &opr, i + opcnt * 7, &uval);
3184 if (ret != 1 || uval == 0) {
3185 // can't treat it as full setter because of ecx=0 case,
3186 // also disallow delayed compare
3187 *branched = 1;
3188 continue;
3189 }
3190 }
3191
2b43685d 3192 return 0;
3193 }
69a3cdfc 3194
5c024ef7 3195 if ((ops[i].flags & (OPF_JMP|OPF_CJMP)) == OPF_JMP)
69a3cdfc 3196 return -1;
69a3cdfc 3197 }
3198
3199 return -1;
3200}
3201
5101a5f9 3202// scan back for cdq, if anything modifies edx, fail
3203static int scan_for_cdq_edx(int i)
3204{
cdfaeed7 3205 while (i >= 0) {
d7857c3a 3206 if (g_labels[i] != NULL) {
cdfaeed7 3207 if (g_label_refs[i].next != NULL)
3208 return -1;
3209 if (i > 0 && LAST_OP(i - 1)) {
3210 i = g_label_refs[i].i;
3211 continue;
3212 }
3213 return -1;
3214 }
3215 i--;
3216
5101a5f9 3217 if (ops[i].op == OP_CDQ)
3218 return i;
3219
3220 if (ops[i].regmask_dst & (1 << xDX))
3221 return -1;
5101a5f9 3222 }
3223
3224 return -1;
3225}
3226
64c59faf 3227static int scan_for_reg_clear(int i, int reg)
3228{
cdfaeed7 3229 while (i >= 0) {
d7857c3a 3230 if (g_labels[i] != NULL) {
cdfaeed7 3231 if (g_label_refs[i].next != NULL)
3232 return -1;
3233 if (i > 0 && LAST_OP(i - 1)) {
3234 i = g_label_refs[i].i;
3235 continue;
3236 }
3237 return -1;
3238 }
3239 i--;
3240
64c59faf 3241 if (ops[i].op == OP_XOR
3242 && ops[i].operand[0].lmod == OPLM_DWORD
3243 && ops[i].operand[0].reg == ops[i].operand[1].reg
3244 && ops[i].operand[0].reg == reg)
3245 return i;
3246
3247 if (ops[i].regmask_dst & (1 << reg))
3248 return -1;
64c59faf 3249 }
3250
3251 return -1;
3252}
3253
ee2361b9 3254static void patch_esp_adjust(struct parsed_op *po, int adj)
3255{
3256 ferr_assert(po, po->op == OP_ADD);
3257 ferr_assert(po, IS(opr_name(po, 0), "esp"));
3258 ferr_assert(po, po->operand[1].type == OPT_CONST);
3259
3260 // this is a bit of a hack, but deals with use of
3261 // single adj for multiple calls
3262 po->operand[1].val -= adj;
3263 po->flags |= OPF_RMD;
3264 if (po->operand[1].val == 0)
3265 po->flags |= OPF_DONE;
3266 ferr_assert(po, (int)po->operand[1].val >= 0);
3267}
3268
1bafb621 3269// scan for positive, constant esp adjust
ee2361b9 3270// multipath case is preliminary
9af2d373 3271static int scan_for_esp_adjust(int i, int opcnt,
ee2361b9 3272 int adj_expect, int *adj, int *is_multipath, int do_update)
1bafb621 3273{
bfacdc83 3274 int adj_expect_unknown = 0;
7ba45c34 3275 struct parsed_op *po;
46411e6c 3276 int first_pop = -1;
bfacdc83 3277 int adj_best = 0;
4741fdfe 3278
ee2361b9 3279 *adj = *is_multipath = 0;
bfacdc83 3280 if (adj_expect < 0) {
3281 adj_expect_unknown = 1;
3282 adj_expect = 32 * 4; // enough?
3283 }
7ba45c34 3284
9af2d373 3285 for (; i < opcnt && *adj < adj_expect; i++) {
d7857c3a 3286 if (g_labels[i] != NULL)
ee2361b9 3287 *is_multipath = 1;
a2c1d768 3288
5e49b270 3289 po = &ops[i];
3290 if (po->flags & OPF_DONE)
3291 continue;
3292
7ba45c34 3293 if (po->op == OP_ADD && po->operand[0].reg == xSP) {
3294 if (po->operand[1].type != OPT_CONST)
1bafb621 3295 ferr(&ops[i], "non-const esp adjust?\n");
7ba45c34 3296 *adj += po->operand[1].val;
1bafb621 3297 if (*adj & 3)
3298 ferr(&ops[i], "unaligned esp adjust: %x\n", *adj);
ee2361b9 3299 if (do_update) {
3300 if (!*is_multipath)
3301 patch_esp_adjust(po, adj_expect);
3302 else
3303 po->flags |= OPF_RMD;
3304 }
1bafb621 3305 return i;
3306 }
5e49b270 3307 else if (po->op == OP_PUSH) {
5c024ef7 3308 //if (first_pop == -1)
3309 // first_pop = -2; // none
7ba45c34 3310 *adj -= lmod_bytes(po, po->operand[0].lmod);
46411e6c 3311 }
5e49b270 3312 else if (po->op == OP_POP) {
91ca764a 3313 if (!(po->flags & OPF_DONE)) {
3314 // seems like msvc only uses 'pop ecx' for stack realignment..
3315 if (po->operand[0].type != OPT_REG || po->operand[0].reg != xCX)
3316 break;
3317 if (first_pop == -1 && *adj >= 0)
3318 first_pop = i;
3319 }
ee2361b9 3320 if (do_update && *adj >= 0) {
3321 po->flags |= OPF_RMD;
3322 if (!*is_multipath)
b2bd20c0 3323 po->flags |= OPF_DONE | OPF_NOREGS;
ee2361b9 3324 }
3325
7ba45c34 3326 *adj += lmod_bytes(po, po->operand[0].lmod);
bfacdc83 3327 if (*adj > adj_best)
3328 adj_best = *adj;
46411e6c 3329 }
e56ab892 3330 else if (po->flags & (OPF_JMP|OPF_TAIL)) {
4741fdfe 3331 if (po->op == OP_JMP && po->btj == NULL) {
5e49b270 3332 if (po->bt_i <= i)
3333 break;
4741fdfe 3334 i = po->bt_i - 1;
3335 continue;
3336 }
e56ab892 3337 if (po->op != OP_CALL)
a2c1d768 3338 break;
e56ab892 3339 if (po->operand[0].type != OPT_LABEL)
a2c1d768 3340 break;
91ca764a 3341 if (po->pp != NULL && po->pp->is_stdcall)
89ff3147 3342 break;
bfacdc83 3343 if (adj_expect_unknown && first_pop >= 0)
3344 break;
91ca764a 3345 // assume it's another cdecl call
e56ab892 3346 }
a2c1d768 3347 }
7ba45c34 3348
108e9fe3 3349 if (first_pop >= 0) {
bfacdc83 3350 // probably only 'pop ecx' was used
3351 *adj = adj_best;
46411e6c 3352 return first_pop;
1bafb621 3353 }
3354
3355 return -1;
3356}
3357
a3684be1 3358static void scan_fwd_set_flags(int i, int opcnt, int magic, int flags)
3359{
3360 struct parsed_op *po;
3361 int j;
3362
3363 if (i < 0)
3364 ferr(ops, "%s: followed bad branch?\n", __func__);
3365
3366 for (; i < opcnt; i++) {
3367 po = &ops[i];
3368 if (po->cc_scratch == magic)
3369 return;
3370 po->cc_scratch = magic;
3371 po->flags |= flags;
3372
3373 if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
3374 if (po->btj != NULL) {
3375 // jumptable
3376 for (j = 0; j < po->btj->count; j++)
3377 scan_fwd_set_flags(po->btj->d[j].bt_i, opcnt, magic, flags);
3378 return;
3379 }
3380
3381 scan_fwd_set_flags(po->bt_i, opcnt, magic, flags);
5c024ef7 3382 if (!(po->flags & OPF_CJMP))
a3684be1 3383 return;
3384 }
3385 if (po->flags & OPF_TAIL)
3386 return;
3387 }
3388}
3389
1cd4a663 3390static const struct parsed_proto *try_recover_pp(
93b5bd18 3391 struct parsed_op *po, const struct parsed_opr *opr,
3392 int is_call, int *search_instead)
1cd4a663 3393{
3394 const struct parsed_proto *pp = NULL;
89ff3147 3395 char buf[256];
3396 char *p;
1cd4a663 3397
3398 // maybe an arg of g_func?
3399 if (opr->type == OPT_REGMEM && is_stack_access(po, opr))
3400 {
3401 char ofs_reg[16] = { 0, };
3402 int arg, arg_s, arg_i;
3403 int stack_ra = 0;
3404 int offset = 0;
3405
9af2d373 3406 if (g_header_mode)
3407 return NULL;
3408
1cd4a663 3409 parse_stack_access(po, opr->name, ofs_reg,
037f4971 3410 &offset, &stack_ra, NULL, 0);
1cd4a663 3411 if (ofs_reg[0] != 0)
3412 ferr(po, "offset reg on arg access?\n");
da87ae38 3413 if (offset <= stack_ra) {
3414 // search who set the stack var instead
3415 if (search_instead != NULL)
3416 *search_instead = 1;
3417 return NULL;
3418 }
1cd4a663 3419
3420 arg_i = (offset - stack_ra - 4) / 4;
3421 for (arg = arg_s = 0; arg < g_func_pp->argc; arg++) {
3422 if (g_func_pp->arg[arg].reg != NULL)
3423 continue;
3424 if (arg_s == arg_i)
3425 break;
3426 arg_s++;
3427 }
3428 if (arg == g_func_pp->argc)
3429 ferr(po, "stack arg %d not in prototype?\n", arg_i);
3430
93b5bd18 3431 pp = g_func_pp->arg[arg].pp;
3432 if (is_call) {
3433 if (pp == NULL)
3434 ferr(po, "icall arg: arg%d has no pp\n", arg + 1);
3435 check_func_pp(po, pp, "icall arg");
3436 }
89ff3147 3437 }
3438 else if (opr->type == OPT_REGMEM && strchr(opr->name + 1, '[')) {
3439 // label[index]
3440 p = strchr(opr->name + 1, '[');
3441 memcpy(buf, opr->name, p - opr->name);
3442 buf[p - opr->name] = 0;
92d715b6 3443 pp = proto_parse(g_fhdr, buf, g_quiet_pp);
1cd4a663 3444 }
3445 else if (opr->type == OPT_OFFSET || opr->type == OPT_LABEL) {
9af2d373 3446 pp = proto_parse(g_fhdr, opr->name, g_quiet_pp);
3447 if (pp == NULL) {
3448 if (!g_header_mode)
3449 ferr(po, "proto_parse failed for icall to '%s'\n", opr->name);
3450 }
3451 else
3452 check_func_pp(po, pp, "reg-fptr ref");
1cd4a663 3453 }
3454
3455 return pp;
3456}
3457
da87ae38 3458static void scan_for_call_type(int i, const struct parsed_opr *opr,
db63af51 3459 int magic, const struct parsed_proto **pp_found, int *pp_i,
3460 int *multi)
1cd4a663 3461{
3462 const struct parsed_proto *pp = NULL;
3463 struct parsed_op *po;
3464 struct label_ref *lr;
3465
037f4971 3466 ops[i].cc_scratch = magic;
1cd4a663 3467
037f4971 3468 while (1) {
d7857c3a 3469 if (g_labels[i] != NULL) {
1cd4a663 3470 lr = &g_label_refs[i];
92d715b6 3471 for (; lr != NULL; lr = lr->next) {
3472 check_i(&ops[i], lr->i);
db63af51 3473 scan_for_call_type(lr->i, opr, magic, pp_found, pp_i, multi);
92d715b6 3474 }
46411e6c 3475 if (i > 0 && LAST_OP(i - 1))
3476 return;
1cd4a663 3477 }
037f4971 3478
1cd4a663 3479 i--;
037f4971 3480 if (i < 0)
3481 break;
3482
3483 if (ops[i].cc_scratch == magic)
3484 return;
3485 ops[i].cc_scratch = magic;
1cd4a663 3486
3487 if (!(ops[i].flags & OPF_DATA))
3488 continue;
3489 if (!is_opr_modified(opr, &ops[i]))
3490 continue;
3491 if (ops[i].op != OP_MOV && ops[i].op != OP_LEA) {
3492 // most probably trashed by some processing
3493 *pp_found = NULL;
3494 return;
3495 }
3496
3497 opr = &ops[i].operand[1];
3498 if (opr->type != OPT_REG)
3499 break;
3500 }
3501
3502 po = (i >= 0) ? &ops[i] : ops;
3503
3504 if (i < 0) {
3505 // reached the top - can only be an arg-reg
04abc5d6 3506 if (opr->type != OPT_REG || g_func_pp == NULL)
1cd4a663 3507 return;
3508
3509 for (i = 0; i < g_func_pp->argc; i++) {
3510 if (g_func_pp->arg[i].reg == NULL)
3511 continue;
3512 if (IS(opr->name, g_func_pp->arg[i].reg))
3513 break;
3514 }
3515 if (i == g_func_pp->argc)
3516 return;
93b5bd18 3517 pp = g_func_pp->arg[i].pp;
1cd4a663 3518 if (pp == NULL)
46411e6c 3519 ferr(po, "icall: arg%d (%s) is not a fptr?\n",
3520 i + 1, g_func_pp->arg[i].reg);
89ff3147 3521 check_func_pp(po, pp, "icall reg-arg");
1cd4a663 3522 }
3523 else
93b5bd18 3524 pp = try_recover_pp(po, opr, 1, NULL);
1cd4a663 3525
89ff3147 3526 if (*pp_found != NULL && pp != NULL && *pp_found != pp) {
1cd4a663 3527 if (!IS((*pp_found)->ret_type.name, pp->ret_type.name)
3528 || (*pp_found)->is_stdcall != pp->is_stdcall
89ff3147 3529 || (*pp_found)->is_fptr != pp->is_fptr
1cd4a663 3530 || (*pp_found)->argc != pp->argc
3531 || (*pp_found)->argc_reg != pp->argc_reg
3532 || (*pp_found)->argc_stack != pp->argc_stack)
3533 {
3534 ferr(po, "icall: parsed_proto mismatch\n");
3535 }
89ff3147 3536 *multi = 1;
1cd4a663 3537 }
db63af51 3538 if (pp != NULL) {
1cd4a663 3539 *pp_found = pp;
db63af51 3540 *pp_i = po - ops;
3541 }
1cd4a663 3542}
3543
66bdb2b0 3544static void add_label_ref(struct label_ref *lr, int op_i)
9af2d373 3545{
66bdb2b0 3546 struct label_ref *lr_new;
9af2d373 3547
66bdb2b0 3548 if (lr->i == -1) {
3549 lr->i = op_i;
3550 return;
3551 }
9af2d373 3552
66bdb2b0 3553 lr_new = calloc(1, sizeof(*lr_new));
3554 lr_new->i = op_i;
3555 lr_new->next = lr->next;
3556 lr->next = lr_new;
3557}
3558
3559static struct parsed_data *try_resolve_jumptab(int i, int opcnt)
3560{
3561 struct parsed_op *po = &ops[i];
3562 struct parsed_data *pd;
3563 char label[NAMELEN], *p;
3564 int len, j, l;
3565
3566 p = strchr(po->operand[0].name, '[');
3567 if (p == NULL)
3568 return NULL;
3569
3570 len = p - po->operand[0].name;
3571 strncpy(label, po->operand[0].name, len);
3572 label[len] = 0;
3573
3574 for (j = 0, pd = NULL; j < g_func_pd_cnt; j++) {
3575 if (IS(g_func_pd[j].label, label)) {
3576 pd = &g_func_pd[j];
3577 break;
3578 }
3579 }
3580 if (pd == NULL)
3581 //ferr(po, "label '%s' not parsed?\n", label);
3582 return NULL;
3583
3584 if (pd->type != OPT_OFFSET)
3585 ferr(po, "label '%s' with non-offset data?\n", label);
3586
3587 // find all labels, link
3588 for (j = 0; j < pd->count; j++) {
3589 for (l = 0; l < opcnt; l++) {
3590 if (g_labels[l] != NULL && IS(g_labels[l], pd->d[j].u.label)) {
3591 add_label_ref(&g_label_refs[l], i);
3592 pd->d[j].bt_i = l;
3593 break;
3594 }
3595 }
3596 }
3597
3598 return pd;
3599}
3600
3601static void clear_labels(int count)
3602{
3603 int i;
3604
3605 for (i = 0; i < count; i++) {
3606 if (g_labels[i] != NULL) {
3607 free(g_labels[i]);
3608 g_labels[i] = NULL;
3609 }
3610 }
3611}
3612
b2bd20c0 3613static int get_pp_arg_regmask_src(const struct parsed_proto *pp)
3614{
3615 int regmask = 0;
3616 int i, reg;
3617
3618 for (i = 0; i < pp->argc; i++) {
3619 if (pp->arg[i].reg != NULL) {
3620 reg = char_array_i(regs_r32,
3621 ARRAY_SIZE(regs_r32), pp->arg[i].reg);
3622 if (reg < 0)
3623 ferr(ops, "arg '%s' of func '%s' is not a reg?\n",
3624 pp->arg[i].reg, pp->name);
3625 regmask |= 1 << reg;
3626 }
3627 }
3628
3629 return regmask;
3630}
3631
3632static int get_pp_arg_regmask_dst(const struct parsed_proto *pp)
3633{
4d247254 3634 int regmask = 0;
3635 int i, reg;
3636
3637 if (pp->has_retreg) {
3638 for (i = 0; i < pp->argc; i++) {
3639 if (pp->arg[i].type.is_retreg) {
3640 reg = char_array_i(regs_r32,
3641 ARRAY_SIZE(regs_r32), pp->arg[i].reg);
3642 ferr_assert(ops, reg >= 0);
3643 regmask |= 1 << reg;
3644 }
3645 }
3646 }
3647
b2bd20c0 3648 if (strstr(pp->ret_type.name, "int64"))
4d247254 3649 return regmask | (1 << xAX) | (1 << xDX);
d4a985bd 3650 if (IS(pp->ret_type.name, "float")
3651 || IS(pp->ret_type.name, "double"))
3652 {
4d247254 3653 return regmask | mxST0;
d4a985bd 3654 }
b2bd20c0 3655 if (strcasecmp(pp->ret_type.name, "void") == 0)
4d247254 3656 return regmask;
b2bd20c0 3657
4d247254 3658 return regmask | mxAX;
b2bd20c0 3659}
3660
93b5bd18 3661static int are_ops_same(struct parsed_op *po1, struct parsed_op *po2)
3662{
3663 return po1->op == po2->op && po1->operand_cnt == po2->operand_cnt
3664 && memcmp(po1->operand, po2->operand,
3665 sizeof(po1->operand[0]) * po1->operand_cnt) == 0;
3666}
3667
66bdb2b0 3668static void resolve_branches_parse_calls(int opcnt)
3669{
d4a985bd 3670 static const struct {
3671 const char *name;
3672 enum op_op op;
3673 unsigned int flags;
3674 unsigned int regmask_src;
3675 unsigned int regmask_dst;
3676 } pseudo_ops[] = {
622eb2ef 3677 { "__allshl", OPP_ALLSHL, OPF_DATA, mxAX|mxDX|mxCX, mxAX|mxDX },
3678 { "__allshr", OPP_ALLSHR, OPF_DATA, mxAX|mxDX|mxCX, mxAX|mxDX },
3679 { "__ftol", OPP_FTOL, OPF_FPOP, mxST0, mxAX | mxDX },
8c83cc48 3680 { "__CIpow", OPP_CIPOW, OPF_FPOP, mxST0|mxST1, mxST0 },
d4a985bd 3681 };
66bdb2b0 3682 const struct parsed_proto *pp_c;
3683 struct parsed_proto *pp;
3684 struct parsed_data *pd;
3685 struct parsed_op *po;
3686 const char *tmpname;
b2bd20c0 3687 int i, l;
3688 int ret;
66bdb2b0 3689
3690 for (i = 0; i < opcnt; i++)
3691 {
3692 po = &ops[i];
3693 po->bt_i = -1;
3694 po->btj = NULL;
3695
865f1aca 3696 if (po->datap != NULL) {
3697 pp = calloc(1, sizeof(*pp));
3698 my_assert_not(pp, NULL);
3699
3700 ret = parse_protostr(po->datap, pp);
3701 if (ret < 0)
3702 ferr(po, "bad protostr supplied: %s\n", (char *)po->datap);
3703 free(po->datap);
3704 po->datap = NULL;
3705 po->pp = pp;
3706 }
3707
66bdb2b0 3708 if (po->op == OP_CALL) {
3709 pp = NULL;
3710
865f1aca 3711 if (po->pp != NULL)
3712 pp = po->pp;
3713 else if (po->operand[0].type == OPT_LABEL)
3714 {
66bdb2b0 3715 tmpname = opr_name(po, 0);
3716 if (IS_START(tmpname, "loc_"))
3717 ferr(po, "call to loc_*\n");
16057ce1 3718 if (IS(tmpname, "__alloca_probe"))
3719 continue;
d4a985bd 3720
3721 // convert some calls to pseudo-ops
3722 for (l = 0; l < ARRAY_SIZE(pseudo_ops); l++) {
3723 if (!IS(tmpname, pseudo_ops[l].name))
3724 continue;
3725
3726 po->op = pseudo_ops[l].op;
3727 po->operand_cnt = 0;
3728 po->regmask_src = pseudo_ops[l].regmask_src;
3729 po->regmask_dst = pseudo_ops[l].regmask_dst;
3730 po->flags = pseudo_ops[l].flags;
3731 po->flags |= po->regmask_dst ? OPF_DATA : 0;
3732 break;
3733 }
3734 if (l < ARRAY_SIZE(pseudo_ops))
3735 continue;
3736
66bdb2b0 3737 pp_c = proto_parse(g_fhdr, tmpname, g_header_mode);
3738 if (!g_header_mode && pp_c == NULL)
3739 ferr(po, "proto_parse failed for call '%s'\n", tmpname);
3740
3741 if (pp_c != NULL) {
3742 pp = proto_clone(pp_c);
3743 my_assert_not(pp, NULL);
3744 }
3745 }
66bdb2b0 3746
3747 if (pp != NULL) {
3748 if (pp->is_fptr)
3749 check_func_pp(po, pp, "fptr var call");
3750 if (pp->is_noreturn)
3751 po->flags |= OPF_TAIL;
3752 }
3753 po->pp = pp;
3754 continue;
3755 }
3756
3757 if (!(po->flags & OPF_JMP) || po->op == OP_RET)
3758 continue;
3759
3760 if (po->operand[0].type == OPT_REGMEM) {
3761 pd = try_resolve_jumptab(i, opcnt);
3762 if (pd == NULL)
3763 goto tailcall;
3764
3765 po->btj = pd;
3766 continue;
3767 }
3768
3769 for (l = 0; l < opcnt; l++) {
3770 if (g_labels[l] != NULL
3771 && IS(po->operand[0].name, g_labels[l]))
3772 {
3773 if (l == i + 1 && po->op == OP_JMP) {
3774 // yet another alignment type..
3775 po->flags |= OPF_RMD|OPF_DONE;
3776 break;
3777 }
3778 add_label_ref(&g_label_refs[l], i);
3779 po->bt_i = l;
3780 break;
3781 }
3782 }
3783
3784 if (po->bt_i != -1 || (po->flags & OPF_RMD))
3785 continue;
3786
3787 if (po->operand[0].type == OPT_LABEL)
3788 // assume tail call
3789 goto tailcall;
3790
3791 ferr(po, "unhandled branch\n");
3792
3793tailcall:
3794 po->op = OP_CALL;
3795 po->flags |= OPF_TAIL;
3796 if (i > 0 && ops[i - 1].op == OP_POP)
3797 po->flags |= OPF_ATAIL;
3798 i--; // reprocess
3799 }
9af2d373 3800}
3801
3802static void scan_prologue_epilogue(int opcnt)
3803{
16057ce1 3804 int ecx_push = 0, esp_sub = 0, pusha = 0;
3805 int sandard_epilogue;
9af2d373 3806 int found;
3807 int i, j, l;
3808
3809 if (ops[0].op == OP_PUSH && IS(opr_name(&ops[0], 0), "ebp")
3810 && ops[1].op == OP_MOV
3811 && IS(opr_name(&ops[1], 0), "ebp")
3812 && IS(opr_name(&ops[1], 1), "esp"))
3813 {
3814 g_bp_frame = 1;
b2bd20c0 3815 ops[0].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
3816 ops[1].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
9af2d373 3817 i = 2;
3818
16057ce1 3819 if (ops[i].op == OP_PUSHA) {
3820 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
3821 pusha = 1;
3822 i++;
3823 }
3824
3825 if (ops[i].op == OP_SUB && IS(opr_name(&ops[i], 0), "esp")) {
3826 g_stack_fsz = opr_const(&ops[i], 1);
3827 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
9af2d373 3828 i++;
3829 }
3830 else {
3831 // another way msvc builds stack frame..
9af2d373 3832 while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
3833 g_stack_fsz += 4;
b2bd20c0 3834 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
9af2d373 3835 ecx_push++;
3836 i++;
3837 }
3838 // and another way..
3839 if (i == 2 && ops[i].op == OP_MOV && ops[i].operand[0].reg == xAX
3840 && ops[i].operand[1].type == OPT_CONST
3841 && ops[i + 1].op == OP_CALL
3842 && IS(opr_name(&ops[i + 1], 0), "__alloca_probe"))
3843 {
3844 g_stack_fsz += ops[i].operand[1].val;
b2bd20c0 3845 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
9af2d373 3846 i++;
b2bd20c0 3847 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
9af2d373 3848 i++;
3849 }
3850 }
3851
3852 found = 0;
3853 do {
3854 for (; i < opcnt; i++)
66bdb2b0 3855 if (ops[i].flags & OPF_TAIL)
9af2d373 3856 break;
3857 j = i - 1;
3858 if (i == opcnt && (ops[j].flags & OPF_JMP)) {
66bdb2b0 3859 if (ops[j].bt_i != -1 || ops[j].btj != NULL)
3860 break;
3861 i--;
9af2d373 3862 j--;
3863 }
3864
16057ce1 3865 sandard_epilogue = 0;
3866 if (ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ebp"))
3867 {
3868 ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
3869 // the standard epilogue is sometimes even used without a sf
3870 if (ops[j - 1].op == OP_MOV
3871 && IS(opr_name(&ops[j - 1], 0), "esp")
3872 && IS(opr_name(&ops[j - 1], 1), "ebp"))
3873 sandard_epilogue = 1;
3874 }
3875 else if (ops[j].op == OP_LEAVE)
9af2d373 3876 {
b2bd20c0 3877 ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
16057ce1 3878 sandard_epilogue = 1;
9af2d373 3879 }
66bdb2b0 3880 else if (ops[i].op == OP_CALL && ops[i].pp != NULL
3881 && ops[i].pp->is_noreturn)
3882 {
3883 // on noreturn, msvc sometimes cleans stack, sometimes not
3884 i++;
3885 found = 1;
3886 continue;
3887 }
9af2d373 3888 else if (!(g_ida_func_attr & IDAFA_NORETURN))
3889 ferr(&ops[j], "'pop ebp' expected\n");
3890
16057ce1 3891 if (g_stack_fsz != 0 || sandard_epilogue) {
bfacdc83 3892 if (ops[j].op == OP_LEAVE)
3893 j--;
16057ce1 3894 else if (sandard_epilogue) // mov esp, ebp
9af2d373 3895 {
b2bd20c0 3896 ops[j - 1].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
bfacdc83 3897 j -= 2;
9af2d373 3898 }
bfacdc83 3899 else if (!(g_ida_func_attr & IDAFA_NORETURN))
9af2d373 3900 {
bfacdc83 3901 ferr(&ops[j], "esp restore expected\n");
9af2d373 3902 }
3903
bfacdc83 3904 if (ecx_push && j >= 0 && ops[j].op == OP_POP
3905 && IS(opr_name(&ops[j], 0), "ecx"))
9af2d373 3906 {
bfacdc83 3907 ferr(&ops[j], "unexpected ecx pop\n");
9af2d373 3908 }
3909 }
3910
16057ce1 3911 if (pusha) {
3912 if (ops[j].op == OP_POPA)
3913 ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
3914 else
3915 ferr(&ops[j], "popa expected\n");
3916 }
3917
9af2d373 3918 found = 1;
3919 i++;
3920 } while (i < opcnt);
3921
66bdb2b0 3922 if (!found)
3923 ferr(ops, "missing ebp epilogue\n");
9af2d373 3924 return;
3925 }
3926
3927 // non-bp frame
3928 i = 0;
3929 while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
b2bd20c0 3930 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
9af2d373 3931 g_stack_fsz += 4;
3932 ecx_push++;
3933 i++;
3934 }
3935
3936 for (; i < opcnt; i++) {
3937 if (ops[i].op == OP_PUSH || (ops[i].flags & (OPF_JMP|OPF_TAIL)))
3938 break;
3939 if (ops[i].op == OP_SUB && ops[i].operand[0].reg == xSP
3940 && ops[i].operand[1].type == OPT_CONST)
3941 {
3942 g_stack_fsz = ops[i].operand[1].val;
b2bd20c0 3943 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
16057ce1 3944 i++;
3945 esp_sub = 1;
3946 break;
3947 }
3948 else if (ops[i].op == OP_MOV && ops[i].operand[0].reg == xAX
3949 && ops[i].operand[1].type == OPT_CONST
3950 && ops[i + 1].op == OP_CALL
3951 && IS(opr_name(&ops[i + 1], 0), "__alloca_probe"))
3952 {
3953 g_stack_fsz += ops[i].operand[1].val;
3954 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
3955 i++;
3956 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
3957 i++;
9af2d373 3958 esp_sub = 1;
3959 break;
3960 }
3961 }
3962
3963 if (ecx_push && !esp_sub) {
3964 // could actually be args for a call..
3965 for (; i < opcnt; i++)
3966 if (ops[i].op != OP_PUSH)
3967 break;
3968
3969 if (ops[i].op == OP_CALL && ops[i].operand[0].type == OPT_LABEL) {
3970 const struct parsed_proto *pp;
3971 pp = proto_parse(g_fhdr, opr_name(&ops[i], 0), 1);
3972 j = pp ? pp->argc_stack : 0;
3973 while (i > 0 && j > 0) {
3974 i--;
3975 if (ops[i].op == OP_PUSH) {
b2bd20c0 3976 ops[i].flags &= ~(OPF_RMD | OPF_DONE | OPF_NOREGS);
9af2d373 3977 j--;
3978 }
3979 }
3980 if (j != 0)
3981 ferr(&ops[i], "unhandled prologue\n");
3982
3983 // recheck
3984 i = g_stack_fsz = ecx_push = 0;
3985 while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
3986 if (!(ops[i].flags & OPF_RMD))
3987 break;
3988 g_stack_fsz += 4;
3989 ecx_push++;
3990 i++;
3991 }
3992 }
3993 }
3994
3995 found = 0;
3996 if (ecx_push || esp_sub)
3997 {
3998 g_sp_frame = 1;
3999
9af2d373 4000 do {
4001 for (; i < opcnt; i++)
66bdb2b0 4002 if (ops[i].flags & OPF_TAIL)
9af2d373 4003 break;
fe18df39 4004
9af2d373 4005 j = i - 1;
4006 if (i == opcnt && (ops[j].flags & OPF_JMP)) {
66bdb2b0 4007 if (ops[j].bt_i != -1 || ops[j].btj != NULL)
4008 break;
4009 i--;
9af2d373 4010 j--;
4011 }
4012
4013 if (ecx_push > 0) {
4014 for (l = 0; l < ecx_push; l++) {
4015 if (ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ecx"))
4016 /* pop ecx */;
4017 else if (ops[j].op == OP_ADD
4018 && IS(opr_name(&ops[j], 0), "esp")
4019 && ops[j].operand[1].type == OPT_CONST)
4020 {
4021 /* add esp, N */
5e49b270 4022 l += ops[j].operand[1].val / 4 - 1;
9af2d373 4023 }
4024 else
4025 ferr(&ops[j], "'pop ecx' expected\n");
4026
b2bd20c0 4027 ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
9af2d373 4028 j--;
4029 }
4030 if (l != ecx_push)
4031 ferr(&ops[j], "epilogue scan failed\n");
4032
4033 found = 1;
4034 }
4035
4036 if (esp_sub) {
4037 if (ops[j].op != OP_ADD
4038 || !IS(opr_name(&ops[j], 0), "esp")
4039 || ops[j].operand[1].type != OPT_CONST
4040 || ops[j].operand[1].val != g_stack_fsz)
622eb2ef 4041 {
4042 if (ops[i].op == OP_CALL && ops[i].pp != NULL
4043 && ops[i].pp->is_noreturn)
4044 {
4045 // noreturn tailcall with no epilogue
4046 i++;
4047 continue;
4048 }
9af2d373 4049 ferr(&ops[j], "'add esp' expected\n");
622eb2ef 4050 }
9af2d373 4051
b2bd20c0 4052 ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
9af2d373 4053 ops[j].operand[1].val = 0; // hack for stack arg scanner
4054 found = 1;
4055 }
4056
4057 i++;
4058 } while (i < opcnt);
66bdb2b0 4059
4060 if (!found)
4061 ferr(ops, "missing esp epilogue\n");
9af2d373 4062 }
4063}
4064
23fd0b11 4065// find an instruction that changed opr before i op
db63af51 4066// *op_i must be set to -1 by the caller
865f1aca 4067// *is_caller is set to 1 if one source is determined to be g_func arg
92d715b6 4068// returns 1 if found, *op_i is then set to origin
865f1aca 4069// returns -1 if multiple origins are found
23fd0b11 4070static int resolve_origin(int i, const struct parsed_opr *opr,
92d715b6 4071 int magic, int *op_i, int *is_caller)
1f84f6b3 4072{
4073 struct label_ref *lr;
4074 int ret = 0;
4075
1f84f6b3 4076 while (1) {
d7857c3a 4077 if (g_labels[i] != NULL) {
1f84f6b3 4078 lr = &g_label_refs[i];
92d715b6 4079 for (; lr != NULL; lr = lr->next) {
4080 check_i(&ops[i], lr->i);
4081 ret |= resolve_origin(lr->i, opr, magic, op_i, is_caller);
4082 }
1f84f6b3 4083 if (i > 0 && LAST_OP(i - 1))
4084 return ret;
4085 }
4086
4087 i--;
92d715b6 4088 if (i < 0) {
4089 if (is_caller != NULL)
4090 *is_caller = 1;
1f84f6b3 4091 return -1;
92d715b6 4092 }
1f84f6b3 4093
4094 if (ops[i].cc_scratch == magic)
ebc4dc43 4095 return ret;
1f84f6b3 4096 ops[i].cc_scratch = magic;
4097
4098 if (!(ops[i].flags & OPF_DATA))
4099 continue;
4100 if (!is_opr_modified(opr, &ops[i]))
4101 continue;
23fd0b11 4102
4103 if (*op_i >= 0) {
93b5bd18 4104 if (*op_i == i || are_ops_same(&ops[*op_i], &ops[i]))
ebc4dc43 4105 return ret | 1;
4106
23fd0b11 4107 return -1;
4108 }
4109
4110 *op_i = i;
ebc4dc43 4111 return ret | 1;
23fd0b11 4112 }
4113}
4114
db63af51 4115// find an instruction that previously referenced opr
4116// if multiple results are found - fail
4117// *op_i must be set to -1 by the caller
4118// returns 1 if found, *op_i is then set to referencer insn
4119static int resolve_last_ref(int i, const struct parsed_opr *opr,
4120 int magic, int *op_i)
4121{
4122 struct label_ref *lr;
4123 int ret = 0;
4124
db63af51 4125 while (1) {
4126 if (g_labels[i] != NULL) {
4127 lr = &g_label_refs[i];
4128 for (; lr != NULL; lr = lr->next) {
4129 check_i(&ops[i], lr->i);
4130 ret |= resolve_last_ref(lr->i, opr, magic, op_i);
4131 }
4132 if (i > 0 && LAST_OP(i - 1))
4133 return ret;
4134 }
4135
4136 i--;
4137 if (i < 0)
4138 return -1;
4139
4140 if (ops[i].cc_scratch == magic)
4141 return 0;
4142 ops[i].cc_scratch = magic;
4143
4144 if (!is_opr_referenced(opr, &ops[i]))
4145 continue;
4146
4147 if (*op_i >= 0)
4148 return -1;
4149
4150 *op_i = i;
4151 return 1;
4152 }
4153}
4154
16057ce1 4155// adjust datap of all reachable 'op' insns when moving back
4156// returns 1 if at least 1 op was found
4157// returns -1 if path without an op was found
4158static int adjust_prev_op(int i, enum op_op op, int magic, void *datap)
4159{
4160 struct label_ref *lr;
4161 int ret = 0;
4162
4163 if (ops[i].cc_scratch == magic)
4164 return 0;
4165 ops[i].cc_scratch = magic;
4166
4167 while (1) {
4168 if (g_labels[i] != NULL) {
4169 lr = &g_label_refs[i];
4170 for (; lr != NULL; lr = lr->next) {
4171 check_i(&ops[i], lr->i);
4172 ret |= adjust_prev_op(lr->i, op, magic, datap);
4173 }
4174 if (i > 0 && LAST_OP(i - 1))
4175 return ret;
4176 }
4177
4178 i--;
4179 if (i < 0)
4180 return -1;
4181
4182 if (ops[i].cc_scratch == magic)
4183 return 0;
4184 ops[i].cc_scratch = magic;
4185
4186 if (ops[i].op != op)
4187 continue;
4188
4189 ops[i].datap = datap;
4190 return 1;
4191 }
4192}
4193
db63af51 4194// find next instruction that reads opr
db63af51 4195// *op_i must be set to -1 by the caller
b2bd20c0 4196// on return, *op_i is set to first referencer insn
4197// returns 1 if exactly 1 referencer is found
db63af51 4198static int find_next_read(int i, int opcnt,
4199 const struct parsed_opr *opr, int magic, int *op_i)
4200{
4201 struct parsed_op *po;
4202 int j, ret = 0;
4203
4204 for (; i < opcnt; i++)
4205 {
4206 if (ops[i].cc_scratch == magic)
b2bd20c0 4207 return ret;
db63af51 4208 ops[i].cc_scratch = magic;
4209
4210 po = &ops[i];
4211 if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
4212 if (po->btj != NULL) {
4213 // jumptable
4214 for (j = 0; j < po->btj->count; j++) {
4215 check_i(po, po->btj->d[j].bt_i);
4216 ret |= find_next_read(po->btj->d[j].bt_i, opcnt, opr,
4217 magic, op_i);
4218 }
4219 return ret;
4220 }
4221
4222 if (po->flags & OPF_RMD)
4223 continue;
4224 check_i(po, po->bt_i);
4225 if (po->flags & OPF_CJMP) {
b2bd20c0 4226 ret |= find_next_read(po->bt_i, opcnt, opr, magic, op_i);
db63af51 4227 if (ret < 0)
4228 return ret;
4229 }
b2bd20c0 4230 else
4231 i = po->bt_i - 1;
db63af51 4232 continue;
4233 }
4234
4235 if (!is_opr_read(opr, po)) {
16057ce1 4236 int full_opr = 1;
4237 if (opr->type == OPT_REG && po->operand[0].type == OPT_REG
4238 && opr->reg == po->operand[0].reg && (po->flags & OPF_DATA))
acd03176 4239 {
16057ce1 4240 full_opr = po->operand[0].lmod >= opr->lmod;
4241 }
4242 if (is_opr_modified(opr, po) && full_opr) {
db63af51 4243 // it's overwritten
b2bd20c0 4244 return ret;
acd03176 4245 }
db63af51 4246 if (po->flags & OPF_TAIL)
b2bd20c0 4247 return ret;
db63af51 4248 continue;
4249 }
4250
4251 if (*op_i >= 0)
4252 return -1;
4253
4254 *op_i = i;
4255 return 1;
4256 }
4257
4258 return 0;
4259}
4260
16057ce1 4261// find next instruction that reads opr
4262// *op_i must be set to -1 by the caller
4263// on return, *op_i is set to first flag user insn
4264// returns 1 if exactly 1 flag user is found
4265static int find_next_flag_use(int i, int opcnt, int magic, int *op_i)
4266{
4267 struct parsed_op *po;
4268 int j, ret = 0;
4269
4270 for (; i < opcnt; i++)
4271 {
4272 if (ops[i].cc_scratch == magic)
4273 return ret;
4274 ops[i].cc_scratch = magic;
4275
4276 po = &ops[i];
4277 if (po->op == OP_CALL)
4278 return -1;
4279 if (po->flags & OPF_JMP) {
4280 if (po->btj != NULL) {
4281 // jumptable
4282 for (j = 0; j < po->btj->count; j++) {
4283 check_i(po, po->btj->d[j].bt_i);
4284 ret |= find_next_flag_use(po->btj->d[j].bt_i, opcnt,
4285 magic, op_i);
4286 }
4287 return ret;
4288 }
4289
4290 if (po->flags & OPF_RMD)
4291 continue;
4292 check_i(po, po->bt_i);
4293 if (po->flags & OPF_CJMP)
4294 goto found;
4295 else
4296 i = po->bt_i - 1;
4297 continue;
4298 }
4299
4300 if (!(po->flags & OPF_CC)) {
4301 if (po->flags & OPF_FLAGS)
4302 // flags changed
4303 return ret;
4304 if (po->flags & OPF_TAIL)
4305 return ret;
4306 continue;
4307 }
4308
4309found:
4310 if (*op_i >= 0)
4311 return -1;
4312
4313 *op_i = i;
4314 return 1;
4315 }
4316
4317 return 0;
4318}
4319
23fd0b11 4320static int try_resolve_const(int i, const struct parsed_opr *opr,
4321 int magic, unsigned int *val)
4322{
4323 int s_i = -1;
92d715b6 4324 int ret;
23fd0b11 4325
92d715b6 4326 ret = resolve_origin(i, opr, magic, &s_i, NULL);
23fd0b11 4327 if (ret == 1) {
4328 i = s_i;
1f84f6b3 4329 if (ops[i].op != OP_MOV && ops[i].operand[1].type != OPT_CONST)
4330 return -1;
4331
4332 *val = ops[i].operand[1].val;
4333 return 1;
4334 }
23fd0b11 4335
4336 return -1;
1f84f6b3 4337}
4338
16057ce1 4339static int resolve_used_bits(int i, int opcnt, int reg,
4340 int *mask, int *is_z_check)
4341{
4342 struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_WORD, reg);
4343 int j = -1, k = -1;
4344 int ret;
4345
4346 ret = find_next_read(i, opcnt, &opr, i + opcnt * 20, &j);
4347 if (ret != 1)
4348 return -1;
4349
4350 find_next_read(j + 1, opcnt, &opr, i + opcnt * 20 + 1, &k);
4351 if (k != -1) {
4352 fnote(&ops[j], "(first read)\n");
4353 ferr(&ops[k], "TODO: bit resolve: multiple readers\n");
4354 }
4355
4356 if (ops[j].op != OP_TEST || ops[j].operand[1].type != OPT_CONST)
4357 ferr(&ops[j], "TODO: bit resolve: not a const test\n");
4358
4359 ferr_assert(&ops[j], ops[j].operand[0].type == OPT_REG);
4360 ferr_assert(&ops[j], ops[j].operand[0].reg == reg);
4361
4362 *mask = ops[j].operand[1].val;
4363 if (ops[j].operand[0].lmod == OPLM_BYTE
4364 && ops[j].operand[0].name[1] == 'h')
4365 {
4366 *mask <<= 8;
4367 }
4368 ferr_assert(&ops[j], (*mask & ~0xffff) == 0);
4369
4370 *is_z_check = 0;
4371 ret = find_next_flag_use(j + 1, opcnt, i + opcnt * 20 + 2, &k);
4372 if (ret == 1)
4373 *is_z_check = ops[k].pfo == PFO_Z;
4374
4375 return 0;
4376}
4377
93b5bd18 4378static const struct parsed_proto *resolve_deref(int i, int magic,
4379 struct parsed_opr *opr, int level)
4380{
4381 struct parsed_opr opr_s = OPR_INIT(OPT_REG, OPLM_DWORD, 0);
4382 const struct parsed_proto *pp = NULL;
4383 int from_caller = 0;
4384 char s_reg[4];
4385 int offset = 0;
4386 int len = 0;
4387 int j = -1;
4388 int k = -1;
4389 int reg;
4390 int ret;
4391
4392 ret = sscanf(opr->name, "%3s+%x%n", s_reg, &offset, &len);
4393 if (ret != 2 || len != strlen(opr->name)) {
4394 ret = sscanf(opr->name, "%3s%n", s_reg, &len);
4395 if (ret != 1 || len != strlen(opr->name))
4396 return NULL;
4397 }
4398
4399 reg = char_array_i(regs_r32, ARRAY_SIZE(regs_r32), s_reg);
4400 if (reg < 0)
4401 return NULL;
4402
4403 opr_s.reg = reg;
4404 ret = resolve_origin(i, &opr_s, i + magic, &j, NULL);
4405 if (ret != 1)
4406 return NULL;
4407
4408 if (ops[j].op == OP_MOV && ops[j].operand[1].type == OPT_REGMEM
4409 && strlen(ops[j].operand[1].name) == 3
4410 && ops[j].operand[0].lmod == OPLM_DWORD
4411 && ops[j].pp == NULL // no hint
4412 && level == 0)
4413 {
4414 // allow one simple dereference (com/directx)
4415 reg = char_array_i(regs_r32, ARRAY_SIZE(regs_r32),
4416 ops[j].operand[1].name);
4417 if (reg < 0)
4418 return NULL;
4419 opr_s.reg = reg;
4420 ret = resolve_origin(j, &opr_s, j + magic, &k, NULL);
4421 if (ret != 1)
4422 return NULL;
4423 j = k;
4424 }
4425 if (ops[j].op != OP_MOV || ops[j].operand[0].lmod != OPLM_DWORD)
4426 return NULL;
4427
4428 if (ops[j].pp != NULL) {
4429 // type hint in asm
4430 pp = ops[j].pp;
4431 }
4432 else if (ops[j].operand[1].type == OPT_REGMEM) {
4433 pp = try_recover_pp(&ops[j], &ops[j].operand[1], 0, NULL);
4434 if (pp == NULL) {
4435 // maybe structure ptr in structure
4436 pp = resolve_deref(j, magic, &ops[j].operand[1], level + 1);
4437 }
4438 }
4439 else if (ops[j].operand[1].type == OPT_LABEL)
4440 pp = proto_parse(g_fhdr, ops[j].operand[1].name, g_quiet_pp);
4441 else if (ops[j].operand[1].type == OPT_REG) {
4442 // maybe arg reg?
4443 k = -1;
4444 ret = resolve_origin(j, &ops[j].operand[1], i + magic,
4445 &k, &from_caller);
4446 if (ret != 1 && from_caller && k == -1 && g_func_pp != NULL) {
4447 for (k = 0; k < g_func_pp->argc; k++) {
4448 if (g_func_pp->arg[k].reg == NULL)
4449 continue;
4450 if (IS(g_func_pp->arg[k].reg, ops[j].operand[1].name)) {
4451 pp = g_func_pp->arg[k].pp;
4452 break;
4453 }
4454 }
4455 }
4456 }
4457
4458 if (pp == NULL)
4459 return NULL;
4460 if (pp->is_func || pp->is_fptr || !pp->type.is_struct) {
4461 if (offset != 0)
4462 ferr(&ops[j], "expected struct, got '%s %s'\n",
4463 pp->type.name, pp->name);
4464 return NULL;
4465 }
4466
4467 return proto_lookup_struct(g_fhdr, pp->type.name, offset);
4468}
4469
865f1aca 4470static const struct parsed_proto *resolve_icall(int i, int opcnt,
4471 int *pp_i, int *multi_src)
4472{
4473 const struct parsed_proto *pp = NULL;
4474 int search_advice = 0;
865f1aca 4475
4476 *multi_src = 0;
4477 *pp_i = -1;
4478
4479 switch (ops[i].operand[0].type) {
4480 case OPT_REGMEM:
4481 // try to resolve struct member calls
93b5bd18 4482 pp = resolve_deref(i, i + opcnt * 19, &ops[i].operand[0], 0);
4483 if (pp != NULL)
865f1aca 4484 break;
865f1aca 4485 // fallthrough
4486 case OPT_LABEL:
4487 case OPT_OFFSET:
93b5bd18 4488 pp = try_recover_pp(&ops[i], &ops[i].operand[0],
4489 1, &search_advice);
865f1aca 4490 if (!search_advice)
4491 break;
4492 // fallthrough
4493 default:
4494 scan_for_call_type(i, &ops[i].operand[0], i + opcnt * 9, &pp,
4495 pp_i, multi_src);
4496 break;
4497 }
4498
4499 return pp;
4500}
4501
26677139 4502static struct parsed_proto *process_call_early(int i, int opcnt,
4503 int *adj_i)
4504{
4505 struct parsed_op *po = &ops[i];
4506 struct parsed_proto *pp;
4507 int multipath = 0;
4508 int adj = 0;
bfacdc83 4509 int j, ret;
26677139 4510
4511 pp = po->pp;
4512 if (pp == NULL || pp->is_vararg || pp->argc_reg != 0)
4513 // leave for later
4514 return NULL;
4515
4516 // look for and make use of esp adjust
4517 *adj_i = ret = -1;
4518 if (!pp->is_stdcall && pp->argc_stack > 0)
4519 ret = scan_for_esp_adjust(i + 1, opcnt,
ee2361b9 4520 pp->argc_stack * 4, &adj, &multipath, 0);
26677139 4521 if (ret >= 0) {
4522 if (pp->argc_stack > adj / 4)
4523 return NULL;
4524 if (multipath)
4525 return NULL;
bfacdc83 4526 if (ops[ret].op == OP_POP) {
4527 for (j = 1; j < adj / 4; j++) {
4528 if (ops[ret + j].op != OP_POP
4529 || ops[ret + j].operand[0].reg != xCX)
4530 {
4531 return NULL;
4532 }
4533 }
4534 }
26677139 4535 }
4536
4537 *adj_i = ret;
4538 return pp;
4539}
4540
9af2d373 4541static struct parsed_proto *process_call(int i, int opcnt)
4542{
4543 struct parsed_op *po = &ops[i];
4544 const struct parsed_proto *pp_c;
4545 struct parsed_proto *pp;
4546 const char *tmpname;
db63af51 4547 int call_i = -1, ref_i = -1;
26677139 4548 int adj = 0, multipath = 0;
9af2d373 4549 int ret, arg;
4550
4551 tmpname = opr_name(po, 0);
4552 pp = po->pp;
4553 if (pp == NULL)
4554 {
4555 // indirect call
db63af51 4556 pp_c = resolve_icall(i, opcnt, &call_i, &multipath);
9af2d373 4557 if (pp_c != NULL) {
4558 if (!pp_c->is_func && !pp_c->is_fptr)
4559 ferr(po, "call to non-func: %s\n", pp_c->name);
4560 pp = proto_clone(pp_c);
4561 my_assert_not(pp, NULL);
26677139 4562 if (multipath)
9af2d373 4563 // not resolved just to single func
4564 pp->is_fptr = 1;
4565
4566 switch (po->operand[0].type) {
4567 case OPT_REG:
4568 // we resolved this call and no longer need the register
4569 po->regmask_src &= ~(1 << po->operand[0].reg);
db63af51 4570
4571 if (!multipath && i != call_i && ops[call_i].op == OP_MOV
4572 && ops[call_i].operand[1].type == OPT_LABEL)
4573 {
4574 // no other source users?
e83ea7ed 4575 ret = resolve_last_ref(i, &po->operand[0], i + opcnt * 10,
db63af51 4576 &ref_i);
4577 if (ret == 1 && call_i == ref_i) {
4578 // and nothing uses it after us?
4579 ref_i = -1;
b2bd20c0 4580 find_next_read(i + 1, opcnt, &po->operand[0],
4581 i + opcnt * 11, &ref_i);
4582 if (ref_i == -1)
db63af51 4583 // then also don't need the source mov
b2bd20c0 4584 ops[call_i].flags |= OPF_RMD | OPF_NOREGS;
db63af51 4585 }
4586 }
9af2d373 4587 break;
4588 case OPT_REGMEM:
4589 pp->is_fptr = 1;
4590 break;
4591 default:
4592 break;
4593 }
4594 }
4595 if (pp == NULL) {
4596 pp = calloc(1, sizeof(*pp));
4597 my_assert_not(pp, NULL);
4598
4599 pp->is_fptr = 1;
ee2361b9 4600 ret = scan_for_esp_adjust(i + 1, opcnt,
bfacdc83 4601 -1, &adj, &multipath, 0);
26677139 4602 if (ret < 0 || adj < 0) {
9af2d373 4603 if (!g_allow_regfunc)
4604 ferr(po, "non-__cdecl indirect call unhandled yet\n");
4605 pp->is_unresolved = 1;
26677139 4606 adj = 0;
9af2d373 4607 }
26677139 4608 adj /= 4;
4609 if (adj > ARRAY_SIZE(pp->arg))
4610 ferr(po, "esp adjust too large: %d\n", adj);
9af2d373 4611 pp->ret_type.name = strdup("int");
26677139 4612 pp->argc = pp->argc_stack = adj;
9af2d373 4613 for (arg = 0; arg < pp->argc; arg++)
4614 pp->arg[arg].type.name = strdup("int");
4615 }
4616 po->pp = pp;
4617 }
4618
4619 // look for and make use of esp adjust
91ca764a 4620 multipath = 0;
9af2d373 4621 ret = -1;
bfacdc83 4622 if (!pp->is_stdcall && pp->argc_stack > 0) {
4623 int adj_expect = pp->is_vararg ? -1 : pp->argc_stack * 4;
9af2d373 4624 ret = scan_for_esp_adjust(i + 1, opcnt,
bfacdc83 4625 adj_expect, &adj, &multipath, 0);
4626 }
9af2d373 4627 if (ret >= 0) {
4628 if (pp->is_vararg) {
26677139 4629 if (adj / 4 < pp->argc_stack) {
4630 fnote(po, "(this call)\n");
4631 ferr(&ops[ret], "esp adjust is too small: %x < %x\n",
4632 adj, pp->argc_stack * 4);
4633 }
9af2d373 4634 // modify pp to make it have varargs as normal args
4635 arg = pp->argc;
26677139 4636 pp->argc += adj / 4 - pp->argc_stack;
9af2d373 4637 for (; arg < pp->argc; arg++) {
4638 pp->arg[arg].type.name = strdup("int");
4639 pp->argc_stack++;
4640 }
4641 if (pp->argc > ARRAY_SIZE(pp->arg))
4642 ferr(po, "too many args for '%s'\n", tmpname);
4643 }
26677139 4644 if (pp->argc_stack > adj / 4) {
9af2d373 4645 fnote(po, "(this call)\n");
4646 ferr(&ops[ret], "stack tracking failed for '%s': %x %x\n",
26677139 4647 tmpname, pp->argc_stack * 4, adj);
9af2d373 4648 }
4649
ee2361b9 4650 scan_for_esp_adjust(i + 1, opcnt,
4651 pp->argc_stack * 4, &adj, &multipath, 1);
9af2d373 4652 }
4653 else if (pp->is_vararg)
4654 ferr(po, "missing esp_adjust for vararg func '%s'\n",
4655 pp->name);
4656
4657 return pp;
4658}
4659
30620174 4660static int collect_call_args_no_push(int i, struct parsed_proto *pp,
4661 int *regmask_ffca)
4662{
4663 struct parsed_op *po;
4664 int offset = 0;
4665 int base_arg;
4666 int j, arg;
4667 int ret;
4668
4669 for (base_arg = 0; base_arg < pp->argc; base_arg++)
4670 if (pp->arg[base_arg].reg == NULL)
4671 break;
4672
4673 for (j = i; j > 0; )
4674 {
4675 ferr_assert(&ops[j], g_labels[j] == NULL);
4676 j--;
4677
4678 po = &ops[j];
4679 ferr_assert(po, po->op != OP_PUSH);
4680 if (po->op == OP_FST)
4681 {
4682 if (po->operand[0].type != OPT_REGMEM)
4683 continue;
4684 ret = parse_stack_esp_offset(po, po->operand[0].name, &offset);
4685 if (ret != 0)
4686 continue;
4687 if (offset < 0 || offset >= pp->argc_stack * 4 || (offset & 3))
4688 ferr(po, "bad offset %d (%d args)\n", offset, pp->argc_stack);
4689
4690 arg = base_arg + offset / 4;
4691 po->p_argnext = -1;
4692 po->p_argnum = arg + 1;
4693 ferr_assert(po, pp->arg[arg].datap == NULL);
4694 pp->arg[arg].datap = po;
4695 po->flags |= OPF_DONE | OPF_FARGNR | OPF_FARG;
4696 if (regmask_ffca != NULL)
4697 *regmask_ffca |= 1 << arg;
4698 }
4699 else if (po->op == OP_SUB && po->operand[0].reg == xSP
4700 && po->operand[1].type == OPT_CONST)
4701 {
4702 po->flags |= OPF_RMD | OPF_DONE | OPF_FARGNR | OPF_FARG;
4703 break;
4704 }
4705 }
4706
4707 for (arg = base_arg; arg < pp->argc; arg++) {
4708 ferr_assert(&ops[i], pp->arg[arg].reg == NULL);
4709 po = pp->arg[arg].datap;
4710 if (po == NULL)
4711 ferr(&ops[i], "arg %d/%d not found\n", arg, pp->argc);
4712 if (po->operand[0].lmod == OPLM_QWORD)
4713 arg++;
4714 }
4715
4716 return 0;
4717}
4718
4719static int collect_call_args_early(int i, struct parsed_proto *pp,
4720 int *regmask, int *regmask_ffca)
26677139 4721{
30620174 4722 struct parsed_op *po;
26677139 4723 int arg, ret;
4724 int j;
4725
4726 for (arg = 0; arg < pp->argc; arg++)
4727 if (pp->arg[arg].reg == NULL)
4728 break;
4729
4730 // first see if it can be easily done
4731 for (j = i; j > 0 && arg < pp->argc; )
4732 {
4733 if (g_labels[j] != NULL)
4734 return -1;
4735 j--;
4736
30620174 4737 po = &ops[j];
4738 if (po->op == OP_CALL)
26677139 4739 return -1;
30620174 4740 else if (po->op == OP_ADD && po->operand[0].reg == xSP)
26677139 4741 return -1;
30620174 4742 else if (po->op == OP_POP)
26677139 4743 return -1;
30620174 4744 else if (po->flags & OPF_CJMP)
26677139 4745 return -1;
30620174 4746 else if (po->op == OP_PUSH) {
4747 if (po->flags & (OPF_FARG|OPF_FARGNR))
26677139 4748 return -1;
622eb2ef 4749 if (!g_header_mode) {
30620174 4750 ret = scan_for_mod(po, j + 1, i, 1);
622eb2ef 4751 if (ret >= 0)
4752 return -1;
4753 }
26677139 4754
4755 if (pp->arg[arg].type.is_va_list)
4756 return -1;
4757
4758 // next arg
4759 for (arg++; arg < pp->argc; arg++)
4760 if (pp->arg[arg].reg == NULL)
4761 break;
4762 }
30620174 4763 else if (po->op == OP_SUB && po->operand[0].reg == xSP
4764 && po->operand[1].type == OPT_CONST)
4765 {
4766 if (po->flags & (OPF_RMD|OPF_DONE))
4767 return -1;
4768 if (po->operand[1].val != pp->argc_stack * 4)
4769 ferr(po, "unexpected esp adjust: %d\n",
4770 po->operand[1].val * 4);
4771 ferr_assert(po, pp->argc - arg == pp->argc_stack);
4772 return collect_call_args_no_push(i, pp, regmask_ffca);
4773 }
26677139 4774 }
4775
4776 if (arg < pp->argc)
4777 return -1;
4778
4779 // now do it
4780 for (arg = 0; arg < pp->argc; arg++)
4781 if (pp->arg[arg].reg == NULL)
4782 break;
4783
4784 for (j = i; j > 0 && arg < pp->argc; )
4785 {
4786 j--;
4787
4788 if (ops[j].op == OP_PUSH)
4789 {
4790 ops[j].p_argnext = -1;
4791 ferr_assert(&ops[j], pp->arg[arg].datap == NULL);
4792 pp->arg[arg].datap = &ops[j];
4793
30620174 4794 if (regmask != NULL && ops[j].operand[0].type == OPT_REG)
26677139 4795 *regmask |= 1 << ops[j].operand[0].reg;
4796
5e49b270 4797 ops[j].flags |= OPF_RMD | OPF_DONE | OPF_FARGNR | OPF_FARG;
26677139 4798 ops[j].flags &= ~OPF_RSAVE;
4799
4800 // next arg
4801 for (arg++; arg < pp->argc; arg++)
4802 if (pp->arg[arg].reg == NULL)
4803 break;
4804 }
4805 }
4806
4807 return 0;
4808}
4809
8c83cc48 4810static int sync_argnum(struct parsed_op *po, int argnum)
4811{
4812 struct parsed_op *po_tmp;
4813
4814 // see if other branches don't have higher argnum
4815 for (po_tmp = po; po_tmp != NULL; ) {
4816 if (argnum < po_tmp->p_argnum)
4817 argnum = po_tmp->p_argnum;
4818 // note: p_argnext is active on current collect_call_args only
4819 po_tmp = po_tmp->p_argnext >= 0 ? &ops[po_tmp->p_argnext] : NULL;
4820 }
4821
4822 // make all argnums consistent
4823 for (po_tmp = po; po_tmp != NULL; ) {
4824 if (po_tmp->p_argnum != 0)
4825 po_tmp->p_argnum = argnum;
4826 po_tmp = po_tmp->p_argnext >= 0 ? &ops[po_tmp->p_argnext] : NULL;
4827 }
4828
4829 return argnum;
4830}
4831
89ff3147 4832static int collect_call_args_r(struct parsed_op *po, int i,
8c83cc48 4833 struct parsed_proto *pp, int *regmask, int *arg_grp,
4834 int arg, int argnum, int magic, int need_op_saving, int may_reuse)
e56ab892 4835{
4836 struct parsed_proto *pp_tmp;
3a5101d7 4837 struct parsed_op *po_tmp;
e56ab892 4838 struct label_ref *lr;
2b43685d 4839 int need_to_save_current;
3a5101d7 4840 int arg_grp_current = 0;
4841 int save_args_seen = 0;
e56ab892 4842 int ret = 0;
5f70a34f 4843 int reg;
23fd0b11 4844 char buf[32];
4845 int j, k;
e56ab892 4846
a3684be1 4847 if (i < 0) {
a2c1d768 4848 ferr(po, "dead label encountered\n");
a3684be1 4849 return -1;
4850 }
e56ab892 4851
8c83cc48 4852 for (; arg < pp->argc; arg++, argnum++)
e56ab892 4853 if (pp->arg[arg].reg == NULL)
4854 break;
a3684be1 4855 magic = (magic & 0xffffff) | (arg << 24);
e56ab892 4856
89ff3147 4857 for (j = i; j >= 0 && (arg < pp->argc || pp->is_unresolved); )
e56ab892 4858 {
a3684be1 4859 if (((ops[j].cc_scratch ^ magic) & 0xffffff) == 0) {
4860 if (ops[j].cc_scratch != magic) {
4861 ferr(&ops[j], "arg collect hit same path with diff args for %s\n",
4862 pp->name);
4863 return -1;
4864 }
4865 // ok: have already been here
4866 return 0;
4867 }
4868 ops[j].cc_scratch = magic;
4869
d7857c3a 4870 if (g_labels[j] != NULL && g_label_refs[j].i != -1) {
e56ab892 4871 lr = &g_label_refs[j];
4872 if (lr->next != NULL)
4873 need_op_saving = 1;
a652aa9f 4874 for (; lr->next; lr = lr->next) {
92d715b6 4875 check_i(&ops[j], lr->i);
5c024ef7 4876 if ((ops[lr->i].flags & (OPF_JMP|OPF_CJMP)) != OPF_JMP)
a652aa9f 4877 may_reuse = 1;
8c83cc48 4878 ret = collect_call_args_r(po, lr->i, pp, regmask, arg_grp,
4879 arg, argnum, magic, need_op_saving, may_reuse);
a3684be1 4880 if (ret < 0)
4881 return ret;
a652aa9f 4882 }
e56ab892 4883
92d715b6 4884 check_i(&ops[j], lr->i);
5c024ef7 4885 if ((ops[lr->i].flags & (OPF_JMP|OPF_CJMP)) != OPF_JMP)
a652aa9f 4886 may_reuse = 1;
de50b98b 4887 if (j > 0 && LAST_OP(j - 1)) {
e56ab892 4888 // follow last branch in reverse
4889 j = lr->i;
4890 continue;
4891 }
4892 need_op_saving = 1;
8c83cc48 4893 ret = collect_call_args_r(po, lr->i, pp, regmask, arg_grp,
4894 arg, argnum, magic, need_op_saving, may_reuse);
a3684be1 4895 if (ret < 0)
4896 return ret;
e56ab892 4897 }
4898 j--;
4899
4900 if (ops[j].op == OP_CALL)
4901 {
89ff3147 4902 if (pp->is_unresolved)
4903 break;
4904
092f64e1 4905 pp_tmp = ops[j].pp;
e56ab892 4906 if (pp_tmp == NULL)
7ae48d73 4907 ferr(po, "arg collect hit unparsed call '%s'\n",
4908 ops[j].operand[0].name);
a652aa9f 4909 if (may_reuse && pp_tmp->argc_stack > 0)
de50b98b 4910 ferr(po, "arg collect %d/%d hit '%s' with %d stack args\n",
4911 arg, pp->argc, opr_name(&ops[j], 0), pp_tmp->argc_stack);
e56ab892 4912 }
fdd5548a 4913 // esp adjust of 0 means we collected it before
4914 else if (ops[j].op == OP_ADD && ops[j].operand[0].reg == xSP
4915 && (ops[j].operand[1].type != OPT_CONST
4916 || ops[j].operand[1].val != 0))
4917 {
89ff3147 4918 if (pp->is_unresolved)
4919 break;
4920
2b70f6d3 4921 fnote(po, "(this call)\n");
4922 ferr(&ops[j], "arg collect %d/%d hit esp adjust of %d\n",
fdd5548a 4923 arg, pp->argc, ops[j].operand[1].val);
de50b98b 4924 }
5e49b270 4925 else if (ops[j].op == OP_POP && !(ops[j].flags & OPF_DONE))
9af2d373 4926 {
89ff3147 4927 if (pp->is_unresolved)
4928 break;
4929
2b70f6d3 4930 fnote(po, "(this call)\n");
4931 ferr(&ops[j], "arg collect %d/%d hit pop\n", arg, pp->argc);
de50b98b 4932 }
5c024ef7 4933 else if (ops[j].flags & OPF_CJMP)
de50b98b 4934 {
89ff3147 4935 if (pp->is_unresolved)
4936 break;
4937
a652aa9f 4938 may_reuse = 1;
de50b98b 4939 }
91ca764a 4940 else if (ops[j].op == OP_PUSH
4941 && !(ops[j].flags & (OPF_FARGNR|OPF_DONE)))
e56ab892 4942 {
89ff3147 4943 if (pp->is_unresolved && (ops[j].flags & OPF_RMD))
4944 break;
4945
3a5101d7 4946 ops[j].p_argnext = -1;
4947 po_tmp = pp->arg[arg].datap;
4948 if (po_tmp != NULL)
4949 ops[j].p_argnext = po_tmp - ops;
e56ab892 4950 pp->arg[arg].datap = &ops[j];
3a5101d7 4951
8c83cc48 4952 argnum = sync_argnum(&ops[j], argnum);
4953
2b43685d 4954 need_to_save_current = 0;
5f70a34f 4955 reg = -1;
4956 if (ops[j].operand[0].type == OPT_REG)
4957 reg = ops[j].operand[0].reg;
4958
e56ab892 4959 if (!need_op_saving) {
89ff3147 4960 ret = scan_for_mod(&ops[j], j + 1, i, 1);
2b43685d 4961 need_to_save_current = (ret >= 0);
e56ab892 4962 }
2b43685d 4963 if (need_op_saving || need_to_save_current) {
8c83cc48 4964 // mark this arg as one that needs operand saving
4965 pp->arg[arg].is_saved = 1;
3a5101d7 4966
8c83cc48 4967 if (save_args_seen & (1 << (argnum - 1))) {
3a5101d7 4968 save_args_seen = 0;
4969 arg_grp_current++;
4970 if (arg_grp_current >= MAX_ARG_GRP)
4971 ferr(&ops[j], "out of arg groups (arg%d), f %s\n",
8c83cc48 4972 argnum, pp->name);
3a5101d7 4973 }
e56ab892 4974 }
5f70a34f 4975 else if (ops[j].p_argnum == 0)
e56ab892 4976 ops[j].flags |= OPF_RMD;
4977
a652aa9f 4978 // some PUSHes are reused by different calls on other branches,
de50b98b 4979 // but that can't happen if we didn't branch, so they
4980 // can be removed from future searches (handles nested calls)
a652aa9f 4981 if (!may_reuse)
9af2d373 4982 ops[j].flags |= OPF_FARGNR;
de50b98b 4983
9af2d373 4984 ops[j].flags |= OPF_FARG;
da87ae38 4985 ops[j].flags &= ~OPF_RSAVE;
4986
23fd0b11 4987 // check for __VALIST
04abc5d6 4988 if (!pp->is_unresolved && g_func_pp != NULL
4989 && pp->arg[arg].type.is_va_list)
4990 {
23fd0b11 4991 k = -1;
92d715b6 4992 ret = resolve_origin(j, &ops[j].operand[0],
4993 magic + 1, &k, NULL);
5f70a34f 4994 if (ret == 1 && k >= 0)
23fd0b11 4995 {
5f70a34f 4996 if (ops[k].op == OP_LEA) {
acd03176 4997 if (!g_func_pp->is_vararg)
4998 ferr(&ops[k], "lea <arg> used, but %s is not vararg?\n",
4999 g_func_pp->name);
5000
5f70a34f 5001 snprintf(buf, sizeof(buf), "arg_%X",
5002 g_func_pp->argc_stack * 4);
acd03176 5003 if (strstr(ops[k].operand[1].name, buf)
5004 || strstr(ops[k].operand[1].name, "arglist"))
5f70a34f 5005 {
b2bd20c0 5006 ops[k].flags |= OPF_RMD | OPF_NOREGS | OPF_DONE;
5007 ops[j].flags |= OPF_RMD | OPF_NOREGS | OPF_VAPUSH;
8c83cc48 5008 pp->arg[arg].is_saved = 0;
5f70a34f 5009 reg = -1;
5010 }
5011 else
acd03176 5012 ferr(&ops[k], "va_list arg detection failed\n");
5f70a34f 5013 }
5014 // check for va_list from g_func_pp arg too
5015 else if (ops[k].op == OP_MOV
5016 && is_stack_access(&ops[k], &ops[k].operand[1]))
5017 {
5018 ret = stack_frame_access(&ops[k], &ops[k].operand[1],
5019 buf, sizeof(buf), ops[k].operand[1].name, "", 1, 0);
5020 if (ret >= 0) {
5e49b270 5021 ops[k].flags |= OPF_RMD | OPF_DONE;
5f70a34f 5022 ops[j].flags |= OPF_RMD;
5023 ops[j].p_argpass = ret + 1;
8c83cc48 5024 pp->arg[arg].is_saved = 0;
5f70a34f 5025 reg = -1;
5026 }
5027 }
23fd0b11 5028 }
5029 }
5030
8c83cc48 5031 if (pp->arg[arg].is_saved) {
5032 ops[j].flags &= ~OPF_RMD;
5033 ops[j].p_argnum = argnum;
5034 }
23fd0b11 5035
5036 // tracking reg usage
5f70a34f 5037 if (reg >= 0)
5038 *regmask |= 1 << reg;
23fd0b11 5039
89ff3147 5040 arg++;
8c83cc48 5041 argnum++;
89ff3147 5042 if (!pp->is_unresolved) {
5043 // next arg
8c83cc48 5044 for (; arg < pp->argc; arg++, argnum++)
89ff3147 5045 if (pp->arg[arg].reg == NULL)
5046 break;
5047 }
a3684be1 5048 magic = (magic & 0xffffff) | (arg << 24);
e56ab892 5049 }
3a5101d7 5050
5051 if (ops[j].p_arggrp > arg_grp_current) {
5052 save_args_seen = 0;
5053 arg_grp_current = ops[j].p_arggrp;
5054 }
5055 if (ops[j].p_argnum > 0)
5056 save_args_seen |= 1 << (ops[j].p_argnum - 1);
e56ab892 5057 }
5058
5059 if (arg < pp->argc) {
5060 ferr(po, "arg collect failed for '%s': %d/%d\n",
5061 pp->name, arg, pp->argc);
89ff3147 5062 return -1;
e56ab892 5063 }
89ff3147 5064
3a5101d7 5065 if (arg_grp_current > *arg_grp)
5066 *arg_grp = arg_grp_current;
5067
89ff3147 5068 return arg;
5069}
5070
5071static int collect_call_args(struct parsed_op *po, int i,
8c83cc48 5072 struct parsed_proto *pp, int *regmask, int magic)
89ff3147 5073{
3a5101d7 5074 // arg group is for cases when pushes for
5075 // multiple funcs are going on
5076 struct parsed_op *po_tmp;
3a5101d7 5077 int arg_grp = 0;
89ff3147 5078 int ret;
5079 int a;
5080
8c83cc48 5081 ret = collect_call_args_r(po, i, pp, regmask, &arg_grp,
5082 0, 1, magic, 0, 0);
89ff3147 5083 if (ret < 0)
5084 return ret;
5085
3a5101d7 5086 if (arg_grp != 0) {
5087 // propagate arg_grp
5088 for (a = 0; a < pp->argc; a++) {
5089 if (pp->arg[a].reg != NULL)
5090 continue;
5091
5092 po_tmp = pp->arg[a].datap;
5093 while (po_tmp != NULL) {
5094 po_tmp->p_arggrp = arg_grp;
8c83cc48 5095 po_tmp = po_tmp->p_argnext >= 0 ? &ops[po_tmp->p_argnext] : NULL;
3a5101d7 5096 }
5097 }
5098 }
3a5101d7 5099
89ff3147 5100 if (pp->is_unresolved) {
5101 pp->argc += ret;
5102 pp->argc_stack += ret;
5103 for (a = 0; a < pp->argc; a++)
5104 if (pp->arg[a].type.name == NULL)
5105 pp->arg[a].type.name = strdup("int");
5106 }
5107
e56ab892 5108 return ret;
5109}
5110
b2bd20c0 5111static void reg_use_pass(int i, int opcnt, unsigned char *cbits,
5112 int regmask_now, int *regmask,
5113 int regmask_save_now, int *regmask_save,
5114 int *regmask_init, int regmask_arg)
5115{
5116 struct parsed_op *po;
5117 int already_saved;
5118 int regmask_new;
5119 int regmask_op;
5120 int flags_set;
5121 int ret, reg;
5122 int j;
5123
5124 for (; i < opcnt; i++)
5125 {
5126 po = &ops[i];
5127 if (cbits[i >> 3] & (1 << (i & 7)))
5128 return;
5129 cbits[i >> 3] |= (1 << (i & 7));
5130
5131 if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
5132 if (po->flags & (OPF_RMD|OPF_DONE))
5133 continue;
5134 if (po->btj != NULL) {
5135 for (j = 0; j < po->btj->count; j++) {
5136 check_i(po, po->btj->d[j].bt_i);
5137 reg_use_pass(po->btj->d[j].bt_i, opcnt, cbits,
5138 regmask_now, regmask, regmask_save_now, regmask_save,
5139 regmask_init, regmask_arg);
5140 }
5141 return;
5142 }
5143
5144 check_i(po, po->bt_i);
5145 if (po->flags & OPF_CJMP)
5146 reg_use_pass(po->bt_i, opcnt, cbits,
5147 regmask_now, regmask, regmask_save_now, regmask_save,
5148 regmask_init, regmask_arg);
5149 else
5150 i = po->bt_i - 1;
5151 continue;
5152 }
5153
5154 if (po->op == OP_PUSH && !(po->flags & (OPF_FARG|OPF_DONE))
5155 && !g_func_pp->is_userstack
5156 && po->operand[0].type == OPT_REG)
5157 {
5158 reg = po->operand[0].reg;
5159 ferr_assert(po, reg >= 0);
5160
5161 already_saved = 0;
5162 flags_set = OPF_RSAVE | OPF_RMD | OPF_DONE;
5163 if (regmask_now & (1 << reg)) {
5164 already_saved = regmask_save_now & (1 << reg);
5165 flags_set = OPF_RSAVE | OPF_DONE;
5166 }
5167
93b5bd18 5168 ret = scan_for_pop(i + 1, opcnt, i + opcnt * 3, reg, 0, 0, 0);
b2bd20c0 5169 if (ret == 1) {
93b5bd18 5170 scan_for_pop(i + 1, opcnt, i + opcnt * 4,
5171 reg, 0, 0, flags_set);
b2bd20c0 5172 }
5173 else {
5174 ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, 0);
5175 if (ret == 1) {
5176 scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg,
5177 flags_set);
5178 }
5179 }
5180 if (ret == 1) {
5181 ferr_assert(po, !already_saved);
5182 po->flags |= flags_set;
5183
5184 if (regmask_now & (1 << reg)) {
5185 regmask_save_now |= (1 << reg);
5186 *regmask_save |= regmask_save_now;
5187 }
5188 continue;
5189 }
5190 }
5191 else if (po->op == OP_POP && (po->flags & OPF_RSAVE)) {
5192 reg = po->operand[0].reg;
5193 ferr_assert(po, reg >= 0);
5194
5195 if (regmask_save_now & (1 << reg))
5196 regmask_save_now &= ~(1 << reg);
5197 else
5198 regmask_now &= ~(1 << reg);
5199 continue;
5200 }
5201 else if (po->op == OP_CALL) {
5202 if ((po->regmask_dst & (1 << xAX))
5203 && !(po->regmask_dst & (1 << xDX)))
5204 {
5205 if (po->flags & OPF_TAIL)
5206 // don't need eax, will do "return f();" or "f(); return;"
5207 po->regmask_dst &= ~(1 << xAX);
5208 else {
5209 struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xAX);
5210 j = -1;
5211 find_next_read(i + 1, opcnt, &opr, i + opcnt * 17, &j);
5212 if (j == -1)
5213 // not used
5214 po->regmask_dst &= ~(1 << xAX);
5215 }
5216 }
fe18df39 5217
5218 // not "full stack" mode and have something in stack
5219 if (!(regmask_now & mxST7_2) && (regmask_now & mxST1_0))
5220 ferr(po, "float stack is not empty on func call\n");
b2bd20c0 5221 }
5222
5223 if (po->flags & OPF_NOREGS)
5224 continue;
5225
acd03176 5226 // if incomplete register is used, clear it on init to avoid
5227 // later use of uninitialized upper part in some situations
5228 if ((po->flags & OPF_DATA) && po->operand[0].type == OPT_REG
5229 && po->operand[0].lmod != OPLM_DWORD)
5230 {
5231 reg = po->operand[0].reg;
5232 ferr_assert(po, reg >= 0);
5233
5234 if (!(regmask_now & (1 << reg)))
5235 *regmask_init |= 1 << reg;
5236 }
5237
b2bd20c0 5238 regmask_op = po->regmask_src | po->regmask_dst;
5239
5240 regmask_new = po->regmask_src & ~regmask_now & ~regmask_arg;
5241 regmask_new &= ~(1 << xSP);
5242 if (g_bp_frame && !(po->flags & OPF_EBP_S))
5243 regmask_new &= ~(1 << xBP);
5244
b2bd20c0 5245 if (regmask_new != 0)
5246 fnote(po, "uninitialized reg mask: %x\n", regmask_new);
5247
5248 if (regmask_op & (1 << xBP)) {
5249 if (g_bp_frame && !(po->flags & OPF_EBP_S)) {
5250 if (po->regmask_dst & (1 << xBP))
5251 // compiler decided to drop bp frame and use ebp as scratch
5252 scan_fwd_set_flags(i + 1, opcnt, i + opcnt * 5, OPF_EBP_S);
5253 else
5254 regmask_op &= ~(1 << xBP);
5255 }
5256 }
5257
622eb2ef 5258 if (po->flags & OPF_FPUSH) {
5259 if (regmask_now & mxST1)
5260 regmask_now |= mxSTa; // switch to "full stack" mode
5261 if (regmask_now & mxSTa)
5262 po->flags |= OPF_FSHIFT;
5263 if (!(regmask_now & mxST7_2)) {
5264 regmask_now =
5265 (regmask_now & ~mxST1_0) | ((regmask_now & mxST0) << 1);
5266 }
5267 }
5268
b2bd20c0 5269 regmask_now |= regmask_op;
5270 *regmask |= regmask_now;
5271
d4a985bd 5272 // released regs
5273 if (po->flags & OPF_FPOP) {
fe18df39 5274 if ((regmask_now & mxSTa) == 0)
d4a985bd 5275 ferr(po, "float pop on empty stack?\n");
fe18df39 5276 if (regmask_now & (mxST7_2 | mxST1))
d4a985bd 5277 po->flags |= OPF_FSHIFT;
fe18df39 5278 if (!(regmask_now & mxST7_2)) {
5279 regmask_now =
5280 (regmask_now & ~mxST1_0) | ((regmask_now & mxST1) >> 1);
5281 }
d4a985bd 5282 }
5283
5284 if (po->flags & OPF_TAIL) {
16057ce1 5285 if (!(regmask_now & mxST7_2)) {
5286 if (get_pp_arg_regmask_dst(g_func_pp) & mxST0) {
5287 if (!(regmask_now & mxST0))
5288 ferr(po, "no st0 on float return, mask: %x\n",
5289 regmask_now);
5290 }
5291 else if (regmask_now & mxST1_0)
5292 ferr(po, "float regs on tail: %x\n", regmask_now);
5293 }
4d247254 5294
5295 // there is support for "conditional tailcall", sort of
5296 if (!(po->flags & OPF_CC))
5297 return;
d4a985bd 5298 }
b2bd20c0 5299 }
5300}
5301
89ff3147 5302static void pp_insert_reg_arg(struct parsed_proto *pp, const char *reg)
5303{
5304 int i;
5305
5306 for (i = 0; i < pp->argc; i++)
5307 if (pp->arg[i].reg == NULL)
5308 break;
5309
5310 if (pp->argc_stack)
5311 memmove(&pp->arg[i + 1], &pp->arg[i],
5312 sizeof(pp->arg[0]) * pp->argc_stack);
5313 memset(&pp->arg[i], 0, sizeof(pp->arg[i]));
5314 pp->arg[i].reg = strdup(reg);
5315 pp->arg[i].type.name = strdup("int");
5316 pp->argc++;
5317 pp->argc_reg++;
5318}
5319
04f8a628 5320static void output_std_flags(FILE *fout, struct parsed_op *po,
5321 int *pfomask, const char *dst_opr_text)
5322{
5323 if (*pfomask & (1 << PFO_Z)) {
5324 fprintf(fout, "\n cond_z = (%s%s == 0);",
5325 lmod_cast_u(po, po->operand[0].lmod), dst_opr_text);
5326 *pfomask &= ~(1 << PFO_Z);
5327 }
5328 if (*pfomask & (1 << PFO_S)) {
5329 fprintf(fout, "\n cond_s = (%s%s < 0);",
5330 lmod_cast_s(po, po->operand[0].lmod), dst_opr_text);
5331 *pfomask &= ~(1 << PFO_S);
5332 }
5333}
5334
c0de9015 5335enum {
5336 OPP_FORCE_NORETURN = (1 << 0),
5337 OPP_SIMPLE_ARGS = (1 << 1),
5338 OPP_ALIGN = (1 << 2),
5339};
5340
c0050df6 5341static void output_pp_attrs(FILE *fout, const struct parsed_proto *pp,
c0de9015 5342 int flags)
c0050df6 5343{
c0de9015 5344 const char *cconv = "";
5345
c0050df6 5346 if (pp->is_fastcall)
c0de9015 5347 cconv = "__fastcall ";
c0050df6 5348 else if (pp->is_stdcall && pp->argc_reg == 0)
c0de9015 5349 cconv = "__stdcall ";
5350
5351 fprintf(fout, (flags & OPP_ALIGN) ? "%-16s" : "%s", cconv);
5352
5353 if (pp->is_noreturn || (flags & OPP_FORCE_NORETURN))
c0050df6 5354 fprintf(fout, "noreturn ");
5355}
5356
c0de9015 5357static void output_pp(FILE *fout, const struct parsed_proto *pp,
5358 int flags)
5359{
5360 int i;
5361
5362 fprintf(fout, (flags & OPP_ALIGN) ? "%-5s" : "%s ",
5363 pp->ret_type.name);
5364 if (pp->is_fptr)
5365 fprintf(fout, "(");
5366 output_pp_attrs(fout, pp, flags);
5367 if (pp->is_fptr)
5368 fprintf(fout, "*");
5369 fprintf(fout, "%s", pp->name);
5370 if (pp->is_fptr)
5371 fprintf(fout, ")");
5372
5373 fprintf(fout, "(");
5374 for (i = 0; i < pp->argc; i++) {
5375 if (i > 0)
5376 fprintf(fout, ", ");
93b5bd18 5377 if (pp->arg[i].pp != NULL && pp->arg[i].pp->is_func
5378 && !(flags & OPP_SIMPLE_ARGS))
5379 {
c0de9015 5380 // func pointer
93b5bd18 5381 output_pp(fout, pp->arg[i].pp, 0);
c0de9015 5382 }
5383 else if (pp->arg[i].type.is_retreg) {
5384 fprintf(fout, "u32 *r_%s", pp->arg[i].reg);
5385 }
5386 else {
5387 fprintf(fout, "%s", pp->arg[i].type.name);
5388 if (!pp->is_fptr)
5389 fprintf(fout, " a%d", i + 1);
5390 }
5391 }
5392 if (pp->is_vararg) {
5393 if (i > 0)
5394 fprintf(fout, ", ");
5395 fprintf(fout, "...");
5396 }
5397 fprintf(fout, ")");
5398}
5399
3a5101d7 5400static char *saved_arg_name(char *buf, size_t buf_size, int grp, int num)
5401{
5402 char buf1[16];
5403
5404 buf1[0] = 0;
5405 if (grp > 0)
5406 snprintf(buf1, sizeof(buf1), "%d", grp);
5407 snprintf(buf, buf_size, "s%s_a%d", buf1, num);
5408
5409 return buf;
5410}
5411
9af2d373 5412static void gen_x_cleanup(int opcnt);
5413
91977a1c 5414static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
5415{
69a3cdfc 5416 struct parsed_op *po, *delayed_flag_op = NULL, *tmp_op;
850c9265 5417 struct parsed_opr *last_arith_dst = NULL;
3ebea2cf 5418 char buf1[256], buf2[256], buf3[256], cast[64];
ddaf8bd7 5419 struct parsed_proto *pp, *pp_tmp;
4c45fa73 5420 struct parsed_data *pd;
3a5101d7 5421 int save_arg_vars[MAX_ARG_GRP] = { 0, };
b2bd20c0 5422 unsigned char cbits[MAX_OPS / 8];
497a6d6b 5423 const char *float_type;
fe18df39 5424 const char *float_st0;
5425 const char *float_st1;
5426 int need_float_stack = 0;
16057ce1 5427 int need_float_sw = 0; // status word
108e9fe3 5428 int need_tmp_var = 0;
2fe80fdb 5429 int need_tmp64 = 0;
fe18df39 5430 int cond_vars = 0;
91977a1c 5431 int had_decl = 0;
3ebea2cf 5432 int label_pending = 0;
497a6d6b 5433 int need_double = 0;
16057ce1 5434 int regmask_save = 0; // used regs saved/restored in this func
b2bd20c0 5435 int regmask_arg; // regs from this function args (fastcall, etc)
5436 int regmask_ret; // regs needed on ret
25a330eb 5437 int regmask_now; // temp
5438 int regmask_init = 0; // regs that need zero initialization
5439 int regmask_pp = 0; // regs used in complex push-pop graph
30620174 5440 int regmask_ffca = 0; // float function call args
25a330eb 5441 int regmask = 0; // used regs
940e8e66 5442 int pfomask = 0;
64c59faf 5443 int found = 0;
fe18df39 5444 int dead_dst;
91977a1c 5445 int no_output;
4c45fa73 5446 int i, j, l;
91977a1c 5447 int arg;
91977a1c 5448 int reg;
5449 int ret;
5450
1bafb621 5451 g_bp_frame = g_sp_frame = g_stack_fsz = 0;
a2c1d768 5452 g_stack_frame_used = 0;
226e8df1 5453 if (g_sct_func_attr & SCTFA_CLEAR_REGS)
5454 regmask_init = g_regmask_init;
91977a1c 5455
36595fd2 5456 g_func_pp = proto_parse(fhdr, funcn, 0);
bd96f656 5457 if (g_func_pp == NULL)
91977a1c 5458 ferr(ops, "proto_parse failed for '%s'\n", funcn);
5459
b2bd20c0 5460 regmask_arg = get_pp_arg_regmask_src(g_func_pp);
5461 regmask_ret = get_pp_arg_regmask_dst(g_func_pp);
5462
91977a1c 5463 // pass1:
87bf6cec 5464 // - resolve all branches
66bdb2b0 5465 // - parse calls with labels
5466 resolve_branches_parse_calls(opcnt);
840257f6 5467
66bdb2b0 5468 // pass2:
5469 // - handle ebp/esp frame, remove ops related to it
5470 scan_prologue_epilogue(opcnt);
87bf6cec 5471
5472 // pass3:
a2c1d768 5473 // - remove dead labels
b2bd20c0 5474 // - set regs needed at ret
1bafb621 5475 for (i = 0; i < opcnt; i++)
5476 {
d7857c3a 5477 if (g_labels[i] != NULL && g_label_refs[i].i == -1) {
5478 free(g_labels[i]);
5479 g_labels[i] = NULL;
5480 }
b2bd20c0 5481
5482 if (ops[i].op == OP_RET)
5483 ops[i].regmask_src |= regmask_ret;
66bdb2b0 5484 }
a2c1d768 5485
66bdb2b0 5486 // pass4:
5487 // - process trivial calls
5488 for (i = 0; i < opcnt; i++)
5489 {
69a3cdfc 5490 po = &ops[i];
5e49b270 5491 if (po->flags & (OPF_RMD|OPF_DONE))
91977a1c 5492 continue;
850c9265 5493
d4e3b5db 5494 if (po->op == OP_CALL)
26677139 5495 {
5496 pp = process_call_early(i, opcnt, &j);
5497 if (pp != NULL) {
30620174 5498 if (!(po->flags & OPF_ATAIL)) {
26677139 5499 // since we know the args, try to collect them
30620174 5500 ret = collect_call_args_early(i, pp, &regmask, &regmask_ffca);
5501 if (ret != 0)
26677139 5502 pp = NULL;
30620174 5503 }
26677139 5504 }
5505
5506 if (pp != NULL) {
5507 if (j >= 0) {
5508 // commit esp adjust
5e49b270 5509 if (ops[j].op != OP_POP)
5510 patch_esp_adjust(&ops[j], pp->argc_stack * 4);
bfacdc83 5511 else {
5512 for (l = 0; l < pp->argc_stack; l++)
b2bd20c0 5513 ops[j + l].flags |= OPF_DONE | OPF_RMD | OPF_NOREGS;
bfacdc83 5514 }
26677139 5515 }
5516
5517 if (strstr(pp->ret_type.name, "int64"))
5518 need_tmp64 = 1;
5519
5520 po->flags |= OPF_DONE;
5521 }
5522 }
5523 }
5524
66bdb2b0 5525 // pass5:
b2bd20c0 5526 // - process calls, stage 2
5527 // - handle some push/pop pairs
5528 // - scan for STD/CLD, propagate DF
16057ce1 5529 // - try to resolve needed x87 status word bits
26677139 5530 for (i = 0; i < opcnt; i++)
5531 {
16057ce1 5532 int mask, z_check;
5533
26677139 5534 po = &ops[i];
b2bd20c0 5535 if (po->flags & OPF_RMD)
26677139 5536 continue;
5537
b2bd20c0 5538 if (po->op == OP_CALL)
69a3cdfc 5539 {
b2bd20c0 5540 if (!(po->flags & OPF_DONE)) {
5541 pp = process_call(i, opcnt);
91977a1c 5542
b2bd20c0 5543 if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) {
5544 // since we know the args, collect them
8c83cc48 5545 collect_call_args(po, i, pp, &regmask, i + opcnt * 2);
b2bd20c0 5546 }
5547 // for unresolved, collect after other passes
89ff3147 5548 }
2b43685d 5549
b2bd20c0 5550 pp = po->pp;
5551 ferr_assert(po, pp != NULL);
5552
5553 po->regmask_src |= get_pp_arg_regmask_src(pp);
5554 po->regmask_dst |= get_pp_arg_regmask_dst(pp);
5555
d4a985bd 5556 if (po->regmask_dst & mxST0)
5557 po->flags |= OPF_FPUSH;
5558
2b43685d 5559 if (strstr(pp->ret_type.name, "int64"))
2fe80fdb 5560 need_tmp64 = 1;
b2bd20c0 5561
5562 continue;
91977a1c 5563 }
b2bd20c0 5564
5565 if (po->flags & OPF_DONE)
5566 continue;
5567
16057ce1 5568 switch (po->op) {
5569 case OP_PUSH:
5570 if (!(po->flags & OPF_FARG) && !(po->flags & OPF_RSAVE)
5571 && po->operand[0].type == OPT_CONST)
5572 {
5573 scan_for_pop_const(i, opcnt, i + opcnt * 12);
5574 }
5575 break;
5576
5577 case OP_POP:
e83ea7ed 5578 scan_pushes_for_pop(i, opcnt, &regmask_pp);
16057ce1 5579 break;
5580
5581 case OP_STD:
b2bd20c0 5582 po->flags |= OPF_DF | OPF_RMD | OPF_DONE;
5583 scan_propagate_df(i + 1, opcnt);
16057ce1 5584 break;
5585
5586 case OP_FNSTSW:
5587 need_float_sw = 1;
5588 if (po->operand[0].type != OPT_REG || po->operand[0].reg != xAX)
5589 ferr(po, "TODO: fnstsw to mem\n");
5590 ret = resolve_used_bits(i + 1, opcnt, xAX, &mask, &z_check);
5591 if (ret != 0)
5592 ferr(po, "fnstsw resolve failed\n");
5593 ret = adjust_prev_op(i, OP_FCOM, i + opcnt * 21,
5594 (void *)(long)(mask | (z_check << 16)));
5595 if (ret != 1)
5596 ferr(po, "failed to find fcom: %d\n", ret);
5597 break;
5598
5599 default:
5600 break;
b2bd20c0 5601 }
d4e3b5db 5602 }
5603
66bdb2b0 5604 // pass6:
d4e3b5db 5605 // - find POPs for PUSHes, rm both
5606 // - scan for all used registers
b2bd20c0 5607 memset(cbits, 0, sizeof(cbits));
226e8df1 5608 reg_use_pass(0, opcnt, cbits, regmask_init, &regmask,
b2bd20c0 5609 0, &regmask_save, &regmask_init, regmask_arg);
5610
11437ea1 5611 need_float_stack = !!(regmask & mxST7_2);
5612
b2bd20c0 5613 // pass7:
d4e3b5db 5614 // - find flag set ops for their users
b2bd20c0 5615 // - do unresolved calls
1bafb621 5616 // - declare indirect functions
16057ce1 5617 // - other op specific processing
26677139 5618 for (i = 0; i < opcnt; i++)
5619 {
d4e3b5db 5620 po = &ops[i];
5e49b270 5621 if (po->flags & (OPF_RMD|OPF_DONE))
d4e3b5db 5622 continue;
5623
d4e3b5db 5624 if (po->flags & OPF_CC)
5625 {
2b43685d 5626 int setters[16], cnt = 0, branched = 0;
5627
7f20f633 5628 ret = scan_for_flag_set(i, opcnt, i + opcnt * 6,
04f8a628 5629 &branched, setters, &cnt);
2b43685d 5630 if (ret < 0 || cnt <= 0)
5631 ferr(po, "unable to trace flag setter(s)\n");
5632 if (cnt > ARRAY_SIZE(setters))
5633 ferr(po, "too many flag setters\n");
d4e3b5db 5634
2b43685d 5635 for (j = 0; j < cnt; j++)
5636 {
5637 tmp_op = &ops[setters[j]]; // flag setter
5638 pfomask = 0;
5639
5640 // to get nicer code, we try to delay test and cmp;
5641 // if we can't because of operand modification, or if we
591721d7 5642 // have arith op, or branch, make it calculate flags explicitly
5643 if (tmp_op->op == OP_TEST || tmp_op->op == OP_CMP)
5644 {
89ff3147 5645 if (branched || scan_for_mod(tmp_op, setters[j] + 1, i, 0) >= 0)
092f64e1 5646 pfomask = 1 << po->pfo;
2b43685d 5647 }
4741fdfe 5648 else if (tmp_op->op == OP_CMPS || tmp_op->op == OP_SCAS) {
092f64e1 5649 pfomask = 1 << po->pfo;
591721d7 5650 }
2b43685d 5651 else {
04f8a628 5652 // see if we'll be able to handle based on op result
5653 if ((tmp_op->op != OP_AND && tmp_op->op != OP_OR
092f64e1 5654 && po->pfo != PFO_Z && po->pfo != PFO_S
5655 && po->pfo != PFO_P)
04f8a628 5656 || branched
2b43685d 5657 || scan_for_mod_opr0(tmp_op, setters[j] + 1, i) >= 0)
092f64e1 5658 {
5659 pfomask = 1 << po->pfo;
5660 }
2fe80fdb 5661
c8dbc5be 5662 if (tmp_op->op == OP_ADD && po->pfo == PFO_C) {
5663 propagate_lmod(tmp_op, &tmp_op->operand[0],
5664 &tmp_op->operand[1]);
5665 if (tmp_op->operand[0].lmod == OPLM_DWORD)
5666 need_tmp64 = 1;
5667 }
2b43685d 5668 }
5669 if (pfomask) {
5670 tmp_op->pfomask |= pfomask;
cb090db0 5671 cond_vars |= pfomask;
2b43685d 5672 }
04f8a628 5673 // note: may overwrite, currently not a problem
5674 po->datap = tmp_op;
d4e3b5db 5675 }
5676
cb090db0 5677 if (po->op == OP_RCL || po->op == OP_RCR
5678 || po->op == OP_ADC || po->op == OP_SBB)
5679 cond_vars |= 1 << PFO_C;
d4e3b5db 5680 }
092f64e1 5681
622eb2ef 5682 switch (po->op) {
5683 case OP_CMPS:
5684 case OP_SCAS:
cb090db0 5685 cond_vars |= 1 << PFO_Z;
622eb2ef 5686 break;
5687
5688 case OP_MUL:
c8dbc5be 5689 if (po->operand[0].lmod == OPLM_DWORD)
5690 need_tmp64 = 1;
622eb2ef 5691 break;
5692
5693 case OP_IMUL:
5694 if (po->operand_cnt == 1 && po->operand[0].lmod == OPLM_DWORD)
5695 need_tmp64 = 1;
5696 break;
5697
5698 case OP_CALL:
26677139 5699 // note: resolved non-reg calls are OPF_DONE already
092f64e1 5700 pp = po->pp;
b2bd20c0 5701 ferr_assert(po, pp != NULL);
89ff3147 5702
5703 if (pp->is_unresolved) {
ddaf8bd7 5704 int regmask_stack = 0;
8c83cc48 5705 collect_call_args(po, i, pp, &regmask, i + opcnt * 2);
89ff3147 5706
b74c31e3 5707 // this is pretty rough guess:
5708 // see ecx and edx were pushed (and not their saved versions)
5709 for (arg = 0; arg < pp->argc; arg++) {
8c83cc48 5710 if (pp->arg[arg].reg != NULL && !pp->arg[arg].is_saved)
b74c31e3 5711 continue;
5712
5713 tmp_op = pp->arg[arg].datap;
5714 if (tmp_op == NULL)
5715 ferr(po, "parsed_op missing for arg%d\n", arg);
8c83cc48 5716 if (tmp_op->operand[0].type == OPT_REG)
b74c31e3 5717 regmask_stack |= 1 << tmp_op->operand[0].reg;
5718 }
5719
ddaf8bd7 5720 if (!((regmask_stack & (1 << xCX))
5721 && (regmask_stack & (1 << xDX))))
89ff3147 5722 {
5723 if (pp->argc_stack != 0
c0050df6 5724 || ((regmask | regmask_arg) & ((1 << xCX)|(1 << xDX))))
89ff3147 5725 {
5726 pp_insert_reg_arg(pp, "ecx");
c0050df6 5727 pp->is_fastcall = 1;
ddaf8bd7 5728 regmask_init |= 1 << xCX;
89ff3147 5729 regmask |= 1 << xCX;
5730 }
5731 if (pp->argc_stack != 0
5732 || ((regmask | regmask_arg) & (1 << xDX)))
5733 {
5734 pp_insert_reg_arg(pp, "edx");
ddaf8bd7 5735 regmask_init |= 1 << xDX;
89ff3147 5736 regmask |= 1 << xDX;
5737 }
5738 }
c0050df6 5739
5740 // note: __cdecl doesn't fall into is_unresolved category
5741 if (pp->argc_stack > 0)
5742 pp->is_stdcall = 1;
ddaf8bd7 5743 }
622eb2ef 5744 break;
5745
5746 case OP_MOV:
5747 if (po->operand[0].pp != NULL && po->operand[1].pp != NULL)
27ebfaed 5748 {
622eb2ef 5749 // <var> = offset <something>
5750 if ((po->operand[1].pp->is_func || po->operand[1].pp->is_fptr)
5751 && !IS_START(po->operand[1].name, "off_"))
5752 {
5753 if (!po->operand[0].pp->is_fptr)
5754 ferr(po, "%s not declared as fptr when it should be\n",
5755 po->operand[0].name);
5756 if (pp_cmp_func(po->operand[0].pp, po->operand[1].pp)) {
5757 pp_print(buf1, sizeof(buf1), po->operand[0].pp);
5758 pp_print(buf2, sizeof(buf2), po->operand[1].pp);
5759 fnote(po, "var: %s\n", buf1);
5760 fnote(po, "func: %s\n", buf2);
5761 ferr(po, "^ mismatch\n");
5762 }
27ebfaed 5763 }
5764 }
622eb2ef 5765 break;
5766
5767 case OP_DIV:
5768 case OP_IDIV:
acd03176 5769 if (po->operand[0].lmod == OPLM_DWORD) {
5770 // 32bit division is common, look for it
5771 if (po->op == OP_DIV)
5772 ret = scan_for_reg_clear(i, xDX);
5773 else
5774 ret = scan_for_cdq_edx(i);
5775 if (ret >= 0)
5776 po->flags |= OPF_32BIT;
5777 else
5778 need_tmp64 = 1;
5779 }
cb090db0 5780 else
acd03176 5781 need_tmp_var = 1;
622eb2ef 5782 break;
5783
5784 case OP_CLD:
5e49b270 5785 po->flags |= OPF_RMD | OPF_DONE;
622eb2ef 5786 break;
5787
5788 case OP_RCL:
5789 case OP_RCR:
5790 case OP_XCHG:
5791 need_tmp_var = 1;
5792 break;
5793
5794 case OP_FLD:
5795 if (po->operand[0].lmod == OPLM_QWORD)
5796 need_double = 1;
5797 break;
5798
5799 case OPP_ALLSHL:
5800 case OPP_ALLSHR:
5801 need_tmp64 = 1;
5802 break;
5803
5804 case OPP_FTOL: {
d4a985bd 5805 struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xDX);
5806 j = -1;
5807 find_next_read(i + 1, opcnt, &opr, i + opcnt * 18, &j);
5808 if (j == -1)
5809 po->flags |= OPF_32BIT;
622eb2ef 5810 break;
d4a985bd 5811 }
cb090db0 5812
622eb2ef 5813 default:
5814 break;
5815 }
8c83cc48 5816
5817 // this might need it's own pass...
5818 if (po->op != OP_FST && po->p_argnum > 0)
5819 save_arg_vars[po->p_arggrp] |= 1 << (po->p_argnum - 1);
11437ea1 5820
5821 // correct for "full stack" mode late enable
5822 if ((po->flags & (OPF_PPUSH|OPF_FPOP)) && need_float_stack)
5823 po->flags |= OPF_FSHIFT;
91977a1c 5824 }
5825
497a6d6b 5826 float_type = need_double ? "double" : "float";
fe18df39 5827 float_st0 = need_float_stack ? "f_st[f_stp & 7]" : "f_st0";
5828 float_st1 = need_float_stack ? "f_st[(f_stp + 1) & 7]" : "f_st1";
497a6d6b 5829
60fe410c 5830 // output starts here
5831
5832 // define userstack size
5833 if (g_func_pp->is_userstack) {
5834 fprintf(fout, "#ifndef US_SZ_%s\n", g_func_pp->name);
5835 fprintf(fout, "#define US_SZ_%s USERSTACK_SIZE\n", g_func_pp->name);
5836 fprintf(fout, "#endif\n");
5837 }
5838
5839 // the function itself
c0de9015 5840 ferr_assert(ops, !g_func_pp->is_fptr);
5841 output_pp(fout, g_func_pp,
5842 (g_ida_func_attr & IDAFA_NORETURN) ? OPP_FORCE_NORETURN : 0);
5843 fprintf(fout, "\n{\n");
60fe410c 5844
5845 // declare indirect functions
5846 for (i = 0; i < opcnt; i++) {
5847 po = &ops[i];
5848 if (po->flags & OPF_RMD)
5849 continue;
5850
5851 if (po->op == OP_CALL) {
5852 pp = po->pp;
5853 if (pp == NULL)
5854 ferr(po, "NULL pp\n");
5855
5856 if (pp->is_fptr && !(pp->name[0] != 0 && pp->is_arg)) {
5857 if (pp->name[0] != 0) {
5858 memmove(pp->name + 2, pp->name, strlen(pp->name) + 1);
5859 memcpy(pp->name, "i_", 2);
5860
5861 // might be declared already
5862 found = 0;
5863 for (j = 0; j < i; j++) {
5864 if (ops[j].op == OP_CALL && (pp_tmp = ops[j].pp)) {
5865 if (pp_tmp->is_fptr && IS(pp->name, pp_tmp->name)) {
5866 found = 1;
5867 break;
5868 }
5869 }
5870 }
5871 if (found)
5872 continue;
5873 }
5874 else
5875 snprintf(pp->name, sizeof(pp->name), "icall%d", i);
5876
c0de9015 5877 fprintf(fout, " ");
5878 output_pp(fout, pp, OPP_SIMPLE_ARGS);
5879 fprintf(fout, ";\n");
60fe410c 5880 }
5881 }
5882 }
da87ae38 5883
4c45fa73 5884 // output LUTs/jumptables
5885 for (i = 0; i < g_func_pd_cnt; i++) {
5886 pd = &g_func_pd[i];
5887 fprintf(fout, " static const ");
5888 if (pd->type == OPT_OFFSET) {
5889 fprintf(fout, "void *jt_%s[] =\n { ", pd->label);
5890
5891 for (j = 0; j < pd->count; j++) {
5892 if (j > 0)
5893 fprintf(fout, ", ");
5894 fprintf(fout, "&&%s", pd->d[j].u.label);
5895 }
5896 }
5897 else {
5898 fprintf(fout, "%s %s[] =\n { ",
5899 lmod_type_u(ops, pd->lmod), pd->label);
5900
5901 for (j = 0; j < pd->count; j++) {
5902 if (j > 0)
5903 fprintf(fout, ", ");
5904 fprintf(fout, "%u", pd->d[j].u.val);
5905 }
5906 }
5907 fprintf(fout, " };\n");
1f84f6b3 5908 had_decl = 1;
4c45fa73 5909 }
5910
4f12f671 5911 // declare stack frame, va_arg
1f84f6b3 5912 if (g_stack_fsz) {
d4a985bd 5913 fprintf(fout, " union { u32 d[%d];", (g_stack_fsz + 3) / 4);
5914 if (g_func_lmods & (1 << OPLM_WORD))
5915 fprintf(fout, " u16 w[%d];", (g_stack_fsz + 1) / 2);
5916 if (g_func_lmods & (1 << OPLM_BYTE))
5917 fprintf(fout, " u8 b[%d];", g_stack_fsz);
5918 if (g_func_lmods & (1 << OPLM_QWORD))
5919 fprintf(fout, " double q[%d];", (g_stack_fsz + 7) / 8);
5920 fprintf(fout, " } sf;\n");
1f84f6b3 5921 had_decl = 1;
5922 }
5923
5924 if (g_func_pp->is_userstack) {
60fe410c 5925 fprintf(fout, " u32 fake_sf[US_SZ_%s / 4];\n", g_func_pp->name);
5926 fprintf(fout, " u32 *esp = &fake_sf[sizeof(fake_sf) / 4];\n");
1f84f6b3 5927 had_decl = 1;
5928 }
850c9265 5929
1f84f6b3 5930 if (g_func_pp->is_vararg) {
4f12f671 5931 fprintf(fout, " va_list ap;\n");
1f84f6b3 5932 had_decl = 1;
5933 }
4f12f671 5934
940e8e66 5935 // declare arg-registers
bd96f656 5936 for (i = 0; i < g_func_pp->argc; i++) {
5937 if (g_func_pp->arg[i].reg != NULL) {
91977a1c 5938 reg = char_array_i(regs_r32,
bd96f656 5939 ARRAY_SIZE(regs_r32), g_func_pp->arg[i].reg);
75ad0378 5940 if (regmask & (1 << reg)) {
1f84f6b3 5941 if (g_func_pp->arg[i].type.is_retreg)
5942 fprintf(fout, " u32 %s = *r_%s;\n",
5943 g_func_pp->arg[i].reg, g_func_pp->arg[i].reg);
5944 else
5945 fprintf(fout, " u32 %s = (u32)a%d;\n",
5946 g_func_pp->arg[i].reg, i + 1);
75ad0378 5947 }
1f84f6b3 5948 else {
5949 if (g_func_pp->arg[i].type.is_retreg)
5950 ferr(ops, "retreg '%s' is unused?\n",
5951 g_func_pp->arg[i].reg);
75ad0378 5952 fprintf(fout, " // %s = a%d; // unused\n",
5953 g_func_pp->arg[i].reg, i + 1);
1f84f6b3 5954 }
91977a1c 5955 had_decl = 1;
5956 }
5957 }
5958
25a330eb 5959 // declare normal registers
75ad0378 5960 regmask_now = regmask & ~regmask_arg;
5961 regmask_now &= ~(1 << xSP);
90307a99 5962 if (regmask_now & 0x00ff) {
91977a1c 5963 for (reg = 0; reg < 8; reg++) {
75ad0378 5964 if (regmask_now & (1 << reg)) {
ddaf8bd7 5965 fprintf(fout, " u32 %s", regs_r32[reg]);
5966 if (regmask_init & (1 << reg))
5967 fprintf(fout, " = 0");
5968 fprintf(fout, ";\n");
91977a1c 5969 had_decl = 1;
5970 }
5971 }
5972 }
d4a985bd 5973 // ... mmx
90307a99 5974 if (regmask_now & 0xff00) {
5975 for (reg = 8; reg < 16; reg++) {
5976 if (regmask_now & (1 << reg)) {
5977 fprintf(fout, " mmxr %s", regs_r32[reg]);
5978 if (regmask_init & (1 << reg))
5979 fprintf(fout, " = { 0, }");
5980 fprintf(fout, ";\n");
5981 had_decl = 1;
5982 }
5983 }
5984 }
d4a985bd 5985 // ... x87
fe18df39 5986 if (need_float_stack) {
5987 fprintf(fout, " %s f_st[8];\n", float_type);
5988 fprintf(fout, " int f_stp = 0;\n");
5989 had_decl = 1;
5990 }
5991 else {
5992 if (regmask_now & 0xff0000) {
5993 for (reg = 16; reg < 24; reg++) {
5994 if (regmask_now & (1 << reg)) {
5995 fprintf(fout, " %s f_st%d", float_type, reg - 16);
5996 if (regmask_init & (1 << reg))
5997 fprintf(fout, " = 0");
5998 fprintf(fout, ";\n");
5999 had_decl = 1;
6000 }
d4a985bd 6001 }
6002 }
6003 }
91977a1c 6004
16057ce1 6005 if (need_float_sw) {
6006 fprintf(fout, " u16 f_sw;\n");
6007 had_decl = 1;
6008 }
6009
d4e3b5db 6010 if (regmask_save) {
6011 for (reg = 0; reg < 8; reg++) {
6012 if (regmask_save & (1 << reg)) {
6013 fprintf(fout, " u32 s_%s;\n", regs_r32[reg]);
6014 had_decl = 1;
6015 }
6016 }
6017 }
6018
3a5101d7 6019 for (i = 0; i < ARRAY_SIZE(save_arg_vars); i++) {
6020 if (save_arg_vars[i] == 0)
6021 continue;
69a3cdfc 6022 for (reg = 0; reg < 32; reg++) {
3a5101d7 6023 if (save_arg_vars[i] & (1 << reg)) {
6024 fprintf(fout, " u32 %s;\n",
6025 saved_arg_name(buf1, sizeof(buf1), i, reg + 1));
69a3cdfc 6026 had_decl = 1;
6027 }
6028 }
6029 }
6030
30620174 6031 if (regmask_ffca) {
6032 for (reg = 0; reg < 32; reg++) {
6033 if (regmask_ffca & (1 << reg)) {
6034 fprintf(fout, " %s fs_%d;\n", float_type, reg + 1);
6035 had_decl = 1;
6036 }
6037 }
6038 }
6039
25a330eb 6040 // declare push-pop temporaries
6041 if (regmask_pp) {
6042 for (reg = 0; reg < 8; reg++) {
6043 if (regmask_pp & (1 << reg)) {
6044 fprintf(fout, " u32 pp_%s;\n", regs_r32[reg]);
6045 had_decl = 1;
6046 }
6047 }
6048 }
6049
cb090db0 6050 if (cond_vars) {
69a3cdfc 6051 for (i = 0; i < 8; i++) {
cb090db0 6052 if (cond_vars & (1 << i)) {
69a3cdfc 6053 fprintf(fout, " u32 cond_%s;\n", parsed_flag_op_names[i]);
6054 had_decl = 1;
6055 }
6056 }
6057 }
6058
108e9fe3 6059 if (need_tmp_var) {
6060 fprintf(fout, " u32 tmp;\n");
6061 had_decl = 1;
6062 }
6063
2fe80fdb 6064 if (need_tmp64) {
6065 fprintf(fout, " u64 tmp64;\n");
87bf6cec 6066 had_decl = 1;
6067 }
6068
91977a1c 6069 if (had_decl)
6070 fprintf(fout, "\n");
6071
7e08c224 6072 // do stack clear, if needed
6073 if (g_sct_func_attr & SCTFA_CLEAR_SF) {
6074 fprintf(fout, " ");
6075 if (g_stack_clear_len != 0) {
6076 if (g_stack_clear_len <= 4) {
6077 for (i = 0; i < g_stack_clear_len; i++)
6078 fprintf(fout, "sf.d[%d] = ", g_stack_clear_start + i);
6079 fprintf(fout, "0;\n");
6080 }
6081 else {
6082 fprintf(fout, "memset(&sf[%d], 0, %d);\n",
6083 g_stack_clear_start, g_stack_clear_len * 4);
6084 }
6085 }
6086 else
6087 fprintf(fout, "memset(&sf, 0, sizeof(sf));\n");
6088 }
6089
bd96f656 6090 if (g_func_pp->is_vararg) {
6091 if (g_func_pp->argc_stack == 0)
4f12f671 6092 ferr(ops, "vararg func without stack args?\n");
bd96f656 6093 fprintf(fout, " va_start(ap, a%d);\n", g_func_pp->argc);
4f12f671 6094 }
6095
91977a1c 6096 // output ops
69a3cdfc 6097 for (i = 0; i < opcnt; i++)
6098 {
d7857c3a 6099 if (g_labels[i] != NULL) {
91977a1c 6100 fprintf(fout, "\n%s:\n", g_labels[i]);
3ebea2cf 6101 label_pending = 1;
2b43685d 6102
6103 delayed_flag_op = NULL;
6104 last_arith_dst = NULL;
3ebea2cf 6105 }
91977a1c 6106
69a3cdfc 6107 po = &ops[i];
6108 if (po->flags & OPF_RMD)
91977a1c 6109 continue;
6110
6111 no_output = 0;
6112
91977a1c 6113 #define assert_operand_cnt(n_) \
850c9265 6114 if (po->operand_cnt != n_) \
6115 ferr(po, "operand_cnt is %d/%d\n", po->operand_cnt, n_)
6116
69a3cdfc 6117 // conditional/flag using op?
6118 if (po->flags & OPF_CC)
850c9265 6119 {
940e8e66 6120 int is_delayed = 0;
69a3cdfc 6121
04f8a628 6122 tmp_op = po->datap;
850c9265 6123
69a3cdfc 6124 // we go through all this trouble to avoid using parsed_flag_op,
6125 // which makes generated code much nicer
6126 if (delayed_flag_op != NULL)
850c9265 6127 {
092f64e1 6128 out_cmp_test(buf1, sizeof(buf1), delayed_flag_op,
6129 po->pfo, po->pfo_inv);
940e8e66 6130 is_delayed = 1;
91977a1c 6131 }
850c9265 6132 else if (last_arith_dst != NULL
092f64e1 6133 && (po->pfo == PFO_Z || po->pfo == PFO_S || po->pfo == PFO_P
04f8a628 6134 || (tmp_op && (tmp_op->op == OP_AND || tmp_op->op == OP_OR))
6135 ))
850c9265 6136 {
3ebea2cf 6137 out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
092f64e1 6138 out_test_for_cc(buf1, sizeof(buf1), po, po->pfo, po->pfo_inv,
850c9265 6139 last_arith_dst->lmod, buf3);
940e8e66 6140 is_delayed = 1;
850c9265 6141 }
04f8a628 6142 else if (tmp_op != NULL) {
7ba45c34 6143 // use preprocessed flag calc results
092f64e1 6144 if (!(tmp_op->pfomask & (1 << po->pfo)))
6145 ferr(po, "not prepared for pfo %d\n", po->pfo);
69a3cdfc 6146
092f64e1 6147 // note: pfo_inv was not yet applied
69a3cdfc 6148 snprintf(buf1, sizeof(buf1), "(%scond_%s)",
092f64e1 6149 po->pfo_inv ? "!" : "", parsed_flag_op_names[po->pfo]);
69a3cdfc 6150 }
6151 else {
6152 ferr(po, "all methods of finding comparison failed\n");
6153 }
850c9265 6154
69a3cdfc 6155 if (po->flags & OPF_JMP) {
092f64e1 6156 fprintf(fout, " if %s", buf1);
850c9265 6157 }
cb090db0 6158 else if (po->op == OP_RCL || po->op == OP_RCR
6159 || po->op == OP_ADC || po->op == OP_SBB)
6160 {
940e8e66 6161 if (is_delayed)
6162 fprintf(fout, " cond_%s = %s;\n",
092f64e1 6163 parsed_flag_op_names[po->pfo], buf1);
850c9265 6164 }
5101a5f9 6165 else if (po->flags & OPF_DATA) { // SETcc
850c9265 6166 out_dst_opr(buf2, sizeof(buf2), po, &po->operand[0]);
6167 fprintf(fout, " %s = %s;", buf2, buf1);
91977a1c 6168 }
69a3cdfc 6169 else {
6170 ferr(po, "unhandled conditional op\n");
6171 }
91977a1c 6172 }
6173
940e8e66 6174 pfomask = po->pfomask;
6175
850c9265 6176 switch (po->op)
91977a1c 6177 {
6178 case OP_MOV:
6179 assert_operand_cnt(2);
850c9265 6180 propagate_lmod(po, &po->operand[0], &po->operand[1]);
de50b98b 6181 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
c7ed83dd 6182 default_cast_to(buf3, sizeof(buf3), &po->operand[0]);
de50b98b 6183 fprintf(fout, " %s = %s;", buf1,
3ebea2cf 6184 out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
c7ed83dd 6185 buf3, 0));
850c9265 6186 break;
6187
6188 case OP_LEA:
6189 assert_operand_cnt(2);
87bf6cec 6190 po->operand[1].lmod = OPLM_DWORD; // always
850c9265 6191 fprintf(fout, " %s = %s;",
6192 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
3ebea2cf 6193 out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
6194 NULL, 1));
850c9265 6195 break;
6196
6197 case OP_MOVZX:
6198 assert_operand_cnt(2);
91977a1c 6199 fprintf(fout, " %s = %s;",
850c9265 6200 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
3ebea2cf 6201 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
850c9265 6202 break;
6203
6204 case OP_MOVSX:
6205 assert_operand_cnt(2);
6206 switch (po->operand[1].lmod) {
6207 case OPLM_BYTE:
6208 strcpy(buf3, "(s8)");
6209 break;
6210 case OPLM_WORD:
6211 strcpy(buf3, "(s16)");
6212 break;
6213 default:
6214 ferr(po, "invalid src lmod: %d\n", po->operand[1].lmod);
6215 }
a2c1d768 6216 fprintf(fout, " %s = %s;",
850c9265 6217 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
a2c1d768 6218 out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
6219 buf3, 0));
850c9265 6220 break;
6221
108e9fe3 6222 case OP_XCHG:
6223 assert_operand_cnt(2);
6224 propagate_lmod(po, &po->operand[0], &po->operand[1]);
6225 fprintf(fout, " tmp = %s;",
6226 out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], "", 0));
6227 fprintf(fout, " %s = %s;",
6228 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
c7ed83dd 6229 out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
6230 default_cast_to(buf3, sizeof(buf3), &po->operand[0]), 0));
6231 fprintf(fout, " %s = %stmp;",
6232 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[1]),
6233 default_cast_to(buf3, sizeof(buf3), &po->operand[1]));
108e9fe3 6234 snprintf(g_comment, sizeof(g_comment), "xchg");
6235 break;
6236
850c9265 6237 case OP_NOT:
6238 assert_operand_cnt(1);
6239 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
6240 fprintf(fout, " %s = ~%s;", buf1, buf1);
6241 break;
6242
04abc5d6 6243 case OP_XLAT:
6244 assert_operand_cnt(2);
6245 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
6246 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
6247 fprintf(fout, " %s = *(u8 *)(%s + %s);", buf1, buf2, buf1);
6248 strcpy(g_comment, "xlat");
6249 break;
6250
5101a5f9 6251 case OP_CDQ:
6252 assert_operand_cnt(2);
6253 fprintf(fout, " %s = (s32)%s >> 31;",
6254 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
3ebea2cf 6255 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
5101a5f9 6256 strcpy(g_comment, "cdq");
6257 break;
6258
622eb2ef 6259 case OP_BSWAP:
6260 assert_operand_cnt(1);
6261 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
6262 fprintf(fout, " %s = __builtin_bswap32(%s);", buf1, buf1);
6263 break;
6264
092f64e1 6265 case OP_LODS:
092f64e1 6266 if (po->flags & OPF_REP) {
acd03176 6267 assert_operand_cnt(3);
092f64e1 6268 // hmh..
6269 ferr(po, "TODO\n");
6270 }
6271 else {
acd03176 6272 assert_operand_cnt(2);
3947cf24 6273 fprintf(fout, " %s = %sesi; esi %c= %d;",
6274 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[1]),
6275 lmod_cast_u_ptr(po, po->operand[1].lmod),
092f64e1 6276 (po->flags & OPF_DF) ? '-' : '+',
3947cf24 6277 lmod_bytes(po, po->operand[1].lmod));
092f64e1 6278 strcpy(g_comment, "lods");
6279 }
6280 break;
6281
33c35af6 6282 case OP_STOS:
33c35af6 6283 if (po->flags & OPF_REP) {
acd03176 6284 assert_operand_cnt(3);
591721d7 6285 fprintf(fout, " for (; ecx != 0; ecx--, edi %c= %d)\n",
6286 (po->flags & OPF_DF) ? '-' : '+',
3947cf24 6287 lmod_bytes(po, po->operand[1].lmod));
d4e3b5db 6288 fprintf(fout, " %sedi = eax;",
3947cf24 6289 lmod_cast_u_ptr(po, po->operand[1].lmod));
33c35af6 6290 strcpy(g_comment, "rep stos");
6291 }
6292 else {
acd03176 6293 assert_operand_cnt(2);
092f64e1 6294 fprintf(fout, " %sedi = eax; edi %c= %d;",
3947cf24 6295 lmod_cast_u_ptr(po, po->operand[1].lmod),
591721d7 6296 (po->flags & OPF_DF) ? '-' : '+',
3947cf24 6297 lmod_bytes(po, po->operand[1].lmod));
33c35af6 6298 strcpy(g_comment, "stos");
6299 }
6300 break;
6301
d4e3b5db 6302 case OP_MOVS:
d4e3b5db 6303 j = lmod_bytes(po, po->operand[0].lmod);
6304 strcpy(buf1, lmod_cast_u_ptr(po, po->operand[0].lmod));
591721d7 6305 l = (po->flags & OPF_DF) ? '-' : '+';
d4e3b5db 6306 if (po->flags & OPF_REP) {
acd03176 6307 assert_operand_cnt(3);
d4e3b5db 6308 fprintf(fout,
591721d7 6309 " for (; ecx != 0; ecx--, edi %c= %d, esi %c= %d)\n",
6310 l, j, l, j);
d4e3b5db 6311 fprintf(fout,
6312 " %sedi = %sesi;", buf1, buf1);
6313 strcpy(g_comment, "rep movs");
6314 }
6315 else {
acd03176 6316 assert_operand_cnt(2);
092f64e1 6317 fprintf(fout, " %sedi = %sesi; edi %c= %d; esi %c= %d;",
591721d7 6318 buf1, buf1, l, j, l, j);
d4e3b5db 6319 strcpy(g_comment, "movs");
6320 }
6321 break;
6322
7ba45c34 6323 case OP_CMPS:
7ba45c34 6324 // repe ~ repeat while ZF=1
7ba45c34 6325 j = lmod_bytes(po, po->operand[0].lmod);
6326 strcpy(buf1, lmod_cast_u_ptr(po, po->operand[0].lmod));
591721d7 6327 l = (po->flags & OPF_DF) ? '-' : '+';
7ba45c34 6328 if (po->flags & OPF_REP) {
acd03176 6329 assert_operand_cnt(3);
7ba45c34 6330 fprintf(fout,
7f20f633 6331 " while (ecx != 0) {\n");
4741fdfe 6332 if (pfomask & (1 << PFO_C)) {
6333 // ugh..
6334 fprintf(fout,
05381e4a 6335 " cond_c = %sesi < %sedi;\n", buf1, buf1);
4741fdfe 6336 pfomask &= ~(1 << PFO_C);
6337 }
7ba45c34 6338 fprintf(fout,
05381e4a 6339 " cond_z = (%sesi == %sedi); esi %c= %d, edi %c= %d;\n",
1f84f6b3 6340 buf1, buf1, l, j, l, j);
6341 fprintf(fout,
7f20f633 6342 " ecx--;\n"
1f84f6b3 6343 " if (cond_z %s 0) break;\n",
6344 (po->flags & OPF_REPZ) ? "==" : "!=");
7ba45c34 6345 fprintf(fout,
4741fdfe 6346 " }");
7ba45c34 6347 snprintf(g_comment, sizeof(g_comment), "rep%s cmps",
6348 (po->flags & OPF_REPZ) ? "e" : "ne");
6349 }
6350 else {
acd03176 6351 assert_operand_cnt(2);
7ba45c34 6352 fprintf(fout,
05381e4a 6353 " cond_z = (%sesi == %sedi); esi %c= %d; edi %c= %d;",
591721d7 6354 buf1, buf1, l, j, l, j);
7ba45c34 6355 strcpy(g_comment, "cmps");
6356 }
6357 pfomask &= ~(1 << PFO_Z);
6358 last_arith_dst = NULL;
6359 delayed_flag_op = NULL;
6360 break;
6361
591721d7 6362 case OP_SCAS:
6363 // only does ZF (for now)
6364 // repe ~ repeat while ZF=1
3947cf24 6365 j = lmod_bytes(po, po->operand[1].lmod);
591721d7 6366 l = (po->flags & OPF_DF) ? '-' : '+';
6367 if (po->flags & OPF_REP) {
acd03176 6368 assert_operand_cnt(3);
591721d7 6369 fprintf(fout,
7f20f633 6370 " while (ecx != 0) {\n");
591721d7 6371 fprintf(fout,
1f84f6b3 6372 " cond_z = (%seax == %sedi); edi %c= %d;\n",
3947cf24 6373 lmod_cast_u(po, po->operand[1].lmod),
6374 lmod_cast_u_ptr(po, po->operand[1].lmod), l, j);
1f84f6b3 6375 fprintf(fout,
7f20f633 6376 " ecx--;\n"
1f84f6b3 6377 " if (cond_z %s 0) break;\n",
591721d7 6378 (po->flags & OPF_REPZ) ? "==" : "!=");
6379 fprintf(fout,
1f84f6b3 6380 " }");
591721d7 6381 snprintf(g_comment, sizeof(g_comment), "rep%s scas",
6382 (po->flags & OPF_REPZ) ? "e" : "ne");
6383 }
6384 else {
acd03176 6385 assert_operand_cnt(2);
05381e4a 6386 fprintf(fout, " cond_z = (%seax == %sedi); edi %c= %d;",
3947cf24 6387 lmod_cast_u(po, po->operand[1].lmod),
6388 lmod_cast_u_ptr(po, po->operand[1].lmod), l, j);
591721d7 6389 strcpy(g_comment, "scas");
6390 }
6391 pfomask &= ~(1 << PFO_Z);
6392 last_arith_dst = NULL;
6393 delayed_flag_op = NULL;
6394 break;
6395
850c9265 6396 // arithmetic w/flags
850c9265 6397 case OP_AND:
2b70f6d3 6398 if (po->operand[1].type == OPT_CONST && !po->operand[1].val)
6399 goto dualop_arith_const;
6400 propagate_lmod(po, &po->operand[0], &po->operand[1]);
6401 goto dualop_arith;
6402
850c9265 6403 case OP_OR:
5101a5f9 6404 propagate_lmod(po, &po->operand[0], &po->operand[1]);
2b70f6d3 6405 if (po->operand[1].type == OPT_CONST) {
6406 j = lmod_bytes(po, po->operand[0].lmod);
6407 if (((1ull << j * 8) - 1) == po->operand[1].val)
6408 goto dualop_arith_const;
6409 }
6410 goto dualop_arith;
6411
850c9265 6412 dualop_arith:
6413 assert_operand_cnt(2);
850c9265 6414 fprintf(fout, " %s %s= %s;",
6415 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
6416 op_to_c(po),
3ebea2cf 6417 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
04f8a628 6418 output_std_flags(fout, po, &pfomask, buf1);
6419 last_arith_dst = &po->operand[0];
6420 delayed_flag_op = NULL;
6421 break;
6422
2b70f6d3 6423 dualop_arith_const:
6424 // and 0, or ~0 used instead mov
6425 assert_operand_cnt(2);
6426 fprintf(fout, " %s = %s;",
6427 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
6428 out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
6429 default_cast_to(buf3, sizeof(buf3), &po->operand[0]), 0));
6430 output_std_flags(fout, po, &pfomask, buf1);
6431 last_arith_dst = &po->operand[0];
6432 delayed_flag_op = NULL;
6433 break;
6434
04f8a628 6435 case OP_SHL:
6436 case OP_SHR:
6437 assert_operand_cnt(2);
6438 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
6439 if (pfomask & (1 << PFO_C)) {
6440 if (po->operand[1].type == OPT_CONST) {
6441 l = lmod_bytes(po, po->operand[0].lmod) * 8;
6442 j = po->operand[1].val;
6443 j %= l;
6444 if (j != 0) {
6445 if (po->op == OP_SHL)
6446 j = l - j;
6447 else
6448 j -= 1;
cb090db0 6449 fprintf(fout, " cond_c = (%s >> %d) & 1;\n",
6450 buf1, j);
04f8a628 6451 }
6452 else
6453 ferr(po, "zero shift?\n");
6454 }
6455 else
6456 ferr(po, "TODO\n");
6457 pfomask &= ~(1 << PFO_C);
840257f6 6458 }
04abc5d6 6459 fprintf(fout, " %s %s= %s", buf1, op_to_c(po),
04f8a628 6460 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
04abc5d6 6461 if (po->operand[1].type != OPT_CONST)
6462 fprintf(fout, " & 0x1f");
6463 fprintf(fout, ";");
04f8a628 6464 output_std_flags(fout, po, &pfomask, buf1);
850c9265 6465 last_arith_dst = &po->operand[0];
69a3cdfc 6466 delayed_flag_op = NULL;
850c9265 6467 break;
6468
d4e3b5db 6469 case OP_SAR:
6470 assert_operand_cnt(2);
6471 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
6472 fprintf(fout, " %s = %s%s >> %s;", buf1,
6473 lmod_cast_s(po, po->operand[0].lmod), buf1,
3ebea2cf 6474 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
04f8a628 6475 output_std_flags(fout, po, &pfomask, buf1);
d4e3b5db 6476 last_arith_dst = &po->operand[0];
6477 delayed_flag_op = NULL;
6478 break;
6479
04abc5d6 6480 case OP_SHLD:
3b2f4044 6481 case OP_SHRD:
6482 assert_operand_cnt(3);
6483 propagate_lmod(po, &po->operand[0], &po->operand[1]);
6484 l = lmod_bytes(po, po->operand[0].lmod) * 8;
3b2f4044 6485 out_src_opr_u32(buf3, sizeof(buf3), po, &po->operand[2]);
acd03176 6486 if (po->operand[2].type != OPT_CONST) {
6487 // no handling for "undefined" case, hopefully not needed
6488 snprintf(buf2, sizeof(buf2), "(%s & 0x1f)", buf3);
6489 strcpy(buf3, buf2);
6490 }
6491 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
6492 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
04abc5d6 6493 if (po->op == OP_SHLD) {
6494 fprintf(fout, " %s <<= %s; %s |= %s >> (%d - %s);",
6495 buf1, buf3, buf1, buf2, l, buf3);
6496 strcpy(g_comment, "shld");
6497 }
6498 else {
6499 fprintf(fout, " %s >>= %s; %s |= %s << (%d - %s);",
6500 buf1, buf3, buf1, buf2, l, buf3);
6501 strcpy(g_comment, "shrd");
6502 }
3b2f4044 6503 output_std_flags(fout, po, &pfomask, buf1);
6504 last_arith_dst = &po->operand[0];
6505 delayed_flag_op = NULL;
6506 break;
6507
d4e3b5db 6508 case OP_ROL:
6509 case OP_ROR:
6510 assert_operand_cnt(2);
6511 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
6512 if (po->operand[1].type == OPT_CONST) {
6513 j = po->operand[1].val;
6514 j %= lmod_bytes(po, po->operand[0].lmod) * 8;
6515 fprintf(fout, po->op == OP_ROL ?
6516 " %s = (%s << %d) | (%s >> %d);" :
6517 " %s = (%s >> %d) | (%s << %d);",
6518 buf1, buf1, j, buf1,
6519 lmod_bytes(po, po->operand[0].lmod) * 8 - j);
6520 }
6521 else
6522 ferr(po, "TODO\n");
04f8a628 6523 output_std_flags(fout, po, &pfomask, buf1);
d4e3b5db 6524 last_arith_dst = &po->operand[0];
6525 delayed_flag_op = NULL;
6526 break;
6527
cb090db0 6528 case OP_RCL:
6529 case OP_RCR:
6530 assert_operand_cnt(2);
6531 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
6532 l = lmod_bytes(po, po->operand[0].lmod) * 8;
6533 if (po->operand[1].type == OPT_CONST) {
6534 j = po->operand[1].val % l;
6535 if (j == 0)
6536 ferr(po, "zero rotate\n");
6537 fprintf(fout, " tmp = (%s >> %d) & 1;\n",
6538 buf1, (po->op == OP_RCL) ? (l - j) : (j - 1));
6539 if (po->op == OP_RCL) {
6540 fprintf(fout,
6541 " %s = (%s << %d) | (cond_c << %d)",
6542 buf1, buf1, j, j - 1);
6543 if (j != 1)
6544 fprintf(fout, " | (%s >> %d)", buf1, l + 1 - j);
6545 }
6546 else {
6547 fprintf(fout,
6548 " %s = (%s >> %d) | (cond_c << %d)",
6549 buf1, buf1, j, l - j);
6550 if (j != 1)
6551 fprintf(fout, " | (%s << %d)", buf1, l + 1 - j);
6552 }
6553 fprintf(fout, ";\n");
6554 fprintf(fout, " cond_c = tmp;");
6555 }
6556 else
6557 ferr(po, "TODO\n");
6558 strcpy(g_comment, (po->op == OP_RCL) ? "rcl" : "rcr");
6559 output_std_flags(fout, po, &pfomask, buf1);
6560 last_arith_dst = &po->operand[0];
6561 delayed_flag_op = NULL;
6562 break;
6563
5101a5f9 6564 case OP_XOR:
850c9265 6565 assert_operand_cnt(2);
6566 propagate_lmod(po, &po->operand[0], &po->operand[1]);
5101a5f9 6567 if (IS(opr_name(po, 0), opr_name(po, 1))) {
6568 // special case for XOR
7f20f633 6569 int z = PFOB_O | PFOB_C | PFOB_S | (1 << PFO_L);
6570 for (j = 0; j <= PFO_LE; j++) {
6571 if (pfomask & (1 << j)) {
6572 fprintf(fout, " cond_%s = %d;\n",
6573 parsed_flag_op_names[j], (1 << j) & z ? 0 : 1);
6574 pfomask &= ~(1 << j);
6575 }
2fe80fdb 6576 }
5101a5f9 6577 fprintf(fout, " %s = 0;",
6578 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
6579 last_arith_dst = &po->operand[0];
6580 delayed_flag_op = NULL;
850c9265 6581 break;
850c9265 6582 }
5101a5f9 6583 goto dualop_arith;
6584
2fe80fdb 6585 case OP_ADD:
6586 assert_operand_cnt(2);
6587 propagate_lmod(po, &po->operand[0], &po->operand[1]);
6588 if (pfomask & (1 << PFO_C)) {
c8dbc5be 6589 out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
6590 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
6591 if (po->operand[0].lmod == OPLM_DWORD) {
6592 fprintf(fout, " tmp64 = (u64)%s + %s;\n", buf1, buf2);
6593 fprintf(fout, " cond_c = tmp64 >> 32;\n");
6594 fprintf(fout, " %s = (u32)tmp64;",
6595 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
d4a985bd 6596 strcat(g_comment, " add64");
c8dbc5be 6597 }
6598 else {
6599 fprintf(fout, " cond_c = ((u32)%s + %s) >> %d;\n",
6600 buf1, buf2, lmod_bytes(po, po->operand[0].lmod) * 8);
6601 fprintf(fout, " %s += %s;",
6602 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
6603 buf2);
6604 }
2fe80fdb 6605 pfomask &= ~(1 << PFO_C);
6606 output_std_flags(fout, po, &pfomask, buf1);
6607 last_arith_dst = &po->operand[0];
6608 delayed_flag_op = NULL;
6609 break;
6610 }
16057ce1 6611 if (pfomask & (1 << PFO_LE)) {
6612 out_cmp_for_cc(buf1, sizeof(buf1), po, PFO_LE, 0, 1);
6613 fprintf(fout, " cond_%s = %s;\n",
6614 parsed_flag_op_names[PFO_LE], buf1);
6615 pfomask &= ~(1 << PFO_LE);
6616 }
2fe80fdb 6617 goto dualop_arith;
6618
6619 case OP_SUB:
6620 assert_operand_cnt(2);
6621 propagate_lmod(po, &po->operand[0], &po->operand[1]);
3b2f4044 6622 if (pfomask & ~((1 << PFO_Z) | (1 << PFO_S))) {
6623 for (j = 0; j <= PFO_LE; j++) {
6624 if (!(pfomask & (1 << j)))
6625 continue;
6626 if (j == PFO_Z || j == PFO_S)
6627 continue;
6628
16057ce1 6629 out_cmp_for_cc(buf1, sizeof(buf1), po, j, 0, 0);
3b2f4044 6630 fprintf(fout, " cond_%s = %s;\n",
6631 parsed_flag_op_names[j], buf1);
6632 pfomask &= ~(1 << j);
6633 }
2fe80fdb 6634 }
6635 goto dualop_arith;
6636
5101a5f9 6637 case OP_ADC:
850c9265 6638 case OP_SBB:
5101a5f9 6639 assert_operand_cnt(2);
6640 propagate_lmod(po, &po->operand[0], &po->operand[1]);
a2c1d768 6641 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
840257f6 6642 if (po->op == OP_SBB
6643 && IS(po->operand[0].name, po->operand[1].name))
6644 {
6645 // avoid use of unitialized var
a2c1d768 6646 fprintf(fout, " %s = -cond_c;", buf1);
94d447fb 6647 // carry remains what it was
6648 pfomask &= ~(1 << PFO_C);
840257f6 6649 }
6650 else {
a2c1d768 6651 fprintf(fout, " %s %s= %s + cond_c;", buf1, op_to_c(po),
3ebea2cf 6652 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
840257f6 6653 }
a2c1d768 6654 output_std_flags(fout, po, &pfomask, buf1);
5101a5f9 6655 last_arith_dst = &po->operand[0];
6656 delayed_flag_op = NULL;
850c9265 6657 break;
6658
1f84f6b3 6659 case OP_BSF:
6660 assert_operand_cnt(2);
6661 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
6662 fprintf(fout, " %s = %s ? __builtin_ffs(%s) - 1 : 0;",
6663 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
6664 buf2, buf2);
6665 output_std_flags(fout, po, &pfomask, buf1);
6666 last_arith_dst = &po->operand[0];
6667 delayed_flag_op = NULL;
d4a985bd 6668 strcat(g_comment, " bsf");
1f84f6b3 6669 break;
6670
850c9265 6671 case OP_DEC:
90307a99 6672 if (pfomask & ~(PFOB_S | PFOB_S | PFOB_C)) {
6673 for (j = 0; j <= PFO_LE; j++) {
6674 if (!(pfomask & (1 << j)))
6675 continue;
6676 if (j == PFO_Z || j == PFO_S || j == PFO_C)
6677 continue;
6678
16057ce1 6679 out_cmp_for_cc(buf1, sizeof(buf1), po, j, 0, 0);
90307a99 6680 fprintf(fout, " cond_%s = %s;\n",
6681 parsed_flag_op_names[j], buf1);
6682 pfomask &= ~(1 << j);
6683 }
6684 }
6685 // fallthrough
6686
6687 case OP_INC:
6688 if (pfomask & (1 << PFO_C))
6689 // carry is unaffected by inc/dec.. wtf?
6690 ferr(po, "carry propagation needed\n");
6691
850c9265 6692 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
1bafb621 6693 if (po->operand[0].type == OPT_REG) {
6694 strcpy(buf2, po->op == OP_INC ? "++" : "--");
6695 fprintf(fout, " %s%s;", buf1, buf2);
6696 }
6697 else {
6698 strcpy(buf2, po->op == OP_INC ? "+" : "-");
6699 fprintf(fout, " %s %s= 1;", buf1, buf2);
6700 }
a2c1d768 6701 output_std_flags(fout, po, &pfomask, buf1);
5101a5f9 6702 last_arith_dst = &po->operand[0];
6703 delayed_flag_op = NULL;
6704 break;
6705
6706 case OP_NEG:
6707 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
3ebea2cf 6708 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]);
5101a5f9 6709 fprintf(fout, " %s = -%s%s;", buf1,
6710 lmod_cast_s(po, po->operand[0].lmod), buf2);
850c9265 6711 last_arith_dst = &po->operand[0];
69a3cdfc 6712 delayed_flag_op = NULL;
0ea6430c 6713 if (pfomask & PFOB_C) {
940e8e66 6714 fprintf(fout, "\n cond_c = (%s != 0);", buf1);
0ea6430c 6715 pfomask &= ~PFOB_C;
940e8e66 6716 }
0ea6430c 6717 output_std_flags(fout, po, &pfomask, buf1);
850c9265 6718 break;
6719
6720 case OP_IMUL:
de50b98b 6721 if (po->operand_cnt == 2) {
6722 propagate_lmod(po, &po->operand[0], &po->operand[1]);
850c9265 6723 goto dualop_arith;
de50b98b 6724 }
87bf6cec 6725 if (po->operand_cnt == 3)
6726 ferr(po, "TODO imul3\n");
6727 // fallthrough
6728 case OP_MUL:
6729 assert_operand_cnt(1);
c8dbc5be 6730 switch (po->operand[0].lmod) {
6731 case OPLM_DWORD:
6732 strcpy(buf1, po->op == OP_IMUL ? "(s64)(s32)" : "(u64)");
6733 fprintf(fout, " tmp64 = %seax * %s%s;\n", buf1, buf1,
6734 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]));
6735 fprintf(fout, " edx = tmp64 >> 32;\n");
6736 fprintf(fout, " eax = tmp64;");
6737 break;
6738 case OPLM_BYTE:
6739 strcpy(buf1, po->op == OP_IMUL ? "(s16)(s8)" : "(u16)(u8)");
6740 fprintf(fout, " LOWORD(eax) = %seax * %s;", buf1,
6741 out_src_opr(buf2, sizeof(buf2), po, &po->operand[0],
6742 buf1, 0));
6743 break;
6744 default:
6745 ferr(po, "TODO: unhandled mul type\n");
6746 break;
6747 }
87bf6cec 6748 last_arith_dst = NULL;
69a3cdfc 6749 delayed_flag_op = NULL;
91977a1c 6750 break;
6751
5101a5f9 6752 case OP_DIV:
6753 case OP_IDIV:
6754 assert_operand_cnt(1);
cb090db0 6755 out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
acd03176 6756 strcpy(cast, lmod_cast(po, po->operand[0].lmod,
cb090db0 6757 po->op == OP_IDIV));
6758 switch (po->operand[0].lmod) {
6759 case OPLM_DWORD:
6760 if (po->flags & OPF_32BIT)
acd03176 6761 snprintf(buf2, sizeof(buf2), "%seax", cast);
cb090db0 6762 else {
6763 fprintf(fout, " tmp64 = ((u64)edx << 32) | eax;\n");
acd03176 6764 snprintf(buf2, sizeof(buf2), "%stmp64",
cb090db0 6765 (po->op == OP_IDIV) ? "(s64)" : "");
6766 }
6767 if (po->operand[0].type == OPT_REG
6768 && po->operand[0].reg == xDX)
6769 {
acd03176 6770 fprintf(fout, " eax = %s / %s%s;\n", buf2, cast, buf1);
6771 fprintf(fout, " edx = %s %% %s%s;", buf2, cast, buf1);
6772 }
6773 else {
6774 fprintf(fout, " edx = %s %% %s%s;\n", buf2, cast, buf1);
6775 fprintf(fout, " eax = %s / %s%s;", buf2, cast, buf1);
6776 }
6777 break;
6778 case OPLM_WORD:
6779 fprintf(fout, " tmp = (edx << 16) | (eax & 0xffff);\n");
6780 snprintf(buf2, sizeof(buf2), "%stmp",
6781 (po->op == OP_IDIV) ? "(s32)" : "");
6782 if (po->operand[0].type == OPT_REG
6783 && po->operand[0].reg == xDX)
6784 {
6785 fprintf(fout, " LOWORD(eax) = %s / %s%s;\n",
6786 buf2, cast, buf1);
6787 fprintf(fout, " LOWORD(edx) = %s %% %s%s;",
6788 buf2, cast, buf1);
cb090db0 6789 }
6790 else {
acd03176 6791 fprintf(fout, " LOWORD(edx) = %s %% %s%s;\n",
6792 buf2, cast, buf1);
6793 fprintf(fout, " LOWORD(eax) = %s / %s%s;",
6794 buf2, cast, buf1);
cb090db0 6795 }
d4a985bd 6796 strcat(g_comment, " div16");
cb090db0 6797 break;
6798 default:
acd03176 6799 ferr(po, "unhandled div lmod %d\n", po->operand[0].lmod);
5101a5f9 6800 }
87bf6cec 6801 last_arith_dst = NULL;
6802 delayed_flag_op = NULL;
5101a5f9 6803 break;
6804
91977a1c 6805 case OP_TEST:
6806 case OP_CMP:
850c9265 6807 propagate_lmod(po, &po->operand[0], &po->operand[1]);
940e8e66 6808 if (pfomask != 0) {
69a3cdfc 6809 for (j = 0; j < 8; j++) {
940e8e66 6810 if (pfomask & (1 << j)) {
69a3cdfc 6811 out_cmp_test(buf1, sizeof(buf1), po, j, 0);
6812 fprintf(fout, " cond_%s = %s;",
6813 parsed_flag_op_names[j], buf1);
6814 }
6815 }
940e8e66 6816 pfomask = 0;
69a3cdfc 6817 }
6818 else
6819 no_output = 1;
7ba45c34 6820 last_arith_dst = NULL;
69a3cdfc 6821 delayed_flag_op = po;
91977a1c 6822 break;
6823
092f64e1 6824 case OP_SCC:
6825 // SETcc - should already be handled
6826 break;
6827
69a3cdfc 6828 // note: we reuse OP_Jcc for SETcc, only flags differ
092f64e1 6829 case OP_JCC:
6830 fprintf(fout, "\n goto %s;", po->operand[0].name);
850c9265 6831 break;
6832
5c024ef7 6833 case OP_JECXZ:
6834 fprintf(fout, " if (ecx == 0)\n");
6835 fprintf(fout, " goto %s;", po->operand[0].name);
d4a985bd 6836 strcat(g_comment, " jecxz");
5c024ef7 6837 break;
6838
04abc5d6 6839 case OP_LOOP:
3947cf24 6840 fprintf(fout, " if (--ecx != 0)\n");
04abc5d6 6841 fprintf(fout, " goto %s;", po->operand[0].name);
d4a985bd 6842 strcat(g_comment, " loop");
04abc5d6 6843 break;
6844
850c9265 6845 case OP_JMP:
87bf6cec 6846 assert_operand_cnt(1);
de50b98b 6847 last_arith_dst = NULL;
6848 delayed_flag_op = NULL;
6849
4c45fa73 6850 if (po->operand[0].type == OPT_REGMEM) {
6851 ret = sscanf(po->operand[0].name, "%[^[][%[^*]*4]",
6852 buf1, buf2);
6853 if (ret != 2)
6854 ferr(po, "parse failure for jmp '%s'\n",
6855 po->operand[0].name);
6856 fprintf(fout, " goto *jt_%s[%s];", buf1, buf2);
6857 break;
6858 }
6859 else if (po->operand[0].type != OPT_LABEL)
6860 ferr(po, "unhandled jmp type\n");
87bf6cec 6861
850c9265 6862 fprintf(fout, " goto %s;", po->operand[0].name);
91977a1c 6863 break;
6864
6865 case OP_CALL:
5101a5f9 6866 assert_operand_cnt(1);
092f64e1 6867 pp = po->pp;
89ff3147 6868 my_assert_not(pp, NULL);
91977a1c 6869
092f64e1 6870 strcpy(buf3, " ");
6871 if (po->flags & OPF_CC) {
6872 // we treat conditional branch to another func
6873 // (yes such code exists..) as conditional tailcall
6874 strcat(buf3, " ");
6875 fprintf(fout, " {\n");
6876 }
6877
8eb12e72 6878 if (pp->is_fptr && !pp->is_arg) {
092f64e1 6879 fprintf(fout, "%s%s = %s;\n", buf3, pp->name,
1cd4a663 6880 out_src_opr(buf1, sizeof(buf1), po, &po->operand[0],
6881 "(void *)", 0));
8eb12e72 6882 if (pp->is_unresolved)
6883 fprintf(fout, "%sunresolved_call(\"%s:%d\", %s);\n",
6884 buf3, asmfn, po->asmln, pp->name);
6885 }
1bafb621 6886
092f64e1 6887 fprintf(fout, "%s", buf3);
2b43685d 6888 if (strstr(pp->ret_type.name, "int64")) {
87bf6cec 6889 if (po->flags & OPF_TAIL)
2b43685d 6890 ferr(po, "int64 and tail?\n");
2fe80fdb 6891 fprintf(fout, "tmp64 = ");
2b43685d 6892 }
6893 else if (!IS(pp->ret_type.name, "void")) {
6894 if (po->flags & OPF_TAIL) {
d4a985bd 6895 if (regmask_ret & mxAX) {
840257f6 6896 fprintf(fout, "return ");
6897 if (g_func_pp->ret_type.is_ptr != pp->ret_type.is_ptr)
6898 fprintf(fout, "(%s)", g_func_pp->ret_type.name);
6899 }
d4a985bd 6900 else if (regmask_ret & mxST0)
6901 ferr(po, "float tailcall\n");
2b43685d 6902 }
d4a985bd 6903 else if (po->regmask_dst & mxAX) {
87bf6cec 6904 fprintf(fout, "eax = ");
2b43685d 6905 if (pp->ret_type.is_ptr)
6906 fprintf(fout, "(u32)");
6907 }
d4a985bd 6908 else if (po->regmask_dst & mxST0) {
fe18df39 6909 ferr_assert(po, po->flags & OPF_FPUSH);
6910 if (need_float_stack)
6911 fprintf(fout, "f_st[--f_stp & 7] = ");
6912 else
6913 fprintf(fout, "f_st0 = ");
d4a985bd 6914 }
91977a1c 6915 }
87bf6cec 6916
ddaf8bd7 6917 if (pp->name[0] == 0)
6918 ferr(po, "missing pp->name\n");
6919 fprintf(fout, "%s%s(", pp->name,
6920 pp->has_structarg ? "_sa" : "");
39b168b8 6921
2fe80fdb 6922 if (po->flags & OPF_ATAIL) {
6923 if (pp->argc_stack != g_func_pp->argc_stack
6924 || (pp->argc_stack > 0
6925 && pp->is_stdcall != g_func_pp->is_stdcall))
6926 ferr(po, "incompatible tailcall\n");
1f84f6b3 6927 if (g_func_pp->has_retreg)
6928 ferr(po, "TODO: retreg+tailcall\n");
87bf6cec 6929
2fe80fdb 6930 for (arg = j = 0; arg < pp->argc; arg++) {
6931 if (arg > 0)
6932 fprintf(fout, ", ");
87bf6cec 6933
2fe80fdb 6934 cast[0] = 0;
6935 if (pp->arg[arg].type.is_ptr)
6936 snprintf(cast, sizeof(cast), "(%s)",
6937 pp->arg[arg].type.name);
91977a1c 6938
2fe80fdb 6939 if (pp->arg[arg].reg != NULL) {
6940 fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
6941 continue;
6942 }
6943 // stack arg
6944 for (; j < g_func_pp->argc; j++)
6945 if (g_func_pp->arg[j].reg == NULL)
6946 break;
6947 fprintf(fout, "%sa%d", cast, j + 1);
6948 j++;
69a3cdfc 6949 }
2fe80fdb 6950 }
6951 else {
6952 for (arg = 0; arg < pp->argc; arg++) {
6953 if (arg > 0)
6954 fprintf(fout, ", ");
6955
6956 cast[0] = 0;
6957 if (pp->arg[arg].type.is_ptr)
6958 snprintf(cast, sizeof(cast), "(%s)",
6959 pp->arg[arg].type.name);
6960
6961 if (pp->arg[arg].reg != NULL) {
1f84f6b3 6962 if (pp->arg[arg].type.is_retreg)
6963 fprintf(fout, "&%s", pp->arg[arg].reg);
8c83cc48 6964 else if (IS(pp->arg[arg].reg, "ebp")
e627c4d0 6965 && g_bp_frame && !(po->flags & OPF_EBP_S))
8c83cc48 6966 {
6967 // rare special case
6968 fprintf(fout, "%s(u32)&sf.b[sizeof(sf)]", cast);
6969 strcat(g_comment, " bp_ref");
6970 }
1f84f6b3 6971 else
6972 fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
2fe80fdb 6973 continue;
6974 }
6975
6976 // stack arg
6977 tmp_op = pp->arg[arg].datap;
6978 if (tmp_op == NULL)
6979 ferr(po, "parsed_op missing for arg%d\n", arg);
23fd0b11 6980
6981 if (tmp_op->flags & OPF_VAPUSH) {
6982 fprintf(fout, "ap");
6983 }
30620174 6984 else if (tmp_op->op == OP_FST) {
6985 fprintf(fout, "fs_%d", tmp_op->p_argnum);
6986 if (tmp_op->operand[0].lmod == OPLM_QWORD)
6987 arg++;
6988 }
5f70a34f 6989 else if (tmp_op->p_argpass != 0) {
6990 fprintf(fout, "a%d", tmp_op->p_argpass);
6991 }
8c83cc48 6992 else if (pp->arg[arg].is_saved) {
6993 ferr_assert(po, tmp_op->p_argnum > 0);
3a5101d7 6994 fprintf(fout, "%s%s", cast,
6995 saved_arg_name(buf1, sizeof(buf1),
6996 tmp_op->p_arggrp, tmp_op->p_argnum));
2fe80fdb 6997 }
6998 else {
6999 fprintf(fout, "%s",
7000 out_src_opr(buf1, sizeof(buf1),
7001 tmp_op, &tmp_op->operand[0], cast, 0));
7002 }
69a3cdfc 7003 }
91977a1c 7004 }
7005 fprintf(fout, ");");
87bf6cec 7006
2b43685d 7007 if (strstr(pp->ret_type.name, "int64")) {
7008 fprintf(fout, "\n");
092f64e1 7009 fprintf(fout, "%sedx = tmp64 >> 32;\n", buf3);
7010 fprintf(fout, "%seax = tmp64;", buf3);
2b43685d 7011 }
7012
89ff3147 7013 if (pp->is_unresolved) {
8eb12e72 7014 snprintf(buf2, sizeof(buf2), " unresolved %dreg",
89ff3147 7015 pp->argc_reg);
092f64e1 7016 strcat(g_comment, buf2);
89ff3147 7017 }
7018
87bf6cec 7019 if (po->flags & OPF_TAIL) {
840257f6 7020 ret = 0;
ddaf8bd7 7021 if (i == opcnt - 1 || pp->is_noreturn)
840257f6 7022 ret = 0;
7023 else if (IS(pp->ret_type.name, "void"))
7024 ret = 1;
b2bd20c0 7025 else if (!(regmask_ret & (1 << xAX)))
840257f6 7026 ret = 1;
7027 // else already handled as 'return f()'
7028
7029 if (ret) {
acd03176 7030 fprintf(fout, "\n%sreturn;", buf3);
7031 strcat(g_comment, " ^ tailcall");
3ebea2cf 7032 }
89ff3147 7033 else
ddaf8bd7 7034 strcat(g_comment, " tailcall");
acd03176 7035
7036 if ((regmask_ret & (1 << xAX))
7037 && IS(pp->ret_type.name, "void") && !pp->is_noreturn)
7038 {
7039 ferr(po, "int func -> void func tailcall?\n");
7040 }
87bf6cec 7041 }
ddaf8bd7 7042 if (pp->is_noreturn)
7043 strcat(g_comment, " noreturn");
2fe80fdb 7044 if ((po->flags & OPF_ATAIL) && pp->argc_stack > 0)
7045 strcat(g_comment, " argframe");
092f64e1 7046 if (po->flags & OPF_CC)
7047 strcat(g_comment, " cond");
7048
7049 if (po->flags & OPF_CC)
7050 fprintf(fout, "\n }");
7051
87bf6cec 7052 delayed_flag_op = NULL;
7053 last_arith_dst = NULL;
91977a1c 7054 break;
7055
7056 case OP_RET:
bd96f656 7057 if (g_func_pp->is_vararg)
4f12f671 7058 fprintf(fout, " va_end(ap);\n");
1f84f6b3 7059 if (g_func_pp->has_retreg) {
7060 for (arg = 0; arg < g_func_pp->argc; arg++)
7061 if (g_func_pp->arg[arg].type.is_retreg)
7062 fprintf(fout, " *r_%s = %s;\n",
7063 g_func_pp->arg[arg].reg, g_func_pp->arg[arg].reg);
7064 }
4f12f671 7065
16057ce1 7066 if (regmask_ret & mxST0) {
7067 fprintf(fout, " return %s;", float_st0);
7068 }
7069 else if (!(regmask_ret & mxAX)) {
3ebea2cf 7070 if (i != opcnt - 1 || label_pending)
7071 fprintf(fout, " return;");
7072 }
bd96f656 7073 else if (g_func_pp->ret_type.is_ptr) {
d4e3b5db 7074 fprintf(fout, " return (%s)eax;",
bd96f656 7075 g_func_pp->ret_type.name);
3ebea2cf 7076 }
2fe80fdb 7077 else if (IS(g_func_pp->ret_type.name, "__int64"))
7078 fprintf(fout, " return ((u64)edx << 32) | eax;");
91977a1c 7079 else
7080 fprintf(fout, " return eax;");
de50b98b 7081
7082 last_arith_dst = NULL;
7083 delayed_flag_op = NULL;
91977a1c 7084 break;
7085
7086 case OP_PUSH:
1f84f6b3 7087 out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
5f70a34f 7088 if (po->p_argnum != 0) {
69a3cdfc 7089 // special case - saved func arg
3a5101d7 7090 fprintf(fout, " %s = %s;",
7091 saved_arg_name(buf2, sizeof(buf2),
7092 po->p_arggrp, po->p_argnum), buf1);
69a3cdfc 7093 break;
7094 }
d4e3b5db 7095 else if (po->flags & OPF_RSAVE) {
d4e3b5db 7096 fprintf(fout, " s_%s = %s;", buf1, buf1);
7097 break;
7098 }
25a330eb 7099 else if (po->flags & OPF_PPUSH) {
7100 tmp_op = po->datap;
7101 ferr_assert(po, tmp_op != NULL);
7102 out_dst_opr(buf2, sizeof(buf2), po, &tmp_op->operand[0]);
7103 fprintf(fout, " pp_%s = %s;", buf2, buf1);
7104 break;
7105 }
1f84f6b3 7106 else if (g_func_pp->is_userstack) {
7107 fprintf(fout, " *(--esp) = %s;", buf1);
7108 break;
7109 }
e56ab892 7110 if (!(g_ida_func_attr & IDAFA_NORETURN))
7111 ferr(po, "stray push encountered\n");
7112 no_output = 1;
91977a1c 7113 break;
7114
7115 case OP_POP:
25a330eb 7116 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
d4e3b5db 7117 if (po->flags & OPF_RSAVE) {
d4e3b5db 7118 fprintf(fout, " %s = s_%s;", buf1, buf1);
7119 break;
7120 }
25a330eb 7121 else if (po->flags & OPF_PPUSH) {
e83ea7ed 7122 // push/pop graph / non-const
25a330eb 7123 ferr_assert(po, po->datap == NULL);
7124 fprintf(fout, " %s = pp_%s;", buf1, buf1);
7125 break;
7126 }
5c024ef7 7127 else if (po->datap != NULL) {
7128 // push/pop pair
7129 tmp_op = po->datap;
5c024ef7 7130 fprintf(fout, " %s = %s;", buf1,
7131 out_src_opr(buf2, sizeof(buf2),
7132 tmp_op, &tmp_op->operand[0],
c7ed83dd 7133 default_cast_to(buf3, sizeof(buf3), &po->operand[0]), 0));
5c024ef7 7134 break;
7135 }
1f84f6b3 7136 else if (g_func_pp->is_userstack) {
25a330eb 7137 fprintf(fout, " %s = *esp++;", buf1);
1f84f6b3 7138 break;
7139 }
7140 else
7141 ferr(po, "stray pop encountered\n");
91977a1c 7142 break;
7143
33c35af6 7144 case OP_NOP:
2b43685d 7145 no_output = 1;
33c35af6 7146 break;
7147
622eb2ef 7148 // pseudo ops
7149 case OPP_ALLSHL:
7150 case OPP_ALLSHR:
7151 fprintf(fout, " tmp64 = ((u64)edx << 32) | eax;\n");
8c83cc48 7152 fprintf(fout, " tmp64 = (s64)tmp64 %s LOBYTE(ecx);\n",
622eb2ef 7153 po->op == OPP_ALLSHL ? "<<" : ">>");
7154 fprintf(fout, " edx = tmp64 >> 32; eax = tmp64;");
7155 strcat(g_comment, po->op == OPP_ALLSHL
7156 ? " allshl" : " allshr");
7157 break;
7158
d4a985bd 7159 // x87
7160 case OP_FLD:
fe18df39 7161 if (need_float_stack) {
7162 out_src_opr_float(buf1, sizeof(buf1),
7163 po, &po->operand[0], 1);
7164 if (po->regmask_src & mxSTa) {
7165 fprintf(fout, " f_st[(f_stp - 1) & 7] = %s; f_stp--;",
7166 buf1);
7167 }
7168 else
7169 fprintf(fout, " f_st[--f_stp & 7] = %s;", buf1);
7170 }
7171 else {
7172 if (po->flags & OPF_FSHIFT)
7173 fprintf(fout, " f_st1 = f_st0;");
7174 if (po->operand[0].type == OPT_REG
7175 && po->operand[0].reg == xST0)
7176 {
7177 strcat(g_comment, " fld st");
7178 break;
7179 }
7180 fprintf(fout, " f_st0 = %s;",
7181 out_src_opr_float(buf1, sizeof(buf1),
7182 po, &po->operand[0], 0));
d4a985bd 7183 }
d4a985bd 7184 strcat(g_comment, " fld");
7185 break;
7186
7187 case OP_FILD:
fe18df39 7188 out_src_opr(buf1, sizeof(buf1), po, &po->operand[0],
7189 lmod_cast(po, po->operand[0].lmod, 1), 0);
7190 snprintf(buf2, sizeof(buf2), "(%s)%s", float_type, buf1);
7191 if (need_float_stack) {
7192 fprintf(fout, " f_st[--f_stp & 7] = %s;", buf2);
7193 }
7194 else {
7195 if (po->flags & OPF_FSHIFT)
7196 fprintf(fout, " f_st1 = f_st0;");
7197 fprintf(fout, " f_st0 = %s;", buf2);
7198 }
d4a985bd 7199 strcat(g_comment, " fild");
7200 break;
7201
7202 case OP_FLDc:
fe18df39 7203 if (need_float_stack)
7204 fprintf(fout, " f_st[--f_stp & 7] = ");
7205 else {
7206 if (po->flags & OPF_FSHIFT)
7207 fprintf(fout, " f_st1 = f_st0;");
7208 fprintf(fout, " f_st0 = ");
7209 }
d4a985bd 7210 switch (po->operand[0].val) {
fe18df39 7211 case X87_CONST_1: fprintf(fout, "1.0;"); break;
7212 case X87_CONST_LN2: fprintf(fout, "0.693147180559945;"); break;
7213 case X87_CONST_Z: fprintf(fout, "0.0;"); break;
d4a985bd 7214 default: ferr(po, "TODO\n"); break;
7215 }
7216 break;
7217
7218 case OP_FST:
30620174 7219 if (po->flags & OPF_FARG) {
7220 // store to stack as func arg
7221 snprintf(buf1, sizeof(buf1), "fs_%d", po->p_argnum);
7222 dead_dst = 0;
7223 }
7224 else {
7225 out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0],
7226 need_float_stack);
7227 dead_dst = po->operand[0].type == OPT_REG
7228 && po->operand[0].reg == xST0;
fe18df39 7229 }
30620174 7230 if (!dead_dst)
7231 fprintf(fout, " %s = %s;", buf1, float_st0);
16057ce1 7232 if (po->flags & OPF_FSHIFT) {
7233 if (need_float_stack)
7234 fprintf(fout, " f_stp++;");
7235 else
fe18df39 7236 fprintf(fout, " f_st0 = f_st1;");
d4a985bd 7237 }
fe18df39 7238 if (dead_dst && !(po->flags & OPF_FSHIFT))
7239 no_output = 1;
7240 else
7241 strcat(g_comment, " fst");
d4a985bd 7242 break;
7243
16057ce1 7244 case OP_FIST:
7245 fprintf(fout, " %s = %s%s;",
7246 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
7247 lmod_cast(po, po->operand[0].lmod, 1), float_st0);
7248 if (po->flags & OPF_FSHIFT) {
7249 if (need_float_stack)
7250 fprintf(fout, " f_stp++;");
7251 else
7252 fprintf(fout, " f_st0 = f_st1;");
7253 }
7254 strcat(g_comment, " fist");
7255 break;
7256
d4a985bd 7257 case OP_FADD:
7258 case OP_FDIV:
7259 case OP_FMUL:
7260 case OP_FSUB:
fe18df39 7261 out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0],
7262 need_float_stack);
7263 out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1],
7264 need_float_stack);
7265 dead_dst = (po->flags & OPF_FPOP)
7266 && po->operand[0].type == OPT_REG
7267 && po->operand[0].reg == xST0;
d4a985bd 7268 switch (po->op) {
7269 case OP_FADD: j = '+'; break;
7270 case OP_FDIV: j = '/'; break;
7271 case OP_FMUL: j = '*'; break;
7272 case OP_FSUB: j = '-'; break;
7273 default: j = 'x'; break;
7274 }
fe18df39 7275 if (need_float_stack) {
7276 if (!dead_dst)
7277 fprintf(fout, " %s %c= %s;", buf1, j, buf2);
7278 if (po->flags & OPF_FSHIFT)
7279 fprintf(fout, " f_stp++;");
d4a985bd 7280 }
7281 else {
fe18df39 7282 if (po->flags & OPF_FSHIFT) {
7283 // note: assumes only 2 regs handled
7284 if (!dead_dst)
7285 fprintf(fout, " f_st0 = f_st1 %c f_st0;", j);
7286 else
7287 fprintf(fout, " f_st0 = f_st1;");
7288 }
7289 else if (!dead_dst)
7290 fprintf(fout, " %s %c= %s;", buf1, j, buf2);
d4a985bd 7291 }
fe18df39 7292 no_output = (dead_dst && !(po->flags & OPF_FSHIFT));
d4a985bd 7293 break;
7294
7295 case OP_FDIVR:
7296 case OP_FSUBR:
fe18df39 7297 out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0],
7298 need_float_stack);
7299 out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1],
7300 need_float_stack);
7301 out_src_opr_float(buf3, sizeof(buf3), po, &po->operand[0],
7302 need_float_stack);
7303 dead_dst = (po->flags & OPF_FPOP)
7304 && po->operand[0].type == OPT_REG
7305 && po->operand[0].reg == xST0;
7306 j = po->op == OP_FDIVR ? '/' : '-';
7307 if (need_float_stack) {
7308 if (!dead_dst)
7309 fprintf(fout, " %s = %s %c %s;", buf1, buf2, j, buf3);
7310 if (po->flags & OPF_FSHIFT)
7311 fprintf(fout, " f_stp++;");
7312 }
7313 else {
7314 if (po->flags & OPF_FSHIFT) {
7315 if (!dead_dst)
7316 fprintf(fout, " f_st0 = f_st0 %c f_st1;", j);
7317 else
7318 fprintf(fout, " f_st0 = f_st1;");
7319 }
7320 else if (!dead_dst)
7321 fprintf(fout, " %s = %s %c %s;", buf1, buf2, j, buf3);
7322 }
7323 no_output = (dead_dst && !(po->flags & OPF_FSHIFT));
d4a985bd 7324 break;
7325
7326 case OP_FIADD:
7327 case OP_FIDIV:
7328 case OP_FIMUL:
7329 case OP_FISUB:
7330 switch (po->op) {
7331 case OP_FIADD: j = '+'; break;
7332 case OP_FIDIV: j = '/'; break;
7333 case OP_FIMUL: j = '*'; break;
7334 case OP_FISUB: j = '-'; break;
7335 default: j = 'x'; break;
7336 }
fe18df39 7337 fprintf(fout, " %s %c= (%s)%s;", float_st0,
7338 j, float_type,
d4a985bd 7339 out_src_opr(buf1, sizeof(buf1), po, &po->operand[0],
7340 lmod_cast(po, po->operand[0].lmod, 1), 0));
7341 break;
7342
7343 case OP_FIDIVR:
7344 case OP_FISUBR:
fe18df39 7345 fprintf(fout, " %s = %s %c %s;", float_st0,
7346 out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1],
7347 need_float_stack),
7348 po->op == OP_FIDIVR ? '/' : '-', float_st0);
7349 break;
7350
16057ce1 7351 case OP_FCOM: {
7352 int mask, z_check;
7353 ferr_assert(po, po->datap != NULL);
7354 mask = (long)po->datap & 0xffff;
7355 z_check = ((long)po->datap >> 16) & 1;
7356 out_src_opr_float(buf1, sizeof(buf1), po, &po->operand[0],
7357 need_float_stack);
7358 if (mask == 0x0100) { // C0 -> <
7359 fprintf(fout, " f_sw = %s < %s ? 0x0100 : 0;",
7360 float_st0, buf1);
7361 }
7362 else if (mask == 0x4000) { // C3 -> =
7363 fprintf(fout, " f_sw = %s == %s ? 0x4000 : 0;",
7364 float_st0, buf1);
7365 }
7366 else if (mask == 0x4100) { // C3, C0
7367 if (z_check) {
7368 fprintf(fout, " f_sw = %s <= %s ? 0x4100 : 0;",
7369 float_st0, buf1);
7370 strcat(g_comment, " z_chk_det");
7371 }
7372 else {
7373 fprintf(fout, " f_sw = %s == %s ? 0x4000 : "
7374 "(%s < %s ? 0x0100 : 0);",
7375 float_st0, buf1, float_st0, buf1);
7376 }
7377 }
7378 else
7379 ferr(po, "unhandled sw mask: %x\n", mask);
7380 if (po->flags & OPF_FSHIFT) {
7381 if (need_float_stack)
7382 fprintf(fout, " f_stp++;");
7383 else
7384 fprintf(fout, " f_st0 = f_st1;");
7385 }
7386 break;
7387 }
7388
7389 case OP_FNSTSW:
7390 fprintf(fout, " %s = f_sw;",
7391 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
7392 break;
7393
fe18df39 7394 case OP_FCHS:
7395 fprintf(fout, " %s = -%s;", float_st0, float_st0);
d4a985bd 7396 break;
7397
497a6d6b 7398 case OP_FCOS:
fe18df39 7399 fprintf(fout, " %s = cos%s(%s);", float_st0,
7400 need_double ? "" : "f", float_st0);
497a6d6b 7401 break;
7402
7403 case OP_FPATAN:
fe18df39 7404 if (need_float_stack) {
7405 fprintf(fout, " %s = atan%s(%s / %s);", float_st1,
7406 need_double ? "" : "f", float_st1, float_st0);
7407 fprintf(fout, " f_stp++;");
7408 }
7409 else {
7410 fprintf(fout, " f_st0 = atan%s(f_st1 / f_st0);",
7411 need_double ? "" : "f");
7412 }
7413 break;
7414
7415 case OP_FYL2X:
7416 if (need_float_stack) {
7417 fprintf(fout, " %s = %s * log2%s(%s);", float_st1,
7418 float_st1, need_double ? "" : "f", float_st0);
7419 fprintf(fout, " f_stp++;");
7420 }
7421 else {
7422 fprintf(fout, " f_st0 = f_st1 * log2%s(f_st0);",
7423 need_double ? "" : "f");
7424 }
8c83cc48 7425 strcat(g_comment, " fyl2x");
497a6d6b 7426 break;
7427
7428 case OP_FSIN:
fe18df39 7429 fprintf(fout, " %s = sin%s(%s);", float_st0,
7430 need_double ? "" : "f", float_st0);
497a6d6b 7431 break;
7432
7433 case OP_FSQRT:
fe18df39 7434 fprintf(fout, " %s = sqrt%s(%s);", float_st0,
7435 need_double ? "" : "f", float_st0);
7436 break;
7437
7438 case OP_FXCH:
7439 dead_dst = po->operand[0].type == OPT_REG
7440 && po->operand[0].reg == xST0;
7441 if (!dead_dst) {
7442 out_src_opr_float(buf1, sizeof(buf1), po, &po->operand[0],
7443 need_float_stack);
7444 fprintf(fout, " { %s t = %s; %s = %s; %s = t; }", float_type,
7445 float_st0, float_st0, buf1, buf1);
7446 strcat(g_comment, " fxch");
7447 }
7448 else
7449 no_output = 1;
497a6d6b 7450 break;
7451
d4a985bd 7452 case OPP_FTOL:
7453 ferr_assert(po, po->flags & OPF_32BIT);
fe18df39 7454 fprintf(fout, " eax = (s32)%s;", float_st0);
7455 if (po->flags & OPF_FSHIFT) {
7456 if (need_float_stack)
7457 fprintf(fout, " f_stp++;");
7458 else
7459 fprintf(fout, " f_st0 = f_st1;");
7460 }
d4a985bd 7461 strcat(g_comment, " ftol");
7462 break;
7463
8c83cc48 7464 case OPP_CIPOW:
7465 if (need_float_stack) {
7466 fprintf(fout, " %s = pow%s(%s, %s);", float_st1,
7467 need_double ? "" : "f", float_st1, float_st0);
7468 fprintf(fout, " f_stp++;");
7469 }
7470 else {
7471 fprintf(fout, " f_st0 = pow%s(f_st1, f_st0);",
7472 need_double ? "" : "f");
7473 }
7474 strcat(g_comment, " CIpow");
7475 break;
7476
11437ea1 7477 case OPP_ABORT:
7478 fprintf(fout, " do_skip_code_abort();");
7479 break;
7480
90307a99 7481 // mmx
7482 case OP_EMMS:
11437ea1 7483 fprintf(fout, " do_emms();");
90307a99 7484 break;
7485
91977a1c 7486 default:
7487 no_output = 1;
69a3cdfc 7488 ferr(po, "unhandled op type %d, flags %x\n",
7489 po->op, po->flags);
91977a1c 7490 break;
7491 }
7492
7493 if (g_comment[0] != 0) {
ddaf8bd7 7494 char *p = g_comment;
7495 while (my_isblank(*p))
7496 p++;
7497 fprintf(fout, " // %s", p);
91977a1c 7498 g_comment[0] = 0;
7499 no_output = 0;
7500 }
7501 if (!no_output)
7502 fprintf(fout, "\n");
5101a5f9 7503
2b43685d 7504 // some sanity checking
591721d7 7505 if (po->flags & OPF_REP) {
7506 if (po->op != OP_STOS && po->op != OP_MOVS
7507 && po->op != OP_CMPS && po->op != OP_SCAS)
7508 ferr(po, "unexpected rep\n");
7509 if (!(po->flags & (OPF_REPZ|OPF_REPNZ))
7510 && (po->op == OP_CMPS || po->op == OP_SCAS))
7511 ferr(po, "cmps/scas with plain rep\n");
7512 }
7513 if ((po->flags & (OPF_REPZ|OPF_REPNZ))
7514 && po->op != OP_CMPS && po->op != OP_SCAS)
2b43685d 7515 ferr(po, "unexpected repz/repnz\n");
7516
940e8e66 7517 if (pfomask != 0)
7ba45c34 7518 ferr(po, "missed flag calc, pfomask=%x\n", pfomask);
940e8e66 7519
5101a5f9 7520 // see is delayed flag stuff is still valid
7521 if (delayed_flag_op != NULL && delayed_flag_op != po) {
89ff3147 7522 if (is_any_opr_modified(delayed_flag_op, po, 0))
5101a5f9 7523 delayed_flag_op = NULL;
7524 }
7525
7526 if (last_arith_dst != NULL && last_arith_dst != &po->operand[0]) {
7527 if (is_opr_modified(last_arith_dst, po))
7528 last_arith_dst = NULL;
7529 }
3ebea2cf 7530
7531 label_pending = 0;
91977a1c 7532 }
7533
a2c1d768 7534 if (g_stack_fsz && !g_stack_frame_used)
7535 fprintf(fout, " (void)sf;\n");
7536
91977a1c 7537 fprintf(fout, "}\n\n");
7538
9af2d373 7539 gen_x_cleanup(opcnt);
7540}
7541
7542static void gen_x_cleanup(int opcnt)
7543{
7544 int i;
7545
91977a1c 7546 for (i = 0; i < opcnt; i++) {
4c45fa73 7547 struct label_ref *lr, *lr_del;
7548
7549 lr = g_label_refs[i].next;
7550 while (lr != NULL) {
7551 lr_del = lr;
7552 lr = lr->next;
7553 free(lr_del);
7554 }
7555 g_label_refs[i].i = -1;
7556 g_label_refs[i].next = NULL;
7557
91977a1c 7558 if (ops[i].op == OP_CALL) {
092f64e1 7559 if (ops[i].pp)
7560 proto_release(ops[i].pp);
91977a1c 7561 }
7562 }
bd96f656 7563 g_func_pp = NULL;
91977a1c 7564}
c36e914d 7565
92d715b6 7566struct func_proto_dep;
7567
7568struct func_prototype {
7569 char name[NAMELEN];
7570 int id;
7571 int argc_stack;
7572 int regmask_dep;
91ca764a 7573 int has_ret:3; // -1, 0, 1: unresolved, no, yes
92d715b6 7574 unsigned int dep_resolved:1;
7575 unsigned int is_stdcall:1;
7576 struct func_proto_dep *dep_func;
7577 int dep_func_cnt;
91ca764a 7578 const struct parsed_proto *pp; // seed pp, if any
92d715b6 7579};
7580
7581struct func_proto_dep {
7582 char *name;
7583 struct func_prototype *proto;
7584 int regmask_live; // .. at the time of call
7585 unsigned int ret_dep:1; // return from this is caller's return
7586};
7587
7588static struct func_prototype *hg_fp;
7589static int hg_fp_cnt;
7590
5fa1256f 7591static struct scanned_var {
7592 char name[NAMELEN];
7593 enum opr_lenmod lmod;
7594 unsigned int is_seeded:1;
61e29183 7595 unsigned int is_c_str:1;
c0de9015 7596 const struct parsed_proto *pp; // seed pp, if any
5fa1256f 7597} *hg_vars;
7598static int hg_var_cnt;
7599
8c999988 7600static char **hg_refs;
7601static int hg_ref_cnt;
7602
9af2d373 7603static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
7604 int count);
7605
8c999988 7606static struct func_prototype *hg_fp_add(const char *funcn)
ebc4dc43 7607{
7608 struct func_prototype *fp;
7609
7610 if ((hg_fp_cnt & 0xff) == 0) {
7611 hg_fp = realloc(hg_fp, sizeof(hg_fp[0]) * (hg_fp_cnt + 0x100));
7612 my_assert_not(hg_fp, NULL);
7613 memset(hg_fp + hg_fp_cnt, 0, sizeof(hg_fp[0]) * 0x100);
7614 }
7615
7616 fp = &hg_fp[hg_fp_cnt];
7617 snprintf(fp->name, sizeof(fp->name), "%s", funcn);
7618 fp->id = hg_fp_cnt;
7619 fp->argc_stack = -1;
7620 hg_fp_cnt++;
7621
7622 return fp;
7623}
7624
92d715b6 7625static struct func_proto_dep *hg_fp_find_dep(struct func_prototype *fp,
7626 const char *name)
7627{
7628 int i;
7629
7630 for (i = 0; i < fp->dep_func_cnt; i++)
7631 if (IS(fp->dep_func[i].name, name))
7632 return &fp->dep_func[i];
7633
7634 return NULL;
7635}
7636
7637static void hg_fp_add_dep(struct func_prototype *fp, const char *name)
7638{
7639 // is it a dupe?
7640 if (hg_fp_find_dep(fp, name))
7641 return;
7642
7643 if ((fp->dep_func_cnt & 0xff) == 0) {
7644 fp->dep_func = realloc(fp->dep_func,
7645 sizeof(fp->dep_func[0]) * (fp->dep_func_cnt + 0x100));
7646 my_assert_not(fp->dep_func, NULL);
7647 memset(&fp->dep_func[fp->dep_func_cnt], 0,
7648 sizeof(fp->dep_func[0]) * 0x100);
7649 }
7650 fp->dep_func[fp->dep_func_cnt].name = strdup(name);
7651 fp->dep_func_cnt++;
7652}
7653
7654static int hg_fp_cmp_name(const void *p1_, const void *p2_)
7655{
7656 const struct func_prototype *p1 = p1_, *p2 = p2_;
7657 return strcmp(p1->name, p2->name);
7658}
7659
7660#if 0
7661static int hg_fp_cmp_id(const void *p1_, const void *p2_)
7662{
7663 const struct func_prototype *p1 = p1_, *p2 = p2_;
7664 return p1->id - p2->id;
7665}
7666#endif
7667
8c999988 7668static void hg_ref_add(const char *name)
7669{
7670 if ((hg_ref_cnt & 0xff) == 0) {
7671 hg_refs = realloc(hg_refs, sizeof(hg_refs[0]) * (hg_ref_cnt + 0x100));
7672 my_assert_not(hg_refs, NULL);
7673 memset(hg_refs + hg_ref_cnt, 0, sizeof(hg_refs[0]) * 0x100);
7674 }
7675
7676 hg_refs[hg_ref_cnt] = strdup(name);
7677 my_assert_not(hg_refs[hg_ref_cnt], NULL);
7678 hg_ref_cnt++;
7679}
7680
91ca764a 7681// recursive register dep pass
7682// - track saved regs (part 2)
7683// - try to figure out arg-regs
7684// - calculate reg deps
7685static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits,
7686 struct func_prototype *fp, int regmask_save, int regmask_dst,
7687 int *regmask_dep, int *has_ret)
7688{
7689 struct func_proto_dep *dep;
7690 struct parsed_op *po;
7691 int from_caller = 0;
91ca764a 7692 int j, l;
7693 int reg;
7694 int ret;
7695
7696 for (; i < opcnt; i++)
7697 {
7698 if (cbits[i >> 3] & (1 << (i & 7)))
7699 return;
7700 cbits[i >> 3] |= (1 << (i & 7));
7701
7702 po = &ops[i];
7703
7704 if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
04abc5d6 7705 if (po->flags & OPF_RMD)
7706 continue;
7707
91ca764a 7708 if (po->btj != NULL) {
7709 // jumptable
7710 for (j = 0; j < po->btj->count; j++) {
db63af51 7711 check_i(po, po->btj->d[j].bt_i);
91ca764a 7712 gen_hdr_dep_pass(po->btj->d[j].bt_i, opcnt, cbits, fp,
7713 regmask_save, regmask_dst, regmask_dep, has_ret);
7714 }
7715 return;
7716 }
7717
db63af51 7718 check_i(po, po->bt_i);
91ca764a 7719 if (po->flags & OPF_CJMP) {
7720 gen_hdr_dep_pass(po->bt_i, opcnt, cbits, fp,
7721 regmask_save, regmask_dst, regmask_dep, has_ret);
7722 }
7723 else {
7724 i = po->bt_i - 1;
7725 }
7726 continue;
7727 }
7728
7729 if (po->flags & OPF_FARG)
7730 /* (just calculate register deps) */;
7731 else if (po->op == OP_PUSH && po->operand[0].type == OPT_REG)
7732 {
7733 reg = po->operand[0].reg;
b2bd20c0 7734 ferr_assert(po, reg >= 0);
91ca764a 7735
7736 if (po->flags & OPF_RSAVE) {
7737 regmask_save |= 1 << reg;
7738 continue;
7739 }
7740 if (po->flags & OPF_DONE)
7741 continue;
7742
93b5bd18 7743 ret = scan_for_pop(i + 1, opcnt, i + opcnt * 2, reg, 0, 0, 0);
91ca764a 7744 if (ret == 1) {
7745 regmask_save |= 1 << reg;
7746 po->flags |= OPF_RMD;
93b5bd18 7747 scan_for_pop(i + 1, opcnt, i + opcnt * 3, reg, 0, 0, OPF_RMD);
91ca764a 7748 continue;
7749 }
7750 }
7751 else if (po->flags & OPF_RMD)
7752 continue;
7753 else if (po->op == OP_CALL) {
7754 po->regmask_dst |= 1 << xAX;
7755
7756 dep = hg_fp_find_dep(fp, po->operand[0].name);
e627c4d0 7757 if (dep != NULL) {
91ca764a 7758 dep->regmask_live = regmask_save | regmask_dst;
e627c4d0 7759 if (g_bp_frame && !(po->flags & OPF_EBP_S))
7760 dep->regmask_live |= 1 << xBP;
7761 }
91ca764a 7762 }
7763 else if (po->op == OP_RET) {
7764 if (po->operand_cnt > 0) {
7765 fp->is_stdcall = 1;
7766 if (fp->argc_stack >= 0
7767 && fp->argc_stack != po->operand[0].val / 4)
7768 ferr(po, "ret mismatch? (%d)\n", fp->argc_stack * 4);
7769 fp->argc_stack = po->operand[0].val / 4;
7770 }
7771 }
7772
ebc4dc43 7773 // if has_ret is 0, there is uninitialized eax path,
7774 // which means it's most likely void func
91ca764a 7775 if (*has_ret != 0 && (po->flags & OPF_TAIL)) {
7776 if (po->op == OP_CALL) {
7777 j = i;
7778 ret = 1;
7779 }
7780 else {
b2bd20c0 7781 struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xAX);
91ca764a 7782 j = -1;
7783 from_caller = 0;
7784 ret = resolve_origin(i, &opr, i + opcnt * 4, &j, &from_caller);
7785 }
7786
ebc4dc43 7787 if (ret != 1 && from_caller) {
91ca764a 7788 // unresolved eax - probably void func
7789 *has_ret = 0;
7790 }
7791 else {
ebc4dc43 7792 if (j >= 0 && ops[j].op == OP_CALL) {
7793 dep = hg_fp_find_dep(fp, ops[j].operand[0].name);
91ca764a 7794 if (dep != NULL)
7795 dep->ret_dep = 1;
7796 else
7797 *has_ret = 1;
7798 }
7799 else
7800 *has_ret = 1;
7801 }
7802 }
7803
7804 l = regmask_save | regmask_dst;
7805 if (g_bp_frame && !(po->flags & OPF_EBP_S))
7806 l |= 1 << xBP;
7807
7808 l = po->regmask_src & ~l;
7809#if 0
7810 if (l)
7811 fnote(po, "dep |= %04x, dst %04x, save %04x (f %x)\n",
7812 l, regmask_dst, regmask_save, po->flags);
7813#endif
7814 *regmask_dep |= l;
7815 regmask_dst |= po->regmask_dst;
7816
7817 if (po->flags & OPF_TAIL)
7818 return;
7819 }
7820}
7821
92d715b6 7822static void gen_hdr(const char *funcn, int opcnt)
7823{
91ca764a 7824 unsigned char cbits[MAX_OPS / 8];
ebc4dc43 7825 const struct parsed_proto *pp_c;
9af2d373 7826 struct parsed_proto *pp;
92d715b6 7827 struct func_prototype *fp;
92d715b6 7828 struct parsed_op *po;
26677139 7829 int regmask_dummy = 0;
91ca764a 7830 int regmask_dep;
92d715b6 7831 int max_bp_offset = 0;
91ca764a 7832 int has_ret;
bfacdc83 7833 int i, j, l;
7834 int ret;
92d715b6 7835
ebc4dc43 7836 pp_c = proto_parse(g_fhdr, funcn, 1);
7837 if (pp_c != NULL)
7838 // already in seed, will add to hg_fp later
9af2d373 7839 return;
ebc4dc43 7840
7841 fp = hg_fp_add(funcn);
9af2d373 7842
7843 g_bp_frame = g_sp_frame = g_stack_fsz = 0;
7844 g_stack_frame_used = 0;
7845
92d715b6 7846 // pass1:
66bdb2b0 7847 // - resolve all branches
7848 // - parse calls with labels
7849 resolve_branches_parse_calls(opcnt);
7850
7851 // pass2:
9af2d373 7852 // - handle ebp/esp frame, remove ops related to it
7853 scan_prologue_epilogue(opcnt);
7854
66bdb2b0 7855 // pass3:
7856 // - remove dead labels
92d715b6 7857 // - collect calls
92d715b6 7858 for (i = 0; i < opcnt; i++)
7859 {
66bdb2b0 7860 if (g_labels[i] != NULL && g_label_refs[i].i == -1) {
7861 free(g_labels[i]);
7862 g_labels[i] = NULL;
7863 }
92d715b6 7864
66bdb2b0 7865 po = &ops[i];
5e49b270 7866 if (po->flags & (OPF_RMD|OPF_DONE))
92d715b6 7867 continue;
7868
7869 if (po->op == OP_CALL) {
66bdb2b0 7870 if (po->operand[0].type == OPT_LABEL)
7871 hg_fp_add_dep(fp, opr_name(po, 0));
7872 else if (po->pp != NULL)
7873 hg_fp_add_dep(fp, po->pp->name);
92d715b6 7874 }
92d715b6 7875 }
7876
66bdb2b0 7877 // pass4:
92d715b6 7878 // - remove dead labels
9af2d373 7879 // - handle push <const>/pop pairs
92d715b6 7880 for (i = 0; i < opcnt; i++)
7881 {
d7857c3a 7882 if (g_labels[i] != NULL && g_label_refs[i].i == -1) {
7883 free(g_labels[i]);
7884 g_labels[i] = NULL;
7885 }
9af2d373 7886
91ca764a 7887 po = &ops[i];
7888 if (po->flags & (OPF_RMD|OPF_DONE))
7889 continue;
7890
7891 if (po->op == OP_PUSH && po->operand[0].type == OPT_CONST)
e83ea7ed 7892 scan_for_pop_const(i, opcnt, i + opcnt * 13);
91ca764a 7893 }
7894
66bdb2b0 7895 // pass5:
91ca764a 7896 // - process trivial calls
7897 for (i = 0; i < opcnt; i++)
7898 {
9af2d373 7899 po = &ops[i];
5e49b270 7900 if (po->flags & (OPF_RMD|OPF_DONE))
9af2d373 7901 continue;
7902
26677139 7903 if (po->op == OP_CALL)
7904 {
7905 pp = process_call_early(i, opcnt, &j);
7906 if (pp != NULL) {
7907 if (!(po->flags & OPF_ATAIL))
7908 // since we know the args, try to collect them
30620174 7909 if (collect_call_args_early(i, pp, NULL, NULL) != 0)
26677139 7910 pp = NULL;
7911 }
7912
7913 if (pp != NULL) {
7914 if (j >= 0) {
7915 // commit esp adjust
5e49b270 7916 if (ops[j].op != OP_POP)
7917 patch_esp_adjust(&ops[j], pp->argc_stack * 4);
bfacdc83 7918 else {
7919 for (l = 0; l < pp->argc_stack; l++)
b2bd20c0 7920 ops[j + l].flags |= OPF_DONE | OPF_RMD | OPF_NOREGS;
bfacdc83 7921 }
26677139 7922 }
7923
7924 po->flags |= OPF_DONE;
7925 }
7926 }
26677139 7927 }
7928
66bdb2b0 7929 // pass6:
5e49b270 7930 // - track saved regs (simple)
26677139 7931 // - process calls
7932 for (i = 0; i < opcnt; i++)
7933 {
7934 po = &ops[i];
5e49b270 7935 if (po->flags & (OPF_RMD|OPF_DONE))
26677139 7936 continue;
7937
e83ea7ed 7938 if (po->op == OP_PUSH && po->operand[0].type == OPT_REG
7939 && po->operand[0].reg != xCX)
5e49b270 7940 {
e83ea7ed 7941 ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, 0);
b2bd20c0 7942 if (ret == 1) {
91ca764a 7943 // regmask_save |= 1 << po->operand[0].reg; // do it later
7944 po->flags |= OPF_RSAVE | OPF_RMD | OPF_DONE;
e83ea7ed 7945 scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, OPF_RMD);
5e49b270 7946 }
7947 }
e83ea7ed 7948 else if (po->op == OP_CALL)
26677139 7949 {
9af2d373 7950 pp = process_call(i, opcnt);
7951
7952 if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) {
9af2d373 7953 // since we know the args, collect them
8c83cc48 7954 ret = collect_call_args(po, i, pp, &regmask_dummy,
7955 i + opcnt * 1);
9af2d373 7956 }
7957 }
92d715b6 7958 }
7959
66bdb2b0 7960 // pass7
91ca764a 7961 memset(cbits, 0, sizeof(cbits));
7962 regmask_dep = 0;
7963 has_ret = -1;
7964
7965 gen_hdr_dep_pass(0, opcnt, cbits, fp, 0, 0, &regmask_dep, &has_ret);
7966
7967 // find unreachable code - must be fixed in IDA
92d715b6 7968 for (i = 0; i < opcnt; i++)
7969 {
91ca764a 7970 if (cbits[i >> 3] & (1 << (i & 7)))
9af2d373 7971 continue;
92d715b6 7972
04abc5d6 7973 if (g_labels[i] == NULL && i > 0 && ops[i - 1].op == OP_CALL
7974 && ops[i - 1].pp != NULL && ops[i - 1].pp->is_osinc)
7975 {
7976 // the compiler sometimes still generates code after
7977 // noreturn OS functions
7978 break;
7979 }
ea43585b 7980 if (ops[i].op != OP_NOP && ops[i].op != OPP_ABORT)
91ca764a 7981 ferr(&ops[i], "unreachable code\n");
92d715b6 7982 }
7983
9af2d373 7984 for (i = 0; i < g_eqcnt; i++) {
92d715b6 7985 if (g_eqs[i].offset > max_bp_offset && g_eqs[i].offset < 4*32)
7986 max_bp_offset = g_eqs[i].offset;
9af2d373 7987 }
92d715b6 7988
9af2d373 7989 if (fp->argc_stack < 0) {
92d715b6 7990 max_bp_offset = (max_bp_offset + 3) & ~3;
9af2d373 7991 fp->argc_stack = max_bp_offset / 4;
7992 if ((g_ida_func_attr & IDAFA_BP_FRAME) && fp->argc_stack > 0)
92d715b6 7993 fp->argc_stack--;
7994 }
7995
622eb2ef 7996 fp->regmask_dep = regmask_dep & ~((1 << xSP) | mxSTa);
92d715b6 7997 fp->has_ret = has_ret;
91ca764a 7998#if 0
7999 printf("// has_ret %d, regmask_dep %x\n",
8000 fp->has_ret, fp->regmask_dep);
8001 output_hdr_fp(stdout, fp, 1);
ebc4dc43 8002 if (IS(funcn, "sub_10007F72")) exit(1);
91ca764a 8003#endif
9af2d373 8004
8005 gen_x_cleanup(opcnt);
92d715b6 8006}
8007
8008static void hg_fp_resolve_deps(struct func_prototype *fp)
8009{
8010 struct func_prototype fp_s;
91ca764a 8011 int dep;
92d715b6 8012 int i;
8013
8014 // this thing is recursive, so mark first..
8015 fp->dep_resolved = 1;
8016
8017 for (i = 0; i < fp->dep_func_cnt; i++) {
8018 strcpy(fp_s.name, fp->dep_func[i].name);
8019 fp->dep_func[i].proto = bsearch(&fp_s, hg_fp, hg_fp_cnt,
8020 sizeof(hg_fp[0]), hg_fp_cmp_name);
8021 if (fp->dep_func[i].proto != NULL) {
8022 if (!fp->dep_func[i].proto->dep_resolved)
8023 hg_fp_resolve_deps(fp->dep_func[i].proto);
8024
91ca764a 8025 dep = ~fp->dep_func[i].regmask_live
8026 & fp->dep_func[i].proto->regmask_dep;
8027 fp->regmask_dep |= dep;
8028 // printf("dep %s %s |= %x\n", fp->name,
8029 // fp->dep_func[i].name, dep);
92d715b6 8030
ebc4dc43 8031 if (fp->has_ret == -1 && fp->dep_func[i].ret_dep)
92d715b6 8032 fp->has_ret = fp->dep_func[i].proto->has_ret;
8033 }
8034 }
8035}
8036
8c999988 8037// make all thiscall/edx arg functions referenced from .data fastcall
8038static void do_func_refs_from_data(void)
8039{
8040 struct func_prototype *fp, fp_s;
8041 int i;
8042
8043 for (i = 0; i < hg_ref_cnt; i++) {
8044 strcpy(fp_s.name, hg_refs[i]);
8045 fp = bsearch(&fp_s, hg_fp, hg_fp_cnt,
8046 sizeof(hg_fp[0]), hg_fp_cmp_name);
8047 if (fp == NULL)
8048 continue;
8049
8050 if (fp->argc_stack != 0 && (fp->regmask_dep & (mxCX | mxDX)))
8051 fp->regmask_dep |= mxCX | mxDX;
8052 }
8053}
8054
9af2d373 8055static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
8056 int count)
92d715b6 8057{
61e29183 8058 const struct parsed_proto *pp;
8059 char *p, namebuf[NAMELEN];
8060 const char *name;
92d715b6 8061 int regmask_dep;
226e8df1 8062 int argc_normal;
9af2d373 8063 int j, arg;
92d715b6 8064
9af2d373 8065 for (; count > 0; count--, fp++) {
92d715b6 8066 if (fp->has_ret == -1)
8067 fprintf(fout, "// ret unresolved\n");
8068#if 0
8069 fprintf(fout, "// dep:");
8070 for (j = 0; j < fp->dep_func_cnt; j++) {
8071 fprintf(fout, " %s/", fp->dep_func[j].name);
8072 if (fp->dep_func[j].proto != NULL)
8073 fprintf(fout, "%04x/%d", fp->dep_func[j].proto->regmask_dep,
8074 fp->dep_func[j].proto->has_ret);
8075 }
8076 fprintf(fout, "\n");
8077#endif
8078
61e29183 8079 p = strchr(fp->name, '@');
8080 if (p != NULL) {
8081 memcpy(namebuf, fp->name, p - fp->name);
8082 namebuf[p - fp->name] = 0;
8083 name = namebuf;
8084 }
8085 else
8086 name = fp->name;
8087 if (name[0] == '_')
8088 name++;
8089
8090 pp = proto_parse(g_fhdr, name, 1);
8091 if (pp != NULL && pp->is_include)
8092 continue;
8093
c0de9015 8094 if (fp->pp != NULL) {
4e81a3a2 8095 // part of seed, output later
8096 continue;
c0de9015 8097 }
8098
92d715b6 8099 regmask_dep = fp->regmask_dep;
226e8df1 8100 argc_normal = fp->argc_stack;
92d715b6 8101
91ca764a 8102 fprintf(fout, "%-5s", fp->pp ? fp->pp->ret_type.name :
8103 (fp->has_ret ? "int" : "void"));
226e8df1 8104 if (regmask_dep && (fp->is_stdcall || fp->argc_stack > 0)
8105 && (regmask_dep & ~mxCX) == 0)
8106 {
8107 fprintf(fout, "/*__thiscall*/ ");
8108 argc_normal++;
8109 regmask_dep = 0;
8110 }
8111 else if (regmask_dep && (fp->is_stdcall || fp->argc_stack == 0)
8112 && (regmask_dep & ~(mxCX | mxDX)) == 0)
92d715b6 8113 {
9af2d373 8114 fprintf(fout, " __fastcall ");
226e8df1 8115 if (!(regmask_dep & (1 << xDX)) && fp->argc_stack == 0)
8116 argc_normal = 1;
92d715b6 8117 else
226e8df1 8118 argc_normal += 2;
92d715b6 8119 regmask_dep = 0;
8120 }
8121 else if (regmask_dep && !fp->is_stdcall) {
8122 fprintf(fout, "/*__usercall*/ ");
92d715b6 8123 }
8124 else if (regmask_dep) {
8125 fprintf(fout, "/*__userpurge*/ ");
92d715b6 8126 }
8127 else if (fp->is_stdcall)
9af2d373 8128 fprintf(fout, " __stdcall ");
92d715b6 8129 else
9af2d373 8130 fprintf(fout, " __cdecl ");
92d715b6 8131
61e29183 8132 fprintf(fout, "%s(", name);
92d715b6 8133
8134 arg = 0;
8135 for (j = 0; j < xSP; j++) {
8136 if (regmask_dep & (1 << j)) {
8137 arg++;
8138 if (arg != 1)
8139 fprintf(fout, ", ");
91ca764a 8140 if (fp->pp != NULL)
8141 fprintf(fout, "%s", fp->pp->arg[arg - 1].type.name);
8142 else
8143 fprintf(fout, "int");
8144 fprintf(fout, " a%d/*<%s>*/", arg, regs_r32[j]);
92d715b6 8145 }
8146 }
8147
226e8df1 8148 for (j = 0; j < argc_normal; j++) {
92d715b6 8149 arg++;
8150 if (arg != 1)
8151 fprintf(fout, ", ");
91ca764a 8152 if (fp->pp != NULL) {
8153 fprintf(fout, "%s", fp->pp->arg[arg - 1].type.name);
8154 if (!fp->pp->arg[arg - 1].type.is_ptr)
8155 fprintf(fout, " ");
8156 }
8157 else
8158 fprintf(fout, "int ");
8159 fprintf(fout, "a%d", arg);
92d715b6 8160 }
8161
8162 fprintf(fout, ");\n");
8163 }
8164}
8165
9af2d373 8166static void output_hdr(FILE *fout)
8167{
5fa1256f 8168 static const char *lmod_c_names[] = {
8169 [OPLM_UNSPEC] = "???",
8170 [OPLM_BYTE] = "uint8_t",
8171 [OPLM_WORD] = "uint16_t",
8172 [OPLM_DWORD] = "uint32_t",
8173 [OPLM_QWORD] = "uint64_t",
8174 };
8175 const struct scanned_var *var;
ebc4dc43 8176 struct func_prototype *fp;
c0de9015 8177 char line[256] = { 0, };
ebc4dc43 8178 char name[256];
9af2d373 8179 int i;
8180
ebc4dc43 8181 // add stuff from headers
8182 for (i = 0; i < pp_cache_size; i++) {
8183 if (pp_cache[i].is_cinc && !pp_cache[i].is_stdcall)
8184 snprintf(name, sizeof(name), "_%s", pp_cache[i].name);
8185 else
8186 snprintf(name, sizeof(name), "%s", pp_cache[i].name);
8187 fp = hg_fp_add(name);
8188 fp->pp = &pp_cache[i];
8189 fp->argc_stack = fp->pp->argc_stack;
8190 fp->is_stdcall = fp->pp->is_stdcall;
b2bd20c0 8191 fp->regmask_dep = get_pp_arg_regmask_src(fp->pp);
ebc4dc43 8192 fp->has_ret = !IS(fp->pp->ret_type.name, "void");
8193 }
8194
9af2d373 8195 // resolve deps
8196 qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_name);
8197 for (i = 0; i < hg_fp_cnt; i++)
8198 hg_fp_resolve_deps(&hg_fp[i]);
8199
8c999988 8200 // adjust functions referenced from data segment
8201 do_func_refs_from_data();
8202
9af2d373 8203 // note: messes up .proto ptr, don't use
8204 //qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_id);
8205
5fa1256f 8206 // output variables
8207 for (i = 0; i < hg_var_cnt; i++) {
8208 var = &hg_vars[i];
8209
4e81a3a2 8210 if (var->pp != NULL)
8211 // part of seed
8212 continue;
c0de9015 8213 else if (var->is_c_str)
61e29183 8214 fprintf(fout, "extern %-8s %s[];", "char", var->name);
8215 else
8216 fprintf(fout, "extern %-8s %s;",
8217 lmod_c_names[var->lmod], var->name);
5fa1256f 8218
8219 if (var->is_seeded)
8220 fprintf(fout, " // seeded");
8221 fprintf(fout, "\n");
8222 }
8223
8224 fprintf(fout, "\n");
8225
8226 // output function prototypes
9af2d373 8227 output_hdr_fp(fout, hg_fp, hg_fp_cnt);
c0de9015 8228
4e81a3a2 8229 // seed passthrough
8230 fprintf(fout, "\n// - seed -\n");
c0de9015 8231
8232 rewind(g_fhdr);
4e81a3a2 8233 while (fgets(line, sizeof(line), g_fhdr))
8234 fwrite(line, 1, strlen(line), fout);
9af2d373 8235}
8236
61e29183 8237// '=' needs special treatment
8238// also ' quote
bfa4a6ee 8239static char *next_word_s(char *w, size_t wsize, char *s)
8240{
61e29183 8241 size_t i;
bfa4a6ee 8242
61e29183 8243 s = sskip(s);
bfa4a6ee 8244
61e29183 8245 i = 0;
8c999988 8246 if (*s == '\'' && s[1] != '\r' && s[1] != '\n') {
61e29183 8247 w[0] = s[0];
8248 for (i = 1; i < wsize - 1; i++) {
8249 if (s[i] == 0) {
8250 printf("warning: missing closing quote: \"%s\"\n", s);
8251 break;
8252 }
8253 if (s[i] == '\'')
8254 break;
8255 w[i] = s[i];
8256 }
8257 }
bfa4a6ee 8258
61e29183 8259 for (; i < wsize - 1; i++) {
8260 if (s[i] == 0 || my_isblank(s[i]) || (s[i] == '=' && i > 0))
8261 break;
8262 w[i] = s[i];
8263 }
8264 w[i] = 0;
8265
8266 if (s[i] != 0 && !my_isblank(s[i]) && s[i] != '=')
8267 printf("warning: '%s' truncated\n", w);
bfa4a6ee 8268
61e29183 8269 return s + i;
bfa4a6ee 8270}
8271
4c20744d 8272static int cmpstringp(const void *p1, const void *p2)
8273{
8274 return strcmp(*(char * const *)p1, *(char * const *)p2);
8275}
8276
8277static int is_xref_needed(char *p, char **rlist, int rlist_len)
8278{
8279 char *p2;
8280
8281 p = sskip(p);
8282 if (strstr(p, "..."))
8283 // unable to determine, assume needed
8284 return 1;
8285
8286 if (*p == '.') // .text, .data, ...
8287 // ref from other data or non-function -> no
8288 return 0;
8289
8290 p2 = strpbrk(p, "+:\r\n\x18");
8291 if (p2 != NULL)
8292 *p2 = 0;
8293 if (bsearch(&p, rlist, rlist_len, sizeof(rlist[0]), cmpstringp))
8294 // referenced from removed code
8295 return 0;
8296
8297 return 1;
8298}
8299
8c999988 8300static int ida_xrefs_show_need(FILE *fasm, char *p,
4c20744d 8301 char **rlist, int rlist_len)
8302{
8303 int found_need = 0;
8304 char line[256];
8305 long pos;
8306
8307 p = strrchr(p, ';');
ea43585b 8308 if (p != NULL && *p == ';') {
8309 if (IS_START(p + 2, "sctref"))
4c20744d 8310 return 1;
ea43585b 8311 if (IS_START(p + 2, "DATA XREF: ")) {
8312 p += 13;
8313 if (is_xref_needed(p, rlist, rlist_len))
8314 return 1;
8315 }
4c20744d 8316 }
8317
8318 pos = ftell(fasm);
8319 while (1)
8320 {
8321 if (!my_fgets(line, sizeof(line), fasm))
8322 break;
8323 // non-first line is always indented
8324 if (!my_isblank(line[0]))
8325 break;
8326
8327 // should be no content, just comment
8328 p = sskip(line);
8329 if (*p != ';')
8330 break;
8331
8332 p = strrchr(p, ';');
8333 p += 2;
ea43585b 8334
8335 if (IS_START(p, "sctref")) {
8336 found_need = 1;
8337 break;
8338 }
8339
4c20744d 8340 // it's printed once, but no harm to check again
8341 if (IS_START(p, "DATA XREF: "))
8342 p += 11;
8343
8344 if (is_xref_needed(p, rlist, rlist_len)) {
8345 found_need = 1;
8346 break;
8347 }
8348 }
8349 fseek(fasm, pos, SEEK_SET);
8350 return found_need;
8351}
8352
8353static void scan_variables(FILE *fasm, char **rlist, int rlist_len)
5fa1256f 8354{
5fa1256f 8355 struct scanned_var *var;
8356 char line[256] = { 0, };
8c999988 8357 char words[4][256];
8358 int no_identifier;
5fa1256f 8359 char *p = NULL;
8360 int wordc;
61e29183 8361 int l;
5fa1256f 8362
8363 while (!feof(fasm))
8364 {
8365 // skip to next data section
8366 while (my_fgets(line, sizeof(line), fasm))
8367 {
8368 asmln++;
8369
8370 p = sskip(line);
8371 if (*p == 0 || *p == ';')
8372 continue;
8373
8374 p = sskip(next_word_s(words[0], sizeof(words[0]), p));
8375 if (*p == 0 || *p == ';')
8376 continue;
8377
8378 if (*p != 's' || !IS_START(p, "segment para public"))
8379 continue;
8380
8381 break;
8382 }
8383
8384 if (p == NULL || !IS_START(p, "segment para public"))
8385 break;
8386 p = sskip(p + 19);
8387
8388 if (!IS_START(p, "'DATA'"))
8389 continue;
8390
8391 // now process it
8392 while (my_fgets(line, sizeof(line), fasm))
8393 {
8394 asmln++;
8395
8396 p = line;
8c999988 8397 no_identifier = my_isblank(*p);
5fa1256f 8398
8399 p = sskip(p);
8400 if (*p == 0 || *p == ';')
8401 continue;
8402
8403 for (wordc = 0; wordc < ARRAY_SIZE(words); wordc++) {
8404 words[wordc][0] = 0;
8405 p = sskip(next_word_s(words[wordc], sizeof(words[0]), p));
8406 if (*p == 0 || *p == ';') {
8407 wordc++;
8408 break;
8409 }
8410 }
8411
8412 if (wordc == 2 && IS(words[1], "ends"))
8413 break;
61e29183 8414 if (wordc < 2)
8415 continue;
5fa1256f 8416
8c999988 8417 if (no_identifier) {
8418 if (wordc >= 3 && IS(words[0], "dd") && IS(words[1], "offset"))
8419 hg_ref_add(words[2]);
8420 continue;
8421 }
8422
9ea60b8d 8423 if (IS_START(words[0], "__IMPORT_DESCRIPTOR_")) {
8424 // when this starts, we don't need anything from this section
8425 break;
8426 }
8427
4c20744d 8428 // check refs comment(s)
8c999988 8429 if (!ida_xrefs_show_need(fasm, p, rlist, rlist_len))
4c20744d 8430 continue;
8431
5fa1256f 8432 if ((hg_var_cnt & 0xff) == 0) {
8433 hg_vars = realloc(hg_vars, sizeof(hg_vars[0])
8434 * (hg_var_cnt + 0x100));
8435 my_assert_not(hg_vars, NULL);
8436 memset(hg_vars + hg_var_cnt, 0, sizeof(hg_vars[0]) * 0x100);
8437 }
8438
8439 var = &hg_vars[hg_var_cnt++];
8440 snprintf(var->name, sizeof(var->name), "%s", words[0]);
8441
8442 // maybe already in seed header?
c0de9015 8443 var->pp = proto_parse(g_fhdr, var->name, 1);
8444 if (var->pp != NULL) {
8445 if (var->pp->is_fptr) {
5fa1256f 8446 var->lmod = OPLM_DWORD;
8447 //var->is_ptr = 1;
8448 }
c0de9015 8449 else if (var->pp->is_func)
8450 aerr("func?\n");
8451 else if (!guess_lmod_from_c_type(&var->lmod, &var->pp->type))
5fa1256f 8452 aerr("unhandled C type '%s' for '%s'\n",
c0de9015 8453 var->pp->type.name, var->name);
5fa1256f 8454
8455 var->is_seeded = 1;
8456 continue;
8457 }
8458
8c999988 8459 if (IS(words[1], "dd")) {
5fa1256f 8460 var->lmod = OPLM_DWORD;
8c999988 8461 if (wordc >= 4 && IS(words[2], "offset"))
8462 hg_ref_add(words[3]);
8463 }
5fa1256f 8464 else if (IS(words[1], "dw"))
8465 var->lmod = OPLM_WORD;
61e29183 8466 else if (IS(words[1], "db")) {
5fa1256f 8467 var->lmod = OPLM_BYTE;
61e29183 8468 if (wordc >= 3 && (l = strlen(words[2])) > 4) {
8469 if (words[2][0] == '\'' && IS(words[2] + l - 2, ",0"))
8470 var->is_c_str = 1;
8471 }
8472 }
5fa1256f 8473 else if (IS(words[1], "dq"))
8474 var->lmod = OPLM_QWORD;
8475 //else if (IS(words[1], "dt"))
8476 else
8477 aerr("type '%s' not known\n", words[1]);
8478 }
8479 }
8480
8481 rewind(fasm);
8482 asmln = 0;
8483}
8484
8485static void set_label(int i, const char *name)
8486{
8487 const char *p;
8488 int len;
8489
8490 len = strlen(name);
8491 p = strchr(name, ':');
8492 if (p != NULL)
8493 len = p - name;
8494
8495 if (g_labels[i] != NULL && !IS_START(g_labels[i], "algn_"))
8496 aerr("dupe label '%s' vs '%s'?\n", name, g_labels[i]);
8497 g_labels[i] = realloc(g_labels[i], len + 1);
8498 my_assert_not(g_labels[i], NULL);
8499 memcpy(g_labels[i], name, len);
8500 g_labels[i][len] = 0;
8501}
8502
e56ab892 8503struct chunk_item {
8504 char *name;
8505 long fptr;
de50b98b 8506 int asmln;
e56ab892 8507};
8508
cdfaeed7 8509static struct chunk_item *func_chunks;
8510static int func_chunk_cnt;
8511static int func_chunk_alloc;
8512
8513static void add_func_chunk(FILE *fasm, const char *name, int line)
8514{
8515 if (func_chunk_cnt >= func_chunk_alloc) {
8516 func_chunk_alloc *= 2;
8517 func_chunks = realloc(func_chunks,
8518 func_chunk_alloc * sizeof(func_chunks[0]));
8519 my_assert_not(func_chunks, NULL);
8520 }
8521 func_chunks[func_chunk_cnt].fptr = ftell(fasm);
8522 func_chunks[func_chunk_cnt].name = strdup(name);
8523 func_chunks[func_chunk_cnt].asmln = line;
8524 func_chunk_cnt++;
8525}
8526
e56ab892 8527static int cmp_chunks(const void *p1, const void *p2)
8528{
8529 const struct chunk_item *c1 = p1, *c2 = p2;
8530 return strcmp(c1->name, c2->name);
8531}
8532
ea43585b 8533static void scan_ahead_for_chunks(FILE *fasm)
cdfaeed7 8534{
8535 char words[2][256];
8536 char line[256];
8537 long oldpos;
8538 int oldasmln;
8539 int wordc;
8540 char *p;
8541 int i;
8542
8543 oldpos = ftell(fasm);
8544 oldasmln = asmln;
8545
5fa1256f 8546 while (my_fgets(line, sizeof(line), fasm))
cdfaeed7 8547 {
8548 wordc = 0;
8549 asmln++;
8550
8551 p = sskip(line);
8552 if (*p == 0)
8553 continue;
8554
8555 if (*p == ';')
8556 {
8557 // get rid of random tabs
8558 for (i = 0; line[i] != 0; i++)
8559 if (line[i] == '\t')
8560 line[i] = ' ';
8561
8562 if (p[2] == 'S' && IS_START(p, "; START OF FUNCTION CHUNK FOR "))
8563 {
8564 p += 30;
8565 next_word(words[0], sizeof(words[0]), p);
8566 if (words[0][0] == 0)
8567 aerr("missing name for func chunk?\n");
8568
8569 add_func_chunk(fasm, words[0], asmln);
8570 }
46b388c2 8571 else if (IS_START(p, "; sctend"))
8572 break;
8573
cdfaeed7 8574 continue;
8575 } // *p == ';'
8576
8577 for (wordc = 0; wordc < ARRAY_SIZE(words); wordc++) {
8578 words[wordc][0] = 0;
8579 p = sskip(next_word_s(words[wordc], sizeof(words[0]), p));
8580 if (*p == 0 || *p == ';') {
8581 wordc++;
8582 break;
8583 }
8584 }
8585
8586 if (wordc == 2 && IS(words[1], "ends"))
8587 break;
8588 }
8589
8590 fseek(fasm, oldpos, SEEK_SET);
8591 asmln = oldasmln;
8592}
8593
91977a1c 8594int main(int argc, char *argv[])
8595{
06c5d854 8596 FILE *fout, *fasm, *frlist;
4c45fa73 8597 struct parsed_data *pd = NULL;
8598 int pd_alloc = 0;
8599 char **rlist = NULL;
8600 int rlist_len = 0;
8601 int rlist_alloc = 0;
e56ab892 8602 int func_chunks_used = 0;
8603 int func_chunks_sorted = 0;
e56ab892 8604 int func_chunk_i = -1;
8605 long func_chunk_ret = 0;
de50b98b 8606 int func_chunk_ret_ln = 0;
cdfaeed7 8607 int scanned_ahead = 0;
91977a1c 8608 char line[256];
a2c1d768 8609 char words[20][256];
4c45fa73 8610 enum opr_lenmod lmod;
ddaf8bd7 8611 char *sctproto = NULL;
91977a1c 8612 int in_func = 0;
4c45fa73 8613 int pending_endp = 0;
11437ea1 8614 int skip_code = 0;
8615 int skip_code_end = 0;
940e8e66 8616 int skip_warned = 0;
91977a1c 8617 int eq_alloc;
bfa4a6ee 8618 int verbose = 0;
1f84f6b3 8619 int multi_seg = 0;
46b388c2 8620 int end = 0;
bfa4a6ee 8621 int arg_out;
89ff3147 8622 int arg;
91977a1c 8623 int pi = 0;
e56ab892 8624 int i, j;
8625 int ret, len;
91977a1c 8626 char *p;
8627 int wordc;
8628
89ff3147 8629 for (arg = 1; arg < argc; arg++) {
8630 if (IS(argv[arg], "-v"))
8631 verbose = 1;
8632 else if (IS(argv[arg], "-rf"))
8633 g_allow_regfunc = 1;
8c83cc48 8634 else if (IS(argv[arg], "-uc"))
8635 g_allow_user_icall = 1;
1f84f6b3 8636 else if (IS(argv[arg], "-m"))
8637 multi_seg = 1;
92d715b6 8638 else if (IS(argv[arg], "-hdr"))
9af2d373 8639 g_header_mode = g_quiet_pp = g_allow_regfunc = 1;
89ff3147 8640 else
8641 break;
bfa4a6ee 8642 }
8643
8644 if (argc < arg + 3) {
315b77eb 8645 printf("usage:\n%s [-v] [-rf] [-m] <.c> <.asm> <hdr.h> [rlist]*\n"
8646 "%s -hdr <out.h> <.asm> <seed.h> [rlist]*\n"
8647 "options:\n"
8648 " -hdr - header generation mode\n"
8649 " -rf - allow unannotated indirect calls\n"
8c83cc48 8650 " -uc - allow ind. calls/refs to __usercall\n"
315b77eb 8651 " -m - allow multiple .text sections\n"
8652 "[rlist] is a file with function names to skip,"
8653 " one per line\n",
92d715b6 8654 argv[0], argv[0]);
91977a1c 8655 return 1;
8656 }
8657
bfa4a6ee 8658 arg_out = arg++;
91977a1c 8659
bfa4a6ee 8660 asmfn = argv[arg++];
91977a1c 8661 fasm = fopen(asmfn, "r");
8662 my_assert_not(fasm, NULL);
8663
bfa4a6ee 8664 hdrfn = argv[arg++];
06c5d854 8665 g_fhdr = fopen(hdrfn, "r");
8666 my_assert_not(g_fhdr, NULL);
bfa4a6ee 8667
8668 rlist_alloc = 64;
8669 rlist = malloc(rlist_alloc * sizeof(rlist[0]));
8670 my_assert_not(rlist, NULL);
8671 // needs special handling..
8672 rlist[rlist_len++] = "__alloca_probe";
8673
e56ab892 8674 func_chunk_alloc = 32;
8675 func_chunks = malloc(func_chunk_alloc * sizeof(func_chunks[0]));
8676 my_assert_not(func_chunks, NULL);
8677
a2c1d768 8678 memset(words, 0, sizeof(words));
8679
bfa4a6ee 8680 for (; arg < argc; arg++) {
11437ea1 8681 int skip_func = 0;
8682
bfa4a6ee 8683 frlist = fopen(argv[arg], "r");
8684 my_assert_not(frlist, NULL);
8685
5fa1256f 8686 while (my_fgets(line, sizeof(line), frlist)) {
bfa4a6ee 8687 p = sskip(line);
1cd4a663 8688 if (*p == 0 || *p == ';')
8689 continue;
8690 if (*p == '#') {
89ff3147 8691 if (IS_START(p, "#if 0")
8692 || (g_allow_regfunc && IS_START(p, "#if NO_REGFUNC")))
8693 {
1cd4a663 8694 skip_func = 1;
89ff3147 8695 }
1cd4a663 8696 else if (IS_START(p, "#endif"))
8697 skip_func = 0;
8698 continue;
8699 }
8700 if (skip_func)
bfa4a6ee 8701 continue;
8702
8703 p = next_word(words[0], sizeof(words[0]), p);
8704 if (words[0][0] == 0)
8705 continue;
8706
8707 if (rlist_len >= rlist_alloc) {
8708 rlist_alloc = rlist_alloc * 2 + 64;
8709 rlist = realloc(rlist, rlist_alloc * sizeof(rlist[0]));
8710 my_assert_not(rlist, NULL);
8711 }
8712 rlist[rlist_len++] = strdup(words[0]);
8713 }
8714
8715 fclose(frlist);
8716 frlist = NULL;
8717 }
8718
8719 if (rlist_len > 0)
8720 qsort(rlist, rlist_len, sizeof(rlist[0]), cmpstringp);
8721
8722 fout = fopen(argv[arg_out], "w");
91977a1c 8723 my_assert_not(fout, NULL);
8724
8725 eq_alloc = 128;
8726 g_eqs = malloc(eq_alloc * sizeof(g_eqs[0]));
8727 my_assert_not(g_eqs, NULL);
8728
4c45fa73 8729 for (i = 0; i < ARRAY_SIZE(g_label_refs); i++) {
8730 g_label_refs[i].i = -1;
8731 g_label_refs[i].next = NULL;
8732 }
8733
5fa1256f 8734 if (g_header_mode)
4c20744d 8735 scan_variables(fasm, rlist, rlist_len);
5fa1256f 8736
8737 while (my_fgets(line, sizeof(line), fasm))
91977a1c 8738 {
4c45fa73 8739 wordc = 0;
91977a1c 8740 asmln++;
8741
8742 p = sskip(line);
1bafb621 8743 if (*p == 0)
91977a1c 8744 continue;
8745
de50b98b 8746 // get rid of random tabs
8747 for (i = 0; line[i] != 0; i++)
8748 if (line[i] == '\t')
8749 line[i] = ' ';
8750
e56ab892 8751 if (*p == ';')
8752 {
e56ab892 8753 if (p[2] == '=' && IS_START(p, "; =============== S U B"))
8754 goto do_pending_endp; // eww..
8755
8756 if (p[2] == 'A' && IS_START(p, "; Attributes:"))
8757 {
8758 static const char *attrs[] = {
8759 "bp-based frame",
8760 "library function",
8761 "static",
8762 "noreturn",
8763 "thunk",
8764 "fpd=",
8765 };
8766
8767 // parse IDA's attribute-list comment
8768 g_ida_func_attr = 0;
8769 p = sskip(p + 13);
8770
8771 for (; *p != 0; p = sskip(p)) {
8772 for (i = 0; i < ARRAY_SIZE(attrs); i++) {
8773 if (!strncmp(p, attrs[i], strlen(attrs[i]))) {
8774 g_ida_func_attr |= 1 << i;
8775 p += strlen(attrs[i]);
8776 break;
8777 }
8778 }
8779 if (i == ARRAY_SIZE(attrs)) {
8780 anote("unparsed IDA attr: %s\n", p);
1bafb621 8781 break;
8782 }
e56ab892 8783 if (IS(attrs[i], "fpd=")) {
8784 p = next_word(words[0], sizeof(words[0]), p);
8785 // ignore for now..
8786 }
1bafb621 8787 }
e56ab892 8788 }
7e08c224 8789 else if (p[2] == 's' && IS_START(p, "; sctattr:"))
8790 {
8791 static const char *attrs[] = {
8792 "clear_sf",
226e8df1 8793 "clear_regmask",
7e08c224 8794 };
8795
8796 // parse manual attribute-list comment
8797 g_sct_func_attr = 0;
8798 p = sskip(p + 10);
8799
8800 for (; *p != 0; p = sskip(p)) {
8801 for (i = 0; i < ARRAY_SIZE(attrs); i++) {
8802 if (!strncmp(p, attrs[i], strlen(attrs[i]))) {
8803 g_sct_func_attr |= 1 << i;
8804 p += strlen(attrs[i]);
8805 break;
8806 }
8807 }
226e8df1 8808 if (*p == '=') {
8809 j = ret = 0;
8810 if (i == 0)
8811 // clear_sf=start,len (in dwords)
8812 ret = sscanf(p, "=%d,%d%n", &g_stack_clear_start,
8813 &g_stack_clear_len, &j);
8814 else if (i == 1)
8815 // clear_regmask=<mask>
6135d8e6 8816 ret = sscanf(p, "=%x%n", &g_regmask_init, &j) + 1;
7e08c224 8817 if (ret < 2) {
226e8df1 8818 anote("unparsed attr value: %s\n", p);
7e08c224 8819 break;
8820 }
8821 p += j;
8822 }
8823 else if (i == ARRAY_SIZE(attrs)) {
8824 anote("unparsed sct attr: %s\n", p);
8825 break;
8826 }
8827 }
8828 }
e56ab892 8829 else if (p[2] == 'S' && IS_START(p, "; START OF FUNCTION CHUNK FOR "))
8830 {
8831 p += 30;
8832 next_word(words[0], sizeof(words[0]), p);
8833 if (words[0][0] == 0)
cdfaeed7 8834 aerr("missing name for func chunk?\n");
8835
8836 if (!scanned_ahead) {
8837 add_func_chunk(fasm, words[0], asmln);
8838 func_chunks_sorted = 0;
e56ab892 8839 }
e56ab892 8840 }
8841 else if (p[2] == 'E' && IS_START(p, "; END OF FUNCTION CHUNK"))
8842 {
8843 if (func_chunk_i >= 0) {
8844 if (func_chunk_i < func_chunk_cnt
8845 && IS(func_chunks[func_chunk_i].name, g_func))
8846 {
8847 // move on to next chunk
8848 ret = fseek(fasm, func_chunks[func_chunk_i].fptr, SEEK_SET);
8849 if (ret)
8850 aerr("seek failed for '%s' chunk #%d\n",
8851 g_func, func_chunk_i);
de50b98b 8852 asmln = func_chunks[func_chunk_i].asmln;
e56ab892 8853 func_chunk_i++;
8854 }
8855 else {
8856 if (func_chunk_ret == 0)
8857 aerr("no return from chunk?\n");
8858 fseek(fasm, func_chunk_ret, SEEK_SET);
de50b98b 8859 asmln = func_chunk_ret_ln;
e56ab892 8860 func_chunk_ret = 0;
8861 pending_endp = 1;
8862 }
1bafb621 8863 }
e56ab892 8864 }
8865 else if (p[2] == 'F' && IS_START(p, "; FUNCTION CHUNK AT ")) {
8866 func_chunks_used = 1;
8867 p += 20;
8868 if (IS_START(g_func, "sub_")) {
8869 unsigned long addr = strtoul(p, NULL, 16);
8870 unsigned long f_addr = strtoul(g_func + 4, NULL, 16);
cdfaeed7 8871 if (addr > f_addr && !scanned_ahead) {
b2bd20c0 8872 //anote("scan_ahead caused by '%s', addr %lx\n",
8873 // g_func, addr);
ea43585b 8874 scan_ahead_for_chunks(fasm);
cdfaeed7 8875 scanned_ahead = 1;
8876 func_chunks_sorted = 0;
8877 }
1bafb621 8878 }
8879 }
8880 continue;
e56ab892 8881 } // *p == ';'
1bafb621 8882
06c5d854 8883parse_words:
a2c1d768 8884 for (i = wordc; i < ARRAY_SIZE(words); i++)
8885 words[i][0] = 0;
cdfaeed7 8886 for (wordc = 0; wordc < ARRAY_SIZE(words); wordc++) {
bfa4a6ee 8887 p = sskip(next_word_s(words[wordc], sizeof(words[0]), p));
91977a1c 8888 if (*p == 0 || *p == ';') {
8889 wordc++;
8890 break;
8891 }
8892 }
a2c1d768 8893 if (*p != 0 && *p != ';')
8894 aerr("too many words\n");
91977a1c 8895
11437ea1 8896 if (skip_code_end) {
8897 skip_code_end = 0;
8898 skip_code = 0;
8899 }
8900
8901 // allow asm patches in comments
ddaf8bd7 8902 if (*p == ';') {
8903 if (IS_START(p, "; sctpatch:")) {
8904 p = sskip(p + 11);
8905 if (*p == 0 || *p == ';')
8906 continue;
8907 goto parse_words; // lame
8908 }
8909 if (IS_START(p, "; sctproto:")) {
8910 sctproto = strdup(p + 11);
8911 }
46b388c2 8912 else if (IS_START(p, "; sctend")) {
8913 end = 1;
8914 if (!pending_endp)
8915 break;
8916 }
11437ea1 8917 else if (IS_START(p, "; sctskip_start")) {
8918 if (in_func && !g_skip_func) {
8919 if (!skip_code) {
8920 ops[pi].op = OPP_ABORT;
8921 ops[pi].asmln = asmln;
8922 pi++;
8923 }
8924 skip_code = 1;
8925 }
8926 }
8927 else if (IS_START(p, "; sctskip_end")) {
8928 if (skip_code)
8929 skip_code_end = 1;
8930 }
06c5d854 8931 }
8932
91977a1c 8933 if (wordc == 0) {
8934 // shouldn't happen
8935 awarn("wordc == 0?\n");
8936 continue;
8937 }
8938
8939 // don't care about this:
8940 if (words[0][0] == '.'
8941 || IS(words[0], "include")
8942 || IS(words[0], "assume") || IS(words[1], "segment")
8943 || IS(words[0], "align"))
8944 {
8945 continue;
8946 }
8947
4c45fa73 8948do_pending_endp:
8949 // do delayed endp processing to collect switch jumptables
8950 if (pending_endp) {
30c8c549 8951 if (in_func && !g_skip_func && !end && wordc >= 2
4c45fa73 8952 && ((words[0][0] == 'd' && words[0][2] == 0)
8953 || (words[1][0] == 'd' && words[1][2] == 0)))
8954 {
8955 i = 1;
8956 if (words[1][0] == 'd' && words[1][2] == 0) {
8957 // label
8958 if (g_func_pd_cnt >= pd_alloc) {
8959 pd_alloc = pd_alloc * 2 + 16;
8960 g_func_pd = realloc(g_func_pd,
8961 sizeof(g_func_pd[0]) * pd_alloc);
8962 my_assert_not(g_func_pd, NULL);
8963 }
8964 pd = &g_func_pd[g_func_pd_cnt];
8965 g_func_pd_cnt++;
8966 memset(pd, 0, sizeof(*pd));
8967 strcpy(pd->label, words[0]);
8968 pd->type = OPT_CONST;
8969 pd->lmod = lmod_from_directive(words[1]);
8970 i = 2;
8971 }
8972 else {
da87ae38 8973 if (pd == NULL) {
8974 if (verbose)
8975 anote("skipping alignment byte?\n");
8976 continue;
8977 }
4c45fa73 8978 lmod = lmod_from_directive(words[0]);
8979 if (lmod != pd->lmod)
8980 aerr("lmod change? %d->%d\n", pd->lmod, lmod);
8981 }
8982
8983 if (pd->count_alloc < pd->count + wordc) {
8984 pd->count_alloc = pd->count_alloc * 2 + 14 + wordc;
8985 pd->d = realloc(pd->d, sizeof(pd->d[0]) * pd->count_alloc);
8986 my_assert_not(pd->d, NULL);
8987 }
8988 for (; i < wordc; i++) {
8989 if (IS(words[i], "offset")) {
8990 pd->type = OPT_OFFSET;
8991 i++;
8992 }
8993 p = strchr(words[i], ',');
8994 if (p != NULL)
8995 *p = 0;
8996 if (pd->type == OPT_OFFSET)
8997 pd->d[pd->count].u.label = strdup(words[i]);
8998 else
e27467d0 8999 pd->d[pd->count].u.val = parse_number(words[i], 0);
4c45fa73 9000 pd->d[pd->count].bt_i = -1;
9001 pd->count++;
9002 }
9003 continue;
9004 }
9005
30c8c549 9006 if (in_func && !g_skip_func) {
9af2d373 9007 if (g_header_mode)
92d715b6 9008 gen_hdr(g_func, pi);
9009 else
9010 gen_func(fout, g_fhdr, g_func, pi);
9011 }
4c45fa73 9012
9013 pending_endp = 0;
9014 in_func = 0;
9015 g_ida_func_attr = 0;
7e08c224 9016 g_sct_func_attr = 0;
9017 g_stack_clear_start = 0;
9018 g_stack_clear_len = 0;
226e8df1 9019 g_regmask_init = 0;
4c45fa73 9020 skip_warned = 0;
30c8c549 9021 g_skip_func = 0;
4c45fa73 9022 g_func[0] = 0;
e56ab892 9023 func_chunks_used = 0;
9024 func_chunk_i = -1;
4c45fa73 9025 if (pi != 0) {
9026 memset(&ops, 0, pi * sizeof(ops[0]));
d7857c3a 9027 clear_labels(pi);
4c45fa73 9028 pi = 0;
9029 }
9030 g_eqcnt = 0;
9031 for (i = 0; i < g_func_pd_cnt; i++) {
9032 pd = &g_func_pd[i];
9033 if (pd->type == OPT_OFFSET) {
9034 for (j = 0; j < pd->count; j++)
9035 free(pd->d[j].u.label);
9036 }
9037 free(pd->d);
9038 pd->d = NULL;
9039 }
9040 g_func_pd_cnt = 0;
d4a985bd 9041 g_func_lmods = 0;
4c45fa73 9042 pd = NULL;
46b388c2 9043
9044 if (end)
9045 break;
4c45fa73 9046 if (wordc == 0)
9047 continue;
9048 }
9049
91977a1c 9050 if (IS(words[1], "proc")) {
9051 if (in_func)
9052 aerr("proc '%s' while in_func '%s'?\n",
9053 words[0], g_func);
bfa4a6ee 9054 p = words[0];
ddaf8bd7 9055 if (bsearch(&p, rlist, rlist_len, sizeof(rlist[0]), cmpstringp))
30c8c549 9056 g_skip_func = 1;
91977a1c 9057 strcpy(g_func, words[0]);
d4e3b5db 9058 set_label(0, words[0]);
91977a1c 9059 in_func = 1;
9060 continue;
9061 }
9062
e56ab892 9063 if (IS(words[1], "endp"))
9064 {
91977a1c 9065 if (!in_func)
9066 aerr("endp '%s' while not in_func?\n", words[0]);
9067 if (!IS(g_func, words[0]))
9068 aerr("endp '%s' while in_func '%s'?\n",
9069 words[0], g_func);
11437ea1 9070 if (skip_code)
9071 aerr("endp '%s' while skipping code\n", words[0]);
bfa4a6ee 9072
ddaf8bd7 9073 if ((g_ida_func_attr & IDAFA_THUNK) && pi == 1
9074 && ops[0].op == OP_JMP && ops[0].operand[0].had_ds)
9075 {
9076 // import jump
30c8c549 9077 g_skip_func = 1;
ddaf8bd7 9078 }
9079
30c8c549 9080 if (!g_skip_func && func_chunks_used) {
e56ab892 9081 // start processing chunks
9082 struct chunk_item *ci, key = { g_func, 0 };
9083
9084 func_chunk_ret = ftell(fasm);
de50b98b 9085 func_chunk_ret_ln = asmln;
e56ab892 9086 if (!func_chunks_sorted) {
9087 qsort(func_chunks, func_chunk_cnt,
9088 sizeof(func_chunks[0]), cmp_chunks);
9089 func_chunks_sorted = 1;
9090 }
9091 ci = bsearch(&key, func_chunks, func_chunk_cnt,
9092 sizeof(func_chunks[0]), cmp_chunks);
9093 if (ci == NULL)
9094 aerr("'%s' needs chunks, but none found\n", g_func);
9095 func_chunk_i = ci - func_chunks;
9096 for (; func_chunk_i > 0; func_chunk_i--)
9097 if (!IS(func_chunks[func_chunk_i - 1].name, g_func))
9098 break;
9099
9100 ret = fseek(fasm, func_chunks[func_chunk_i].fptr, SEEK_SET);
9101 if (ret)
9102 aerr("seek failed for '%s' chunk #%d\n", g_func, func_chunk_i);
de50b98b 9103 asmln = func_chunks[func_chunk_i].asmln;
e56ab892 9104 func_chunk_i++;
9105 continue;
9106 }
4c45fa73 9107 pending_endp = 1;
91977a1c 9108 continue;
9109 }
9110
1f84f6b3 9111 if (wordc == 2 && IS(words[1], "ends")) {
46b388c2 9112 if (!multi_seg) {
9113 end = 1;
9114 if (pending_endp)
9115 goto do_pending_endp;
1f84f6b3 9116 break;
46b388c2 9117 }
1f84f6b3 9118
9119 // scan for next text segment
5fa1256f 9120 while (my_fgets(line, sizeof(line), fasm)) {
1f84f6b3 9121 asmln++;
9122 p = sskip(line);
9123 if (*p == 0 || *p == ';')
9124 continue;
9125
9126 if (strstr(p, "segment para public 'CODE' use32"))
9127 break;
9128 }
9129
9130 continue;
9131 }
a2c1d768 9132
bfa4a6ee 9133 p = strchr(words[0], ':');
9134 if (p != NULL) {
d4e3b5db 9135 set_label(pi, words[0]);
bfa4a6ee 9136 continue;
9137 }
9138
11437ea1 9139 if (!in_func || g_skip_func || skip_code) {
30c8c549 9140 if (!skip_warned && !g_skip_func && g_labels[pi] != NULL) {
bfa4a6ee 9141 if (verbose)
9142 anote("skipping from '%s'\n", g_labels[pi]);
9143 skip_warned = 1;
9144 }
d7857c3a 9145 free(g_labels[pi]);
9146 g_labels[pi] = NULL;
bfa4a6ee 9147 continue;
9148 }
9149
ddaf8bd7 9150 if (wordc > 1 && IS(words[1], "="))
9151 {
91977a1c 9152 if (wordc != 5)
9153 aerr("unhandled equ, wc=%d\n", wordc);
9154 if (g_eqcnt >= eq_alloc) {
9155 eq_alloc *= 2;
9156 g_eqs = realloc(g_eqs, eq_alloc * sizeof(g_eqs[0]));
9157 my_assert_not(g_eqs, NULL);
9158 }
9159
9160 len = strlen(words[0]);
9161 if (len > sizeof(g_eqs[0].name) - 1)
9162 aerr("equ name too long: %d\n", len);
9163 strcpy(g_eqs[g_eqcnt].name, words[0]);
9164
9165 if (!IS(words[3], "ptr"))
9166 aerr("unhandled equ\n");
9167 if (IS(words[2], "dword"))
9168 g_eqs[g_eqcnt].lmod = OPLM_DWORD;
9169 else if (IS(words[2], "word"))
9170 g_eqs[g_eqcnt].lmod = OPLM_WORD;
9171 else if (IS(words[2], "byte"))
9172 g_eqs[g_eqcnt].lmod = OPLM_BYTE;
90307a99 9173 else if (IS(words[2], "qword"))
9174 g_eqs[g_eqcnt].lmod = OPLM_QWORD;
91977a1c 9175 else
9176 aerr("bad lmod: '%s'\n", words[2]);
9177
e27467d0 9178 g_eqs[g_eqcnt].offset = parse_number(words[4], 0);
91977a1c 9179 g_eqcnt++;
9180 continue;
9181 }
9182
9183 if (pi >= ARRAY_SIZE(ops))
9184 aerr("too many ops\n");
9185
91977a1c 9186 parse_op(&ops[pi], words, wordc);
ddaf8bd7 9187
865f1aca 9188 ops[pi].datap = sctproto;
9189 sctproto = NULL;
91977a1c 9190 pi++;
91977a1c 9191 }
9192
9af2d373 9193 if (g_header_mode)
92d715b6 9194 output_hdr(fout);
9195
91977a1c 9196 fclose(fout);
9197 fclose(fasm);
06c5d854 9198 fclose(g_fhdr);
91977a1c 9199
9200 return 0;
c36e914d 9201}
91977a1c 9202
9203// vim:ts=2:shiftwidth=2:expandtab