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