d99b05282d
A big change. Micro Python objects are allocated as individual structs with the first element being a pointer to the type information (which is itself an object). This scheme follows CPython. Much more flexible, not necessarily slower, uses same heap memory, and can allocate objects statically. Also change name prefix, from py_ to mp_ (mp for Micro Python).
208 lines
7.0 KiB
C
208 lines
7.0 KiB
C
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "misc.h"
|
|
#include "mpconfig.h"
|
|
#include "lexer.h"
|
|
#include "parse.h"
|
|
#include "scope.h"
|
|
#include "runtime0.h"
|
|
#include "emit.h"
|
|
#include "asmthumb.h"
|
|
|
|
#if MICROPY_EMIT_INLINE_THUMB
|
|
|
|
struct _emit_inline_asm_t {
|
|
int pass;
|
|
scope_t *scope;
|
|
int max_num_labels;
|
|
qstr *label_lookup;
|
|
asm_thumb_t *as;
|
|
};
|
|
|
|
emit_inline_asm_t *emit_inline_thumb_new(uint max_num_labels) {
|
|
emit_inline_asm_t *emit = m_new(emit_inline_asm_t, 1);
|
|
emit->max_num_labels = max_num_labels;
|
|
emit->label_lookup = m_new(qstr, max_num_labels);
|
|
memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr));
|
|
emit->as = asm_thumb_new(max_num_labels);
|
|
return emit;
|
|
}
|
|
|
|
static void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, scope_t *scope) {
|
|
emit->pass = pass;
|
|
emit->scope = scope;
|
|
asm_thumb_start_pass(emit->as, pass);
|
|
asm_thumb_entry(emit->as, 0);
|
|
}
|
|
|
|
static void emit_inline_thumb_end_pass(emit_inline_asm_t *emit) {
|
|
asm_thumb_exit(emit->as);
|
|
asm_thumb_end_pass(emit->as);
|
|
|
|
if (emit->pass == PASS_3) {
|
|
void *f = asm_thumb_get_code(emit->as);
|
|
rt_assign_inline_asm_code(emit->scope->unique_code_id, f, asm_thumb_get_code_size(emit->as), emit->scope->num_params);
|
|
}
|
|
}
|
|
|
|
static int emit_inline_thumb_count_params(emit_inline_asm_t *emit, int n_params, mp_parse_node_t *pn_params) {
|
|
if (n_params > 4) {
|
|
printf("SyntaxError: can only have up to 4 parameters to inline thumb assembly\n");
|
|
return 0;
|
|
}
|
|
for (int i = 0; i < n_params; i++) {
|
|
if (!MP_PARSE_NODE_IS_ID(pn_params[i])) {
|
|
printf("SyntaxError: parameter to inline assembler must be an identifier\n");
|
|
return 0;
|
|
}
|
|
const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i]));
|
|
if (!(strlen(p) == 2 && p[0] == 'r' && p[1] == '0' + i)) {
|
|
printf("SyntaxError: parameter %d to inline assembler must be r%d\n", i + 1, i);
|
|
return 0;
|
|
}
|
|
}
|
|
return n_params;
|
|
}
|
|
|
|
static void emit_inline_thumb_label(emit_inline_asm_t *emit, int label_num, qstr label_id) {
|
|
assert(label_num < emit->max_num_labels);
|
|
emit->label_lookup[label_num] = label_id;
|
|
asm_thumb_label_assign(emit->as, label_num);
|
|
}
|
|
|
|
static bool check_n_arg(qstr op, int n_args, int wanted_n_args) {
|
|
if (wanted_n_args == n_args) {
|
|
return true;
|
|
} else {
|
|
printf("SyntaxError: '%s' expects %d arguments'\n", qstr_str(op), wanted_n_args);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static uint get_arg_rlo(qstr op, mp_parse_node_t *pn_args, int wanted_arg_num) {
|
|
if (!MP_PARSE_NODE_IS_ID(pn_args[wanted_arg_num])) {
|
|
printf("SyntaxError: '%s' expects a register in position %d\n", qstr_str(op), wanted_arg_num);
|
|
return 0;
|
|
}
|
|
qstr reg_qstr = MP_PARSE_NODE_LEAF_ARG(pn_args[wanted_arg_num]);
|
|
const char *reg_str = qstr_str(reg_qstr);
|
|
if (!(strlen(reg_str) == 2 && reg_str[0] == 'r' && ('0' <= reg_str[1] && reg_str[1] <= '7'))) {
|
|
printf("SyntaxError: '%s' expects a register in position %d\n", qstr_str(op), wanted_arg_num);
|
|
return 0;
|
|
}
|
|
return reg_str[1] - '0';
|
|
}
|
|
|
|
static int get_arg_i(qstr op, mp_parse_node_t *pn_args, int wanted_arg_num, int fit_mask) {
|
|
if (!MP_PARSE_NODE_IS_SMALL_INT(pn_args[wanted_arg_num])) {
|
|
printf("SyntaxError: '%s' expects an integer in position %d\n", qstr_str(op), wanted_arg_num);
|
|
return 0;
|
|
}
|
|
int i = MP_PARSE_NODE_LEAF_ARG(pn_args[wanted_arg_num]);
|
|
if ((i & (~fit_mask)) != 0) {
|
|
printf("SyntaxError: '%s' integer 0x%x does not fit in mask 0x%x\n", qstr_str(op), i, fit_mask);
|
|
return 0;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static int get_arg_label(emit_inline_asm_t *emit, qstr op, mp_parse_node_t *pn_args, int wanted_arg_num) {
|
|
if (!MP_PARSE_NODE_IS_ID(pn_args[wanted_arg_num])) {
|
|
printf("SyntaxError: '%s' expects a label in position %d\n", qstr_str(op), wanted_arg_num);
|
|
return 0;
|
|
}
|
|
qstr label_qstr = MP_PARSE_NODE_LEAF_ARG(pn_args[wanted_arg_num]);
|
|
for (int i = 0; i < emit->max_num_labels; i++) {
|
|
if (emit->label_lookup[i] == label_qstr) {
|
|
return i;
|
|
}
|
|
}
|
|
// only need to have the labels on the last pass
|
|
if (emit->pass == PASS_3) {
|
|
printf("SyntaxError: label '%s' not defined\n", qstr_str(label_qstr));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, int n_args, mp_parse_node_t *pn_args) {
|
|
// TODO perhaps make two tables:
|
|
// one_args =
|
|
// "b", LAB, asm_thumb_b_n,
|
|
// "bgt", LAB, asm_thumb_bgt_n,
|
|
// two_args =
|
|
// "movs", RLO, I8, asm_thumb_movs_reg_i8
|
|
// "movw", REG, REG, asm_thumb_movw_reg_i16
|
|
// three_args =
|
|
// "subs", RLO, RLO, I3, asm_thumb_subs_reg_reg_i3
|
|
|
|
// 1 arg
|
|
if (strcmp(qstr_str(op), "b") == 0) {
|
|
if (!check_n_arg(op, n_args, 1)) {
|
|
return;
|
|
}
|
|
int label_num = get_arg_label(emit, op, pn_args, 0);
|
|
// TODO check that this succeeded, ie branch was within range
|
|
asm_thumb_b_n(emit->as, label_num);
|
|
} else if (strcmp(qstr_str(op), "bgt") == 0) {
|
|
if (!check_n_arg(op, n_args, 1)) {
|
|
return;
|
|
}
|
|
int label_num = get_arg_label(emit, op, pn_args, 0);
|
|
// TODO check that this succeeded, ie branch was within range
|
|
asm_thumb_bcc_n(emit->as, THUMB_CC_GT, label_num);
|
|
|
|
// 2 args
|
|
} else if (strcmp(qstr_str(op), "movs") == 0) {
|
|
if (!check_n_arg(op, n_args, 2)) {
|
|
return;
|
|
}
|
|
uint rlo_dest = get_arg_rlo(op, pn_args, 0);
|
|
int i_src = get_arg_i(op, pn_args, 1, 0xff);
|
|
asm_thumb_movs_rlo_i8(emit->as, rlo_dest, i_src);
|
|
} else if (strcmp(qstr_str(op), "movw") == 0) {
|
|
if (!check_n_arg(op, n_args, 2)) {
|
|
return;
|
|
}
|
|
uint rlo_dest = get_arg_rlo(op, pn_args, 0); // TODO can be reg lo or hi
|
|
int i_src = get_arg_i(op, pn_args, 1, 0xffff);
|
|
asm_thumb_movw_reg_i16(emit->as, rlo_dest, i_src);
|
|
} else if (strcmp(qstr_str(op), "cmp") == 0) {
|
|
if (!check_n_arg(op, n_args, 2)) {
|
|
return;
|
|
}
|
|
uint rlo = get_arg_rlo(op, pn_args, 0);
|
|
int i8 = get_arg_i(op, pn_args, 1, 0xff);
|
|
asm_thumb_cmp_rlo_i8(emit->as, rlo, i8);
|
|
|
|
// 3 args
|
|
} else if (strcmp(qstr_str(op), "subs") == 0) {
|
|
if (!check_n_arg(op, n_args, 3)) {
|
|
return;
|
|
}
|
|
uint rlo_dest = get_arg_rlo(op, pn_args, 0);
|
|
uint rlo_src = get_arg_rlo(op, pn_args, 1);
|
|
int i3_src = get_arg_i(op, pn_args, 2, 0x7);
|
|
asm_thumb_subs_rlo_rlo_i3(emit->as, rlo_dest, rlo_src, i3_src);
|
|
|
|
// unknown op
|
|
} else {
|
|
printf("SyntaxError: unsupported ARM Thumb instruction '%s'\n", qstr_str(op));
|
|
return;
|
|
}
|
|
}
|
|
|
|
const emit_inline_asm_method_table_t emit_inline_thumb_method_table = {
|
|
emit_inline_thumb_start_pass,
|
|
emit_inline_thumb_end_pass,
|
|
emit_inline_thumb_count_params,
|
|
emit_inline_thumb_label,
|
|
emit_inline_thumb_op,
|
|
};
|
|
|
|
#endif // MICROPY_EMIT_INLINE_THUMB
|