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