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