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