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