Allow returning something of type void in a function that returns void
[delight/core.git] / d-asm-i386.h
blob7ce7e362681a70cd4d6197db8bf7185cbb0fa67d
2 typedef enum {
3 Reg_Invalid = -1,
4 Reg_EAX = 0,
5 Reg_EBX,
6 Reg_ECX,
7 Reg_EDX,
8 Reg_ESI,
9 Reg_EDI,
10 Reg_EBP,
11 Reg_ESP,
12 Reg_ST,
13 Reg_ST1, Reg_ST2, Reg_ST3, Reg_ST4, Reg_ST5, Reg_ST6, Reg_ST7,
14 Reg_MM0, Reg_MM1, Reg_MM2, Reg_MM3, Reg_MM4, Reg_MM5, Reg_MM6, Reg_MM7,
15 Reg_XMM0, Reg_XMM1, Reg_XMM2, Reg_XMM3, Reg_XMM4, Reg_XMM5, Reg_XMM6, Reg_XMM7,
16 // will need 64-bit rax,etc. eventually
17 // xmm8-15?
18 Reg_EFLAGS,
19 Reg_CS,
20 Reg_DS,
21 Reg_SS,
22 Reg_ES,
23 Reg_FS,
24 Reg_GS,
25 Reg_AX, Reg_BX, Reg_CX, Reg_DX, Reg_SI, Reg_DI, Reg_BP, Reg_SP,
26 Reg_AL, Reg_AH, Reg_BL, Reg_BH, Reg_CL, Reg_CH, Reg_DL, Reg_DH,
27 Reg_CR0, Reg_CR2, Reg_CR3, Reg_CR4,
28 Reg_DR0, Reg_DR1, Reg_DR2, Reg_DR3, Reg_DR6, Reg_DR7,
29 Reg_TR3, Reg_TR4, Reg_TR5, Reg_TR6, Reg_TR7
30 } Reg;
32 static const int N_Regs = /*gp*/ 8 + /*fp*/ 8 + /*mmx*/ 8 + /*sse*/ 8 +
33 /*seg*/ 6 + /*16bit*/ 8 + /*8bit*/ 8 + /*sys*/ 4+6+5 + /*flags*/ + 1;
35 static struct {
36 const char * name;
37 tree gccName; // GAS will take upper case, but GCC won't (needed for the clobber list)
38 Identifier * ident;
39 char size;
40 char baseReg; // %% todo: Reg, Reg_XX
41 } regInfo[N_Regs] = {
42 { "EAX", NULL_TREE, NULL, 4, Reg_EAX },
43 { "EBX", NULL_TREE, NULL, 4, Reg_EBX },
44 { "ECX", NULL_TREE, NULL, 4, Reg_ECX },
45 { "EDX", NULL_TREE, NULL, 4, Reg_EDX },
46 { "ESI", NULL_TREE, NULL, 4, Reg_ESI },
47 { "EDI", NULL_TREE, NULL, 4, Reg_EDI },
48 { "EBP", NULL_TREE, NULL, 4, Reg_EBP },
49 { "ESP", NULL_TREE, NULL, 4, Reg_ESP },
50 { "ST", NULL_TREE, NULL, 10, Reg_ST },
51 { "ST(1)", NULL_TREE, NULL,10, Reg_ST1 },
52 { "ST(2)", NULL_TREE, NULL,10, Reg_ST2 },
53 { "ST(3)", NULL_TREE, NULL,10, Reg_ST3 },
54 { "ST(4)", NULL_TREE, NULL,10, Reg_ST4 },
55 { "ST(5)", NULL_TREE, NULL,10, Reg_ST5 },
56 { "ST(6)", NULL_TREE, NULL,10, Reg_ST6 },
57 { "ST(7)", NULL_TREE, NULL,10, Reg_ST7 },
58 { "MM0", NULL_TREE, NULL, 8, Reg_MM0 },
59 { "MM1", NULL_TREE, NULL, 8, Reg_MM1 },
60 { "MM2", NULL_TREE, NULL, 8, Reg_MM2 },
61 { "MM3", NULL_TREE, NULL, 8, Reg_MM3 },
62 { "MM4", NULL_TREE, NULL, 8, Reg_MM4 },
63 { "MM5", NULL_TREE, NULL, 8, Reg_MM5 },
64 { "MM6", NULL_TREE, NULL, 8, Reg_MM6 },
65 { "MM7", NULL_TREE, NULL, 8, Reg_MM7 },
66 { "XMM0", NULL_TREE, NULL, 16, Reg_XMM0 },
67 { "XMM1", NULL_TREE, NULL, 16, Reg_XMM1 },
68 { "XMM2", NULL_TREE, NULL, 16, Reg_XMM2 },
69 { "XMM3", NULL_TREE, NULL, 16, Reg_XMM3 },
70 { "XMM4", NULL_TREE, NULL, 16, Reg_XMM4 },
71 { "XMM5", NULL_TREE, NULL, 16, Reg_XMM5 },
72 { "XMM6", NULL_TREE, NULL, 16, Reg_XMM6 },
73 { "XMM7", NULL_TREE, NULL, 16, Reg_XMM7 },
74 { "FLAGS", NULL_TREE, NULL, 0, Reg_EFLAGS }, // the gcc name is "flags"; not used in assembler input
75 { "CS", NULL_TREE, NULL, 2, -1 },
76 { "DS", NULL_TREE, NULL, 2, -1 },
77 { "SS", NULL_TREE, NULL, 2, -1 },
78 { "ES", NULL_TREE, NULL, 2, -1 },
79 { "FS", NULL_TREE, NULL, 2, -1 },
80 { "GS", NULL_TREE, NULL, 2, -1 },
81 { "AX", NULL_TREE, NULL, 2, Reg_EAX },
82 { "BX", NULL_TREE, NULL, 2, Reg_EBX },
83 { "CX", NULL_TREE, NULL, 2, Reg_ECX },
84 { "DX", NULL_TREE, NULL, 2, Reg_EDX },
85 { "SI", NULL_TREE, NULL, 2, Reg_ESI },
86 { "DI", NULL_TREE, NULL, 2, Reg_EDI },
87 { "BP", NULL_TREE, NULL, 2, Reg_EBP },
88 { "SP", NULL_TREE, NULL, 2, Reg_ESP },
89 { "AL", NULL_TREE, NULL, 1, Reg_EAX },
90 { "AH", NULL_TREE, NULL, 1, Reg_EAX },
91 { "BL", NULL_TREE, NULL, 1, Reg_EBX },
92 { "BH", NULL_TREE, NULL, 1, Reg_EBX },
93 { "CL", NULL_TREE, NULL, 1, Reg_ECX },
94 { "CH", NULL_TREE, NULL, 1, Reg_ECX },
95 { "DL", NULL_TREE, NULL, 1, Reg_EDX },
96 { "DH", NULL_TREE, NULL, 1, Reg_EDX },
97 { "CR0", NULL_TREE, NULL, 0, -1 },
98 { "CR2", NULL_TREE, NULL, 0, -1 },
99 { "CR3", NULL_TREE, NULL, 0, -1 },
100 { "CR4", NULL_TREE, NULL, 0, -1 },
101 { "DR0", NULL_TREE, NULL, 0, -1 },
102 { "DR1", NULL_TREE, NULL, 0, -1 },
103 { "DR2", NULL_TREE, NULL, 0, -1 },
104 { "DR3", NULL_TREE, NULL, 0, -1 },
105 { "DR6", NULL_TREE, NULL, 0, -1 },
106 { "DR7", NULL_TREE, NULL, 0, -1 },
107 { "TR3", NULL_TREE, NULL, 0, -1 },
108 { "TR4", NULL_TREE, NULL, 0, -1 },
109 { "TR5", NULL_TREE, NULL, 0, -1 },
110 { "TR6", NULL_TREE, NULL, 0, -1 },
111 { "TR7", NULL_TREE, NULL, 0, -1 }
114 typedef enum {
115 No_Type_Needed,
116 Int_Types,
117 Word_Types, // same as Int_Types, but byte is not allowed
118 FP_Types,
119 FPInt_Types,
120 Byte_NoType, // byte only, but no type suffix
121 } TypeNeeded;
123 typedef enum {
124 No_Link,
125 Out_Mnemonic,
126 Next_Form
127 } OpLink;
129 typedef enum {
130 Clb_SizeAX = 0x01,
131 Clb_SizeDXAX = 0x02,
132 Clb_EAX = 0x03,
133 Clb_DXAX_Mask = 0x03,
135 Clb_Flags = 0x04,
136 Clb_DI = 0x08,
137 Clb_SI = 0x10,
138 Clb_CX = 0x20,
139 Clb_ST = 0x40,
140 Clb_SP = 0x80 // Doesn't actually let GCC know the frame pointer is modified
141 } ImplicitClober;
143 // "^ +/..\([A-Za-z_0-9]+\).*" -> " \1,"
144 typedef enum {
145 Op_Invalid,
146 Op_Adjust,
147 Op_Dst,
148 Op_Upd,
149 Op_DstW,
150 Op_DstF,
151 Op_UpdF,
152 Op_DstSrc,
153 Op_DstSrcF,
154 Op_UpdSrcF,
155 Op_DstSrcFW,
156 Op_UpdSrcFW,
157 Op_DstSrcSSE,
158 Op_DstSrcMMX,
159 Op_DstSrcImmS,
160 Op_DstSrcImmM,
161 Op_UpdSrcShft,
162 Op_DstSrcNT,
163 Op_UpdSrcNT,
164 Op_DstMemNT,
165 Op_DstRMBNT,
166 Op_DstRMWNT,
167 Op_UpdUpd,
168 Op_UpdUpdF,
169 Op_Src,
170 Op_SrcRMWNT,
171 Op_SrcW,
172 Op_SrcImm,
173 Op_Src_DXAXF,
174 Op_SrcMemNT,
175 Op_SrcMemNTF,
176 Op_SrcSrc,
177 Op_SrcSrcF,
178 Op_SrcSrcFW,
179 Op_SrcSrcSSEF,
180 Op_SrcSrcMMX,
181 Op_Shift,
182 Op_Branch,
183 Op_CBranch,
184 Op_0,
185 Op_0_AX,
186 Op_0_DXAX,
187 Op_Loop,
188 Op_Flags,
189 Op_F0_ST,
190 Op_F0_P,
191 Op_Fs_P,
192 Op_Fis,
193 Op_Fis_ST,
194 Op_Fis_P,
195 Op_Fid,
196 Op_Fid_P,
197 Op_Ffd,
198 Op_FfdR,
199 Op_Ffd_P,
200 Op_FfdR_P,
201 Op_Fd_P,
202 Op_FdST,
203 Op_FMath,
204 Op_FdSTiSTi,
205 Op_FPMath,
206 Op_FCmp,
207 Op_FCmp1,
208 Op_FCmpP,
209 Op_FCmpP1,
210 Op_FCmpFlg,
211 Op_FCmpFlgP,
212 Op_fld,
213 Op_fldR,
214 Op_fxch,
215 Op_fxch1,
216 Op_fxch0,
217 Op_SizedStack,
218 Op_bound,
219 Op_bswap,
220 Op_cmps,
221 Op_cmpsd,
222 Op_cmpsX,
223 Op_cmpxchg8b,
224 Op_cmpxchg,
225 Op_cpuid,
226 Op_enter,
227 Op_fdisi,
228 Op_feni,
229 Op_fsetpm,
230 Op_fXstsw,
231 Op_imul,
232 Op_imul2,
233 Op_imul1,
234 Op_in,
235 Op_ins,
236 Op_insX,
237 Op_iret,
238 Op_iretd,
239 Op_lods,
240 Op_lodsX,
241 Op_movs,
242 Op_movsd,
243 Op_movsX,
244 Op_movsx,
245 Op_movzx,
246 Op_mul,
247 Op_out,
248 Op_outs,
249 Op_outsX,
250 Op_push,
251 Op_ret,
252 Op_retf,
253 Op_scas,
254 Op_scasX,
255 Op_stos,
256 Op_stosX,
257 Op_xlat,
258 N_AsmOpInfo,
259 Op_Align,
260 Op_Even,
261 Op_Naked,
262 Op_db,
263 Op_ds,
264 Op_di,
265 Op_dl,
266 Op_df,
267 Op_dd,
268 Op_de
269 } AsmOp;
271 typedef enum {
272 Opr_None = 0,
273 OprC_MRI = 1,
274 OprC_MR = 2,
275 OprC_Mem = 3,
276 OprC_Reg = 4,
277 OprC_Imm = 5,
278 OprC_SSE = 6,
279 OprC_SSE_Mem = 7,
280 OprC_R32 = 8,
281 OprC_RWord = 9,
282 OprC_RFP = 10,
283 OprC_AbsRel = 11,
284 OprC_Relative = 12,
285 OprC_Port = 13, // DX or imm
286 OprC_AX = 14, // AL,AX,EAX
287 OprC_DX = 15, // only DX
288 OprC_MMX = 16,
289 OprC_MMX_Mem = 17,
290 OprC_Shift = 18, // imm or CL
292 Opr_ClassMask = 0x1f,
294 Opr_Dest = 0x20,
295 Opr_Update = 0x60,
297 Opr_NoType = 0x80,
298 } OprVals;
301 typedef struct {
302 } AsmOprInfo;
304 typedef unsigned char Opr;
306 typedef struct {
307 Opr operands[3];
308 unsigned char
309 needsType : 3,
310 implicitClobbers : 8,
311 linkType : 2;
312 unsigned link;
315 bool takesLabel() {
316 return operands[0] & Opr_Label;
320 unsigned nOperands() {
321 if (!operands[0])
322 return 0;
323 else if (!operands[1])
324 return 1;
325 else if (!operands[2])
326 return 2;
327 else
328 return 3;
330 } AsmOpInfo;
332 typedef enum {
333 Mn_fdisi,
334 Mn_feni,
335 Mn_fsetpm,
336 Mn_iretw,
337 Mn_iret,
338 Mn_lret,
339 Mn_cmpxchg8b,
340 N_AltMn
341 } Alternate_Mnemonics;
343 static const char * alternateMnemonics[N_AltMn] = {
344 ".byte 0xdb, 0xe1",
345 ".byte 0xdb, 0xe0",
346 ".byte 0xdb, 0xe4",
347 "iretw",
348 "iret",
349 "lret",
350 "cmpxchg8b" };
352 #define mri OprC_MRI
353 #define mr OprC_MR
354 #define mem OprC_Mem
355 // for now mfp=mem
356 #define mfp OprC_Mem
357 #define reg OprC_Reg
358 #define imm OprC_Imm
359 #define sse OprC_SSE
360 #define ssem OprC_SSE_Mem
361 #define mmx OprC_MMX
362 #define mmxm OprC_MMX_Mem
363 #define r32 OprC_R32
364 #define rw OprC_RWord
365 #define rfp OprC_RFP
366 #define port OprC_Port
367 #define ax OprC_AX
368 #define dx OprC_DX
369 #define shft OprC_Shift
370 #define D Opr_Dest
371 #define U Opr_Update
372 #define N Opr_NoType
373 //#define L Opr_Label
375 // D=dest, N=notype
376 static AsmOpInfo asmOpInfo[N_AsmOpInfo] = {
377 /* Op_Invalid */ {},
378 /* Op_Adjust */ { 0,0,0, 0, Clb_EAX /*just AX*/ },
379 /* Op_Dst */ { D|mr, 0, 0, 1 },
380 /* Op_Upd */ { U|mr, 0, 0, 1 },
381 /* Op_DstW */ { D|mr, 0, 0, Word_Types },
382 /* Op_DstF */ { D|mr, 0, 0, 1, Clb_Flags },
383 /* Op_UpdF */ { U|mr, 0, 0, 1, Clb_Flags },
384 /* Op_DstSrc */ { D|mr, mri, 0,/**/1 },
385 /* Op_DstSrcF */ { D|mr, mri, 0,/**/1, Clb_Flags },
386 /* Op_UpdSrcF */ { U|mr, mri, 0,/**/1, Clb_Flags },
387 /* Op_DstSrcFW */ { D|mr, mri, 0,/**/Word_Types, Clb_Flags },
388 /* Op_UpdSrcFW */ { U|mr, mri, 0,/**/Word_Types, Clb_Flags },
389 /* Op_DstSrcSSE */ { U|sse, ssem, 0 }, // some may not be update %%
390 /* Op_DstSrcMMX */ { U|mmx, mmxm, 0 }, // some may not be update %%
391 /* Op_DstSrcImmS*/ { U|sse, ssem, N|imm }, // some may not be update %%
392 /* Op_DstSrcImmM*/ { U|mmx, mmxm, N|imm }, // some may not be update %%
393 /* Op_UpdSrcShft*/ { U|mr, reg, N|shft, 1, Clb_Flags }, // 16/32 only
394 /* Op_DstSrcNT */ { D|mr, mr, 0, 0 }, // used for movd .. operands can be rm32,sse,mmx
395 /* Op_UpdSrcNT */ { U|mr, mr, 0, 0 }, // used for movd .. operands can be rm32,sse,mmx
396 /* Op_DstMemNT */ { D|mem, 0, 0 },
397 /* Op_DstRMBNT */ { D|mr, 0, 0, Byte_NoType },
398 /* Op_DstRMWNT */ { D|mr, 0, 0 },
399 /* Op_UpdUpd */ { U|mr,U|mr, 0,/**/1 },
400 /* Op_UpdUpdF */ { U|mr,U|mr, 0,/**/1, Clb_Flags },
401 /* Op_Src */ { mri, 0, 0, 1 },
402 /* Op_SrcRMWNT */ { mr, 0, 0, 0 },
403 /* Op_SrcW */ { mri, 0, 0, Word_Types },
404 /* Op_SrcImm */ { imm },
405 /* Op_Src_DXAXF */ { mr, 0, 0, 1, Clb_SizeDXAX|Clb_Flags },
406 /* Op_SrcMemNT */ { mem, 0, 0 },
407 /* Op_SrcMemNTF */ { mem, 0, 0, 0, Clb_Flags },
408 /* Op_SrcSrc */ { mr, mri, 0, 1 },
409 /* Op_SrcSrcF */ { mr, mri, 0, 1, Clb_Flags },
410 /* Op_SrcSrcFW */ { mr, mri, 0, Word_Types, Clb_Flags },
411 /* Op_SrcSrcSSEF*/ { sse, ssem, 0, 0, Clb_Flags },
412 /* Op_SrcSrcMMX */ { mmx, mmx, 0, },
413 /* Op_Shift */ { D|mr,N|shft, 0,/**/1, Clb_Flags },
414 /* Op_Branch */ { mri },
415 /* Op_CBranch */ { imm },
416 /* Op_0 */ { 0,0,0 },
417 /* Op_0_AX */ { 0,0,0, 0, Clb_SizeAX },
418 /* Op_0_DXAX */ { 0,0,0, 0, Clb_SizeDXAX }, // but for cwd/cdq -- how do know the size..
419 /* Op_Loop */ { imm, 0, 0, 0, Clb_CX },
420 /* Op_Flags */ { 0,0,0, 0, Clb_Flags },
421 /* Op_F0_ST */ { 0,0,0, 0, Clb_ST },
422 /* Op_F0_P */ { 0,0,0, 0, Clb_ST }, // push, pops, etc. not sure how to inform gcc..
423 /* Op_Fs_P */ { mem, 0, 0, 0, Clb_ST }, // "
424 /* Op_Fis */ { mem, 0, 0, FPInt_Types }, // only 16bit and 32bit, DMD defaults to 16bit
425 /* Op_Fis_ST */ { mem, 0, 0, FPInt_Types, Clb_ST }, // "
426 /* Op_Fis_P */ { mem, 0, 0, FPInt_Types, Clb_ST }, // push and pop, fild so also 64 bit
427 /* Op_Fid */ { D|mem, 0, 0, FPInt_Types }, // only 16bit and 32bit, DMD defaults to 16bit
428 /* Op_Fid_P */ { D|mem, 0, 0, FPInt_Types, Clb_ST }, // push and pop, fild so also 64 bit
429 /* Op_Ffd */ { D|mfp, 0, 0, FP_Types, 0, Next_Form, Op_FfdR }, // only 16bit and 32bit, DMD defaults to 16bit, reg form doesn't need type
430 /* Op_FfdR */ { D|rfp, 0, 0 },
431 /* Op_Ffd_P */ { D|mfp, 0, 0, FP_Types, Clb_ST, Next_Form, Op_FfdR_P, }, // pop, fld so also 80 bit, "
432 /* Op_FfdR_P */ { D|rfp, 0, 0, 0, Clb_ST },
433 /* Op_Fd_P */ { D|mem, 0, 0, 0, Clb_ST }, // "
434 /* Op_FdST */ { D|rfp, 0, 0 },
435 /* Op_FMath */ { mfp, 0, 0, FP_Types, Clb_ST, Next_Form, Op_FdSTiSTi }, // and only single or double prec
436 /* Op_FdSTiSTi */ { D|rfp, rfp, 0, },
437 /* Op_FPMath */ { D|rfp, rfp, 0, 0, Clb_ST, Next_Form, Op_F0_P }, // pops
438 /* Op_FCmp */ { mfp, 0, 0, FP_Types, 0, Next_Form, Op_FCmp1 }, // DMD defaults to float ptr
439 /* Op_FCmp1 */ { rfp, 0, 0, 0, 0, Next_Form, Op_0 },
440 /* Op_FCmpP */ { mfp, 0, 0, FP_Types, 0, Next_Form, Op_FCmpP1 }, // pops
441 /* Op_FCmpP1 */ { rfp, 0, 0, 0, 0, Next_Form, Op_F0_P }, // pops
442 /* Op_FCmpFlg */ { rfp, rfp, 0, 0, Clb_Flags },
443 /* Op_FCmpFlgP */ { rfp, rfp, 0, 0, Clb_Flags }, // pops
444 /* Op_fld */ { mfp, 0, 0, FP_Types, Clb_ST, Next_Form, Op_fldR },
445 /* Op_fldR */ { rfp, 0, 0, 0, Clb_ST },
446 /* Op_fxch */ { D|rfp,D|rfp, 0, 0, Clb_ST, Next_Form, Op_fxch1 }, // not in intel manual?, but DMD allows it (gas won't), second arg must be ST
447 /* Op_fxch1 */ { D|rfp, 0, 0, 0, Clb_ST, Next_Form, Op_fxch0 },
448 /* Op_fxch0 */ { 0, 0, 0, 0, Clb_ST }, // Also clobbers ST(1)
449 /* Op_SizedStack*/ { 0, 0, 0, 0, Clb_SP }, // type suffix special case
450 /* Op_bound */ { mr, mri, 0, Word_Types }, // operands *not* reversed for gas
451 /* Op_bswap */ { D|r32 },
452 /* Op_cmps */ { mem, mem, 0, 1, Clb_DI|Clb_SI|Clb_Flags },
453 /* Op_cmpsd */ { 0, 0, 0, 0, Clb_DI|Clb_SI|Clb_Flags, Next_Form, Op_DstSrcImmS },
454 /* Op_cmpsX */ { 0, 0, 0, 0, Clb_DI|Clb_SI|Clb_Flags },
455 /* Op_cmpxchg8b */ { D|mem/*64*/,0,0, 0, Clb_SizeDXAX/*32*/|Clb_Flags, Out_Mnemonic, Mn_cmpxchg8b },
456 /* Op_cmpxchg */ { D|mr, reg, 0, 1, Clb_SizeAX|Clb_Flags },
457 /* Op_cpuid */ { 0,0,0 }, // Clobbers eax, ebx, ecx, and edx. Handled specially below.
458 /* Op_enter */ { imm, imm }, // operands *not* reversed for gas, %% inform gcc of EBP clobber?,
459 /* Op_fdisi */ { 0,0,0, 0, 0, Out_Mnemonic, Mn_fdisi },
460 /* Op_feni */ { 0,0,0, 0, 0, Out_Mnemonic, Mn_feni },
461 /* Op_fsetpm */ { 0,0,0, 0, 0, Out_Mnemonic, Mn_fsetpm },
462 /* Op_fXstsw */ { D|mr, 0, 0, }, // ax is the only allowed register
463 /* Op_imul */ { D|reg, mr, imm, 1, Clb_Flags, Next_Form, Op_imul2 }, // 16/32 only
464 /* Op_imul2 */ { D|reg, mri, 0, 1, Clb_Flags, Next_Form, Op_imul1 }, // 16/32 only
465 /* Op_imul1 */ { mr, 0, 0, 1, Clb_Flags|Clb_SizeDXAX },
466 /* Op_in */ { D|ax,N|port,0, 1 },
467 /* Op_ins */ { mem,N|dx, 0, 1, Clb_DI }, // can't override ES segment for this one
468 /* Op_insX */ { 0, 0, 0, 0, Clb_DI }, // output segment overrides %% needs work
469 /* Op_iret */ { 0,0,0, 0, 0, Out_Mnemonic, Mn_iretw },
470 /* Op_iretd */ { 0,0,0, 0, 0, Out_Mnemonic, Mn_iret },
471 /* Op_lods */ { mem, 0, 0, 1, Clb_SI },
472 /* Op_lodsX */ { 0, 0, 0, 0, Clb_SI },
473 /* Op_movs */ { mem, mem, 0, 1, Clb_DI|Clb_SI }, // only src/DS can be overridden
474 /* Op_movsd */ { 0, 0, 0, 0, Clb_DI|Clb_SI, Next_Form, Op_DstSrcSSE }, // %% gas doesn't accept movsd .. has to movsl
475 /* Op_movsX */ { 0, 0, 0, 0, Clb_DI|Clb_SI },
476 /* Op_movsx */ { D|reg, mr, 0, 1 }, // type suffix is special case
477 /* Op_movzx */ { D|reg, mr, 0, 1 }, // type suffix is special case
478 /* Op_mul */ { U|ax, mr, 0, 1, Clb_SizeDXAX|Clb_Flags, Next_Form, Op_Src_DXAXF },
479 /* Op_out */ { N|port,ax, 0, 1 },
480 /* Op_outs */ { N|dx, mem, 0, 1, Clb_SI },
481 /* Op_outsX */ { 0, 0, 0, 0, Clb_SI },
482 /* Op_push */ { mri, 0, 0, Word_Types, Clb_SP }, // would be Op_SrcW, but DMD defaults to 32-bit for immediate form
483 /* Op_ret */ { imm, 0, 0, 0, 0, Next_Form, Op_0 },
484 /* Op_retf */ { 0, 0, 0, 0, 0, Out_Mnemonic, Mn_lret },
485 /* Op_scas */ { mem, 0, 0, 1, Clb_DI|Clb_Flags },
486 /* Op_scasX */ { 0, 0, 0, 0, Clb_DI|Clb_Flags },
487 /* Op_stos */ { mem, 0, 0, 1, Clb_DI },
488 /* Op_stosX */ { 0, 0, 0, 0, Clb_DI },
489 /* Op_xlat */ { mem, 0, 0, 0, Clb_SizeAX }
491 /// * Op_arpl */ { D|mr, reg }, // 16 only -> DstSrc
492 /// * Op_bsX */ { rw, mrw, 0, 1, Clb_Flags },//->srcsrcf
493 /// * Op_bt */ { mrw, riw, 0, 1, Clb_Flags },//->srcsrcf
494 /// * Op_btX */ { D|mrw, riw, 0, 1, Clb_Flags },//->dstsrcf .. immediate does not contribute to size
495 /// * Op_cmovCC */ { D|rw, mrw, 0, 1 } // ->dstsrc
498 #undef mri
499 #undef mr
500 #undef mem
501 #undef mfp
502 #undef reg
503 #undef imm
504 #undef sse
505 #undef ssem
506 #undef mmx
507 #undef mmxm
508 #undef r32
509 #undef rw
510 #undef rfp
511 #undef port
512 #undef ax
513 #undef dx
514 #undef shft
515 #undef D
516 #undef U
517 #undef N
518 //#undef L
520 typedef struct {
521 const char * inMnemonic;
522 AsmOp asmOp;
523 } AsmOpEnt;
525 /* Some opcodes which have data size restrictions, but we don't check
527 cmov, l<segreg> ?, lea, lsl, shld
529 todo: push <immediate> is always the 32-bit form, even tho push <mem> is 16-bit
532 static AsmOpEnt opData[] = {
533 { "aaa", Op_Adjust },
534 { "aad", Op_Adjust },
535 { "aam", Op_Adjust },
536 { "aas", Op_Adjust },
537 { "adc", Op_UpdSrcF },
538 { "add", Op_UpdSrcF },
539 { "addpd", Op_DstSrcSSE },
540 { "addps", Op_DstSrcSSE },
541 { "addsd", Op_DstSrcSSE },
542 { "addss", Op_DstSrcSSE },
543 { "addsubpd", Op_DstSrcSSE },
544 { "addsubps", Op_DstSrcSSE },
545 { "align", Op_Align },
546 { "and", Op_UpdSrcF },
547 { "andnpd", Op_DstSrcSSE },
548 { "andnps", Op_DstSrcSSE },
549 { "andpd", Op_DstSrcSSE },
550 { "andps", Op_DstSrcSSE },
551 { "arpl", Op_UpdSrcNT },
552 { "bound", Op_bound },
553 { "bsf", Op_SrcSrcFW },
554 { "bsr", Op_SrcSrcFW },
555 { "bswap", Op_bswap },
556 { "bt", Op_SrcSrcFW },
557 { "btc", Op_UpdSrcFW },
558 { "btr", Op_UpdSrcFW },
559 { "bts", Op_UpdSrcFW },
560 { "call", Op_Branch },
561 { "cbw", Op_0_AX },
562 { "cdq", Op_0_DXAX },
563 { "clc", Op_Flags },
564 { "cld", Op_Flags },
565 { "clflush",Op_SrcMemNT },
566 { "cli", Op_Flags },
567 { "clts", Op_0 },
568 { "cmc", Op_Flags },
569 { "cmova", Op_DstSrc },
570 { "cmovae", Op_DstSrc },
571 { "cmovb", Op_DstSrc },
572 { "cmovbe", Op_DstSrc },
573 { "cmovc", Op_DstSrc },
574 { "cmove", Op_DstSrc },
575 { "cmovg", Op_DstSrc },
576 { "cmovge", Op_DstSrc },
577 { "cmovl", Op_DstSrc },
578 { "cmovle", Op_DstSrc },
579 { "cmovna", Op_DstSrc },
580 { "cmovnae",Op_DstSrc },
581 { "cmovnb", Op_DstSrc },
582 { "cmovnbe",Op_DstSrc },
583 { "cmovnc", Op_DstSrc },
584 { "cmovne", Op_DstSrc },
585 { "cmovng", Op_DstSrc },
586 { "cmovnge",Op_DstSrc },
587 { "cmovnl", Op_DstSrc },
588 { "cmovnle",Op_DstSrc },
589 { "cmovno", Op_DstSrc },
590 { "cmovnp", Op_DstSrc },
591 { "cmovns", Op_DstSrc },
592 { "cmovnz", Op_DstSrc },
593 { "cmovo", Op_DstSrc },
594 { "cmovp", Op_DstSrc },
595 { "cmovpe", Op_DstSrc },
596 { "cmovpo", Op_DstSrc },
597 { "cmovs", Op_DstSrc },
598 { "cmovz", Op_DstSrc },
599 { "cmp", Op_SrcSrcF },
600 { "cmppd", Op_DstSrcImmS },
601 { "cmpps", Op_DstSrcImmS },
602 { "cmps", Op_cmps },
603 { "cmpsb", Op_cmpsX },
604 { "cmpsd", Op_cmpsd }, // string cmp, and SSE cmp
605 { "cmpss", Op_DstSrcImmS },
606 { "cmpsw", Op_cmpsX },
607 { "cmpxch8b", Op_cmpxchg8b }, // %% DMD opcode typo?
608 { "cmpxchg", Op_cmpxchg },
609 { "comisd", Op_SrcSrcSSEF },
610 { "comiss", Op_SrcSrcSSEF },
611 { "cpuid", Op_cpuid },
612 { "cvtdq2pd", Op_DstSrcSSE },
613 { "cvtdq2ps", Op_DstSrcSSE },
614 { "cvtpd2dq", Op_DstSrcSSE },
615 { "cvtpd2pi", Op_DstSrcSSE },
616 { "cvtpd2ps", Op_DstSrcSSE },
617 { "cvtpi2pd", Op_DstSrcSSE },
618 { "cvtpi2ps", Op_DstSrcSSE },
619 { "cvtps2dq", Op_DstSrcSSE },
620 { "cvtps2pd", Op_DstSrcSSE },
621 { "cvtps2pi", Op_DstSrcSSE },
622 { "cvtsd2si", Op_DstSrcSSE },
623 { "cvtsd2ss", Op_DstSrcSSE },
624 { "cvtsi2sd", Op_DstSrcSSE },
625 { "cvtsi2ss", Op_DstSrcSSE },
626 { "cvtss2sd", Op_DstSrcSSE },
627 { "cvtss2si", Op_DstSrcSSE },
628 { "cvttpd2dq", Op_DstSrcSSE },
629 { "cvttpd2pi", Op_DstSrcSSE },
630 { "cvttps2dq", Op_DstSrcSSE },
631 { "cvttps2pi", Op_DstSrcSSE },
632 { "cvttsd2si", Op_DstSrcSSE },
633 { "cvttss2si", Op_DstSrcSSE },
634 { "cwd", Op_0_DXAX },
635 { "cwde", Op_0_AX },
636 //{ "da", Op_ },// dunno what this is -- takes labels?
637 { "daa", Op_Adjust },
638 { "das", Op_Adjust },
639 { "db", Op_db },
640 { "dd", Op_dd },
641 { "de", Op_de },
642 { "dec", Op_UpdF },
643 { "df", Op_df },
644 { "di", Op_di },
645 { "div", Op_Src_DXAXF },
646 { "divpd", Op_DstSrcSSE },
647 { "divps", Op_DstSrcSSE },
648 { "divsd", Op_DstSrcSSE },
649 { "divss", Op_DstSrcSSE },
650 { "dl", Op_dl },
651 { "dq", Op_dl },
652 { "ds", Op_ds },
653 { "dt", Op_de },
654 { "dw", Op_ds },
655 { "emms", Op_0 }, // clobber all mmx/fp?
656 { "enter", Op_enter },
657 { "even", Op_Even },
658 { "f2xm1", Op_F0_ST }, // %% most of these are update...
659 { "fabs", Op_F0_ST },
660 { "fadd", Op_FMath },
661 { "faddp", Op_FPMath },
662 { "fbld", Op_Fs_P },
663 { "fbstp", Op_Fd_P },
664 { "fchs", Op_F0_ST },
665 { "fclex", Op_0 },
666 { "fcmovb", Op_FdSTiSTi }, // but only ST(0) can be the destination -- should be FdST0STi
667 { "fcmovbe", Op_FdSTiSTi },
668 { "fcmove", Op_FdSTiSTi },
669 { "fcmovnb", Op_FdSTiSTi },
670 { "fcmovnbe", Op_FdSTiSTi },
671 { "fcmovne", Op_FdSTiSTi },
672 { "fcmovnu", Op_FdSTiSTi },
673 { "fcmovu", Op_FdSTiSTi },
674 { "fcom", Op_FCmp },
675 { "fcomi", Op_FCmpFlg },
676 { "fcomip", Op_FCmpFlgP },
677 { "fcomp", Op_FCmpP },
678 { "fcompp", Op_F0_P }, // pops twice
679 { "fcos", Op_F0_ST },
680 { "fdecstp",Op_F0_P }, // changes stack
681 { "fdisi", Op_fdisi },
682 { "fdiv", Op_FMath },
683 { "fdivp", Op_FPMath },
684 { "fdivr", Op_FMath },
685 { "fdivrp", Op_FPMath },
686 { "feni", Op_feni },
687 { "ffree", Op_FdST },
688 { "fiadd", Op_Fis_ST },
689 { "ficom", Op_Fis },
690 { "ficomp", Op_Fis_P },
691 { "fidiv", Op_Fis_ST },
692 { "fidivr", Op_Fis_ST },
693 { "fild", Op_Fis_P },
694 { "fimul", Op_Fis_ST },
695 { "fincstp",Op_F0_P },
696 { "finit", Op_F0_P },
697 { "fist", Op_Fid }, // only 16,32bit
698 { "fistp", Op_Fid_P },
699 { "fisttp", Op_Fid_P },
700 { "fisub", Op_Fis_ST },
701 { "fisubr", Op_Fis_ST },
702 { "fld", Op_fld },
703 { "fld1", Op_F0_P },
704 { "fldcw", Op_SrcMemNT },
705 { "fldenv", Op_SrcMemNT },
706 { "fldl2e", Op_F0_P },
707 { "fldl2t", Op_F0_P },
708 { "fldlg2", Op_F0_P },
709 { "fldln2", Op_F0_P },
710 { "fldpi", Op_F0_P },
711 { "fldz", Op_F0_P },
712 { "fmul", Op_FMath },
713 { "fmulp", Op_FPMath },
714 { "fnclex", Op_0 },
715 { "fndisi", Op_fdisi }, // ??
716 { "fneni", Op_feni }, // ??
717 { "fninit", Op_0 },
718 { "fnop", Op_0 },
719 { "fnsave", Op_DstMemNT },
720 { "fnstcw", Op_DstMemNT },
721 { "fnstenv",Op_DstMemNT },
722 { "fnstsw", Op_fXstsw },
723 { "fpatan", Op_F0_P }, // pop and modify new ST
724 { "fprem", Op_F0_ST },
725 { "fprem1", Op_F0_ST },
726 { "fptan", Op_F0_P }, // modify ST and push 1.0
727 { "frndint",Op_F0_ST },
728 { "frstor", Op_SrcMemNT }, // but clobbers everything
729 { "fsave", Op_DstMemNT },
730 { "fscale", Op_F0_ST },
731 { "fsetpm", Op_fsetpm },
732 { "fsin", Op_F0_ST },
733 { "fsincos",Op_F0_P },
734 { "fsqrt", Op_F0_ST },
735 { "fst", Op_Ffd },
736 { "fstcw", Op_DstMemNT },
737 { "fstenv", Op_DstMemNT },
738 { "fstp", Op_Ffd_P },
739 { "fstsw", Op_fXstsw },
740 { "fsub", Op_FMath },
741 { "fsubp", Op_FPMath },
742 { "fsubr", Op_FMath },
743 { "fsubrp", Op_FPMath },
744 { "ftst", Op_0 },
745 { "fucom", Op_FCmp },
746 { "fucomi", Op_FCmpFlg },
747 { "fucomip",Op_FCmpFlgP },
748 { "fucomp", Op_FCmpP },
749 { "fucompp",Op_F0_P }, // pops twice
750 { "fwait", Op_0 },
751 { "fxam", Op_0 },
752 { "fxch", Op_fxch },
753 { "fxrstor",Op_SrcMemNT }, // clobbers FP,MMX,SSE
754 { "fxsave", Op_DstMemNT },
755 { "fxtract",Op_F0_P }, // pushes
756 { "fyl2x", Op_F0_P }, // pops
757 { "fyl2xp1",Op_F0_P }, // pops
758 { "haddpd", Op_DstSrcSSE },
759 { "haddps", Op_DstSrcSSE },
760 { "hlt", Op_0 },
761 { "hsubpd", Op_DstSrcSSE },
762 { "hsubps", Op_DstSrcSSE },
763 { "idiv", Op_Src_DXAXF },
764 { "imul", Op_imul },
765 { "in", Op_in },
766 { "inc", Op_UpdF },
767 { "ins", Op_ins },
768 { "insb", Op_insX },
769 { "insd", Op_insX },
770 { "insw", Op_insX },
771 { "int", Op_SrcImm },
772 { "into", Op_0 },
773 { "invd", Op_0 },
774 { "invlpg", Op_SrcMemNT },
775 { "iret", Op_iret },
776 { "iretd", Op_iretd },
777 { "ja", Op_CBranch },
778 { "jae", Op_CBranch },
779 { "jb", Op_CBranch },
780 { "jbe", Op_CBranch },
781 { "jc", Op_CBranch },
782 { "jcxz", Op_CBranch },
783 { "je", Op_CBranch },
784 { "jecxz", Op_CBranch },
785 { "jg", Op_CBranch },
786 { "jge", Op_CBranch },
787 { "jl", Op_CBranch },
788 { "jle", Op_CBranch },
789 { "jmp", Op_Branch },
790 { "jna", Op_CBranch },
791 { "jnae", Op_CBranch },
792 { "jnb", Op_CBranch },
793 { "jnbe", Op_CBranch },
794 { "jnc", Op_CBranch },
795 { "jne", Op_CBranch },
796 { "jng", Op_CBranch },
797 { "jnge", Op_CBranch },
798 { "jnl", Op_CBranch },
799 { "jnle", Op_CBranch },
800 { "jno", Op_CBranch },
801 { "jnp", Op_CBranch },
802 { "jns", Op_CBranch },
803 { "jnz", Op_CBranch },
804 { "jo", Op_CBranch },
805 { "jp", Op_CBranch },
806 { "jpe", Op_CBranch },
807 { "jpo", Op_CBranch },
808 { "js", Op_CBranch },
809 { "jz", Op_CBranch },
810 { "lahf", Op_0_AX },
811 { "lar", Op_DstSrcFW }, // reg dest only
812 { "lddqu", Op_DstSrcSSE },
813 { "ldmxcsr", Op_SrcMemNT },
814 { "lds", Op_DstSrc }, // reg dest only
815 { "lea", Op_DstSrc }, // "
816 { "leave", Op_0 }, // EBP,ESP clobbers
817 { "les", Op_DstSrc },
818 { "lfence",Op_0 },
819 { "lfs", Op_DstSrc },
820 { "lgdt", Op_SrcMemNT },
821 { "lgs", Op_DstSrc },
822 { "lidt", Op_SrcMemNT },
823 { "lldt", Op_SrcRMWNT },
824 { "lmsw", Op_SrcRMWNT },
825 { "lock", Op_0 },
826 { "lods", Op_lods },
827 { "lodsb", Op_lodsX },
828 { "lodsd", Op_lodsX },
829 { "lodsw", Op_lodsX },
830 { "loop", Op_Loop },
831 { "loope", Op_Loop },
832 { "loopne",Op_Loop },
833 { "loopnz",Op_Loop },
834 { "loopz", Op_Loop },
835 { "lsl", Op_DstSrcFW }, // reg dest only
836 { "lss", Op_DstSrc },
837 { "ltr", Op_DstMemNT },
838 { "maskmovdqu", Op_SrcSrcMMX }, // writes to [edi]
839 { "maskmovq", Op_SrcSrcMMX },
840 { "maxpd", Op_DstSrcSSE },
841 { "maxps", Op_DstSrcSSE },
842 { "maxsd", Op_DstSrcSSE },
843 { "maxss", Op_DstSrcSSE },
844 { "mfence",Op_0},
845 { "minpd", Op_DstSrcSSE },
846 { "minps", Op_DstSrcSSE },
847 { "minsd", Op_DstSrcSSE },
848 { "minss", Op_DstSrcSSE },
849 { "monitor", Op_0 },
850 { "mov", Op_DstSrc },
851 { "movapd", Op_DstSrcSSE },
852 { "movaps", Op_DstSrcSSE },
853 { "movd", Op_DstSrcNT }, // also mmx and sse
854 { "movddup", Op_DstSrcSSE },
855 { "movdq2q", Op_DstSrcNT }, // mmx/sse
856 { "movdqa", Op_DstSrcSSE },
857 { "movdqu", Op_DstSrcSSE },
858 { "movhlps", Op_DstSrcSSE },
859 { "movhpd", Op_DstSrcSSE },
860 { "movhps", Op_DstSrcSSE },
861 { "movlhps", Op_DstSrcSSE },
862 { "movlpd", Op_DstSrcSSE },
863 { "movlps", Op_DstSrcSSE },
864 { "movmskpd",Op_DstSrcSSE },
865 { "movmskps",Op_DstSrcSSE },
866 { "movntdq", Op_DstSrcNT }, // limited to sse, but mem dest
867 { "movnti", Op_DstSrcNT }, // limited to gpr, but mem dest
868 { "movntpd", Op_DstSrcNT }, // limited to sse, but mem dest
869 { "movntps", Op_DstSrcNT }, // limited to sse, but mem dest
870 { "movntq", Op_DstSrcNT }, // limited to mmx, but mem dest
871 { "movq", Op_DstSrcNT }, // limited to sse and mmx
872 { "movq2dq", Op_DstSrcNT }, // limited to sse <- mmx regs
873 { "movs", Op_movs },
874 { "movsb", Op_movsX },
875 { "movsd", Op_movsd },
876 { "movshdup", Op_DstSrcSSE },
877 { "movsldup", Op_DstSrcSSE },
878 { "movss", Op_DstSrcSSE },
879 { "movsw", Op_movsX },
880 { "movsx", Op_movsx }, // word-only, reg dest
881 { "movupd",Op_DstSrcSSE },
882 { "movups",Op_DstSrcSSE },
883 { "movzx", Op_movzx },
884 { "mul", Op_mul },
885 { "mulpd", Op_DstSrcSSE },
886 { "mulps", Op_DstSrcSSE },
887 { "mulsd", Op_DstSrcSSE },
888 { "mulss", Op_DstSrcSSE },
889 { "mwait", Op_0 },
890 { "naked", Op_Naked },
891 { "neg", Op_UpdF },
892 { "nop", Op_0 },
893 { "not", Op_Upd },
894 { "or", Op_UpdSrcF },
895 { "orpd", Op_DstSrcSSE },
896 { "orps", Op_DstSrcSSE },
897 { "out", Op_out },
898 { "outs", Op_outs },
899 { "outsb", Op_outsX },
900 { "outsd", Op_outsX },
901 { "outsw", Op_outsX },
902 { "packssdw", Op_DstSrcMMX }, // %% also SSE
903 { "packsswb", Op_DstSrcMMX },
904 { "packuswb", Op_DstSrcMMX },
905 { "paddb", Op_DstSrcMMX },
906 { "paddd", Op_DstSrcMMX },
907 { "paddq", Op_DstSrcMMX },
908 { "paddsb", Op_DstSrcMMX },
909 { "paddsw", Op_DstSrcMMX },
910 { "paddusb", Op_DstSrcMMX },
911 { "paddusw", Op_DstSrcMMX },
912 { "paddw", Op_DstSrcMMX },
913 { "pand", Op_DstSrcMMX },
914 { "pandn", Op_DstSrcMMX },
915 { "pavgb", Op_DstSrcMMX },
916 { "pavgw", Op_DstSrcMMX },
917 { "pcmpeqb", Op_DstSrcMMX },
918 { "pcmpeqd", Op_DstSrcMMX },
919 { "pcmpeqw", Op_DstSrcMMX },
920 { "pcmpgtb", Op_DstSrcMMX },
921 { "pcmpgtd", Op_DstSrcMMX },
922 { "pcmpgtw", Op_DstSrcMMX },
923 { "pextrw", Op_DstSrcImmM }, // gpr32 dest
924 { "pinsrw", Op_DstSrcImmM }, // gpr32(16), mem16 src, sse too
925 { "pmaddwd", Op_DstSrcMMX },
926 { "pmaxsw", Op_DstSrcMMX },
927 { "pmaxub", Op_DstSrcMMX },
928 { "pminsw", Op_DstSrcMMX },
929 { "pminub", Op_DstSrcMMX },
930 { "pmovmskb", Op_DstSrcMMX },
931 { "pmulhuw", Op_DstSrcMMX },
932 { "pmulhw", Op_DstSrcMMX },
933 { "pmullw", Op_DstSrcMMX },
934 { "pmuludq", Op_DstSrcMMX }, // also sse
935 { "pop", Op_DstW },
936 { "popa", Op_SizedStack }, // For intel this is always 16-bit
937 { "popad", Op_SizedStack }, // GAS doesn't accept 'popad' -- these clobber everything, but supposedly it would be used to preserve clobbered regs
938 { "popf", Op_SizedStack }, // rewrite the insn with a special case
939 { "popfd", Op_SizedStack },
940 { "por", Op_DstSrcMMX },
941 { "prefetchnta", Op_SrcMemNT },
942 { "prefetcht0", Op_SrcMemNT },
943 { "prefetcht1", Op_SrcMemNT },
944 { "prefetcht2", Op_SrcMemNT },
945 { "psadbw", Op_DstSrcMMX },
946 { "pshufd", Op_DstSrcImmM },
947 { "pshufhw", Op_DstSrcImmM },
948 { "pshuflw", Op_DstSrcImmM },
949 { "pshufw", Op_DstSrcImmM },
950 { "pslld", Op_DstSrcMMX }, // immediate operands...
951 { "pslldq", Op_DstSrcMMX },
952 { "psllq", Op_DstSrcMMX },
953 { "psllw", Op_DstSrcMMX },
954 { "psrad", Op_DstSrcMMX },
955 { "psraw", Op_DstSrcMMX },
956 { "psrld", Op_DstSrcMMX },
957 { "psrldq", Op_DstSrcMMX },
958 { "psrlq", Op_DstSrcMMX },
959 { "psrlw", Op_DstSrcMMX },
960 { "psubb", Op_DstSrcMMX },
961 { "psubd", Op_DstSrcMMX },
962 { "psubq", Op_DstSrcMMX },
963 { "psubsb", Op_DstSrcMMX },
964 { "psubsw", Op_DstSrcMMX },
965 { "psubusb", Op_DstSrcMMX },
966 { "psubusw", Op_DstSrcMMX },
967 { "psubw", Op_DstSrcMMX },
968 { "punpckhbw", Op_DstSrcMMX },
969 { "punpckhdq", Op_DstSrcMMX },
970 { "punpckhqdq",Op_DstSrcMMX },
971 { "punpckhwd", Op_DstSrcMMX },
972 { "punpcklbw", Op_DstSrcMMX },
973 { "punpckldq", Op_DstSrcMMX },
974 { "punpcklqdq",Op_DstSrcMMX },
975 { "punpcklwd", Op_DstSrcMMX },
976 { "push", Op_push },
977 { "pusha", Op_SizedStack },
978 { "pushad", Op_SizedStack },
979 { "pushf", Op_SizedStack },
980 { "pushfd", Op_SizedStack },
981 { "pxor", Op_DstSrcMMX },
982 { "rcl", Op_Shift }, // limited src operands -- change to shift
983 { "rcpps", Op_DstSrcSSE },
984 { "rcpss", Op_DstSrcSSE },
985 { "rcr", Op_Shift },
986 { "rdmsr", Op_0_DXAX },
987 { "rdpmc", Op_0_DXAX },
988 { "rdtsc", Op_0_DXAX },
989 { "rep", Op_0 },
990 { "repe", Op_0 },
991 { "repne", Op_0 },
992 { "repnz", Op_0 },
993 { "repz", Op_0 },
994 { "ret", Op_ret },
995 { "retf", Op_retf },
996 { "rol", Op_Shift },
997 { "ror", Op_Shift },
998 { "rsm", Op_0 },
999 { "rsqrtps", Op_DstSrcSSE },
1000 { "rsqrtss", Op_DstSrcSSE },
1001 { "sahf", Op_Flags },
1002 { "sal", Op_Shift },
1003 { "sar", Op_Shift },
1004 { "sbb", Op_UpdSrcF },
1005 { "scas", Op_scas },
1006 { "scasb", Op_scasX },
1007 { "scasd", Op_scasX },
1008 { "scasw", Op_scasX },
1009 { "seta", Op_DstRMBNT }, // also gpr8
1010 { "setae", Op_DstRMBNT },
1011 { "setb", Op_DstRMBNT },
1012 { "setbe", Op_DstRMBNT },
1013 { "setc", Op_DstRMBNT },
1014 { "sete", Op_DstRMBNT },
1015 { "setg", Op_DstRMBNT },
1016 { "setge", Op_DstRMBNT },
1017 { "setl", Op_DstRMBNT },
1018 { "setle", Op_DstRMBNT },
1019 { "setna", Op_DstRMBNT },
1020 { "setnae", Op_DstRMBNT },
1021 { "setnb", Op_DstRMBNT },
1022 { "setnbe", Op_DstRMBNT },
1023 { "setnc", Op_DstRMBNT },
1024 { "setne", Op_DstRMBNT },
1025 { "setng", Op_DstRMBNT },
1026 { "setnge", Op_DstRMBNT },
1027 { "setnl", Op_DstRMBNT },
1028 { "setnle", Op_DstRMBNT },
1029 { "setno", Op_DstRMBNT },
1030 { "setnp", Op_DstRMBNT },
1031 { "setns", Op_DstRMBNT },
1032 { "setnz", Op_DstRMBNT },
1033 { "seto", Op_DstRMBNT },
1034 { "setp", Op_DstRMBNT },
1035 { "setpe", Op_DstRMBNT },
1036 { "setpo", Op_DstRMBNT },
1037 { "sets", Op_DstRMBNT },
1038 { "setz", Op_DstRMBNT },
1039 { "sfence", Op_0 },
1040 { "sgdt", Op_DstMemNT },
1041 { "shl", Op_Shift },
1042 { "shld", Op_UpdSrcShft },
1043 { "shr", Op_Shift },
1044 { "shrd", Op_UpdSrcShft },
1045 { "shufpd", Op_DstSrcImmS },
1046 { "shufps", Op_DstSrcImmS },
1047 { "sidt", Op_DstMemNT },
1048 { "sldt", Op_DstRMWNT },
1049 { "smsw", Op_DstRMWNT },
1050 { "sqrtpd", Op_DstSrcSSE },
1051 { "sqrtps", Op_DstSrcSSE },
1052 { "sqrtsd", Op_DstSrcSSE },
1053 { "sqrtss", Op_DstSrcSSE },
1054 { "stc", Op_Flags },
1055 { "std", Op_Flags },
1056 { "sti", Op_Flags },
1057 { "stmxcsr",Op_DstMemNT },
1058 { "stos", Op_stos },
1059 { "stosb", Op_stosX },
1060 { "stosd", Op_stosX },
1061 { "stosw", Op_stosX },
1062 { "str", Op_DstMemNT }, // also r16
1063 { "sub", Op_UpdSrcF },
1064 { "subpd", Op_DstSrcSSE },
1065 { "subps", Op_DstSrcSSE },
1066 { "subsd", Op_DstSrcSSE },
1067 { "subss", Op_DstSrcSSE },
1068 { "sysenter",Op_0 },
1069 { "sysexit", Op_0 },
1070 { "test", Op_SrcSrcF },
1071 { "ucomisd", Op_SrcSrcSSEF },
1072 { "ucomiss", Op_SrcSrcSSEF },
1073 { "ud2", Op_0 },
1074 { "unpckhpd", Op_DstSrcSSE },
1075 { "unpckhps", Op_DstSrcSSE },
1076 { "unpcklpd", Op_DstSrcSSE },
1077 { "unpcklps", Op_DstSrcSSE },
1078 { "verr", Op_SrcMemNTF },
1079 { "verw", Op_SrcMemNTF },
1080 { "wait", Op_0 },
1081 { "wbinvd", Op_0 },
1082 { "wrmsr", Op_0 },
1083 { "xadd", Op_UpdUpdF },
1084 { "xchg", Op_UpdUpd },
1085 { "xlat", Op_xlat },
1086 { "xlatb", Op_0_AX },
1087 { "xor", Op_DstSrcF },
1088 { "xorpd", Op_DstSrcSSE },
1089 { "xorps", Op_DstSrcSSE },
1092 typedef enum {
1093 Default_Ptr = 0,
1094 Byte_Ptr = 1,
1095 Short_Ptr = 2,
1096 Int_Ptr = 4,
1097 QWord_Ptr = 8,
1098 Float_Ptr = 4,
1099 Double_Ptr = 8,
1100 Extended_Ptr = 10,
1101 Near_Ptr = 98,
1102 Far_Ptr = 99,
1103 N_PtrTypes
1104 } PtrType;
1106 static const int N_PtrNames = 8;
1107 static const char * ptrTypeNameTable[N_PtrNames] = {
1108 "word", "dword", "qword",
1109 "float", "double", "extended",
1110 "near", "far"
1113 static Identifier * ptrTypeIdentTable[N_PtrNames];
1114 static PtrType ptrTypeValueTable[N_PtrNames] = {
1115 Short_Ptr, Int_Ptr, QWord_Ptr,
1116 Float_Ptr, Double_Ptr, Extended_Ptr,
1117 Near_Ptr, Far_Ptr
1120 typedef enum {
1121 Opr_Invalid,
1122 Opr_Immediate,
1123 Opr_Reg,
1124 Opr_Mem
1125 } OperandClass;
1127 /* kill inlining if we reference a local? */
1129 /* DMD seems to allow only one 'symbol' per operand .. include __LOCAL_SIZE */
1131 /* DMD offset usage: <parm>[<reg>] seems to always be relative to EBP+8 .. even
1132 if naked.. */
1134 // mov eax, 4
1135 // mov eax, fs:4
1136 // -- have to assume we know wheter or not to use '$'
1138 static Token eof_tok;
1139 static Expression * Handled;
1140 static Identifier * ident_seg;
1142 struct AsmProcessor
1144 typedef struct {
1145 int inBracket;
1146 int hasBracket;
1147 int hasNumber;
1148 int isOffset;
1150 Reg segmentPrefix;
1151 Reg reg;
1152 sinteger_t constDisplacement; // use to build up.. should be int constant in the end..
1153 Array symbolDisplacement; // array of expressions or..
1154 Reg baseReg;
1155 Reg indexReg;
1156 int scale;
1158 OperandClass cls;
1159 PtrType dataSize;
1160 PtrType dataSizeHint; // DMD can use the type of a referenced variable
1161 } Operand;
1163 static const unsigned Max_Operands = 3;
1165 AsmStatement * stmt;
1166 Scope * sc;
1168 Token * token;
1169 OutBuffer * insnTemplate;
1171 AsmOp op;
1172 AsmOpInfo * opInfo;
1173 Operand operands[Max_Operands];
1174 Identifier * opIdent;
1175 Operand * operand;
1177 AsmProcessor(Scope * sc, AsmStatement * stmt)
1179 this->sc = sc;
1180 this->stmt = stmt;
1181 token = stmt->tokens;
1182 insnTemplate = new OutBuffer;
1184 opInfo = NULL;
1186 if ( ! regInfo[0].ident ) {
1187 char buf[8], *p;
1189 for (int i = 0; i < N_Regs; i++) {
1190 strncpy(buf, regInfo[i].name, sizeof(buf) - 1);
1191 for (p = buf; *p; p++)
1192 *p = TOLOWER(*p);
1193 regInfo[i].gccName = build_string(p - buf, buf);
1194 dkeep(regInfo[i].gccName);
1195 if ( (i <= Reg_ST || i > Reg_ST7) && i != Reg_EFLAGS )
1196 regInfo[i].ident = Lexer::idPool(regInfo[i].name);
1199 for (int i = 0; i < N_PtrNames; i++)
1200 ptrTypeIdentTable[i] = Lexer::idPool(ptrTypeNameTable[i]);
1202 Handled = new Expression(0, TOKvoid, sizeof(Expression));
1204 ident_seg = Lexer::idPool("seg");
1206 eof_tok.value = TOKeof;
1207 eof_tok.next = 0;
1211 void run()
1213 parse();
1216 void nextToken() {
1217 if (token->next)
1218 token = token->next;
1219 else
1220 token = & eof_tok;
1223 Token * peekToken() {
1224 if (token->next)
1225 return token->next;
1226 else
1227 return & eof_tok;
1230 void expectEnd() {
1231 if (token->value != TOKeof)
1232 stmt->error("expected end of statement"); // %% extra at end...
1235 void parse() {
1236 op = parseOpcode();
1238 switch (op) {
1239 case Op_Align:
1240 doAlign();
1241 expectEnd();
1242 break;
1243 case Op_Even:
1244 doEven();
1245 expectEnd();
1246 break;
1247 case Op_Naked:
1248 doNaked();
1249 expectEnd();
1250 break;
1251 case Op_Invalid:
1252 break;
1253 default:
1254 if (op >= Op_db && op <= Op_de)
1255 doData();
1256 else
1257 doInstruction();
1261 AsmOp parseOpcode() {
1262 static const int N_ents = sizeof(opData)/sizeof(AsmOpEnt);
1264 switch (token->value) {
1265 case TOKalign:
1266 nextToken();
1267 return Op_Align;
1268 case TOKin:
1269 nextToken();
1270 opIdent = Id::___in;
1271 return Op_in;
1272 case TOKint32: // "int"
1273 nextToken();
1274 opIdent = Id::__int;
1275 return Op_SrcImm;
1276 case TOKout:
1277 nextToken();
1278 opIdent = Id::___out;
1279 return Op_out;
1280 case TOKidentifier:
1281 // search for mnemonic below
1282 break;
1283 default:
1284 stmt->error("expected opcode");
1285 return Op_Invalid;
1288 opIdent = token->ident;
1289 const char * opcode = token->ident->string;
1291 nextToken();
1293 // %% okay to use bsearch?
1294 int i = 0, j = N_ents, k, l;
1295 do {
1296 k = (i + j) / 2;
1297 l = strcmp(opcode, opData[k].inMnemonic);
1298 if (! l)
1299 return opData[k].asmOp;
1300 else if (l < 0)
1301 j = k;
1302 else
1303 i = k + 1;
1304 } while (i != j);
1306 stmt->error("unknown opcode '%s'", opcode);
1308 return Op_Invalid;
1311 // need clobber information.. use information is good too...
1312 void doInstruction() {
1313 bool ok = true;
1314 unsigned operand_i = 0;
1316 opInfo = & asmOpInfo[op];
1317 memset(operands, 0, sizeof(operands));
1319 while (token->value != TOKeof) {
1320 if (operand_i < Max_Operands) {
1321 operand = & operands[operand_i];
1322 operand->reg = operand->baseReg = operand->indexReg =
1323 operand->segmentPrefix = Reg_Invalid;
1324 parseOperand();
1325 operand_i++;
1326 } else {
1327 ok = false;
1328 stmt->error("too many operands for instruction");
1329 break;
1332 if (token->value == TOKcomma)
1333 nextToken();
1334 else if (token->value != TOKeof) {
1335 ok = false;
1336 stmt->error("expected comma after operand");
1337 return;
1340 // if (operand_i < opInfo->minOperands) {
1341 // ok = false;
1342 // stmt->error("too few operands for instruction");
1343 // }
1345 if ( matchOperands(operand_i) ) {
1346 AsmCode * asmcode = new AsmCode;
1348 if (formatInstruction(operand_i, asmcode))
1349 stmt->asmcode = (code *) asmcode;
1353 void setAsmCode() {
1354 AsmCode * asmcode = new AsmCode;
1355 asmcode->insnTemplateLen = insnTemplate->offset;
1356 asmcode->insnTemplate = (char*) insnTemplate->extractData();
1357 stmt->asmcode = (code*) asmcode;
1360 // note: doesn't update AsmOp op
1361 bool matchOperands(unsigned nOperands) {
1362 bool wrong_number = true;
1364 for (unsigned i = 0; i < nOperands; i++)
1365 classifyOperand(& operands[i]);
1367 while (1) {
1368 if (nOperands == opInfo->nOperands()) {
1369 wrong_number = false;
1370 /* Cases in which number of operands is not
1371 enough for a match: Op_FCmp/Op_FCmp1,
1372 Op_FCmpP/Op_FCmpP1 */
1373 for (unsigned i = 0; i < nOperands; i++) {
1374 Operand * operand = & operands[i];
1376 switch (opInfo->operands[i] & Opr_ClassMask) {
1377 case OprC_Mem: // no FPMem currently
1378 if (operand->cls != Opr_Mem)
1379 goto no_match;
1380 break;
1381 case OprC_RFP:
1382 if (! (operand->reg >= Reg_ST && operand->reg <= Reg_ST7))
1383 goto no_match;
1384 break;
1385 default:
1386 break;
1390 return true;
1392 no_match:
1393 if (opInfo->linkType == Next_Form)
1394 opInfo = & asmOpInfo[ op = (AsmOp) opInfo->link ];
1395 else
1396 break;
1398 if (wrong_number)
1399 stmt->error("wrong number of operands");
1400 else
1401 stmt->error("wrong operand types");
1402 return false;
1405 void addOperand(const char * fmt, AsmArgType type, Expression * e, AsmCode * asmcode, AsmArgMode mode = Mode_Input) {
1406 insnTemplate->writestring((char*) fmt);
1407 insnTemplate->printf("%d", asmcode->args.dim);
1408 asmcode->args.push( new AsmArg(type, e, mode) );
1411 void addLabel(unsigned n) {
1412 // No longer taking the address of the actual label -- doesn't seem like it would help.
1413 char buf[64];
1415 d_format_priv_asm_label(buf, n);
1416 insnTemplate->writestring(buf);
1419 /* Determines whether the operand is a register, memory reference
1420 or immediate. Immediate addresses are currently classified as
1421 memory. This function is called before the exact instructions
1422 is known and thus, should not use opInfo. */
1423 void classifyOperand(Operand * operand) {
1424 operand->cls = classifyOperand1(operand);
1427 OperandClass classifyOperand1(Operand * operand) {
1428 bool is_localsize = false;
1429 bool really_have_symbol = false;
1431 if (operand->symbolDisplacement.dim) {
1432 is_localsize = isLocalSize( (Expression *) operand->symbolDisplacement.data[0] );
1433 really_have_symbol = ! is_localsize;
1436 if (operand->isOffset && ! operand->hasBracket)
1437 return Opr_Immediate;
1439 if (operand->hasBracket || really_have_symbol) { // %% redo for 'offset' function
1440 if (operand->reg != Reg_Invalid) {
1441 invalidExpression();
1442 return Opr_Invalid;
1445 return Opr_Mem;
1448 if (operand->reg != Reg_Invalid && operand->constDisplacement != 0) {
1449 invalidExpression();
1450 return Opr_Invalid;
1453 if (operand->segmentPrefix != Reg_Invalid) {
1454 if (operand->reg != Reg_Invalid) {
1455 invalidExpression();
1456 return Opr_Invalid;
1459 return Opr_Mem;
1462 if (operand->reg != Reg_Invalid && ! operand->hasNumber)
1463 return Opr_Reg;
1465 // should check immediate given (operand->hasNumber);
1467 if (operand->hasNumber || is_localsize) {
1468 // size determination not correct if there are symbols Opr_Immediate
1469 if (operand->dataSize == Default_Ptr) {
1470 if (operand->constDisplacement < 0x100)
1471 operand->dataSize = Byte_Ptr;
1472 else if (operand->constDisplacement < 0x10000)
1473 operand->dataSize = Short_Ptr;
1474 else
1475 operand->dataSize = Int_Ptr;
1477 return Opr_Immediate;
1480 // probably a bug,?
1481 stmt->error("invalid operand");
1482 return Opr_Invalid;
1485 void writeReg(Reg reg) {
1486 insnTemplate->writestring((char*) "%%");
1487 insnTemplate->write(TREE_STRING_POINTER( regInfo[reg].gccName ),
1488 TREE_STRING_LENGTH( regInfo[reg].gccName ));
1491 bool opTakesLabel() {
1492 switch(op) {
1493 case Op_Branch:
1494 case Op_CBranch:
1495 case Op_Loop:
1496 return true;
1497 default:
1498 return false;
1502 bool getTypeChar(TypeNeeded needed, PtrType ptrtype, char & type_char)
1504 switch (needed) {
1505 case Byte_NoType:
1506 return ptrtype == Byte_Ptr;
1507 case Word_Types:
1508 if (ptrtype == Byte_Ptr)
1509 return false;
1510 // drop through
1511 case Int_Types:
1512 switch (ptrtype) {
1513 case Byte_Ptr: type_char = 'b'; break;
1514 case Short_Ptr: type_char = 'w'; break;
1515 case Int_Ptr: type_char = 'l'; break;
1516 default:
1517 // %% these may be too strict
1518 return false;
1520 break;
1521 case FPInt_Types:
1522 switch (ptrtype) {
1523 case Short_Ptr: type_char = 0; break;
1524 case Int_Ptr: type_char = 'l'; break;
1525 case QWord_Ptr: type_char = 'q'; break;
1526 default:
1527 return false;
1529 break;
1530 case FP_Types:
1531 switch (ptrtype) {
1532 case Float_Ptr: type_char = 's'; break;
1533 case Double_Ptr: type_char = 'l'; break;
1534 case Extended_Ptr: type_char = 't'; break;
1535 default:
1536 return false;
1538 break;
1539 default:
1540 return false;
1542 return true;
1545 // also set impl clobbers
1546 bool formatInstruction(int nOperands, AsmCode * asmcode) {
1547 const char *fmt;
1548 const char *mnemonic;
1549 char type_char = 0;
1550 bool use_star;
1551 AsmArgMode mode;
1553 insnTemplate = new OutBuffer;
1554 if (opInfo->linkType == Out_Mnemonic)
1555 mnemonic = alternateMnemonics[opInfo->link];
1556 else
1557 mnemonic = opIdent->string;
1559 if (opInfo->needsType) {
1560 PtrType exact_type = Default_Ptr;
1561 PtrType min_type = Default_Ptr;
1562 PtrType hint_type = Default_Ptr;
1564 /* Default types: This attempts to match the observed behavior of DMD */
1565 switch (opInfo->needsType) {
1566 case Int_Types: min_type = Byte_Ptr; break;
1567 case Word_Types: min_type = Short_Ptr; break;
1568 case FPInt_Types:
1569 if (op == Op_Fis_ST) // integer math instructions
1570 min_type = Int_Ptr;
1571 else // compare, load, store
1572 min_type = Short_Ptr;
1573 break;
1574 case FP_Types: min_type = Float_Ptr; break;
1576 if (op == Op_push && operands[0].cls == Opr_Immediate)
1577 min_type = Int_Ptr;
1579 for (int i = 0; i < nOperands; i++) {
1580 if (hint_type == Default_Ptr &&
1581 ! (opInfo->operands[i] & Opr_NoType))
1582 hint_type = operands[i].dataSizeHint;
1584 if ((opInfo->operands[i] & Opr_NoType) ||
1585 operands[i].dataSize == Default_Ptr)
1586 continue;
1587 if (operands[i].cls == Opr_Immediate) {
1588 min_type = operands[i].dataSize > min_type ?
1589 operands[i].dataSize : min_type;
1590 } else {
1591 exact_type = operands[i].dataSize; // could check for conflicting types
1592 break;
1596 bool type_ok;
1597 if (exact_type == Default_Ptr) {
1598 type_ok = getTypeChar((TypeNeeded) opInfo->needsType, hint_type, type_char);
1599 if (! type_ok)
1600 type_ok = getTypeChar((TypeNeeded) opInfo->needsType, min_type, type_char);
1601 } else
1602 type_ok = getTypeChar((TypeNeeded) opInfo->needsType, exact_type, type_char);
1604 if (! type_ok) {
1605 stmt->error("invalid operand size");
1606 return false;
1608 } else if (op == Op_Branch) {
1609 if (operands[0].dataSize == Far_Ptr) // %% type=Far_Ptr not set by Seg:Ofss OTOH, we don't support that..
1610 insnTemplate->writebyte('l');
1611 } else if (op == Op_fxch) {
1612 // gas won't accept the two-operand form
1613 if (operands[1].cls == Opr_Reg && operands[1].reg == Reg_ST) {
1614 nOperands = 1;
1615 } else {
1616 stmt->error("invalid operands");
1617 return false;
1621 switch (op) {
1622 case Op_SizedStack:
1624 int mlen = strlen(mnemonic);
1625 if (mnemonic[mlen-1] == 'd')
1626 insnTemplate->write(mnemonic, mlen-1);
1627 else {
1628 insnTemplate->writestring((char*) mnemonic);
1629 insnTemplate->writebyte('w');
1632 break;
1633 case Op_cmpsd:
1634 case Op_insX:
1635 case Op_lodsX:
1636 case Op_movsd:
1637 case Op_outsX:
1638 case Op_scasX:
1639 case Op_stosX:
1641 int mlen = strlen(mnemonic);
1642 if (mnemonic[mlen-1] == 'd') {
1643 insnTemplate->write(mnemonic, mlen-1);
1644 insnTemplate->writebyte('l');
1645 } else {
1646 insnTemplate->writestring((char*) mnemonic);
1649 break;
1650 case Op_movsx:
1651 case Op_movzx:
1653 char tc_1;
1654 int mlen = strlen(mnemonic);
1655 PtrType op1_size = operands[1].dataSize;
1656 if (op1_size == Default_Ptr)
1657 op1_size = operands[1].dataSizeHint;
1658 // Need type char for source arg
1659 switch (op1_size) {
1660 case Byte_Ptr:
1661 case Default_Ptr:
1662 tc_1 = 'b';
1663 break;
1664 case Short_Ptr:
1665 tc_1 = 'w';
1666 break;
1667 default:
1668 stmt->error("invalid operand size/type");
1669 return false;
1671 assert(type_char != 0);
1672 insnTemplate->write(mnemonic, mlen-1);
1673 insnTemplate->writebyte(tc_1);
1674 insnTemplate->writebyte(type_char);
1676 break;
1677 default:
1678 insnTemplate->writestring((char*) mnemonic);
1679 if (type_char)
1680 insnTemplate->writebyte(type_char);
1681 break;
1684 switch (opInfo->implicitClobbers & Clb_DXAX_Mask) {
1685 case Clb_SizeAX:
1686 case Clb_EAX:
1687 stmt->regs |= (1<<Reg_EAX);
1688 break;
1689 case Clb_SizeDXAX:
1690 stmt->regs |= (1<<Reg_EAX);
1691 if (type_char != 'b')
1692 stmt->regs |= (1<<Reg_EDX);
1693 break;
1694 default:
1695 // nothing
1696 break;
1699 if (opInfo->implicitClobbers & Clb_DI)
1700 stmt->regs |= (1 << Reg_EDI);
1701 if (opInfo->implicitClobbers & Clb_SI)
1702 stmt->regs |= (1 << Reg_ESI);
1703 if (opInfo->implicitClobbers & Clb_CX)
1704 stmt->regs |= (1 << Reg_ECX);
1705 if (opInfo->implicitClobbers & Clb_SP)
1706 stmt->regs |= (1 << Reg_ESP);
1707 if (opInfo->implicitClobbers & Clb_ST)
1709 /* Can't figure out how to tell GCC that an
1710 asm statement leaves an arg pushed on the stack.
1711 Maybe if the statment had and input or output
1712 operand it would work... In any case, clobbering
1713 all FP prevents incorrect code generation. */
1714 stmt->regs |= (1 << Reg_ST);
1715 stmt->regs |= (1 << Reg_ST1);
1716 stmt->regs |= (1 << Reg_ST2);
1717 stmt->regs |= (1 << Reg_ST3);
1718 stmt->regs |= (1 << Reg_ST4);
1719 stmt->regs |= (1 << Reg_ST5);
1720 stmt->regs |= (1 << Reg_ST6);
1721 stmt->regs |= (1 << Reg_ST7);
1723 if (opInfo->implicitClobbers & Clb_Flags)
1724 asmcode->moreRegs |= (1 << (Reg_EFLAGS - 32));
1725 if (op == Op_cpuid)
1726 stmt->regs |= (1 << Reg_EAX)|
1727 (1 << Reg_ECX)|(1 << Reg_EDX);
1729 insnTemplate->writebyte('\t');
1730 for (int i__ = 0; i__ < nOperands; i__++) {
1731 int i;
1732 if (i__ != 0)
1733 insnTemplate->writestring((char*) ", ");
1735 fmt = "%";
1737 switch (op) {
1738 case Op_mul:
1739 // gas won't accept the two-operand form; skip to the source operand
1740 i__ = 1;
1741 // drop through
1742 case Op_bound:
1743 case Op_enter:
1744 i = i__;
1745 break;
1746 default:
1747 i = nOperands - 1 - i__; // operand = & operands[ nOperands - 1 - i ];
1748 break;
1750 operand = & operands[ i ];
1752 switch (operand->cls) {
1753 case Opr_Immediate:
1754 // for implementing offset:
1755 // $var + $7 // fails
1756 // $var + 7 // ok
1757 // $7 + $var // ok
1759 // DMD doesn't seem to allow this
1761 if (opInfo->takesLabel()) tho... (near ptr <Number> would be abs?)
1762 fmt = "%a"; // GAS won't accept "jmp $42"; must be "jmp 42" (rel) or "jmp *42" (abs)
1764 if (opTakesLabel()/*opInfo->takesLabel()*/) {
1765 // "relative addressing not allowed in branch instructions" ..
1766 stmt->error("integer constant not allowed in branch instructions");
1767 return false;
1770 if (operand->symbolDisplacement.dim &&
1771 isLocalSize( (Expression *) operand->symbolDisplacement.data[0] )) {
1772 // handle __LOCAL_SIZE, which in this constant, is an immediate
1773 // should do this in slotexp..
1774 addOperand("%", Arg_LocalSize,
1775 (Expression *) operand->symbolDisplacement.data[0], asmcode);
1776 if (operand->constDisplacement)
1777 insnTemplate->writebyte('+');
1778 else
1779 break;
1782 if (operand->symbolDisplacement.dim) {
1783 fmt = "%a";
1784 addOperand("%", Arg_Pointer,
1785 (Expression *) operand->symbolDisplacement.data[0],
1786 asmcode);
1788 if (operand->constDisplacement)
1789 insnTemplate->writebyte('+');
1790 else
1791 // skip the addOperand(fmt, Arg_Integer...) below
1792 break;
1794 addOperand(fmt, Arg_Integer, newIntExp(operand->constDisplacement), asmcode);
1795 break;
1796 case Opr_Reg:
1797 if (opInfo->operands[i] & Opr_Dest) {
1798 Reg clbr_reg = (Reg) regInfo[operand->reg].baseReg;
1799 if (clbr_reg != Reg_Invalid) {
1800 if (clbr_reg < 32)
1801 stmt->regs |= (1 << clbr_reg);
1802 else
1803 asmcode->moreRegs |= (1 << (clbr_reg - 32));
1806 if (opTakesLabel()/*opInfo->takesLabel()*/)
1807 insnTemplate->writebyte('*');
1808 writeReg(operand->reg);
1810 insnTemplate->writestring("%%");
1811 insnTemplate->writestring(regInfo[operand->reg].name);
1813 break;
1814 case Opr_Mem:
1815 // better: use output operands for simple variable references
1816 if ( (opInfo->operands[i] & Opr_Update) == Opr_Update) {
1817 mode = Mode_Update;
1818 } else if (opInfo->operands[i] & Opr_Dest) {
1819 mode = Mode_Output;
1820 } else {
1821 mode = Mode_Input;
1824 use_star = opTakesLabel();//opInfo->takesLabel();
1825 if (operand->segmentPrefix != Reg_Invalid) {
1826 writeReg(operand->segmentPrefix);
1827 insnTemplate->writebyte(':');
1829 if (operand->symbolDisplacement.dim) {
1830 Expression * e = (Expression *) operand->symbolDisplacement.data[0];
1831 Declaration * decl = 0;
1833 /* We are generating a memory reference, but the
1834 operand could be a floating point constant. If this
1835 is the case, it will be turned into a const static
1836 VAR_DECL later in AsmStatement::toIR. */
1838 if (e->op == TOKvar)
1839 decl = ((VarExp *) e)->var;
1841 /* The GCC will error (or just warn) on references to
1842 nonlocal vars, but it cannot catch closure vars. */
1844 /* TODO: Could allow this for the frame-relative
1845 case if it's a closure var. We do not know if
1846 it is a closure var at this point, however.
1847 Just accept it for now and return false from
1848 getFrameRelativeOffset if it turns out not to be.
1850 if (decl && !(decl->isDataseg() || decl->isCodeseg()) &&
1851 decl->toParent2() != sc->func)
1852 stmt->error("cannot access nonlocal variable %s", decl->toChars());
1854 if (operand->baseReg != Reg_Invalid &&
1855 decl && ! decl->isDataseg()) {
1857 // Use the offset from frame pointer
1859 /* GCC doesn't give the front end access to stack offsets
1860 when optimization is turned on (3.x) or at all (4.x).
1862 Try to convert var[EBP] (or var[ESP] for naked funcs) to
1863 a memory expression that does not require us to know
1864 the stack offset.
1867 if (operand->indexReg == Reg_Invalid &&
1868 decl->isVarDeclaration() &&
1869 ((operand->baseReg == Reg_EBP && ! sc->func->naked ) ||
1870 (operand->baseReg == Reg_ESP && sc->func->naked)) ) {
1872 e = new AddrExp(0, e);
1873 e->type = decl->type->pointerTo();
1875 /* DMD uses the same frame offsets for naked functions. */
1876 if (sc->func->naked)
1877 operand->constDisplacement += 4;
1879 if (operand->constDisplacement) {
1880 e = new AddExp(0, e,
1881 new IntegerExp(0, operand->constDisplacement,
1882 Type::tint32));
1883 e->type = decl->type->pointerTo();
1885 e = new PtrExp(0, e);
1886 e->type = decl->type;
1888 operand->constDisplacement = 0;
1889 operand->baseReg = Reg_Invalid;
1891 addOperand(fmt, Arg_Memory, e, asmcode, mode);
1893 } else {
1894 addOperand("%a", Arg_FrameRelative, e, asmcode);
1896 if (opInfo->operands[i] & Opr_Dest)
1897 asmcode->clobbersMemory = 1;
1898 } else {
1899 // Plain memory reference to variable
1901 /* If in a reg, DMD moves to memory.. even with -O, so we'll do the same
1902 by always using the "m" contraint.
1904 In order to get the correct output for function and label symbols,
1905 the %an format must be used with the "p" constraint.
1907 if (isDollar(e)) {
1908 unsigned lbl_num = ++d_priv_asm_label_serial;
1909 addLabel(lbl_num);
1910 asmcode->dollarLabel = lbl_num; // could make the dollar label part of the same asm..
1911 } else if (e->op == TOKdsymbol) {
1912 LabelDsymbol * lbl = (LabelDsymbol *) ((DsymbolExp *) e)->s;
1913 if (! lbl->asmLabelNum)
1914 lbl->asmLabelNum = ++d_priv_asm_label_serial;
1916 use_star = false;
1917 addLabel(lbl->asmLabelNum);
1918 } else if ((decl && decl->isCodeseg())) { // if function or label
1919 use_star = false;
1920 addOperand("%a", Arg_Pointer, e, asmcode);
1921 } else {
1922 if (use_star) {
1923 insnTemplate->writebyte('*');
1924 use_star = false;
1926 addOperand(fmt, Arg_Memory, e, asmcode, mode);
1930 if (use_star)
1931 insnTemplate->writebyte('*');
1932 if (operand->constDisplacement) {
1933 if (operand->symbolDisplacement.dim)
1934 insnTemplate->writebyte('+');
1935 addOperand("%a", Arg_Integer, newIntExp(operand->constDisplacement), asmcode);
1936 if (opInfo->operands[i] & Opr_Dest)
1937 asmcode->clobbersMemory = 1;
1939 if (operand->baseReg != Reg_Invalid || operand->indexReg != Reg_Invalid) {
1940 insnTemplate->writebyte('(');
1941 if (operand->baseReg != Reg_Invalid)
1942 writeReg(operand->baseReg);
1943 if (operand->indexReg != Reg_Invalid) {
1944 insnTemplate->writebyte(',');
1945 writeReg(operand->indexReg);
1946 if (operand->scale) {
1947 insnTemplate->printf(",%d", operand->scale);
1950 insnTemplate->writebyte(')');
1951 if (opInfo->operands[i] & Opr_Dest)
1952 asmcode->clobbersMemory = 1;
1954 break;
1955 case Opr_Invalid:
1956 return false;
1960 asmcode->insnTemplateLen = insnTemplate->offset;
1961 asmcode->insnTemplate = (char*) insnTemplate->extractData();
1962 return true;
1965 bool isIntExp(Expression * exp) {
1966 if (exp->op == TOKint64)
1967 return 1;
1968 if (exp->op == TOKvar) {
1969 Declaration * v = ((VarExp *) exp)->var;
1970 if (v->isConst() && v->type->isintegral())
1971 return 1;
1973 return 0;
1975 bool isRegExp(Expression * exp) { return exp->op == TOKmod; } // ewww.%%
1976 bool isLocalSize(Expression * exp) {
1977 // cleanup: make a static var
1978 return exp->op == TOKidentifier && ((IdentifierExp *) exp)->ident == Id::__LOCAL_SIZE;
1980 bool isDollar(Expression * exp) {
1981 return exp->op == TOKidentifier && ((IdentifierExp *) exp)->ident == Id::__dollar;
1984 Expression * newRegExp(int regno) {
1985 IntegerExp * e = new IntegerExp(regno);
1986 e->op = TOKmod;
1987 return e;
1990 Expression * newIntExp(int v /* %% type */) {
1991 // Only handles 32-bit numbers as this is IA-32.
1992 return new IntegerExp(stmt->loc, v, Type::tint32);
1995 void slotExp(Expression * exp) {
1997 if offset, make a note
1999 if integer, add to immediate
2000 if reg:
2001 if not in bracket, set reg (else error)
2002 if in bracket:
2003 if not base, set base
2004 if not index, set index
2005 else, error
2006 if symbol:
2007 set symbol field
2010 bool is_offset = false;
2011 if (exp->op == TOKaddress) {
2012 exp = ((AddrExp *) exp)->e1;
2013 is_offset = true;
2016 if (isIntExp(exp)) {
2017 if (is_offset)
2018 invalidExpression();
2019 operand->constDisplacement += exp->toInteger();
2020 if (! operand->inBracket)
2021 operand->hasNumber = 1;
2022 } else if (isRegExp(exp)) {
2023 if (is_offset)
2024 invalidExpression();
2025 if (! operand->inBracket) {
2026 if (operand->reg == Reg_Invalid)
2027 operand->reg = (Reg) exp->toInteger();
2028 else
2029 stmt->error("too many registers in operand (use brackets)");
2030 } else {
2031 if (operand->baseReg == Reg_Invalid)
2032 operand->baseReg = (Reg) exp->toInteger();
2033 else if (operand->indexReg == Reg_Invalid) {
2034 operand->indexReg = (Reg) exp->toInteger();
2035 operand->scale = 1;
2036 } else {
2037 stmt->error("too many registers memory operand");
2040 } else if (exp->op == TOKvar) {
2041 VarDeclaration * v = ((VarExp *) exp)->var->isVarDeclaration();
2043 if (v && v->storage_class & STCfield) {
2044 operand->constDisplacement += v->offset;
2045 if (! operand->inBracket)
2046 operand->hasNumber = 1;
2047 } else {
2048 if (v && v->type->isscalar())
2050 // DMD doesn't check Tcomplex*, and counts Tcomplex32 as Tfloat64
2051 TY ty = v->type->toBasetype()->ty;
2052 operand->dataSizeHint = ty == Tfloat80 || ty == Timaginary80 ?
2053 Extended_Ptr : (PtrType) v->type->size(0);
2056 if (! operand->symbolDisplacement.dim) {
2057 if (is_offset && ! operand->inBracket)
2058 operand->isOffset = 1;
2059 operand->symbolDisplacement.push( exp );
2060 } else {
2061 stmt->error("too many symbols in operand");
2064 } else if (exp->op == TOKidentifier || exp->op == TOKdsymbol) {
2065 // %% localsize could be treated as a simple constant..
2066 // change to addSymbolDisp(e)
2067 if (! operand->symbolDisplacement.dim) {
2068 operand->symbolDisplacement.push( exp );
2069 } else {
2070 stmt->error("too many symbols in operand");
2072 } else if (exp->op == TOKfloat64) {
2073 if (exp->type && exp->type->isscalar())
2075 // DMD doesn't check Tcomplex*, and counts Tcomplex32 as Tfloat64
2076 TY ty = exp->type->toBasetype()->ty;
2077 operand->dataSizeHint = ty == Tfloat80 || ty == Timaginary80 ?
2078 Extended_Ptr : (PtrType) exp->type->size(0);
2080 // Will be converted to a VAR_DECL in AsmStatement::toIR
2081 operand->symbolDisplacement.push( exp );
2082 } else if (exp == Handled) {
2083 // nothing
2084 } else {
2085 stmt->error("invalid operand");
2089 void invalidExpression() {
2090 // %% report operand number
2091 stmt->error("invalid expression");
2094 Expression * intOp(TOK op, Expression * e1, Expression * e2) {
2095 Expression * e;
2096 if (isIntExp(e1) && (! e2 || isIntExp(e2))) {
2097 switch (op) {
2098 case TOKadd:
2099 if (e2)
2100 e = new AddExp(stmt->loc, e1, e2);
2101 else
2102 e = e1;
2103 break;
2104 case TOKmin:
2105 if (e2)
2106 e = new MinExp(stmt->loc, e1, e2);
2107 else
2108 e = new NegExp(stmt->loc, e1);
2109 break;
2110 case TOKmul:
2111 e = new MulExp(stmt->loc, e1, e2);
2112 break;
2113 case TOKdiv:
2114 e = new DivExp(stmt->loc, e1, e2);
2115 break;
2116 case TOKmod:
2117 e = new ModExp(stmt->loc, e1, e2);
2118 break;
2119 case TOKshl:
2120 e = new ShlExp(stmt->loc, e1, e2);
2121 break;
2122 case TOKshr:
2123 e = new ShrExp(stmt->loc, e1, e2);
2124 break;
2125 case TOKushr:
2126 e = new UshrExp(stmt->loc, e1, e2);
2127 break;
2128 case TOKnot:
2129 e = new NotExp(stmt->loc, e1);
2130 break;
2131 case TOKtilde:
2132 e = new ComExp(stmt->loc, e1);
2133 break;
2134 default:
2135 assert(0);
2137 e = e->semantic(sc);
2138 return e->optimize(WANTvalue | WANTinterpret);
2139 } else {
2140 stmt->error("expected integer operand(s) for '%s'", Token::tochars[op]);
2141 return newIntExp(0);
2145 void parseOperand() {
2146 Expression * exp = parseAsmExp();
2147 slotExp(exp);
2148 if (isRegExp(exp))
2149 operand->dataSize = (PtrType) regInfo[exp->toInteger()].size;
2152 Expression * parseAsmExp() {
2153 return parseShiftExp();
2156 Expression * parseShiftExp() {
2157 Expression * e1 = parseAddExp();
2158 Expression * e2;
2160 while (1) {
2161 TOK tv = token->value;
2162 switch (tv) {
2163 case TOKshl:
2164 case TOKshr:
2165 case TOKushr:
2166 nextToken();
2167 e2 = parseAddExp();
2168 e1 = intOp(tv, e1, e2);
2169 continue;
2170 default:
2171 break;
2173 break;
2175 return e1;
2178 Expression * parseAddExp() {
2179 Expression * e1 = parseMultExp();
2180 Expression * e2;
2182 while (1) {
2183 TOK tv = token->value;
2184 switch (tv) {
2185 case TOKadd:
2186 nextToken();
2187 e2 = parseMultExp();
2188 if (isIntExp(e1) && isIntExp(e2))
2189 e1 = intOp(tv, e1, e2);
2190 else {
2191 slotExp(e1);
2192 slotExp(e2);
2193 e1 = Handled;
2195 continue;
2196 case TOKmin:
2197 // Note: no support for symbol address difference
2198 nextToken();
2199 e2 = parseMultExp();
2200 if (isIntExp(e1) && isIntExp(e2))
2201 e1 = intOp(tv, e1, e2);
2202 else {
2203 slotExp(e1);
2204 e2 = intOp(TOKmin, e2, NULL); // verifies e2 is an int
2205 slotExp(e2);
2206 e1 = Handled;
2208 continue;
2209 default:
2210 break;
2212 break;
2214 return e1;
2217 bool tryScale(Expression * e1, Expression * e2) {
2218 Expression * et;
2219 if (isIntExp(e1) && isRegExp(e2)) {
2220 et = e1;
2221 e1 = e2;
2222 e2 = et;
2223 goto do_scale;
2224 } else if (isRegExp(e1) && isIntExp(e2)) {
2225 do_scale:
2226 if (! operand->inBracket) {
2227 invalidExpression(); // maybe should allow, e.g. DS:EBX+EAX*4
2230 if (operand->scale || operand->indexReg != Reg_Invalid) {
2231 invalidExpression();
2232 return true;
2235 operand->indexReg = (Reg) e1->toInteger();
2236 operand->scale = e2->toInteger();
2237 switch (operand->scale) {
2238 case 1:
2239 case 2:
2240 case 4:
2241 case 8:
2242 // ok; do nothing
2243 break;
2244 default:
2245 stmt->error("invalid index register scale '%d'", operand->scale);
2246 return true;
2249 return true;
2251 return false;
2254 Expression * parseMultExp() {
2255 Expression * e1 = parseBrExp();
2256 Expression * e2;
2258 while (1) {
2259 TOK tv = token->value;
2260 switch (tv) {
2261 case TOKmul:
2262 nextToken();
2263 e2 = parseMultExp();
2264 if (isIntExp(e1) && isIntExp(e2))
2265 e1 = intOp(tv, e1, e2);
2266 else if (tryScale(e1,e2))
2267 e1 = Handled;
2268 else
2269 invalidExpression();
2270 continue;
2271 case TOKdiv:
2272 case TOKmod:
2273 nextToken();
2274 e2 = parseMultExp();
2275 e1 = intOp(tv, e1, e2);
2276 continue;
2277 default:
2278 break;
2280 break;
2282 return e1;
2285 Expression * parseBrExp() {
2286 // %% check (why is bracket lower precends..)
2287 // 3+4[eax] -> 3 + (4 [EAX]) ..
2289 // only one bracked allowed, so this doesn't quite handle
2290 // the spec'd syntax
2291 Expression * e;
2293 if (token->value == TOKlbracket)
2294 e = Handled;
2295 else
2296 e = parseUnaExp();
2298 // DMD allows multiple bracket expressions.
2299 while (token->value == TOKlbracket) {
2300 nextToken();
2302 operand->inBracket = operand->hasBracket = 1;
2303 slotExp(parseAsmExp());
2304 operand->inBracket = 0;
2306 if (token->value == TOKrbracket)
2307 nextToken();
2308 else
2309 stmt->error("missing ']'");
2312 return e;
2315 PtrType isPtrType(Token * tok) {
2316 switch (tok->value) {
2317 case TOKint8: return Byte_Ptr;
2318 case TOKint16: return Short_Ptr;
2319 case TOKint32: return Int_Ptr;
2320 // 'long ptr' isn't accepted?
2321 case TOKfloat32: return Float_Ptr;
2322 case TOKfloat64: return Double_Ptr;
2323 case TOKfloat80: return Extended_Ptr;
2324 case TOKidentifier:
2325 for (int i = 0; i < N_PtrNames; i++)
2326 if (tok->ident == ptrTypeIdentTable[i])
2327 return ptrTypeValueTable[i];
2328 break;
2329 default:
2330 break;
2332 return Default_Ptr;
2335 Expression * parseUnaExp() {
2336 Expression * e = NULL;
2337 PtrType ptr_type;
2339 // First, check for type prefix.
2340 if (token->value != TOKeof &&
2341 peekToken()->value == TOKidentifier &&
2342 peekToken()->ident == Id::ptr) {
2344 ptr_type = isPtrType(token);
2345 if (ptr_type != Default_Ptr) {
2346 if (operand->dataSize == Default_Ptr)
2347 operand->dataSize = ptr_type;
2348 else
2349 stmt->error("multiple specifications of operand size");
2350 } else
2351 stmt->error("unknown operand size '%s'", token->toChars());
2352 nextToken();
2353 nextToken();
2354 return parseAsmExp();
2357 TOK tv = token->value;
2358 switch (tv) {
2359 case TOKidentifier:
2360 if (token->ident == ident_seg) {
2361 nextToken();
2362 stmt->error("'seg' not supported");
2363 e = parseAsmExp();
2364 } else if (token->ident == Id::offset ||
2365 token->ident == Id::offsetof) {
2366 if (token->ident == Id::offset && ! global.params.useDeprecated)
2367 stmt->error("offset deprecated, use offsetof");
2368 nextToken();
2369 e = parseAsmExp();
2370 e = new AddrExp(stmt->loc, e);
2371 } else {
2372 // primary exp
2373 break;
2375 return e;
2376 case TOKadd:
2377 case TOKmin:
2378 case TOKnot:
2379 case TOKtilde:
2380 nextToken();
2381 e = parseUnaExp();
2382 return intOp(tv, e, NULL);
2383 default:
2384 // primary exp
2385 break;
2387 return parsePrimaryExp();
2390 Expression * parsePrimaryExp() {
2391 Expression * e;
2392 Identifier * ident = NULL;
2394 switch (token->value) {
2395 case TOKint32v:
2396 case TOKuns32v:
2397 case TOKint64v:
2398 case TOKuns64v:
2399 // semantic here?
2400 // %% for tok64 really should use 64bit type
2401 e = new IntegerExp(stmt->loc, token->uns64value, Type::tint32);
2402 nextToken();
2403 break;
2404 case TOKfloat32v:
2405 case TOKfloat64v:
2406 case TOKfloat80v:
2407 // %% need different types?
2408 e = new RealExp(stmt->loc, token->float80value, Type::tfloat80);
2409 nextToken();
2410 break;
2411 case TOKidentifier:
2413 ident = token->ident;
2414 nextToken();
2416 if (ident == Id::__LOCAL_SIZE) {
2417 return new IdentifierExp(stmt->loc, ident);
2418 } else if (ident == Id::__dollar) {
2419 do_dollar:
2420 return new IdentifierExp(stmt->loc, ident);
2421 } else {
2422 e = new IdentifierExp(stmt->loc, ident);
2425 // If this is more than one component ref, it gets complicated: *(&Field + n)
2426 // maybe just do one step at a time..
2427 // simple case is Type.f -> VarDecl(field)
2428 // actually, DMD only supports on level...
2429 // X.y+Y.z[EBX] is supported, tho..
2430 // %% doesn't handle properties (check%%)
2431 while (token->value == TOKdot) {
2432 nextToken();
2433 if (token->value == TOKidentifier) {
2434 e = new DotIdExp(stmt->loc, e, token->ident);
2435 nextToken();
2436 } else {
2437 stmt->error("expected identifier");
2438 return Handled;
2442 // check for reg first then dotexp is an error?
2443 if (e->op == TOKidentifier) {
2444 for (int i = 0; i < N_Regs; i++) {
2445 if (ident == regInfo[i].ident) {
2446 if ( (Reg) i == Reg_ST && token->value == TOKlparen) {
2447 nextToken();
2448 switch (token->value) {
2449 case TOKint32v: case TOKuns32v:
2450 case TOKint64v: case TOKuns64v:
2451 if (token->uns64value < 8)
2452 e = newRegExp( (Reg) (Reg_ST + token->uns64value) );
2453 else {
2454 stmt->error("invalid floating point register index");
2455 e = Handled;
2457 nextToken();
2458 if (token->value == TOKrparen)
2459 nextToken();
2460 else
2461 stmt->error("expected ')'");
2462 return e;
2463 default:
2464 break;
2466 invalidExpression();
2467 return Handled;
2468 } else if (token->value == TOKcolon) {
2469 nextToken();
2470 if (operand->segmentPrefix != Reg_Invalid)
2471 stmt->error("too many segment prefixes");
2472 else if (i >= Reg_CS && i <= Reg_GS)
2473 operand->segmentPrefix = (Reg) i;
2474 else
2475 stmt->error("'%s' is not a segment register", ident->string);
2476 return parseAsmExp();
2477 } else {
2478 return newRegExp( (Reg) i );
2484 if (opTakesLabel()/*opInfo->takesLabel()*/ && e->op == TOKidentifier) {
2485 // DMD uses labels secondarily to other symbols, so check
2486 // if IdentifierExp::semantic won't find anything.
2487 Dsymbol *scopesym;
2489 if ( ! sc->search(stmt->loc, ident, & scopesym) )
2490 return new DsymbolExp(stmt->loc,
2491 sc->func->searchLabel( ident ));
2494 e = e->semantic(sc);
2496 /* Special case for floating point literals: Try to
2497 resolve to a memory reference. If not possible,
2498 leave it as a literal and generate an anonymous
2499 var in AsmStatement::toIR.
2501 if (e->op == TOKfloat64) {
2502 Dsymbol * sym = sc->search(stmt->loc, ident, NULL);
2503 if ( sym ) {
2504 VarDeclaration *v = sym->isVarDeclaration();
2505 if ( v && v->isDataseg() ) {
2506 Expression *ve = new VarExp(stmt->loc, v);
2507 ve->type = e->type;
2508 e = ve;
2512 return e;
2514 break;
2515 case TOKdollar:
2516 nextToken();
2517 ident = Id::__dollar;
2518 goto do_dollar;
2519 break;
2520 default:
2521 invalidExpression();
2522 return Handled;
2524 return e;
2527 void doAlign() {
2528 // .align bits vs. bytes...
2529 // apparently a.out platforms use bits instead of bytes...
2531 // parse primary: DMD allows 'MyAlign' (const int) but not '2+2'
2532 // GAS is padding with NOPs last time I checked.
2533 Expression * e = parseAsmExp()->optimize(WANTvalue | WANTinterpret);
2534 integer_t align = e->toInteger();
2536 if (align >= 0) {
2537 // %% is this printf portable?
2538 #ifdef HAVE_GAS_BALIGN_AND_P2ALIGN
2539 insnTemplate->printf(".balign\t%u", (unsigned) align);
2540 #else
2541 insnTemplate->printf(".align\t%u", (unsigned) align);
2542 #endif
2543 } else {
2544 stmt->error("alignment must be a power of 2");
2547 setAsmCode();
2550 void doEven() {
2551 // .align for GAS is in bits, others probably use bytes..
2552 #ifdef HAVE_GAS_BALIGN_AND_P2ALIGN
2553 insnTemplate->writestring((char *) ".align\t2");
2554 #else
2555 insnTemplate->writestring((char *) ".align\t2");
2556 #endif
2557 setAsmCode();
2560 void doNaked() {
2561 // %% can we assume sc->func != 0?
2562 sc->func->naked = 1;
2565 void doData() {
2566 static const char * directives[] = { ".byte", ".short", ".long", ".long",
2567 "", "", "" };
2569 machine_mode mode;
2571 insnTemplate->writestring((char*) directives[op - Op_db]);
2572 insnTemplate->writebyte('\t');
2574 do {
2575 // DMD is pretty strict here, not even constant expressions are allowed..
2576 switch (op) {
2577 case Op_db:
2578 case Op_ds:
2579 case Op_di:
2580 case Op_dl:
2581 if (token->value == TOKint32v || token->value == TOKuns32v ||
2582 token->value == TOKint64v || token->value == TOKuns64v) {
2583 // As per usual with GNU, assume at least 32-bit host
2584 if (op != Op_dl)
2585 insnTemplate->printf("%u", (d_uns32) token->uns64value);
2586 else {
2587 // Output two .longS. GAS has .quad, but would have to rely on 'L' format ..
2588 // just need to use HOST_WIDE_INT_PRINT_DEC
2589 insnTemplate->printf("%u,%u",
2590 (d_uns32) token->uns64value, (d_uns32) (token->uns64value >> 32));
2592 } else {
2593 stmt->error("expected integer constant");
2595 break;
2596 case Op_df:
2597 mode = SFmode;
2598 goto do_float;
2599 case Op_dd:
2600 mode = DFmode;
2601 goto do_float;
2602 case Op_de:
2603 #ifndef TARGET_80387
2604 #define XFmode TFmode
2605 #endif
2606 mode = XFmode; // not TFmode
2607 // drop through
2608 do_float:
2609 if (token->value == TOKfloat32v || token->value == TOKfloat64v ||
2610 token->value == TOKfloat80v) {
2611 long words[3];
2612 real_to_target(words, & token->float80value.rv(), mode);
2613 // don't use directives..., just use .long like GCC
2614 insnTemplate->printf(".long\t%u", words[0]);
2615 if (mode != SFmode)
2616 insnTemplate->printf(",%u", words[1]);
2617 // DMD outputs 10 bytes, so we need to switch to .short here
2618 if (mode == XFmode)
2619 insnTemplate->printf("\n\t.short\t%u", words[2]);
2620 } else {
2621 stmt->error("expected float constant");
2623 break;
2624 default:
2625 abort();
2628 nextToken();
2629 if (token->value == TOKcomma) {
2630 insnTemplate->writebyte(',');
2631 nextToken();
2632 } else if (token->value == TOKeof) {
2633 break;
2634 } else {
2635 stmt->error("expected comma");
2637 } while (1);
2639 setAsmCode();
2643 #if D_GCC_VER < 40
2644 // struct rtx was modified for c++; this macro from rtl.h needs to
2645 // be modified accordingly.
2646 #undef XEXP
2647 #define XEXP(RTX, N) (RTL_CHECK2 (RTX, N, 'e', 'u').rt_rtx)
2648 #endif
2650 bool getFrameRelativeValue(tree decl, HOST_WIDE_INT * result)
2652 /* Using this instead of DECL_RTL for struct args seems like a
2653 good way to get hit by a truck because it may not agree with
2654 non-asm access, but asm code wouldn't know what GCC does anyway. */
2655 rtx r = DECL_INCOMING_RTL(decl);
2656 rtx e1, e2;
2658 // Local variables don't have DECL_INCOMING_RTL
2659 if (r == NULL_RTX)
2660 r = DECL_RTL(decl);
2662 if (r != NULL_RTX && GET_CODE(r) == MEM /* && r->frame_related */ ) {
2663 r = XEXP(r, 0);
2664 if (GET_CODE(r) == PLUS) {
2665 e1 = XEXP(r, 0);
2666 e2 = XEXP(r, 1);
2667 if (e1 == virtual_incoming_args_rtx && GET_CODE(e2) == CONST_INT) {
2668 *result = INTVAL(e2) + 8; // %% 8 is 32-bit specific...
2669 return true;
2670 } else if (e1 == virtual_stack_vars_rtx && GET_CODE(e2) == CONST_INT) {
2671 *result = INTVAL(e2); // %% 8 is 32-bit specific...
2672 return true;
2674 } else if (r == virtual_incoming_args_rtx) {
2675 *result = 8;
2676 return true; // %% same as above
2678 // shouldn't have virtual_stack_vars_rtx by itself
2681 return false;