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