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