Branch data Line data Source code
1 : : /*
2 : : * This file is part of the MicroPython project, http://micropython.org/
3 : : *
4 : : * The MIT License (MIT)
5 : : *
6 : : * Copyright (c) 2013, 2014 Damien P. George
7 : : *
8 : : * Permission is hereby granted, free of charge, to any person obtaining a copy
9 : : * of this software and associated documentation files (the "Software"), to deal
10 : : * in the Software without restriction, including without limitation the rights
11 : : * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : : * copies of the Software, and to permit persons to whom the Software is
13 : : * furnished to do so, subject to the following conditions:
14 : : *
15 : : * The above copyright notice and this permission notice shall be included in
16 : : * all copies or substantial portions of the Software.
17 : : *
18 : : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : : * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 : : * THE SOFTWARE.
25 : : */
26 : :
27 : : #include <stdint.h>
28 : : #include <stdio.h>
29 : : #include <assert.h>
30 : : #include <string.h>
31 : :
32 : : #include "py/mpconfig.h"
33 : :
34 : : // wrapper around everything in this file
35 : : #if MICROPY_EMIT_X64
36 : :
37 : : #include "py/asmx64.h"
38 : :
39 : : /* all offsets are measured in multiples of 8 bytes */
40 : : #define WORD_SIZE (8)
41 : :
42 : : #define OPCODE_NOP (0x90)
43 : : #define OPCODE_PUSH_R64 (0x50) /* +rq */
44 : : #define OPCODE_PUSH_I64 (0x68)
45 : : #define OPCODE_PUSH_M64 (0xff) /* /6 */
46 : : #define OPCODE_POP_R64 (0x58) /* +rq */
47 : : #define OPCODE_RET (0xc3)
48 : : #define OPCODE_MOV_I8_TO_R8 (0xb0) /* +rb */
49 : : #define OPCODE_MOV_I64_TO_R64 (0xb8) /* +rq */
50 : : #define OPCODE_MOV_I32_TO_RM32 (0xc7)
51 : : #define OPCODE_MOV_R8_TO_RM8 (0x88) /* /r */
52 : : #define OPCODE_MOV_R64_TO_RM64 (0x89) /* /r */
53 : : #define OPCODE_MOV_RM64_TO_R64 (0x8b) /* /r */
54 : : #define OPCODE_MOVZX_RM8_TO_R64 (0xb6) /* 0x0f 0xb6/r */
55 : : #define OPCODE_MOVZX_RM16_TO_R64 (0xb7) /* 0x0f 0xb7/r */
56 : : #define OPCODE_LEA_MEM_TO_R64 (0x8d) /* /r */
57 : : #define OPCODE_AND_R64_TO_RM64 (0x21) /* /r */
58 : : #define OPCODE_OR_R64_TO_RM64 (0x09) /* /r */
59 : : #define OPCODE_XOR_R64_TO_RM64 (0x31) /* /r */
60 : : #define OPCODE_ADD_R64_TO_RM64 (0x01) /* /r */
61 : : #define OPCODE_ADD_I32_TO_RM32 (0x81) /* /0 */
62 : : #define OPCODE_ADD_I8_TO_RM32 (0x83) /* /0 */
63 : : #define OPCODE_SUB_R64_FROM_RM64 (0x29)
64 : : #define OPCODE_SUB_I32_FROM_RM64 (0x81) /* /5 */
65 : : #define OPCODE_SUB_I8_FROM_RM64 (0x83) /* /5 */
66 : : // #define OPCODE_SHL_RM32_BY_I8 (0xc1) /* /4 */
67 : : // #define OPCODE_SHR_RM32_BY_I8 (0xc1) /* /5 */
68 : : // #define OPCODE_SAR_RM32_BY_I8 (0xc1) /* /7 */
69 : : #define OPCODE_SHL_RM64_CL (0xd3) /* /4 */
70 : : #define OPCODE_SHR_RM64_CL (0xd3) /* /5 */
71 : : #define OPCODE_SAR_RM64_CL (0xd3) /* /7 */
72 : : // #define OPCODE_CMP_I32_WITH_RM32 (0x81) /* /7 */
73 : : // #define OPCODE_CMP_I8_WITH_RM32 (0x83) /* /7 */
74 : : #define OPCODE_CMP_R64_WITH_RM64 (0x39) /* /r */
75 : : // #define OPCODE_CMP_RM32_WITH_R32 (0x3b)
76 : : #define OPCODE_TEST_R8_WITH_RM8 (0x84) /* /r */
77 : : #define OPCODE_TEST_R64_WITH_RM64 (0x85) /* /r */
78 : : #define OPCODE_JMP_REL8 (0xeb)
79 : : #define OPCODE_JMP_REL32 (0xe9)
80 : : #define OPCODE_JMP_RM64 (0xff) /* /4 */
81 : : #define OPCODE_JCC_REL8 (0x70) /* | jcc type */
82 : : #define OPCODE_JCC_REL32_A (0x0f)
83 : : #define OPCODE_JCC_REL32_B (0x80) /* | jcc type */
84 : : #define OPCODE_SETCC_RM8_A (0x0f)
85 : : #define OPCODE_SETCC_RM8_B (0x90) /* | jcc type, /0 */
86 : : #define OPCODE_CALL_REL32 (0xe8)
87 : : #define OPCODE_CALL_RM32 (0xff) /* /2 */
88 : : #define OPCODE_LEAVE (0xc9)
89 : :
90 : : #define MODRM_R64(x) (((x) & 0x7) << 3)
91 : : #define MODRM_RM_DISP0 (0x00)
92 : : #define MODRM_RM_DISP8 (0x40)
93 : : #define MODRM_RM_DISP32 (0x80)
94 : : #define MODRM_RM_REG (0xc0)
95 : : #define MODRM_RM_R64(x) ((x) & 0x7)
96 : :
97 : : #define OP_SIZE_PREFIX (0x66)
98 : :
99 : : #define REX_PREFIX (0x40)
100 : : #define REX_W (0x08) // width
101 : : #define REX_R (0x04) // register
102 : : #define REX_X (0x02) // index
103 : : #define REX_B (0x01) // base
104 : : #define REX_W_FROM_R64(r64) ((r64) >> 0 & 0x08)
105 : : #define REX_R_FROM_R64(r64) ((r64) >> 1 & 0x04)
106 : : #define REX_X_FROM_R64(r64) ((r64) >> 2 & 0x02)
107 : : #define REX_B_FROM_R64(r64) ((r64) >> 3 & 0x01)
108 : :
109 : : #define IMM32_L0(x) ((x) & 0xff)
110 : : #define IMM32_L1(x) (((x) >> 8) & 0xff)
111 : : #define IMM32_L2(x) (((x) >> 16) & 0xff)
112 : : #define IMM32_L3(x) (((x) >> 24) & 0xff)
113 : : #define IMM64_L4(x) (((x) >> 32) & 0xff)
114 : : #define IMM64_L5(x) (((x) >> 40) & 0xff)
115 : : #define IMM64_L6(x) (((x) >> 48) & 0xff)
116 : : #define IMM64_L7(x) (((x) >> 56) & 0xff)
117 : :
118 : : #define UNSIGNED_FIT8(x) (((x) & 0xffffffffffffff00) == 0)
119 : : #define UNSIGNED_FIT32(x) (((x) & 0xffffffff00000000) == 0)
120 : : #define SIGNED_FIT8(x) (((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80)
121 : :
122 : 4586645 : static inline byte *asm_x64_get_cur_to_write_bytes(asm_x64_t *as, int n) {
123 : 4586645 : return mp_asm_base_get_cur_to_write_bytes(&as->base, n);
124 : : }
125 : :
126 : 2284232 : STATIC void asm_x64_write_byte_1(asm_x64_t *as, byte b1) {
127 : 2284232 : byte *c = asm_x64_get_cur_to_write_bytes(as, 1);
128 [ + + ]: 2284232 : if (c != NULL) {
129 : 752683 : c[0] = b1;
130 : : }
131 : 2284232 : }
132 : :
133 : 1328481 : STATIC void asm_x64_write_byte_2(asm_x64_t *as, byte b1, byte b2) {
134 : 1328481 : byte *c = asm_x64_get_cur_to_write_bytes(as, 2);
135 [ + + ]: 1328481 : if (c != NULL) {
136 : 439740 : c[0] = b1;
137 : 439740 : c[1] = b2;
138 : : }
139 : 1328481 : }
140 : :
141 : 171769 : STATIC void asm_x64_write_byte_3(asm_x64_t *as, byte b1, byte b2, byte b3) {
142 : 171769 : byte *c = asm_x64_get_cur_to_write_bytes(as, 3);
143 [ + + ]: 171769 : if (c != NULL) {
144 : 56830 : c[0] = b1;
145 : 56830 : c[1] = b2;
146 : 56830 : c[2] = b3;
147 : : }
148 : 171769 : }
149 : :
150 : 800267 : STATIC void asm_x64_write_word32(asm_x64_t *as, int w32) {
151 : 800267 : byte *c = asm_x64_get_cur_to_write_bytes(as, 4);
152 [ + + ]: 800267 : if (c != NULL) {
153 : 264399 : c[0] = IMM32_L0(w32);
154 : 264399 : c[1] = IMM32_L1(w32);
155 : 264399 : c[2] = IMM32_L2(w32);
156 : 264399 : c[3] = IMM32_L3(w32);
157 : : }
158 : 800267 : }
159 : :
160 : 1896 : STATIC void asm_x64_write_word64(asm_x64_t *as, int64_t w64) {
161 : 1896 : byte *c = asm_x64_get_cur_to_write_bytes(as, 8);
162 [ + + ]: 1896 : if (c != NULL) {
163 : 632 : c[0] = IMM32_L0(w64);
164 : 632 : c[1] = IMM32_L1(w64);
165 : 632 : c[2] = IMM32_L2(w64);
166 : 632 : c[3] = IMM32_L3(w64);
167 : 632 : c[4] = IMM64_L4(w64);
168 : 632 : c[5] = IMM64_L5(w64);
169 : 632 : c[6] = IMM64_L6(w64);
170 : 632 : c[7] = IMM64_L7(w64);
171 : : }
172 : 1896 : }
173 : :
174 : : /* unused
175 : : STATIC void asm_x64_write_word32_to(asm_x64_t *as, int offset, int w32) {
176 : : byte* c;
177 : : assert(offset + 4 <= as->code_size);
178 : : c = as->code_base + offset;
179 : : c[0] = IMM32_L0(w32);
180 : : c[1] = IMM32_L1(w32);
181 : : c[2] = IMM32_L2(w32);
182 : : c[3] = IMM32_L3(w32);
183 : : }
184 : : */
185 : :
186 : 918304 : STATIC void asm_x64_write_r64_disp(asm_x64_t *as, int r64, int disp_r64, int disp_offset) {
187 : 918304 : uint8_t rm_disp;
188 [ + + + + ]: 918304 : if (disp_offset == 0 && (disp_r64 & 7) != ASM_X64_REG_RBP) {
189 : : rm_disp = MODRM_RM_DISP0;
190 [ + + + - ]: 906120 : } else if (SIGNED_FIT8(disp_offset)) {
191 : : rm_disp = MODRM_RM_DISP8;
192 : : } else {
193 : 404944 : rm_disp = MODRM_RM_DISP32;
194 : : }
195 : 918304 : asm_x64_write_byte_1(as, MODRM_R64(r64) | rm_disp | MODRM_RM_R64(disp_r64));
196 [ + + ]: 918304 : if ((disp_r64 & 7) == ASM_X64_REG_RSP) {
197 : : // Special case for rsp and r12, they need a SIB byte
198 : 454168 : asm_x64_write_byte_1(as, 0x24);
199 : : }
200 [ + + ]: 918304 : if (rm_disp == MODRM_RM_DISP8) {
201 : 501176 : asm_x64_write_byte_1(as, IMM32_L0(disp_offset));
202 [ + + ]: 417128 : } else if (rm_disp == MODRM_RM_DISP32) {
203 : 404944 : asm_x64_write_word32(as, disp_offset);
204 : : }
205 : 918304 : }
206 : :
207 : 137255 : STATIC void asm_x64_generic_r64_r64(asm_x64_t *as, int dest_r64, int src_r64, int op) {
208 : 137255 : asm_x64_write_byte_3(as, REX_PREFIX | REX_W | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), op, MODRM_R64(src_r64) | MODRM_RM_REG | MODRM_RM_R64(dest_r64));
209 : 137255 : }
210 : :
211 : 0 : void asm_x64_nop(asm_x64_t *as) {
212 : 0 : asm_x64_write_byte_1(as, OPCODE_NOP);
213 : 0 : }
214 : :
215 : 48084 : void asm_x64_push_r64(asm_x64_t *as, int src_r64) {
216 [ + + ]: 48084 : if (src_r64 < 8) {
217 : 24042 : asm_x64_write_byte_1(as, OPCODE_PUSH_R64 | src_r64);
218 : : } else {
219 : 24042 : asm_x64_write_byte_2(as, REX_PREFIX | REX_B, OPCODE_PUSH_R64 | (src_r64 & 7));
220 : : }
221 : 48084 : }
222 : :
223 : : /*
224 : : void asm_x64_push_i32(asm_x64_t *as, int src_i32) {
225 : : asm_x64_write_byte_1(as, OPCODE_PUSH_I64);
226 : : asm_x64_write_word32(as, src_i32); // will be sign extended to 64 bits
227 : : }
228 : : */
229 : :
230 : : /*
231 : : void asm_x64_push_disp(asm_x64_t *as, int src_r64, int src_offset) {
232 : : assert(src_r64 < 8);
233 : : asm_x64_write_byte_1(as, OPCODE_PUSH_M64);
234 : : asm_x64_write_r64_disp(as, 6, src_r64, src_offset);
235 : : }
236 : : */
237 : :
238 : 50884 : void asm_x64_pop_r64(asm_x64_t *as, int dest_r64) {
239 [ + + ]: 50884 : if (dest_r64 < 8) {
240 : 25442 : asm_x64_write_byte_1(as, OPCODE_POP_R64 | dest_r64);
241 : : } else {
242 : 25442 : asm_x64_write_byte_2(as, REX_PREFIX | REX_B, OPCODE_POP_R64 | (dest_r64 & 7));
243 : : }
244 : 50884 : }
245 : :
246 : 12721 : STATIC void asm_x64_ret(asm_x64_t *as) {
247 : 12721 : asm_x64_write_byte_1(as, OPCODE_RET);
248 : 12721 : }
249 : :
250 : 104631 : void asm_x64_mov_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
251 : 104631 : asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_MOV_R64_TO_RM64);
252 : 104631 : }
253 : :
254 : 60 : void asm_x64_mov_r8_to_mem8(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp) {
255 [ + - ]: 60 : if (src_r64 < 8 && dest_r64 < 8) {
256 : 60 : asm_x64_write_byte_1(as, OPCODE_MOV_R8_TO_RM8);
257 : : } else {
258 : 0 : asm_x64_write_byte_2(as, REX_PREFIX | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), OPCODE_MOV_R8_TO_RM8);
259 : : }
260 : 60 : asm_x64_write_r64_disp(as, src_r64, dest_r64, dest_disp);
261 : 60 : }
262 : :
263 : 24 : void asm_x64_mov_r16_to_mem16(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp) {
264 [ + - ]: 24 : if (src_r64 < 8 && dest_r64 < 8) {
265 : 24 : asm_x64_write_byte_2(as, OP_SIZE_PREFIX, OPCODE_MOV_R64_TO_RM64);
266 : : } else {
267 : 0 : asm_x64_write_byte_3(as, OP_SIZE_PREFIX, REX_PREFIX | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), OPCODE_MOV_R64_TO_RM64);
268 : : }
269 : 24 : asm_x64_write_r64_disp(as, src_r64, dest_r64, dest_disp);
270 : 24 : }
271 : :
272 : 32 : void asm_x64_mov_r32_to_mem32(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp) {
273 [ + - ]: 32 : if (src_r64 < 8 && dest_r64 < 8) {
274 : 32 : asm_x64_write_byte_1(as, OPCODE_MOV_R64_TO_RM64);
275 : : } else {
276 : 0 : asm_x64_write_byte_2(as, REX_PREFIX | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), OPCODE_MOV_R64_TO_RM64);
277 : : }
278 : 32 : asm_x64_write_r64_disp(as, src_r64, dest_r64, dest_disp);
279 : 32 : }
280 : :
281 : 234327 : void asm_x64_mov_r64_to_mem64(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp) {
282 : : // use REX prefix for 64 bit operation
283 : 234327 : asm_x64_write_byte_2(as, REX_PREFIX | REX_W | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), OPCODE_MOV_R64_TO_RM64);
284 : 234327 : asm_x64_write_r64_disp(as, src_r64, dest_r64, dest_disp);
285 : 234327 : }
286 : :
287 : 90 : void asm_x64_mov_mem8_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) {
288 [ + - ]: 90 : if (src_r64 < 8 && dest_r64 < 8) {
289 : 90 : asm_x64_write_byte_2(as, 0x0f, OPCODE_MOVZX_RM8_TO_R64);
290 : : } else {
291 : 0 : asm_x64_write_byte_3(as, REX_PREFIX | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64), 0x0f, OPCODE_MOVZX_RM8_TO_R64);
292 : : }
293 : 90 : asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp);
294 : 90 : }
295 : :
296 : 24 : void asm_x64_mov_mem16_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) {
297 [ + - ]: 24 : if (src_r64 < 8 && dest_r64 < 8) {
298 : 24 : asm_x64_write_byte_2(as, 0x0f, OPCODE_MOVZX_RM16_TO_R64);
299 : : } else {
300 : 0 : asm_x64_write_byte_3(as, REX_PREFIX | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64), 0x0f, OPCODE_MOVZX_RM16_TO_R64);
301 : : }
302 : 24 : asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp);
303 : 24 : }
304 : :
305 : 24 : void asm_x64_mov_mem32_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) {
306 [ + - ]: 24 : if (src_r64 < 8 && dest_r64 < 8) {
307 : 24 : asm_x64_write_byte_1(as, OPCODE_MOV_RM64_TO_R64);
308 : : } else {
309 : 0 : asm_x64_write_byte_2(as, REX_PREFIX | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64), OPCODE_MOV_RM64_TO_R64);
310 : : }
311 : 24 : asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp);
312 : 24 : }
313 : :
314 : 605334 : void asm_x64_mov_mem64_to_r64(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) {
315 : : // use REX prefix for 64 bit operation
316 : 605334 : asm_x64_write_byte_2(as, REX_PREFIX | REX_W | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64), OPCODE_MOV_RM64_TO_R64);
317 : 605334 : asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp);
318 : 605334 : }
319 : :
320 : 78389 : STATIC void asm_x64_lea_disp_to_r64(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) {
321 : : // use REX prefix for 64 bit operation
322 : 78389 : asm_x64_write_byte_2(as, REX_PREFIX | REX_W | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64), OPCODE_LEA_MEM_TO_R64);
323 : 78389 : asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp);
324 : 78389 : }
325 : :
326 : : /*
327 : : void asm_x64_mov_i8_to_r8(asm_x64_t *as, int src_i8, int dest_r64) {
328 : : assert(dest_r64 < 8);
329 : : asm_x64_write_byte_2(as, OPCODE_MOV_I8_TO_R8 | dest_r64, src_i8);
330 : : }
331 : : */
332 : :
333 : 309550 : size_t asm_x64_mov_i32_to_r64(asm_x64_t *as, int src_i32, int dest_r64) {
334 : : // cpu defaults to i32 to r64, with zero extension
335 [ + + ]: 309550 : if (dest_r64 < 8) {
336 : 309487 : asm_x64_write_byte_1(as, OPCODE_MOV_I64_TO_R64 | dest_r64);
337 : : } else {
338 : 63 : asm_x64_write_byte_2(as, REX_PREFIX | REX_B, OPCODE_MOV_I64_TO_R64 | (dest_r64 & 7));
339 : : }
340 : 309550 : size_t loc = mp_asm_base_get_code_pos(&as->base);
341 : 309550 : asm_x64_write_word32(as, src_i32);
342 : 309550 : return loc;
343 : : }
344 : :
345 : 1896 : void asm_x64_mov_i64_to_r64(asm_x64_t *as, int64_t src_i64, int dest_r64) {
346 : : // cpu defaults to i32 to r64
347 : : // to mov i64 to r64 need to use REX prefix
348 : 1896 : asm_x64_write_byte_2(as,
349 : : REX_PREFIX | REX_W | (dest_r64 < 8 ? 0 : REX_B),
350 [ + - ]: 1896 : OPCODE_MOV_I64_TO_R64 | (dest_r64 & 7));
351 : 1896 : asm_x64_write_word64(as, src_i64);
352 : 1896 : }
353 : :
354 : 311446 : void asm_x64_mov_i64_to_r64_optimised(asm_x64_t *as, int64_t src_i64, int dest_r64) {
355 : : // TODO use movzx, movsx if possible
356 [ + + ]: 311446 : if (UNSIGNED_FIT32(src_i64)) {
357 : : // 5 bytes
358 : 309550 : asm_x64_mov_i32_to_r64(as, src_i64 & 0xffffffff, dest_r64);
359 : : } else {
360 : : // 10 bytes
361 : 1896 : asm_x64_mov_i64_to_r64(as, src_i64, dest_r64);
362 : : }
363 : 311446 : }
364 : :
365 : 60 : void asm_x64_and_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
366 : 60 : asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_AND_R64_TO_RM64);
367 : 60 : }
368 : :
369 : 60 : void asm_x64_or_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
370 : 60 : asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_OR_R64_TO_RM64);
371 : 60 : }
372 : :
373 : 9952 : void asm_x64_xor_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
374 : 9952 : asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_XOR_R64_TO_RM64);
375 : 9952 : }
376 : :
377 : 48 : void asm_x64_shl_r64_cl(asm_x64_t *as, int dest_r64) {
378 : 48 : asm_x64_generic_r64_r64(as, dest_r64, 4, OPCODE_SHL_RM64_CL);
379 : 48 : }
380 : :
381 : 6 : void asm_x64_shr_r64_cl(asm_x64_t *as, int dest_r64) {
382 : 6 : asm_x64_generic_r64_r64(as, dest_r64, 5, OPCODE_SHR_RM64_CL);
383 : 6 : }
384 : :
385 : 42 : void asm_x64_sar_r64_cl(asm_x64_t *as, int dest_r64) {
386 : 42 : asm_x64_generic_r64_r64(as, dest_r64, 7, OPCODE_SAR_RM64_CL);
387 : 42 : }
388 : :
389 : 6151 : void asm_x64_add_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
390 : 6151 : asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_ADD_R64_TO_RM64);
391 : 6151 : }
392 : :
393 : 24 : void asm_x64_sub_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
394 : 24 : asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_SUB_R64_FROM_RM64);
395 : 24 : }
396 : :
397 : 24 : void asm_x64_mul_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
398 : : // imul reg64, reg/mem64 -- 0x0f 0xaf /r
399 : 24 : asm_x64_write_byte_1(as, REX_PREFIX | REX_W | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64));
400 : 24 : asm_x64_write_byte_3(as, 0x0f, 0xaf, MODRM_R64(dest_r64) | MODRM_RM_REG | MODRM_RM_R64(src_r64));
401 : 24 : }
402 : :
403 : : /*
404 : : void asm_x64_sub_i32_from_r32(asm_x64_t *as, int src_i32, int dest_r32) {
405 : : if (SIGNED_FIT8(src_i32)) {
406 : : // defaults to 32 bit operation
407 : : asm_x64_write_byte_2(as, OPCODE_SUB_I8_FROM_RM64, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(dest_r32));
408 : : asm_x64_write_byte_1(as, src_i32 & 0xff);
409 : : } else {
410 : : // defaults to 32 bit operation
411 : : asm_x64_write_byte_2(as, OPCODE_SUB_I32_FROM_RM64, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(dest_r32));
412 : : asm_x64_write_word32(as, src_i32);
413 : : }
414 : : }
415 : : */
416 : :
417 : 24742 : STATIC void asm_x64_sub_r64_i32(asm_x64_t *as, int dest_r64, int src_i32) {
418 [ - + ]: 24742 : assert(dest_r64 < 8);
419 [ + + + + ]: 24742 : if (SIGNED_FIT8(src_i32)) {
420 : : // use REX prefix for 64 bit operation
421 : 9478 : asm_x64_write_byte_3(as, REX_PREFIX | REX_W, OPCODE_SUB_I8_FROM_RM64, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(dest_r64));
422 : 9478 : asm_x64_write_byte_1(as, src_i32 & 0xff);
423 : : } else {
424 : : // use REX prefix for 64 bit operation
425 : 15264 : asm_x64_write_byte_3(as, REX_PREFIX | REX_W, OPCODE_SUB_I32_FROM_RM64, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(dest_r64));
426 : 15264 : asm_x64_write_word32(as, src_i32);
427 : : }
428 : 24742 : }
429 : :
430 : : /*
431 : : void asm_x64_shl_r32_by_imm(asm_x64_t *as, int r32, int imm) {
432 : : asm_x64_write_byte_2(as, OPCODE_SHL_RM32_BY_I8, MODRM_R64(4) | MODRM_RM_REG | MODRM_RM_R64(r32));
433 : : asm_x64_write_byte_1(as, imm);
434 : : }
435 : :
436 : : void asm_x64_shr_r32_by_imm(asm_x64_t *as, int r32, int imm) {
437 : : asm_x64_write_byte_2(as, OPCODE_SHR_RM32_BY_I8, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(r32));
438 : : asm_x64_write_byte_1(as, imm);
439 : : }
440 : :
441 : : void asm_x64_sar_r32_by_imm(asm_x64_t *as, int r32, int imm) {
442 : : asm_x64_write_byte_2(as, OPCODE_SAR_RM32_BY_I8, MODRM_R64(7) | MODRM_RM_REG | MODRM_RM_R64(r32));
443 : : asm_x64_write_byte_1(as, imm);
444 : : }
445 : : */
446 : :
447 : 976 : void asm_x64_cmp_r64_with_r64(asm_x64_t *as, int src_r64_a, int src_r64_b) {
448 : 976 : asm_x64_generic_r64_r64(as, src_r64_b, src_r64_a, OPCODE_CMP_R64_WITH_RM64);
449 : 976 : }
450 : :
451 : : /*
452 : : void asm_x64_cmp_i32_with_r32(asm_x64_t *as, int src_i32, int src_r32) {
453 : : if (SIGNED_FIT8(src_i32)) {
454 : : asm_x64_write_byte_2(as, OPCODE_CMP_I8_WITH_RM32, MODRM_R64(7) | MODRM_RM_REG | MODRM_RM_R64(src_r32));
455 : : asm_x64_write_byte_1(as, src_i32 & 0xff);
456 : : } else {
457 : : asm_x64_write_byte_2(as, OPCODE_CMP_I32_WITH_RM32, MODRM_R64(7) | MODRM_RM_REG | MODRM_RM_R64(src_r32));
458 : : asm_x64_write_word32(as, src_i32);
459 : : }
460 : : }
461 : : */
462 : :
463 : 18786 : void asm_x64_test_r8_with_r8(asm_x64_t *as, int src_r64_a, int src_r64_b) {
464 [ - + ]: 18786 : assert(src_r64_a < 8);
465 [ - + ]: 18786 : assert(src_r64_b < 8);
466 : 18786 : asm_x64_write_byte_2(as, OPCODE_TEST_R8_WITH_RM8, MODRM_R64(src_r64_a) | MODRM_RM_REG | MODRM_RM_R64(src_r64_b));
467 : 18786 : }
468 : :
469 : 15305 : void asm_x64_test_r64_with_r64(asm_x64_t *as, int src_r64_a, int src_r64_b) {
470 : 15305 : asm_x64_generic_r64_r64(as, src_r64_b, src_r64_a, OPCODE_TEST_R64_WITH_RM64);
471 : 15305 : }
472 : :
473 : 268 : void asm_x64_setcc_r8(asm_x64_t *as, int jcc_type, int dest_r8) {
474 [ - + ]: 268 : assert(dest_r8 < 8);
475 : 268 : asm_x64_write_byte_3(as, OPCODE_SETCC_RM8_A, OPCODE_SETCC_RM8_B | jcc_type, MODRM_R64(0) | MODRM_RM_REG | MODRM_RM_R64(dest_r8));
476 : 268 : }
477 : :
478 : 3110 : void asm_x64_jmp_reg(asm_x64_t *as, int src_r64) {
479 [ - + ]: 3110 : assert(src_r64 < 8);
480 : 3110 : asm_x64_write_byte_2(as, OPCODE_JMP_RM64, MODRM_R64(4) | MODRM_RM_REG | MODRM_RM_R64(src_r64));
481 : 3110 : }
482 : :
483 : 73955 : STATIC mp_uint_t get_label_dest(asm_x64_t *as, mp_uint_t label) {
484 [ - + ]: 73955 : assert(label < as->base.max_num_labels);
485 : 73955 : return as->base.label_offsets[label];
486 : : }
487 : :
488 : 29676 : void asm_x64_jmp_label(asm_x64_t *as, mp_uint_t label) {
489 : 29676 : mp_uint_t dest = get_label_dest(as, label);
490 : 29676 : mp_int_t rel = dest - as->base.code_offset;
491 [ + + ]: 29676 : if (dest != (mp_uint_t)-1 && rel < 0) {
492 : : // is a backwards jump, so we know the size of the jump on the first pass
493 : : // calculate rel assuming 8 bit relative jump
494 : 1530 : rel -= 2;
495 [ + + ]: 1530 : if (SIGNED_FIT8(rel)) {
496 : 402 : asm_x64_write_byte_2(as, OPCODE_JMP_REL8, rel & 0xff);
497 : : } else {
498 : 1128 : rel += 2;
499 : 1128 : goto large_jump;
500 : : }
501 : : } else {
502 : : // is a forwards jump, so need to assume it's large
503 : 28146 : large_jump:
504 : 29274 : rel -= 5;
505 : 29274 : asm_x64_write_byte_1(as, OPCODE_JMP_REL32);
506 : 29274 : asm_x64_write_word32(as, rel);
507 : : }
508 : 29676 : }
509 : :
510 : 34799 : void asm_x64_jcc_label(asm_x64_t *as, int jcc_type, mp_uint_t label) {
511 : 34799 : mp_uint_t dest = get_label_dest(as, label);
512 : 34799 : mp_int_t rel = dest - as->base.code_offset;
513 [ + + ]: 34799 : if (dest != (mp_uint_t)-1 && rel < 0) {
514 : : // is a backwards jump, so we know the size of the jump on the first pass
515 : : // calculate rel assuming 8 bit relative jump
516 : 3930 : rel -= 2;
517 [ + + ]: 3930 : if (SIGNED_FIT8(rel)) {
518 : 3044 : asm_x64_write_byte_2(as, OPCODE_JCC_REL8 | jcc_type, rel & 0xff);
519 : : } else {
520 : 886 : rel += 2;
521 : 886 : goto large_jump;
522 : : }
523 : : } else {
524 : : // is a forwards jump, so need to assume it's large
525 : 30869 : large_jump:
526 : 31755 : rel -= 6;
527 : 31755 : asm_x64_write_byte_2(as, OPCODE_JCC_REL32_A, OPCODE_JCC_REL32_B | jcc_type);
528 : 31755 : asm_x64_write_word32(as, rel);
529 : : }
530 : 34799 : }
531 : :
532 : 12021 : void asm_x64_entry(asm_x64_t *as, int num_locals) {
533 [ - + ]: 12021 : assert(num_locals >= 0);
534 : 12021 : asm_x64_push_r64(as, ASM_X64_REG_RBP);
535 : 12021 : asm_x64_push_r64(as, ASM_X64_REG_RBX);
536 : 12021 : asm_x64_push_r64(as, ASM_X64_REG_R12);
537 : 12021 : asm_x64_push_r64(as, ASM_X64_REG_R13);
538 : 12021 : num_locals |= 1; // make it odd so stack is aligned on 16 byte boundary
539 : 12021 : asm_x64_sub_r64_i32(as, ASM_X64_REG_RSP, num_locals * WORD_SIZE);
540 : 12021 : as->num_locals = num_locals;
541 : 12021 : }
542 : :
543 : 12721 : void asm_x64_exit(asm_x64_t *as) {
544 : 12721 : asm_x64_sub_r64_i32(as, ASM_X64_REG_RSP, -as->num_locals * WORD_SIZE);
545 : 12721 : asm_x64_pop_r64(as, ASM_X64_REG_R13);
546 : 12721 : asm_x64_pop_r64(as, ASM_X64_REG_R12);
547 : 12721 : asm_x64_pop_r64(as, ASM_X64_REG_RBX);
548 : 12721 : asm_x64_pop_r64(as, ASM_X64_REG_RBP);
549 : 12721 : asm_x64_ret(as);
550 : 12721 : }
551 : :
552 : : // locals:
553 : : // - stored on the stack in ascending order
554 : : // - numbered 0 through as->num_locals-1
555 : : // - RSP points to the first local
556 : : //
557 : : // | RSP
558 : : // v
559 : : // l0 l1 l2 ... l(n-1)
560 : : // ^ ^
561 : : // | low address | high address in RAM
562 : : //
563 : 465744 : STATIC int asm_x64_local_offset_from_rsp(asm_x64_t *as, int local_num) {
564 : 465744 : (void)as;
565 : : // Stack is full descending, RSP points to local0
566 : 465744 : return local_num * WORD_SIZE;
567 : : }
568 : :
569 : 153583 : void asm_x64_mov_local_to_r64(asm_x64_t *as, int src_local_num, int dest_r64) {
570 : 153583 : asm_x64_mov_mem64_to_r64(as, ASM_X64_REG_RSP, asm_x64_local_offset_from_rsp(as, src_local_num), dest_r64);
571 : 153583 : }
572 : :
573 : 222097 : void asm_x64_mov_r64_to_local(asm_x64_t *as, int src_r64, int dest_local_num) {
574 : 222097 : asm_x64_mov_r64_to_mem64(as, src_r64, ASM_X64_REG_RSP, asm_x64_local_offset_from_rsp(as, dest_local_num));
575 : 222097 : }
576 : :
577 : 90064 : void asm_x64_mov_local_addr_to_r64(asm_x64_t *as, int local_num, int dest_r64) {
578 : 90064 : int offset = asm_x64_local_offset_from_rsp(as, local_num);
579 [ + + ]: 90064 : if (offset == 0) {
580 : 11675 : asm_x64_mov_r64_r64(as, dest_r64, ASM_X64_REG_RSP);
581 : : } else {
582 : 78389 : asm_x64_lea_disp_to_r64(as, ASM_X64_REG_RSP, offset, dest_r64);
583 : : }
584 : 90064 : }
585 : :
586 : 9480 : void asm_x64_mov_reg_pcrel(asm_x64_t *as, int dest_r64, mp_uint_t label) {
587 : 9480 : mp_uint_t dest = get_label_dest(as, label);
588 : 9480 : mp_int_t rel = dest - (as->base.code_offset + 7);
589 : 9480 : asm_x64_write_byte_3(as, REX_PREFIX | REX_W | REX_R_FROM_R64(dest_r64), OPCODE_LEA_MEM_TO_R64, MODRM_R64(dest_r64) | MODRM_RM_R64(5));
590 : 9480 : asm_x64_write_word32(as, rel);
591 : 9480 : }
592 : :
593 : : /*
594 : : void asm_x64_push_local(asm_x64_t *as, int local_num) {
595 : : asm_x64_push_disp(as, ASM_X64_REG_RSP, asm_x64_local_offset_from_rsp(as, local_num));
596 : : }
597 : :
598 : : void asm_x64_push_local_addr(asm_x64_t *as, int local_num, int temp_r64) {
599 : : asm_x64_mov_r64_r64(as, temp_r64, ASM_X64_REG_RSP);
600 : : asm_x64_add_i32_to_r32(as, asm_x64_local_offset_from_rsp(as, local_num), temp_r64);
601 : : asm_x64_push_r64(as, temp_r64);
602 : : }
603 : : */
604 : :
605 : : /*
606 : : can't use these because code might be relocated when resized
607 : :
608 : : void asm_x64_call(asm_x64_t *as, void* func) {
609 : : asm_x64_sub_i32_from_r32(as, 8, ASM_X64_REG_RSP);
610 : : asm_x64_write_byte_1(as, OPCODE_CALL_REL32);
611 : : asm_x64_write_word32(as, func - (void*)(as->code_cur + 4));
612 : : asm_x64_mov_r64_r64(as, ASM_X64_REG_RSP, ASM_X64_REG_RBP);
613 : : }
614 : :
615 : : void asm_x64_call_i1(asm_x64_t *as, void* func, int i1) {
616 : : asm_x64_sub_i32_from_r32(as, 8, ASM_X64_REG_RSP);
617 : : asm_x64_sub_i32_from_r32(as, 12, ASM_X64_REG_RSP);
618 : : asm_x64_push_i32(as, i1);
619 : : asm_x64_write_byte_1(as, OPCODE_CALL_REL32);
620 : : asm_x64_write_word32(as, func - (void*)(as->code_cur + 4));
621 : : asm_x64_add_i32_to_r32(as, 16, ASM_X64_REG_RSP);
622 : : asm_x64_mov_r64_r64(as, ASM_X64_REG_RSP, ASM_X64_REG_RBP);
623 : : }
624 : : */
625 : :
626 : 301753 : void asm_x64_call_ind(asm_x64_t *as, size_t fun_id, int temp_r64) {
627 [ - + ]: 301753 : assert(temp_r64 < 8);
628 : 301753 : asm_x64_mov_mem64_to_r64(as, ASM_X64_REG_FUN_TABLE, fun_id * WORD_SIZE, temp_r64);
629 : 301753 : asm_x64_write_byte_2(as, OPCODE_CALL_RM32, MODRM_R64(2) | MODRM_RM_REG | MODRM_RM_R64(temp_r64));
630 : 301753 : }
631 : :
632 : : #endif // MICROPY_EMIT_X64
|