aboutsummaryrefslogtreecommitdiff
path: root/py/emitglue.c
blob: 95be7f661abed437f7906899469c05816b3cc070 (plain)
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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
138
139
140
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
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
224
225
226
227
228
229
230
231
232
233
234
/*
 * This file is part of the MicroPython 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.
 */

// This code glues the code emitters to the runtime.

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

#include "py/emitglue.h"
#include "py/runtime0.h"
#include "py/bc.h"
#include "py/objfun.h"
#include "py/profile.h"

#if MICROPY_DEBUG_VERBOSE // print debugging info
#define DEBUG_PRINT (1)
#define WRITE_CODE (1)
#define DEBUG_printf DEBUG_printf
#define DEBUG_OP_printf(...) DEBUG_printf(__VA_ARGS__)
#else // don't print debugging info
#define DEBUG_printf(...) (void)0
#define DEBUG_OP_printf(...) (void)0
#endif

#if MICROPY_DEBUG_PRINTERS
mp_uint_t mp_verbose_flag = 0;
#endif

mp_raw_code_t *mp_emit_glue_new_raw_code(void) {
    mp_raw_code_t *rc = m_new0(mp_raw_code_t, 1);
    rc->kind = MP_CODE_RESERVED;
    #if MICROPY_PY_SYS_SETTRACE
    rc->line_of_definition = 0;
    #endif
    return rc;
}

void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code,
    #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS
    size_t len,
    #endif
    mp_raw_code_t **children,
    #if MICROPY_PERSISTENT_CODE_SAVE
    size_t n_children,
    #endif
    mp_uint_t scope_flags) {

    rc->kind = MP_CODE_BYTECODE;
    rc->scope_flags = scope_flags;
    rc->fun_data = code;
    #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS
    rc->fun_data_len = len;
    #endif
    rc->children = children;

    #if MICROPY_PERSISTENT_CODE_SAVE
    rc->n_children = n_children;
    #endif

    #if MICROPY_PY_SYS_SETTRACE
    mp_bytecode_prelude_t *prelude = &rc->prelude;
    mp_prof_extract_prelude(code, prelude);
    #endif

    #ifdef DEBUG_PRINT
    #if !(MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS)
    const size_t len = 0;
    #endif
    DEBUG_printf("assign byte code: code=%p len=" UINT_FMT " flags=%x\n", code, len, (uint)scope_flags);
    #endif
}

#if MICROPY_EMIT_MACHINE_CODE
void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len,
    mp_raw_code_t **children,
    #if MICROPY_PERSISTENT_CODE_SAVE
    size_t n_children,
    uint16_t prelude_offset,
    #endif
    mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_t type_sig) {

    assert(kind == MP_CODE_NATIVE_PY || kind == MP_CODE_NATIVE_VIPER || kind == MP_CODE_NATIVE_ASM);

    // Some architectures require flushing/invalidation of the I/D caches,
    // so that the generated native code which was created in data RAM will
    // be available for execution from instruction RAM.
    #if MICROPY_EMIT_THUMB || MICROPY_EMIT_INLINE_THUMB
    #if __ICACHE_PRESENT == 1
    // Flush D-cache, so the code emitted is stored in RAM.
    MP_HAL_CLEAN_DCACHE(fun_data, fun_len);
    // Invalidate I-cache, so the newly-created code is reloaded from RAM.
    SCB_InvalidateICache();
    #endif
    #elif MICROPY_EMIT_ARM
    #if (defined(__linux__) && defined(__GNUC__)) || __ARM_ARCH == 7
    __builtin___clear_cache(fun_data, (uint8_t *)fun_data + fun_len);
    #elif defined(__arm__)
    // Flush I-cache and D-cache.
    asm volatile (
        "0:"
        "mrc p15, 0, r15, c7, c10, 3\n" // test and clean D-cache
        "bne 0b\n"
        "mov r0, #0\n"
        "mcr p15, 0, r0, c7, c7, 0\n" // invalidate I-cache and D-cache
        : : : "r0", "cc");
    #endif
    #endif

    rc->kind = kind;
    rc->scope_flags = scope_flags;
    rc->fun_data = fun_data;

    #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS
    rc->fun_data_len = fun_len;
    #endif
    rc->children = children;

    #if MICROPY_PERSISTENT_CODE_SAVE
    rc->n_children = n_children;
    rc->prelude_offset = prelude_offset;
    #endif

    // These two entries are only needed for MP_CODE_NATIVE_ASM.
    rc->n_pos_args = n_pos_args;
    rc->type_sig = type_sig;

    #ifdef DEBUG_PRINT
    DEBUG_printf("assign native: kind=%d fun=%p len=" UINT_FMT " n_pos_args=" UINT_FMT " flags=%x\n", kind, fun_data, fun_len, n_pos_args, (uint)scope_flags);
    for (mp_uint_t i = 0; i < fun_len; i++) {
        if (i > 0 && i % 16 == 0) {
            DEBUG_printf("\n");
        }
        DEBUG_printf(" %02x", ((byte *)fun_data)[i]);
    }
    DEBUG_printf("\n");

    #ifdef WRITE_CODE
    FILE *fp_write_code = fopen("out-code", "wb");
    fwrite(fun_data, fun_len, 1, fp_write_code);
    fclose(fp_write_code);
    #endif
    #else
    (void)fun_len;
    #endif
}
#endif

mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, const mp_module_context_t *context, const mp_obj_t *def_args) {
    DEBUG_OP_printf("make_function_from_raw_code %p\n", rc);
    assert(rc != NULL);

    // def_args must be MP_OBJ_NULL or a tuple
    assert(def_args == NULL || def_args[0] == MP_OBJ_NULL || mp_obj_is_type(def_args[0], &mp_type_tuple));

    // def_kw_args must be MP_OBJ_NULL or a dict
    assert(def_args == NULL || def_args[1] == MP_OBJ_NULL || mp_obj_is_type(def_args[1], &mp_type_dict));

    // make the function, depending on the raw code kind
    mp_obj_t fun;
    switch (rc->kind) {
        #if MICROPY_EMIT_NATIVE
        case MP_CODE_NATIVE_PY:
        case MP_CODE_NATIVE_VIPER:
            fun = mp_obj_new_fun_native(def_args, rc->fun_data, context, rc->children);
            // Check for a generator function, and if so change the type of the object
            if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) {
                ((mp_obj_base_t *)MP_OBJ_TO_PTR(fun))->type = &mp_type_native_gen_wrap;
            }
            break;
        #endif
        #if MICROPY_EMIT_INLINE_ASM
        case MP_CODE_NATIVE_ASM:
            fun = mp_obj_new_fun_asm(rc->n_pos_args, rc->fun_data, rc->type_sig);
            break;
        #endif
        default:
            // rc->kind should always be set and BYTECODE is the only remaining case
            assert(rc->kind == MP_CODE_BYTECODE);
            fun = mp_obj_new_fun_bc(def_args, rc->fun_data, context, rc->children);
            // check for generator functions and if so change the type of the object
            if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) {
                ((mp_obj_base_t *)MP_OBJ_TO_PTR(fun))->type = &mp_type_gen_wrap;
            }

            #if MICROPY_PY_SYS_SETTRACE
            mp_obj_fun_bc_t *self_fun = (mp_obj_fun_bc_t *)MP_OBJ_TO_PTR(fun);
            self_fun->rc = rc;
            #endif

            break;
    }

    return fun;
}

mp_obj_t mp_make_closure_from_raw_code(const mp_raw_code_t *rc, const mp_module_context_t *context, mp_uint_t n_closed_over, const mp_obj_t *args) {
    DEBUG_OP_printf("make_closure_from_raw_code %p " UINT_FMT " %p\n", rc, n_closed_over, args);
    // make function object
    mp_obj_t ffun;
    if (n_closed_over & 0x100) {
        // default positional and keyword args given
        ffun = mp_make_function_from_raw_code(rc, context, args);
    } else {
        // default positional and keyword args not given
        ffun = mp_make_function_from_raw_code(rc, context, NULL);
    }
    // wrap function in closure object
    return mp_obj_new_closure(ffun, n_closed_over & 0xff, args + ((n_closed_over >> 7) & 2));
}