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