emitinlinethumb.c 11.2 KB
Newer Older
1
2
3
#include <stdint.h>
#include <stdio.h>
#include <string.h>
4
#include <stdarg.h>
5
6
7
#include <assert.h>

#include "misc.h"
8
#include "mpconfig.h"
9
#include "qstr.h"
10
11
12
#include "lexer.h"
#include "parse.h"
#include "scope.h"
13
#include "runtime0.h"
14
#include "emit.h"
15
#include "emitglue.h"
16
17
#include "asmthumb.h"

18
#if MICROPY_EMIT_INLINE_THUMB
19
20

struct _emit_inline_asm_t {
21
22
    uint16_t pass;
    uint16_t success;
23
    scope_t *scope;
24
    uint max_num_labels;
25
26
27
28
    qstr *label_lookup;
    asm_thumb_t *as;
};

29
30
31
32
33
34
35
36
37
void emit_inline_thumb_error(emit_inline_asm_t *emit, const char *fmt, ...) {
    printf("SyntaxError: ");
    emit->success = false;
    va_list ap;
    va_start(ap, fmt);
    vprintf(fmt, ap);
    va_end(ap);
}

38
emit_inline_asm_t *emit_inline_thumb_new(uint max_num_labels) {
39
    emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t);
40
41
42
43
44
45
46
    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;
}

47
48
49
50
51
52
void emit_inline_thumb_free(emit_inline_asm_t *emit) {
    m_del(qstr, emit->label_lookup, emit->max_num_labels);
    asm_thumb_free(emit->as, false);
    m_del_obj(emit_inline_asm_t, emit);
}

53
STATIC void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, scope_t *scope) {
54
    emit->pass = pass;
55
    emit->success = true;
56
57
58
59
60
    emit->scope = scope;
    asm_thumb_start_pass(emit->as, pass);
    asm_thumb_entry(emit->as, 0);
}

61
STATIC bool emit_inline_thumb_end_pass(emit_inline_asm_t *emit) {
62
63
64
65
    asm_thumb_exit(emit->as);
    asm_thumb_end_pass(emit->as);

    if (emit->pass == PASS_3) {
66
        void *f = asm_thumb_get_code(emit->as);
67
        mp_emit_glue_assign_inline_asm_code(emit->scope->unique_code_id, f, asm_thumb_get_code_size(emit->as), emit->scope->num_params);
68
    }
69
70

    return emit->success;
71
72
}

73
STATIC int emit_inline_thumb_count_params(emit_inline_asm_t *emit, int n_params, mp_parse_node_t *pn_params) {
74
    if (n_params > 4) {
75
        emit_inline_thumb_error(emit, "can only have up to 4 parameters to inline thumb assembly\n");
76
77
78
        return 0;
    }
    for (int i = 0; i < n_params; i++) {
79
        if (!MP_PARSE_NODE_IS_ID(pn_params[i])) {
80
            emit_inline_thumb_error(emit, "parameter to inline assembler must be an identifier\n");
81
82
            return 0;
        }
83
        const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i]));
84
        if (!(strlen(p) == 2 && p[0] == 'r' && p[1] == '0' + i)) {
85
            emit_inline_thumb_error(emit, "parameter %d to inline assembler must be r%d\n", i + 1, i);
86
87
88
89
90
91
            return 0;
        }
    }
    return n_params;
}

92
STATIC void emit_inline_thumb_label(emit_inline_asm_t *emit, uint label_num, qstr label_id) {
93
94
95
96
97
    assert(label_num < emit->max_num_labels);
    emit->label_lookup[label_num] = label_id;
    asm_thumb_label_assign(emit->as, label_num);
}

98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
typedef struct _reg_name_t { byte reg; byte name[3]; } reg_name_t;
STATIC const reg_name_t reg_name_table[] = {
    {0, "r0\0"},
    {1, "r1\0"},
    {2, "r2\0"},
    {3, "r3\0"},
    {4, "r4\0"},
    {5, "r5\0"},
    {6, "r6\0"},
    {7, "r7\0"},
    {8, "r8\0"},
    {9, "r9\0"},
    {10, "r10"},
    {11, "r11"},
    {12, "r12"},
    {13, "r13"},
    {14, "r14"},
    {15, "r15"},
    {10, "sl\0"},
    {11, "fp\0"},
    {13, "sp\0"},
    {14, "lr\0"},
    {15, "pc\0"},
};

STATIC uint get_arg_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t *pn_args, uint wanted_arg_num, uint max_reg) {
    if (MP_PARSE_NODE_IS_ID(pn_args[wanted_arg_num])) {
        qstr reg_qstr = MP_PARSE_NODE_LEAF_ARG(pn_args[wanted_arg_num]);
        const char *reg_str = qstr_str(reg_qstr);
        for (uint i = 0; i < sizeof(reg_name_table) / sizeof(reg_name_table[0]); i++) {
            const reg_name_t *r = &reg_name_table[i];
            if (reg_str[0] == r->name[0] && reg_str[1] == r->name[1] && reg_str[2] == r->name[2] && (reg_str[2] == '\0' || reg_str[3] == '\0')) {
                if (r->reg > max_reg) {
                    emit_inline_thumb_error(emit, "'%s' expects at most r%d in position %d\n", op, max_reg, wanted_arg_num);
                    return 0;
                } else {
                    return r->reg;
                }
            }
        }
138
    }
139
140
    emit_inline_thumb_error(emit, "'%s' expects a register in position %d\n", op, wanted_arg_num);
    return 0;
141
142
}

143
STATIC int get_arg_i(emit_inline_asm_t *emit, const char *op, mp_parse_node_t *pn_args, int wanted_arg_num, int fit_mask) {
144
    if (!MP_PARSE_NODE_IS_SMALL_INT(pn_args[wanted_arg_num])) {
145
        emit_inline_thumb_error(emit, "'%s' expects an integer in position %d\n", op, wanted_arg_num);
146
147
        return 0;
    }
148
    int i = MP_PARSE_NODE_LEAF_SMALL_INT(pn_args[wanted_arg_num]);
149
    if ((i & (~fit_mask)) != 0) {
150
        emit_inline_thumb_error(emit, "'%s' integer 0x%x does not fit in mask 0x%x\n", op, i, fit_mask);
151
152
153
154
155
        return 0;
    }
    return i;
}

156
STATIC int get_arg_label(emit_inline_asm_t *emit, const char *op, mp_parse_node_t *pn_args, int wanted_arg_num) {
157
    if (!MP_PARSE_NODE_IS_ID(pn_args[wanted_arg_num])) {
158
        emit_inline_thumb_error(emit, "'%s' expects a label in position %d\n", op, wanted_arg_num);
159
160
        return 0;
    }
161
    qstr label_qstr = MP_PARSE_NODE_LEAF_ARG(pn_args[wanted_arg_num]);
162
163
164
165
166
    for (int i = 0; i < emit->max_num_labels; i++) {
        if (emit->label_lookup[i] == label_qstr) {
            return i;
        }
    }
167
168
    // only need to have the labels on the last pass
    if (emit->pass == PASS_3) {
169
        emit_inline_thumb_error(emit, "label '%s' not defined\n", qstr_str(label_qstr));
170
    }
171
172
173
    return 0;
}

174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
typedef struct _cc_name_t { byte cc; byte name[2]; } cc_name_t;
STATIC const cc_name_t cc_name_table[] = {
    {THUMB_CC_EQ, "eq"},
    {THUMB_CC_NE, "ne"},
    {THUMB_CC_CS, "cs"},
    {THUMB_CC_CC, "cc"},
    {THUMB_CC_MI, "mi"},
    {THUMB_CC_PL, "pl"},
    {THUMB_CC_VS, "vs"},
    {THUMB_CC_VC, "vc"},
    {THUMB_CC_HI, "hi"},
    {THUMB_CC_LS, "ls"},
    {THUMB_CC_GE, "ge"},
    {THUMB_CC_LT, "lt"},
    {THUMB_CC_GT, "gt"},
    {THUMB_CC_LE, "le"},
};

192
STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, int n_args, mp_parse_node_t *pn_args) {
193
    // TODO perhaps make two tables:
Damien's avatar
Damien committed
194
195
196
    // one_args =
    // "b", LAB, asm_thumb_b_n,
    // "bgt", LAB, asm_thumb_bgt_n,
197
198
199
200
201
202
    // 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

203
204
205
206
207
208
209
210
    const char *op_str = qstr_str(op);
    uint op_len = strlen(op_str);

    if (n_args == 0) {
        if (strcmp(op_str, "ite.ge") == 0) { // TODO correct name for this op?
            asm_thumb_ite_ge(emit->as);
        } else {
            goto unknown_op;
211
        }
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232

    } else if (n_args == 1) {
        if (strcmp(op_str, "b") == 0) {
            int label_num = get_arg_label(emit, op_str, pn_args, 0);
            // TODO check that this succeeded, ie branch was within range
            asm_thumb_b_n(emit->as, label_num);
        } else if (op_str[0] == 'b' && op_len == 3) {
            uint cc = -1;
            for (uint i = 0; i < (sizeof cc_name_table) / (sizeof cc_name_table[0]); i++) {
                if (op_str[1] == cc_name_table[i].name[0] && op_str[2] == cc_name_table[i].name[1]) {
                    cc = cc_name_table[i].cc;
                }
            }
            if (cc == -1) {
                goto unknown_op;
            }
            int label_num = get_arg_label(emit, op_str, pn_args, 0);
            // TODO check that this succeeded, ie branch was within range
            asm_thumb_bcc_n(emit->as, cc, label_num);
        } else {
            goto unknown_op;
233
        }
234
235
236

    } else if (n_args == 2) {
        if (strcmp(op_str, "mov") == 0) {
237
238
            uint rlo_dest = get_arg_reg(emit, op_str, pn_args, 0, 7);
            uint rlo_src = get_arg_reg(emit, op_str, pn_args, 1, 7);
239
240
            asm_thumb_mov_reg_reg(emit->as, rlo_dest, rlo_src);
        } else if (strcmp(op_str, "movs") == 0) {
241
242
            uint rlo_dest = get_arg_reg(emit, op_str, pn_args, 0, 7);
            int i_src = get_arg_i(emit, op_str, pn_args, 1, 0xff);
243
244
            asm_thumb_movs_rlo_i8(emit->as, rlo_dest, i_src);
        } else if (strcmp(op_str, "movw") == 0) {
245
246
247
            uint reg_dest = get_arg_reg(emit, op_str, pn_args, 0, 15);
            int i_src = get_arg_i(emit, op_str, pn_args, 1, 0xffff);
            asm_thumb_movw_reg_i16(emit->as, reg_dest, i_src);
248
        } else if (strcmp(op_str, "movt") == 0) {
249
250
251
252
253
254
255
256
257
258
            uint reg_dest = get_arg_reg(emit, op_str, pn_args, 0, 15);
            int i_src = get_arg_i(emit, op_str, pn_args, 1, 0xffff);
            asm_thumb_movt_reg_i16(emit->as, reg_dest, i_src);
        } else if (strcmp(op_str, "movwt") == 0) {
            // this is a convenience instruction
            // we clear the MSB since it might be set from extracting the small int value
            uint reg_dest = get_arg_reg(emit, op_str, pn_args, 0, 15);
            int i_src = get_arg_i(emit, op_str, pn_args, 1, 0xffffffff);
            asm_thumb_movw_reg_i16(emit->as, reg_dest, i_src & 0xffff);
            asm_thumb_movt_reg_i16(emit->as, reg_dest, (i_src >> 16) & 0x7fff);
259
        } else if (strcmp(op_str, "cmp") == 0) {
260
261
            uint rlo = get_arg_reg(emit, op_str, pn_args, 0, 7);
            int i8 = get_arg_i(emit, op_str, pn_args, 1, 0xff);
262
263
264
            asm_thumb_cmp_rlo_i8(emit->as, rlo, i8);
        } else {
            goto unknown_op;
265
        }
266
267
268

    } else if (n_args == 3) {
        if (strcmp(op_str, "add") == 0) {
269
270
271
272
            uint rlo_dest = get_arg_reg(emit, op_str, pn_args, 0, 7);
            uint rlo_src_a = get_arg_reg(emit, op_str, pn_args, 1, 7);
            uint rlo_src_b = get_arg_reg(emit, op_str, pn_args, 2, 7);
            asm_thumb_add_rlo_rlo_rlo(emit->as, rlo_dest, rlo_src_a, rlo_src_b);
273
        } else if (strcmp(op_str, "subs") == 0) {
274
275
276
            uint rlo_dest = get_arg_reg(emit, op_str, pn_args, 0, 7);
            uint rlo_src = get_arg_reg(emit, op_str, pn_args, 1, 7);
            int i3_src = get_arg_i(emit, op_str, pn_args, 2, 0x7);
277
            asm_thumb_subs_rlo_rlo_i3(emit->as, rlo_dest, rlo_src, i3_src);
278
279
280
281
282
283
284
285
286
287
288
        } else if (strcmp(op_str, "ldr") == 0) {
            // TODO maybe use ldr(rd, [rb, 4]) syntax?
            uint rlo_dest = get_arg_reg(emit, op_str, pn_args, 0, 7);
            uint rlo_base = get_arg_reg(emit, op_str, pn_args, 1, 7);
            int i5 = get_arg_i(emit, op_str, pn_args, 2, 0x7c);
            asm_thumb_ldr_rlo_rlo_i5(emit->as, rlo_dest, rlo_base, i5 >> 2);
        } else if (strcmp(op_str, "str") == 0) {
            uint rlo_src = get_arg_reg(emit, op_str, pn_args, 0, 7);
            uint rlo_base = get_arg_reg(emit, op_str, pn_args, 1, 7);
            int i5 = get_arg_i(emit, op_str, pn_args, 2, 0x7c);
            asm_thumb_str_rlo_rlo_i5(emit->as, rlo_src, rlo_base, i5 >> 2);
289
290
        } else {
            goto unknown_op;
291
292
293
        }

    } else {
294
        goto unknown_op;
295
    }
296
297
298
299

    return;

unknown_op:
300
    emit_inline_thumb_error(emit, "unsupported Thumb instruction '%s' with %d arguments\n", op_str, n_args);
301
302
303
304
305
}

const emit_inline_asm_method_table_t emit_inline_thumb_method_table = {
    emit_inline_thumb_start_pass,
    emit_inline_thumb_end_pass,
306
    emit_inline_thumb_count_params,
307
308
309
310
    emit_inline_thumb_label,
    emit_inline_thumb_op,
};

311
#endif // MICROPY_EMIT_INLINE_THUMB