translate: seh finalize handler
[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);
0a2c6da1 3822 if (IS_START(tmpname, "loc_")) {
3823 if (!g_seh_found)
3824 ferr(po, "call to loc_*\n");
3825 // eliminate_seh() must take care of it
3826 continue;
3827 }
16057ce1 3828 if (IS(tmpname, "__alloca_probe"))
3829 continue;
2eae9f23 3830 if (IS(tmpname, "__SEH_prolog")) {
3831 ferr_assert(po, g_seh_found == 0);
3832 g_seh_found = 2;
3833 continue;
3834 }
3835 if (IS(tmpname, "__SEH_epilog"))
3836 continue;
d4a985bd 3837
3838 // convert some calls to pseudo-ops
3839 for (l = 0; l < ARRAY_SIZE(pseudo_ops); l++) {
3840 if (!IS(tmpname, pseudo_ops[l].name))
3841 continue;
3842
3843 po->op = pseudo_ops[l].op;
3844 po->operand_cnt = 0;
3845 po->regmask_src = pseudo_ops[l].regmask_src;
3846 po->regmask_dst = pseudo_ops[l].regmask_dst;
3847 po->flags = pseudo_ops[l].flags;
3848 po->flags |= po->regmask_dst ? OPF_DATA : 0;
3849 break;
3850 }
3851 if (l < ARRAY_SIZE(pseudo_ops))
3852 continue;
3853
66bdb2b0 3854 pp_c = proto_parse(g_fhdr, tmpname, g_header_mode);
3855 if (!g_header_mode && pp_c == NULL)
3856 ferr(po, "proto_parse failed for call '%s'\n", tmpname);
3857
3858 if (pp_c != NULL) {
3859 pp = proto_clone(pp_c);
3860 my_assert_not(pp, NULL);
3861 }
3862 }
66bdb2b0 3863
3864 if (pp != NULL) {
3865 if (pp->is_fptr)
3866 check_func_pp(po, pp, "fptr var call");
56b49358 3867 if (pp->is_noreturn) {
66bdb2b0 3868 po->flags |= OPF_TAIL;
56b49358 3869 po->flags &= ~OPF_ATAIL; // most likely...
3870 }
66bdb2b0 3871 }
3872 po->pp = pp;
3873 continue;
3874 }
3875
3876 if (!(po->flags & OPF_JMP) || po->op == OP_RET)
3877 continue;
3878
3879 if (po->operand[0].type == OPT_REGMEM) {
3880 pd = try_resolve_jumptab(i, opcnt);
3881 if (pd == NULL)
3882 goto tailcall;
3883
3884 po->btj = pd;
3885 continue;
3886 }
3887
3888 for (l = 0; l < opcnt; l++) {
3889 if (g_labels[l] != NULL
3890 && IS(po->operand[0].name, g_labels[l]))
3891 {
3892 if (l == i + 1 && po->op == OP_JMP) {
3893 // yet another alignment type..
3894 po->flags |= OPF_RMD|OPF_DONE;
3895 break;
3896 }
3897 add_label_ref(&g_label_refs[l], i);
3898 po->bt_i = l;
3899 break;
3900 }
3901 }
3902
3903 if (po->bt_i != -1 || (po->flags & OPF_RMD))
3904 continue;
3905
3906 if (po->operand[0].type == OPT_LABEL)
3907 // assume tail call
3908 goto tailcall;
3909
3910 ferr(po, "unhandled branch\n");
3911
3912tailcall:
3913 po->op = OP_CALL;
3914 po->flags |= OPF_TAIL;
56b49358 3915 prev_op = i > 0 ? ops[i - 1].op : OP_UD2;
3916 if (prev_op == OP_POP)
66bdb2b0 3917 po->flags |= OPF_ATAIL;
56b49358 3918 if (g_stack_fsz + g_bp_frame == 0 && prev_op != OP_PUSH
3919 && (g_func_pp == NULL || g_func_pp->argc_stack > 0))
3920 {
3921 po->flags |= OPF_ATAIL;
3922 }
66bdb2b0 3923 i--; // reprocess
3924 }
9af2d373 3925}
3926
ba93cc12 3927static int resolve_origin(int i, const struct parsed_opr *opr,
3928 int magic, int *op_i, int *is_caller);
0a2c6da1 3929static void set_label(int i, const char *name);
ba93cc12 3930
2eae9f23 3931static void eliminate_seh_writes(int opcnt)
3932{
3933 const struct parsed_opr *opr;
3934 char ofs_reg[16];
3935 int offset;
3936 int i;
3937
3938 // assume all sf writes above g_seh_size to be seh related
3939 // (probably unsafe but oh well)
3940 for (i = 0; i < opcnt; i++) {
3941 if (ops[i].op != OP_MOV)
3942 continue;
3943 opr = &ops[i].operand[0];
3944 if (opr->type != OPT_REGMEM)
3945 continue;
3946 if (!is_stack_access(&ops[i], opr))
3947 continue;
3948
3949 offset = 0;
3950 parse_stack_access(&ops[i], opr->name, ofs_reg, &offset,
3951 NULL, NULL, 0);
3952 if (offset < 0 && offset >= -g_seh_size)
3953 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
3954 }
3955}
3956
0a2c6da1 3957static void eliminate_seh_finally(int opcnt)
3958{
3959 const char *target_name = NULL;
3960 const char *return_name = NULL;
3961 int exits[MAX_EXITS];
3962 int exit_count = 0;
3963 int call_i = -1;
3964 int target_i = -1;
3965 int return_i = -1;
3966 int tgend_i = -1;
3967 int i;
3968
3969 for (i = 0; i < opcnt; i++) {
3970 if (ops[i].op != OP_CALL)
3971 continue;
3972 if (!IS_START(opr_name(&ops[i], 0), "loc_"))
3973 continue;
3974 if (target_name != NULL)
3975 ferr(&ops[i], "multiple finally calls? (last was %s)\n",
3976 target_name);
3977 target_name = opr_name(&ops[i], 0);
3978 call_i = i;
3979
3980 if (g_labels[i + 1] == NULL)
3981 set_label(i + 1, "seh_fin_done");
3982 return_name = g_labels[i + 1];
3983 return_i = i + 1;
3984 }
3985
3986 if (call_i == -1)
3987 // no finally block
3988 return;
3989
3990 // find finally code (bt_i is not set because it's call)
3991 for (i = 0; i < opcnt; i++) {
3992 if (g_labels[i] == NULL)
3993 continue;
3994 if (!IS(g_labels[i], target_name))
3995 continue;
3996
3997 ferr_assert(&ops[i], target_i == -1);
3998 target_i = i;
3999 }
4000 ferr_assert(&ops[0], target_i != -1);
4001
4002 find_reachable_exits(target_i, opcnt, target_i + opcnt * 24,
4003 exits, &exit_count);
4004 ferr_assert(&ops[target_i], exit_count == 1);
4005 ferr_assert(&ops[target_i], ops[exits[0]].op == OP_RET);
4006 tgend_i = exits[0];
4007
4008 // convert to jumps, link
4009 ops[call_i].op = OP_JMP;
4010 ops[call_i].bt_i = target_i;
4011 add_label_ref(&g_label_refs[target_i], call_i);
4012
4013 ops[tgend_i].op = OP_JMP;
4014 ops[tgend_i].flags &= ~OPF_TAIL;
4015 ops[tgend_i].flags |= OPF_JMP;
4016 ops[tgend_i].bt_i = return_i;
4017 ops[tgend_i].operand_cnt = 1;
4018 ops[tgend_i].operand[0].type = OPT_LABEL;
4019 snprintf(ops[tgend_i].operand[0].name, NAMELEN, "%s", return_name);
4020 add_label_ref(&g_label_refs[return_i], tgend_i);
4021
4022 // rm seh finally entry code
4023 for (i = target_i - 1; i >= 0; i--) {
4024 if (g_labels[i] != NULL && g_label_refs[i].i != -1)
4025 return;
4026 if (ops[i].flags & OPF_CJMP)
4027 return;
4028 if (ops[i].flags & (OPF_JMP | OPF_TAIL))
4029 break;
4030 }
4031 for (i = target_i - 1; i >= 0; i--) {
4032 if (ops[i].flags & (OPF_JMP | OPF_TAIL))
4033 break;
4034 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
4035 }
4036}
4037
ba93cc12 4038static void eliminate_seh(int opcnt)
4039{
4040 int i, j, k, ret;
4041
4042 for (i = 0; i < opcnt; i++) {
4043 if (ops[i].op != OP_MOV)
4044 continue;
4045 if (ops[i].operand[0].segment != SEG_FS)
4046 continue;
4047 if (!IS(opr_name(&ops[i], 0), "0"))
4048 continue;
4049
4050 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
4051 if (ops[i].operand[1].reg == xSP) {
4052 for (j = i - 1; j >= 0; j--) {
4053 if (ops[j].op != OP_PUSH)
4054 continue;
4055 ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
4056 g_seh_size += 4;
4057 if (ops[j].operand[0].val == ~0)
4058 break;
4059 if (ops[j].operand[0].type == OPT_REG) {
4060 k = -1;
4061 ret = resolve_origin(j, &ops[j].operand[0],
4062 j + opcnt * 22, &k, NULL);
4063 if (ret == 1)
4064 ops[k].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
4065 }
4066 }
4067 if (j < 0)
4068 ferr(ops, "missing seh terminator\n");
4069 }
4070 else {
4071 k = -1;
4072 ret = resolve_origin(i, &ops[i].operand[1],
4073 i + opcnt * 23, &k, NULL);
4074 if (ret == 1)
4075 ops[k].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
4076 }
4077 }
4078
2eae9f23 4079 eliminate_seh_writes(opcnt);
0a2c6da1 4080 eliminate_seh_finally(opcnt);
2eae9f23 4081}
ba93cc12 4082
2eae9f23 4083static void eliminate_seh_calls(int opcnt)
4084{
4085 int epilog_found = 0;
4086 int i;
4087
4088 g_bp_frame = 1;
4089 g_seh_size = 0x10;
4090
4091 i = 0;
4092 ferr_assert(&ops[i], ops[i].op == OP_PUSH
4093 && ops[i].operand[0].type == OPT_CONST);
4094 g_stack_fsz = g_seh_size + ops[i].operand[0].val;
4095 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
4096
4097 i++;
4098 ferr_assert(&ops[i], ops[i].op == OP_PUSH
4099 && ops[i].operand[0].type == OPT_OFFSET);
4100 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
4101
4102 i++;
4103 ferr_assert(&ops[i], ops[i].op == OP_CALL
4104 && IS(opr_name(&ops[i], 0), "__SEH_prolog"));
4105 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
4106
4107 for (i++; i < opcnt; i++) {
4108 if (ops[i].op != OP_CALL)
ba93cc12 4109 continue;
2eae9f23 4110 if (!IS(opr_name(&ops[i], 0), "__SEH_epilog"))
ba93cc12 4111 continue;
4112
2eae9f23 4113 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
4114 epilog_found = 1;
4115 }
4116 ferr_assert(ops, epilog_found);
4117
4118 eliminate_seh_writes(opcnt);
0a2c6da1 4119 eliminate_seh_finally(opcnt);
2eae9f23 4120}
4121
4122static int scan_prologue(int i, int opcnt, int *ecx_push, int *esp_sub)
4123{
4124 int j;
4125
4126 for (; i < opcnt; i++)
4127 if (!(ops[i].flags & OPF_DONE))
4128 break;
4129
4130 while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
4131 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
4132 g_stack_fsz += 4;
4133 (*ecx_push)++;
4134 i++;
4135 }
4136
4137 for (; i < opcnt; i++) {
4138 if (i > 0 && g_labels[i] != NULL)
4139 break;
4140 if (ops[i].op == OP_PUSH || (ops[i].flags & (OPF_JMP|OPF_TAIL)))
4141 break;
4142 if (ops[i].op == OP_SUB && ops[i].operand[0].reg == xSP
4143 && ops[i].operand[1].type == OPT_CONST)
4144 {
4145 g_stack_fsz += opr_const(&ops[i], 1);
ba93cc12 4146 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
2eae9f23 4147 i++;
4148 *esp_sub = 1;
4149 break;
4150 }
4151 if (ops[i].op == OP_MOV && ops[i].operand[0].reg == xAX
4152 && ops[i].operand[1].type == OPT_CONST)
4153 {
4154 for (j = i + 1; j < opcnt; j++)
4155 if (!(ops[j].flags & OPF_DONE))
4156 break;
4157 if (ops[j].op == OP_CALL
4158 && IS(opr_name(&ops[j], 0), "__alloca_probe"))
4159 {
4160 g_stack_fsz += opr_const(&ops[i], 1);
4161 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
4162 ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
4163 i = j + 1;
4164 *esp_sub = 1;
4165 }
4166 break;
4167 }
ba93cc12 4168 }
2eae9f23 4169
4170 return i;
ba93cc12 4171}
4172
f9327ad4 4173static void scan_prologue_epilogue(int opcnt, int *stack_align)
9af2d373 4174{
16057ce1 4175 int ecx_push = 0, esp_sub = 0, pusha = 0;
4176 int sandard_epilogue;
9af2d373 4177 int found;
4178 int i, j, l;
4179
2eae9f23 4180 if (g_seh_found == 2) {
4181 eliminate_seh_calls(opcnt);
4182 return;
4183 }
ba93cc12 4184 if (g_seh_found) {
4185 eliminate_seh(opcnt);
4186 // ida treats seh as part of sf
4187 g_stack_fsz = g_seh_size;
4188 esp_sub = 1;
4189 }
4190
9af2d373 4191 if (ops[0].op == OP_PUSH && IS(opr_name(&ops[0], 0), "ebp")
4192 && ops[1].op == OP_MOV
4193 && IS(opr_name(&ops[1], 0), "ebp")
4194 && IS(opr_name(&ops[1], 1), "esp"))
4195 {
4196 g_bp_frame = 1;
b2bd20c0 4197 ops[0].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
4198 ops[1].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
ba93cc12 4199
4200 for (i = 2; i < opcnt; i++)
4201 if (!(ops[i].flags & OPF_DONE))
4202 break;
9af2d373 4203
16057ce1 4204 if (ops[i].op == OP_PUSHA) {
4205 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
4206 pusha = 1;
4207 i++;
4208 }
4209
f9327ad4 4210 if (ops[i].op == OP_AND && ops[i].operand[0].reg == xSP
4211 && ops[i].operand[1].type == OPT_CONST)
4212 {
4213 l = ops[i].operand[1].val;
4214 j = ffs(l) - 1;
4215 if (j == -1 || (l >> j) != -1)
4216 ferr(&ops[i], "unhandled esp align: %x\n", l);
4217 if (stack_align != NULL)
4218 *stack_align = 1 << j;
4219 ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
4220 i++;
4221 }
4222
2eae9f23 4223 i = scan_prologue(i, opcnt, &ecx_push, &esp_sub);
9af2d373 4224
4225 found = 0;
4226 do {
4227 for (; i < opcnt; i++)
66bdb2b0 4228 if (ops[i].flags & OPF_TAIL)
9af2d373 4229 break;
4230 j = i - 1;
4231 if (i == opcnt && (ops[j].flags & OPF_JMP)) {
66bdb2b0 4232 if (ops[j].bt_i != -1 || ops[j].btj != NULL)
4233 break;
4234 i--;
9af2d373 4235 j--;
4236 }
4237
16057ce1 4238 sandard_epilogue = 0;
4239 if (ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ebp"))
4240 {
4241 ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
4242 // the standard epilogue is sometimes even used without a sf
4243 if (ops[j - 1].op == OP_MOV
4244 && IS(opr_name(&ops[j - 1], 0), "esp")
4245 && IS(opr_name(&ops[j - 1], 1), "ebp"))
4246 sandard_epilogue = 1;
4247 }
4248 else if (ops[j].op == OP_LEAVE)
9af2d373 4249 {
b2bd20c0 4250 ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
16057ce1 4251 sandard_epilogue = 1;
9af2d373 4252 }
66bdb2b0 4253 else if (ops[i].op == OP_CALL && ops[i].pp != NULL
4254 && ops[i].pp->is_noreturn)
4255 {
4256 // on noreturn, msvc sometimes cleans stack, sometimes not
4257 i++;
4258 found = 1;
4259 continue;
4260 }
9af2d373 4261 else if (!(g_ida_func_attr & IDAFA_NORETURN))
4262 ferr(&ops[j], "'pop ebp' expected\n");
4263
16057ce1 4264 if (g_stack_fsz != 0 || sandard_epilogue) {
bfacdc83 4265 if (ops[j].op == OP_LEAVE)
4266 j--;
16057ce1 4267 else if (sandard_epilogue) // mov esp, ebp
9af2d373 4268 {
b2bd20c0 4269 ops[j - 1].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
bfacdc83 4270 j -= 2;
9af2d373 4271 }
bfacdc83 4272 else if (!(g_ida_func_attr & IDAFA_NORETURN))
9af2d373 4273 {
bfacdc83 4274 ferr(&ops[j], "esp restore expected\n");
9af2d373 4275 }
4276
bfacdc83 4277 if (ecx_push && j >= 0 && ops[j].op == OP_POP
4278 && IS(opr_name(&ops[j], 0), "ecx"))
9af2d373 4279 {
bfacdc83 4280 ferr(&ops[j], "unexpected ecx pop\n");
9af2d373 4281 }
4282 }
4283
16057ce1 4284 if (pusha) {
4285 if (ops[j].op == OP_POPA)
4286 ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
4287 else
4288 ferr(&ops[j], "popa expected\n");
4289 }
4290
9af2d373 4291 found = 1;
4292 i++;
4293 } while (i < opcnt);
4294
66bdb2b0 4295 if (!found)
4296 ferr(ops, "missing ebp epilogue\n");
9af2d373 4297 return;
4298 }
4299
4300 // non-bp frame
2eae9f23 4301 i = scan_prologue(0, opcnt, &ecx_push, &esp_sub);
9af2d373 4302
4303 if (ecx_push && !esp_sub) {
4304 // could actually be args for a call..
4305 for (; i < opcnt; i++)
4306 if (ops[i].op != OP_PUSH)
4307 break;
4308
4309 if (ops[i].op == OP_CALL && ops[i].operand[0].type == OPT_LABEL) {
4310 const struct parsed_proto *pp;
4311 pp = proto_parse(g_fhdr, opr_name(&ops[i], 0), 1);
4312 j = pp ? pp->argc_stack : 0;
4313 while (i > 0 && j > 0) {
4314 i--;
4315 if (ops[i].op == OP_PUSH) {
b2bd20c0 4316 ops[i].flags &= ~(OPF_RMD | OPF_DONE | OPF_NOREGS);
9af2d373 4317 j--;
4318 }
4319 }
4320 if (j != 0)
4321 ferr(&ops[i], "unhandled prologue\n");
4322
4323 // recheck
ba93cc12 4324 i = ecx_push = 0;
4325 g_stack_fsz = g_seh_size;
9af2d373 4326 while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
4327 if (!(ops[i].flags & OPF_RMD))
4328 break;
4329 g_stack_fsz += 4;
4330 ecx_push++;
4331 i++;
4332 }
4333 }
4334 }
4335
4336 found = 0;
4337 if (ecx_push || esp_sub)
4338 {
4339 g_sp_frame = 1;
4340
9af2d373 4341 do {
4342 for (; i < opcnt; i++)
66bdb2b0 4343 if (ops[i].flags & OPF_TAIL)
9af2d373 4344 break;
fe18df39 4345
9af2d373 4346 j = i - 1;
4347 if (i == opcnt && (ops[j].flags & OPF_JMP)) {
66bdb2b0 4348 if (ops[j].bt_i != -1 || ops[j].btj != NULL)
4349 break;
4350 i--;
9af2d373 4351 j--;
4352 }
2eae9f23 4353 else if (i < opcnt && (ops[i].flags & OPF_ATAIL)) {
4354 // skip arg updates for arg-reuse tailcall
4355 for (; j >= 0; j--) {
4356 if (ops[j].op != OP_MOV)
4357 break;
4358 if (ops[j].operand[0].type != OPT_REGMEM)
4359 break;
4360 if (strstr(ops[j].operand[0].name, "arg_") == NULL)
4361 break;
4362 }
4363 }
9af2d373 4364
ba93cc12 4365 if (ecx_push > 0 && !esp_sub) {
2eae9f23 4366 for (l = 0; l < ecx_push && j >= 0; l++) {
9af2d373 4367 if (ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ecx"))
4368 /* pop ecx */;
4369 else if (ops[j].op == OP_ADD
4370 && IS(opr_name(&ops[j], 0), "esp")
4371 && ops[j].operand[1].type == OPT_CONST)
4372 {
4373 /* add esp, N */
5e49b270 4374 l += ops[j].operand[1].val / 4 - 1;
9af2d373 4375 }
4376 else
56b49358 4377 break;
9af2d373 4378
b2bd20c0 4379 ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
9af2d373 4380 j--;
4381 }
56b49358 4382 if (l != ecx_push) {
4383 if (i < opcnt && ops[i].op == OP_CALL
4384 && ops[i].pp != NULL && ops[i].pp->is_noreturn)
4385 {
4386 // noreturn tailcall with no epilogue
4387 i++;
4388 found = 1;
4389 continue;
4390 }
9af2d373 4391 ferr(&ops[j], "epilogue scan failed\n");
56b49358 4392 }
9af2d373 4393
4394 found = 1;
4395 }
4396
4397 if (esp_sub) {
4398 if (ops[j].op != OP_ADD
4399 || !IS(opr_name(&ops[j], 0), "esp")
71d50aa7 4400 || ops[j].operand[1].type != OPT_CONST)
622eb2ef 4401 {
56b49358 4402 if (i < opcnt && ops[i].op == OP_CALL
4403 && ops[i].pp != NULL && ops[i].pp->is_noreturn)
622eb2ef 4404 {
4405 // noreturn tailcall with no epilogue
4406 i++;
56b49358 4407 found = 1;
622eb2ef 4408 continue;
4409 }
9af2d373 4410 ferr(&ops[j], "'add esp' expected\n");
622eb2ef 4411 }
9af2d373 4412
71d50aa7 4413 if (ops[j].operand[1].val < g_stack_fsz)
4414 ferr(&ops[j], "esp adj is too low (need %d)\n", g_stack_fsz);
4415
4416 ops[j].operand[1].val -= g_stack_fsz; // for stack arg scanner
4417 if (ops[j].operand[1].val == 0)
4418 ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
9af2d373 4419 found = 1;
4420 }
4421
4422 i++;
4423 } while (i < opcnt);
66bdb2b0 4424
4425 if (!found)
4426 ferr(ops, "missing esp epilogue\n");
9af2d373 4427 }
4428}
4429
23fd0b11 4430// find an instruction that changed opr before i op
db63af51 4431// *op_i must be set to -1 by the caller
865f1aca 4432// *is_caller is set to 1 if one source is determined to be g_func arg
92d715b6 4433// returns 1 if found, *op_i is then set to origin
865f1aca 4434// returns -1 if multiple origins are found
23fd0b11 4435static int resolve_origin(int i, const struct parsed_opr *opr,
92d715b6 4436 int magic, int *op_i, int *is_caller)
1f84f6b3 4437{
4438 struct label_ref *lr;
4439 int ret = 0;
4440
1f84f6b3 4441 while (1) {
d7857c3a 4442 if (g_labels[i] != NULL) {
1f84f6b3 4443 lr = &g_label_refs[i];
92d715b6 4444 for (; lr != NULL; lr = lr->next) {
4445 check_i(&ops[i], lr->i);
4446 ret |= resolve_origin(lr->i, opr, magic, op_i, is_caller);
4447 }
1f84f6b3 4448 if (i > 0 && LAST_OP(i - 1))
4449 return ret;
4450 }
4451
4452 i--;
92d715b6 4453 if (i < 0) {
4454 if (is_caller != NULL)
4455 *is_caller = 1;
1f84f6b3 4456 return -1;
92d715b6 4457 }
1f84f6b3 4458
4459 if (ops[i].cc_scratch == magic)
ebc4dc43 4460 return ret;
1f84f6b3 4461 ops[i].cc_scratch = magic;
4462
4463 if (!(ops[i].flags & OPF_DATA))
4464 continue;
4465 if (!is_opr_modified(opr, &ops[i]))
4466 continue;
23fd0b11 4467
4468 if (*op_i >= 0) {
93b5bd18 4469 if (*op_i == i || are_ops_same(&ops[*op_i], &ops[i]))
ebc4dc43 4470 return ret | 1;
4471
23fd0b11 4472 return -1;
4473 }
4474
4475 *op_i = i;
ebc4dc43 4476 return ret | 1;
23fd0b11 4477 }
4478}
4479
db63af51 4480// find an instruction that previously referenced opr
4481// if multiple results are found - fail
4482// *op_i must be set to -1 by the caller
4483// returns 1 if found, *op_i is then set to referencer insn
4484static int resolve_last_ref(int i, const struct parsed_opr *opr,
4485 int magic, int *op_i)
4486{
4487 struct label_ref *lr;
4488 int ret = 0;
4489
db63af51 4490 while (1) {
4491 if (g_labels[i] != NULL) {
4492 lr = &g_label_refs[i];
4493 for (; lr != NULL; lr = lr->next) {
4494 check_i(&ops[i], lr->i);
4495 ret |= resolve_last_ref(lr->i, opr, magic, op_i);
4496 }
4497 if (i > 0 && LAST_OP(i - 1))
4498 return ret;
4499 }
4500
4501 i--;
4502 if (i < 0)
4503 return -1;
4504
4505 if (ops[i].cc_scratch == magic)
4506 return 0;
4507 ops[i].cc_scratch = magic;
4508
4509 if (!is_opr_referenced(opr, &ops[i]))
4510 continue;
4511
4512 if (*op_i >= 0)
4513 return -1;
4514
4515 *op_i = i;
4516 return 1;
4517 }
4518}
4519
16057ce1 4520// adjust datap of all reachable 'op' insns when moving back
4521// returns 1 if at least 1 op was found
4522// returns -1 if path without an op was found
4523static int adjust_prev_op(int i, enum op_op op, int magic, void *datap)
4524{
4525 struct label_ref *lr;
4526 int ret = 0;
4527
4528 if (ops[i].cc_scratch == magic)
4529 return 0;
4530 ops[i].cc_scratch = magic;
4531
4532 while (1) {
4533 if (g_labels[i] != NULL) {
4534 lr = &g_label_refs[i];
4535 for (; lr != NULL; lr = lr->next) {
4536 check_i(&ops[i], lr->i);
4537 ret |= adjust_prev_op(lr->i, op, magic, datap);
4538 }
4539 if (i > 0 && LAST_OP(i - 1))
4540 return ret;
4541 }
4542
4543 i--;
4544 if (i < 0)
4545 return -1;
4546
4547 if (ops[i].cc_scratch == magic)
4548 return 0;
4549 ops[i].cc_scratch = magic;
4550
4551 if (ops[i].op != op)
4552 continue;
4553
4554 ops[i].datap = datap;
4555 return 1;
4556 }
4557}
4558
db63af51 4559// find next instruction that reads opr
db63af51 4560// *op_i must be set to -1 by the caller
b2bd20c0 4561// on return, *op_i is set to first referencer insn
4562// returns 1 if exactly 1 referencer is found
db63af51 4563static int find_next_read(int i, int opcnt,
4564 const struct parsed_opr *opr, int magic, int *op_i)
4565{
4566 struct parsed_op *po;
4567 int j, ret = 0;
4568
4569 for (; i < opcnt; i++)
4570 {
4571 if (ops[i].cc_scratch == magic)
b2bd20c0 4572 return ret;
db63af51 4573 ops[i].cc_scratch = magic;
4574
4575 po = &ops[i];
4576 if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
4577 if (po->btj != NULL) {
4578 // jumptable
4579 for (j = 0; j < po->btj->count; j++) {
4580 check_i(po, po->btj->d[j].bt_i);
4581 ret |= find_next_read(po->btj->d[j].bt_i, opcnt, opr,
4582 magic, op_i);
4583 }
4584 return ret;
4585 }
4586
4587 if (po->flags & OPF_RMD)
4588 continue;
4589 check_i(po, po->bt_i);
4590 if (po->flags & OPF_CJMP) {
b2bd20c0 4591 ret |= find_next_read(po->bt_i, opcnt, opr, magic, op_i);
db63af51 4592 if (ret < 0)
4593 return ret;
4594 }
b2bd20c0 4595 else
4596 i = po->bt_i - 1;
db63af51 4597 continue;
4598 }
4599
4600 if (!is_opr_read(opr, po)) {
16057ce1 4601 int full_opr = 1;
4602 if (opr->type == OPT_REG && po->operand[0].type == OPT_REG
4603 && opr->reg == po->operand[0].reg && (po->flags & OPF_DATA))
acd03176 4604 {
16057ce1 4605 full_opr = po->operand[0].lmod >= opr->lmod;
4606 }
4607 if (is_opr_modified(opr, po) && full_opr) {
db63af51 4608 // it's overwritten
b2bd20c0 4609 return ret;
acd03176 4610 }
db63af51 4611 if (po->flags & OPF_TAIL)
b2bd20c0 4612 return ret;
db63af51 4613 continue;
4614 }
4615
4616 if (*op_i >= 0)
4617 return -1;
4618
4619 *op_i = i;
4620 return 1;
4621 }
4622
4623 return 0;
4624}
4625
16057ce1 4626// find next instruction that reads opr
4627// *op_i must be set to -1 by the caller
4628// on return, *op_i is set to first flag user insn
4629// returns 1 if exactly 1 flag user is found
4630static int find_next_flag_use(int i, int opcnt, int magic, int *op_i)
4631{
4632 struct parsed_op *po;
4633 int j, ret = 0;
4634
4635 for (; i < opcnt; i++)
4636 {
4637 if (ops[i].cc_scratch == magic)
4638 return ret;
4639 ops[i].cc_scratch = magic;
4640
4641 po = &ops[i];
4642 if (po->op == OP_CALL)
4643 return -1;
4644 if (po->flags & OPF_JMP) {
4645 if (po->btj != NULL) {
4646 // jumptable
4647 for (j = 0; j < po->btj->count; j++) {
4648 check_i(po, po->btj->d[j].bt_i);
4649 ret |= find_next_flag_use(po->btj->d[j].bt_i, opcnt,
4650 magic, op_i);
4651 }
4652 return ret;
4653 }
4654
4655 if (po->flags & OPF_RMD)
4656 continue;
4657 check_i(po, po->bt_i);
4658 if (po->flags & OPF_CJMP)
4659 goto found;
4660 else
4661 i = po->bt_i - 1;
4662 continue;
4663 }
4664
4665 if (!(po->flags & OPF_CC)) {
4666 if (po->flags & OPF_FLAGS)
4667 // flags changed
4668 return ret;
4669 if (po->flags & OPF_TAIL)
4670 return ret;
4671 continue;
4672 }
4673
4674found:
4675 if (*op_i >= 0)
4676 return -1;
4677
4678 *op_i = i;
4679 return 1;
4680 }
4681
4682 return 0;
4683}
4684
23fd0b11 4685static int try_resolve_const(int i, const struct parsed_opr *opr,
4686 int magic, unsigned int *val)
4687{
4688 int s_i = -1;
92d715b6 4689 int ret;
23fd0b11 4690
92d715b6 4691 ret = resolve_origin(i, opr, magic, &s_i, NULL);
23fd0b11 4692 if (ret == 1) {
4693 i = s_i;
1f84f6b3 4694 if (ops[i].op != OP_MOV && ops[i].operand[1].type != OPT_CONST)
4695 return -1;
4696
4697 *val = ops[i].operand[1].val;
4698 return 1;
4699 }
23fd0b11 4700
4701 return -1;
1f84f6b3 4702}
4703
16057ce1 4704static int resolve_used_bits(int i, int opcnt, int reg,
4705 int *mask, int *is_z_check)
4706{
4707 struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_WORD, reg);
4708 int j = -1, k = -1;
4709 int ret;
4710
4711 ret = find_next_read(i, opcnt, &opr, i + opcnt * 20, &j);
4712 if (ret != 1)
4713 return -1;
4714
4715 find_next_read(j + 1, opcnt, &opr, i + opcnt * 20 + 1, &k);
4716 if (k != -1) {
4717 fnote(&ops[j], "(first read)\n");
4718 ferr(&ops[k], "TODO: bit resolve: multiple readers\n");
4719 }
4720
4721 if (ops[j].op != OP_TEST || ops[j].operand[1].type != OPT_CONST)
4722 ferr(&ops[j], "TODO: bit resolve: not a const test\n");
4723
4724 ferr_assert(&ops[j], ops[j].operand[0].type == OPT_REG);
4725 ferr_assert(&ops[j], ops[j].operand[0].reg == reg);
4726
4727 *mask = ops[j].operand[1].val;
4728 if (ops[j].operand[0].lmod == OPLM_BYTE
4729 && ops[j].operand[0].name[1] == 'h')
4730 {
4731 *mask <<= 8;
4732 }
4733 ferr_assert(&ops[j], (*mask & ~0xffff) == 0);
4734
4735 *is_z_check = 0;
4736 ret = find_next_flag_use(j + 1, opcnt, i + opcnt * 20 + 2, &k);
4737 if (ret == 1)
4738 *is_z_check = ops[k].pfo == PFO_Z;
4739
4740 return 0;
4741}
4742
93b5bd18 4743static const struct parsed_proto *resolve_deref(int i, int magic,
4744 struct parsed_opr *opr, int level)
4745{
4746 struct parsed_opr opr_s = OPR_INIT(OPT_REG, OPLM_DWORD, 0);
4747 const struct parsed_proto *pp = NULL;
4748 int from_caller = 0;
4749 char s_reg[4];
4750 int offset = 0;
4751 int len = 0;
4752 int j = -1;
4753 int k = -1;
4754 int reg;
4755 int ret;
4756
4757 ret = sscanf(opr->name, "%3s+%x%n", s_reg, &offset, &len);
4758 if (ret != 2 || len != strlen(opr->name)) {
4759 ret = sscanf(opr->name, "%3s%n", s_reg, &len);
4760 if (ret != 1 || len != strlen(opr->name))
4761 return NULL;
4762 }
4763
4764 reg = char_array_i(regs_r32, ARRAY_SIZE(regs_r32), s_reg);
4765 if (reg < 0)
4766 return NULL;
4767
4768 opr_s.reg = reg;
4769 ret = resolve_origin(i, &opr_s, i + magic, &j, NULL);
4770 if (ret != 1)
4771 return NULL;
4772
4773 if (ops[j].op == OP_MOV && ops[j].operand[1].type == OPT_REGMEM
4774 && strlen(ops[j].operand[1].name) == 3
4775 && ops[j].operand[0].lmod == OPLM_DWORD
4776 && ops[j].pp == NULL // no hint
4777 && level == 0)
4778 {
4779 // allow one simple dereference (com/directx)
4780 reg = char_array_i(regs_r32, ARRAY_SIZE(regs_r32),
4781 ops[j].operand[1].name);
4782 if (reg < 0)
4783 return NULL;
4784 opr_s.reg = reg;
4785 ret = resolve_origin(j, &opr_s, j + magic, &k, NULL);
4786 if (ret != 1)
4787 return NULL;
4788 j = k;
4789 }
4790 if (ops[j].op != OP_MOV || ops[j].operand[0].lmod != OPLM_DWORD)
4791 return NULL;
4792
4793 if (ops[j].pp != NULL) {
4794 // type hint in asm
4795 pp = ops[j].pp;
4796 }
4797 else if (ops[j].operand[1].type == OPT_REGMEM) {
4798 pp = try_recover_pp(&ops[j], &ops[j].operand[1], 0, NULL);
4799 if (pp == NULL) {
4800 // maybe structure ptr in structure
4801 pp = resolve_deref(j, magic, &ops[j].operand[1], level + 1);
4802 }
4803 }
4804 else if (ops[j].operand[1].type == OPT_LABEL)
4805 pp = proto_parse(g_fhdr, ops[j].operand[1].name, g_quiet_pp);
4806 else if (ops[j].operand[1].type == OPT_REG) {
4807 // maybe arg reg?
4808 k = -1;
4809 ret = resolve_origin(j, &ops[j].operand[1], i + magic,
4810 &k, &from_caller);
4811 if (ret != 1 && from_caller && k == -1 && g_func_pp != NULL) {
4812 for (k = 0; k < g_func_pp->argc; k++) {
4813 if (g_func_pp->arg[k].reg == NULL)
4814 continue;
4815 if (IS(g_func_pp->arg[k].reg, ops[j].operand[1].name)) {
4816 pp = g_func_pp->arg[k].pp;
4817 break;
4818 }
4819 }
4820 }
4821 }
4822
4823 if (pp == NULL)
4824 return NULL;
4825 if (pp->is_func || pp->is_fptr || !pp->type.is_struct) {
4826 if (offset != 0)
4827 ferr(&ops[j], "expected struct, got '%s %s'\n",
4828 pp->type.name, pp->name);
4829 return NULL;
4830 }
4831
4832 return proto_lookup_struct(g_fhdr, pp->type.name, offset);
4833}
4834
865f1aca 4835static const struct parsed_proto *resolve_icall(int i, int opcnt,
4836 int *pp_i, int *multi_src)
4837{
4838 const struct parsed_proto *pp = NULL;
4839 int search_advice = 0;
865f1aca 4840
4841 *multi_src = 0;
4842 *pp_i = -1;
4843
4844 switch (ops[i].operand[0].type) {
4845 case OPT_REGMEM:
4846 // try to resolve struct member calls
93b5bd18 4847 pp = resolve_deref(i, i + opcnt * 19, &ops[i].operand[0], 0);
4848 if (pp != NULL)
865f1aca 4849 break;
865f1aca 4850 // fallthrough
4851 case OPT_LABEL:
4852 case OPT_OFFSET:
93b5bd18 4853 pp = try_recover_pp(&ops[i], &ops[i].operand[0],
4854 1, &search_advice);
865f1aca 4855 if (!search_advice)
4856 break;
4857 // fallthrough
4858 default:
4859 scan_for_call_type(i, &ops[i].operand[0], i + opcnt * 9, &pp,
4860 pp_i, multi_src);
4861 break;
4862 }
4863
4864 return pp;
4865}
4866
26677139 4867static struct parsed_proto *process_call_early(int i, int opcnt,
4868 int *adj_i)
4869{
4870 struct parsed_op *po = &ops[i];
4871 struct parsed_proto *pp;
4872 int multipath = 0;
4873 int adj = 0;
bfacdc83 4874 int j, ret;
26677139 4875
4876 pp = po->pp;
4877 if (pp == NULL || pp->is_vararg || pp->argc_reg != 0)
4878 // leave for later
4879 return NULL;
4880
4881 // look for and make use of esp adjust
4882 *adj_i = ret = -1;
4883 if (!pp->is_stdcall && pp->argc_stack > 0)
4884 ret = scan_for_esp_adjust(i + 1, opcnt,
ee2361b9 4885 pp->argc_stack * 4, &adj, &multipath, 0);
26677139 4886 if (ret >= 0) {
4887 if (pp->argc_stack > adj / 4)
4888 return NULL;
4889 if (multipath)
4890 return NULL;
bfacdc83 4891 if (ops[ret].op == OP_POP) {
4892 for (j = 1; j < adj / 4; j++) {
4893 if (ops[ret + j].op != OP_POP
4894 || ops[ret + j].operand[0].reg != xCX)
4895 {
4896 return NULL;
4897 }
4898 }
4899 }
26677139 4900 }
4901
4902 *adj_i = ret;
4903 return pp;
4904}
4905
9af2d373 4906static struct parsed_proto *process_call(int i, int opcnt)
4907{
4908 struct parsed_op *po = &ops[i];
4909 const struct parsed_proto *pp_c;
4910 struct parsed_proto *pp;
4911 const char *tmpname;
db63af51 4912 int call_i = -1, ref_i = -1;
26677139 4913 int adj = 0, multipath = 0;
9af2d373 4914 int ret, arg;
4915
4916 tmpname = opr_name(po, 0);
4917 pp = po->pp;
4918 if (pp == NULL)
4919 {
4920 // indirect call
db63af51 4921 pp_c = resolve_icall(i, opcnt, &call_i, &multipath);
9af2d373 4922 if (pp_c != NULL) {
4923 if (!pp_c->is_func && !pp_c->is_fptr)
4924 ferr(po, "call to non-func: %s\n", pp_c->name);
4925 pp = proto_clone(pp_c);
4926 my_assert_not(pp, NULL);
26677139 4927 if (multipath)
9af2d373 4928 // not resolved just to single func
4929 pp->is_fptr = 1;
4930
4931 switch (po->operand[0].type) {
4932 case OPT_REG:
4933 // we resolved this call and no longer need the register
4934 po->regmask_src &= ~(1 << po->operand[0].reg);
db63af51 4935
4936 if (!multipath && i != call_i && ops[call_i].op == OP_MOV
4937 && ops[call_i].operand[1].type == OPT_LABEL)
4938 {
4939 // no other source users?
e83ea7ed 4940 ret = resolve_last_ref(i, &po->operand[0], i + opcnt * 10,
db63af51 4941 &ref_i);
4942 if (ret == 1 && call_i == ref_i) {
4943 // and nothing uses it after us?
4944 ref_i = -1;
b2bd20c0 4945 find_next_read(i + 1, opcnt, &po->operand[0],
4946 i + opcnt * 11, &ref_i);
4947 if (ref_i == -1)
db63af51 4948 // then also don't need the source mov
b2bd20c0 4949 ops[call_i].flags |= OPF_RMD | OPF_NOREGS;
db63af51 4950 }
4951 }
9af2d373 4952 break;
4953 case OPT_REGMEM:
4954 pp->is_fptr = 1;
4955 break;
4956 default:
4957 break;
4958 }
4959 }
4960 if (pp == NULL) {
4961 pp = calloc(1, sizeof(*pp));
4962 my_assert_not(pp, NULL);
4963
4964 pp->is_fptr = 1;
ee2361b9 4965 ret = scan_for_esp_adjust(i + 1, opcnt,
bfacdc83 4966 -1, &adj, &multipath, 0);
26677139 4967 if (ret < 0 || adj < 0) {
9af2d373 4968 if (!g_allow_regfunc)
4969 ferr(po, "non-__cdecl indirect call unhandled yet\n");
4970 pp->is_unresolved = 1;
26677139 4971 adj = 0;
9af2d373 4972 }
26677139 4973 adj /= 4;
4974 if (adj > ARRAY_SIZE(pp->arg))
4975 ferr(po, "esp adjust too large: %d\n", adj);
9af2d373 4976 pp->ret_type.name = strdup("int");
26677139 4977 pp->argc = pp->argc_stack = adj;
9af2d373 4978 for (arg = 0; arg < pp->argc; arg++)
4979 pp->arg[arg].type.name = strdup("int");
4980 }
4981 po->pp = pp;
4982 }
4983
4984 // look for and make use of esp adjust
91ca764a 4985 multipath = 0;
9af2d373 4986 ret = -1;
bfacdc83 4987 if (!pp->is_stdcall && pp->argc_stack > 0) {
4988 int adj_expect = pp->is_vararg ? -1 : pp->argc_stack * 4;
9af2d373 4989 ret = scan_for_esp_adjust(i + 1, opcnt,
bfacdc83 4990 adj_expect, &adj, &multipath, 0);
4991 }
9af2d373 4992 if (ret >= 0) {
4993 if (pp->is_vararg) {
26677139 4994 if (adj / 4 < pp->argc_stack) {
4995 fnote(po, "(this call)\n");
4996 ferr(&ops[ret], "esp adjust is too small: %x < %x\n",
4997 adj, pp->argc_stack * 4);
4998 }
9af2d373 4999 // modify pp to make it have varargs as normal args
5000 arg = pp->argc;
26677139 5001 pp->argc += adj / 4 - pp->argc_stack;
9af2d373 5002 for (; arg < pp->argc; arg++) {
5003 pp->arg[arg].type.name = strdup("int");
5004 pp->argc_stack++;
5005 }
5006 if (pp->argc > ARRAY_SIZE(pp->arg))
5007 ferr(po, "too many args for '%s'\n", tmpname);
5008 }
26677139 5009 if (pp->argc_stack > adj / 4) {
56b49358 5010 if (pp->is_noreturn)
5011 // assume no stack adjust was emited
5012 goto out;
9af2d373 5013 fnote(po, "(this call)\n");
5014 ferr(&ops[ret], "stack tracking failed for '%s': %x %x\n",
26677139 5015 tmpname, pp->argc_stack * 4, adj);
9af2d373 5016 }
5017
ee2361b9 5018 scan_for_esp_adjust(i + 1, opcnt,
5019 pp->argc_stack * 4, &adj, &multipath, 1);
9af2d373 5020 }
5021 else if (pp->is_vararg)
5022 ferr(po, "missing esp_adjust for vararg func '%s'\n",
5023 pp->name);
5024
56b49358 5025out:
9af2d373 5026 return pp;
5027}
5028
b62264bc 5029static void mark_float_arg(struct parsed_op *po,
5030 struct parsed_proto *pp, int arg, int *regmask_ffca)
5031{
5032 po->p_argnext = -1;
5033 po->p_argnum = arg + 1;
5034 ferr_assert(po, pp->arg[arg].datap == NULL);
5035 pp->arg[arg].datap = po;
5036 po->flags |= OPF_DONE | OPF_FARGNR | OPF_FARG;
5037 if (regmask_ffca != NULL)
5038 *regmask_ffca |= 1 << arg;
5039}
5040
5041static int check_for_stp(int i, int i_to)
5042{
5043 struct parsed_op *po;
5044
5045 for (; i < i_to; i++) {
5046 po = &ops[i];
5047 if (po->op == OP_FST)
5048 return i;
5049 if (g_labels[i] != NULL || (po->flags & OPF_JMP))
5050 return -1;
5051 if (po->op == OP_CALL || po->op == OP_PUSH || po->op == OP_POP)
5052 return -1;
5053 if (po->op == OP_ADD && po->operand[0].reg == xSP)
5054 return -1;
5055 }
5056
5057 return -1;
5058}
5059
30620174 5060static int collect_call_args_no_push(int i, struct parsed_proto *pp,
5061 int *regmask_ffca)
5062{
5063 struct parsed_op *po;
5064 int offset = 0;
5065 int base_arg;
5066 int j, arg;
5067 int ret;
5068
5069 for (base_arg = 0; base_arg < pp->argc; base_arg++)
5070 if (pp->arg[base_arg].reg == NULL)
5071 break;
5072
5073 for (j = i; j > 0; )
5074 {
5075 ferr_assert(&ops[j], g_labels[j] == NULL);
5076 j--;
5077
5078 po = &ops[j];
5079 ferr_assert(po, po->op != OP_PUSH);
5080 if (po->op == OP_FST)
5081 {
5082 if (po->operand[0].type != OPT_REGMEM)
5083 continue;
5084 ret = parse_stack_esp_offset(po, po->operand[0].name, &offset);
5085 if (ret != 0)
5086 continue;
71d50aa7 5087 if (offset < 0 || offset >= pp->argc_stack * 4 || (offset & 3)) {
5088 //ferr(po, "offset %d, %d args\n", offset, pp->argc_stack);
5089 continue;
5090 }
30620174 5091
5092 arg = base_arg + offset / 4;
b62264bc 5093 mark_float_arg(po, pp, arg, regmask_ffca);
30620174 5094 }
5095 else if (po->op == OP_SUB && po->operand[0].reg == xSP
5096 && po->operand[1].type == OPT_CONST)
5097 {
5098 po->flags |= OPF_RMD | OPF_DONE | OPF_FARGNR | OPF_FARG;
5099 break;
5100 }
5101 }
5102
5103 for (arg = base_arg; arg < pp->argc; arg++) {
5104 ferr_assert(&ops[i], pp->arg[arg].reg == NULL);
5105 po = pp->arg[arg].datap;
5106 if (po == NULL)
5107 ferr(&ops[i], "arg %d/%d not found\n", arg, pp->argc);
5108 if (po->operand[0].lmod == OPLM_QWORD)
5109 arg++;
5110 }
5111
5112 return 0;
5113}
5114
5115static int collect_call_args_early(int i, struct parsed_proto *pp,
5116 int *regmask, int *regmask_ffca)
26677139 5117{
30620174 5118 struct parsed_op *po;
26677139 5119 int arg, ret;
b62264bc 5120 int offset;
5121 int j, k;
26677139 5122
5123 for (arg = 0; arg < pp->argc; arg++)
5124 if (pp->arg[arg].reg == NULL)
5125 break;
5126
5127 // first see if it can be easily done
5128 for (j = i; j > 0 && arg < pp->argc; )
5129 {
5130 if (g_labels[j] != NULL)
5131 return -1;
5132 j--;
5133
30620174 5134 po = &ops[j];
5135 if (po->op == OP_CALL)
26677139 5136 return -1;
30620174 5137 else if (po->op == OP_ADD && po->operand[0].reg == xSP)
26677139 5138 return -1;
30620174 5139 else if (po->op == OP_POP)
26677139 5140 return -1;
30620174 5141 else if (po->flags & OPF_CJMP)
26677139 5142 return -1;
30620174 5143 else if (po->op == OP_PUSH) {
5144 if (po->flags & (OPF_FARG|OPF_FARGNR))
26677139 5145 return -1;
622eb2ef 5146 if (!g_header_mode) {
30620174 5147 ret = scan_for_mod(po, j + 1, i, 1);
622eb2ef 5148 if (ret >= 0)
5149 return -1;
5150 }
26677139 5151
5152 if (pp->arg[arg].type.is_va_list)
5153 return -1;
5154
5155 // next arg
5156 for (arg++; arg < pp->argc; arg++)
5157 if (pp->arg[arg].reg == NULL)
5158 break;
5159 }
30620174 5160 else if (po->op == OP_SUB && po->operand[0].reg == xSP
5161 && po->operand[1].type == OPT_CONST)
5162 {
5163 if (po->flags & (OPF_RMD|OPF_DONE))
5164 return -1;
5165 if (po->operand[1].val != pp->argc_stack * 4)
5166 ferr(po, "unexpected esp adjust: %d\n",
5167 po->operand[1].val * 4);
5168 ferr_assert(po, pp->argc - arg == pp->argc_stack);
5169 return collect_call_args_no_push(i, pp, regmask_ffca);
5170 }
26677139 5171 }
5172
5173 if (arg < pp->argc)
5174 return -1;
5175
5176 // now do it
5177 for (arg = 0; arg < pp->argc; arg++)
5178 if (pp->arg[arg].reg == NULL)
5179 break;
5180
5181 for (j = i; j > 0 && arg < pp->argc; )
5182 {
5183 j--;
5184
5185 if (ops[j].op == OP_PUSH)
5186 {
5187 ops[j].p_argnext = -1;
5188 ferr_assert(&ops[j], pp->arg[arg].datap == NULL);
26677139 5189
b62264bc 5190 k = check_for_stp(j + 1, i);
5191 if (k != -1) {
5192 // push ecx; fstp dword ptr [esp]
5193 ret = parse_stack_esp_offset(&ops[k],
5194 ops[k].operand[0].name, &offset);
5195 if (ret == 0 && offset == 0) {
5196 if (!pp->arg[arg].type.is_float)
5197 ferr(&ops[i], "arg %d should be float\n", arg + 1);
5198 mark_float_arg(&ops[k], pp, arg, regmask_ffca);
5199 }
5200 }
5201
5202 if (pp->arg[arg].datap == NULL) {
5203 pp->arg[arg].datap = &ops[j];
5204 if (regmask != NULL && ops[j].operand[0].type == OPT_REG)
5205 *regmask |= 1 << ops[j].operand[0].reg;
5206 }
26677139 5207
5e49b270 5208 ops[j].flags |= OPF_RMD | OPF_DONE | OPF_FARGNR | OPF_FARG;
26677139 5209 ops[j].flags &= ~OPF_RSAVE;
5210
5211 // next arg
5212 for (arg++; arg < pp->argc; arg++)
5213 if (pp->arg[arg].reg == NULL)
5214 break;
5215 }
5216 }
5217
5218 return 0;
5219}
5220
8c83cc48 5221static int sync_argnum(struct parsed_op *po, int argnum)
5222{
5223 struct parsed_op *po_tmp;
5224
5225 // see if other branches don't have higher argnum
5226 for (po_tmp = po; po_tmp != NULL; ) {
5227 if (argnum < po_tmp->p_argnum)
5228 argnum = po_tmp->p_argnum;
5229 // note: p_argnext is active on current collect_call_args only
5230 po_tmp = po_tmp->p_argnext >= 0 ? &ops[po_tmp->p_argnext] : NULL;
5231 }
5232
5233 // make all argnums consistent
5234 for (po_tmp = po; po_tmp != NULL; ) {
5235 if (po_tmp->p_argnum != 0)
5236 po_tmp->p_argnum = argnum;
5237 po_tmp = po_tmp->p_argnext >= 0 ? &ops[po_tmp->p_argnext] : NULL;
5238 }
5239
5240 return argnum;
5241}
5242
89ff3147 5243static int collect_call_args_r(struct parsed_op *po, int i,
8c83cc48 5244 struct parsed_proto *pp, int *regmask, int *arg_grp,
5245 int arg, int argnum, int magic, int need_op_saving, int may_reuse)
e56ab892 5246{
5247 struct parsed_proto *pp_tmp;
3a5101d7 5248 struct parsed_op *po_tmp;
e56ab892 5249 struct label_ref *lr;
2b43685d 5250 int need_to_save_current;
3a5101d7 5251 int arg_grp_current = 0;
5252 int save_args_seen = 0;
e56ab892 5253 int ret = 0;
5f70a34f 5254 int reg;
23fd0b11 5255 char buf[32];
5256 int j, k;
e56ab892 5257
a3684be1 5258 if (i < 0) {
a2c1d768 5259 ferr(po, "dead label encountered\n");
a3684be1 5260 return -1;
5261 }
e56ab892 5262
8c83cc48 5263 for (; arg < pp->argc; arg++, argnum++)
e56ab892 5264 if (pp->arg[arg].reg == NULL)
5265 break;
a3684be1 5266 magic = (magic & 0xffffff) | (arg << 24);
e56ab892 5267
89ff3147 5268 for (j = i; j >= 0 && (arg < pp->argc || pp->is_unresolved); )
e56ab892 5269 {
a3684be1 5270 if (((ops[j].cc_scratch ^ magic) & 0xffffff) == 0) {
5271 if (ops[j].cc_scratch != magic) {
5272 ferr(&ops[j], "arg collect hit same path with diff args for %s\n",
5273 pp->name);
5274 return -1;
5275 }
5276 // ok: have already been here
5277 return 0;
5278 }
5279 ops[j].cc_scratch = magic;
5280
d7857c3a 5281 if (g_labels[j] != NULL && g_label_refs[j].i != -1) {
e56ab892 5282 lr = &g_label_refs[j];
5283 if (lr->next != NULL)
5284 need_op_saving = 1;
a652aa9f 5285 for (; lr->next; lr = lr->next) {
92d715b6 5286 check_i(&ops[j], lr->i);
5c024ef7 5287 if ((ops[lr->i].flags & (OPF_JMP|OPF_CJMP)) != OPF_JMP)
a652aa9f 5288 may_reuse = 1;
8c83cc48 5289 ret = collect_call_args_r(po, lr->i, pp, regmask, arg_grp,
5290 arg, argnum, magic, need_op_saving, may_reuse);
a3684be1 5291 if (ret < 0)
5292 return ret;
a652aa9f 5293 }
e56ab892 5294
92d715b6 5295 check_i(&ops[j], lr->i);
5c024ef7 5296 if ((ops[lr->i].flags & (OPF_JMP|OPF_CJMP)) != OPF_JMP)
a652aa9f 5297 may_reuse = 1;
de50b98b 5298 if (j > 0 && LAST_OP(j - 1)) {
e56ab892 5299 // follow last branch in reverse
5300 j = lr->i;
5301 continue;
5302 }
5303 need_op_saving = 1;
8c83cc48 5304 ret = collect_call_args_r(po, lr->i, pp, regmask, arg_grp,
5305 arg, argnum, magic, need_op_saving, may_reuse);
a3684be1 5306 if (ret < 0)
5307 return ret;
e56ab892 5308 }
5309 j--;
5310
5311 if (ops[j].op == OP_CALL)
5312 {
89ff3147 5313 if (pp->is_unresolved)
5314 break;
5315
092f64e1 5316 pp_tmp = ops[j].pp;
e56ab892 5317 if (pp_tmp == NULL)
56b49358 5318 ferr(po, "arg collect %d/%d hit unparsed call '%s'\n",
5319 arg, pp->argc, ops[j].operand[0].name);
a652aa9f 5320 if (may_reuse && pp_tmp->argc_stack > 0)
de50b98b 5321 ferr(po, "arg collect %d/%d hit '%s' with %d stack args\n",
5322 arg, pp->argc, opr_name(&ops[j], 0), pp_tmp->argc_stack);
e56ab892 5323 }
fdd5548a 5324 // esp adjust of 0 means we collected it before
5325 else if (ops[j].op == OP_ADD && ops[j].operand[0].reg == xSP
5326 && (ops[j].operand[1].type != OPT_CONST
5327 || ops[j].operand[1].val != 0))
5328 {
89ff3147 5329 if (pp->is_unresolved)
5330 break;
5331
2b70f6d3 5332 fnote(po, "(this call)\n");
5333 ferr(&ops[j], "arg collect %d/%d hit esp adjust of %d\n",
fdd5548a 5334 arg, pp->argc, ops[j].operand[1].val);
de50b98b 5335 }
5e49b270 5336 else if (ops[j].op == OP_POP && !(ops[j].flags & OPF_DONE))
9af2d373 5337 {
89ff3147 5338 if (pp->is_unresolved)
5339 break;
5340
2b70f6d3 5341 fnote(po, "(this call)\n");
5342 ferr(&ops[j], "arg collect %d/%d hit pop\n", arg, pp->argc);
de50b98b 5343 }
5c024ef7 5344 else if (ops[j].flags & OPF_CJMP)
de50b98b 5345 {
89ff3147 5346 if (pp->is_unresolved)
5347 break;
5348
a652aa9f 5349 may_reuse = 1;
de50b98b 5350 }
91ca764a 5351 else if (ops[j].op == OP_PUSH
5352 && !(ops[j].flags & (OPF_FARGNR|OPF_DONE)))
e56ab892 5353 {
89ff3147 5354 if (pp->is_unresolved && (ops[j].flags & OPF_RMD))
5355 break;
5356
3a5101d7 5357 ops[j].p_argnext = -1;
5358 po_tmp = pp->arg[arg].datap;
5359 if (po_tmp != NULL)
5360 ops[j].p_argnext = po_tmp - ops;
e56ab892 5361 pp->arg[arg].datap = &ops[j];
3a5101d7 5362
8c83cc48 5363 argnum = sync_argnum(&ops[j], argnum);
5364
2b43685d 5365 need_to_save_current = 0;
5f70a34f 5366 reg = -1;
5367 if (ops[j].operand[0].type == OPT_REG)
5368 reg = ops[j].operand[0].reg;
5369
e56ab892 5370 if (!need_op_saving) {
89ff3147 5371 ret = scan_for_mod(&ops[j], j + 1, i, 1);
2b43685d 5372 need_to_save_current = (ret >= 0);
e56ab892 5373 }
2b43685d 5374 if (need_op_saving || need_to_save_current) {
8c83cc48 5375 // mark this arg as one that needs operand saving
5376 pp->arg[arg].is_saved = 1;
3a5101d7 5377
8c83cc48 5378 if (save_args_seen & (1 << (argnum - 1))) {
3a5101d7 5379 save_args_seen = 0;
5380 arg_grp_current++;
5381 if (arg_grp_current >= MAX_ARG_GRP)
5382 ferr(&ops[j], "out of arg groups (arg%d), f %s\n",
8c83cc48 5383 argnum, pp->name);
3a5101d7 5384 }
e56ab892 5385 }
5f70a34f 5386 else if (ops[j].p_argnum == 0)
e56ab892 5387 ops[j].flags |= OPF_RMD;
5388
a652aa9f 5389 // some PUSHes are reused by different calls on other branches,
de50b98b 5390 // but that can't happen if we didn't branch, so they
5391 // can be removed from future searches (handles nested calls)
a652aa9f 5392 if (!may_reuse)
9af2d373 5393 ops[j].flags |= OPF_FARGNR;
de50b98b 5394
9af2d373 5395 ops[j].flags |= OPF_FARG;
da87ae38 5396 ops[j].flags &= ~OPF_RSAVE;
5397
23fd0b11 5398 // check for __VALIST
04abc5d6 5399 if (!pp->is_unresolved && g_func_pp != NULL
5400 && pp->arg[arg].type.is_va_list)
5401 {
23fd0b11 5402 k = -1;
92d715b6 5403 ret = resolve_origin(j, &ops[j].operand[0],
5404 magic + 1, &k, NULL);
5f70a34f 5405 if (ret == 1 && k >= 0)
23fd0b11 5406 {
5f70a34f 5407 if (ops[k].op == OP_LEA) {
acd03176 5408 if (!g_func_pp->is_vararg)
5409 ferr(&ops[k], "lea <arg> used, but %s is not vararg?\n",
5410 g_func_pp->name);
5411
5f70a34f 5412 snprintf(buf, sizeof(buf), "arg_%X",
5413 g_func_pp->argc_stack * 4);
acd03176 5414 if (strstr(ops[k].operand[1].name, buf)
5415 || strstr(ops[k].operand[1].name, "arglist"))
5f70a34f 5416 {
b2bd20c0 5417 ops[k].flags |= OPF_RMD | OPF_NOREGS | OPF_DONE;
5418 ops[j].flags |= OPF_RMD | OPF_NOREGS | OPF_VAPUSH;
8c83cc48 5419 pp->arg[arg].is_saved = 0;
5f70a34f 5420 reg = -1;
5421 }
5422 else
acd03176 5423 ferr(&ops[k], "va_list arg detection failed\n");
5f70a34f 5424 }
5425 // check for va_list from g_func_pp arg too
5426 else if (ops[k].op == OP_MOV
5427 && is_stack_access(&ops[k], &ops[k].operand[1]))
5428 {
5429 ret = stack_frame_access(&ops[k], &ops[k].operand[1],
5430 buf, sizeof(buf), ops[k].operand[1].name, "", 1, 0);
5431 if (ret >= 0) {
5e49b270 5432 ops[k].flags |= OPF_RMD | OPF_DONE;
5f70a34f 5433 ops[j].flags |= OPF_RMD;
5434 ops[j].p_argpass = ret + 1;
8c83cc48 5435 pp->arg[arg].is_saved = 0;
5f70a34f 5436 reg = -1;
5437 }
5438 }
23fd0b11 5439 }
5440 }
5441
8c83cc48 5442 if (pp->arg[arg].is_saved) {
5443 ops[j].flags &= ~OPF_RMD;
5444 ops[j].p_argnum = argnum;
5445 }
23fd0b11 5446
5447 // tracking reg usage
5f70a34f 5448 if (reg >= 0)
5449 *regmask |= 1 << reg;
23fd0b11 5450
89ff3147 5451 arg++;
8c83cc48 5452 argnum++;
89ff3147 5453 if (!pp->is_unresolved) {
5454 // next arg
8c83cc48 5455 for (; arg < pp->argc; arg++, argnum++)
89ff3147 5456 if (pp->arg[arg].reg == NULL)
5457 break;
5458 }
a3684be1 5459 magic = (magic & 0xffffff) | (arg << 24);
e56ab892 5460 }
3a5101d7 5461
5462 if (ops[j].p_arggrp > arg_grp_current) {
5463 save_args_seen = 0;
5464 arg_grp_current = ops[j].p_arggrp;
5465 }
5466 if (ops[j].p_argnum > 0)
5467 save_args_seen |= 1 << (ops[j].p_argnum - 1);
e56ab892 5468 }
5469
5470 if (arg < pp->argc) {
5471 ferr(po, "arg collect failed for '%s': %d/%d\n",
5472 pp->name, arg, pp->argc);
89ff3147 5473 return -1;
e56ab892 5474 }
89ff3147 5475
3a5101d7 5476 if (arg_grp_current > *arg_grp)
5477 *arg_grp = arg_grp_current;
5478
89ff3147 5479 return arg;
5480}
5481
5482static int collect_call_args(struct parsed_op *po, int i,
8c83cc48 5483 struct parsed_proto *pp, int *regmask, int magic)
89ff3147 5484{
3a5101d7 5485 // arg group is for cases when pushes for
5486 // multiple funcs are going on
5487 struct parsed_op *po_tmp;
3a5101d7 5488 int arg_grp = 0;
89ff3147 5489 int ret;
5490 int a;
5491
8c83cc48 5492 ret = collect_call_args_r(po, i, pp, regmask, &arg_grp,
5493 0, 1, magic, 0, 0);
89ff3147 5494 if (ret < 0)
5495 return ret;
5496
3a5101d7 5497 if (arg_grp != 0) {
5498 // propagate arg_grp
5499 for (a = 0; a < pp->argc; a++) {
5500 if (pp->arg[a].reg != NULL)
5501 continue;
5502
5503 po_tmp = pp->arg[a].datap;
5504 while (po_tmp != NULL) {
5505 po_tmp->p_arggrp = arg_grp;
8c83cc48 5506 po_tmp = po_tmp->p_argnext >= 0 ? &ops[po_tmp->p_argnext] : NULL;
3a5101d7 5507 }
5508 }
5509 }
3a5101d7 5510
89ff3147 5511 if (pp->is_unresolved) {
5512 pp->argc += ret;
5513 pp->argc_stack += ret;
5514 for (a = 0; a < pp->argc; a++)
5515 if (pp->arg[a].type.name == NULL)
5516 pp->arg[a].type.name = strdup("int");
5517 }
5518
e56ab892 5519 return ret;
5520}
5521
b2bd20c0 5522static void reg_use_pass(int i, int opcnt, unsigned char *cbits,
5523 int regmask_now, int *regmask,
5524 int regmask_save_now, int *regmask_save,
5525 int *regmask_init, int regmask_arg)
5526{
5527 struct parsed_op *po;
5528 int already_saved;
5529 int regmask_new;
5530 int regmask_op;
5531 int flags_set;
5532 int ret, reg;
5533 int j;
5534
5535 for (; i < opcnt; i++)
5536 {
5537 po = &ops[i];
5538 if (cbits[i >> 3] & (1 << (i & 7)))
5539 return;
5540 cbits[i >> 3] |= (1 << (i & 7));
5541
5542 if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
5543 if (po->flags & (OPF_RMD|OPF_DONE))
5544 continue;
5545 if (po->btj != NULL) {
5546 for (j = 0; j < po->btj->count; j++) {
5547 check_i(po, po->btj->d[j].bt_i);
5548 reg_use_pass(po->btj->d[j].bt_i, opcnt, cbits,
5549 regmask_now, regmask, regmask_save_now, regmask_save,
5550 regmask_init, regmask_arg);
5551 }
5552 return;
5553 }
5554
5555 check_i(po, po->bt_i);
5556 if (po->flags & OPF_CJMP)
5557 reg_use_pass(po->bt_i, opcnt, cbits,
5558 regmask_now, regmask, regmask_save_now, regmask_save,
5559 regmask_init, regmask_arg);
5560 else
5561 i = po->bt_i - 1;
5562 continue;
5563 }
5564
5565 if (po->op == OP_PUSH && !(po->flags & (OPF_FARG|OPF_DONE))
5566 && !g_func_pp->is_userstack
5567 && po->operand[0].type == OPT_REG)
5568 {
5569 reg = po->operand[0].reg;
5570 ferr_assert(po, reg >= 0);
5571
5572 already_saved = 0;
5573 flags_set = OPF_RSAVE | OPF_RMD | OPF_DONE;
5574 if (regmask_now & (1 << reg)) {
5575 already_saved = regmask_save_now & (1 << reg);
5576 flags_set = OPF_RSAVE | OPF_DONE;
5577 }
5578
93b5bd18 5579 ret = scan_for_pop(i + 1, opcnt, i + opcnt * 3, reg, 0, 0, 0);
b2bd20c0 5580 if (ret == 1) {
93b5bd18 5581 scan_for_pop(i + 1, opcnt, i + opcnt * 4,
5582 reg, 0, 0, flags_set);
b2bd20c0 5583 }
5584 else {
5585 ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, 0);
5586 if (ret == 1) {
5587 scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg,
5588 flags_set);
5589 }
5590 }
5591 if (ret == 1) {
5592 ferr_assert(po, !already_saved);
5593 po->flags |= flags_set;
5594
5595 if (regmask_now & (1 << reg)) {
5596 regmask_save_now |= (1 << reg);
5597 *regmask_save |= regmask_save_now;
5598 }
5599 continue;
5600 }
5601 }
5602 else if (po->op == OP_POP && (po->flags & OPF_RSAVE)) {
5603 reg = po->operand[0].reg;
5604 ferr_assert(po, reg >= 0);
5605
5606 if (regmask_save_now & (1 << reg))
5607 regmask_save_now &= ~(1 << reg);
5608 else
5609 regmask_now &= ~(1 << reg);
5610 continue;
5611 }
5612 else if (po->op == OP_CALL) {
5613 if ((po->regmask_dst & (1 << xAX))
5614 && !(po->regmask_dst & (1 << xDX)))
5615 {
5616 if (po->flags & OPF_TAIL)
5617 // don't need eax, will do "return f();" or "f(); return;"
5618 po->regmask_dst &= ~(1 << xAX);
5619 else {
5620 struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xAX);
5621 j = -1;
5622 find_next_read(i + 1, opcnt, &opr, i + opcnt * 17, &j);
5623 if (j == -1)
5624 // not used
5625 po->regmask_dst &= ~(1 << xAX);
5626 }
5627 }
fe18df39 5628
5629 // not "full stack" mode and have something in stack
5630 if (!(regmask_now & mxST7_2) && (regmask_now & mxST1_0))
5631 ferr(po, "float stack is not empty on func call\n");
b2bd20c0 5632 }
5633
5634 if (po->flags & OPF_NOREGS)
5635 continue;
5636
acd03176 5637 // if incomplete register is used, clear it on init to avoid
5638 // later use of uninitialized upper part in some situations
5639 if ((po->flags & OPF_DATA) && po->operand[0].type == OPT_REG
5640 && po->operand[0].lmod != OPLM_DWORD)
5641 {
5642 reg = po->operand[0].reg;
5643 ferr_assert(po, reg >= 0);
5644
5645 if (!(regmask_now & (1 << reg)))
5646 *regmask_init |= 1 << reg;
5647 }
5648
b2bd20c0 5649 regmask_op = po->regmask_src | po->regmask_dst;
5650
5651 regmask_new = po->regmask_src & ~regmask_now & ~regmask_arg;
5652 regmask_new &= ~(1 << xSP);
5653 if (g_bp_frame && !(po->flags & OPF_EBP_S))
5654 regmask_new &= ~(1 << xBP);
5655
b2bd20c0 5656 if (regmask_new != 0)
5657 fnote(po, "uninitialized reg mask: %x\n", regmask_new);
5658
5659 if (regmask_op & (1 << xBP)) {
5660 if (g_bp_frame && !(po->flags & OPF_EBP_S)) {
5661 if (po->regmask_dst & (1 << xBP))
5662 // compiler decided to drop bp frame and use ebp as scratch
5663 scan_fwd_set_flags(i + 1, opcnt, i + opcnt * 5, OPF_EBP_S);
5664 else
5665 regmask_op &= ~(1 << xBP);
5666 }
5667 }
5668
622eb2ef 5669 if (po->flags & OPF_FPUSH) {
5670 if (regmask_now & mxST1)
5671 regmask_now |= mxSTa; // switch to "full stack" mode
5672 if (regmask_now & mxSTa)
5673 po->flags |= OPF_FSHIFT;
5674 if (!(regmask_now & mxST7_2)) {
5675 regmask_now =
5676 (regmask_now & ~mxST1_0) | ((regmask_now & mxST0) << 1);
5677 }
5678 }
5679
b2bd20c0 5680 regmask_now |= regmask_op;
5681 *regmask |= regmask_now;
5682
d4a985bd 5683 // released regs
88eed95d 5684 if (po->flags & OPF_FPOPP) {
5685 if ((regmask_now & mxSTa) == 0)
5686 ferr(po, "float pop on empty stack?\n");
5687 if (regmask_now & mxST7_2)
5688 po->flags |= OPF_FSHIFT;
5689 if (!(regmask_now & mxST7_2))
5690 regmask_now &= ~mxST1_0;
5691 }
5692 else if (po->flags & OPF_FPOP) {
fe18df39 5693 if ((regmask_now & mxSTa) == 0)
d4a985bd 5694 ferr(po, "float pop on empty stack?\n");
fe18df39 5695 if (regmask_now & (mxST7_2 | mxST1))
d4a985bd 5696 po->flags |= OPF_FSHIFT;
fe18df39 5697 if (!(regmask_now & mxST7_2)) {
5698 regmask_now =
5699 (regmask_now & ~mxST1_0) | ((regmask_now & mxST1) >> 1);
5700 }
d4a985bd 5701 }
5702
5703 if (po->flags & OPF_TAIL) {
16057ce1 5704 if (!(regmask_now & mxST7_2)) {
5705 if (get_pp_arg_regmask_dst(g_func_pp) & mxST0) {
5706 if (!(regmask_now & mxST0))
5707 ferr(po, "no st0 on float return, mask: %x\n",
5708 regmask_now);
5709 }
5710 else if (regmask_now & mxST1_0)
5711 ferr(po, "float regs on tail: %x\n", regmask_now);
5712 }
4d247254 5713
5714 // there is support for "conditional tailcall", sort of
5715 if (!(po->flags & OPF_CC))
5716 return;
d4a985bd 5717 }
b2bd20c0 5718 }
5719}
5720
89ff3147 5721static void pp_insert_reg_arg(struct parsed_proto *pp, const char *reg)
5722{
5723 int i;
5724
5725 for (i = 0; i < pp->argc; i++)
5726 if (pp->arg[i].reg == NULL)
5727 break;
5728
5729 if (pp->argc_stack)
5730 memmove(&pp->arg[i + 1], &pp->arg[i],
5731 sizeof(pp->arg[0]) * pp->argc_stack);
5732 memset(&pp->arg[i], 0, sizeof(pp->arg[i]));
5733 pp->arg[i].reg = strdup(reg);
5734 pp->arg[i].type.name = strdup("int");
5735 pp->argc++;
5736 pp->argc_reg++;
5737}
5738
f9327ad4 5739static void output_std_flag_z(FILE *fout, struct parsed_op *po,
04f8a628 5740 int *pfomask, const char *dst_opr_text)
5741{
5742 if (*pfomask & (1 << PFO_Z)) {
5743 fprintf(fout, "\n cond_z = (%s%s == 0);",
5744 lmod_cast_u(po, po->operand[0].lmod), dst_opr_text);
5745 *pfomask &= ~(1 << PFO_Z);
5746 }
f9327ad4 5747}
5748
5749static void output_std_flag_s(FILE *fout, struct parsed_op *po,
5750 int *pfomask, const char *dst_opr_text)
5751{
04f8a628 5752 if (*pfomask & (1 << PFO_S)) {
5753 fprintf(fout, "\n cond_s = (%s%s < 0);",
5754 lmod_cast_s(po, po->operand[0].lmod), dst_opr_text);
5755 *pfomask &= ~(1 << PFO_S);
5756 }
5757}
5758
f9327ad4 5759static void output_std_flags(FILE *fout, struct parsed_op *po,
5760 int *pfomask, const char *dst_opr_text)
5761{
5762 output_std_flag_z(fout, po, pfomask, dst_opr_text);
5763 output_std_flag_s(fout, po, pfomask, dst_opr_text);
5764}
5765
c0de9015 5766enum {
5767 OPP_FORCE_NORETURN = (1 << 0),
5768 OPP_SIMPLE_ARGS = (1 << 1),
5769 OPP_ALIGN = (1 << 2),
5770};
5771
c0050df6 5772static void output_pp_attrs(FILE *fout, const struct parsed_proto *pp,
c0de9015 5773 int flags)
c0050df6 5774{
c0de9015 5775 const char *cconv = "";
5776
c0050df6 5777 if (pp->is_fastcall)
c0de9015 5778 cconv = "__fastcall ";
c0050df6 5779 else if (pp->is_stdcall && pp->argc_reg == 0)
c0de9015 5780 cconv = "__stdcall ";
5781
5782 fprintf(fout, (flags & OPP_ALIGN) ? "%-16s" : "%s", cconv);
5783
5784 if (pp->is_noreturn || (flags & OPP_FORCE_NORETURN))
c0050df6 5785 fprintf(fout, "noreturn ");
5786}
5787
c0de9015 5788static void output_pp(FILE *fout, const struct parsed_proto *pp,
5789 int flags)
5790{
5791 int i;
5792
5793 fprintf(fout, (flags & OPP_ALIGN) ? "%-5s" : "%s ",
5794 pp->ret_type.name);
5795 if (pp->is_fptr)
5796 fprintf(fout, "(");
5797 output_pp_attrs(fout, pp, flags);
5798 if (pp->is_fptr)
5799 fprintf(fout, "*");
5800 fprintf(fout, "%s", pp->name);
5801 if (pp->is_fptr)
5802 fprintf(fout, ")");
5803
5804 fprintf(fout, "(");
5805 for (i = 0; i < pp->argc; i++) {
5806 if (i > 0)
5807 fprintf(fout, ", ");
93b5bd18 5808 if (pp->arg[i].pp != NULL && pp->arg[i].pp->is_func
5809 && !(flags & OPP_SIMPLE_ARGS))
5810 {
c0de9015 5811 // func pointer
93b5bd18 5812 output_pp(fout, pp->arg[i].pp, 0);
c0de9015 5813 }
5814 else if (pp->arg[i].type.is_retreg) {
5815 fprintf(fout, "u32 *r_%s", pp->arg[i].reg);
5816 }
5817 else {
5818 fprintf(fout, "%s", pp->arg[i].type.name);
5819 if (!pp->is_fptr)
5820 fprintf(fout, " a%d", i + 1);
5821 }
2c31fb4c 5822
5823 if (pp->arg[i].type.is_64bit)
5824 i++;
c0de9015 5825 }
5826 if (pp->is_vararg) {
5827 if (i > 0)
5828 fprintf(fout, ", ");
5829 fprintf(fout, "...");
5830 }
5831 fprintf(fout, ")");
5832}
5833
3a5101d7 5834static char *saved_arg_name(char *buf, size_t buf_size, int grp, int num)
5835{
5836 char buf1[16];
5837
5838 buf1[0] = 0;
5839 if (grp > 0)
5840 snprintf(buf1, sizeof(buf1), "%d", grp);
5841 snprintf(buf, buf_size, "s%s_a%d", buf1, num);
5842
5843 return buf;
5844}
5845
9af2d373 5846static void gen_x_cleanup(int opcnt);
5847
91977a1c 5848static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
5849{
69a3cdfc 5850 struct parsed_op *po, *delayed_flag_op = NULL, *tmp_op;
850c9265 5851 struct parsed_opr *last_arith_dst = NULL;
3ebea2cf 5852 char buf1[256], buf2[256], buf3[256], cast[64];
ddaf8bd7 5853 struct parsed_proto *pp, *pp_tmp;
4c45fa73 5854 struct parsed_data *pd;
3a5101d7 5855 int save_arg_vars[MAX_ARG_GRP] = { 0, };
b2bd20c0 5856 unsigned char cbits[MAX_OPS / 8];
497a6d6b 5857 const char *float_type;
fe18df39 5858 const char *float_st0;
5859 const char *float_st1;
5860 int need_float_stack = 0;
16057ce1 5861 int need_float_sw = 0; // status word
108e9fe3 5862 int need_tmp_var = 0;
2fe80fdb 5863 int need_tmp64 = 0;
fe18df39 5864 int cond_vars = 0;
91977a1c 5865 int had_decl = 0;
3ebea2cf 5866 int label_pending = 0;
497a6d6b 5867 int need_double = 0;
f9327ad4 5868 int stack_align = 0;
2c31fb4c 5869 int stack_fsz_adj = 0;
16057ce1 5870 int regmask_save = 0; // used regs saved/restored in this func
b2bd20c0 5871 int regmask_arg; // regs from this function args (fastcall, etc)
5872 int regmask_ret; // regs needed on ret
25a330eb 5873 int regmask_now; // temp
5874 int regmask_init = 0; // regs that need zero initialization
5875 int regmask_pp = 0; // regs used in complex push-pop graph
30620174 5876 int regmask_ffca = 0; // float function call args
25a330eb 5877 int regmask = 0; // used regs
940e8e66 5878 int pfomask = 0;
64c59faf 5879 int found = 0;
fe18df39 5880 int dead_dst;
91977a1c 5881 int no_output;
4c45fa73 5882 int i, j, l;
91977a1c 5883 int arg;
91977a1c 5884 int reg;
5885 int ret;
5886
1bafb621 5887 g_bp_frame = g_sp_frame = g_stack_fsz = 0;
a2c1d768 5888 g_stack_frame_used = 0;
ba93cc12 5889 g_seh_size = 0;
226e8df1 5890 if (g_sct_func_attr & SCTFA_CLEAR_REGS)
5891 regmask_init = g_regmask_init;
91977a1c 5892
36595fd2 5893 g_func_pp = proto_parse(fhdr, funcn, 0);
bd96f656 5894 if (g_func_pp == NULL)
91977a1c 5895 ferr(ops, "proto_parse failed for '%s'\n", funcn);
5896
b2bd20c0 5897 regmask_arg = get_pp_arg_regmask_src(g_func_pp);
5898 regmask_ret = get_pp_arg_regmask_dst(g_func_pp);
5899
91977a1c 5900 // pass1:
87bf6cec 5901 // - resolve all branches
66bdb2b0 5902 // - parse calls with labels
5903 resolve_branches_parse_calls(opcnt);
840257f6 5904
66bdb2b0 5905 // pass2:
5906 // - handle ebp/esp frame, remove ops related to it
f9327ad4 5907 scan_prologue_epilogue(opcnt, &stack_align);
87bf6cec 5908
2c31fb4c 5909 // handle a case where sf size is unalignment, but is
5910 // placed in a way that elements are still aligned
5911 if (g_stack_fsz & 4) {
5912 for (i = 0; i < g_eqcnt; i++) {
5913 if (g_eqs[i].lmod != OPLM_QWORD)
5914 continue;
5915 if (!(g_eqs[i].offset & 4)) {
5916 g_stack_fsz += 4;
5917 stack_fsz_adj = 4;
5918 }
5919 break;
5920 }
5921 }
5922
87bf6cec 5923 // pass3:
a2c1d768 5924 // - remove dead labels
b2bd20c0 5925 // - set regs needed at ret
1bafb621 5926 for (i = 0; i < opcnt; i++)
5927 {
d7857c3a 5928 if (g_labels[i] != NULL && g_label_refs[i].i == -1) {
5929 free(g_labels[i]);
5930 g_labels[i] = NULL;
5931 }
b2bd20c0 5932
5933 if (ops[i].op == OP_RET)
5934 ops[i].regmask_src |= regmask_ret;
66bdb2b0 5935 }
a2c1d768 5936
66bdb2b0 5937 // pass4:
5938 // - process trivial calls
5939 for (i = 0; i < opcnt; i++)
5940 {
69a3cdfc 5941 po = &ops[i];
5e49b270 5942 if (po->flags & (OPF_RMD|OPF_DONE))
91977a1c 5943 continue;
850c9265 5944
d4e3b5db 5945 if (po->op == OP_CALL)
26677139 5946 {
5947 pp = process_call_early(i, opcnt, &j);
5948 if (pp != NULL) {
30620174 5949 if (!(po->flags & OPF_ATAIL)) {
26677139 5950 // since we know the args, try to collect them
30620174 5951 ret = collect_call_args_early(i, pp, &regmask, &regmask_ffca);
5952 if (ret != 0)
26677139 5953 pp = NULL;
30620174 5954 }
26677139 5955 }
5956
5957 if (pp != NULL) {
5958 if (j >= 0) {
5959 // commit esp adjust
5e49b270 5960 if (ops[j].op != OP_POP)
5961 patch_esp_adjust(&ops[j], pp->argc_stack * 4);
bfacdc83 5962 else {
5963 for (l = 0; l < pp->argc_stack; l++)
b2bd20c0 5964 ops[j + l].flags |= OPF_DONE | OPF_RMD | OPF_NOREGS;
bfacdc83 5965 }
26677139 5966 }
5967
5968 if (strstr(pp->ret_type.name, "int64"))
5969 need_tmp64 = 1;
5970
5971 po->flags |= OPF_DONE;
5972 }
5973 }
5974 }
5975
66bdb2b0 5976 // pass5:
b2bd20c0 5977 // - process calls, stage 2
5978 // - handle some push/pop pairs
5979 // - scan for STD/CLD, propagate DF
16057ce1 5980 // - try to resolve needed x87 status word bits
26677139 5981 for (i = 0; i < opcnt; i++)
5982 {
16057ce1 5983 int mask, z_check;
5984
26677139 5985 po = &ops[i];
b2bd20c0 5986 if (po->flags & OPF_RMD)
26677139 5987 continue;
5988
b2bd20c0 5989 if (po->op == OP_CALL)
69a3cdfc 5990 {
b2bd20c0 5991 if (!(po->flags & OPF_DONE)) {
5992 pp = process_call(i, opcnt);
91977a1c 5993
b2bd20c0 5994 if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) {
5995 // since we know the args, collect them
8c83cc48 5996 collect_call_args(po, i, pp, &regmask, i + opcnt * 2);
b2bd20c0 5997 }
5998 // for unresolved, collect after other passes
89ff3147 5999 }
2b43685d 6000
b2bd20c0 6001 pp = po->pp;
6002 ferr_assert(po, pp != NULL);
6003
6004 po->regmask_src |= get_pp_arg_regmask_src(pp);
6005 po->regmask_dst |= get_pp_arg_regmask_dst(pp);
6006
d4a985bd 6007 if (po->regmask_dst & mxST0)
6008 po->flags |= OPF_FPUSH;
6009
2b43685d 6010 if (strstr(pp->ret_type.name, "int64"))
2fe80fdb 6011 need_tmp64 = 1;
b2bd20c0 6012
6013 continue;
91977a1c 6014 }
b2bd20c0 6015
6016 if (po->flags & OPF_DONE)
6017 continue;
6018
16057ce1 6019 switch (po->op) {
6020 case OP_PUSH:
6021 if (!(po->flags & OPF_FARG) && !(po->flags & OPF_RSAVE)
6022 && po->operand[0].type == OPT_CONST)
6023 {
6024 scan_for_pop_const(i, opcnt, i + opcnt * 12);
6025 }
6026 break;
6027
6028 case OP_POP:
e83ea7ed 6029 scan_pushes_for_pop(i, opcnt, &regmask_pp);
16057ce1 6030 break;
6031
6032 case OP_STD:
b2bd20c0 6033 po->flags |= OPF_DF | OPF_RMD | OPF_DONE;
6034 scan_propagate_df(i + 1, opcnt);
16057ce1 6035 break;
6036
6037 case OP_FNSTSW:
6038 need_float_sw = 1;
6039 if (po->operand[0].type != OPT_REG || po->operand[0].reg != xAX)
6040 ferr(po, "TODO: fnstsw to mem\n");
6041 ret = resolve_used_bits(i + 1, opcnt, xAX, &mask, &z_check);
6042 if (ret != 0)
6043 ferr(po, "fnstsw resolve failed\n");
6044 ret = adjust_prev_op(i, OP_FCOM, i + opcnt * 21,
6045 (void *)(long)(mask | (z_check << 16)));
6046 if (ret != 1)
6047 ferr(po, "failed to find fcom: %d\n", ret);
6048 break;
6049
6050 default:
6051 break;
b2bd20c0 6052 }
d4e3b5db 6053 }
6054
66bdb2b0 6055 // pass6:
d4e3b5db 6056 // - find POPs for PUSHes, rm both
6057 // - scan for all used registers
b2bd20c0 6058 memset(cbits, 0, sizeof(cbits));
226e8df1 6059 reg_use_pass(0, opcnt, cbits, regmask_init, &regmask,
b2bd20c0 6060 0, &regmask_save, &regmask_init, regmask_arg);
6061
11437ea1 6062 need_float_stack = !!(regmask & mxST7_2);
6063
b2bd20c0 6064 // pass7:
d4e3b5db 6065 // - find flag set ops for their users
b2bd20c0 6066 // - do unresolved calls
1bafb621 6067 // - declare indirect functions
16057ce1 6068 // - other op specific processing
26677139 6069 for (i = 0; i < opcnt; i++)
6070 {
d4e3b5db 6071 po = &ops[i];
5e49b270 6072 if (po->flags & (OPF_RMD|OPF_DONE))
d4e3b5db 6073 continue;
6074
d4e3b5db 6075 if (po->flags & OPF_CC)
6076 {
2b43685d 6077 int setters[16], cnt = 0, branched = 0;
6078
7f20f633 6079 ret = scan_for_flag_set(i, opcnt, i + opcnt * 6,
04f8a628 6080 &branched, setters, &cnt);
2b43685d 6081 if (ret < 0 || cnt <= 0)
6082 ferr(po, "unable to trace flag setter(s)\n");
6083 if (cnt > ARRAY_SIZE(setters))
6084 ferr(po, "too many flag setters\n");
d4e3b5db 6085
2b43685d 6086 for (j = 0; j < cnt; j++)
6087 {
6088 tmp_op = &ops[setters[j]]; // flag setter
6089 pfomask = 0;
6090
6091 // to get nicer code, we try to delay test and cmp;
6092 // if we can't because of operand modification, or if we
591721d7 6093 // have arith op, or branch, make it calculate flags explicitly
6094 if (tmp_op->op == OP_TEST || tmp_op->op == OP_CMP)
6095 {
89ff3147 6096 if (branched || scan_for_mod(tmp_op, setters[j] + 1, i, 0) >= 0)
092f64e1 6097 pfomask = 1 << po->pfo;
2b43685d 6098 }
4741fdfe 6099 else if (tmp_op->op == OP_CMPS || tmp_op->op == OP_SCAS) {
092f64e1 6100 pfomask = 1 << po->pfo;
591721d7 6101 }
2b43685d 6102 else {
04f8a628 6103 // see if we'll be able to handle based on op result
6104 if ((tmp_op->op != OP_AND && tmp_op->op != OP_OR
092f64e1 6105 && po->pfo != PFO_Z && po->pfo != PFO_S
6106 && po->pfo != PFO_P)
04f8a628 6107 || branched
2b43685d 6108 || scan_for_mod_opr0(tmp_op, setters[j] + 1, i) >= 0)
092f64e1 6109 {
6110 pfomask = 1 << po->pfo;
6111 }
2fe80fdb 6112
c8dbc5be 6113 if (tmp_op->op == OP_ADD && po->pfo == PFO_C) {
6114 propagate_lmod(tmp_op, &tmp_op->operand[0],
6115 &tmp_op->operand[1]);
6116 if (tmp_op->operand[0].lmod == OPLM_DWORD)
6117 need_tmp64 = 1;
6118 }
2b43685d 6119 }
6120 if (pfomask) {
6121 tmp_op->pfomask |= pfomask;
cb090db0 6122 cond_vars |= pfomask;
2b43685d 6123 }
04f8a628 6124 // note: may overwrite, currently not a problem
6125 po->datap = tmp_op;
d4e3b5db 6126 }
6127
cb090db0 6128 if (po->op == OP_RCL || po->op == OP_RCR
6129 || po->op == OP_ADC || po->op == OP_SBB)
6130 cond_vars |= 1 << PFO_C;
d4e3b5db 6131 }
092f64e1 6132
622eb2ef 6133 switch (po->op) {
6134 case OP_CMPS:
6135 case OP_SCAS:
cb090db0 6136 cond_vars |= 1 << PFO_Z;
622eb2ef 6137 break;
6138
6139 case OP_MUL:
c8dbc5be 6140 if (po->operand[0].lmod == OPLM_DWORD)
6141 need_tmp64 = 1;
622eb2ef 6142 break;
6143
6144 case OP_IMUL:
6145 if (po->operand_cnt == 1 && po->operand[0].lmod == OPLM_DWORD)
6146 need_tmp64 = 1;
6147 break;
6148
6149 case OP_CALL:
26677139 6150 // note: resolved non-reg calls are OPF_DONE already
092f64e1 6151 pp = po->pp;
b2bd20c0 6152 ferr_assert(po, pp != NULL);
89ff3147 6153
6154 if (pp->is_unresolved) {
ddaf8bd7 6155 int regmask_stack = 0;
8c83cc48 6156 collect_call_args(po, i, pp, &regmask, i + opcnt * 2);
89ff3147 6157
b74c31e3 6158 // this is pretty rough guess:
6159 // see ecx and edx were pushed (and not their saved versions)
6160 for (arg = 0; arg < pp->argc; arg++) {
8c83cc48 6161 if (pp->arg[arg].reg != NULL && !pp->arg[arg].is_saved)
b74c31e3 6162 continue;
6163
6164 tmp_op = pp->arg[arg].datap;
6165 if (tmp_op == NULL)
6166 ferr(po, "parsed_op missing for arg%d\n", arg);
8c83cc48 6167 if (tmp_op->operand[0].type == OPT_REG)
b74c31e3 6168 regmask_stack |= 1 << tmp_op->operand[0].reg;
6169 }
6170
ddaf8bd7 6171 if (!((regmask_stack & (1 << xCX))
6172 && (regmask_stack & (1 << xDX))))
89ff3147 6173 {
6174 if (pp->argc_stack != 0
c0050df6 6175 || ((regmask | regmask_arg) & ((1 << xCX)|(1 << xDX))))
89ff3147 6176 {
6177 pp_insert_reg_arg(pp, "ecx");
c0050df6 6178 pp->is_fastcall = 1;
ddaf8bd7 6179 regmask_init |= 1 << xCX;
89ff3147 6180 regmask |= 1 << xCX;
6181 }
6182 if (pp->argc_stack != 0
6183 || ((regmask | regmask_arg) & (1 << xDX)))
6184 {
6185 pp_insert_reg_arg(pp, "edx");
ddaf8bd7 6186 regmask_init |= 1 << xDX;
89ff3147 6187 regmask |= 1 << xDX;
6188 }
6189 }
c0050df6 6190
6191 // note: __cdecl doesn't fall into is_unresolved category
6192 if (pp->argc_stack > 0)
6193 pp->is_stdcall = 1;
ddaf8bd7 6194 }
622eb2ef 6195 break;
6196
6197 case OP_MOV:
6198 if (po->operand[0].pp != NULL && po->operand[1].pp != NULL)
27ebfaed 6199 {
622eb2ef 6200 // <var> = offset <something>
6201 if ((po->operand[1].pp->is_func || po->operand[1].pp->is_fptr)
6202 && !IS_START(po->operand[1].name, "off_"))
6203 {
6204 if (!po->operand[0].pp->is_fptr)
6205 ferr(po, "%s not declared as fptr when it should be\n",
6206 po->operand[0].name);
6207 if (pp_cmp_func(po->operand[0].pp, po->operand[1].pp)) {
6208 pp_print(buf1, sizeof(buf1), po->operand[0].pp);
6209 pp_print(buf2, sizeof(buf2), po->operand[1].pp);
6210 fnote(po, "var: %s\n", buf1);
6211 fnote(po, "func: %s\n", buf2);
6212 ferr(po, "^ mismatch\n");
6213 }
27ebfaed 6214 }
6215 }
622eb2ef 6216 break;
6217
6218 case OP_DIV:
6219 case OP_IDIV:
acd03176 6220 if (po->operand[0].lmod == OPLM_DWORD) {
6221 // 32bit division is common, look for it
6222 if (po->op == OP_DIV)
6223 ret = scan_for_reg_clear(i, xDX);
6224 else
6225 ret = scan_for_cdq_edx(i);
6226 if (ret >= 0)
6227 po->flags |= OPF_32BIT;
6228 else
6229 need_tmp64 = 1;
6230 }
cb090db0 6231 else
acd03176 6232 need_tmp_var = 1;
622eb2ef 6233 break;
6234
6235 case OP_CLD:
5e49b270 6236 po->flags |= OPF_RMD | OPF_DONE;
622eb2ef 6237 break;
6238
6239 case OP_RCL:
6240 case OP_RCR:
6241 case OP_XCHG:
6242 need_tmp_var = 1;
6243 break;
6244
6245 case OP_FLD:
6246 if (po->operand[0].lmod == OPLM_QWORD)
6247 need_double = 1;
6248 break;
6249
6250 case OPP_ALLSHL:
6251 case OPP_ALLSHR:
6252 need_tmp64 = 1;
6253 break;
6254
6255 case OPP_FTOL: {
d4a985bd 6256 struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xDX);
6257 j = -1;
6258 find_next_read(i + 1, opcnt, &opr, i + opcnt * 18, &j);
6259 if (j == -1)
6260 po->flags |= OPF_32BIT;
622eb2ef 6261 break;
d4a985bd 6262 }
cb090db0 6263
622eb2ef 6264 default:
6265 break;
6266 }
8c83cc48 6267
6268 // this might need it's own pass...
6269 if (po->op != OP_FST && po->p_argnum > 0)
6270 save_arg_vars[po->p_arggrp] |= 1 << (po->p_argnum - 1);
11437ea1 6271
6272 // correct for "full stack" mode late enable
88eed95d 6273 if ((po->flags & (OPF_PPUSH|OPF_FPOP|OPF_FPOPP))
6274 && need_float_stack)
11437ea1 6275 po->flags |= OPF_FSHIFT;
91977a1c 6276 }
6277
497a6d6b 6278 float_type = need_double ? "double" : "float";
fe18df39 6279 float_st0 = need_float_stack ? "f_st[f_stp & 7]" : "f_st0";
6280 float_st1 = need_float_stack ? "f_st[(f_stp + 1) & 7]" : "f_st1";
497a6d6b 6281
60fe410c 6282 // output starts here
6283
ba93cc12 6284 if (g_seh_found)
6285 fprintf(fout, "// had SEH\n");
6286
60fe410c 6287 // define userstack size
6288 if (g_func_pp->is_userstack) {
6289 fprintf(fout, "#ifndef US_SZ_%s\n", g_func_pp->name);
6290 fprintf(fout, "#define US_SZ_%s USERSTACK_SIZE\n", g_func_pp->name);
6291 fprintf(fout, "#endif\n");
6292 }
6293
6294 // the function itself
c0de9015 6295 ferr_assert(ops, !g_func_pp->is_fptr);
6296 output_pp(fout, g_func_pp,
6297 (g_ida_func_attr & IDAFA_NORETURN) ? OPP_FORCE_NORETURN : 0);
6298 fprintf(fout, "\n{\n");
60fe410c 6299
6300 // declare indirect functions
6301 for (i = 0; i < opcnt; i++) {
6302 po = &ops[i];
6303 if (po->flags & OPF_RMD)
6304 continue;
6305
6306 if (po->op == OP_CALL) {
6307 pp = po->pp;
6308 if (pp == NULL)
6309 ferr(po, "NULL pp\n");
6310
6311 if (pp->is_fptr && !(pp->name[0] != 0 && pp->is_arg)) {
6312 if (pp->name[0] != 0) {
6313 memmove(pp->name + 2, pp->name, strlen(pp->name) + 1);
6314 memcpy(pp->name, "i_", 2);
6315
6316 // might be declared already
6317 found = 0;
6318 for (j = 0; j < i; j++) {
6319 if (ops[j].op == OP_CALL && (pp_tmp = ops[j].pp)) {
6320 if (pp_tmp->is_fptr && IS(pp->name, pp_tmp->name)) {
6321 found = 1;
6322 break;
6323 }
6324 }
6325 }
6326 if (found)
6327 continue;
6328 }
6329 else
6330 snprintf(pp->name, sizeof(pp->name), "icall%d", i);
6331
c0de9015 6332 fprintf(fout, " ");
6333 output_pp(fout, pp, OPP_SIMPLE_ARGS);
6334 fprintf(fout, ";\n");
60fe410c 6335 }
6336 }
6337 }
da87ae38 6338
4c45fa73 6339 // output LUTs/jumptables
6340 for (i = 0; i < g_func_pd_cnt; i++) {
6341 pd = &g_func_pd[i];
6342 fprintf(fout, " static const ");
6343 if (pd->type == OPT_OFFSET) {
6344 fprintf(fout, "void *jt_%s[] =\n { ", pd->label);
6345
6346 for (j = 0; j < pd->count; j++) {
6347 if (j > 0)
6348 fprintf(fout, ", ");
6349 fprintf(fout, "&&%s", pd->d[j].u.label);
6350 }
6351 }
6352 else {
6353 fprintf(fout, "%s %s[] =\n { ",
6354 lmod_type_u(ops, pd->lmod), pd->label);
6355
6356 for (j = 0; j < pd->count; j++) {
6357 if (j > 0)
6358 fprintf(fout, ", ");
6359 fprintf(fout, "%u", pd->d[j].u.val);
6360 }
6361 }
6362 fprintf(fout, " };\n");
1f84f6b3 6363 had_decl = 1;
4c45fa73 6364 }
6365
4f12f671 6366 // declare stack frame, va_arg
1f84f6b3 6367 if (g_stack_fsz) {
2c31fb4c 6368 if (stack_fsz_adj)
6369 fprintf(fout, " // stack_fsz_adj %d\n", stack_fsz_adj);
6370
d4a985bd 6371 fprintf(fout, " union { u32 d[%d];", (g_stack_fsz + 3) / 4);
6372 if (g_func_lmods & (1 << OPLM_WORD))
6373 fprintf(fout, " u16 w[%d];", (g_stack_fsz + 1) / 2);
6374 if (g_func_lmods & (1 << OPLM_BYTE))
6375 fprintf(fout, " u8 b[%d];", g_stack_fsz);
6376 if (g_func_lmods & (1 << OPLM_QWORD))
6377 fprintf(fout, " double q[%d];", (g_stack_fsz + 7) / 8);
2c31fb4c 6378
f9327ad4 6379 if (stack_align > 8)
6380 ferr(ops, "unhandled stack align of %d\n", stack_align);
6381 else if (stack_align == 8)
6382 fprintf(fout, " u64 align;");
d4a985bd 6383 fprintf(fout, " } sf;\n");
1f84f6b3 6384 had_decl = 1;
6385 }
6386
6387 if (g_func_pp->is_userstack) {
60fe410c 6388 fprintf(fout, " u32 fake_sf[US_SZ_%s / 4];\n", g_func_pp->name);
6389 fprintf(fout, " u32 *esp = &fake_sf[sizeof(fake_sf) / 4];\n");
1f84f6b3 6390 had_decl = 1;
6391 }
850c9265 6392
1f84f6b3 6393 if (g_func_pp->is_vararg) {
4f12f671 6394 fprintf(fout, " va_list ap;\n");
1f84f6b3 6395 had_decl = 1;
6396 }
4f12f671 6397
940e8e66 6398 // declare arg-registers
bd96f656 6399 for (i = 0; i < g_func_pp->argc; i++) {
6400 if (g_func_pp->arg[i].reg != NULL) {
91977a1c 6401 reg = char_array_i(regs_r32,
bd96f656 6402 ARRAY_SIZE(regs_r32), g_func_pp->arg[i].reg);
75ad0378 6403 if (regmask & (1 << reg)) {
1f84f6b3 6404 if (g_func_pp->arg[i].type.is_retreg)
6405 fprintf(fout, " u32 %s = *r_%s;\n",
6406 g_func_pp->arg[i].reg, g_func_pp->arg[i].reg);
6407 else
6408 fprintf(fout, " u32 %s = (u32)a%d;\n",
6409 g_func_pp->arg[i].reg, i + 1);
75ad0378 6410 }
1f84f6b3 6411 else {
6412 if (g_func_pp->arg[i].type.is_retreg)
6413 ferr(ops, "retreg '%s' is unused?\n",
6414 g_func_pp->arg[i].reg);
75ad0378 6415 fprintf(fout, " // %s = a%d; // unused\n",
6416 g_func_pp->arg[i].reg, i + 1);
1f84f6b3 6417 }
91977a1c 6418 had_decl = 1;
6419 }
6420 }
6421
25a330eb 6422 // declare normal registers
75ad0378 6423 regmask_now = regmask & ~regmask_arg;
6424 regmask_now &= ~(1 << xSP);
90307a99 6425 if (regmask_now & 0x00ff) {
91977a1c 6426 for (reg = 0; reg < 8; reg++) {
75ad0378 6427 if (regmask_now & (1 << reg)) {
ddaf8bd7 6428 fprintf(fout, " u32 %s", regs_r32[reg]);
6429 if (regmask_init & (1 << reg))
6430 fprintf(fout, " = 0");
6431 fprintf(fout, ";\n");
91977a1c 6432 had_decl = 1;
6433 }
6434 }
6435 }
d4a985bd 6436 // ... mmx
90307a99 6437 if (regmask_now & 0xff00) {
6438 for (reg = 8; reg < 16; reg++) {
6439 if (regmask_now & (1 << reg)) {
6440 fprintf(fout, " mmxr %s", regs_r32[reg]);
6441 if (regmask_init & (1 << reg))
6442 fprintf(fout, " = { 0, }");
6443 fprintf(fout, ";\n");
6444 had_decl = 1;
6445 }
6446 }
6447 }
d4a985bd 6448 // ... x87
fe18df39 6449 if (need_float_stack) {
6450 fprintf(fout, " %s f_st[8];\n", float_type);
6451 fprintf(fout, " int f_stp = 0;\n");
6452 had_decl = 1;
6453 }
6454 else {
6455 if (regmask_now & 0xff0000) {
6456 for (reg = 16; reg < 24; reg++) {
6457 if (regmask_now & (1 << reg)) {
6458 fprintf(fout, " %s f_st%d", float_type, reg - 16);
6459 if (regmask_init & (1 << reg))
6460 fprintf(fout, " = 0");
6461 fprintf(fout, ";\n");
6462 had_decl = 1;
6463 }
d4a985bd 6464 }
6465 }
6466 }
91977a1c 6467
16057ce1 6468 if (need_float_sw) {
6469 fprintf(fout, " u16 f_sw;\n");
6470 had_decl = 1;
6471 }
6472
d4e3b5db 6473 if (regmask_save) {
6474 for (reg = 0; reg < 8; reg++) {
6475 if (regmask_save & (1 << reg)) {
6476 fprintf(fout, " u32 s_%s;\n", regs_r32[reg]);
6477 had_decl = 1;
6478 }
6479 }
6480 }
6481
3a5101d7 6482 for (i = 0; i < ARRAY_SIZE(save_arg_vars); i++) {
6483 if (save_arg_vars[i] == 0)
6484 continue;
69a3cdfc 6485 for (reg = 0; reg < 32; reg++) {
3a5101d7 6486 if (save_arg_vars[i] & (1 << reg)) {
6487 fprintf(fout, " u32 %s;\n",
6488 saved_arg_name(buf1, sizeof(buf1), i, reg + 1));
69a3cdfc 6489 had_decl = 1;
6490 }
6491 }
6492 }
6493
30620174 6494 if (regmask_ffca) {
6495 for (reg = 0; reg < 32; reg++) {
6496 if (regmask_ffca & (1 << reg)) {
6497 fprintf(fout, " %s fs_%d;\n", float_type, reg + 1);
6498 had_decl = 1;
6499 }
6500 }
6501 }
6502
25a330eb 6503 // declare push-pop temporaries
6504 if (regmask_pp) {
6505 for (reg = 0; reg < 8; reg++) {
6506 if (regmask_pp & (1 << reg)) {
6507 fprintf(fout, " u32 pp_%s;\n", regs_r32[reg]);
6508 had_decl = 1;
6509 }
6510 }
6511 }
6512
cb090db0 6513 if (cond_vars) {
69a3cdfc 6514 for (i = 0; i < 8; i++) {
cb090db0 6515 if (cond_vars & (1 << i)) {
69a3cdfc 6516 fprintf(fout, " u32 cond_%s;\n", parsed_flag_op_names[i]);
6517 had_decl = 1;
6518 }
6519 }
6520 }
6521
108e9fe3 6522 if (need_tmp_var) {
6523 fprintf(fout, " u32 tmp;\n");
6524 had_decl = 1;
6525 }
6526
2fe80fdb 6527 if (need_tmp64) {
6528 fprintf(fout, " u64 tmp64;\n");
87bf6cec 6529 had_decl = 1;
6530 }
6531
91977a1c 6532 if (had_decl)
6533 fprintf(fout, "\n");
6534
7e08c224 6535 // do stack clear, if needed
6536 if (g_sct_func_attr & SCTFA_CLEAR_SF) {
6537 fprintf(fout, " ");
6538 if (g_stack_clear_len != 0) {
6539 if (g_stack_clear_len <= 4) {
6540 for (i = 0; i < g_stack_clear_len; i++)
6541 fprintf(fout, "sf.d[%d] = ", g_stack_clear_start + i);
6542 fprintf(fout, "0;\n");
6543 }
6544 else {
6545 fprintf(fout, "memset(&sf[%d], 0, %d);\n",
6546 g_stack_clear_start, g_stack_clear_len * 4);
6547 }
6548 }
6549 else
6550 fprintf(fout, "memset(&sf, 0, sizeof(sf));\n");
6551 }
6552
bd96f656 6553 if (g_func_pp->is_vararg) {
6554 if (g_func_pp->argc_stack == 0)
4f12f671 6555 ferr(ops, "vararg func without stack args?\n");
bd96f656 6556 fprintf(fout, " va_start(ap, a%d);\n", g_func_pp->argc);
4f12f671 6557 }
6558
91977a1c 6559 // output ops
69a3cdfc 6560 for (i = 0; i < opcnt; i++)
6561 {
d7857c3a 6562 if (g_labels[i] != NULL) {
91977a1c 6563 fprintf(fout, "\n%s:\n", g_labels[i]);
3ebea2cf 6564 label_pending = 1;
2b43685d 6565
6566 delayed_flag_op = NULL;
6567 last_arith_dst = NULL;
3ebea2cf 6568 }
91977a1c 6569
69a3cdfc 6570 po = &ops[i];
6571 if (po->flags & OPF_RMD)
91977a1c 6572 continue;
6573
6574 no_output = 0;
6575
91977a1c 6576 #define assert_operand_cnt(n_) \
850c9265 6577 if (po->operand_cnt != n_) \
6578 ferr(po, "operand_cnt is %d/%d\n", po->operand_cnt, n_)
6579
69a3cdfc 6580 // conditional/flag using op?
6581 if (po->flags & OPF_CC)
850c9265 6582 {
940e8e66 6583 int is_delayed = 0;
69a3cdfc 6584
04f8a628 6585 tmp_op = po->datap;
850c9265 6586
69a3cdfc 6587 // we go through all this trouble to avoid using parsed_flag_op,
6588 // which makes generated code much nicer
6589 if (delayed_flag_op != NULL)
850c9265 6590 {
092f64e1 6591 out_cmp_test(buf1, sizeof(buf1), delayed_flag_op,
6592 po->pfo, po->pfo_inv);
940e8e66 6593 is_delayed = 1;
91977a1c 6594 }
850c9265 6595 else if (last_arith_dst != NULL
092f64e1 6596 && (po->pfo == PFO_Z || po->pfo == PFO_S || po->pfo == PFO_P
04f8a628 6597 || (tmp_op && (tmp_op->op == OP_AND || tmp_op->op == OP_OR))
6598 ))
850c9265 6599 {
3ebea2cf 6600 out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
092f64e1 6601 out_test_for_cc(buf1, sizeof(buf1), po, po->pfo, po->pfo_inv,
850c9265 6602 last_arith_dst->lmod, buf3);
940e8e66 6603 is_delayed = 1;
850c9265 6604 }
04f8a628 6605 else if (tmp_op != NULL) {
7ba45c34 6606 // use preprocessed flag calc results
092f64e1 6607 if (!(tmp_op->pfomask & (1 << po->pfo)))
6608 ferr(po, "not prepared for pfo %d\n", po->pfo);
69a3cdfc 6609
092f64e1 6610 // note: pfo_inv was not yet applied
69a3cdfc 6611 snprintf(buf1, sizeof(buf1), "(%scond_%s)",
092f64e1 6612 po->pfo_inv ? "!" : "", parsed_flag_op_names[po->pfo]);
69a3cdfc 6613 }
6614 else {
6615 ferr(po, "all methods of finding comparison failed\n");
6616 }
850c9265 6617
69a3cdfc 6618 if (po->flags & OPF_JMP) {
092f64e1 6619 fprintf(fout, " if %s", buf1);
850c9265 6620 }
cb090db0 6621 else if (po->op == OP_RCL || po->op == OP_RCR
6622 || po->op == OP_ADC || po->op == OP_SBB)
6623 {
940e8e66 6624 if (is_delayed)
6625 fprintf(fout, " cond_%s = %s;\n",
092f64e1 6626 parsed_flag_op_names[po->pfo], buf1);
850c9265 6627 }
5101a5f9 6628 else if (po->flags & OPF_DATA) { // SETcc
850c9265 6629 out_dst_opr(buf2, sizeof(buf2), po, &po->operand[0]);
6630 fprintf(fout, " %s = %s;", buf2, buf1);
91977a1c 6631 }
69a3cdfc 6632 else {
6633 ferr(po, "unhandled conditional op\n");
6634 }
91977a1c 6635 }
6636
940e8e66 6637 pfomask = po->pfomask;
6638
850c9265 6639 switch (po->op)
91977a1c 6640 {
6641 case OP_MOV:
6642 assert_operand_cnt(2);
850c9265 6643 propagate_lmod(po, &po->operand[0], &po->operand[1]);
de50b98b 6644 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
c7ed83dd 6645 default_cast_to(buf3, sizeof(buf3), &po->operand[0]);
de50b98b 6646 fprintf(fout, " %s = %s;", buf1,
3ebea2cf 6647 out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
c7ed83dd 6648 buf3, 0));
850c9265 6649 break;
6650
6651 case OP_LEA:
6652 assert_operand_cnt(2);
87bf6cec 6653 po->operand[1].lmod = OPLM_DWORD; // always
850c9265 6654 fprintf(fout, " %s = %s;",
6655 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
3ebea2cf 6656 out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
6657 NULL, 1));
850c9265 6658 break;
6659
6660 case OP_MOVZX:
6661 assert_operand_cnt(2);
91977a1c 6662 fprintf(fout, " %s = %s;",
850c9265 6663 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
3ebea2cf 6664 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
850c9265 6665 break;
6666
6667 case OP_MOVSX:
6668 assert_operand_cnt(2);
6669 switch (po->operand[1].lmod) {
6670 case OPLM_BYTE:
6671 strcpy(buf3, "(s8)");
6672 break;
6673 case OPLM_WORD:
6674 strcpy(buf3, "(s16)");
6675 break;
6676 default:
6677 ferr(po, "invalid src lmod: %d\n", po->operand[1].lmod);
6678 }
a2c1d768 6679 fprintf(fout, " %s = %s;",
850c9265 6680 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
a2c1d768 6681 out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
6682 buf3, 0));
850c9265 6683 break;
6684
108e9fe3 6685 case OP_XCHG:
6686 assert_operand_cnt(2);
6687 propagate_lmod(po, &po->operand[0], &po->operand[1]);
6688 fprintf(fout, " tmp = %s;",
6689 out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], "", 0));
6690 fprintf(fout, " %s = %s;",
6691 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
c7ed83dd 6692 out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
6693 default_cast_to(buf3, sizeof(buf3), &po->operand[0]), 0));
6694 fprintf(fout, " %s = %stmp;",
6695 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[1]),
6696 default_cast_to(buf3, sizeof(buf3), &po->operand[1]));
108e9fe3 6697 snprintf(g_comment, sizeof(g_comment), "xchg");
6698 break;
6699
850c9265 6700 case OP_NOT:
6701 assert_operand_cnt(1);
6702 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
6703 fprintf(fout, " %s = ~%s;", buf1, buf1);
6704 break;
6705
04abc5d6 6706 case OP_XLAT:
6707 assert_operand_cnt(2);
6708 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
6709 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
6710 fprintf(fout, " %s = *(u8 *)(%s + %s);", buf1, buf2, buf1);
6711 strcpy(g_comment, "xlat");
6712 break;
6713
5101a5f9 6714 case OP_CDQ:
6715 assert_operand_cnt(2);
6716 fprintf(fout, " %s = (s32)%s >> 31;",
6717 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
3ebea2cf 6718 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
5101a5f9 6719 strcpy(g_comment, "cdq");
6720 break;
6721
622eb2ef 6722 case OP_BSWAP:
6723 assert_operand_cnt(1);
6724 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
6725 fprintf(fout, " %s = __builtin_bswap32(%s);", buf1, buf1);
6726 break;
6727
092f64e1 6728 case OP_LODS:
092f64e1 6729 if (po->flags & OPF_REP) {
acd03176 6730 assert_operand_cnt(3);
092f64e1 6731 // hmh..
6732 ferr(po, "TODO\n");
6733 }
6734 else {
acd03176 6735 assert_operand_cnt(2);
3947cf24 6736 fprintf(fout, " %s = %sesi; esi %c= %d;",
6737 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[1]),
6738 lmod_cast_u_ptr(po, po->operand[1].lmod),
092f64e1 6739 (po->flags & OPF_DF) ? '-' : '+',
3947cf24 6740 lmod_bytes(po, po->operand[1].lmod));
092f64e1 6741 strcpy(g_comment, "lods");
6742 }
6743 break;
6744
33c35af6 6745 case OP_STOS:
33c35af6 6746 if (po->flags & OPF_REP) {
acd03176 6747 assert_operand_cnt(3);
591721d7 6748 fprintf(fout, " for (; ecx != 0; ecx--, edi %c= %d)\n",
6749 (po->flags & OPF_DF) ? '-' : '+',
3947cf24 6750 lmod_bytes(po, po->operand[1].lmod));
d4e3b5db 6751 fprintf(fout, " %sedi = eax;",
3947cf24 6752 lmod_cast_u_ptr(po, po->operand[1].lmod));
33c35af6 6753 strcpy(g_comment, "rep stos");
6754 }
6755 else {
acd03176 6756 assert_operand_cnt(2);
092f64e1 6757 fprintf(fout, " %sedi = eax; edi %c= %d;",
3947cf24 6758 lmod_cast_u_ptr(po, po->operand[1].lmod),
591721d7 6759 (po->flags & OPF_DF) ? '-' : '+',
3947cf24 6760 lmod_bytes(po, po->operand[1].lmod));
33c35af6 6761 strcpy(g_comment, "stos");
6762 }
6763 break;
6764
d4e3b5db 6765 case OP_MOVS:
d4e3b5db 6766 j = lmod_bytes(po, po->operand[0].lmod);
6767 strcpy(buf1, lmod_cast_u_ptr(po, po->operand[0].lmod));
591721d7 6768 l = (po->flags & OPF_DF) ? '-' : '+';
d4e3b5db 6769 if (po->flags & OPF_REP) {
acd03176 6770 assert_operand_cnt(3);
d4e3b5db 6771 fprintf(fout,
591721d7 6772 " for (; ecx != 0; ecx--, edi %c= %d, esi %c= %d)\n",
6773 l, j, l, j);
d4e3b5db 6774 fprintf(fout,
6775 " %sedi = %sesi;", buf1, buf1);
6776 strcpy(g_comment, "rep movs");
6777 }
6778 else {
acd03176 6779 assert_operand_cnt(2);
092f64e1 6780 fprintf(fout, " %sedi = %sesi; edi %c= %d; esi %c= %d;",
591721d7 6781 buf1, buf1, l, j, l, j);
d4e3b5db 6782 strcpy(g_comment, "movs");
6783 }
6784 break;
6785
7ba45c34 6786 case OP_CMPS:
7ba45c34 6787 // repe ~ repeat while ZF=1
7ba45c34 6788 j = lmod_bytes(po, po->operand[0].lmod);
6789 strcpy(buf1, lmod_cast_u_ptr(po, po->operand[0].lmod));
591721d7 6790 l = (po->flags & OPF_DF) ? '-' : '+';
7ba45c34 6791 if (po->flags & OPF_REP) {
acd03176 6792 assert_operand_cnt(3);
7ba45c34 6793 fprintf(fout,
7f20f633 6794 " while (ecx != 0) {\n");
4741fdfe 6795 if (pfomask & (1 << PFO_C)) {
6796 // ugh..
6797 fprintf(fout,
05381e4a 6798 " cond_c = %sesi < %sedi;\n", buf1, buf1);
4741fdfe 6799 pfomask &= ~(1 << PFO_C);
6800 }
7ba45c34 6801 fprintf(fout,
05381e4a 6802 " cond_z = (%sesi == %sedi); esi %c= %d, edi %c= %d;\n",
1f84f6b3 6803 buf1, buf1, l, j, l, j);
6804 fprintf(fout,
7f20f633 6805 " ecx--;\n"
1f84f6b3 6806 " if (cond_z %s 0) break;\n",
6807 (po->flags & OPF_REPZ) ? "==" : "!=");
7ba45c34 6808 fprintf(fout,
4741fdfe 6809 " }");
7ba45c34 6810 snprintf(g_comment, sizeof(g_comment), "rep%s cmps",
6811 (po->flags & OPF_REPZ) ? "e" : "ne");
6812 }
6813 else {
acd03176 6814 assert_operand_cnt(2);
7ba45c34 6815 fprintf(fout,
05381e4a 6816 " cond_z = (%sesi == %sedi); esi %c= %d; edi %c= %d;",
591721d7 6817 buf1, buf1, l, j, l, j);
7ba45c34 6818 strcpy(g_comment, "cmps");
6819 }
6820 pfomask &= ~(1 << PFO_Z);
6821 last_arith_dst = NULL;
6822 delayed_flag_op = NULL;
6823 break;
6824
591721d7 6825 case OP_SCAS:
6826 // only does ZF (for now)
6827 // repe ~ repeat while ZF=1
3947cf24 6828 j = lmod_bytes(po, po->operand[1].lmod);
591721d7 6829 l = (po->flags & OPF_DF) ? '-' : '+';
6830 if (po->flags & OPF_REP) {
acd03176 6831 assert_operand_cnt(3);
591721d7 6832 fprintf(fout,
7f20f633 6833 " while (ecx != 0) {\n");
591721d7 6834 fprintf(fout,
1f84f6b3 6835 " cond_z = (%seax == %sedi); edi %c= %d;\n",
3947cf24 6836 lmod_cast_u(po, po->operand[1].lmod),
6837 lmod_cast_u_ptr(po, po->operand[1].lmod), l, j);
1f84f6b3 6838 fprintf(fout,
7f20f633 6839 " ecx--;\n"
1f84f6b3 6840 " if (cond_z %s 0) break;\n",
591721d7 6841 (po->flags & OPF_REPZ) ? "==" : "!=");
6842 fprintf(fout,
1f84f6b3 6843 " }");
591721d7 6844 snprintf(g_comment, sizeof(g_comment), "rep%s scas",
6845 (po->flags & OPF_REPZ) ? "e" : "ne");
6846 }
6847 else {
acd03176 6848 assert_operand_cnt(2);
05381e4a 6849 fprintf(fout, " cond_z = (%seax == %sedi); edi %c= %d;",
3947cf24 6850 lmod_cast_u(po, po->operand[1].lmod),
6851 lmod_cast_u_ptr(po, po->operand[1].lmod), l, j);
591721d7 6852 strcpy(g_comment, "scas");
6853 }
6854 pfomask &= ~(1 << PFO_Z);
6855 last_arith_dst = NULL;
6856 delayed_flag_op = NULL;
6857 break;
6858
850c9265 6859 // arithmetic w/flags
850c9265 6860 case OP_AND:
2b70f6d3 6861 if (po->operand[1].type == OPT_CONST && !po->operand[1].val)
6862 goto dualop_arith_const;
6863 propagate_lmod(po, &po->operand[0], &po->operand[1]);
6864 goto dualop_arith;
6865
850c9265 6866 case OP_OR:
5101a5f9 6867 propagate_lmod(po, &po->operand[0], &po->operand[1]);
2b70f6d3 6868 if (po->operand[1].type == OPT_CONST) {
6869 j = lmod_bytes(po, po->operand[0].lmod);
6870 if (((1ull << j * 8) - 1) == po->operand[1].val)
6871 goto dualop_arith_const;
6872 }
6873 goto dualop_arith;
6874
850c9265 6875 dualop_arith:
6876 assert_operand_cnt(2);
850c9265 6877 fprintf(fout, " %s %s= %s;",
6878 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
6879 op_to_c(po),
3ebea2cf 6880 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
04f8a628 6881 output_std_flags(fout, po, &pfomask, buf1);
6882 last_arith_dst = &po->operand[0];
6883 delayed_flag_op = NULL;
6884 break;
6885
2b70f6d3 6886 dualop_arith_const:
6887 // and 0, or ~0 used instead mov
6888 assert_operand_cnt(2);
6889 fprintf(fout, " %s = %s;",
6890 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
6891 out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
6892 default_cast_to(buf3, sizeof(buf3), &po->operand[0]), 0));
6893 output_std_flags(fout, po, &pfomask, buf1);
6894 last_arith_dst = &po->operand[0];
6895 delayed_flag_op = NULL;
6896 break;
6897
04f8a628 6898 case OP_SHL:
6899 case OP_SHR:
6900 assert_operand_cnt(2);
6901 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
6902 if (pfomask & (1 << PFO_C)) {
6903 if (po->operand[1].type == OPT_CONST) {
6904 l = lmod_bytes(po, po->operand[0].lmod) * 8;
6905 j = po->operand[1].val;
6906 j %= l;
6907 if (j != 0) {
6908 if (po->op == OP_SHL)
6909 j = l - j;
6910 else
6911 j -= 1;
cb090db0 6912 fprintf(fout, " cond_c = (%s >> %d) & 1;\n",
6913 buf1, j);
04f8a628 6914 }
6915 else
6916 ferr(po, "zero shift?\n");
6917 }
6918 else
6919 ferr(po, "TODO\n");
6920 pfomask &= ~(1 << PFO_C);
840257f6 6921 }
04abc5d6 6922 fprintf(fout, " %s %s= %s", buf1, op_to_c(po),
04f8a628 6923 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
04abc5d6 6924 if (po->operand[1].type != OPT_CONST)
6925 fprintf(fout, " & 0x1f");
6926 fprintf(fout, ";");
04f8a628 6927 output_std_flags(fout, po, &pfomask, buf1);
850c9265 6928 last_arith_dst = &po->operand[0];
69a3cdfc 6929 delayed_flag_op = NULL;
850c9265 6930 break;
6931
d4e3b5db 6932 case OP_SAR:
6933 assert_operand_cnt(2);
6934 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
6935 fprintf(fout, " %s = %s%s >> %s;", buf1,
6936 lmod_cast_s(po, po->operand[0].lmod), buf1,
3ebea2cf 6937 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
04f8a628 6938 output_std_flags(fout, po, &pfomask, buf1);
d4e3b5db 6939 last_arith_dst = &po->operand[0];
6940 delayed_flag_op = NULL;
6941 break;
6942
04abc5d6 6943 case OP_SHLD:
3b2f4044 6944 case OP_SHRD:
6945 assert_operand_cnt(3);
6946 propagate_lmod(po, &po->operand[0], &po->operand[1]);
6947 l = lmod_bytes(po, po->operand[0].lmod) * 8;
3b2f4044 6948 out_src_opr_u32(buf3, sizeof(buf3), po, &po->operand[2]);
acd03176 6949 if (po->operand[2].type != OPT_CONST) {
6950 // no handling for "undefined" case, hopefully not needed
6951 snprintf(buf2, sizeof(buf2), "(%s & 0x1f)", buf3);
6952 strcpy(buf3, buf2);
6953 }
6954 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
6955 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
04abc5d6 6956 if (po->op == OP_SHLD) {
6957 fprintf(fout, " %s <<= %s; %s |= %s >> (%d - %s);",
6958 buf1, buf3, buf1, buf2, l, buf3);
6959 strcpy(g_comment, "shld");
6960 }
6961 else {
6962 fprintf(fout, " %s >>= %s; %s |= %s << (%d - %s);",
6963 buf1, buf3, buf1, buf2, l, buf3);
6964 strcpy(g_comment, "shrd");
6965 }
3b2f4044 6966 output_std_flags(fout, po, &pfomask, buf1);
6967 last_arith_dst = &po->operand[0];
6968 delayed_flag_op = NULL;
6969 break;
6970
d4e3b5db 6971 case OP_ROL:
6972 case OP_ROR:
6973 assert_operand_cnt(2);
6974 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
6975 if (po->operand[1].type == OPT_CONST) {
6976 j = po->operand[1].val;
6977 j %= lmod_bytes(po, po->operand[0].lmod) * 8;
6978 fprintf(fout, po->op == OP_ROL ?
6979 " %s = (%s << %d) | (%s >> %d);" :
6980 " %s = (%s >> %d) | (%s << %d);",
6981 buf1, buf1, j, buf1,
6982 lmod_bytes(po, po->operand[0].lmod) * 8 - j);
6983 }
6984 else
6985 ferr(po, "TODO\n");
04f8a628 6986 output_std_flags(fout, po, &pfomask, buf1);
d4e3b5db 6987 last_arith_dst = &po->operand[0];
6988 delayed_flag_op = NULL;
6989 break;
6990
cb090db0 6991 case OP_RCL:
6992 case OP_RCR:
6993 assert_operand_cnt(2);
6994 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
6995 l = lmod_bytes(po, po->operand[0].lmod) * 8;
6996 if (po->operand[1].type == OPT_CONST) {
6997 j = po->operand[1].val % l;
6998 if (j == 0)
6999 ferr(po, "zero rotate\n");
7000 fprintf(fout, " tmp = (%s >> %d) & 1;\n",
7001 buf1, (po->op == OP_RCL) ? (l - j) : (j - 1));
7002 if (po->op == OP_RCL) {
7003 fprintf(fout,
7004 " %s = (%s << %d) | (cond_c << %d)",
7005 buf1, buf1, j, j - 1);
7006 if (j != 1)
7007 fprintf(fout, " | (%s >> %d)", buf1, l + 1 - j);
7008 }
7009 else {
7010 fprintf(fout,
7011 " %s = (%s >> %d) | (cond_c << %d)",
7012 buf1, buf1, j, l - j);
7013 if (j != 1)
7014 fprintf(fout, " | (%s << %d)", buf1, l + 1 - j);
7015 }
7016 fprintf(fout, ";\n");
7017 fprintf(fout, " cond_c = tmp;");
7018 }
7019 else
7020 ferr(po, "TODO\n");
7021 strcpy(g_comment, (po->op == OP_RCL) ? "rcl" : "rcr");
7022 output_std_flags(fout, po, &pfomask, buf1);
7023 last_arith_dst = &po->operand[0];
7024 delayed_flag_op = NULL;
7025 break;
7026
5101a5f9 7027 case OP_XOR:
850c9265 7028 assert_operand_cnt(2);
7029 propagate_lmod(po, &po->operand[0], &po->operand[1]);
5101a5f9 7030 if (IS(opr_name(po, 0), opr_name(po, 1))) {
7031 // special case for XOR
7f20f633 7032 int z = PFOB_O | PFOB_C | PFOB_S | (1 << PFO_L);
7033 for (j = 0; j <= PFO_LE; j++) {
7034 if (pfomask & (1 << j)) {
7035 fprintf(fout, " cond_%s = %d;\n",
7036 parsed_flag_op_names[j], (1 << j) & z ? 0 : 1);
7037 pfomask &= ~(1 << j);
7038 }
2fe80fdb 7039 }
5101a5f9 7040 fprintf(fout, " %s = 0;",
7041 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
7042 last_arith_dst = &po->operand[0];
7043 delayed_flag_op = NULL;
850c9265 7044 break;
850c9265 7045 }
5101a5f9 7046 goto dualop_arith;
7047
2fe80fdb 7048 case OP_ADD:
7049 assert_operand_cnt(2);
7050 propagate_lmod(po, &po->operand[0], &po->operand[1]);
7051 if (pfomask & (1 << PFO_C)) {
c8dbc5be 7052 out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
7053 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
7054 if (po->operand[0].lmod == OPLM_DWORD) {
7055 fprintf(fout, " tmp64 = (u64)%s + %s;\n", buf1, buf2);
7056 fprintf(fout, " cond_c = tmp64 >> 32;\n");
7057 fprintf(fout, " %s = (u32)tmp64;",
7058 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
d4a985bd 7059 strcat(g_comment, " add64");
c8dbc5be 7060 }
7061 else {
7062 fprintf(fout, " cond_c = ((u32)%s + %s) >> %d;\n",
7063 buf1, buf2, lmod_bytes(po, po->operand[0].lmod) * 8);
7064 fprintf(fout, " %s += %s;",
7065 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
7066 buf2);
7067 }
2fe80fdb 7068 pfomask &= ~(1 << PFO_C);
7069 output_std_flags(fout, po, &pfomask, buf1);
7070 last_arith_dst = &po->operand[0];
7071 delayed_flag_op = NULL;
7072 break;
7073 }
16057ce1 7074 if (pfomask & (1 << PFO_LE)) {
7075 out_cmp_for_cc(buf1, sizeof(buf1), po, PFO_LE, 0, 1);
7076 fprintf(fout, " cond_%s = %s;\n",
7077 parsed_flag_op_names[PFO_LE], buf1);
7078 pfomask &= ~(1 << PFO_LE);
7079 }
2fe80fdb 7080 goto dualop_arith;
7081
7082 case OP_SUB:
7083 assert_operand_cnt(2);
7084 propagate_lmod(po, &po->operand[0], &po->operand[1]);
3b2f4044 7085 if (pfomask & ~((1 << PFO_Z) | (1 << PFO_S))) {
7086 for (j = 0; j <= PFO_LE; j++) {
7087 if (!(pfomask & (1 << j)))
7088 continue;
7089 if (j == PFO_Z || j == PFO_S)
7090 continue;
7091
16057ce1 7092 out_cmp_for_cc(buf1, sizeof(buf1), po, j, 0, 0);
3b2f4044 7093 fprintf(fout, " cond_%s = %s;\n",
7094 parsed_flag_op_names[j], buf1);
7095 pfomask &= ~(1 << j);
7096 }
2fe80fdb 7097 }
7098 goto dualop_arith;
7099
5101a5f9 7100 case OP_ADC:
850c9265 7101 case OP_SBB:
5101a5f9 7102 assert_operand_cnt(2);
7103 propagate_lmod(po, &po->operand[0], &po->operand[1]);
a2c1d768 7104 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
840257f6 7105 if (po->op == OP_SBB
7106 && IS(po->operand[0].name, po->operand[1].name))
7107 {
7108 // avoid use of unitialized var
a2c1d768 7109 fprintf(fout, " %s = -cond_c;", buf1);
94d447fb 7110 // carry remains what it was
7111 pfomask &= ~(1 << PFO_C);
840257f6 7112 }
7113 else {
a2c1d768 7114 fprintf(fout, " %s %s= %s + cond_c;", buf1, op_to_c(po),
3ebea2cf 7115 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
840257f6 7116 }
a2c1d768 7117 output_std_flags(fout, po, &pfomask, buf1);
5101a5f9 7118 last_arith_dst = &po->operand[0];
7119 delayed_flag_op = NULL;
850c9265 7120 break;
7121
1f84f6b3 7122 case OP_BSF:
f9327ad4 7123 case OP_BSR:
7124 // on SKL, if src is 0, dst is left unchanged
1f84f6b3 7125 assert_operand_cnt(2);
f9327ad4 7126 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
1f84f6b3 7127 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
f9327ad4 7128 output_std_flag_z(fout, po, &pfomask, buf2);
7129 if (po->op == OP_BSF)
7130 snprintf(buf3, sizeof(buf3), "__builtin_ffs(%s) - 1", buf2);
7131 else
7132 snprintf(buf3, sizeof(buf3), "31 - __builtin_clz(%s)", buf2);
7133 fprintf(fout, " if (%s) %s = %s;", buf2, buf1, buf3);
1f84f6b3 7134 last_arith_dst = &po->operand[0];
7135 delayed_flag_op = NULL;
f9327ad4 7136 strcat(g_comment, po->op == OP_BSF ? " bsf" : " bsr");
1f84f6b3 7137 break;
7138
850c9265 7139 case OP_DEC:
90307a99 7140 if (pfomask & ~(PFOB_S | PFOB_S | PFOB_C)) {
7141 for (j = 0; j <= PFO_LE; j++) {
7142 if (!(pfomask & (1 << j)))
7143 continue;
7144 if (j == PFO_Z || j == PFO_S || j == PFO_C)
7145 continue;
7146
16057ce1 7147 out_cmp_for_cc(buf1, sizeof(buf1), po, j, 0, 0);
90307a99 7148 fprintf(fout, " cond_%s = %s;\n",
7149 parsed_flag_op_names[j], buf1);
7150 pfomask &= ~(1 << j);
7151 }
7152 }
7153 // fallthrough
7154
7155 case OP_INC:
7156 if (pfomask & (1 << PFO_C))
7157 // carry is unaffected by inc/dec.. wtf?
7158 ferr(po, "carry propagation needed\n");
7159
850c9265 7160 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
1bafb621 7161 if (po->operand[0].type == OPT_REG) {
7162 strcpy(buf2, po->op == OP_INC ? "++" : "--");
7163 fprintf(fout, " %s%s;", buf1, buf2);
7164 }
7165 else {
7166 strcpy(buf2, po->op == OP_INC ? "+" : "-");
7167 fprintf(fout, " %s %s= 1;", buf1, buf2);
7168 }
a2c1d768 7169 output_std_flags(fout, po, &pfomask, buf1);
5101a5f9 7170 last_arith_dst = &po->operand[0];
7171 delayed_flag_op = NULL;
7172 break;
7173
7174 case OP_NEG:
7175 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
3ebea2cf 7176 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]);
5101a5f9 7177 fprintf(fout, " %s = -%s%s;", buf1,
7178 lmod_cast_s(po, po->operand[0].lmod), buf2);
850c9265 7179 last_arith_dst = &po->operand[0];
69a3cdfc 7180 delayed_flag_op = NULL;
0ea6430c 7181 if (pfomask & PFOB_C) {
940e8e66 7182 fprintf(fout, "\n cond_c = (%s != 0);", buf1);
0ea6430c 7183 pfomask &= ~PFOB_C;
940e8e66 7184 }
0ea6430c 7185 output_std_flags(fout, po, &pfomask, buf1);
850c9265 7186 break;
7187
7188 case OP_IMUL:
de50b98b 7189 if (po->operand_cnt == 2) {
7190 propagate_lmod(po, &po->operand[0], &po->operand[1]);
850c9265 7191 goto dualop_arith;
de50b98b 7192 }
87bf6cec 7193 if (po->operand_cnt == 3)
7194 ferr(po, "TODO imul3\n");
7195 // fallthrough
7196 case OP_MUL:
7197 assert_operand_cnt(1);
c8dbc5be 7198 switch (po->operand[0].lmod) {
7199 case OPLM_DWORD:
7200 strcpy(buf1, po->op == OP_IMUL ? "(s64)(s32)" : "(u64)");
7201 fprintf(fout, " tmp64 = %seax * %s%s;\n", buf1, buf1,
7202 out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]));
7203 fprintf(fout, " edx = tmp64 >> 32;\n");
7204 fprintf(fout, " eax = tmp64;");
7205 break;
7206 case OPLM_BYTE:
7207 strcpy(buf1, po->op == OP_IMUL ? "(s16)(s8)" : "(u16)(u8)");
7208 fprintf(fout, " LOWORD(eax) = %seax * %s;", buf1,
7209 out_src_opr(buf2, sizeof(buf2), po, &po->operand[0],
7210 buf1, 0));
7211 break;
7212 default:
7213 ferr(po, "TODO: unhandled mul type\n");
7214 break;
7215 }
87bf6cec 7216 last_arith_dst = NULL;
69a3cdfc 7217 delayed_flag_op = NULL;
91977a1c 7218 break;
7219
5101a5f9 7220 case OP_DIV:
7221 case OP_IDIV:
7222 assert_operand_cnt(1);
cb090db0 7223 out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
acd03176 7224 strcpy(cast, lmod_cast(po, po->operand[0].lmod,
cb090db0 7225 po->op == OP_IDIV));
7226 switch (po->operand[0].lmod) {
7227 case OPLM_DWORD:
7228 if (po->flags & OPF_32BIT)
acd03176 7229 snprintf(buf2, sizeof(buf2), "%seax", cast);
cb090db0 7230 else {
7231 fprintf(fout, " tmp64 = ((u64)edx << 32) | eax;\n");
acd03176 7232 snprintf(buf2, sizeof(buf2), "%stmp64",
cb090db0 7233 (po->op == OP_IDIV) ? "(s64)" : "");
7234 }
7235 if (po->operand[0].type == OPT_REG
7236 && po->operand[0].reg == xDX)
7237 {
acd03176 7238 fprintf(fout, " eax = %s / %s%s;\n", buf2, cast, buf1);
7239 fprintf(fout, " edx = %s %% %s%s;", buf2, cast, buf1);
7240 }
7241 else {
7242 fprintf(fout, " edx = %s %% %s%s;\n", buf2, cast, buf1);
7243 fprintf(fout, " eax = %s / %s%s;", buf2, cast, buf1);
7244 }
7245 break;
7246 case OPLM_WORD:
7247 fprintf(fout, " tmp = (edx << 16) | (eax & 0xffff);\n");
7248 snprintf(buf2, sizeof(buf2), "%stmp",
7249 (po->op == OP_IDIV) ? "(s32)" : "");
7250 if (po->operand[0].type == OPT_REG
7251 && po->operand[0].reg == xDX)
7252 {
7253 fprintf(fout, " LOWORD(eax) = %s / %s%s;\n",
7254 buf2, cast, buf1);
7255 fprintf(fout, " LOWORD(edx) = %s %% %s%s;",
7256 buf2, cast, buf1);
cb090db0 7257 }
7258 else {
acd03176 7259 fprintf(fout, " LOWORD(edx) = %s %% %s%s;\n",
7260 buf2, cast, buf1);
7261 fprintf(fout, " LOWORD(eax) = %s / %s%s;",
7262 buf2, cast, buf1);
cb090db0 7263 }
d4a985bd 7264 strcat(g_comment, " div16");
cb090db0 7265 break;
7266 default:
acd03176 7267 ferr(po, "unhandled div lmod %d\n", po->operand[0].lmod);
5101a5f9 7268 }
87bf6cec 7269 last_arith_dst = NULL;
7270 delayed_flag_op = NULL;
5101a5f9 7271 break;
7272
91977a1c 7273 case OP_TEST:
7274 case OP_CMP:
850c9265 7275 propagate_lmod(po, &po->operand[0], &po->operand[1]);
940e8e66 7276 if (pfomask != 0) {
69a3cdfc 7277 for (j = 0; j < 8; j++) {
940e8e66 7278 if (pfomask & (1 << j)) {
69a3cdfc 7279 out_cmp_test(buf1, sizeof(buf1), po, j, 0);
7280 fprintf(fout, " cond_%s = %s;",
7281 parsed_flag_op_names[j], buf1);
7282 }
7283 }
940e8e66 7284 pfomask = 0;
69a3cdfc 7285 }
7286 else
7287 no_output = 1;
7ba45c34 7288 last_arith_dst = NULL;
69a3cdfc 7289 delayed_flag_op = po;
91977a1c 7290 break;
7291
092f64e1 7292 case OP_SCC:
7293 // SETcc - should already be handled
7294 break;
7295
69a3cdfc 7296 // note: we reuse OP_Jcc for SETcc, only flags differ
092f64e1 7297 case OP_JCC:
7298 fprintf(fout, "\n goto %s;", po->operand[0].name);
850c9265 7299 break;
7300
5c024ef7 7301 case OP_JECXZ:
7302 fprintf(fout, " if (ecx == 0)\n");
7303 fprintf(fout, " goto %s;", po->operand[0].name);
d4a985bd 7304 strcat(g_comment, " jecxz");
5c024ef7 7305 break;
7306
04abc5d6 7307 case OP_LOOP:
3947cf24 7308 fprintf(fout, " if (--ecx != 0)\n");
04abc5d6 7309 fprintf(fout, " goto %s;", po->operand[0].name);
d4a985bd 7310 strcat(g_comment, " loop");
04abc5d6 7311 break;
7312
850c9265 7313 case OP_JMP:
87bf6cec 7314 assert_operand_cnt(1);
de50b98b 7315 last_arith_dst = NULL;
7316 delayed_flag_op = NULL;
7317
4c45fa73 7318 if (po->operand[0].type == OPT_REGMEM) {
7319 ret = sscanf(po->operand[0].name, "%[^[][%[^*]*4]",
7320 buf1, buf2);
7321 if (ret != 2)
7322 ferr(po, "parse failure for jmp '%s'\n",
7323 po->operand[0].name);
7324 fprintf(fout, " goto *jt_%s[%s];", buf1, buf2);
7325 break;
7326 }
7327 else if (po->operand[0].type != OPT_LABEL)
7328 ferr(po, "unhandled jmp type\n");
87bf6cec 7329
850c9265 7330 fprintf(fout, " goto %s;", po->operand[0].name);
91977a1c 7331 break;
7332
7333 case OP_CALL:
5101a5f9 7334 assert_operand_cnt(1);
092f64e1 7335 pp = po->pp;
89ff3147 7336 my_assert_not(pp, NULL);
91977a1c 7337
092f64e1 7338 strcpy(buf3, " ");
7339 if (po->flags & OPF_CC) {
7340 // we treat conditional branch to another func
7341 // (yes such code exists..) as conditional tailcall
7342 strcat(buf3, " ");
7343 fprintf(fout, " {\n");
7344 }
7345
8eb12e72 7346 if (pp->is_fptr && !pp->is_arg) {
092f64e1 7347 fprintf(fout, "%s%s = %s;\n", buf3, pp->name,
1cd4a663 7348 out_src_opr(buf1, sizeof(buf1), po, &po->operand[0],
7349 "(void *)", 0));
2eae9f23 7350 if (pp->is_unresolved || IS_START(pp->name, "i_guess"))
8eb12e72 7351 fprintf(fout, "%sunresolved_call(\"%s:%d\", %s);\n",
7352 buf3, asmfn, po->asmln, pp->name);
7353 }
1bafb621 7354
092f64e1 7355 fprintf(fout, "%s", buf3);
2b43685d 7356 if (strstr(pp->ret_type.name, "int64")) {
87bf6cec 7357 if (po->flags & OPF_TAIL)
2b43685d 7358 ferr(po, "int64 and tail?\n");
2fe80fdb 7359 fprintf(fout, "tmp64 = ");
2b43685d 7360 }
7361 else if (!IS(pp->ret_type.name, "void")) {
7362 if (po->flags & OPF_TAIL) {
d4a985bd 7363 if (regmask_ret & mxAX) {
840257f6 7364 fprintf(fout, "return ");
7365 if (g_func_pp->ret_type.is_ptr != pp->ret_type.is_ptr)
7366 fprintf(fout, "(%s)", g_func_pp->ret_type.name);
7367 }
d4a985bd 7368 else if (regmask_ret & mxST0)
7369 ferr(po, "float tailcall\n");
2b43685d 7370 }
d4a985bd 7371 else if (po->regmask_dst & mxAX) {
87bf6cec 7372 fprintf(fout, "eax = ");
2b43685d 7373 if (pp->ret_type.is_ptr)
7374 fprintf(fout, "(u32)");
7375 }
d4a985bd 7376 else if (po->regmask_dst & mxST0) {
fe18df39 7377 ferr_assert(po, po->flags & OPF_FPUSH);
7378 if (need_float_stack)
7379 fprintf(fout, "f_st[--f_stp & 7] = ");
7380 else
7381 fprintf(fout, "f_st0 = ");
d4a985bd 7382 }
91977a1c 7383 }
87bf6cec 7384
ddaf8bd7 7385 if (pp->name[0] == 0)
7386 ferr(po, "missing pp->name\n");
7387 fprintf(fout, "%s%s(", pp->name,
7388 pp->has_structarg ? "_sa" : "");
39b168b8 7389
2fe80fdb 7390 if (po->flags & OPF_ATAIL) {
56b49358 7391 int check_compat =
7392 g_func_pp->is_stdcall && g_func_pp->argc_stack > 0;
7393 check_compat |= pp->argc_stack > 0;
7394 if (check_compat
7395 && (pp->argc_stack != g_func_pp->argc_stack
7396 || pp->is_stdcall != g_func_pp->is_stdcall))
7397 ferr(po, "incompatible arg-reuse tailcall\n");
1f84f6b3 7398 if (g_func_pp->has_retreg)
7399 ferr(po, "TODO: retreg+tailcall\n");
87bf6cec 7400
2fe80fdb 7401 for (arg = j = 0; arg < pp->argc; arg++) {
7402 if (arg > 0)
7403 fprintf(fout, ", ");
87bf6cec 7404
2fe80fdb 7405 cast[0] = 0;
7406 if (pp->arg[arg].type.is_ptr)
7407 snprintf(cast, sizeof(cast), "(%s)",
7408 pp->arg[arg].type.name);
91977a1c 7409
2fe80fdb 7410 if (pp->arg[arg].reg != NULL) {
7411 fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
7412 continue;
7413 }
7414 // stack arg
7415 for (; j < g_func_pp->argc; j++)
7416 if (g_func_pp->arg[j].reg == NULL)
7417 break;
7418 fprintf(fout, "%sa%d", cast, j + 1);
7419 j++;
69a3cdfc 7420 }
2fe80fdb 7421 }
7422 else {
7423 for (arg = 0; arg < pp->argc; arg++) {
7424 if (arg > 0)
7425 fprintf(fout, ", ");
7426
7427 cast[0] = 0;
7428 if (pp->arg[arg].type.is_ptr)
7429 snprintf(cast, sizeof(cast), "(%s)",
7430 pp->arg[arg].type.name);
7431
7432 if (pp->arg[arg].reg != NULL) {
1f84f6b3 7433 if (pp->arg[arg].type.is_retreg)
7434 fprintf(fout, "&%s", pp->arg[arg].reg);
8c83cc48 7435 else if (IS(pp->arg[arg].reg, "ebp")
e627c4d0 7436 && g_bp_frame && !(po->flags & OPF_EBP_S))
8c83cc48 7437 {
7438 // rare special case
7439 fprintf(fout, "%s(u32)&sf.b[sizeof(sf)]", cast);
7440 strcat(g_comment, " bp_ref");
7441 }
1f84f6b3 7442 else
7443 fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
2fe80fdb 7444 continue;
7445 }
7446
7447 // stack arg
7448 tmp_op = pp->arg[arg].datap;
7449 if (tmp_op == NULL)
7450 ferr(po, "parsed_op missing for arg%d\n", arg);
23fd0b11 7451
7452 if (tmp_op->flags & OPF_VAPUSH) {
7453 fprintf(fout, "ap");
7454 }
30620174 7455 else if (tmp_op->op == OP_FST) {
7456 fprintf(fout, "fs_%d", tmp_op->p_argnum);
7457 if (tmp_op->operand[0].lmod == OPLM_QWORD)
7458 arg++;
7459 }
57860365 7460 else if (pp->arg[arg].type.is_64bit) {
7461 ferr_assert(po, tmp_op->p_argpass == 0);
7462 ferr_assert(po, !pp->arg[arg].is_saved);
b62264bc 7463 ferr_assert(po, !pp->arg[arg].type.is_float);
57860365 7464 ferr_assert(po, cast[0] == 0);
7465 out_src_opr(buf1, sizeof(buf1),
7466 tmp_op, &tmp_op->operand[0], cast, 0);
7467 tmp_op = pp->arg[++arg].datap;
7468 ferr_assert(po, tmp_op != NULL);
7469 out_src_opr(buf2, sizeof(buf2),
7470 tmp_op, &tmp_op->operand[0], cast, 0);
7471 fprintf(fout, "((u64)(%s) << 32) | (%s)",
7472 buf2, buf1);
7473 }
5f70a34f 7474 else if (tmp_op->p_argpass != 0) {
b62264bc 7475 ferr_assert(po, !pp->arg[arg].type.is_float);
5f70a34f 7476 fprintf(fout, "a%d", tmp_op->p_argpass);
7477 }
8c83cc48 7478 else if (pp->arg[arg].is_saved) {
7479 ferr_assert(po, tmp_op->p_argnum > 0);
b62264bc 7480 ferr_assert(po, !pp->arg[arg].type.is_float);
3a5101d7 7481 fprintf(fout, "%s%s", cast,
7482 saved_arg_name(buf1, sizeof(buf1),
7483 tmp_op->p_arggrp, tmp_op->p_argnum));
2fe80fdb 7484 }
b62264bc 7485 else if (pp->arg[arg].type.is_float) {
7486 ferr_assert(po, !pp->arg[arg].type.is_64bit);
7487 fprintf(fout, "%s",
7488 out_src_opr_float(buf1, sizeof(buf1),
7489 tmp_op, &tmp_op->operand[0], need_float_stack));
7490 }
2fe80fdb 7491 else {
7492 fprintf(fout, "%s",
7493 out_src_opr(buf1, sizeof(buf1),
7494 tmp_op, &tmp_op->operand[0], cast, 0));
7495 }
69a3cdfc 7496 }
91977a1c 7497 }
7498 fprintf(fout, ");");
87bf6cec 7499
2b43685d 7500 if (strstr(pp->ret_type.name, "int64")) {
7501 fprintf(fout, "\n");
092f64e1 7502 fprintf(fout, "%sedx = tmp64 >> 32;\n", buf3);
7503 fprintf(fout, "%seax = tmp64;", buf3);
2b43685d 7504 }
7505
89ff3147 7506 if (pp->is_unresolved) {
8eb12e72 7507 snprintf(buf2, sizeof(buf2), " unresolved %dreg",
89ff3147 7508 pp->argc_reg);
092f64e1 7509 strcat(g_comment, buf2);
89ff3147 7510 }
7511
87bf6cec 7512 if (po->flags & OPF_TAIL) {
840257f6 7513 ret = 0;
ddaf8bd7 7514 if (i == opcnt - 1 || pp->is_noreturn)
840257f6 7515 ret = 0;
7516 else if (IS(pp->ret_type.name, "void"))
7517 ret = 1;
b2bd20c0 7518 else if (!(regmask_ret & (1 << xAX)))
840257f6 7519 ret = 1;
7520 // else already handled as 'return f()'
7521
7522 if (ret) {
acd03176 7523 fprintf(fout, "\n%sreturn;", buf3);
7524 strcat(g_comment, " ^ tailcall");
3ebea2cf 7525 }
89ff3147 7526 else
ddaf8bd7 7527 strcat(g_comment, " tailcall");
acd03176 7528
7529 if ((regmask_ret & (1 << xAX))
7530 && IS(pp->ret_type.name, "void") && !pp->is_noreturn)
7531 {
7532 ferr(po, "int func -> void func tailcall?\n");
7533 }
87bf6cec 7534 }
ddaf8bd7 7535 if (pp->is_noreturn)
7536 strcat(g_comment, " noreturn");
2fe80fdb 7537 if ((po->flags & OPF_ATAIL) && pp->argc_stack > 0)
7538 strcat(g_comment, " argframe");
092f64e1 7539 if (po->flags & OPF_CC)
7540 strcat(g_comment, " cond");
7541
7542 if (po->flags & OPF_CC)
7543 fprintf(fout, "\n }");
7544
87bf6cec 7545 delayed_flag_op = NULL;
7546 last_arith_dst = NULL;
91977a1c 7547 break;
7548
7549 case OP_RET:
bd96f656 7550 if (g_func_pp->is_vararg)
4f12f671 7551 fprintf(fout, " va_end(ap);\n");
1f84f6b3 7552 if (g_func_pp->has_retreg) {
7553 for (arg = 0; arg < g_func_pp->argc; arg++)
7554 if (g_func_pp->arg[arg].type.is_retreg)
7555 fprintf(fout, " *r_%s = %s;\n",
7556 g_func_pp->arg[arg].reg, g_func_pp->arg[arg].reg);
7557 }
4f12f671 7558
16057ce1 7559 if (regmask_ret & mxST0) {
7560 fprintf(fout, " return %s;", float_st0);
7561 }
7562 else if (!(regmask_ret & mxAX)) {
3ebea2cf 7563 if (i != opcnt - 1 || label_pending)
7564 fprintf(fout, " return;");
7565 }
bd96f656 7566 else if (g_func_pp->ret_type.is_ptr) {
d4e3b5db 7567 fprintf(fout, " return (%s)eax;",
bd96f656 7568 g_func_pp->ret_type.name);
3ebea2cf 7569 }
2fe80fdb 7570 else if (IS(g_func_pp->ret_type.name, "__int64"))
7571 fprintf(fout, " return ((u64)edx << 32) | eax;");
91977a1c 7572 else
7573 fprintf(fout, " return eax;");
de50b98b 7574
7575 last_arith_dst = NULL;
7576 delayed_flag_op = NULL;
91977a1c 7577 break;
7578
7579 case OP_PUSH:
1f84f6b3 7580 out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
5f70a34f 7581 if (po->p_argnum != 0) {
69a3cdfc 7582 // special case - saved func arg
3a5101d7 7583 fprintf(fout, " %s = %s;",
7584 saved_arg_name(buf2, sizeof(buf2),
7585 po->p_arggrp, po->p_argnum), buf1);
69a3cdfc 7586 break;
7587 }
d4e3b5db 7588 else if (po->flags & OPF_RSAVE) {
d4e3b5db 7589 fprintf(fout, " s_%s = %s;", buf1, buf1);
7590 break;
7591 }
25a330eb 7592 else if (po->flags & OPF_PPUSH) {
7593 tmp_op = po->datap;
7594 ferr_assert(po, tmp_op != NULL);
7595 out_dst_opr(buf2, sizeof(buf2), po, &tmp_op->operand[0]);
7596 fprintf(fout, " pp_%s = %s;", buf2, buf1);
7597 break;
7598 }
1f84f6b3 7599 else if (g_func_pp->is_userstack) {
7600 fprintf(fout, " *(--esp) = %s;", buf1);
7601 break;
7602 }
e56ab892 7603 if (!(g_ida_func_attr & IDAFA_NORETURN))
7604 ferr(po, "stray push encountered\n");
7605 no_output = 1;
91977a1c 7606 break;
7607
7608 case OP_POP:
25a330eb 7609 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
d4e3b5db 7610 if (po->flags & OPF_RSAVE) {
d4e3b5db 7611 fprintf(fout, " %s = s_%s;", buf1, buf1);
7612 break;
7613 }
25a330eb 7614 else if (po->flags & OPF_PPUSH) {
e83ea7ed 7615 // push/pop graph / non-const
25a330eb 7616 ferr_assert(po, po->datap == NULL);
7617 fprintf(fout, " %s = pp_%s;", buf1, buf1);
7618 break;
7619 }
5c024ef7 7620 else if (po->datap != NULL) {
7621 // push/pop pair
7622 tmp_op = po->datap;
5c024ef7 7623 fprintf(fout, " %s = %s;", buf1,
7624 out_src_opr(buf2, sizeof(buf2),
7625 tmp_op, &tmp_op->operand[0],
c7ed83dd 7626 default_cast_to(buf3, sizeof(buf3), &po->operand[0]), 0));
5c024ef7 7627 break;
7628 }
1f84f6b3 7629 else if (g_func_pp->is_userstack) {
25a330eb 7630 fprintf(fout, " %s = *esp++;", buf1);
1f84f6b3 7631 break;
7632 }
7633 else
7634 ferr(po, "stray pop encountered\n");
91977a1c 7635 break;
7636
33c35af6 7637 case OP_NOP:
2b43685d 7638 no_output = 1;
33c35af6 7639 break;
7640
622eb2ef 7641 // pseudo ops
7642 case OPP_ALLSHL:
7643 case OPP_ALLSHR:
7644 fprintf(fout, " tmp64 = ((u64)edx << 32) | eax;\n");
8c83cc48 7645 fprintf(fout, " tmp64 = (s64)tmp64 %s LOBYTE(ecx);\n",
622eb2ef 7646 po->op == OPP_ALLSHL ? "<<" : ">>");
7647 fprintf(fout, " edx = tmp64 >> 32; eax = tmp64;");
7648 strcat(g_comment, po->op == OPP_ALLSHL
7649 ? " allshl" : " allshr");
7650 break;
7651
d4a985bd 7652 // x87
7653 case OP_FLD:
fe18df39 7654 if (need_float_stack) {
7655 out_src_opr_float(buf1, sizeof(buf1),
7656 po, &po->operand[0], 1);
7657 if (po->regmask_src & mxSTa) {
7658 fprintf(fout, " f_st[(f_stp - 1) & 7] = %s; f_stp--;",
7659 buf1);
7660 }
7661 else
7662 fprintf(fout, " f_st[--f_stp & 7] = %s;", buf1);
7663 }
7664 else {
7665 if (po->flags & OPF_FSHIFT)
7666 fprintf(fout, " f_st1 = f_st0;");
7667 if (po->operand[0].type == OPT_REG
7668 && po->operand[0].reg == xST0)
7669 {
7670 strcat(g_comment, " fld st");
7671 break;
7672 }
7673 fprintf(fout, " f_st0 = %s;",
7674 out_src_opr_float(buf1, sizeof(buf1),
7675 po, &po->operand[0], 0));
d4a985bd 7676 }
d4a985bd 7677 strcat(g_comment, " fld");
7678 break;
7679
7680 case OP_FILD:
fe18df39 7681 out_src_opr(buf1, sizeof(buf1), po, &po->operand[0],
7682 lmod_cast(po, po->operand[0].lmod, 1), 0);
7683 snprintf(buf2, sizeof(buf2), "(%s)%s", float_type, buf1);
7684 if (need_float_stack) {
7685 fprintf(fout, " f_st[--f_stp & 7] = %s;", buf2);
7686 }
7687 else {
7688 if (po->flags & OPF_FSHIFT)
7689 fprintf(fout, " f_st1 = f_st0;");
7690 fprintf(fout, " f_st0 = %s;", buf2);
7691 }
d4a985bd 7692 strcat(g_comment, " fild");
7693 break;
7694
7695 case OP_FLDc:
fe18df39 7696 if (need_float_stack)
7697 fprintf(fout, " f_st[--f_stp & 7] = ");
7698 else {
7699 if (po->flags & OPF_FSHIFT)
7700 fprintf(fout, " f_st1 = f_st0;");
7701 fprintf(fout, " f_st0 = ");
7702 }
d4a985bd 7703 switch (po->operand[0].val) {
fe18df39 7704 case X87_CONST_1: fprintf(fout, "1.0;"); break;
b62264bc 7705 case X87_CONST_L2T: fprintf(fout, "3.321928094887362;"); break;
7706 case X87_CONST_L2E: fprintf(fout, "M_LOG2E;"); break;
7707 case X87_CONST_PI: fprintf(fout, "M_PI;"); break;
7708 case X87_CONST_LG2: fprintf(fout, "0.301029995663981;"); break;
7709 case X87_CONST_LN2: fprintf(fout, "M_LN2;"); break;
fe18df39 7710 case X87_CONST_Z: fprintf(fout, "0.0;"); break;
b62264bc 7711 default: ferr_assert(po, 0); break;
d4a985bd 7712 }
7713 break;
7714
7715 case OP_FST:
30620174 7716 if (po->flags & OPF_FARG) {
7717 // store to stack as func arg
7718 snprintf(buf1, sizeof(buf1), "fs_%d", po->p_argnum);
7719 dead_dst = 0;
7720 }
7721 else {
7722 out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0],
7723 need_float_stack);
7724 dead_dst = po->operand[0].type == OPT_REG
7725 && po->operand[0].reg == xST0;
fe18df39 7726 }
30620174 7727 if (!dead_dst)
7728 fprintf(fout, " %s = %s;", buf1, float_st0);
16057ce1 7729 if (po->flags & OPF_FSHIFT) {
7730 if (need_float_stack)
7731 fprintf(fout, " f_stp++;");
7732 else
fe18df39 7733 fprintf(fout, " f_st0 = f_st1;");
d4a985bd 7734 }
fe18df39 7735 if (dead_dst && !(po->flags & OPF_FSHIFT))
7736 no_output = 1;
7737 else
7738 strcat(g_comment, " fst");
d4a985bd 7739 break;
7740
16057ce1 7741 case OP_FIST:
7742 fprintf(fout, " %s = %s%s;",
7743 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
7744 lmod_cast(po, po->operand[0].lmod, 1), float_st0);
7745 if (po->flags & OPF_FSHIFT) {
7746 if (need_float_stack)
7747 fprintf(fout, " f_stp++;");
7748 else
7749 fprintf(fout, " f_st0 = f_st1;");
7750 }
7751 strcat(g_comment, " fist");
7752 break;
7753
d4a985bd 7754 case OP_FADD:
7755 case OP_FDIV:
7756 case OP_FMUL:
7757 case OP_FSUB:
fe18df39 7758 out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0],
7759 need_float_stack);
7760 out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1],
7761 need_float_stack);
7762 dead_dst = (po->flags & OPF_FPOP)
7763 && po->operand[0].type == OPT_REG
7764 && po->operand[0].reg == xST0;
d4a985bd 7765 switch (po->op) {
7766 case OP_FADD: j = '+'; break;
7767 case OP_FDIV: j = '/'; break;
7768 case OP_FMUL: j = '*'; break;
7769 case OP_FSUB: j = '-'; break;
7770 default: j = 'x'; break;
7771 }
fe18df39 7772 if (need_float_stack) {
7773 if (!dead_dst)
7774 fprintf(fout, " %s %c= %s;", buf1, j, buf2);
7775 if (po->flags & OPF_FSHIFT)
7776 fprintf(fout, " f_stp++;");
d4a985bd 7777 }
7778 else {
fe18df39 7779 if (po->flags & OPF_FSHIFT) {
7780 // note: assumes only 2 regs handled
7781 if (!dead_dst)
7782 fprintf(fout, " f_st0 = f_st1 %c f_st0;", j);
7783 else
7784 fprintf(fout, " f_st0 = f_st1;");
7785 }
7786 else if (!dead_dst)
7787 fprintf(fout, " %s %c= %s;", buf1, j, buf2);
d4a985bd 7788 }
fe18df39 7789 no_output = (dead_dst && !(po->flags & OPF_FSHIFT));
d4a985bd 7790 break;
7791
7792 case OP_FDIVR:
7793 case OP_FSUBR:
fe18df39 7794 out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0],
7795 need_float_stack);
7796 out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1],
7797 need_float_stack);
7798 out_src_opr_float(buf3, sizeof(buf3), po, &po->operand[0],
7799 need_float_stack);
7800 dead_dst = (po->flags & OPF_FPOP)
7801 && po->operand[0].type == OPT_REG
7802 && po->operand[0].reg == xST0;
7803 j = po->op == OP_FDIVR ? '/' : '-';
7804 if (need_float_stack) {
7805 if (!dead_dst)
7806 fprintf(fout, " %s = %s %c %s;", buf1, buf2, j, buf3);
7807 if (po->flags & OPF_FSHIFT)
7808 fprintf(fout, " f_stp++;");
7809 }
7810 else {
7811 if (po->flags & OPF_FSHIFT) {
7812 if (!dead_dst)
7813 fprintf(fout, " f_st0 = f_st0 %c f_st1;", j);
7814 else
7815 fprintf(fout, " f_st0 = f_st1;");
7816 }
7817 else if (!dead_dst)
7818 fprintf(fout, " %s = %s %c %s;", buf1, buf2, j, buf3);
7819 }
7820 no_output = (dead_dst && !(po->flags & OPF_FSHIFT));
d4a985bd 7821 break;
7822
7823 case OP_FIADD:
7824 case OP_FIDIV:
7825 case OP_FIMUL:
7826 case OP_FISUB:
7827 switch (po->op) {
7828 case OP_FIADD: j = '+'; break;
7829 case OP_FIDIV: j = '/'; break;
7830 case OP_FIMUL: j = '*'; break;
7831 case OP_FISUB: j = '-'; break;
7832 default: j = 'x'; break;
7833 }
fe18df39 7834 fprintf(fout, " %s %c= (%s)%s;", float_st0,
7835 j, float_type,
d4a985bd 7836 out_src_opr(buf1, sizeof(buf1), po, &po->operand[0],
7837 lmod_cast(po, po->operand[0].lmod, 1), 0));
7838 break;
7839
7840 case OP_FIDIVR:
7841 case OP_FISUBR:
fe18df39 7842 fprintf(fout, " %s = %s %c %s;", float_st0,
7843 out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1],
7844 need_float_stack),
7845 po->op == OP_FIDIVR ? '/' : '-', float_st0);
7846 break;
7847
16057ce1 7848 case OP_FCOM: {
7849 int mask, z_check;
7850 ferr_assert(po, po->datap != NULL);
7851 mask = (long)po->datap & 0xffff;
7852 z_check = ((long)po->datap >> 16) & 1;
7853 out_src_opr_float(buf1, sizeof(buf1), po, &po->operand[0],
7854 need_float_stack);
2c31fb4c 7855 if (mask == 0x0100 || mask == 0x0500) { // C0 -> <
16057ce1 7856 fprintf(fout, " f_sw = %s < %s ? 0x0100 : 0;",
7857 float_st0, buf1);
7858 }
88eed95d 7859 else if (mask == 0x4000 || mask == 0x4400) { // C3 -> =
16057ce1 7860 fprintf(fout, " f_sw = %s == %s ? 0x4000 : 0;",
7861 float_st0, buf1);
7862 }
7863 else if (mask == 0x4100) { // C3, C0
7864 if (z_check) {
7865 fprintf(fout, " f_sw = %s <= %s ? 0x4100 : 0;",
7866 float_st0, buf1);
7867 strcat(g_comment, " z_chk_det");
7868 }
7869 else {
7870 fprintf(fout, " f_sw = %s == %s ? 0x4000 : "
7871 "(%s < %s ? 0x0100 : 0);",
7872 float_st0, buf1, float_st0, buf1);
7873 }
7874 }
7875 else
7876 ferr(po, "unhandled sw mask: %x\n", mask);
7877 if (po->flags & OPF_FSHIFT) {
88eed95d 7878 if (need_float_stack) {
7879 if (po->flags & OPF_FPOPP)
7880 fprintf(fout, " f_stp += 2;");
7881 else
7882 fprintf(fout, " f_stp++;");
7883 }
7884 else {
7885 ferr_assert(po, !(po->flags & OPF_FPOPP));
16057ce1 7886 fprintf(fout, " f_st0 = f_st1;");
88eed95d 7887 }
16057ce1 7888 }
7889 break;
7890 }
7891
7892 case OP_FNSTSW:
7893 fprintf(fout, " %s = f_sw;",
7894 out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
7895 break;
7896
fe18df39 7897 case OP_FCHS:
7898 fprintf(fout, " %s = -%s;", float_st0, float_st0);
d4a985bd 7899 break;
7900
497a6d6b 7901 case OP_FCOS:
fe18df39 7902 fprintf(fout, " %s = cos%s(%s);", float_st0,
7903 need_double ? "" : "f", float_st0);
497a6d6b 7904 break;
7905
7906 case OP_FPATAN:
fe18df39 7907 if (need_float_stack) {
7908 fprintf(fout, " %s = atan%s(%s / %s);", float_st1,
7909 need_double ? "" : "f", float_st1, float_st0);
7910 fprintf(fout, " f_stp++;");
7911 }
7912 else {
7913 fprintf(fout, " f_st0 = atan%s(f_st1 / f_st0);",
7914 need_double ? "" : "f");
7915 }
7916 break;
7917
7918 case OP_FYL2X:
7919 if (need_float_stack) {
7920 fprintf(fout, " %s = %s * log2%s(%s);", float_st1,
7921 float_st1, need_double ? "" : "f", float_st0);
7922 fprintf(fout, " f_stp++;");
7923 }
7924 else {
7925 fprintf(fout, " f_st0 = f_st1 * log2%s(f_st0);",
7926 need_double ? "" : "f");
7927 }
8c83cc48 7928 strcat(g_comment, " fyl2x");
497a6d6b 7929 break;
7930
7931 case OP_FSIN:
fe18df39 7932 fprintf(fout, " %s = sin%s(%s);", float_st0,
7933 need_double ? "" : "f", float_st0);
497a6d6b 7934 break;
7935
7936 case OP_FSQRT:
fe18df39 7937 fprintf(fout, " %s = sqrt%s(%s);", float_st0,
7938 need_double ? "" : "f", float_st0);
7939 break;
7940
7941 case OP_FXCH:
7942 dead_dst = po->operand[0].type == OPT_REG
7943 && po->operand[0].reg == xST0;
7944 if (!dead_dst) {
7945 out_src_opr_float(buf1, sizeof(buf1), po, &po->operand[0],
7946 need_float_stack);
7947 fprintf(fout, " { %s t = %s; %s = %s; %s = t; }", float_type,
7948 float_st0, float_st0, buf1, buf1);
7949 strcat(g_comment, " fxch");
7950 }
7951 else
7952 no_output = 1;
497a6d6b 7953 break;
7954
d4a985bd 7955 case OPP_FTOL:
7956 ferr_assert(po, po->flags & OPF_32BIT);
fe18df39 7957 fprintf(fout, " eax = (s32)%s;", float_st0);
7958 if (po->flags & OPF_FSHIFT) {
7959 if (need_float_stack)
7960 fprintf(fout, " f_stp++;");
7961 else
7962 fprintf(fout, " f_st0 = f_st1;");
7963 }
d4a985bd 7964 strcat(g_comment, " ftol");
7965 break;
7966
8c83cc48 7967 case OPP_CIPOW:
7968 if (need_float_stack) {
7969 fprintf(fout, " %s = pow%s(%s, %s);", float_st1,
7970 need_double ? "" : "f", float_st1, float_st0);
7971 fprintf(fout, " f_stp++;");
7972 }
7973 else {
7974 fprintf(fout, " f_st0 = pow%s(f_st1, f_st0);",
7975 need_double ? "" : "f");
7976 }
7977 strcat(g_comment, " CIpow");
7978 break;
7979
11437ea1 7980 case OPP_ABORT:
7981 fprintf(fout, " do_skip_code_abort();");
7982 break;
7983
90307a99 7984 // mmx
7985 case OP_EMMS:
11437ea1 7986 fprintf(fout, " do_emms();");
90307a99 7987 break;
7988
91977a1c 7989 default:
7990 no_output = 1;
69a3cdfc 7991 ferr(po, "unhandled op type %d, flags %x\n",
7992 po->op, po->flags);
91977a1c 7993 break;
7994 }
7995
7996 if (g_comment[0] != 0) {
ddaf8bd7 7997 char *p = g_comment;
7998 while (my_isblank(*p))
7999 p++;
8000 fprintf(fout, " // %s", p);
91977a1c 8001 g_comment[0] = 0;
8002 no_output = 0;
8003 }
8004 if (!no_output)
8005 fprintf(fout, "\n");
5101a5f9 8006
2b43685d 8007 // some sanity checking
591721d7 8008 if (po->flags & OPF_REP) {
8009 if (po->op != OP_STOS && po->op != OP_MOVS
8010 && po->op != OP_CMPS && po->op != OP_SCAS)
8011 ferr(po, "unexpected rep\n");
8012 if (!(po->flags & (OPF_REPZ|OPF_REPNZ))
8013 && (po->op == OP_CMPS || po->op == OP_SCAS))
8014 ferr(po, "cmps/scas with plain rep\n");
8015 }
8016 if ((po->flags & (OPF_REPZ|OPF_REPNZ))
8017 && po->op != OP_CMPS && po->op != OP_SCAS)
2b43685d 8018 ferr(po, "unexpected repz/repnz\n");
8019
940e8e66 8020 if (pfomask != 0)
7ba45c34 8021 ferr(po, "missed flag calc, pfomask=%x\n", pfomask);
940e8e66 8022
5101a5f9 8023 // see is delayed flag stuff is still valid
8024 if (delayed_flag_op != NULL && delayed_flag_op != po) {
89ff3147 8025 if (is_any_opr_modified(delayed_flag_op, po, 0))
5101a5f9 8026 delayed_flag_op = NULL;
8027 }
8028
8029 if (last_arith_dst != NULL && last_arith_dst != &po->operand[0]) {
8030 if (is_opr_modified(last_arith_dst, po))
8031 last_arith_dst = NULL;
8032 }
3ebea2cf 8033
2c31fb4c 8034 if (!no_output)
8035 label_pending = 0;
91977a1c 8036 }
8037
a2c1d768 8038 if (g_stack_fsz && !g_stack_frame_used)
8039 fprintf(fout, " (void)sf;\n");
8040
91977a1c 8041 fprintf(fout, "}\n\n");
8042
9af2d373 8043 gen_x_cleanup(opcnt);
8044}
8045
8046static void gen_x_cleanup(int opcnt)
8047{
8048 int i;
8049
91977a1c 8050 for (i = 0; i < opcnt; i++) {
4c45fa73 8051 struct label_ref *lr, *lr_del;
8052
8053 lr = g_label_refs[i].next;
8054 while (lr != NULL) {
8055 lr_del = lr;
8056 lr = lr->next;
8057 free(lr_del);
8058 }
8059 g_label_refs[i].i = -1;
8060 g_label_refs[i].next = NULL;
8061
91977a1c 8062 if (ops[i].op == OP_CALL) {
092f64e1 8063 if (ops[i].pp)
8064 proto_release(ops[i].pp);
91977a1c 8065 }
8066 }
bd96f656 8067 g_func_pp = NULL;
91977a1c 8068}
c36e914d 8069
92d715b6 8070struct func_proto_dep;
8071
8072struct func_prototype {
8073 char name[NAMELEN];
8074 int id;
8075 int argc_stack;
8076 int regmask_dep;
91ca764a 8077 int has_ret:3; // -1, 0, 1: unresolved, no, yes
92d715b6 8078 unsigned int dep_resolved:1;
8079 unsigned int is_stdcall:1;
8080 struct func_proto_dep *dep_func;
8081 int dep_func_cnt;
91ca764a 8082 const struct parsed_proto *pp; // seed pp, if any
92d715b6 8083};
8084
8085struct func_proto_dep {
8086 char *name;
8087 struct func_prototype *proto;
8088 int regmask_live; // .. at the time of call
8089 unsigned int ret_dep:1; // return from this is caller's return
8090};
8091
8092static struct func_prototype *hg_fp;
8093static int hg_fp_cnt;
8094
5fa1256f 8095static struct scanned_var {
8096 char name[NAMELEN];
8097 enum opr_lenmod lmod;
8098 unsigned int is_seeded:1;
61e29183 8099 unsigned int is_c_str:1;
c0de9015 8100 const struct parsed_proto *pp; // seed pp, if any
5fa1256f 8101} *hg_vars;
8102static int hg_var_cnt;
8103
8c999988 8104static char **hg_refs;
8105static int hg_ref_cnt;
8106
9af2d373 8107static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
8108 int count);
8109
8c999988 8110static struct func_prototype *hg_fp_add(const char *funcn)
ebc4dc43 8111{
8112 struct func_prototype *fp;
8113
8114 if ((hg_fp_cnt & 0xff) == 0) {
8115 hg_fp = realloc(hg_fp, sizeof(hg_fp[0]) * (hg_fp_cnt + 0x100));
8116 my_assert_not(hg_fp, NULL);
8117 memset(hg_fp + hg_fp_cnt, 0, sizeof(hg_fp[0]) * 0x100);
8118 }
8119
8120 fp = &hg_fp[hg_fp_cnt];
8121 snprintf(fp->name, sizeof(fp->name), "%s", funcn);
8122 fp->id = hg_fp_cnt;
8123 fp->argc_stack = -1;
8124 hg_fp_cnt++;
8125
8126 return fp;
8127}
8128
92d715b6 8129static struct func_proto_dep *hg_fp_find_dep(struct func_prototype *fp,
8130 const char *name)
8131{
8132 int i;
8133
8134 for (i = 0; i < fp->dep_func_cnt; i++)
8135 if (IS(fp->dep_func[i].name, name))
8136 return &fp->dep_func[i];
8137
8138 return NULL;
8139}
8140
8141static void hg_fp_add_dep(struct func_prototype *fp, const char *name)
8142{
8143 // is it a dupe?
8144 if (hg_fp_find_dep(fp, name))
8145 return;
8146
8147 if ((fp->dep_func_cnt & 0xff) == 0) {
8148 fp->dep_func = realloc(fp->dep_func,
8149 sizeof(fp->dep_func[0]) * (fp->dep_func_cnt + 0x100));
8150 my_assert_not(fp->dep_func, NULL);
8151 memset(&fp->dep_func[fp->dep_func_cnt], 0,
8152 sizeof(fp->dep_func[0]) * 0x100);
8153 }
8154 fp->dep_func[fp->dep_func_cnt].name = strdup(name);
8155 fp->dep_func_cnt++;
8156}
8157
8158static int hg_fp_cmp_name(const void *p1_, const void *p2_)
8159{
8160 const struct func_prototype *p1 = p1_, *p2 = p2_;
8161 return strcmp(p1->name, p2->name);
8162}
8163
8164#if 0
8165static int hg_fp_cmp_id(const void *p1_, const void *p2_)
8166{
8167 const struct func_prototype *p1 = p1_, *p2 = p2_;
8168 return p1->id - p2->id;
8169}
8170#endif
8171
8c999988 8172static void hg_ref_add(const char *name)
8173{
8174 if ((hg_ref_cnt & 0xff) == 0) {
8175 hg_refs = realloc(hg_refs, sizeof(hg_refs[0]) * (hg_ref_cnt + 0x100));
8176 my_assert_not(hg_refs, NULL);
8177 memset(hg_refs + hg_ref_cnt, 0, sizeof(hg_refs[0]) * 0x100);
8178 }
8179
8180 hg_refs[hg_ref_cnt] = strdup(name);
8181 my_assert_not(hg_refs[hg_ref_cnt], NULL);
8182 hg_ref_cnt++;
8183}
8184
91ca764a 8185// recursive register dep pass
8186// - track saved regs (part 2)
8187// - try to figure out arg-regs
8188// - calculate reg deps
8189static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits,
8190 struct func_prototype *fp, int regmask_save, int regmask_dst,
8191 int *regmask_dep, int *has_ret)
8192{
8193 struct func_proto_dep *dep;
8194 struct parsed_op *po;
8195 int from_caller = 0;
91ca764a 8196 int j, l;
8197 int reg;
8198 int ret;
8199
8200 for (; i < opcnt; i++)
8201 {
8202 if (cbits[i >> 3] & (1 << (i & 7)))
8203 return;
8204 cbits[i >> 3] |= (1 << (i & 7));
8205
8206 po = &ops[i];
8207
8208 if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
04abc5d6 8209 if (po->flags & OPF_RMD)
8210 continue;
8211
91ca764a 8212 if (po->btj != NULL) {
8213 // jumptable
8214 for (j = 0; j < po->btj->count; j++) {
db63af51 8215 check_i(po, po->btj->d[j].bt_i);
91ca764a 8216 gen_hdr_dep_pass(po->btj->d[j].bt_i, opcnt, cbits, fp,
8217 regmask_save, regmask_dst, regmask_dep, has_ret);
8218 }
8219 return;
8220 }
8221
db63af51 8222 check_i(po, po->bt_i);
91ca764a 8223 if (po->flags & OPF_CJMP) {
8224 gen_hdr_dep_pass(po->bt_i, opcnt, cbits, fp,
8225 regmask_save, regmask_dst, regmask_dep, has_ret);
8226 }
8227 else {
8228 i = po->bt_i - 1;
8229 }
8230 continue;
8231 }
8232
8233 if (po->flags & OPF_FARG)
8234 /* (just calculate register deps) */;
8235 else if (po->op == OP_PUSH && po->operand[0].type == OPT_REG)
8236 {
8237 reg = po->operand[0].reg;
b2bd20c0 8238 ferr_assert(po, reg >= 0);
91ca764a 8239
8240 if (po->flags & OPF_RSAVE) {
8241 regmask_save |= 1 << reg;
8242 continue;
8243 }
8244 if (po->flags & OPF_DONE)
8245 continue;
8246
93b5bd18 8247 ret = scan_for_pop(i + 1, opcnt, i + opcnt * 2, reg, 0, 0, 0);
91ca764a 8248 if (ret == 1) {
8249 regmask_save |= 1 << reg;
8250 po->flags |= OPF_RMD;
93b5bd18 8251 scan_for_pop(i + 1, opcnt, i + opcnt * 3, reg, 0, 0, OPF_RMD);
91ca764a 8252 continue;
8253 }
8254 }
8255 else if (po->flags & OPF_RMD)
8256 continue;
8257 else if (po->op == OP_CALL) {
8258 po->regmask_dst |= 1 << xAX;
8259
8260 dep = hg_fp_find_dep(fp, po->operand[0].name);
e627c4d0 8261 if (dep != NULL) {
91ca764a 8262 dep->regmask_live = regmask_save | regmask_dst;
e627c4d0 8263 if (g_bp_frame && !(po->flags & OPF_EBP_S))
8264 dep->regmask_live |= 1 << xBP;
8265 }
91ca764a 8266 }
8267 else if (po->op == OP_RET) {
8268 if (po->operand_cnt > 0) {
8269 fp->is_stdcall = 1;
8270 if (fp->argc_stack >= 0
8271 && fp->argc_stack != po->operand[0].val / 4)
8272 ferr(po, "ret mismatch? (%d)\n", fp->argc_stack * 4);
8273 fp->argc_stack = po->operand[0].val / 4;
8274 }
8275 }
8276
ebc4dc43 8277 // if has_ret is 0, there is uninitialized eax path,
8278 // which means it's most likely void func
91ca764a 8279 if (*has_ret != 0 && (po->flags & OPF_TAIL)) {
8280 if (po->op == OP_CALL) {
8281 j = i;
8282 ret = 1;
8283 }
8284 else {
b2bd20c0 8285 struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xAX);
91ca764a 8286 j = -1;
8287 from_caller = 0;
8288 ret = resolve_origin(i, &opr, i + opcnt * 4, &j, &from_caller);
8289 }
8290
ebc4dc43 8291 if (ret != 1 && from_caller) {
91ca764a 8292 // unresolved eax - probably void func
8293 *has_ret = 0;
8294 }
8295 else {
ebc4dc43 8296 if (j >= 0 && ops[j].op == OP_CALL) {
8297 dep = hg_fp_find_dep(fp, ops[j].operand[0].name);
91ca764a 8298 if (dep != NULL)
8299 dep->ret_dep = 1;
8300 else
8301 *has_ret = 1;
8302 }
8303 else
8304 *has_ret = 1;
8305 }
8306 }
8307
8308 l = regmask_save | regmask_dst;
8309 if (g_bp_frame && !(po->flags & OPF_EBP_S))
8310 l |= 1 << xBP;
8311
8312 l = po->regmask_src & ~l;
8313#if 0
8314 if (l)
8315 fnote(po, "dep |= %04x, dst %04x, save %04x (f %x)\n",
8316 l, regmask_dst, regmask_save, po->flags);
8317#endif
8318 *regmask_dep |= l;
8319 regmask_dst |= po->regmask_dst;
8320
8321 if (po->flags & OPF_TAIL)
8322 return;
8323 }
8324}
8325
92d715b6 8326static void gen_hdr(const char *funcn, int opcnt)
8327{
91ca764a 8328 unsigned char cbits[MAX_OPS / 8];
ebc4dc43 8329 const struct parsed_proto *pp_c;
9af2d373 8330 struct parsed_proto *pp;
92d715b6 8331 struct func_prototype *fp;
92d715b6 8332 struct parsed_op *po;
26677139 8333 int regmask_dummy = 0;
91ca764a 8334 int regmask_dep;
92d715b6 8335 int max_bp_offset = 0;
91ca764a 8336 int has_ret;
bfacdc83 8337 int i, j, l;
8338 int ret;
92d715b6 8339
ebc4dc43 8340 pp_c = proto_parse(g_fhdr, funcn, 1);
8341 if (pp_c != NULL)
8342 // already in seed, will add to hg_fp later
9af2d373 8343 return;
ebc4dc43 8344
8345 fp = hg_fp_add(funcn);
9af2d373 8346
8347 g_bp_frame = g_sp_frame = g_stack_fsz = 0;
8348 g_stack_frame_used = 0;
ba93cc12 8349 g_seh_size = 0;
9af2d373 8350
92d715b6 8351 // pass1:
66bdb2b0 8352 // - resolve all branches
8353 // - parse calls with labels
8354 resolve_branches_parse_calls(opcnt);
8355
8356 // pass2:
9af2d373 8357 // - handle ebp/esp frame, remove ops related to it
f9327ad4 8358 scan_prologue_epilogue(opcnt, NULL);
9af2d373 8359
66bdb2b0 8360 // pass3:
8361 // - remove dead labels
92d715b6 8362 // - collect calls
92d715b6 8363 for (i = 0; i < opcnt; i++)
8364 {
66bdb2b0 8365 if (g_labels[i] != NULL && g_label_refs[i].i == -1) {
8366 free(g_labels[i]);
8367 g_labels[i] = NULL;
8368 }
92d715b6 8369
66bdb2b0 8370 po = &ops[i];
5e49b270 8371 if (po->flags & (OPF_RMD|OPF_DONE))
92d715b6 8372 continue;
8373
8374 if (po->op == OP_CALL) {
66bdb2b0 8375 if (po->operand[0].type == OPT_LABEL)
8376 hg_fp_add_dep(fp, opr_name(po, 0));
8377 else if (po->pp != NULL)
8378 hg_fp_add_dep(fp, po->pp->name);
92d715b6 8379 }
92d715b6 8380 }
8381
66bdb2b0 8382 // pass4:
92d715b6 8383 // - remove dead labels
9af2d373 8384 // - handle push <const>/pop pairs
92d715b6 8385 for (i = 0; i < opcnt; i++)
8386 {
d7857c3a 8387 if (g_labels[i] != NULL && g_label_refs[i].i == -1) {
8388 free(g_labels[i]);
8389 g_labels[i] = NULL;
8390 }
9af2d373 8391
91ca764a 8392 po = &ops[i];
8393 if (po->flags & (OPF_RMD|OPF_DONE))
8394 continue;
8395
8396 if (po->op == OP_PUSH && po->operand[0].type == OPT_CONST)
e83ea7ed 8397 scan_for_pop_const(i, opcnt, i + opcnt * 13);
91ca764a 8398 }
8399
66bdb2b0 8400 // pass5:
91ca764a 8401 // - process trivial calls
8402 for (i = 0; i < opcnt; i++)
8403 {
9af2d373 8404 po = &ops[i];
5e49b270 8405 if (po->flags & (OPF_RMD|OPF_DONE))
9af2d373 8406 continue;
8407
26677139 8408 if (po->op == OP_CALL)
8409 {
8410 pp = process_call_early(i, opcnt, &j);
8411 if (pp != NULL) {
8412 if (!(po->flags & OPF_ATAIL))
8413 // since we know the args, try to collect them
30620174 8414 if (collect_call_args_early(i, pp, NULL, NULL) != 0)
26677139 8415 pp = NULL;
8416 }
8417
8418 if (pp != NULL) {
8419 if (j >= 0) {
8420 // commit esp adjust
5e49b270 8421 if (ops[j].op != OP_POP)
8422 patch_esp_adjust(&ops[j], pp->argc_stack * 4);
bfacdc83 8423 else {
8424 for (l = 0; l < pp->argc_stack; l++)
b2bd20c0 8425 ops[j + l].flags |= OPF_DONE | OPF_RMD | OPF_NOREGS;
bfacdc83 8426 }
26677139 8427 }
8428
8429 po->flags |= OPF_DONE;
8430 }
8431 }
26677139 8432 }
8433
66bdb2b0 8434 // pass6:
5e49b270 8435 // - track saved regs (simple)
26677139 8436 // - process calls
8437 for (i = 0; i < opcnt; i++)
8438 {
8439 po = &ops[i];
5e49b270 8440 if (po->flags & (OPF_RMD|OPF_DONE))
26677139 8441 continue;
8442
e83ea7ed 8443 if (po->op == OP_PUSH && po->operand[0].type == OPT_REG
8444 && po->operand[0].reg != xCX)
5e49b270 8445 {
e83ea7ed 8446 ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, 0);
b2bd20c0 8447 if (ret == 1) {
91ca764a 8448 // regmask_save |= 1 << po->operand[0].reg; // do it later
8449 po->flags |= OPF_RSAVE | OPF_RMD | OPF_DONE;
e83ea7ed 8450 scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, OPF_RMD);
5e49b270 8451 }
8452 }
e83ea7ed 8453 else if (po->op == OP_CALL)
26677139 8454 {
9af2d373 8455 pp = process_call(i, opcnt);
8456
8457 if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) {
9af2d373 8458 // since we know the args, collect them
8c83cc48 8459 ret = collect_call_args(po, i, pp, &regmask_dummy,
8460 i + opcnt * 1);
9af2d373 8461 }
8462 }
92d715b6 8463 }
8464
66bdb2b0 8465 // pass7
91ca764a 8466 memset(cbits, 0, sizeof(cbits));
8467 regmask_dep = 0;
8468 has_ret = -1;
8469
8470 gen_hdr_dep_pass(0, opcnt, cbits, fp, 0, 0, &regmask_dep, &has_ret);
8471
8472 // find unreachable code - must be fixed in IDA
92d715b6 8473 for (i = 0; i < opcnt; i++)
8474 {
91ca764a 8475 if (cbits[i >> 3] & (1 << (i & 7)))
9af2d373 8476 continue;
92d715b6 8477
04abc5d6 8478 if (g_labels[i] == NULL && i > 0 && ops[i - 1].op == OP_CALL
8479 && ops[i - 1].pp != NULL && ops[i - 1].pp->is_osinc)
8480 {
8481 // the compiler sometimes still generates code after
8482 // noreturn OS functions
8483 break;
8484 }
0a2c6da1 8485 if (!(ops[i].flags & OPF_RMD)
8486 && ops[i].op != OP_NOP && ops[i].op != OPP_ABORT)
8487 {
91ca764a 8488 ferr(&ops[i], "unreachable code\n");
0a2c6da1 8489 }
92d715b6 8490 }
8491
9af2d373 8492 for (i = 0; i < g_eqcnt; i++) {
92d715b6 8493 if (g_eqs[i].offset > max_bp_offset && g_eqs[i].offset < 4*32)
8494 max_bp_offset = g_eqs[i].offset;
9af2d373 8495 }
92d715b6 8496
9af2d373 8497 if (fp->argc_stack < 0) {
92d715b6 8498 max_bp_offset = (max_bp_offset + 3) & ~3;
9af2d373 8499 fp->argc_stack = max_bp_offset / 4;
8500 if ((g_ida_func_attr & IDAFA_BP_FRAME) && fp->argc_stack > 0)
92d715b6 8501 fp->argc_stack--;
8502 }
8503
622eb2ef 8504 fp->regmask_dep = regmask_dep & ~((1 << xSP) | mxSTa);
92d715b6 8505 fp->has_ret = has_ret;
91ca764a 8506#if 0
8507 printf("// has_ret %d, regmask_dep %x\n",
8508 fp->has_ret, fp->regmask_dep);
8509 output_hdr_fp(stdout, fp, 1);
ebc4dc43 8510 if (IS(funcn, "sub_10007F72")) exit(1);
91ca764a 8511#endif
9af2d373 8512
8513 gen_x_cleanup(opcnt);
92d715b6 8514}
8515
8516static void hg_fp_resolve_deps(struct func_prototype *fp)
8517{
8518 struct func_prototype fp_s;
91ca764a 8519 int dep;
92d715b6 8520 int i;
8521
8522 // this thing is recursive, so mark first..
8523 fp->dep_resolved = 1;
8524
8525 for (i = 0; i < fp->dep_func_cnt; i++) {
8526 strcpy(fp_s.name, fp->dep_func[i].name);
8527 fp->dep_func[i].proto = bsearch(&fp_s, hg_fp, hg_fp_cnt,
8528 sizeof(hg_fp[0]), hg_fp_cmp_name);
8529 if (fp->dep_func[i].proto != NULL) {
8530 if (!fp->dep_func[i].proto->dep_resolved)
8531 hg_fp_resolve_deps(fp->dep_func[i].proto);
8532
91ca764a 8533 dep = ~fp->dep_func[i].regmask_live
8534 & fp->dep_func[i].proto->regmask_dep;
8535 fp->regmask_dep |= dep;
8536 // printf("dep %s %s |= %x\n", fp->name,
8537 // fp->dep_func[i].name, dep);
92d715b6 8538
ebc4dc43 8539 if (fp->has_ret == -1 && fp->dep_func[i].ret_dep)
92d715b6 8540 fp->has_ret = fp->dep_func[i].proto->has_ret;
8541 }
8542 }
8543}
8544
8c999988 8545// make all thiscall/edx arg functions referenced from .data fastcall
8546static void do_func_refs_from_data(void)
8547{
8548 struct func_prototype *fp, fp_s;
8549 int i;
8550
8551 for (i = 0; i < hg_ref_cnt; i++) {
8552 strcpy(fp_s.name, hg_refs[i]);
8553 fp = bsearch(&fp_s, hg_fp, hg_fp_cnt,
8554 sizeof(hg_fp[0]), hg_fp_cmp_name);
8555 if (fp == NULL)
8556 continue;
8557
8558 if (fp->argc_stack != 0 && (fp->regmask_dep & (mxCX | mxDX)))
8559 fp->regmask_dep |= mxCX | mxDX;
8560 }
8561}
8562
9af2d373 8563static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
8564 int count)
92d715b6 8565{
61e29183 8566 const struct parsed_proto *pp;
8567 char *p, namebuf[NAMELEN];
8568 const char *name;
92d715b6 8569 int regmask_dep;
226e8df1 8570 int argc_normal;
9af2d373 8571 int j, arg;
92d715b6 8572
9af2d373 8573 for (; count > 0; count--, fp++) {
92d715b6 8574 if (fp->has_ret == -1)
8575 fprintf(fout, "// ret unresolved\n");
8576#if 0
8577 fprintf(fout, "// dep:");
8578 for (j = 0; j < fp->dep_func_cnt; j++) {
8579 fprintf(fout, " %s/", fp->dep_func[j].name);
8580 if (fp->dep_func[j].proto != NULL)
8581 fprintf(fout, "%04x/%d", fp->dep_func[j].proto->regmask_dep,
8582 fp->dep_func[j].proto->has_ret);
8583 }
8584 fprintf(fout, "\n");
8585#endif
8586
61e29183 8587 p = strchr(fp->name, '@');
8588 if (p != NULL) {
8589 memcpy(namebuf, fp->name, p - fp->name);
8590 namebuf[p - fp->name] = 0;
8591 name = namebuf;
8592 }
8593 else
8594 name = fp->name;
8595 if (name[0] == '_')
8596 name++;
8597
8598 pp = proto_parse(g_fhdr, name, 1);
8599 if (pp != NULL && pp->is_include)
8600 continue;
8601
c0de9015 8602 if (fp->pp != NULL) {
4e81a3a2 8603 // part of seed, output later
8604 continue;
c0de9015 8605 }
8606
92d715b6 8607 regmask_dep = fp->regmask_dep;
226e8df1 8608 argc_normal = fp->argc_stack;
92d715b6 8609
91ca764a 8610 fprintf(fout, "%-5s", fp->pp ? fp->pp->ret_type.name :
8611 (fp->has_ret ? "int" : "void"));
226e8df1 8612 if (regmask_dep && (fp->is_stdcall || fp->argc_stack > 0)
8613 && (regmask_dep & ~mxCX) == 0)
8614 {
8615 fprintf(fout, "/*__thiscall*/ ");
8616 argc_normal++;
8617 regmask_dep = 0;
8618 }
8619 else if (regmask_dep && (fp->is_stdcall || fp->argc_stack == 0)
8620 && (regmask_dep & ~(mxCX | mxDX)) == 0)
92d715b6 8621 {
9af2d373 8622 fprintf(fout, " __fastcall ");
226e8df1 8623 if (!(regmask_dep & (1 << xDX)) && fp->argc_stack == 0)
8624 argc_normal = 1;
92d715b6 8625 else
226e8df1 8626 argc_normal += 2;
92d715b6 8627 regmask_dep = 0;
8628 }
8629 else if (regmask_dep && !fp->is_stdcall) {
8630 fprintf(fout, "/*__usercall*/ ");
92d715b6 8631 }
8632 else if (regmask_dep) {
8633 fprintf(fout, "/*__userpurge*/ ");
92d715b6 8634 }
8635 else if (fp->is_stdcall)
9af2d373 8636 fprintf(fout, " __stdcall ");
92d715b6 8637 else
9af2d373 8638 fprintf(fout, " __cdecl ");
92d715b6 8639
61e29183 8640 fprintf(fout, "%s(", name);
92d715b6 8641
8642 arg = 0;
8643 for (j = 0; j < xSP; j++) {
8644 if (regmask_dep & (1 << j)) {
8645 arg++;
8646 if (arg != 1)
8647 fprintf(fout, ", ");
91ca764a 8648 if (fp->pp != NULL)
8649 fprintf(fout, "%s", fp->pp->arg[arg - 1].type.name);
8650 else
8651 fprintf(fout, "int");
8652 fprintf(fout, " a%d/*<%s>*/", arg, regs_r32[j]);
92d715b6 8653 }
8654 }
8655
226e8df1 8656 for (j = 0; j < argc_normal; j++) {
92d715b6 8657 arg++;
8658 if (arg != 1)
8659 fprintf(fout, ", ");
91ca764a 8660 if (fp->pp != NULL) {
8661 fprintf(fout, "%s", fp->pp->arg[arg - 1].type.name);
8662 if (!fp->pp->arg[arg - 1].type.is_ptr)
8663 fprintf(fout, " ");
8664 }
8665 else
8666 fprintf(fout, "int ");
8667 fprintf(fout, "a%d", arg);
92d715b6 8668 }
8669
8670 fprintf(fout, ");\n");
8671 }
8672}
8673
9af2d373 8674static void output_hdr(FILE *fout)
8675{
5fa1256f 8676 static const char *lmod_c_names[] = {
8677 [OPLM_UNSPEC] = "???",
8678 [OPLM_BYTE] = "uint8_t",
8679 [OPLM_WORD] = "uint16_t",
8680 [OPLM_DWORD] = "uint32_t",
8681 [OPLM_QWORD] = "uint64_t",
8682 };
8683 const struct scanned_var *var;
ebc4dc43 8684 struct func_prototype *fp;
c0de9015 8685 char line[256] = { 0, };
ebc4dc43 8686 char name[256];
9af2d373 8687 int i;
8688
ebc4dc43 8689 // add stuff from headers
8690 for (i = 0; i < pp_cache_size; i++) {
8691 if (pp_cache[i].is_cinc && !pp_cache[i].is_stdcall)
8692 snprintf(name, sizeof(name), "_%s", pp_cache[i].name);
8693 else
8694 snprintf(name, sizeof(name), "%s", pp_cache[i].name);
8695 fp = hg_fp_add(name);
8696 fp->pp = &pp_cache[i];
8697 fp->argc_stack = fp->pp->argc_stack;
8698 fp->is_stdcall = fp->pp->is_stdcall;
b2bd20c0 8699 fp->regmask_dep = get_pp_arg_regmask_src(fp->pp);
ebc4dc43 8700 fp->has_ret = !IS(fp->pp->ret_type.name, "void");
8701 }
8702
9af2d373 8703 // resolve deps
8704 qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_name);
8705 for (i = 0; i < hg_fp_cnt; i++)
8706 hg_fp_resolve_deps(&hg_fp[i]);
8707
8c999988 8708 // adjust functions referenced from data segment
8709 do_func_refs_from_data();
8710
9af2d373 8711 // note: messes up .proto ptr, don't use
8712 //qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_id);
8713
5fa1256f 8714 // output variables
8715 for (i = 0; i < hg_var_cnt; i++) {
8716 var = &hg_vars[i];
8717
4e81a3a2 8718 if (var->pp != NULL)
8719 // part of seed
8720 continue;
c0de9015 8721 else if (var->is_c_str)
61e29183 8722 fprintf(fout, "extern %-8s %s[];", "char", var->name);
8723 else
8724 fprintf(fout, "extern %-8s %s;",
8725 lmod_c_names[var->lmod], var->name);
5fa1256f 8726
8727 if (var->is_seeded)
8728 fprintf(fout, " // seeded");
8729 fprintf(fout, "\n");
8730 }
8731
8732 fprintf(fout, "\n");
8733
8734 // output function prototypes
9af2d373 8735 output_hdr_fp(fout, hg_fp, hg_fp_cnt);
c0de9015 8736
4e81a3a2 8737 // seed passthrough
8738 fprintf(fout, "\n// - seed -\n");
c0de9015 8739
8740 rewind(g_fhdr);
4e81a3a2 8741 while (fgets(line, sizeof(line), g_fhdr))
8742 fwrite(line, 1, strlen(line), fout);
9af2d373 8743}
8744
61e29183 8745// '=' needs special treatment
8746// also ' quote
bfa4a6ee 8747static char *next_word_s(char *w, size_t wsize, char *s)
8748{
61e29183 8749 size_t i;
bfa4a6ee 8750
61e29183 8751 s = sskip(s);
bfa4a6ee 8752
61e29183 8753 i = 0;
8c999988 8754 if (*s == '\'' && s[1] != '\r' && s[1] != '\n') {
61e29183 8755 w[0] = s[0];
8756 for (i = 1; i < wsize - 1; i++) {
8757 if (s[i] == 0) {
8758 printf("warning: missing closing quote: \"%s\"\n", s);
8759 break;
8760 }
8761 if (s[i] == '\'')
8762 break;
8763 w[i] = s[i];
8764 }
8765 }
bfa4a6ee 8766
61e29183 8767 for (; i < wsize - 1; i++) {
8768 if (s[i] == 0 || my_isblank(s[i]) || (s[i] == '=' && i > 0))
8769 break;
8770 w[i] = s[i];
8771 }
8772 w[i] = 0;
8773
8774 if (s[i] != 0 && !my_isblank(s[i]) && s[i] != '=')
8775 printf("warning: '%s' truncated\n", w);
bfa4a6ee 8776
61e29183 8777 return s + i;
bfa4a6ee 8778}
8779
4c20744d 8780static int cmpstringp(const void *p1, const void *p2)
8781{
8782 return strcmp(*(char * const *)p1, *(char * const *)p2);
8783}
8784
8785static int is_xref_needed(char *p, char **rlist, int rlist_len)
8786{
8787 char *p2;
8788
8789 p = sskip(p);
8790 if (strstr(p, "..."))
8791 // unable to determine, assume needed
8792 return 1;
8793
8794 if (*p == '.') // .text, .data, ...
8795 // ref from other data or non-function -> no
8796 return 0;
8797
8798 p2 = strpbrk(p, "+:\r\n\x18");
8799 if (p2 != NULL)
8800 *p2 = 0;
8801 if (bsearch(&p, rlist, rlist_len, sizeof(rlist[0]), cmpstringp))
8802 // referenced from removed code
8803 return 0;
8804
8805 return 1;
8806}
8807
8c999988 8808static int ida_xrefs_show_need(FILE *fasm, char *p,
4c20744d 8809 char **rlist, int rlist_len)
8810{
8811 int found_need = 0;
8812 char line[256];
8813 long pos;
8814
8815 p = strrchr(p, ';');
ea43585b 8816 if (p != NULL && *p == ';') {
8817 if (IS_START(p + 2, "sctref"))
4c20744d 8818 return 1;
ea43585b 8819 if (IS_START(p + 2, "DATA XREF: ")) {
8820 p += 13;
8821 if (is_xref_needed(p, rlist, rlist_len))
8822 return 1;
8823 }
4c20744d 8824 }
8825
8826 pos = ftell(fasm);
8827 while (1)
8828 {
8829 if (!my_fgets(line, sizeof(line), fasm))
8830 break;
8831 // non-first line is always indented
8832 if (!my_isblank(line[0]))
8833 break;
8834
8835 // should be no content, just comment
8836 p = sskip(line);
8837 if (*p != ';')
8838 break;
8839
8840 p = strrchr(p, ';');
8841 p += 2;
ea43585b 8842
8843 if (IS_START(p, "sctref")) {
8844 found_need = 1;
8845 break;
8846 }
8847
4c20744d 8848 // it's printed once, but no harm to check again
8849 if (IS_START(p, "DATA XREF: "))
8850 p += 11;
8851
8852 if (is_xref_needed(p, rlist, rlist_len)) {
8853 found_need = 1;
8854 break;
8855 }
8856 }
8857 fseek(fasm, pos, SEEK_SET);
8858 return found_need;
8859}
8860
8861static void scan_variables(FILE *fasm, char **rlist, int rlist_len)
5fa1256f 8862{
5fa1256f 8863 struct scanned_var *var;
8864 char line[256] = { 0, };
8c999988 8865 char words[4][256];
8866 int no_identifier;
5fa1256f 8867 char *p = NULL;
8868 int wordc;
61e29183 8869 int l;
5fa1256f 8870
8871 while (!feof(fasm))
8872 {
8873 // skip to next data section
8874 while (my_fgets(line, sizeof(line), fasm))
8875 {
8876 asmln++;
8877
8878 p = sskip(line);
8879 if (*p == 0 || *p == ';')
8880 continue;
8881
8882 p = sskip(next_word_s(words[0], sizeof(words[0]), p));
8883 if (*p == 0 || *p == ';')
8884 continue;
8885
8886 if (*p != 's' || !IS_START(p, "segment para public"))
8887 continue;
8888
8889 break;
8890 }
8891
8892 if (p == NULL || !IS_START(p, "segment para public"))
8893 break;
8894 p = sskip(p + 19);
8895
8896 if (!IS_START(p, "'DATA'"))
8897 continue;
8898
8899 // now process it
8900 while (my_fgets(line, sizeof(line), fasm))
8901 {
8902 asmln++;
8903
8904 p = line;
8c999988 8905 no_identifier = my_isblank(*p);
5fa1256f 8906
8907 p = sskip(p);
8908 if (*p == 0 || *p == ';')
8909 continue;
8910
8911 for (wordc = 0; wordc < ARRAY_SIZE(words); wordc++) {
8912 words[wordc][0] = 0;
8913 p = sskip(next_word_s(words[wordc], sizeof(words[0]), p));
8914 if (*p == 0 || *p == ';') {
8915 wordc++;
8916 break;
8917 }
8918 }
8919
8920 if (wordc == 2 && IS(words[1], "ends"))
8921 break;
61e29183 8922 if (wordc < 2)
8923 continue;
5fa1256f 8924
8c999988 8925 if (no_identifier) {
8926 if (wordc >= 3 && IS(words[0], "dd") && IS(words[1], "offset"))
8927 hg_ref_add(words[2]);
8928 continue;
8929 }
8930
9ea60b8d 8931 if (IS_START(words[0], "__IMPORT_DESCRIPTOR_")) {
8932 // when this starts, we don't need anything from this section
8933 break;
8934 }
8935
4c20744d 8936 // check refs comment(s)
8c999988 8937 if (!ida_xrefs_show_need(fasm, p, rlist, rlist_len))
4c20744d 8938 continue;
8939
5fa1256f 8940 if ((hg_var_cnt & 0xff) == 0) {
8941 hg_vars = realloc(hg_vars, sizeof(hg_vars[0])
8942 * (hg_var_cnt + 0x100));
8943 my_assert_not(hg_vars, NULL);
8944 memset(hg_vars + hg_var_cnt, 0, sizeof(hg_vars[0]) * 0x100);
8945 }
8946
8947 var = &hg_vars[hg_var_cnt++];
8948 snprintf(var->name, sizeof(var->name), "%s", words[0]);
8949
8950 // maybe already in seed header?
c0de9015 8951 var->pp = proto_parse(g_fhdr, var->name, 1);
8952 if (var->pp != NULL) {
8953 if (var->pp->is_fptr) {
5fa1256f 8954 var->lmod = OPLM_DWORD;
8955 //var->is_ptr = 1;
8956 }
c0de9015 8957 else if (var->pp->is_func)
8958 aerr("func?\n");
8959 else if (!guess_lmod_from_c_type(&var->lmod, &var->pp->type))
5fa1256f 8960 aerr("unhandled C type '%s' for '%s'\n",
c0de9015 8961 var->pp->type.name, var->name);
5fa1256f 8962
8963 var->is_seeded = 1;
8964 continue;
8965 }
8966
8c999988 8967 if (IS(words[1], "dd")) {
5fa1256f 8968 var->lmod = OPLM_DWORD;
8c999988 8969 if (wordc >= 4 && IS(words[2], "offset"))
8970 hg_ref_add(words[3]);
8971 }
5fa1256f 8972 else if (IS(words[1], "dw"))
8973 var->lmod = OPLM_WORD;
61e29183 8974 else if (IS(words[1], "db")) {
5fa1256f 8975 var->lmod = OPLM_BYTE;
61e29183 8976 if (wordc >= 3 && (l = strlen(words[2])) > 4) {
8977 if (words[2][0] == '\'' && IS(words[2] + l - 2, ",0"))
8978 var->is_c_str = 1;
8979 }
8980 }
5fa1256f 8981 else if (IS(words[1], "dq"))
8982 var->lmod = OPLM_QWORD;
8983 //else if (IS(words[1], "dt"))
8984 else
8985 aerr("type '%s' not known\n", words[1]);
8986 }
8987 }
8988
8989 rewind(fasm);
8990 asmln = 0;
8991}
8992
8993static void set_label(int i, const char *name)
8994{
8995 const char *p;
8996 int len;
8997
8998 len = strlen(name);
8999 p = strchr(name, ':');
9000 if (p != NULL)
9001 len = p - name;
9002
9003 if (g_labels[i] != NULL && !IS_START(g_labels[i], "algn_"))
9004 aerr("dupe label '%s' vs '%s'?\n", name, g_labels[i]);
9005 g_labels[i] = realloc(g_labels[i], len + 1);
9006 my_assert_not(g_labels[i], NULL);
9007 memcpy(g_labels[i], name, len);
9008 g_labels[i][len] = 0;
9009}
9010
e56ab892 9011struct chunk_item {
9012 char *name;
9013 long fptr;
de50b98b 9014 int asmln;
e56ab892 9015};
9016
cdfaeed7 9017static struct chunk_item *func_chunks;
9018static int func_chunk_cnt;
9019static int func_chunk_alloc;
9020
9021static void add_func_chunk(FILE *fasm, const char *name, int line)
9022{
9023 if (func_chunk_cnt >= func_chunk_alloc) {
9024 func_chunk_alloc *= 2;
9025 func_chunks = realloc(func_chunks,
9026 func_chunk_alloc * sizeof(func_chunks[0]));
9027 my_assert_not(func_chunks, NULL);
9028 }
9029 func_chunks[func_chunk_cnt].fptr = ftell(fasm);
9030 func_chunks[func_chunk_cnt].name = strdup(name);
9031 func_chunks[func_chunk_cnt].asmln = line;
9032 func_chunk_cnt++;
9033}
9034
e56ab892 9035static int cmp_chunks(const void *p1, const void *p2)
9036{
9037 const struct chunk_item *c1 = p1, *c2 = p2;
9038 return strcmp(c1->name, c2->name);
9039}
9040
ea43585b 9041static void scan_ahead_for_chunks(FILE *fasm)
cdfaeed7 9042{
9043 char words[2][256];
9044 char line[256];
9045 long oldpos;
9046 int oldasmln;
9047 int wordc;
9048 char *p;
9049 int i;
9050
9051 oldpos = ftell(fasm);
9052 oldasmln = asmln;
9053
5fa1256f 9054 while (my_fgets(line, sizeof(line), fasm))
cdfaeed7 9055 {
9056 wordc = 0;
9057 asmln++;
9058
9059 p = sskip(line);
9060 if (*p == 0)
9061 continue;
9062
9063 if (*p == ';')
9064 {
9065 // get rid of random tabs
9066 for (i = 0; line[i] != 0; i++)
9067 if (line[i] == '\t')
9068 line[i] = ' ';
9069
9070 if (p[2] == 'S' && IS_START(p, "; START OF FUNCTION CHUNK FOR "))
9071 {
9072 p += 30;
9073 next_word(words[0], sizeof(words[0]), p);
9074 if (words[0][0] == 0)
9075 aerr("missing name for func chunk?\n");
9076
9077 add_func_chunk(fasm, words[0], asmln);
9078 }
46b388c2 9079 else if (IS_START(p, "; sctend"))
9080 break;
9081
cdfaeed7 9082 continue;
9083 } // *p == ';'
9084
9085 for (wordc = 0; wordc < ARRAY_SIZE(words); wordc++) {
9086 words[wordc][0] = 0;
9087 p = sskip(next_word_s(words[wordc], sizeof(words[0]), p));
9088 if (*p == 0 || *p == ';') {
9089 wordc++;
9090 break;
9091 }
9092 }
9093
9094 if (wordc == 2 && IS(words[1], "ends"))
9095 break;
9096 }
9097
9098 fseek(fasm, oldpos, SEEK_SET);
9099 asmln = oldasmln;
9100}
9101
91977a1c 9102int main(int argc, char *argv[])
9103{
06c5d854 9104 FILE *fout, *fasm, *frlist;
4c45fa73 9105 struct parsed_data *pd = NULL;
9106 int pd_alloc = 0;
9107 char **rlist = NULL;
9108 int rlist_len = 0;
9109 int rlist_alloc = 0;
e56ab892 9110 int func_chunks_used = 0;
9111 int func_chunks_sorted = 0;
e56ab892 9112 int func_chunk_i = -1;
9113 long func_chunk_ret = 0;
de50b98b 9114 int func_chunk_ret_ln = 0;
cdfaeed7 9115 int scanned_ahead = 0;
91977a1c 9116 char line[256];
a2c1d768 9117 char words[20][256];
4c45fa73 9118 enum opr_lenmod lmod;
ddaf8bd7 9119 char *sctproto = NULL;
91977a1c 9120 int in_func = 0;
4c45fa73 9121 int pending_endp = 0;
11437ea1 9122 int skip_code = 0;
9123 int skip_code_end = 0;
940e8e66 9124 int skip_warned = 0;
91977a1c 9125 int eq_alloc;
bfa4a6ee 9126 int verbose = 0;
1f84f6b3 9127 int multi_seg = 0;
46b388c2 9128 int end = 0;
bfa4a6ee 9129 int arg_out;
89ff3147 9130 int arg;
91977a1c 9131 int pi = 0;
e56ab892 9132 int i, j;
9133 int ret, len;
91977a1c 9134 char *p;
9135 int wordc;
9136
89ff3147 9137 for (arg = 1; arg < argc; arg++) {
9138 if (IS(argv[arg], "-v"))
9139 verbose = 1;
9140 else if (IS(argv[arg], "-rf"))
9141 g_allow_regfunc = 1;
8c83cc48 9142 else if (IS(argv[arg], "-uc"))
9143 g_allow_user_icall = 1;
1f84f6b3 9144 else if (IS(argv[arg], "-m"))
9145 multi_seg = 1;
92d715b6 9146 else if (IS(argv[arg], "-hdr"))
9af2d373 9147 g_header_mode = g_quiet_pp = g_allow_regfunc = 1;
89ff3147 9148 else
9149 break;
bfa4a6ee 9150 }
9151
9152 if (argc < arg + 3) {
315b77eb 9153 printf("usage:\n%s [-v] [-rf] [-m] <.c> <.asm> <hdr.h> [rlist]*\n"
9154 "%s -hdr <out.h> <.asm> <seed.h> [rlist]*\n"
9155 "options:\n"
9156 " -hdr - header generation mode\n"
9157 " -rf - allow unannotated indirect calls\n"
8c83cc48 9158 " -uc - allow ind. calls/refs to __usercall\n"
315b77eb 9159 " -m - allow multiple .text sections\n"
9160 "[rlist] is a file with function names to skip,"
9161 " one per line\n",
92d715b6 9162 argv[0], argv[0]);
91977a1c 9163 return 1;
9164 }
9165
bfa4a6ee 9166 arg_out = arg++;
91977a1c 9167
bfa4a6ee 9168 asmfn = argv[arg++];
91977a1c 9169 fasm = fopen(asmfn, "r");
9170 my_assert_not(fasm, NULL);
9171
bfa4a6ee 9172 hdrfn = argv[arg++];
06c5d854 9173 g_fhdr = fopen(hdrfn, "r");
9174 my_assert_not(g_fhdr, NULL);
bfa4a6ee 9175
9176 rlist_alloc = 64;
9177 rlist = malloc(rlist_alloc * sizeof(rlist[0]));
9178 my_assert_not(rlist, NULL);
9179 // needs special handling..
9180 rlist[rlist_len++] = "__alloca_probe";
9181
e56ab892 9182 func_chunk_alloc = 32;
9183 func_chunks = malloc(func_chunk_alloc * sizeof(func_chunks[0]));
9184 my_assert_not(func_chunks, NULL);
9185
a2c1d768 9186 memset(words, 0, sizeof(words));
9187
bfa4a6ee 9188 for (; arg < argc; arg++) {
11437ea1 9189 int skip_func = 0;
9190
bfa4a6ee 9191 frlist = fopen(argv[arg], "r");
9192 my_assert_not(frlist, NULL);
9193
5fa1256f 9194 while (my_fgets(line, sizeof(line), frlist)) {
bfa4a6ee 9195 p = sskip(line);
1cd4a663 9196 if (*p == 0 || *p == ';')
9197 continue;
9198 if (*p == '#') {
89ff3147 9199 if (IS_START(p, "#if 0")
9200 || (g_allow_regfunc && IS_START(p, "#if NO_REGFUNC")))
9201 {
1cd4a663 9202 skip_func = 1;
89ff3147 9203 }
1cd4a663 9204 else if (IS_START(p, "#endif"))
9205 skip_func = 0;
9206 continue;
9207 }
9208 if (skip_func)
bfa4a6ee 9209 continue;
9210
9211 p = next_word(words[0], sizeof(words[0]), p);
9212 if (words[0][0] == 0)
9213 continue;
9214
9215 if (rlist_len >= rlist_alloc) {
9216 rlist_alloc = rlist_alloc * 2 + 64;
9217 rlist = realloc(rlist, rlist_alloc * sizeof(rlist[0]));
9218 my_assert_not(rlist, NULL);
9219 }
9220 rlist[rlist_len++] = strdup(words[0]);
9221 }
9222
9223 fclose(frlist);
9224 frlist = NULL;
9225 }
9226
9227 if (rlist_len > 0)
9228 qsort(rlist, rlist_len, sizeof(rlist[0]), cmpstringp);
9229
9230 fout = fopen(argv[arg_out], "w");
91977a1c 9231 my_assert_not(fout, NULL);
9232
9233 eq_alloc = 128;
9234 g_eqs = malloc(eq_alloc * sizeof(g_eqs[0]));
9235 my_assert_not(g_eqs, NULL);
9236
4c45fa73 9237 for (i = 0; i < ARRAY_SIZE(g_label_refs); i++) {
9238 g_label_refs[i].i = -1;
9239 g_label_refs[i].next = NULL;
9240 }
9241
5fa1256f 9242 if (g_header_mode)
4c20744d 9243 scan_variables(fasm, rlist, rlist_len);
5fa1256f 9244
9245 while (my_fgets(line, sizeof(line), fasm))
91977a1c 9246 {
4c45fa73 9247 wordc = 0;
91977a1c 9248 asmln++;
9249
9250 p = sskip(line);
1bafb621 9251 if (*p == 0)
91977a1c 9252 continue;
9253
de50b98b 9254 // get rid of random tabs
9255 for (i = 0; line[i] != 0; i++)
9256 if (line[i] == '\t')
9257 line[i] = ' ';
9258
e56ab892 9259 if (*p == ';')
9260 {
e56ab892 9261 if (p[2] == '=' && IS_START(p, "; =============== S U B"))
9262 goto do_pending_endp; // eww..
9263
9264 if (p[2] == 'A' && IS_START(p, "; Attributes:"))
9265 {
9266 static const char *attrs[] = {
9267 "bp-based frame",
9268 "library function",
9269 "static",
9270 "noreturn",
9271 "thunk",
9272 "fpd=",
9273 };
9274
9275 // parse IDA's attribute-list comment
9276 g_ida_func_attr = 0;
9277 p = sskip(p + 13);
9278
9279 for (; *p != 0; p = sskip(p)) {
9280 for (i = 0; i < ARRAY_SIZE(attrs); i++) {
9281 if (!strncmp(p, attrs[i], strlen(attrs[i]))) {
9282 g_ida_func_attr |= 1 << i;
9283 p += strlen(attrs[i]);
9284 break;
9285 }
9286 }
9287 if (i == ARRAY_SIZE(attrs)) {
9288 anote("unparsed IDA attr: %s\n", p);
1bafb621 9289 break;
9290 }
e56ab892 9291 if (IS(attrs[i], "fpd=")) {
9292 p = next_word(words[0], sizeof(words[0]), p);
9293 // ignore for now..
9294 }
1bafb621 9295 }
e56ab892 9296 }
7e08c224 9297 else if (p[2] == 's' && IS_START(p, "; sctattr:"))
9298 {
9299 static const char *attrs[] = {
9300 "clear_sf",
226e8df1 9301 "clear_regmask",
7e08c224 9302 };
9303
9304 // parse manual attribute-list comment
9305 g_sct_func_attr = 0;
9306 p = sskip(p + 10);
9307
9308 for (; *p != 0; p = sskip(p)) {
9309 for (i = 0; i < ARRAY_SIZE(attrs); i++) {
9310 if (!strncmp(p, attrs[i], strlen(attrs[i]))) {
9311 g_sct_func_attr |= 1 << i;
9312 p += strlen(attrs[i]);
9313 break;
9314 }
9315 }
226e8df1 9316 if (*p == '=') {
9317 j = ret = 0;
9318 if (i == 0)
9319 // clear_sf=start,len (in dwords)
9320 ret = sscanf(p, "=%d,%d%n", &g_stack_clear_start,
9321 &g_stack_clear_len, &j);
9322 else if (i == 1)
9323 // clear_regmask=<mask>
6135d8e6 9324 ret = sscanf(p, "=%x%n", &g_regmask_init, &j) + 1;
7e08c224 9325 if (ret < 2) {
226e8df1 9326 anote("unparsed attr value: %s\n", p);
7e08c224 9327 break;
9328 }
9329 p += j;
9330 }
9331 else if (i == ARRAY_SIZE(attrs)) {
9332 anote("unparsed sct attr: %s\n", p);
9333 break;
9334 }
9335 }
9336 }
e56ab892 9337 else if (p[2] == 'S' && IS_START(p, "; START OF FUNCTION CHUNK FOR "))
9338 {
9339 p += 30;
9340 next_word(words[0], sizeof(words[0]), p);
9341 if (words[0][0] == 0)
cdfaeed7 9342 aerr("missing name for func chunk?\n");
9343
9344 if (!scanned_ahead) {
9345 add_func_chunk(fasm, words[0], asmln);
9346 func_chunks_sorted = 0;
e56ab892 9347 }
e56ab892 9348 }
9349 else if (p[2] == 'E' && IS_START(p, "; END OF FUNCTION CHUNK"))
9350 {
9351 if (func_chunk_i >= 0) {
9352 if (func_chunk_i < func_chunk_cnt
9353 && IS(func_chunks[func_chunk_i].name, g_func))
9354 {
9355 // move on to next chunk
9356 ret = fseek(fasm, func_chunks[func_chunk_i].fptr, SEEK_SET);
9357 if (ret)
9358 aerr("seek failed for '%s' chunk #%d\n",
9359 g_func, func_chunk_i);
de50b98b 9360 asmln = func_chunks[func_chunk_i].asmln;
e56ab892 9361 func_chunk_i++;
9362 }
9363 else {
9364 if (func_chunk_ret == 0)
9365 aerr("no return from chunk?\n");
9366 fseek(fasm, func_chunk_ret, SEEK_SET);
de50b98b 9367 asmln = func_chunk_ret_ln;
e56ab892 9368 func_chunk_ret = 0;
9369 pending_endp = 1;
9370 }
1bafb621 9371 }
e56ab892 9372 }
9373 else if (p[2] == 'F' && IS_START(p, "; FUNCTION CHUNK AT ")) {
9374 func_chunks_used = 1;
9375 p += 20;
9376 if (IS_START(g_func, "sub_")) {
9377 unsigned long addr = strtoul(p, NULL, 16);
9378 unsigned long f_addr = strtoul(g_func + 4, NULL, 16);
cdfaeed7 9379 if (addr > f_addr && !scanned_ahead) {
b2bd20c0 9380 //anote("scan_ahead caused by '%s', addr %lx\n",
9381 // g_func, addr);
ea43585b 9382 scan_ahead_for_chunks(fasm);
cdfaeed7 9383 scanned_ahead = 1;
9384 func_chunks_sorted = 0;
9385 }
1bafb621 9386 }
9387 }
9388 continue;
e56ab892 9389 } // *p == ';'
1bafb621 9390
06c5d854 9391parse_words:
a2c1d768 9392 for (i = wordc; i < ARRAY_SIZE(words); i++)
9393 words[i][0] = 0;
cdfaeed7 9394 for (wordc = 0; wordc < ARRAY_SIZE(words); wordc++) {
bfa4a6ee 9395 p = sskip(next_word_s(words[wordc], sizeof(words[0]), p));
91977a1c 9396 if (*p == 0 || *p == ';') {
9397 wordc++;
9398 break;
9399 }
9400 }
a2c1d768 9401 if (*p != 0 && *p != ';')
9402 aerr("too many words\n");
91977a1c 9403
11437ea1 9404 if (skip_code_end) {
9405 skip_code_end = 0;
9406 skip_code = 0;
9407 }
9408
9409 // allow asm patches in comments
ddaf8bd7 9410 if (*p == ';') {
9411 if (IS_START(p, "; sctpatch:")) {
9412 p = sskip(p + 11);
9413 if (*p == 0 || *p == ';')
9414 continue;
9415 goto parse_words; // lame
9416 }
9417 if (IS_START(p, "; sctproto:")) {
9418 sctproto = strdup(p + 11);
9419 }
46b388c2 9420 else if (IS_START(p, "; sctend")) {
9421 end = 1;
9422 if (!pending_endp)
9423 break;
9424 }
11437ea1 9425 else if (IS_START(p, "; sctskip_start")) {
9426 if (in_func && !g_skip_func) {
9427 if (!skip_code) {
9428 ops[pi].op = OPP_ABORT;
9429 ops[pi].asmln = asmln;
9430 pi++;
9431 }
9432 skip_code = 1;
9433 }
9434 }
9435 else if (IS_START(p, "; sctskip_end")) {
9436 if (skip_code)
9437 skip_code_end = 1;
9438 }
06c5d854 9439 }
9440
91977a1c 9441 if (wordc == 0) {
9442 // shouldn't happen
9443 awarn("wordc == 0?\n");
9444 continue;
9445 }
9446
9447 // don't care about this:
9448 if (words[0][0] == '.'
9449 || IS(words[0], "include")
9450 || IS(words[0], "assume") || IS(words[1], "segment")
9451 || IS(words[0], "align"))
9452 {
9453 continue;
9454 }
9455
4c45fa73 9456do_pending_endp:
9457 // do delayed endp processing to collect switch jumptables
9458 if (pending_endp) {
30c8c549 9459 if (in_func && !g_skip_func && !end && wordc >= 2
4c45fa73 9460 && ((words[0][0] == 'd' && words[0][2] == 0)
9461 || (words[1][0] == 'd' && words[1][2] == 0)))
9462 {
9463 i = 1;
9464 if (words[1][0] == 'd' && words[1][2] == 0) {
9465 // label
9466 if (g_func_pd_cnt >= pd_alloc) {
9467 pd_alloc = pd_alloc * 2 + 16;
9468 g_func_pd = realloc(g_func_pd,
9469 sizeof(g_func_pd[0]) * pd_alloc);
9470 my_assert_not(g_func_pd, NULL);
9471 }
9472 pd = &g_func_pd[g_func_pd_cnt];
9473 g_func_pd_cnt++;
9474 memset(pd, 0, sizeof(*pd));
9475 strcpy(pd->label, words[0]);
9476 pd->type = OPT_CONST;
9477 pd->lmod = lmod_from_directive(words[1]);
9478 i = 2;
9479 }
9480 else {
da87ae38 9481 if (pd == NULL) {
9482 if (verbose)
9483 anote("skipping alignment byte?\n");
9484 continue;
9485 }
4c45fa73 9486 lmod = lmod_from_directive(words[0]);
9487 if (lmod != pd->lmod)
9488 aerr("lmod change? %d->%d\n", pd->lmod, lmod);
9489 }
9490
9491 if (pd->count_alloc < pd->count + wordc) {
9492 pd->count_alloc = pd->count_alloc * 2 + 14 + wordc;
9493 pd->d = realloc(pd->d, sizeof(pd->d[0]) * pd->count_alloc);
9494 my_assert_not(pd->d, NULL);
9495 }
9496 for (; i < wordc; i++) {
9497 if (IS(words[i], "offset")) {
9498 pd->type = OPT_OFFSET;
9499 i++;
9500 }
9501 p = strchr(words[i], ',');
9502 if (p != NULL)
9503 *p = 0;
9504 if (pd->type == OPT_OFFSET)
9505 pd->d[pd->count].u.label = strdup(words[i]);
9506 else
e27467d0 9507 pd->d[pd->count].u.val = parse_number(words[i], 0);
4c45fa73 9508 pd->d[pd->count].bt_i = -1;
9509 pd->count++;
9510 }
9511 continue;
9512 }
9513
30c8c549 9514 if (in_func && !g_skip_func) {
9af2d373 9515 if (g_header_mode)
92d715b6 9516 gen_hdr(g_func, pi);
9517 else
9518 gen_func(fout, g_fhdr, g_func, pi);
9519 }
4c45fa73 9520
9521 pending_endp = 0;
9522 in_func = 0;
9523 g_ida_func_attr = 0;
7e08c224 9524 g_sct_func_attr = 0;
9525 g_stack_clear_start = 0;
9526 g_stack_clear_len = 0;
226e8df1 9527 g_regmask_init = 0;
4c45fa73 9528 skip_warned = 0;
30c8c549 9529 g_skip_func = 0;
4c45fa73 9530 g_func[0] = 0;
ba93cc12 9531 g_seh_found = 0;
e56ab892 9532 func_chunks_used = 0;
9533 func_chunk_i = -1;
4c45fa73 9534 if (pi != 0) {
9535 memset(&ops, 0, pi * sizeof(ops[0]));
d7857c3a 9536 clear_labels(pi);
4c45fa73 9537 pi = 0;
9538 }
9539 g_eqcnt = 0;
9540 for (i = 0; i < g_func_pd_cnt; i++) {
9541 pd = &g_func_pd[i];
9542 if (pd->type == OPT_OFFSET) {
9543 for (j = 0; j < pd->count; j++)
9544 free(pd->d[j].u.label);
9545 }
9546 free(pd->d);
9547 pd->d = NULL;
9548 }
9549 g_func_pd_cnt = 0;
d4a985bd 9550 g_func_lmods = 0;
4c45fa73 9551 pd = NULL;
46b388c2 9552
9553 if (end)
9554 break;
4c45fa73 9555 if (wordc == 0)
9556 continue;
9557 }
9558
91977a1c 9559 if (IS(words[1], "proc")) {
9560 if (in_func)
9561 aerr("proc '%s' while in_func '%s'?\n",
9562 words[0], g_func);
bfa4a6ee 9563 p = words[0];
ddaf8bd7 9564 if (bsearch(&p, rlist, rlist_len, sizeof(rlist[0]), cmpstringp))
30c8c549 9565 g_skip_func = 1;
91977a1c 9566 strcpy(g_func, words[0]);
d4e3b5db 9567 set_label(0, words[0]);
91977a1c 9568 in_func = 1;
9569 continue;
9570 }
9571
e56ab892 9572 if (IS(words[1], "endp"))
9573 {
91977a1c 9574 if (!in_func)
9575 aerr("endp '%s' while not in_func?\n", words[0]);
9576 if (!IS(g_func, words[0]))
9577 aerr("endp '%s' while in_func '%s'?\n",
9578 words[0], g_func);
11437ea1 9579 if (skip_code)
9580 aerr("endp '%s' while skipping code\n", words[0]);
bfa4a6ee 9581
ddaf8bd7 9582 if ((g_ida_func_attr & IDAFA_THUNK) && pi == 1
ba93cc12 9583 && ops[0].op == OP_JMP && ops[0].operand[0].segment)
ddaf8bd7 9584 {
9585 // import jump
30c8c549 9586 g_skip_func = 1;
ddaf8bd7 9587 }
9588
30c8c549 9589 if (!g_skip_func && func_chunks_used) {
e56ab892 9590 // start processing chunks
9591 struct chunk_item *ci, key = { g_func, 0 };
9592
9593 func_chunk_ret = ftell(fasm);
de50b98b 9594 func_chunk_ret_ln = asmln;
e56ab892 9595 if (!func_chunks_sorted) {
9596 qsort(func_chunks, func_chunk_cnt,
9597 sizeof(func_chunks[0]), cmp_chunks);
9598 func_chunks_sorted = 1;
9599 }
9600 ci = bsearch(&key, func_chunks, func_chunk_cnt,
9601 sizeof(func_chunks[0]), cmp_chunks);
9602 if (ci == NULL)
9603 aerr("'%s' needs chunks, but none found\n", g_func);
9604 func_chunk_i = ci - func_chunks;
9605 for (; func_chunk_i > 0; func_chunk_i--)
9606 if (!IS(func_chunks[func_chunk_i - 1].name, g_func))
9607 break;
9608
9609 ret = fseek(fasm, func_chunks[func_chunk_i].fptr, SEEK_SET);
9610 if (ret)
9611 aerr("seek failed for '%s' chunk #%d\n", g_func, func_chunk_i);
de50b98b 9612 asmln = func_chunks[func_chunk_i].asmln;
e56ab892 9613 func_chunk_i++;
9614 continue;
9615 }
4c45fa73 9616 pending_endp = 1;
91977a1c 9617 continue;
9618 }
9619
1f84f6b3 9620 if (wordc == 2 && IS(words[1], "ends")) {
46b388c2 9621 if (!multi_seg) {
9622 end = 1;
9623 if (pending_endp)
9624 goto do_pending_endp;
1f84f6b3 9625 break;
46b388c2 9626 }
1f84f6b3 9627
9628 // scan for next text segment
5fa1256f 9629 while (my_fgets(line, sizeof(line), fasm)) {
1f84f6b3 9630 asmln++;
9631 p = sskip(line);
9632 if (*p == 0 || *p == ';')
9633 continue;
9634
9635 if (strstr(p, "segment para public 'CODE' use32"))
9636 break;
9637 }
9638
9639 continue;
9640 }
a2c1d768 9641
bfa4a6ee 9642 p = strchr(words[0], ':');
9643 if (p != NULL) {
d4e3b5db 9644 set_label(pi, words[0]);
bfa4a6ee 9645 continue;
9646 }
9647
11437ea1 9648 if (!in_func || g_skip_func || skip_code) {
30c8c549 9649 if (!skip_warned && !g_skip_func && g_labels[pi] != NULL) {
bfa4a6ee 9650 if (verbose)
9651 anote("skipping from '%s'\n", g_labels[pi]);
9652 skip_warned = 1;
9653 }
d7857c3a 9654 free(g_labels[pi]);
9655 g_labels[pi] = NULL;
bfa4a6ee 9656 continue;
9657 }
9658
ddaf8bd7 9659 if (wordc > 1 && IS(words[1], "="))
9660 {
91977a1c 9661 if (wordc != 5)
9662 aerr("unhandled equ, wc=%d\n", wordc);
9663 if (g_eqcnt >= eq_alloc) {
9664 eq_alloc *= 2;
9665 g_eqs = realloc(g_eqs, eq_alloc * sizeof(g_eqs[0]));
9666 my_assert_not(g_eqs, NULL);
9667 }
9668
9669 len = strlen(words[0]);
9670 if (len > sizeof(g_eqs[0].name) - 1)
9671 aerr("equ name too long: %d\n", len);
9672 strcpy(g_eqs[g_eqcnt].name, words[0]);
9673
9674 if (!IS(words[3], "ptr"))
9675 aerr("unhandled equ\n");
9676 if (IS(words[2], "dword"))
9677 g_eqs[g_eqcnt].lmod = OPLM_DWORD;
9678 else if (IS(words[2], "word"))
9679 g_eqs[g_eqcnt].lmod = OPLM_WORD;
9680 else if (IS(words[2], "byte"))
9681 g_eqs[g_eqcnt].lmod = OPLM_BYTE;
90307a99 9682 else if (IS(words[2], "qword"))
9683 g_eqs[g_eqcnt].lmod = OPLM_QWORD;
91977a1c 9684 else
9685 aerr("bad lmod: '%s'\n", words[2]);
9686
e27467d0 9687 g_eqs[g_eqcnt].offset = parse_number(words[4], 0);
91977a1c 9688 g_eqcnt++;
9689 continue;
9690 }
9691
9692 if (pi >= ARRAY_SIZE(ops))
9693 aerr("too many ops\n");
9694
91977a1c 9695 parse_op(&ops[pi], words, wordc);
ddaf8bd7 9696
865f1aca 9697 ops[pi].datap = sctproto;
9698 sctproto = NULL;
91977a1c 9699 pi++;
91977a1c 9700 }
9701
9af2d373 9702 if (g_header_mode)
92d715b6 9703 output_hdr(fout);
9704
91977a1c 9705 fclose(fout);
9706 fclose(fasm);
06c5d854 9707 fclose(g_fhdr);
91977a1c 9708
9709 return 0;
c36e914d 9710}
91977a1c 9711
9712// vim:ts=2:shiftwidth=2:expandtab