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