emitinlinethumb.c 17.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
 * This file is part of the Micro Python project, http://micropython.org/
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2013, 2014 Damien P. George
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

27
28
29
#include <stdint.h>
#include <stdio.h>
#include <string.h>
30
#include <stdarg.h>
31
32
33
#include <assert.h>

#include "misc.h"
34
#include "mpconfig.h"
35
#include "qstr.h"
36
37
#include "lexer.h"
#include "parse.h"
38
39
#include "obj.h"
#include "emitglue.h"
40
#include "scope.h"
41
#include "runtime0.h"
42
43
44
#include "emit.h"
#include "asmthumb.h"

45
#if MICROPY_EMIT_INLINE_THUMB
46

47
48
49
50
51
52
53
54
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;

55
struct _emit_inline_asm_t {
56
57
    uint16_t pass;
    uint16_t success;
58
    scope_t *scope;
59
    uint max_num_labels;
60
61
62
63
    qstr *label_lookup;
    asm_thumb_t *as;
};

64
65
66
67
68
69
70
71
72
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);
}

73
emit_inline_asm_t *emit_inline_thumb_new(uint max_num_labels) {
74
    emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t);
75
76
77
78
79
80
81
    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;
}

82
83
84
85
86
87
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);
}

88
STATIC void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, scope_t *scope) {
89
    emit->pass = pass;
90
    emit->success = true;
91
92
93
94
95
    emit->scope = scope;
    asm_thumb_start_pass(emit->as, pass);
    asm_thumb_entry(emit->as, 0);
}

96
STATIC bool emit_inline_thumb_end_pass(emit_inline_asm_t *emit) {
97
98
99
100
    asm_thumb_exit(emit->as);
    asm_thumb_end_pass(emit->as);

    if (emit->pass == PASS_3) {
101
        void *f = asm_thumb_get_code(emit->as);
102
        mp_emit_glue_assign_inline_asm_code(emit->scope->raw_code, f, asm_thumb_get_code_size(emit->as), emit->scope->num_pos_args);
103
    }
104
105

    return emit->success;
106
107
}

108
STATIC int emit_inline_thumb_count_params(emit_inline_asm_t *emit, int n_params, mp_parse_node_t *pn_params) {
109
    if (n_params > 4) {
110
        emit_inline_thumb_error(emit, "can only have up to 4 parameters to inline thumb assembly\n");
111
112
113
        return 0;
    }
    for (int i = 0; i < n_params; i++) {
114
        if (!MP_PARSE_NODE_IS_ID(pn_params[i])) {
115
            emit_inline_thumb_error(emit, "parameter to inline assembler must be an identifier\n");
116
117
            return 0;
        }
118
        const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i]));
119
        if (!(strlen(p) == 2 && p[0] == 'r' && p[1] == '0' + i)) {
120
            emit_inline_thumb_error(emit, "parameter %d to inline assembler must be r%d\n", i + 1, i);
121
122
123
124
125
126
            return 0;
        }
    }
    return n_params;
}

127
STATIC void emit_inline_thumb_label(emit_inline_asm_t *emit, uint label_num, qstr label_id) {
128
129
130
131
132
    assert(label_num < emit->max_num_labels);
    emit->label_lookup[label_num] = label_id;
    asm_thumb_label_assign(emit->as, label_num);
}

133
134
135
136
137
138
139
140
STATIC void emit_inline_thumb_align(emit_inline_asm_t *emit, uint align) {
    asm_thumb_align(emit->as, align);
}

STATIC void emit_inline_thumb_data(emit_inline_asm_t *emit, uint bytesize, uint val) {
    asm_thumb_data(emit->as, bytesize, val);
}

141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
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"},
};

166
167
168
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);
169
        const char *reg_str = qstr_str(reg_qstr);
170
        for (uint i = 0; i < ARRAY_SIZE(reg_name_table); i++) {
171
172
173
            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) {
174
                    emit_inline_thumb_error(emit, "'%s' expects at most r%d\n", op, max_reg);
175
176
177
178
179
180
                    return 0;
                } else {
                    return r->reg;
                }
            }
        }
181
    }
182
    emit_inline_thumb_error(emit, "'%s' expects a register\n", op);
183
    return 0;
184
185
}

186
187
188
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);
189
190
        return 0;
    }
191
    int i = MP_PARSE_NODE_LEAF_SMALL_INT(pn);
192
    if ((i & (~fit_mask)) != 0) {
193
        emit_inline_thumb_error(emit, "'%s' integer 0x%x does not fit in mask 0x%x\n", op, i, fit_mask);
194
195
196
197
198
        return 0;
    }
    return i;
}

199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
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);
224
225
        return 0;
    }
226
    qstr label_qstr = MP_PARSE_NODE_LEAF_ARG(pn);
227
228
229
230
231
    for (int i = 0; i < emit->max_num_labels; i++) {
        if (emit->label_lookup[i] == label_qstr) {
            return i;
        }
    }
232
233
    // only need to have the labels on the last pass
    if (emit->pass == PASS_3) {
234
        emit_inline_thumb_error(emit, "label '%s' not defined\n", qstr_str(label_qstr));
235
    }
236
237
238
    return 0;
}

239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
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"},
};

257
STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, int n_args, mp_parse_node_t *pn_args) {
258
    // TODO perhaps make two tables:
Damien's avatar
Damien committed
259
260
261
    // one_args =
    // "b", LAB, asm_thumb_b_n,
    // "bgt", LAB, asm_thumb_bgt_n,
262
263
264
265
266
267
    // 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

268
269
270
271
    const char *op_str = qstr_str(op);
    uint op_len = strlen(op_str);

    if (n_args == 0) {
272
273
274
275
276
        if (strcmp(op_str, "nop") == 0) {
            asm_thumb_op16(emit->as, ASM_THUMB_OP_NOP);
        } else if (strcmp(op_str, "wfi") == 0) {
            asm_thumb_op16(emit->as, ASM_THUMB_OP_WFI);
        } else if (strcmp(op_str, "ite.ge") == 0) { // TODO correct name for this op?
277
278
279
            asm_thumb_ite_ge(emit->as);
        } else {
            goto unknown_op;
280
        }
281
282
283

    } else if (n_args == 1) {
        if (strcmp(op_str, "b") == 0) {
284
            int label_num = get_arg_label(emit, op_str, pn_args[0]);
285
286
287
288
            // 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;
289
            for (uint i = 0; i < ARRAY_SIZE(cc_name_table); i++) {
290
291
292
293
294
295
296
                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;
            }
297
            int label_num = get_arg_label(emit, op_str, pn_args[0]);
298
299
            // TODO check that this succeeded, ie branch was within range
            asm_thumb_bcc_n(emit->as, cc, label_num);
300
301
302
303
304
305
        } else if (strcmp(op_str, "cpsid")) {
            // TODO check pn_args[0] == i
            asm_thumb_op16(emit->as, ASM_THUMB_OP_CPSID_I);
        } else if (strcmp(op_str, "cpsie")) {
            // TODO check pn_args[0] == i
            asm_thumb_op16(emit->as, ASM_THUMB_OP_CPSIE_I);
306
307
        } else {
            goto unknown_op;
308
        }
309
310

    } else if (n_args == 2) {
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
        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;
            }
345
        } else {
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
            // 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;
            }
414
        }
415
416

    } else if (n_args == 3) {
417
        uint op_code;
418
        if (strcmp(op_str, "add") == 0) {
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
            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;
436
437
        } else {
            goto unknown_op;
438
439
440
        }

    } else {
441
        goto unknown_op;
442
    }
443
444
445
446

    return;

unknown_op:
447
    emit_inline_thumb_error(emit, "unsupported Thumb instruction '%s' with %d arguments\n", op_str, n_args);
448
449
450
451
452
}

const emit_inline_asm_method_table_t emit_inline_thumb_method_table = {
    emit_inline_thumb_start_pass,
    emit_inline_thumb_end_pass,
453
    emit_inline_thumb_count_params,
454
    emit_inline_thumb_label,
455
456
    emit_inline_thumb_align,
    emit_inline_thumb_data,
457
458
459
    emit_inline_thumb_op,
};

460
#endif // MICROPY_EMIT_INLINE_THUMB