blob: 01966a2626dd6127554e55bd318ea5289de9399a [file] [log] [blame]
Damien Georgec90f59e2014-09-06 23:06:36 +01001/*
2 * This file is part of the Micro Python project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 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 "mpconfig.h"
33#include "misc.h"
34
35// wrapper around everything in this file
36#if MICROPY_EMIT_X86
37
38#include "asmx86.h"
39
40/* all offsets are measured in multiples of 4 bytes */
41#define WORD_SIZE (4)
42
43#define OPCODE_NOP (0x90)
44#define OPCODE_PUSH_R32 (0x50)
45//#define OPCODE_PUSH_I32 (0x68)
46//#define OPCODE_PUSH_M32 (0xff) /* /6 */
47#define OPCODE_POP_R32 (0x58)
48#define OPCODE_RET (0xc3)
49//#define OPCODE_MOV_I8_TO_R8 (0xb0) /* +rb */
50#define OPCODE_MOV_I32_TO_R32 (0xb8)
51//#define OPCODE_MOV_I32_TO_RM32 (0xc7)
Damien Georged66e4862014-09-29 10:13:49 +010052#define OPCODE_MOV_R8_TO_RM8 (0x88) /* /r */
Damien George91cfd412014-10-12 16:59:29 +010053#define OPCODE_MOV_R32_TO_RM32 (0x89) /* /r */
54#define OPCODE_MOV_RM32_TO_R32 (0x8b) /* /r */
55#define OPCODE_MOVZX_RM8_TO_R32 (0xb6) /* 0x0f 0xb6/r */
56#define OPCODE_MOVZX_RM16_TO_R32 (0xb7) /* 0x0f 0xb7/r */
Damien Georgec90f59e2014-09-06 23:06:36 +010057#define OPCODE_LEA_MEM_TO_R32 (0x8d) /* /r */
Damien George1ef23482014-10-12 14:21:06 +010058#define OPCODE_AND_R32_TO_RM32 (0x21) /* /r */
59#define OPCODE_OR_R32_TO_RM32 (0x09) /* /r */
Damien Georgec90f59e2014-09-06 23:06:36 +010060#define OPCODE_XOR_R32_TO_RM32 (0x31) /* /r */
61#define OPCODE_ADD_R32_TO_RM32 (0x01)
Damien George03281b32014-09-06 22:38:50 +000062#define OPCODE_ADD_I32_TO_RM32 (0x81) /* /0 */
63#define OPCODE_ADD_I8_TO_RM32 (0x83) /* /0 */
Damien George3112cde2014-09-29 18:45:42 +010064#define OPCODE_SUB_R32_FROM_RM32 (0x29)
Damien Georgec90f59e2014-09-06 23:06:36 +010065#define OPCODE_SUB_I32_FROM_RM32 (0x81) /* /5 */
66#define OPCODE_SUB_I8_FROM_RM32 (0x83) /* /5 */
67//#define OPCODE_SHL_RM32_BY_I8 (0xc1) /* /4 */
68//#define OPCODE_SHR_RM32_BY_I8 (0xc1) /* /5 */
69//#define OPCODE_SAR_RM32_BY_I8 (0xc1) /* /7 */
Damien George3112cde2014-09-29 18:45:42 +010070#define OPCODE_SHL_RM32_CL (0xd3) /* /4 */
71#define OPCODE_SAR_RM32_CL (0xd3) /* /7 */
Damien Georgec90f59e2014-09-06 23:06:36 +010072//#define OPCODE_CMP_I32_WITH_RM32 (0x81) /* /7 */
73//#define OPCODE_CMP_I8_WITH_RM32 (0x83) /* /7 */
74#define OPCODE_CMP_R32_WITH_RM32 (0x39)
75//#define OPCODE_CMP_RM32_WITH_R32 (0x3b)
76#define OPCODE_TEST_R8_WITH_RM8 (0x84) /* /r */
77#define OPCODE_JMP_REL8 (0xeb)
78#define OPCODE_JMP_REL32 (0xe9)
79#define OPCODE_JCC_REL8 (0x70) /* | jcc type */
80#define OPCODE_JCC_REL32_A (0x0f)
81#define OPCODE_JCC_REL32_B (0x80) /* | jcc type */
82#define OPCODE_SETCC_RM8_A (0x0f)
83#define OPCODE_SETCC_RM8_B (0x90) /* | jcc type, /0 */
84#define OPCODE_CALL_REL32 (0xe8)
85#define OPCODE_CALL_RM32 (0xff) /* /2 */
86#define OPCODE_LEAVE (0xc9)
87
88#define MODRM_R32(x) ((x) << 3)
89#define MODRM_RM_DISP0 (0x00)
90#define MODRM_RM_DISP8 (0x40)
91#define MODRM_RM_DISP32 (0x80)
92#define MODRM_RM_REG (0xc0)
93#define MODRM_RM_R32(x) (x)
94
Damien Georged66e4862014-09-29 10:13:49 +010095#define OP_SIZE_PREFIX (0x66)
96
Damien Georgec90f59e2014-09-06 23:06:36 +010097#define IMM32_L0(x) ((x) & 0xff)
98#define IMM32_L1(x) (((x) >> 8) & 0xff)
99#define IMM32_L2(x) (((x) >> 16) & 0xff)
100#define IMM32_L3(x) (((x) >> 24) & 0xff)
101
102#define SIGNED_FIT8(x) (((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80)
103
104struct _asm_x86_t {
105 uint pass;
106 mp_uint_t code_offset;
107 mp_uint_t code_size;
108 byte *code_base;
109 byte dummy_data[8];
110
Damien George0b610de2014-09-29 16:25:04 +0100111 mp_uint_t max_num_labels;
112 mp_uint_t *label_offsets;
Damien Georgec90f59e2014-09-06 23:06:36 +0100113 int num_locals;
114};
115
116asm_x86_t *asm_x86_new(mp_uint_t max_num_labels) {
117 asm_x86_t *as;
118
119 as = m_new0(asm_x86_t, 1);
120 as->max_num_labels = max_num_labels;
Damien George0b610de2014-09-29 16:25:04 +0100121 as->label_offsets = m_new(mp_uint_t, max_num_labels);
Damien Georgec90f59e2014-09-06 23:06:36 +0100122
123 return as;
124}
125
126void asm_x86_free(asm_x86_t *as, bool free_code) {
127 if (free_code) {
128 MP_PLAT_FREE_EXEC(as->code_base, as->code_size);
129 }
Damien George0b610de2014-09-29 16:25:04 +0100130 m_del(mp_uint_t, as->label_offsets, as->max_num_labels);
Damien Georgec90f59e2014-09-06 23:06:36 +0100131 m_del_obj(asm_x86_t, as);
132}
133
134void asm_x86_start_pass(asm_x86_t *as, mp_uint_t pass) {
135 as->pass = pass;
136 as->code_offset = 0;
137 if (pass == ASM_X86_PASS_COMPUTE) {
138 // reset all labels
Damien George0b610de2014-09-29 16:25:04 +0100139 memset(as->label_offsets, -1, as->max_num_labels * sizeof(mp_uint_t));
Damien Georgec90f59e2014-09-06 23:06:36 +0100140 }
141}
142
143void asm_x86_end_pass(asm_x86_t *as) {
144 if (as->pass == ASM_X86_PASS_COMPUTE) {
145 MP_PLAT_ALLOC_EXEC(as->code_offset, (void**) &as->code_base, &as->code_size);
146 if(as->code_base == NULL) {
147 assert(0);
148 }
149 }
150}
151
152// all functions must go through this one to emit bytes
153STATIC byte *asm_x86_get_cur_to_write_bytes(asm_x86_t *as, int num_bytes_to_write) {
154 //printf("emit %d\n", num_bytes_to_write);
155 if (as->pass < ASM_X86_PASS_EMIT) {
156 as->code_offset += num_bytes_to_write;
157 return as->dummy_data;
158 } else {
159 assert(as->code_offset + num_bytes_to_write <= as->code_size);
160 byte *c = as->code_base + as->code_offset;
161 as->code_offset += num_bytes_to_write;
162 return c;
163 }
164}
165
166mp_uint_t asm_x86_get_code_size(asm_x86_t *as) {
167 return as->code_size;
168}
169
170void *asm_x86_get_code(asm_x86_t *as) {
171 return as->code_base;
172}
173
174STATIC void asm_x86_write_byte_1(asm_x86_t *as, byte b1) {
175 byte* c = asm_x86_get_cur_to_write_bytes(as, 1);
176 c[0] = b1;
177}
178
179STATIC void asm_x86_write_byte_2(asm_x86_t *as, byte b1, byte b2) {
180 byte* c = asm_x86_get_cur_to_write_bytes(as, 2);
181 c[0] = b1;
182 c[1] = b2;
183}
184
185STATIC void asm_x86_write_byte_3(asm_x86_t *as, byte b1, byte b2, byte b3) {
186 byte* c = asm_x86_get_cur_to_write_bytes(as, 3);
187 c[0] = b1;
188 c[1] = b2;
189 c[2] = b3;
190}
191
192STATIC void asm_x86_write_word32(asm_x86_t *as, int w32) {
193 byte* c = asm_x86_get_cur_to_write_bytes(as, 4);
194 c[0] = IMM32_L0(w32);
195 c[1] = IMM32_L1(w32);
196 c[2] = IMM32_L2(w32);
197 c[3] = IMM32_L3(w32);
198}
199
200STATIC void asm_x86_write_r32_disp(asm_x86_t *as, int r32, int disp_r32, int disp_offset) {
Damien George0b610de2014-09-29 16:25:04 +0100201 assert(disp_r32 != ASM_X86_REG_ESP);
Damien Georgec90f59e2014-09-06 23:06:36 +0100202
Damien George0b610de2014-09-29 16:25:04 +0100203 if (disp_offset == 0 && disp_r32 != ASM_X86_REG_EBP) {
Damien Georgec90f59e2014-09-06 23:06:36 +0100204 asm_x86_write_byte_1(as, MODRM_R32(r32) | MODRM_RM_DISP0 | MODRM_RM_R32(disp_r32));
205 } else if (SIGNED_FIT8(disp_offset)) {
206 asm_x86_write_byte_2(as, MODRM_R32(r32) | MODRM_RM_DISP8 | MODRM_RM_R32(disp_r32), IMM32_L0(disp_offset));
207 } else {
208 asm_x86_write_byte_1(as, MODRM_R32(r32) | MODRM_RM_DISP32 | MODRM_RM_R32(disp_r32));
209 asm_x86_write_word32(as, disp_offset);
210 }
211}
212
Damien George3112cde2014-09-29 18:45:42 +0100213STATIC void asm_x86_generic_r32_r32(asm_x86_t *as, int dest_r32, int src_r32, int op) {
214 asm_x86_write_byte_2(as, op, MODRM_R32(src_r32) | MODRM_RM_REG | MODRM_RM_R32(dest_r32));
215}
216
Damien Georgec90f59e2014-09-06 23:06:36 +0100217STATIC void asm_x86_nop(asm_x86_t *as) {
218 asm_x86_write_byte_1(as, OPCODE_NOP);
219}
220
221STATIC void asm_x86_push_r32(asm_x86_t *as, int src_r32) {
222 asm_x86_write_byte_1(as, OPCODE_PUSH_R32 | src_r32);
223}
224
225#if 0
226void asm_x86_push_i32(asm_x86_t *as, int src_i32) {
227 asm_x86_write_byte_1(as, OPCODE_PUSH_I32);
228 asm_x86_write_word32(as, src_i32);
229}
230
231void asm_x86_push_disp(asm_x86_t *as, int src_r32, int src_offset) {
232 asm_x86_write_byte_1(as, OPCODE_PUSH_M32);
233 asm_x86_write_r32_disp(as, 6, src_r32, src_offset);
234}
235#endif
236
237STATIC void asm_x86_pop_r32(asm_x86_t *as, int dest_r32) {
238 asm_x86_write_byte_1(as, OPCODE_POP_R32 | dest_r32);
239}
240
241STATIC void asm_x86_ret(asm_x86_t *as) {
242 asm_x86_write_byte_1(as, OPCODE_RET);
243}
244
Damien George3112cde2014-09-29 18:45:42 +0100245void asm_x86_mov_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
246 asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_MOV_R32_TO_RM32);
Damien Georgec90f59e2014-09-06 23:06:36 +0100247}
248
Damien George91cfd412014-10-12 16:59:29 +0100249void asm_x86_mov_r8_to_mem8(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp) {
Damien Georged66e4862014-09-29 10:13:49 +0100250 asm_x86_write_byte_1(as, OPCODE_MOV_R8_TO_RM8);
251 asm_x86_write_r32_disp(as, src_r32, dest_r32, dest_disp);
252}
253
Damien George91cfd412014-10-12 16:59:29 +0100254void asm_x86_mov_r16_to_mem16(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp) {
Damien Georged66e4862014-09-29 10:13:49 +0100255 asm_x86_write_byte_2(as, OP_SIZE_PREFIX, OPCODE_MOV_R32_TO_RM32);
256 asm_x86_write_r32_disp(as, src_r32, dest_r32, dest_disp);
257}
258
Damien George91cfd412014-10-12 16:59:29 +0100259void asm_x86_mov_r32_to_mem32(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp) {
Damien Georgec90f59e2014-09-06 23:06:36 +0100260 asm_x86_write_byte_1(as, OPCODE_MOV_R32_TO_RM32);
261 asm_x86_write_r32_disp(as, src_r32, dest_r32, dest_disp);
262}
263
Damien George91cfd412014-10-12 16:59:29 +0100264void asm_x86_mov_mem8_to_r32zx(asm_x86_t *as, int src_r32, int src_disp, int dest_r32) {
265 asm_x86_write_byte_2(as, 0x0f, OPCODE_MOVZX_RM8_TO_R32);
266 asm_x86_write_r32_disp(as, dest_r32, src_r32, src_disp);
267}
268
269void asm_x86_mov_mem16_to_r32zx(asm_x86_t *as, int src_r32, int src_disp, int dest_r32) {
270 asm_x86_write_byte_2(as, 0x0f, OPCODE_MOVZX_RM16_TO_R32);
271 asm_x86_write_r32_disp(as, dest_r32, src_r32, src_disp);
272}
273
274void asm_x86_mov_mem32_to_r32(asm_x86_t *as, int src_r32, int src_disp, int dest_r32) {
Damien Georgec90f59e2014-09-06 23:06:36 +0100275 asm_x86_write_byte_1(as, OPCODE_MOV_RM32_TO_R32);
276 asm_x86_write_r32_disp(as, dest_r32, src_r32, src_disp);
277}
278
279STATIC void asm_x86_lea_disp_to_r32(asm_x86_t *as, int src_r32, int src_disp, int dest_r32) {
280 asm_x86_write_byte_1(as, OPCODE_LEA_MEM_TO_R32);
281 asm_x86_write_r32_disp(as, dest_r32, src_r32, src_disp);
282}
283
284#if 0
285void asm_x86_mov_i8_to_r8(asm_x86_t *as, int src_i8, int dest_r32) {
286 asm_x86_write_byte_2(as, OPCODE_MOV_I8_TO_R8 | dest_r32, src_i8);
287}
288#endif
289
290void asm_x86_mov_i32_to_r32(asm_x86_t *as, int src_i32, int dest_r32) {
291 asm_x86_write_byte_1(as, OPCODE_MOV_I32_TO_R32 | dest_r32);
292 asm_x86_write_word32(as, src_i32);
293}
294
295// src_i32 is stored as a full word in the code, and aligned to machine-word boundary
296void asm_x86_mov_i32_to_r32_aligned(asm_x86_t *as, int32_t src_i32, int dest_r32) {
297 // mov instruction uses 1 byte for the instruction, before the i32
298 while (((as->code_offset + 1) & (WORD_SIZE - 1)) != 0) {
299 asm_x86_nop(as);
300 }
301 asm_x86_mov_i32_to_r32(as, src_i32, dest_r32);
302}
303
Damien George1ef23482014-10-12 14:21:06 +0100304void asm_x86_and_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
305 asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_AND_R32_TO_RM32);
306}
307
308void asm_x86_or_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
309 asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_OR_R32_TO_RM32);
310}
311
Damien George3112cde2014-09-29 18:45:42 +0100312void asm_x86_xor_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
313 asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_XOR_R32_TO_RM32);
Damien Georgec90f59e2014-09-06 23:06:36 +0100314}
315
Damien George3112cde2014-09-29 18:45:42 +0100316void asm_x86_shl_r32_cl(asm_x86_t* as, int dest_r32) {
317 asm_x86_generic_r32_r32(as, dest_r32, 4, OPCODE_SHL_RM32_CL);
318}
319
320void asm_x86_sar_r32_cl(asm_x86_t* as, int dest_r32) {
321 asm_x86_generic_r32_r32(as, dest_r32, 7, OPCODE_SAR_RM32_CL);
322}
323
324void asm_x86_add_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
325 asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_ADD_R32_TO_RM32);
Damien Georgec90f59e2014-09-06 23:06:36 +0100326}
327
Damien George03281b32014-09-06 22:38:50 +0000328void asm_x86_add_i32_to_r32(asm_x86_t *as, int src_i32, int dest_r32) {
329 if (SIGNED_FIT8(src_i32)) {
Damien Georgec90f59e2014-09-06 23:06:36 +0100330 asm_x86_write_byte_2(as, OPCODE_ADD_I8_TO_RM32, MODRM_R32(0) | MODRM_RM_REG | MODRM_RM_R32(dest_r32));
331 asm_x86_write_byte_1(as, src_i32 & 0xff);
Damien George03281b32014-09-06 22:38:50 +0000332 } else {
Damien Georgec90f59e2014-09-06 23:06:36 +0100333 asm_x86_write_byte_2(as, OPCODE_ADD_I32_TO_RM32, MODRM_R32(0) | MODRM_RM_REG | MODRM_RM_R32(dest_r32));
334 asm_x86_write_word32(as, src_i32);
335 }
336}
337
Damien George3112cde2014-09-29 18:45:42 +0100338void asm_x86_sub_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
339 asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_SUB_R32_FROM_RM32);
Damien Georgec90f59e2014-09-06 23:06:36 +0100340}
Damien Georgec90f59e2014-09-06 23:06:36 +0100341
Damien George3112cde2014-09-29 18:45:42 +0100342STATIC void asm_x86_sub_r32_i32(asm_x86_t *as, int dest_r32, int src_i32) {
Damien Georgec90f59e2014-09-06 23:06:36 +0100343 if (SIGNED_FIT8(src_i32)) {
344 // defaults to 32 bit operation
345 asm_x86_write_byte_2(as, OPCODE_SUB_I8_FROM_RM32, MODRM_R32(5) | MODRM_RM_REG | MODRM_RM_R32(dest_r32));
346 asm_x86_write_byte_1(as, src_i32 & 0xff);
347 } else {
348 // defaults to 32 bit operation
349 asm_x86_write_byte_2(as, OPCODE_SUB_I32_FROM_RM32, MODRM_R32(5) | MODRM_RM_REG | MODRM_RM_R32(dest_r32));
350 asm_x86_write_word32(as, src_i32);
351 }
352}
353
354#if 0
355/* shifts not tested */
356void asm_x86_shl_r32_by_imm(asm_x86_t *as, int r32, int imm) {
357 asm_x86_write_byte_2(as, OPCODE_SHL_RM32_BY_I8, MODRM_R32(4) | MODRM_RM_REG | MODRM_RM_R32(r32));
358 asm_x86_write_byte_1(as, imm);
359}
360
361void asm_x86_shr_r32_by_imm(asm_x86_t *as, int r32, int imm) {
362 asm_x86_write_byte_2(as, OPCODE_SHR_RM32_BY_I8, MODRM_R32(5) | MODRM_RM_REG | MODRM_RM_R32(r32));
363 asm_x86_write_byte_1(as, imm);
364}
365
366void asm_x86_sar_r32_by_imm(asm_x86_t *as, int r32, int imm) {
367 asm_x86_write_byte_2(as, OPCODE_SAR_RM32_BY_I8, MODRM_R32(7) | MODRM_RM_REG | MODRM_RM_R32(r32));
368 asm_x86_write_byte_1(as, imm);
369}
370#endif
371
372void asm_x86_cmp_r32_with_r32(asm_x86_t *as, int src_r32_a, int src_r32_b) {
373 asm_x86_write_byte_2(as, OPCODE_CMP_R32_WITH_RM32, MODRM_R32(src_r32_a) | MODRM_RM_REG | MODRM_RM_R32(src_r32_b));
374}
375
376#if 0
377void asm_x86_cmp_i32_with_r32(asm_x86_t *as, int src_i32, int src_r32) {
378 if (SIGNED_FIT8(src_i32)) {
379 asm_x86_write_byte_2(as, OPCODE_CMP_I8_WITH_RM32, MODRM_R32(7) | MODRM_RM_REG | MODRM_RM_R32(src_r32));
380 asm_x86_write_byte_1(as, src_i32 & 0xff);
381 } else {
382 asm_x86_write_byte_2(as, OPCODE_CMP_I32_WITH_RM32, MODRM_R32(7) | MODRM_RM_REG | MODRM_RM_R32(src_r32));
383 asm_x86_write_word32(as, src_i32);
384 }
385}
386#endif
387
388void asm_x86_test_r8_with_r8(asm_x86_t *as, int src_r32_a, int src_r32_b) {
389 // TODO implement for other registers
Damien George0b610de2014-09-29 16:25:04 +0100390 assert(src_r32_a == ASM_X86_REG_EAX);
391 assert(src_r32_b == ASM_X86_REG_EAX);
Damien Georgec90f59e2014-09-06 23:06:36 +0100392 asm_x86_write_byte_2(as, OPCODE_TEST_R8_WITH_RM8, MODRM_R32(src_r32_a) | MODRM_RM_REG | MODRM_RM_R32(src_r32_b));
393}
394
395void asm_x86_setcc_r8(asm_x86_t *as, mp_uint_t jcc_type, int dest_r8) {
396 asm_x86_write_byte_3(as, OPCODE_SETCC_RM8_A, OPCODE_SETCC_RM8_B | jcc_type, MODRM_R32(0) | MODRM_RM_REG | MODRM_RM_R32(dest_r8));
397}
398
399void asm_x86_label_assign(asm_x86_t *as, mp_uint_t label) {
400 assert(label < as->max_num_labels);
401 if (as->pass < ASM_X86_PASS_EMIT) {
402 // assign label offset
403 assert(as->label_offsets[label] == -1);
404 as->label_offsets[label] = as->code_offset;
405 } else {
406 // ensure label offset has not changed from PASS_COMPUTE to PASS_EMIT
407 //printf("l%d: (at %d=%ld)\n", label, as->label_offsets[label], as->code_offset);
408 assert(as->label_offsets[label] == as->code_offset);
409 }
410}
411
Damien George0b610de2014-09-29 16:25:04 +0100412STATIC mp_uint_t get_label_dest(asm_x86_t *as, int label) {
Damien Georgec90f59e2014-09-06 23:06:36 +0100413 assert(label < as->max_num_labels);
414 return as->label_offsets[label];
415}
416
417void asm_x86_jmp_label(asm_x86_t *as, mp_uint_t label) {
Damien George0b610de2014-09-29 16:25:04 +0100418 mp_uint_t dest = get_label_dest(as, label);
419 mp_int_t rel = dest - as->code_offset;
420 if (dest != -1 && rel < 0) {
Damien Georgec90f59e2014-09-06 23:06:36 +0100421 // is a backwards jump, so we know the size of the jump on the first pass
422 // calculate rel assuming 8 bit relative jump
423 rel -= 2;
424 if (SIGNED_FIT8(rel)) {
425 asm_x86_write_byte_2(as, OPCODE_JMP_REL8, rel & 0xff);
426 } else {
427 rel += 2;
428 goto large_jump;
429 }
430 } else {
431 // is a forwards jump, so need to assume it's large
432 large_jump:
433 rel -= 5;
434 asm_x86_write_byte_1(as, OPCODE_JMP_REL32);
435 asm_x86_write_word32(as, rel);
436 }
437}
438
439void asm_x86_jcc_label(asm_x86_t *as, mp_uint_t jcc_type, mp_uint_t label) {
Damien George0b610de2014-09-29 16:25:04 +0100440 mp_uint_t dest = get_label_dest(as, label);
441 mp_int_t rel = dest - as->code_offset;
442 if (dest != -1 && rel < 0) {
Damien Georgec90f59e2014-09-06 23:06:36 +0100443 // is a backwards jump, so we know the size of the jump on the first pass
444 // calculate rel assuming 8 bit relative jump
445 rel -= 2;
446 if (SIGNED_FIT8(rel)) {
447 asm_x86_write_byte_2(as, OPCODE_JCC_REL8 | jcc_type, rel & 0xff);
448 } else {
449 rel += 2;
450 goto large_jump;
451 }
452 } else {
453 // is a forwards jump, so need to assume it's large
454 large_jump:
455 rel -= 6;
456 asm_x86_write_byte_2(as, OPCODE_JCC_REL32_A, OPCODE_JCC_REL32_B | jcc_type);
457 asm_x86_write_word32(as, rel);
458 }
459}
460
461void asm_x86_entry(asm_x86_t *as, mp_uint_t num_locals) {
Damien George0b610de2014-09-29 16:25:04 +0100462 asm_x86_push_r32(as, ASM_X86_REG_EBP);
Damien George3112cde2014-09-29 18:45:42 +0100463 asm_x86_mov_r32_r32(as, ASM_X86_REG_EBP, ASM_X86_REG_ESP);
Damien George25d90412014-09-06 23:24:32 +0000464 if (num_locals > 0) {
Damien George3112cde2014-09-29 18:45:42 +0100465 asm_x86_sub_r32_i32(as, ASM_X86_REG_ESP, num_locals * WORD_SIZE);
Damien George25d90412014-09-06 23:24:32 +0000466 }
Damien George0b610de2014-09-29 16:25:04 +0100467 asm_x86_push_r32(as, ASM_X86_REG_EBX);
468 asm_x86_push_r32(as, ASM_X86_REG_ESI);
469 asm_x86_push_r32(as, ASM_X86_REG_EDI);
Damien George03281b32014-09-06 22:38:50 +0000470 // TODO align stack on 16-byte boundary
Damien Georgec90f59e2014-09-06 23:06:36 +0100471 as->num_locals = num_locals;
472}
473
474void asm_x86_exit(asm_x86_t *as) {
Damien George0b610de2014-09-29 16:25:04 +0100475 asm_x86_pop_r32(as, ASM_X86_REG_EDI);
476 asm_x86_pop_r32(as, ASM_X86_REG_ESI);
477 asm_x86_pop_r32(as, ASM_X86_REG_EBX);
Damien Georgec90f59e2014-09-06 23:06:36 +0100478 asm_x86_write_byte_1(as, OPCODE_LEAVE);
479 asm_x86_ret(as);
480}
481
482#if 0
483void asm_x86_push_arg(asm_x86_t *as, int src_arg_num) {
Damien George0b610de2014-09-29 16:25:04 +0100484 asm_x86_push_disp(as, ASM_X86_REG_EBP, 2 * WORD_SIZE + src_arg_num * WORD_SIZE);
Damien Georgec90f59e2014-09-06 23:06:36 +0100485}
Damien George03281b32014-09-06 22:38:50 +0000486#endif
Damien Georgec90f59e2014-09-06 23:06:36 +0100487
488void asm_x86_mov_arg_to_r32(asm_x86_t *as, int src_arg_num, int dest_r32) {
Damien George91cfd412014-10-12 16:59:29 +0100489 asm_x86_mov_mem32_to_r32(as, ASM_X86_REG_EBP, 2 * WORD_SIZE + src_arg_num * WORD_SIZE, dest_r32);
Damien Georgec90f59e2014-09-06 23:06:36 +0100490}
491
Damien George03281b32014-09-06 22:38:50 +0000492#if 0
Damien Georgec90f59e2014-09-06 23:06:36 +0100493void asm_x86_mov_r32_to_arg(asm_x86_t *as, int src_r32, int dest_arg_num) {
Damien George91cfd412014-10-12 16:59:29 +0100494 asm_x86_mov_r32_to_mem32(as, src_r32, ASM_X86_REG_EBP, 2 * WORD_SIZE + dest_arg_num * WORD_SIZE);
Damien Georgec90f59e2014-09-06 23:06:36 +0100495}
496#endif
497
498// locals:
499// - stored on the stack in ascending order
500// - numbered 0 through as->num_locals-1
501// - EBP points above the last local
502//
503// | EPB
504// v
505// l0 l1 l2 ... l(n-1)
506// ^ ^
507// | low address | high address in RAM
508//
509STATIC int asm_x86_local_offset_from_ebp(asm_x86_t *as, int local_num) {
510 return (-as->num_locals + local_num) * WORD_SIZE;
511}
512
513void asm_x86_mov_local_to_r32(asm_x86_t *as, int src_local_num, int dest_r32) {
Damien George91cfd412014-10-12 16:59:29 +0100514 asm_x86_mov_mem32_to_r32(as, ASM_X86_REG_EBP, asm_x86_local_offset_from_ebp(as, src_local_num), dest_r32);
Damien Georgec90f59e2014-09-06 23:06:36 +0100515}
516
517void asm_x86_mov_r32_to_local(asm_x86_t *as, int src_r32, int dest_local_num) {
Damien George91cfd412014-10-12 16:59:29 +0100518 asm_x86_mov_r32_to_mem32(as, src_r32, ASM_X86_REG_EBP, asm_x86_local_offset_from_ebp(as, dest_local_num));
Damien Georgec90f59e2014-09-06 23:06:36 +0100519}
520
521void asm_x86_mov_local_addr_to_r32(asm_x86_t *as, int local_num, int dest_r32) {
522 int offset = asm_x86_local_offset_from_ebp(as, local_num);
523 if (offset == 0) {
Damien George3112cde2014-09-29 18:45:42 +0100524 asm_x86_mov_r32_r32(as, dest_r32, ASM_X86_REG_EBP);
Damien Georgec90f59e2014-09-06 23:06:36 +0100525 } else {
Damien George0b610de2014-09-29 16:25:04 +0100526 asm_x86_lea_disp_to_r32(as, ASM_X86_REG_EBP, offset, dest_r32);
Damien Georgec90f59e2014-09-06 23:06:36 +0100527 }
528}
529
530#if 0
531void asm_x86_push_local(asm_x86_t *as, int local_num) {
Damien George0b610de2014-09-29 16:25:04 +0100532 asm_x86_push_disp(as, ASM_X86_REG_EBP, asm_x86_local_offset_from_ebp(as, local_num));
Damien Georgec90f59e2014-09-06 23:06:36 +0100533}
534
535void asm_x86_push_local_addr(asm_x86_t *as, int local_num, int temp_r32)
536{
Damien George3112cde2014-09-29 18:45:42 +0100537 asm_x86_mov_r32_r32(as, temp_r32, ASM_X86_REG_EBP);
Damien Georgec90f59e2014-09-06 23:06:36 +0100538 asm_x86_add_i32_to_r32(as, asm_x86_local_offset_from_ebp(as, local_num), temp_r32);
539 asm_x86_push_r32(as, temp_r32);
540}
541#endif
542
543void asm_x86_call_ind(asm_x86_t *as, void *ptr, mp_uint_t n_args, int temp_r32) {
Damien George03281b32014-09-06 22:38:50 +0000544 // TODO align stack on 16-byte boundary before the call
Damien Georgec90f59e2014-09-06 23:06:36 +0100545 assert(n_args <= 3);
546 if (n_args > 2) {
Damien George6eae8612014-09-08 22:16:35 +0000547 asm_x86_push_r32(as, ASM_X86_REG_ARG_3);
Damien Georgec90f59e2014-09-06 23:06:36 +0100548 }
549 if (n_args > 1) {
Damien George6eae8612014-09-08 22:16:35 +0000550 asm_x86_push_r32(as, ASM_X86_REG_ARG_2);
Damien Georgec90f59e2014-09-06 23:06:36 +0100551 }
552 if (n_args > 0) {
Damien George6eae8612014-09-08 22:16:35 +0000553 asm_x86_push_r32(as, ASM_X86_REG_ARG_1);
Damien Georgec90f59e2014-09-06 23:06:36 +0100554 }
555#ifdef __LP64__
556 // We wouldn't run x86 code on an x64 machine. This is here to enable
557 // testing of the x86 emitter only.
558 asm_x86_mov_i32_to_r32(as, (int32_t)(int64_t)ptr, temp_r32);
559#else
560 // If we get here, sizeof(int) == sizeof(void*).
561 asm_x86_mov_i32_to_r32(as, (int32_t)ptr, temp_r32);
562#endif
563 asm_x86_write_byte_2(as, OPCODE_CALL_RM32, MODRM_R32(2) | MODRM_RM_REG | MODRM_RM_R32(temp_r32));
564 // this reduces code size by 2 bytes per call, but doesn't seem to speed it up at all
565 /*
566 asm_x86_write_byte_1(as, OPCODE_CALL_REL32);
567 asm_x86_write_word32(as, ptr - (void*)(as->code_base + as->code_offset + 4));
568 */
Damien George03281b32014-09-06 22:38:50 +0000569
570 // the caller must clean up the stack
571 if (n_args > 0) {
Damien George0b610de2014-09-29 16:25:04 +0100572 asm_x86_add_i32_to_r32(as, WORD_SIZE * n_args, ASM_X86_REG_ESP);
Damien George03281b32014-09-06 22:38:50 +0000573 }
Damien Georgec90f59e2014-09-06 23:06:36 +0100574}
575
576#endif // MICROPY_EMIT_X86