Add amd64 support patch from Zalman Stern
[mono.git] / mono / arch / amd64 / amd64-codegen.h
1 /*
2  * amd64-codegen.h: Macros for generating x86 code
3  *
4  * Authors:
5  *   Paolo Molaro (lupus@ximian.com)
6  *   Intel Corporation (ORP Project)
7  *   Sergey Chaban (serge@wildwestsoftware.com)
8  *   Dietmar Maurer (dietmar@ximian.com)
9  *   Patrik Torstensson
10  *   Zalman Stern
11  * 
12  *  Not all routines are done for AMD64. Much could also be removed from here if supporting tramp.c is the only goal.
13  * 
14  * Copyright (C)  2000 Intel Corporation.  All rights reserved.
15  * Copyright (C)  2001, 2002 Ximian, Inc.
16  */
17
18 #ifndef AMD64_H
19 #define AMD64_H
20
21 typedef enum {
22         AMD64_RAX = 0,
23         AMD64_RCX = 1,
24         AMD64_RDX = 2,
25         AMD64_RBX = 3,
26         AMD64_RSP = 4,
27         AMD64_RBP = 5,
28         AMD64_RSI = 6,
29         AMD64_RDI = 7,
30         AMD64_R8 = 8,
31         AMD64_R9 = 9,
32         AMD64_R10 = 10,
33         AMD64_R11 = 11,
34         AMD64_R12 = 12,
35         AMD64_R13 = 13,
36         AMD64R_14 = 14,
37         AMD64_R15 = 15,
38         AMD64_NREG
39 } AMD64_Reg_No;
40
41 typedef enum {
42         AMD64_XMM0 = 0,
43         AMD64_XMM1 = 1,
44         AMD64_XMM2 = 2,
45         AMD64_XMM3 = 3,
46         AMD64_XMM4 = 4,
47         AMD64_XMM5 = 5,
48         AMD64_XMM6 = 6,
49         AMD64_XMM8 = 8,
50         AMD64_XMM9 = 9,
51         AMD64_XMM10 = 10,
52         AMD64_XMM11 = 11,
53         AMD64_XMM12 = 12,
54         AMD64_XMM13 = 13,
55         AMD64_XMM14 = 14,
56         AMD64_XMM15 = 15,
57         AMD64_XMM_NREG = 16,
58 } AMD64_XMM_Reg_No;
59
60 typedef enum
61 {
62   AMD64_REX_B = 1, /* The register in r/m field, base register in SIB byte, or reg in opcode is 8-15 rather than 0-7 */
63   AMD64_REX_X = 2, /* The index register in SIB byte is 8-15 rather than 0-7 */
64   AMD64_REX_R = 4, /* The reg field of ModRM byte is 8-15 rather than 0-7 */
65   AMD64_REX_W = 8  /* Opeartion is 64-bits instead of 32 (default) or 16 (with 0x66 prefix) */
66 } AMD64_REX_Bits;
67
68 #define AMD64_REX(bits) ((unsigned char)(0x40 | (bits)))
69 #define amd64_emit_rex(inst, width, reg_modrm, reg_index, reg_rm_base_opcode) \
70         { \
71                 unsigned char _amd64_rex_bits = \
72                         (((width) > 4) ? AMD64_REX_W : 0) | \
73                         (((reg_modrm) > 7) ? AMD64_REX_R : 0) | \
74                         (((reg_index) > 7) ? AMD64_REX_X : 0) | \
75                         (((reg_rm_base_opcode) > 7) ? AMD64_REX_B : 0); \
76                 if (_amd64_rex_bits != 0) *(inst)++ = AMD64_REX(_amd64_rex_bits); \
77         }
78
79 typedef union {
80         long val;
81         unsigned char b [8];
82 } amd64_imm_buf;
83
84 #include "../x86/x86-codegen.h"
85
86
87 /* Need to fill this info in for amd64. */
88
89 #if 0
90 /*
91 // bitvector mask for callee-saved registers
92 */
93 #define X86_ESI_MASK (1<<X86_ESI)
94 #define X86_EDI_MASK (1<<X86_EDI)
95 #define X86_EBX_MASK (1<<X86_EBX)
96 #define X86_EBP_MASK (1<<X86_EBP)
97
98 #define X86_CALLEE_REGS ((1<<X86_EAX) | (1<<X86_ECX) | (1<<X86_EDX))
99 #define X86_CALLER_REGS ((1<<X86_EBX) | (1<<X86_EBP) | (1<<X86_ESI) | (1<<X86_EDI))
100 #define X86_BYTE_REGS   ((1<<X86_EAX) | (1<<X86_ECX) | (1<<X86_EDX) | (1<<X86_EBX))
101
102 #define X86_IS_SCRATCH(reg) (X86_CALLER_REGS & (1 << (reg))) /* X86_EAX, X86_ECX, or X86_EDX */
103 #define X86_IS_CALLEE(reg)  (X86_CALLEE_REGS & (1 << (reg)))    /* X86_ESI, X86_EDI, X86_EBX, or X86_EBP */
104
105 #define X86_IS_BYTE_REG(reg) ((reg) < 4)
106
107 /*
108 // Frame structure:
109 //
110 //      +--------------------------------+
111 //      | in_arg[0]       = var[0]           |
112 //      | in_arg[1]           = var[1]       |
113 //      |             . . .                              |
114 //      | in_arg[n_arg-1] = var[n_arg-1] |
115 //      +--------------------------------+
116 //      |       return IP                |
117 //      +--------------------------------+
118 //      |       saved EBP                | <-- frame pointer (EBP)
119 //      +--------------------------------+
120 //      |            ...                 |  n_extra
121 //      +--------------------------------+
122 //      |           var[n_arg]               |
123 //      |           var[n_arg+1]             |  local variables area
124 //      |          . . .                 |
125 //      |           var[n_var-1]             | 
126 //      +--------------------------------+
127 //      |                                            |
128 //      |                                            |  
129 //      |               spill area               | area for spilling mimic stack
130 //      |                                            |
131 //      +--------------------------------|
132 //      |          ebx                   |
133 //      |          ebp [ESP_Frame only]  |
134 //      |              esi                   |  0..3 callee-saved regs
135 //      |          edi                   | <-- stack pointer (ESP)
136 //      +--------------------------------+
137 //      |       stk0                         |
138 //      |       stk1                         |  operand stack area/
139 //      |       . . .                        |  out args
140 //      |       stkn-1                       |
141 //      +--------------------------------|
142 //
143 //
144 */
145 #endif
146
147 #define x86_imm_emit64(inst,imm)     \
148         do {    \
149                         amd64_imm_buf imb; imb.val = (long) (imm);      \
150                         *(inst)++ = imb.b [0];  \
151                         *(inst)++ = imb.b [1];  \
152                         *(inst)++ = imb.b [2];  \
153                         *(inst)++ = imb.b [3];  \
154                         *(inst)++ = imb.b [4];  \
155                         *(inst)++ = imb.b [5];  \
156                         *(inst)++ = imb.b [6];  \
157                         *(inst)++ = imb.b [7];  \
158         } while (0)
159
160 #define amd64_alu_reg_imm(inst,opc,reg,imm)     \
161         do {    \
162                 if ((reg) == X86_EAX) { \
163                         amd64_emit_rex(inst, 8, 0, 0, 0); \
164                         *(inst)++ = (((unsigned char)(opc)) << 3) + 5;  \
165                         x86_imm_emit64 ((inst), (imm)); \
166                         break;  \
167                 }       \
168                 if (x86_is_imm8((imm))) {       \
169                         amd64_emit_rex(inst, 8, 0, 0, (reg)); \
170                         *(inst)++ = (unsigned char)0x83;        \
171                         x86_reg_emit ((inst), (opc), (reg));    \
172                         x86_imm_emit8 ((inst), (imm));  \
173                 } else {        \
174                         amd64_emit_rex(inst, 8, 0, 0, (reg)); \
175                         *(inst)++ = (unsigned char)0x81;        \
176                         x86_reg_emit ((inst), (opc), (reg));    \
177                         x86_imm_emit32 ((inst), (imm)); \
178                 }       \
179         } while (0)
180
181 #define amd64_alu_reg_reg(inst,opc,dreg,reg)    \
182         do {    \
183                 amd64_emit_rex(inst, 8, (dreg), 0, (reg)); \
184                 *(inst)++ = (((unsigned char)(opc)) << 3) + 3;  \
185                 x86_reg_emit ((inst), (dreg), (reg));   \
186         } while (0)
187
188 #define amd64_mov_regp_reg(inst,regp,reg,size)  \
189         do {    \
190                 if ((size) == 2) \
191                         *(inst)++ = (unsigned char)0x66; \
192                 amd64_emit_rex(inst, (size), (reg), 0, (regp)); \
193                 switch ((size)) {       \
194                 case 1: *(inst)++ = (unsigned char)0x88; break; \
195                 case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \
196                 default: assert (0);    \
197                 }       \
198                 x86_regp_emit ((inst), (reg), (regp));  \
199         } while (0)
200
201 #define amd64_mov_membase_reg(inst,basereg,disp,reg,size)       \
202         do {    \
203                 if ((size) == 2) \
204                         *(inst)++ = (unsigned char)0x66; \
205                 amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \
206                 switch ((size)) {       \
207                 case 1: *(inst)++ = (unsigned char)0x88; break; \
208                 case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \
209                 default: assert (0);    \
210                 }       \
211                 x86_membase_emit ((inst), (reg), (basereg), (disp));    \
212         } while (0)
213
214
215 #define amd64_mov_reg_reg(inst,dreg,reg,size)   \
216         do {    \
217                 if ((size) == 2) \
218                         *(inst)++ = (unsigned char)0x66; \
219                 amd64_emit_rex(inst, (size), (dreg), 0, (reg)); \
220                 switch ((size)) {       \
221                 case 1: *(inst)++ = (unsigned char)0x8a; break; \
222                 case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \
223                 default: assert (0);    \
224                 }       \
225                 x86_reg_emit ((inst), (dreg), (reg));   \
226         } while (0)
227
228 #define amd64_mov_reg_mem(inst,reg,mem,size)    \
229         do {    \
230                 if ((size) == 2) \
231                         *(inst)++ = (unsigned char)0x66; \
232                 amd64_emit_rex(inst, (size), (reg), 0, 0); \
233                 switch ((size)) {       \
234                 case 1: *(inst)++ = (unsigned char)0x8a; break; \
235                 case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \
236                 default: assert (0);    \
237                 }       \
238                 x86_mem_emit ((inst), (reg), (mem));    \
239         } while (0)
240
241 #define amd64_mov_reg_membase(inst,reg,basereg,disp,size)       \
242         do {    \
243                 if ((size) == 2) \
244                         *(inst)++ = (unsigned char)0x66; \
245                 amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \
246                 switch ((size)) {       \
247                 case 1: *(inst)++ = (unsigned char)0x8a; break; \
248                 case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \
249                 default: assert (0);    \
250                 }       \
251                 x86_membase_emit ((inst), (reg), (basereg), (disp));    \
252         } while (0)
253
254 #define amd64_movzx_reg_membase(inst,reg,basereg,disp,size)     \
255         do {    \
256                 amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \
257                 switch ((size)) {       \
258                 case 1: *(inst)++ = (unsigned char)0x0f; *(inst)++ = (unsigned char)0xb6; break;        \
259                 case 2: *(inst)++ = (unsigned char)0x0f; *(inst)++ = (unsigned char)0xb7; break;        \
260                 case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \
261                 default: assert (0);    \
262                 }       \
263                 x86_membase_emit ((inst), (reg), (basereg), (disp));    \
264         } while (0)
265
266 /* Pretty much the only instruction that supports a 64-bit immediate. Optimize for common case of
267  * 32-bit immediate. Pepper with casts to avoid warnings.
268  */
269 #define amd64_mov_reg_imm(inst,reg,imm) \
270         do {    \
271                 int _amd64_width_temp = ((long)(imm) == (long)(int)(long)(imm)); \
272                 amd64_emit_rex(inst, _amd64_width_temp ? 8 : 4, 0, 0, (reg)); \
273                 *(inst)++ = (unsigned char)0xb8 + ((reg) & 0x7);        \
274                 if (_amd64_width_temp) \
275                         x86_imm_emit64 ((inst), (long)(imm));   \
276                 else \
277                         x86_imm_emit32 ((inst), (int)(long)(imm));      \
278         } while (0)
279
280 #define amd64_mov_membase_imm(inst,basereg,disp,imm,size)       \
281         do {    \
282                 if ((size) == 2) \
283                         *(inst)++ = (unsigned char)0x66; \
284                 amd64_emit_rex(inst, (size), 0, 0, (basereg)); \
285                 if ((size) == 1) {      \
286                         *(inst)++ = (unsigned char)0xc6;        \
287                         x86_membase_emit ((inst), 0, (basereg), (disp));        \
288                         x86_imm_emit8 ((inst), (imm));  \
289                 } else if ((size) == 2) {       \
290                         *(inst)++ = (unsigned char)0xc7;        \
291                         x86_membase_emit ((inst), 0, (basereg), (disp));        \
292                         x86_imm_emit16 ((inst), (imm)); \
293                 } else {        \
294                         *(inst)++ = (unsigned char)0xc7;        \
295                         x86_membase_emit ((inst), 0, (basereg), (disp));        \
296                         x86_imm_emit32 ((inst), (imm)); \
297                 }       \
298         } while (0)
299
300 #define amd64_lea_membase(inst,reg,basereg,disp)        \
301         do {    \
302                 amd64_emit_rex(inst, 8, (reg), 0, (basereg)); \
303                 *(inst)++ = (unsigned char)0x8d;        \
304                 x86_membase_emit ((inst), (reg), (basereg), (disp));    \
305         } while (0)
306
307 /* Instruction are implicitly 64-bits so don't generate REX for just the size. */
308 #define amd64_push_reg(inst,reg)        \
309         do {    \
310                 amd64_emit_rex(inst, 0, 0, 0, (reg)); \
311                 *(inst)++ = (unsigned char)0x50 + ((reg) & 0x7);        \
312         } while (0)
313
314 /* Instruction is implicitly 64-bits so don't generate REX for just the size. */
315 #define amd64_push_membase(inst,basereg,disp)   \
316         do {    \
317                 amd64_emit_rex(inst, 0, 0, 0, (basereg)); \
318                 *(inst)++ = (unsigned char)0xff;        \
319                 x86_membase_emit ((inst), 6, (basereg), (disp));        \
320         } while (0)
321
322 #define amd64_pop_reg(inst,reg) \
323         do {    \
324                 amd64_emit_rex(inst, 0, 0, 0, (reg)); \
325                 *(inst)++ = (unsigned char)0x58 + (reg);        \
326         } while (0)
327
328 #define amd64_call_reg(inst,reg)        \
329         do {    \
330                 amd64_emit_rex(inst, 0, 0, 0, (reg)); \
331                 *(inst)++ = (unsigned char)0xff;        \
332                 x86_reg_emit ((inst), 2, (reg));        \
333         } while (0)
334
335 #define amd64_ret(inst) do { *(inst)++ = (unsigned char)0xc3; } while (0)
336 #define amd64_leave(inst) do { *(inst)++ = (unsigned char)0xc9; } while (0)
337 #define amd64_movsd_reg_regp(inst,reg,regp)     \
338         do {    \
339                 *(inst)++ = (unsigned char)0xf2;        \
340                 amd64_emit_rex(inst, 0, (reg), 0, (regp)); \
341                 *(inst)++ = (unsigned char)0x0f;        \
342                 *(inst)++ = (unsigned char)0x10;        \
343                 x86_regp_emit ((inst), (reg), (regp));  \
344         } while (0)
345
346 #define amd64_movsd_regp_reg(inst,regp,reg)     \
347         do {    \
348                 *(inst)++ = (unsigned char)0xf2;        \
349                 amd64_emit_rex(inst, 0, (reg), 0, (regp)); \
350                 *(inst)++ = (unsigned char)0x0f;        \
351                 *(inst)++ = (unsigned char)0x11;        \
352                 x86_regp_emit ((inst), (reg), (regp));  \
353         } while (0)
354
355 #define amd64_movss_reg_regp(inst,reg,regp)     \
356         do {    \
357                 *(inst)++ = (unsigned char)0xf3;        \
358                 amd64_emit_rex(inst, 0, (reg), 0, (regp)); \
359                 *(inst)++ = (unsigned char)0x0f;        \
360                 *(inst)++ = (unsigned char)0x10;        \
361                 x86_regp_emit ((inst), (reg), (regp));  \
362         } while (0)
363
364 #define amd64_movss_regp_reg(inst,regp,reg)     \
365         do {    \
366                 *(inst)++ = (unsigned char)0xf3;        \
367                 amd64_emit_rex(inst, 0, (reg), 0, (regp)); \
368                 *(inst)++ = (unsigned char)0x0f;        \
369                 *(inst)++ = (unsigned char)0x11;        \
370                 x86_regp_emit ((inst), (reg), (regp));  \
371         } while (0)
372
373 #define amd64_movsd_reg_membase(inst,reg,basereg,disp)  \
374         do {    \
375                 *(inst)++ = (unsigned char)0xf2;        \
376                 amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \
377                 *(inst)++ = (unsigned char)0x0f;        \
378                 *(inst)++ = (unsigned char)0x10;        \
379                 x86_membase_emit ((inst), (reg), (basereg), (disp));    \
380         } while (0)
381
382 #define amd64_movss_reg_membase(inst,reg,basereg,disp)  \
383         do {    \
384                 *(inst)++ = (unsigned char)0xf3;        \
385                 amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \
386                 *(inst)++ = (unsigned char)0x0f;        \
387                 *(inst)++ = (unsigned char)0x10;        \
388                 x86_membase_emit ((inst), (reg), (basereg), (disp));    \
389         } while (0)
390
391 #define amd64_movsd_membase_reg(inst,reg,basereg,disp)  \
392         do {    \
393                 *(inst)++ = (unsigned char)0xf2;        \
394                 amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \
395                 *(inst)++ = (unsigned char)0x0f;        \
396                 *(inst)++ = (unsigned char)0x11;        \
397                 x86_membase_emit ((inst), (reg), (basereg), (disp));    \
398         } while (0)
399
400 #define amd64_movss_membase_reg(inst,reg,basereg,disp)  \
401         do {    \
402                 *(inst)++ = (unsigned char)0xf3;        \
403                 amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \
404                 *(inst)++ = (unsigned char)0x0f;        \
405                 *(inst)++ = (unsigned char)0x11;        \
406                 x86_membase_emit ((inst), (reg), (basereg), (disp));    \
407         } while (0)
408
409 #endif // AMD64_H