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