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