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