extmod: Add modmpycross.

.. with supporting changes across the core.

Signed-off-by: Jeff Epler <jepler@gmail.com>
This commit is contained in:
Jeff Epler 2025-08-28 16:44:39 -05:00
parent af745a5982
commit 9251732f74
10 changed files with 316 additions and 12 deletions

View file

@ -31,6 +31,7 @@ SRC_EXTMOD_C += \
extmod/modlwip.c \
extmod/modmachine.c \
extmod/modmarshal.c \
extmod/modmpycross.c \
extmod/modnetwork.c \
extmod/modonewire.c \
extmod/modopenamp.c \

251
extmod/modmpycross.c Normal file
View file

@ -0,0 +1,251 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2025 Jeff Epler
*
* 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.
*/
#include "py/runtime.h"
#if MICROPY_PY_MPYCROSS
#include "py/builtin.h"
#include "py/compile.h"
#include "py/persistentcode.h"
static bool parse_arch(qstr arch) {
#if MICROPY_EMIT_X86
if (arch == MP_QSTR_x86) {
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_X86;
mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_X86;
return true;
}
#endif
#if MICROPY_EMIT_X64
if (arch == MP_QSTR_x64) {
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_X64;
mp_dynamic_compiler.nlr_buf_num_regs = MAX(MICROPY_NLR_NUM_REGS_X64, MICROPY_NLR_NUM_REGS_X64_WIN);
return true;
}
#endif
#if MICROPY_EMIT_INLINE_THUMB_FLOAT
if (arch == MP_QSTR_armv6) {
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_ARMV6;
mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_ARM_THUMB_FP;
return true;
}
if (arch == MP_QSTR_armv6m) {
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_ARMV6M;
mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_ARM_THUMB_FP; // need to be conservative so this code can run on armv7emdp
return true;
}
#endif
#if MICROPY_EMIT_THUMB_ARMV7M
if (arch == MP_QSTR_armv7m) {
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_ARMV7M;
mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_ARM_THUMB_FP;
return true;
}
if (arch == MP_QSTR_armv7em) {
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_ARMV7EM;
mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_ARM_THUMB_FP;
return true;
}
if (arch == MP_QSTR_armv7emsp) {
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_ARMV7EMSP;
mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_ARM_THUMB_FP;
return true;
}
if (arch == MP_QSTR_armv7emdp) {
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_ARMV7EMDP;
mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_ARM_THUMB_FP;
return true;
}
#endif
#if MICROPY_EMIT_XTENSA
if (arch == MP_QSTR_xtensa) {
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_XTENSA;
mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_XTENSA;
return true;
}
#endif
#if MICROPY_EMIT_XTENSAWIN
if (arch == MP_QSTR_xtensawin) {
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_XTENSAWIN;
mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_XTENSAWIN;
return true;
}
#endif
#if MICROPY_EMIT_RV32
if (arch == MP_QSTR_rv32imc) {
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_RV32IMC;
mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_RV32I;
return true;
}
#endif
#if MICROPY_EMIT_NATIVE_DEBUG
if (arch == MP_QSTR_debug) {
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_DEBUG;
mp_dynamic_compiler.nlr_buf_num_regs = 0;
return true;
}
#endif
if (arch == MP_QSTR_host) {
#if defined(__i386__) || defined(_M_IX86)
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_X86;
mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_X86;
return true;
#elif defined(__x86_64__) || defined(_M_X64)
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_X64;
mp_dynamic_compiler.nlr_buf_num_regs = MAX(MICROPY_NLR_NUM_REGS_X64, MICROPY_NLR_NUM_REGS_X64_WIN);
return true;
#elif defined(__arm__) && !defined(__thumb2__)
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_ARMV6;
mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_ARM_THUMB_FP;
return true;
#else
mp_raise_ValueError(MP_ERROR_TEXT("unable to determine host architecture for emit_arch=\"host\"\n"));
#endif
}
return false;
}
static mp_obj_t mpy_compile(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_source, ARG_filename,
ARG_emit_arch, ARG_small_int_bits, ARG_nlr_buf_num_regs,
#if MICROPY_EMIT_NATIVE
ARG_emit_opt,
#endif
};
mp_arg_t allowed_args[] = {
{MP_QSTR_source, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_ROM_NONE}},
{MP_QSTR_filename, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_ROM_NONE}},
{MP_QSTR_emit_arch, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE}},
{MP_QSTR_small_int_bits, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 31}},
{MP_QSTR_nlr_buf_num_regs, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1}},
#if MICROPY_EMIT_NATIVE
{MP_QSTR_emit_opt, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = MP_EMIT_OPT_NONE}},
#endif
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
struct mp_dynamic_compiler_t restore_compiler = mp_dynamic_compiler;
#if MICROPY_EMIT_NATIVE
uint restore_emit_opt = MP_STATE_VM(default_emit_opt);
#endif
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
mp_dynamic_compiler.small_int_bits = args[ARG_small_int_bits].u_int;
if (args[ARG_emit_arch].u_obj == MP_ROM_NONE) {
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_NONE;
} else {
if (!parse_arch(mp_obj_str_get_qstr(args[ARG_emit_arch].u_obj))) {
mp_raise_ValueError(MP_ERROR_TEXT("Invalid emit_arch"));
}
}
if (args[ARG_nlr_buf_num_regs].u_int >= 0) {
mp_dynamic_compiler.nlr_buf_num_regs = args[ARG_nlr_buf_num_regs].u_int;
}
#if MICROPY_EMIT_NATIVE
// Set default emitter options
if (args[ARG_emit_opt].u_int < 0 || args[ARG_emit_opt].u_int > MP_EMIT_OPT_ASM) {
mp_raise_ValueError(MP_ERROR_TEXT("Invalid emit_opt"));
}
MP_STATE_VM(default_emit_opt) = args[ARG_emit_opt].u_int;
if ((MP_STATE_VM(default_emit_opt) == MP_EMIT_OPT_NATIVE_PYTHON
|| MP_STATE_VM(default_emit_opt) == MP_EMIT_OPT_VIPER)
&& mp_dynamic_compiler.native_arch == MP_NATIVE_ARCH_NONE) {
mp_raise_ValueError(MP_ERROR_TEXT("arch not specified\n"));
}
#endif
size_t str_len;
const char *str = mp_obj_str_get_data(args[ARG_source].u_obj, &str_len);
// get the filename
qstr filename = mp_obj_str_get_qstr(args[ARG_filename].u_obj);
// create the lexer
mp_lexer_t *lex = mp_lexer_new_from_str_len(filename, str, str_len, 0);
mp_parse_tree_t parse_tree = mp_parse(lex, MP_PARSE_FILE_INPUT);
mp_module_context_t ctx;
ctx.module.globals = NULL;
mp_compiled_module_t cm;
cm.context = &ctx;
mp_compile_to_raw_code(&parse_tree, lex->source_name, false, &cm);
vstr_t vstr;
vstr_init(&vstr, 64);
mp_print_t print = {&vstr, (mp_print_strn_t)vstr_add_strn};
mp_raw_code_save(&cm, &print);
mp_obj_t result = mp_obj_new_bytes_from_vstr(&vstr);
nlr_pop();
mp_dynamic_compiler = restore_compiler;
#if MICROPY_EMIT_NATIVE
MP_STATE_VM(default_emit_opt) = restore_emit_opt;
#endif
return result;
} else {
mp_dynamic_compiler = restore_compiler;
#if MICROPY_EMIT_NATIVE
MP_STATE_VM(default_emit_opt) = restore_emit_opt;
#endif
nlr_raise(MP_OBJ_FROM_PTR(nlr.ret_val));
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(mpy_compile_obj, 1, mpy_compile);
static const mp_rom_map_elem_t mp_module_mpycross_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mpycross) },
{ MP_ROM_QSTR(MP_QSTR_compile), MP_ROM_PTR(&mpy_compile_obj) },
{ MP_ROM_QSTR(MP_QSTR_EMIT_OPT_NONE), MP_ROM_INT(MP_EMIT_OPT_NONE) },
{ MP_ROM_QSTR(MP_QSTR_EMIT_OPT_BYTECODE), MP_ROM_INT(MP_EMIT_OPT_BYTECODE) },
#if MICROPY_EMIT_NATIVE
{ MP_ROM_QSTR(MP_QSTR_EMIT_OPT_NATIVE_PYTHON), MP_ROM_INT(MP_EMIT_OPT_NATIVE_PYTHON) },
{ MP_ROM_QSTR(MP_QSTR_EMIT_OPT_VIPER), MP_ROM_INT(MP_EMIT_OPT_VIPER) },
{ MP_ROM_QSTR(MP_QSTR_EMIT_OPT_ASM), MP_ROM_INT(MP_EMIT_OPT_ASM) },
#endif
};
static MP_DEFINE_CONST_DICT(mp_module_mpycross_globals, mp_module_mpycross_globals_table);
const mp_obj_module_t mp_module_mpycross = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&mp_module_mpycross_globals,
};
MP_REGISTER_MODULE(MP_QSTR_mpycross, mp_module_mpycross);
#endif

View file

@ -50,26 +50,27 @@ typedef struct _mmap_region_t {
void mp_unix_alloc_exec(size_t min_size, void **ptr, size_t *size) {
// size needs to be a multiple of the page size
*size = (min_size + 0xfff) & (~0xfff);
*ptr = mmap(NULL, *size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
size_t rounded_size = (min_size + 0xfff) & (~0xfff);
*ptr = mmap(NULL, rounded_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (*ptr == MAP_FAILED) {
*ptr = NULL;
}
*size = min_size;
// add new link to the list of mmap'd regions
mmap_region_t *rg = m_new_obj(mmap_region_t);
rg->ptr = *ptr;
rg->len = min_size;
rg->len = rounded_size;
rg->next = MP_STATE_VM(mmap_region_head);
MP_STATE_VM(mmap_region_head) = rg;
}
void mp_unix_free_exec(void *ptr, size_t size) {
munmap(ptr, size);
// unlink the mmap'd region from the list
for (mmap_region_t **rg = (mmap_region_t **)&MP_STATE_VM(mmap_region_head); *rg != NULL; *rg = (*rg)->next) {
if ((*rg)->ptr == ptr) {
munmap(ptr, (*rg)->len);
mmap_region_t *next = (*rg)->next;
m_del_obj(mmap_region_t, *rg);
*rg = next;

View file

@ -56,6 +56,10 @@
#include "input.h"
#include "stack_size.h"
#if MICROPY_DYNAMIC_COMPILER
#include "py/persistentcode.h"
#endif
// Command line options, with their defaults
static bool compile_only = false;
static uint emit_opt = MP_EMIT_OPT_NONE;
@ -506,6 +510,31 @@ MP_NOINLINE int main_(int argc, char **argv) {
signal(SIGPIPE, SIG_IGN);
#endif
#if MICROPY_EMIT_NATIVE
// Set default emitter options
MP_STATE_VM(default_emit_opt) = emit_opt;
#endif
#if MICROPY_DYNAMIC_COMPILER
// set default compiler configuration
mp_dynamic_compiler.small_int_bits = 8 * sizeof(mp_int_t) - 1;
// don't support native emitter unless -march is specified
#if defined(__i386__) || defined(_M_IX86)
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_X86;
mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_X86;
#elif defined(__x86_64__) || defined(_M_X64)
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_X64;
mp_dynamic_compiler.nlr_buf_num_regs = MAX(MICROPY_NLR_NUM_REGS_X64, MICROPY_NLR_NUM_REGS_X64_WIN);
#elif defined(__arm__) && !defined(__thumb2__)
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_ARMV6;
mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_ARM_THUMB_FP;
#else
mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_NONE;
mp_dynamic_compiler.nlr_buf_num_regs = 0;
#endif
#endif
pre_process_options(argc, argv);
#if MICROPY_ENABLE_GC

View file

@ -53,3 +53,20 @@
#define MICROPY_HW_MCU_NAME MICROPY_PY_SYS_PLATFORM
// Keep the standard banner message
#define MICROPY_BANNER_MACHINE MICROPY_PY_SYS_PLATFORM " [" MICROPY_PLATFORM_COMPILER "] version"
#define MICROPY_PY_MPYCROSS (1)
#define MICROPY_EMIT_X64 (1)
#define MICROPY_EMIT_X86 (1)
#define MICROPY_EMIT_THUMB (1)
#define MICROPY_EMIT_INLINE_THUMB (1)
#define MICROPY_EMIT_ARM (1)
#define MICROPY_EMIT_XTENSA (1)
#define MICROPY_EMIT_INLINE_XTENSA (1)
#define MICROPY_EMIT_XTENSAWIN (1)
#define MICROPY_EMIT_RV32 (1)
#define MICROPY_EMIT_INLINE_RV32 (1)
#define MICROPY_EMIT_NATIVE_DEBUG (1)
#define MICROPY_EMIT_NATIVE_DEBUG_PRINTER (&mp_plat_print)
#define MICROPY_DYNAMIC_COMPILER (1)

View file

@ -703,8 +703,6 @@ void asm_rv32_meta_comparison_le(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2
void asm_rv32_emit_optimised_load_immediate(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate);
#ifdef GENERIC_ASM_API
void asm_rv32_emit_call_ind(asm_rv32_t *state, mp_uint_t index);
void asm_rv32_emit_jump(asm_rv32_t *state, mp_uint_t label);
void asm_rv32_emit_jump_if_reg_eq(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t label);
@ -717,6 +715,8 @@ void asm_rv32_emit_mov_reg_pcrel(asm_rv32_t *state, mp_uint_t rd, mp_uint_t labe
void asm_rv32_emit_optimised_xor(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs);
void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t source, mp_uint_t base, int32_t offset, mp_uint_t operation_size);
#ifdef GENERIC_ASM_API
#define ASM_T asm_rv32_t
#define ASM_ENTRY(state, labels, name) asm_rv32_entry(state, labels)
#define ASM_EXIT(state) asm_rv32_exit(state)

View file

@ -287,7 +287,7 @@ void asm_xtensa_mov_reg_pcrel(asm_xtensa_t *as, uint reg_dest, uint label) {
asm_xtensa_op_add_n(as, reg_dest, reg_dest, ASM_XTENSA_REG_A0);
}
void asm_xtensa_l32i_optimised(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint word_offset) {
static void asm_xtensa_l32i_optimised(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint word_offset) {
if (word_offset < 16) {
asm_xtensa_op_l32i_n(as, reg_dest, reg_base, word_offset);
} else if (word_offset < 256) {

View file

@ -74,7 +74,7 @@ $(BUILD)/%.o: %.s
define compile_c
$(ECHO) "CC $<"
$(Q)$(CC) $(CFLAGS) -c -MD -MF $(@:.o=.d) -o $@ $< || (echo -e $(HELP_BUILD_ERROR); false)
$(Q)$(CC) $(CFLAGS) -c -MD -MF $(@:.o=.d) -MT "$@" -o $@ $< || (echo -e $(HELP_BUILD_ERROR); false)
@# The following fixes the dependency file.
@# See http://make.paulandlesley.org/autodep.html for details.
@# Regex adjusted from the above to play better with Windows paths, etc.

View file

@ -1831,6 +1831,10 @@ typedef time_t mp_timestamp_t;
#define MICROPY_PY_OS_STATVFS (MICROPY_PY_OS)
#endif
#ifndef MICROPY_PY_MPYCROSS
#define MICROPY_PY_MPYCROSS (0)
#endif
#ifndef MICROPY_PY_RE
#define MICROPY_PY_RE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)
#endif

View file

@ -72,13 +72,14 @@ cppexample cryptolib deflate errno
example_package ffi framebuf
gc hashlib heapq io
json machine marshal math
os platform random re
select socket struct sys
termios time tls uctypes
vfs websocket
mpycross os platform random
re select socket struct
sys termios time tls
uctypes vfs websocket
me
micropython machine marshal math
mpycross
argv atexit byteorder exc_info
executable exit getsizeof implementation