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