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