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