Numworks Epsilon  1.4.1
Graphing Calculator Operating System
emitinlinextensa.c
Go to the documentation of this file.
1 /*
2  * This file is part of the MicroPython project, http://micropython.org/
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2013-2016 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 <string.h>
30 #include <stdarg.h>
31 #include <assert.h>
32 
33 #include "py/emit.h"
34 #include "py/asmxtensa.h"
35 
36 #if MICROPY_EMIT_INLINE_XTENSA
37 
38 struct _emit_inline_asm_t {
39  asm_xtensa_t as;
40  uint16_t pass;
41  mp_obj_t *error_slot;
42  mp_uint_t max_num_labels;
43  qstr *label_lookup;
44 };
45 
46 STATIC void emit_inline_xtensa_error_msg(emit_inline_asm_t *emit, const char *msg) {
47  *emit->error_slot = mp_obj_new_exception_msg(&mp_type_SyntaxError, msg);
48 }
49 
50 STATIC void emit_inline_xtensa_error_exc(emit_inline_asm_t *emit, mp_obj_t exc) {
51  *emit->error_slot = exc;
52 }
53 
56  memset(&emit->as, 0, sizeof(emit->as));
57  mp_asm_base_init(&emit->as.base, max_num_labels);
58  emit->max_num_labels = max_num_labels;
59  emit->label_lookup = m_new(qstr, max_num_labels);
60  return emit;
61 }
62 
64  m_del(qstr, emit->label_lookup, emit->max_num_labels);
65  mp_asm_base_deinit(&emit->as.base, false);
67 }
68 
69 STATIC void emit_inline_xtensa_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, mp_obj_t *error_slot) {
70  emit->pass = pass;
71  emit->error_slot = error_slot;
72  if (emit->pass == MP_PASS_CODE_SIZE) {
73  memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr));
74  }
76  asm_xtensa_entry(&emit->as, 0);
77 }
78 
79 STATIC void emit_inline_xtensa_end_pass(emit_inline_asm_t *emit, mp_uint_t type_sig) {
80  asm_xtensa_exit(&emit->as);
81  asm_xtensa_end_pass(&emit->as);
82 }
83 
84 STATIC mp_uint_t emit_inline_xtensa_count_params(emit_inline_asm_t *emit, mp_uint_t n_params, mp_parse_node_t *pn_params) {
85  if (n_params > 4) {
86  emit_inline_xtensa_error_msg(emit, "can only have up to 4 parameters to Xtensa assembly");
87  return 0;
88  }
89  for (mp_uint_t i = 0; i < n_params; i++) {
90  if (!MP_PARSE_NODE_IS_ID(pn_params[i])) {
91  emit_inline_xtensa_error_msg(emit, "parameters must be registers in sequence a2 to a5");
92  return 0;
93  }
94  const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i]));
95  if (!(strlen(p) == 2 && p[0] == 'a' && p[1] == '2' + i)) {
96  emit_inline_xtensa_error_msg(emit, "parameters must be registers in sequence a2 to a5");
97  return 0;
98  }
99  }
100  return n_params;
101 }
102 
103 STATIC bool emit_inline_xtensa_label(emit_inline_asm_t *emit, mp_uint_t label_num, qstr label_id) {
104  assert(label_num < emit->max_num_labels);
105  if (emit->pass == MP_PASS_CODE_SIZE) {
106  // check for duplicate label on first pass
107  for (uint i = 0; i < emit->max_num_labels; i++) {
108  if (emit->label_lookup[i] == label_id) {
109  return false;
110  }
111  }
112  }
113  emit->label_lookup[label_num] = label_id;
114  mp_asm_base_label_assign(&emit->as.base, label_num);
115  return true;
116 }
117 
118 typedef struct _reg_name_t { byte reg; byte name[3]; } reg_name_t;
119 STATIC const reg_name_t reg_name_table[] = {
120  {0, "a0\0"},
121  {1, "a1\0"},
122  {2, "a2\0"},
123  {3, "a3\0"},
124  {4, "a4\0"},
125  {5, "a5\0"},
126  {6, "a6\0"},
127  {7, "a7\0"},
128  {8, "a8\0"},
129  {9, "a9\0"},
130  {10, "a10"},
131  {11, "a11"},
132  {12, "a12"},
133  {13, "a13"},
134  {14, "a14"},
135  {15, "a15"},
136 };
137 
138 // return empty string in case of error, so we can attempt to parse the string
139 // without a special check if it was in fact a string
140 STATIC const char *get_arg_str(mp_parse_node_t pn) {
141  if (MP_PARSE_NODE_IS_ID(pn)) {
142  qstr qst = MP_PARSE_NODE_LEAF_ARG(pn);
143  return qstr_str(qst);
144  } else {
145  return "";
146  }
147 }
148 
149 STATIC mp_uint_t get_arg_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
150  const char *reg_str = get_arg_str(pn);
151  for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(reg_name_table); i++) {
152  const reg_name_t *r = &reg_name_table[i];
153  if (reg_str[0] == r->name[0]
154  && reg_str[1] == r->name[1]
155  && reg_str[2] == r->name[2]
156  && (reg_str[2] == '\0' || reg_str[3] == '\0')) {
157  return r->reg;
158  }
159  }
160  emit_inline_xtensa_error_exc(emit,
162  "'%s' expects a register", op));
163  return 0;
164 }
165 
166 STATIC uint32_t get_arg_i(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, int min, int max) {
167  mp_obj_t o;
168  if (!mp_parse_node_get_int_maybe(pn, &o)) {
169  emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "'%s' expects an integer", op));
170  return 0;
171  }
173  if (min != max && ((int)i < min || (int)i > max)) {
174  emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "'%s' integer %d is not within range %d..%d", op, i, min, max));
175  return 0;
176  }
177  return i;
178 }
179 
180 STATIC int get_arg_label(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
181  if (!MP_PARSE_NODE_IS_ID(pn)) {
182  emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "'%s' expects a label", op));
183  return 0;
184  }
185  qstr label_qstr = MP_PARSE_NODE_LEAF_ARG(pn);
186  for (uint i = 0; i < emit->max_num_labels; i++) {
187  if (emit->label_lookup[i] == label_qstr) {
188  return i;
189  }
190  }
191  // only need to have the labels on the last pass
192  if (emit->pass == MP_PASS_EMIT) {
193  emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "label '%q' not defined", label_qstr));
194  }
195  return 0;
196 }
197 
198 #define RRR (0)
199 #define RRI8 (1)
200 #define RRI8_B (2)
201 
202 typedef struct _opcode_table_3arg_t {
203  uint16_t name; // actually a qstr, which should fit in 16 bits
204  uint8_t type;
205  uint8_t a0 : 4;
206  uint8_t a1 : 4;
207 } opcode_table_3arg_t;
208 
209 STATIC const opcode_table_3arg_t opcode_table_3arg[] = {
210  // arithmetic opcodes: reg, reg, reg
211  {MP_QSTR_and_, RRR, 0, 1},
212  {MP_QSTR_or_, RRR, 0, 2},
213  {MP_QSTR_xor, RRR, 0, 3},
214  {MP_QSTR_add, RRR, 0, 8},
215  {MP_QSTR_sub, RRR, 0, 12},
216  {MP_QSTR_mull, RRR, 2, 8},
217 
218  // load/store/addi opcodes: reg, reg, imm
219  // upper nibble of type encodes the range of the immediate arg
220  {MP_QSTR_l8ui, RRI8 | 0x10, 2, 0},
221  {MP_QSTR_l16ui, RRI8 | 0x30, 2, 1},
222  {MP_QSTR_l32i, RRI8 | 0x50, 2, 2},
223  {MP_QSTR_s8i, RRI8 | 0x10, 2, 4},
224  {MP_QSTR_s16i, RRI8 | 0x30, 2, 5},
225  {MP_QSTR_s32i, RRI8 | 0x50, 2, 6},
226  {MP_QSTR_l16si, RRI8 | 0x30, 2, 9},
227  {MP_QSTR_addi, RRI8 | 0x00, 2, 12},
228 
229  // branch opcodes: reg, reg, label
230  {MP_QSTR_ball, RRI8_B, ASM_XTENSA_CC_ALL, 0},
231  {MP_QSTR_bany, RRI8_B, ASM_XTENSA_CC_ANY, 0},
232  {MP_QSTR_bbc, RRI8_B, ASM_XTENSA_CC_BC, 0},
233  {MP_QSTR_bbs, RRI8_B, ASM_XTENSA_CC_BS, 0},
234  {MP_QSTR_beq, RRI8_B, ASM_XTENSA_CC_EQ, 0},
235  {MP_QSTR_bge, RRI8_B, ASM_XTENSA_CC_GE, 0},
236  {MP_QSTR_bgeu, RRI8_B, ASM_XTENSA_CC_GEU, 0},
237  {MP_QSTR_blt, RRI8_B, ASM_XTENSA_CC_LT, 0},
238  {MP_QSTR_bnall, RRI8_B, ASM_XTENSA_CC_NALL, 0},
239  {MP_QSTR_bne, RRI8_B, ASM_XTENSA_CC_NE, 0},
240  {MP_QSTR_bnone, RRI8_B, ASM_XTENSA_CC_NONE, 0},
241 };
242 
243 STATIC void emit_inline_xtensa_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_args, mp_parse_node_t *pn_args) {
244  size_t op_len;
245  const char *op_str = (const char*)qstr_data(op, &op_len);
246 
247  if (n_args == 0) {
248  if (op == MP_QSTR_ret_n) {
249  asm_xtensa_op_ret_n(&emit->as);
250  } else {
251  goto unknown_op;
252  }
253 
254  } else if (n_args == 1) {
255  if (op == MP_QSTR_callx0) {
256  uint r0 = get_arg_reg(emit, op_str, pn_args[0]);
257  asm_xtensa_op_callx0(&emit->as, r0);
258  } else if (op == MP_QSTR_j) {
259  int label = get_arg_label(emit, op_str, pn_args[0]);
260  asm_xtensa_j_label(&emit->as, label);
261  } else if (op == MP_QSTR_jx) {
262  uint r0 = get_arg_reg(emit, op_str, pn_args[0]);
263  asm_xtensa_op_jx(&emit->as, r0);
264  } else {
265  goto unknown_op;
266  }
267 
268  } else if (n_args == 2) {
269  uint r0 = get_arg_reg(emit, op_str, pn_args[0]);
270  if (op == MP_QSTR_beqz) {
271  int label = get_arg_label(emit, op_str, pn_args[1]);
272  asm_xtensa_bccz_reg_label(&emit->as, ASM_XTENSA_CCZ_EQ, r0, label);
273  } else if (op == MP_QSTR_bnez) {
274  int label = get_arg_label(emit, op_str, pn_args[1]);
275  asm_xtensa_bccz_reg_label(&emit->as, ASM_XTENSA_CCZ_NE, r0, label);
276  } else if (op == MP_QSTR_mov || op == MP_QSTR_mov_n) {
277  // we emit mov.n for both "mov" and "mov_n" opcodes
278  uint r1 = get_arg_reg(emit, op_str, pn_args[1]);
279  asm_xtensa_op_mov_n(&emit->as, r0, r1);
280  } else if (op == MP_QSTR_movi) {
281  // for convenience we emit l32r if the integer doesn't fit in movi
282  uint32_t imm = get_arg_i(emit, op_str, pn_args[1], 0, 0);
283  asm_xtensa_mov_reg_i32(&emit->as, r0, imm);
284  } else {
285  goto unknown_op;
286  }
287 
288  } else if (n_args == 3) {
289  // search table for 3 arg instructions
290  for (uint i = 0; i < MP_ARRAY_SIZE(opcode_table_3arg); i++) {
291  const opcode_table_3arg_t *o = &opcode_table_3arg[i];
292  if (op == o->name) {
293  uint r0 = get_arg_reg(emit, op_str, pn_args[0]);
294  uint r1 = get_arg_reg(emit, op_str, pn_args[1]);
295  if (o->type == RRR) {
296  uint r2 = get_arg_reg(emit, op_str, pn_args[2]);
297  asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, o->a0, o->a1, r0, r1, r2));
298  } else if (o->type == RRI8_B) {
299  int label = get_arg_label(emit, op_str, pn_args[2]);
300  asm_xtensa_bcc_reg_reg_label(&emit->as, o->a0, r0, r1, label);
301  } else {
302  int shift, min, max;
303  if ((o->type & 0xf0) == 0) {
304  shift = 0;
305  min = -128;
306  max = 127;
307  } else {
308  shift = (o->type & 0xf0) >> 5;
309  min = 0;
310  max = 0xff << shift;
311  }
312  uint32_t imm = get_arg_i(emit, op_str, pn_args[2], min, max);
313  asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRI8(o->a0, o->a1, r1, r0, (imm >> shift) & 0xff));
314  }
315  return;
316  }
317  }
318  goto unknown_op;
319 
320  } else {
321  goto unknown_op;
322  }
323 
324  return;
325 
326 unknown_op:
327  emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "unsupported Xtensa instruction '%s' with %d arguments", op_str, n_args));
328  return;
329 
330  /*
331 branch_not_in_range:
332  emit_inline_xtensa_error_msg(emit, "branch not in range");
333  return;
334  */
335 }
336 
338  emit_inline_xtensa_start_pass,
339  emit_inline_xtensa_end_pass,
340  emit_inline_xtensa_count_params,
341  emit_inline_xtensa_label,
342  emit_inline_xtensa_op,
343 };
344 
345 #endif // MICROPY_EMIT_INLINE_XTENSA
#define MP_ASM_PASS_COMPUTE
Definition: asmbase.h:32
#define ASM_XTENSA_CC_GE
Definition: asmxtensa.h:69
uintptr_t mp_uint_t
Definition: mpconfigport.h:74
void asm_xtensa_end_pass(asm_xtensa_t *as)
void * memset(void *b, int c, size_t len)
Definition: memset.c:3
const char * qstr_str(qstr q)
Definition: qstr.c:278
#define assert(e)
Definition: assert.h:9
void mp_asm_base_label_assign(mp_asm_base_t *as, size_t label)
void asm_xtensa_j_label(asm_xtensa_t *as, uint label)
#define m_del(type, ptr, num)
Definition: misc.h:77
mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const char *fmt,...)
Definition: objexcept.c:380
#define ASM_XTENSA_CC_EQ
Definition: asmxtensa.h:62
#define ASM_XTENSA_CC_NE
Definition: asmxtensa.h:68
void asm_xtensa_bccz_reg_label(asm_xtensa_t *as, uint cond, uint reg, uint label)
unsigned short uint16_t
Definition: stdint.h:5
#define MP_ARRAY_SIZE(a)
Definition: misc.h:106
unsigned char uint8_t
Definition: stdint.h:4
void mp_asm_base_init(mp_asm_base_t *as, size_t max_num_labels)
#define MP_ASM_PASS_EMIT
Definition: asmbase.h:33
uintptr_t mp_parse_node_t
Definition: parse.h:52
#define STATIC
Definition: mpconfig.h:1178
const mp_obj_type_t mp_type_SyntaxError
void asm_xtensa_op24(asm_xtensa_t *as, uint32_t op)
bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o)
mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, const char *msg)
Definition: objexcept.c:343
#define ASM_XTENSA_CC_BS
Definition: asmxtensa.h:72
void asm_xtensa_entry(asm_xtensa_t *as, int num_locals)
#define MP_PARSE_NODE_IS_ID(pn)
Definition: parse.h:69
#define m_del_obj(type, ptr)
Definition: misc.h:80
void mp_asm_base_start_pass(mp_asm_base_t *as, int pass)
size_t strlen(const char *s)
Definition: strlen.c:3
#define ASM_XTENSA_CC_GEU
Definition: asmxtensa.h:70
void emit_inline_xtensa_free(emit_inline_asm_t *emit)
unsigned int uint32_t
Definition: stdint.h:6
#define ASM_XTENSA_CC_LT
Definition: asmxtensa.h:63
size_t qstr
Definition: qstr.h:48
#define ASM_XTENSA_CCZ_NE
Definition: asmxtensa.h:58
unsigned char byte
Definition: misc.h:37
#define ASM_XTENSA_CC_ALL
Definition: asmxtensa.h:65
#define ASM_XTENSA_CC_BC
Definition: asmxtensa.h:66
const byte * qstr_data(qstr q, size_t *len)
Definition: qstr.c:283
void asm_xtensa_exit(asm_xtensa_t *as)
#define ASM_XTENSA_ENCODE_RRR(op0, op1, op2, r, s, t)
Definition: asmxtensa.h:75
#define MP_PARSE_NODE_LEAF_ARG(pn)
Definition: parse.h:74
emit_inline_asm_t * emit_inline_xtensa_new(mp_uint_t max_num_labels)
#define ASM_XTENSA_ENCODE_RRI8(op0, r, s, t, imm8)
Definition: asmxtensa.h:79
#define ASM_XTENSA_CC_NALL
Definition: asmxtensa.h:71
#define ASM_XTENSA_CC_ANY
Definition: asmxtensa.h:67
#define ASM_XTENSA_CCZ_EQ
Definition: asmxtensa.h:57
void asm_xtensa_mov_reg_i32(asm_xtensa_t *as, uint reg_dest, uint32_t i32)
uint64_t mp_obj_t
Definition: obj.h:39
const emit_inline_asm_method_table_t emit_inline_xtensa_method_table
#define m_new_obj(type)
Definition: misc.h:60
void mp_asm_base_deinit(mp_asm_base_t *as, bool free_code)
mp_int_t mp_obj_get_int_truncated(mp_const_obj_t arg)
Definition: obj.c:247
pass_kind_t
Definition: emit.h:42
#define m_new(type, num)
Definition: misc.h:57
#define ASM_XTENSA_CC_NONE
Definition: asmxtensa.h:61
void asm_xtensa_bcc_reg_reg_label(asm_xtensa_t *as, uint cond, uint reg1, uint reg2, uint label)
struct _emit_inline_asm_t emit_inline_asm_t
Definition: emit.h:260
unsigned int uint
Definition: misc.h:38