emitinlinethumb.c 15.8 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
#include "lexer.h"
#include "parse.h"
12
13
#include "obj.h"
#include "emitglue.h"
14
#include "scope.h"
15
#include "runtime0.h"
16
17
18
#include "emit.h"
#include "asmthumb.h"

19
#if MICROPY_EMIT_INLINE_THUMB
20

21
22
23
24
25
26
27
28
typedef enum {
    PN_none = 0,
#define DEF_RULE(rule, comp, kind, ...) PN_##rule,
#include "grammar.h"
#undef DEF_RULE
    PN_maximum_number_of,
} pn_kind_t;

29
struct _emit_inline_asm_t {
30
31
    uint16_t pass;
    uint16_t success;
32
    scope_t *scope;
33
    uint max_num_labels;
34
35
36
37
    qstr *label_lookup;
    asm_thumb_t *as;
};

38
39
40
41
42
43
44
45
46
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);
}

47
emit_inline_asm_t *emit_inline_thumb_new(uint max_num_labels) {
48
    emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t);
49
50
51
52
53
54
55
    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;
}

56
57
58
59
60
61
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);
}

62
STATIC void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, scope_t *scope) {
63
    emit->pass = pass;
64
    emit->success = true;
65
66
67
68
69
    emit->scope = scope;
    asm_thumb_start_pass(emit->as, pass);
    asm_thumb_entry(emit->as, 0);
}

70
STATIC bool emit_inline_thumb_end_pass(emit_inline_asm_t *emit) {
71
72
73
74
    asm_thumb_exit(emit->as);
    asm_thumb_end_pass(emit->as);

    if (emit->pass == PASS_3) {
75
        void *f = asm_thumb_get_code(emit->as);
76
        mp_emit_glue_assign_inline_asm_code(emit->scope->raw_code, f, asm_thumb_get_code_size(emit->as), emit->scope->num_params);
77
    }
78
79

    return emit->success;
80
81
}

82
STATIC int emit_inline_thumb_count_params(emit_inline_asm_t *emit, int n_params, mp_parse_node_t *pn_params) {
83
    if (n_params > 4) {
84
        emit_inline_thumb_error(emit, "can only have up to 4 parameters to inline thumb assembly\n");
85
86
87
        return 0;
    }
    for (int i = 0; i < n_params; i++) {
88
        if (!MP_PARSE_NODE_IS_ID(pn_params[i])) {
89
            emit_inline_thumb_error(emit, "parameter to inline assembler must be an identifier\n");
90
91
            return 0;
        }
92
        const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i]));
93
        if (!(strlen(p) == 2 && p[0] == 'r' && p[1] == '0' + i)) {
94
            emit_inline_thumb_error(emit, "parameter %d to inline assembler must be r%d\n", i + 1, i);
95
96
97
98
99
100
            return 0;
        }
    }
    return n_params;
}

101
STATIC void emit_inline_thumb_label(emit_inline_asm_t *emit, uint label_num, qstr label_id) {
102
103
104
105
106
    assert(label_num < emit->max_num_labels);
    emit->label_lookup[label_num] = label_id;
    asm_thumb_label_assign(emit->as, label_num);
}

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
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"},
};

132
133
134
STATIC uint get_arg_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, uint max_reg) {
    if (MP_PARSE_NODE_IS_ID(pn)) {
        qstr reg_qstr = MP_PARSE_NODE_LEAF_ARG(pn);
135
136
137
138
139
        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) {
140
                    emit_inline_thumb_error(emit, "'%s' expects at most r%d\n", op, max_reg);
141
142
143
144
145
146
                    return 0;
                } else {
                    return r->reg;
                }
            }
        }
147
    }
148
    emit_inline_thumb_error(emit, "'%s' expects a register\n", op);
149
    return 0;
150
151
}

152
153
154
STATIC int get_arg_i(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, int fit_mask) {
    if (!MP_PARSE_NODE_IS_SMALL_INT(pn)) {
        emit_inline_thumb_error(emit, "'%s' expects an integer\n", op);
155
156
        return 0;
    }
157
    int i = MP_PARSE_NODE_LEAF_SMALL_INT(pn);
158
    if ((i & (~fit_mask)) != 0) {
159
        emit_inline_thumb_error(emit, "'%s' integer 0x%x does not fit in mask 0x%x\n", op, i, fit_mask);
160
161
162
163
164
        return 0;
    }
    return i;
}

165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
STATIC bool get_arg_addr(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, mp_parse_node_t *pn_base, mp_parse_node_t *pn_offset) {
    if (!MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_bracket)) {
        goto bad_arg;
    }
    mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
    if (!MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
        goto bad_arg;
    }
    pns = (mp_parse_node_struct_t*)pns->nodes[0];
    if (MP_PARSE_NODE_STRUCT_NUM_NODES(pns) != 2) {
        goto bad_arg;
    }

    *pn_base = pns->nodes[0];
    *pn_offset = pns->nodes[1];
    return true;

bad_arg:
    emit_inline_thumb_error(emit, "'%s' expects an address of the form [a, b]\n", op);
    return false;
}

STATIC int get_arg_label(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
    if (!MP_PARSE_NODE_IS_ID(pn)) {
        emit_inline_thumb_error(emit, "'%s' expects a label\n", op);
190
191
        return 0;
    }
192
    qstr label_qstr = MP_PARSE_NODE_LEAF_ARG(pn);
193
194
195
196
197
    for (int i = 0; i < emit->max_num_labels; i++) {
        if (emit->label_lookup[i] == label_qstr) {
            return i;
        }
    }
198
199
    // only need to have the labels on the last pass
    if (emit->pass == PASS_3) {
200
        emit_inline_thumb_error(emit, "label '%s' not defined\n", qstr_str(label_qstr));
201
    }
202
203
204
    return 0;
}

205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
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"},
};

223
STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, int n_args, mp_parse_node_t *pn_args) {
224
    // TODO perhaps make two tables:
Damien's avatar
Damien committed
225
226
227
    // one_args =
    // "b", LAB, asm_thumb_b_n,
    // "bgt", LAB, asm_thumb_bgt_n,
228
229
230
231
232
233
    // 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

234
235
236
237
238
239
240
241
    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;
242
        }
243
244
245

    } else if (n_args == 1) {
        if (strcmp(op_str, "b") == 0) {
246
            int label_num = get_arg_label(emit, op_str, pn_args[0]);
247
248
249
250
251
252
253
254
255
256
257
258
            // 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;
            }
259
            int label_num = get_arg_label(emit, op_str, pn_args[0]);
260
261
262
263
            // TODO check that this succeeded, ie branch was within range
            asm_thumb_bcc_n(emit->as, cc, label_num);
        } else {
            goto unknown_op;
264
        }
265
266

    } else if (n_args == 2) {
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
        if (MP_PARSE_NODE_IS_ID(pn_args[1])) {
            // second arg is a register (or should be)
            uint op_code;
            if (strcmp(op_str, "mov") == 0) {
                uint reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
                uint reg_src = get_arg_reg(emit, op_str, pn_args[1], 15);
                asm_thumb_mov_reg_reg(emit->as, reg_dest, reg_src);
            } else if (strcmp(op_str, "and") == 0) {
                op_code = ASM_THUMB_FORMAT_4_AND;
                uint reg_dest, reg_src;
                op_format_4:
                reg_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
                reg_src = get_arg_reg(emit, op_str, pn_args[1], 7);
                asm_thumb_format_4(emit->as, op_code, reg_dest, reg_src);
            // TODO probably uses less ROM if these ops are in a lookup table
            } else if (strcmp(op_str, "and") == 0) { op_code = ASM_THUMB_FORMAT_4_AND; goto op_format_4;
            } else if (strcmp(op_str, "eor") == 0) { op_code = ASM_THUMB_FORMAT_4_EOR; goto op_format_4;
            } else if (strcmp(op_str, "lsl") == 0) { op_code = ASM_THUMB_FORMAT_4_LSL; goto op_format_4;
            } else if (strcmp(op_str, "lsr") == 0) { op_code = ASM_THUMB_FORMAT_4_LSR; goto op_format_4;
            } else if (strcmp(op_str, "asr") == 0) { op_code = ASM_THUMB_FORMAT_4_ASR; goto op_format_4;
            } else if (strcmp(op_str, "adc") == 0) { op_code = ASM_THUMB_FORMAT_4_ADC; goto op_format_4;
            } else if (strcmp(op_str, "sbc") == 0) { op_code = ASM_THUMB_FORMAT_4_SBC; goto op_format_4;
            } else if (strcmp(op_str, "ror") == 0) { op_code = ASM_THUMB_FORMAT_4_ROR; goto op_format_4;
            } else if (strcmp(op_str, "tst") == 0) { op_code = ASM_THUMB_FORMAT_4_TST; goto op_format_4;
            } else if (strcmp(op_str, "neg") == 0) { op_code = ASM_THUMB_FORMAT_4_NEG; goto op_format_4;
            } else if (strcmp(op_str, "cmp") == 0) { op_code = ASM_THUMB_FORMAT_4_CMP; goto op_format_4;
            } else if (strcmp(op_str, "cmn") == 0) { op_code = ASM_THUMB_FORMAT_4_CMN; goto op_format_4;
            } else if (strcmp(op_str, "orr") == 0) { op_code = ASM_THUMB_FORMAT_4_ORR; goto op_format_4;
            } else if (strcmp(op_str, "mul") == 0) { op_code = ASM_THUMB_FORMAT_4_MUL; goto op_format_4;
            } else if (strcmp(op_str, "bic") == 0) { op_code = ASM_THUMB_FORMAT_4_BIC; goto op_format_4;
            } else if (strcmp(op_str, "mvn") == 0) { op_code = ASM_THUMB_FORMAT_4_MVN; goto op_format_4;
            } else {
                goto unknown_op;
            }
301
        } else {
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
            // second arg is not a register
            uint op_code;
            if (strcmp(op_str, "mov") == 0) {
                op_code = ASM_THUMB_FORMAT_3_MOV;
                uint rlo_dest, i8_src;
                op_format_3:
                rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
                i8_src = get_arg_i(emit, op_str, pn_args[1], 0xff);
                asm_thumb_format_3(emit->as, op_code, rlo_dest, i8_src);
            } else if (strcmp(op_str, "cmp") == 0) {
                op_code = ASM_THUMB_FORMAT_3_CMP;
                goto op_format_3;
            } else if (strcmp(op_str, "add") == 0) {
                op_code = ASM_THUMB_FORMAT_3_ADD;
                goto op_format_3;
            } else if (strcmp(op_str, "sub") == 0) {
                op_code = ASM_THUMB_FORMAT_3_SUB;
                goto op_format_3;
            } else if (strcmp(op_str, "movw") == 0) {
                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);
            } else if (strcmp(op_str, "movt") == 0) {
                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);
            } else if (strcmp(op_str, "ldr") == 0) {
                op_code = ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_WORD_TRANSFER;
                uint rlo_dest, rlo_base, i5;
                mp_parse_node_t pn_base, pn_offset;
                op_format_9_10:
                rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
                if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) {
                    rlo_base = get_arg_reg(emit, op_str, pn_base, 7);
                    if (op_code & ASM_THUMB_FORMAT_9_BYTE_TRANSFER) {
                        i5 = get_arg_i(emit, op_str, pn_offset, 0x1f);
                    } else if (op_code & ASM_THUMB_FORMAT_10_STRH) { // also catches LDRH
                        i5 = get_arg_i(emit, op_str, pn_offset, 0x3e) >> 1;
                    } else {
                        i5 = get_arg_i(emit, op_str, pn_offset, 0x7c) >> 2;
                    }
                    asm_thumb_format_9_10(emit->as, op_code, rlo_dest, rlo_base, i5);
                }
            } else if (strcmp(op_str, "ldrb") == 0) {
                op_code = ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER;
                goto op_format_9_10;
            } else if (strcmp(op_str, "ldrh") == 0) {
                op_code = ASM_THUMB_FORMAT_10_LDRH;
                goto op_format_9_10;
            } else if (strcmp(op_str, "str") == 0) {
                op_code = ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_WORD_TRANSFER;
                goto op_format_9_10;
            } else if (strcmp(op_str, "strb") == 0) {
                op_code = ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER;
                goto op_format_9_10;
            } else if (strcmp(op_str, "strh") == 0) {
                op_code = ASM_THUMB_FORMAT_10_STRH;
                goto op_format_9_10;
            } else {
                goto unknown_op;
            }
370
        }
371
372

    } else if (n_args == 3) {
373
        uint op_code;
374
        if (strcmp(op_str, "add") == 0) {
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
            op_code = ASM_THUMB_FORMAT_2_ADD;
            uint rlo_dest, rlo_src;
            op_format_2:
            rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
            rlo_src = get_arg_reg(emit, op_str, pn_args[1], 7);
            int src_b;
            if (MP_PARSE_NODE_IS_ID(pn_args[2])) {
                op_code |= ASM_THUMB_FORMAT_2_REG_OPERAND;
                src_b = get_arg_reg(emit, op_str, pn_args[2], 7);
            } else {
                op_code |= ASM_THUMB_FORMAT_2_IMM_OPERAND;
                src_b = get_arg_i(emit, op_str, pn_args[2], 0x7);
            }
            asm_thumb_format_2(emit->as, op_code, rlo_dest, rlo_src, src_b);
        } else if (strcmp(op_str, "sub") == 0) {
            op_code = ASM_THUMB_FORMAT_2_SUB;
            goto op_format_2;
392
393
        } else {
            goto unknown_op;
394
395
396
        }

    } else {
397
        goto unknown_op;
398
    }
399
400
401
402

    return;

unknown_op:
403
    emit_inline_thumb_error(emit, "unsupported Thumb instruction '%s' with %d arguments\n", op_str, n_args);
404
405
406
407
408
}

const emit_inline_asm_method_table_t emit_inline_thumb_method_table = {
    emit_inline_thumb_start_pass,
    emit_inline_thumb_end_pass,
409
    emit_inline_thumb_count_params,
410
411
412
413
    emit_inline_thumb_label,
    emit_inline_thumb_op,
};

414
#endif // MICROPY_EMIT_INLINE_THUMB