From d07fa9d6ced9a83bc40eff3cd7e8163b0e591f73 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 19 Dec 2025 21:20:49 +0100 Subject: [PATCH 1/5] py/persistentcode: Detect the target architecture from compiler defines. This commit replaces the target architecture definition method, from relying on defines in MicroPython's configuration, to using the host compiler's environment definitions instead. Before these changes the target type was inferred from which kind of native emitter was enabled, since there can be only just one available at all times and it has to be the correct one otherwise generated code would crash the target. However, with the introduction of RV64 there is now a platform without an emitter, and thus RV64 targets would not be able to report their platform via "sys.implementation._mpy". The target is reported only if the interpreter is configured to load external MPY code. Now a series of compile definitions are used to detect the target platform the firmware image is compiled for, making the target definition more accurate and much harder to force the interpreter to report the wrong target architecture due to a misconfiguration. Whilst this is probably not directly affecting user-facing code, some infrastructure components like the testing framework and its associated feature scripts rely on this feature to properly execute test runs. This is also a concern for situations in which loading external binary code is needed but all code generation functions have to be disabled. Signed-off-by: Alessandro Gatti (cherry picked from commit 5ae928a2b4ebaab62971280ce531b4e47014f247) --- py/persistentcode.h | 55 ++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/py/persistentcode.h b/py/persistentcode.h index 85668e608c8..e8e3431c8e6 100644 --- a/py/persistentcode.h +++ b/py/persistentcode.h @@ -53,31 +53,44 @@ #define MPY_FEATURE_DECODE_ARCH(feat) (((feat) >> 2) & 0x2F) // Define the host architecture -#if MICROPY_EMIT_X86 - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_X86) -#elif MICROPY_EMIT_X64 - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_X64) -#elif MICROPY_EMIT_THUMB - #if defined(__thumb2__) - #if defined(__ARM_FP) && (__ARM_FP & 8) == 8 - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EMDP) - #elif defined(__ARM_FP) && (__ARM_FP & 4) == 4 - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EMSP) +#if MICROPY_PERSISTENT_CODE_LOAD_NATIVE + #if defined(__i386__) || defined(_M_IX86) + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_X86) + #elif defined(__x86_64__) || defined(_M_X64) + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_X64) + #elif defined(__thumb2__) || defined(__thumb__) + #if defined(__thumb2__) + #if defined(__ARM_FP) && (__ARM_FP & 8) == 8 + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EMDP) + #elif defined(__ARM_FP) && (__ARM_FP & 4) == 4 + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EMSP) + #else + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EM) + #endif #else - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EM) + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV6M) + #endif + #define MPY_FEATURE_ARCH_TEST(x) (MP_NATIVE_ARCH_ARMV6M <= (x) && (x) <= MPY_FEATURE_ARCH) + #elif defined(__arm__) + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV6) + #elif defined(__xtensa__) + #include + #if XCHAL_HAVE_WINDOWED + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_XTENSAWIN) + #else + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_XTENSA) + #endif + #elif defined(__riscv) + #if __riscv_xlen == 32 + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_RV32IMC) + #elif __riscv_xlen == 64 + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_RV64IMC) + #else + #error "Unsupported RISC-V architecture." #endif #else - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV6M) + #error "Unsupported native architecture." #endif - #define MPY_FEATURE_ARCH_TEST(x) (MP_NATIVE_ARCH_ARMV6M <= (x) && (x) <= MPY_FEATURE_ARCH) -#elif MICROPY_EMIT_ARM - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV6) -#elif MICROPY_EMIT_XTENSA - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_XTENSA) -#elif MICROPY_EMIT_XTENSAWIN - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_XTENSAWIN) -#elif MICROPY_EMIT_RV32 - #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_RV32IMC) #else #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_NONE) #endif From 09c7e02e9ee51a9885eda94062df2aae9764ff5d Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 20 Dec 2025 07:13:51 +0100 Subject: [PATCH 2/5] py/persistentcode: Decouple native code loading from emitters' presence. This commit lets the interpreter load MPY files containing native code even if the target platform does not have a native emitter, or if native code generation is disabled. Native code loading has been tied to native code generation being enabled as a discriminant to allow said operation. This blocks native code loading on platforms that could benefit from such a thing but they don't (and probably won't) have a native code generation target written for them (ie. AArch64 and RISC-V 64). This also forces a firmware image to have a full native code compiler present even if it doesn't need to generate anything, as native modules already have all the code they will ever need to load. There is a new configuration setting, MICROPY_PERSISTENT_CODE_LOAD_NATIVE, that if enabled it will allow loading native code modules even if code generation (MICROPY_EMIT_ and MICROPY_EMIT_INLINE_) is explicitly turned off. Signed-off-by: Alessandro Gatti (cherry picked from commit b0f3ecd96c8ca87f38366422f8cb74e0ce9fa2ec) --- py/bc.c | 2 +- py/emitglue.c | 4 ++-- py/emitglue.h | 4 ++-- py/mpconfig.h | 15 ++++++++++++--- py/nativeglue.c | 10 +++++----- py/nativeglue.h | 4 ++-- py/objfun.c | 10 +++------- py/objfun.h | 2 +- py/objgenerator.c | 10 +++++----- py/persistentcode.c | 14 +++++++------- py/runtime.c | 2 +- 11 files changed, 41 insertions(+), 36 deletions(-) diff --git a/py/bc.c b/py/bc.c index 4caa022e3d8..c302b2ec86a 100644 --- a/py/bc.c +++ b/py/bc.c @@ -336,7 +336,7 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw mp_setup_code_state_helper(code_state, n_args, n_kw, args); } -#if MICROPY_EMIT_NATIVE +#if MICROPY_ENABLE_NATIVE_CODE // On entry code_state should be allocated somewhere (stack/heap) and // contain the following valid entries: // - code_state->fun_bc should contain a pointer to the function object diff --git a/py/emitglue.c b/py/emitglue.c index c6ad3e3bc38..0553643bb41 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -98,7 +98,7 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, #endif } -#if MICROPY_EMIT_MACHINE_CODE +#if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, const void *fun_data, mp_uint_t fun_len, mp_raw_code_t **children, #if MICROPY_PERSISTENT_CODE_SAVE @@ -219,7 +219,7 @@ mp_obj_t mp_make_function_from_proto_fun(mp_proto_fun_t proto_fun, const mp_modu // make the function, depending on the raw code kind mp_obj_t fun; switch (rc->kind) { - #if MICROPY_EMIT_NATIVE + #if MICROPY_ENABLE_NATIVE_CODE case MP_CODE_NATIVE_PY: 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 diff --git a/py/emitglue.h b/py/emitglue.h index d19503b823f..674807910c1 100644 --- a/py/emitglue.h +++ b/py/emitglue.h @@ -83,7 +83,7 @@ typedef struct _mp_raw_code_t { #if MICROPY_PERSISTENT_CODE_SAVE uint32_t fun_data_len; // for mp_raw_code_save uint16_t n_children; - #if MICROPY_EMIT_MACHINE_CODE + #if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE uint16_t prelude_offset; #endif #if MICROPY_PY_SYS_SETTRACE @@ -116,7 +116,7 @@ typedef struct _mp_raw_code_truncated_t { #if MICROPY_PERSISTENT_CODE_SAVE uint32_t fun_data_len; uint16_t n_children; - #if MICROPY_EMIT_MACHINE_CODE + #if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE uint16_t prelude_offset; #endif #if MICROPY_PY_SYS_SETTRACE diff --git a/py/mpconfig.h b/py/mpconfig.h index 0e440066d78..63a39c6f8a3 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -426,6 +426,11 @@ typedef uint64_t mp_uint_t; #define MICROPY_PERSISTENT_CODE_LOAD (0) #endif +// Whether to support loading of persistent native code +#ifndef MICROPY_PERSISTENT_CODE_LOAD_NATIVE +#define MICROPY_PERSISTENT_CODE_LOAD_NATIVE (MICROPY_EMIT_MACHINE_CODE) +#endif + // Whether to support saving of persistent code, i.e. for mpy-cross to // generate .mpy files. Enabling this enables additional metadata on raw code // objects which is also required for sys.settrace. @@ -446,7 +451,7 @@ typedef uint64_t mp_uint_t; // Whether generated code can persist independently of the VM/runtime instance // This is enabled automatically when needed by other features #ifndef MICROPY_PERSISTENT_CODE -#define MICROPY_PERSISTENT_CODE (MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE || MICROPY_MODULE_FROZEN_MPY) +#define MICROPY_PERSISTENT_CODE (MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_LOAD_NATIVE || MICROPY_PERSISTENT_CODE_SAVE || MICROPY_MODULE_FROZEN_MPY) #endif // Whether bytecode uses a qstr_table to map internal qstr indices in the bytecode @@ -546,6 +551,10 @@ typedef uint64_t mp_uint_t; // Convenience definition for whether any native or inline assembler emitter is enabled #define MICROPY_EMIT_MACHINE_CODE (MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM) +// Convenience definition for whether native code has to be dealt with (either +// generated or loaded from a file). This does not cover inline asm code. +#define MICROPY_ENABLE_NATIVE_CODE (MICROPY_EMIT_NATIVE || MICROPY_PERSISTENT_CODE_LOAD_NATIVE) + /*****************************************************************************/ /* Compiler configuration */ @@ -2327,7 +2336,7 @@ typedef time_t mp_timestamp_t; // can be overridden if needed by defining both MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA // and MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA. #ifndef MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA -#if MICROPY_EMIT_MACHINE_CODE && MICROPY_PERSISTENT_CODE_LOAD +#if (MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE) && MICROPY_PERSISTENT_CODE_LOAD // Pointer tracking is required when loading native code is enabled. #if defined(MP_PLAT_ALLOC_EXEC) || defined(MP_PLAT_COMMIT_EXEC) // If a port defined a custom allocator or commit function for native text, then the @@ -2348,7 +2357,7 @@ typedef time_t mp_timestamp_t; #define MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA (1) #define MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA (0) #endif -#else // MICROPY_EMIT_MACHINE_CODE && MICROPY_PERSISTENT_CODE_LOAD +#else // (MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE) && MICROPY_PERSISTENT_CODE_LOAD // Pointer tracking not needed when loading native code is disabled. #define MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA (0) #define MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA (0) diff --git a/py/nativeglue.c b/py/nativeglue.c index 3b072ba8c38..2613312e2b7 100644 --- a/py/nativeglue.c +++ b/py/nativeglue.c @@ -43,7 +43,7 @@ #define DEBUG_printf(...) (void)0 #endif -#if MICROPY_EMIT_NATIVE +#if MICROPY_ENABLE_NATIVE_CODE int mp_native_type_from_qstr(qstr qst) { switch (qst) { @@ -93,7 +93,7 @@ mp_uint_t mp_native_from_obj(mp_obj_t obj, mp_uint_t type) { #endif -#if MICROPY_EMIT_MACHINE_CODE +#if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE // convert a native value to a MicroPython object based on type mp_obj_t mp_native_to_obj(mp_uint_t val, mp_uint_t type) { @@ -117,7 +117,7 @@ mp_obj_t mp_native_to_obj(mp_uint_t val, mp_uint_t type) { #endif -#if MICROPY_EMIT_NATIVE && !MICROPY_DYNAMIC_COMPILER +#if MICROPY_ENABLE_NATIVE_CODE && !MICROPY_DYNAMIC_COMPILER #if !MICROPY_PY_BUILTINS_SET mp_obj_t mp_obj_new_set(size_t n_args, mp_obj_t *items) { @@ -359,8 +359,8 @@ const mp_fun_table_t mp_fun_table = { &mp_stream_write_obj, }; -#elif MICROPY_EMIT_NATIVE && MICROPY_DYNAMIC_COMPILER +#elif MICROPY_ENABLE_NATIVE_CODE && MICROPY_DYNAMIC_COMPILER const int mp_fun_table; -#endif // MICROPY_EMIT_NATIVE +#endif // MICROPY_ENABLE_NATIVE_CODE diff --git a/py/nativeglue.h b/py/nativeglue.h index 01825ac60ed..f6ba600c99f 100644 --- a/py/nativeglue.h +++ b/py/nativeglue.h @@ -184,9 +184,9 @@ typedef struct _mp_fun_table_t { const mp_obj_fun_builtin_var_t *stream_write_obj; } mp_fun_table_t; -#if (MICROPY_EMIT_NATIVE && !MICROPY_DYNAMIC_COMPILER) || MICROPY_ENABLE_DYNRUNTIME +#if (MICROPY_ENABLE_NATIVE_CODE && !MICROPY_DYNAMIC_COMPILER) || MICROPY_ENABLE_DYNRUNTIME extern const mp_fun_table_t mp_fun_table; -#elif MICROPY_EMIT_NATIVE && MICROPY_DYNAMIC_COMPILER +#elif MICROPY_ENABLE_NATIVE_CODE && MICROPY_DYNAMIC_COMPILER // In dynamic-compiler mode eliminate dependency on entries in mp_fun_table. // This only needs to be an independent pointer, content doesn't matter. extern const int mp_fun_table; diff --git a/py/objfun.c b/py/objfun.c index 34565cf6336..4d421b08e0c 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -139,7 +139,7 @@ qstr mp_obj_fun_get_name(mp_const_obj_t fun_in) { const mp_obj_fun_bc_t *fun = MP_OBJ_TO_PTR(fun_in); const byte *bc = fun->bytecode; - #if MICROPY_EMIT_NATIVE + #if MICROPY_ENABLE_NATIVE_CODE if (fun->base.type == &mp_type_fun_native || fun->base.type == &mp_type_native_gen_wrap) { bc = mp_obj_fun_native_get_prelude_ptr(fun); } @@ -448,7 +448,7 @@ mp_obj_t mp_obj_new_fun_bc(const mp_obj_t *def_args, const byte *code, const mp_ /******************************************************************************/ /* native functions */ -#if MICROPY_EMIT_NATIVE +#if MICROPY_ENABLE_NATIVE_CODE // CIRCUITPY-CHANGE: PLACE_IN_ITCM static mp_obj_t PLACE_IN_ITCM(fun_native_call)(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { @@ -478,13 +478,9 @@ MP_DEFINE_CONST_OBJ_TYPE( call, fun_native_call ); -#endif // MICROPY_EMIT_NATIVE - /******************************************************************************/ /* viper functions */ -#if MICROPY_EMIT_NATIVE - static mp_obj_t fun_viper_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_cstack_check(); mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); @@ -499,7 +495,7 @@ MP_DEFINE_CONST_OBJ_TYPE( call, fun_viper_call ); -#endif // MICROPY_EMIT_NATIVE +#endif // MICROPY_ENABLE_NATIVE_CODE /******************************************************************************/ /* inline assembler functions */ diff --git a/py/objfun.h b/py/objfun.h index 4059343983c..06f85224b10 100644 --- a/py/objfun.h +++ b/py/objfun.h @@ -53,7 +53,7 @@ typedef struct _mp_obj_fun_asm_t { mp_obj_t mp_obj_new_fun_bc(const mp_obj_t *def_args, const byte *code, const mp_module_context_t *cm, struct _mp_raw_code_t *const *raw_code_table); void mp_obj_fun_bc_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest); -#if MICROPY_EMIT_NATIVE +#if MICROPY_ENABLE_NATIVE_CODE static inline mp_obj_t mp_obj_new_fun_native(const mp_obj_t *def_args, const void *fun_data, const mp_module_context_t *mc, struct _mp_raw_code_t *const *child_table) { mp_obj_fun_bc_t *o = (mp_obj_fun_bc_t *)MP_OBJ_TO_PTR(mp_obj_new_fun_bc(def_args, (const byte *)fun_data, mc, child_table)); diff --git a/py/objgenerator.c b/py/objgenerator.c index de063d38458..a1f86c66aae 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -114,7 +114,7 @@ MP_DEFINE_CONST_OBJ_TYPE( /******************************************************************************/ // native generator wrapper -#if MICROPY_EMIT_NATIVE +#if MICROPY_ENABLE_NATIVE_CODE // Based on mp_obj_gen_instance_t. typedef struct _mp_obj_gen_instance_native_t { @@ -184,7 +184,7 @@ MP_DEFINE_CONST_OBJ_TYPE( ); #endif -#endif // MICROPY_EMIT_NATIVE +#endif // MICROPY_ENABLE_NATIVE_CODE /******************************************************************************/ /* generator instance */ @@ -237,7 +237,7 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_ // If the generator is started, allow sending a value. void *state_start = self->code_state.state - 1; - #if MICROPY_EMIT_NATIVE + #if MICROPY_ENABLE_NATIVE_CODE if (self->code_state.exc_sp_idx == MP_CODE_STATE_EXC_SP_IDX_SENTINEL) { state_start = ((mp_obj_gen_instance_native_t *)self)->code_state.state - 1; } @@ -259,7 +259,7 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_ mp_vm_return_kind_t ret_kind; - #if MICROPY_EMIT_NATIVE + #if MICROPY_ENABLE_NATIVE_CODE if (self->code_state.exc_sp_idx == MP_CODE_STATE_EXC_SP_IDX_SENTINEL) { // A native generator. typedef uintptr_t (*mp_fun_native_gen_t)(void *, mp_obj_t); @@ -297,7 +297,7 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_ case MP_VM_RETURN_EXCEPTION: { self->code_state.ip = 0; - #if MICROPY_EMIT_NATIVE + #if MICROPY_ENABLE_NATIVE_CODE if (self->code_state.exc_sp_idx == MP_CODE_STATE_EXC_SP_IDX_SENTINEL) { *ret_val = ((mp_obj_gen_instance_native_t *)self)->code_state.state[0]; } else diff --git a/py/persistentcode.c b/py/persistentcode.c index 5c3de3174bc..5fcc7bd357f 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -76,7 +76,7 @@ typedef struct _bytecode_prelude_t { static int read_byte(mp_reader_t *reader); static size_t read_uint(mp_reader_t *reader); -#if MICROPY_EMIT_MACHINE_CODE +#if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE #if MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA || MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA @@ -229,7 +229,7 @@ static mp_obj_t mp_obj_new_str_static(const mp_obj_type_t *type, const byte *dat static mp_obj_t load_obj(mp_reader_t *reader) { byte obj_type = read_byte(reader); - #if MICROPY_EMIT_MACHINE_CODE + #if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE if (obj_type == MP_PERSISTENT_OBJ_FUN_TABLE) { return MP_OBJ_FROM_PTR(&mp_fun_table); } else @@ -302,14 +302,14 @@ static mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *co bool has_children = !!(kind_len & 4); size_t fun_data_len = kind_len >> 3; - #if !MICROPY_EMIT_MACHINE_CODE + #if !(MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE) if (kind != MP_CODE_BYTECODE) { mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file")); } #endif uint8_t *fun_data = NULL; - #if MICROPY_EMIT_MACHINE_CODE + #if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE size_t prelude_offset = 0; mp_uint_t native_scope_flags = 0; mp_uint_t native_n_pos_args = 0; @@ -329,7 +329,7 @@ static mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *co read_bytes(reader, fun_data, fun_data_len); } - #if MICROPY_EMIT_MACHINE_CODE + #if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE } else { // Allocate memory for native data and load it size_t fun_alloc; @@ -356,7 +356,7 @@ static mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *co size_t n_children = 0; mp_raw_code_t **children = NULL; - #if MICROPY_EMIT_MACHINE_CODE + #if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE // Load optional BSS/rodata for viper. uint8_t *rodata = NULL; uint8_t *bss = NULL; @@ -411,7 +411,7 @@ static mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *co #endif scope_flags); - #if MICROPY_EMIT_MACHINE_CODE + #if MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE } else { const uint8_t *prelude_ptr = NULL; #if MICROPY_EMIT_NATIVE_PRELUDE_SEPARATE_FROM_MACHINE_CODE diff --git a/py/runtime.c b/py/runtime.c index 81abc3de875..2664db68cb6 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -134,7 +134,7 @@ void mp_init(void) { MP_STATE_VM(mp_module_builtins_override_dict) = NULL; #endif - #if MICROPY_EMIT_MACHINE_CODE && (MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA || MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA) + #if (MICROPY_EMIT_INLINE_ASM || MICROPY_ENABLE_NATIVE_CODE) && (MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA || MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA) MP_STATE_VM(persistent_code_root_pointers) = MP_OBJ_NULL; #endif From 4da9e5afdf2ffd8ac29bf66a99463fcbc1ba7efb Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sat, 20 Dec 2025 10:02:57 +0100 Subject: [PATCH 3/5] py/emitglue: Flush caches when loading native code without emitters. This commit forces a data/instruction cache flush after loading native code blocks even if there's no emitter available. Caches flush upon native code load was still tied to the presence of a native emitter, but this assumption is no longer valid due to previous commits decoupling native code loading from code generation availability. Signed-off-by: Alessandro Gatti (cherry picked from commit 9e9da6cb0d794e85dbafcabd39972528afc15474) --- py/emitglue.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/py/emitglue.c b/py/emitglue.c index 0553643bb41..10931640999 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -113,7 +113,7 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, cons // 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 MICROPY_EMIT_THUMB || MICROPY_EMIT_INLINE_THUMB || (MP_NATIVE_ARCH_ARMV6M <= MPY_FEATURE_ARCH && MPY_FEATURE_ARCH <= MP_NATIVE_ARCH_ARMV7EMDP) // CIRCUITPY-CHANGE: prevent warning #if defined(__ICACHE_PRESENT) && __ICACHE_PRESENT == 1 // Flush D-cache, so the code emitted is stored in RAM. @@ -121,7 +121,7 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, cons // Invalidate I-cache, so the newly-created code is reloaded from RAM. SCB_InvalidateICache(); #endif - #elif MICROPY_EMIT_ARM + #elif MICROPY_EMIT_ARM || (MPY_FEATURE_ARCH == MP_NATIVE_ARCH_ARMV6) #if (defined(__linux__) && defined(__GNUC__)) || __ARM_ARCH == 7 __builtin___clear_cache((void *)fun_data, (char *)fun_data + fun_len); #elif defined(__arm__) @@ -134,7 +134,7 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, cons "mcr p15, 0, r0, c7, c7, 0\n" // invalidate I-cache and D-cache : : : "r0", "cc"); #endif - #elif (MICROPY_EMIT_RV32 || MICROPY_EMIT_INLINE_RV32) && defined(MP_HAL_CLEAN_DCACHE) + #elif (MICROPY_EMIT_RV32 || MICROPY_EMIT_INLINE_RV32 || (MPY_FEATURE_ARCH == MP_NATIVE_ARCH_RV32IMC)) && defined(MP_HAL_CLEAN_DCACHE) // Flush the D-cache. MP_HAL_CLEAN_DCACHE(fun_data, fun_len); #endif From c033932ecbb40d03b480853801f13ade7cf680dd Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 22 Dec 2025 08:34:41 +0100 Subject: [PATCH 4/5] mpy-cross/mpconfigport: Explicitly disable native code loading. This commit makes use of the new native code loading changes to provide a test bed for a target with the native compiler framework available but without no code loading capabilities. mpy-cross already fully disables native code loading, however those changes do not really propagate anywhere outside `py/persistentcode`. Recent changes in the code loading framework actually touch the core code in a few more places, but no default CI target is configured to run with the new native code loading defines switched off. Given that mpy-cross doesn't really need to load code anyway, it is a good target for setting up a configuration that goes a bit deeper when it comes to disabling code loading. Since mpy-cross is built several times during the CI process, it can be repurposed as an early warning system for issues related to these new changes. Normal operation should not be affected in any way. Signed-off-by: Alessandro Gatti (cherry picked from commit 8fb848f3aacd0d2216e02954cff4e9e15b0915a2) --- mpy-cross/mpconfigport.h | 1 + 1 file changed, 1 insertion(+) diff --git a/mpy-cross/mpconfigport.h b/mpy-cross/mpconfigport.h index 36bcbc11394..bae8779a244 100644 --- a/mpy-cross/mpconfigport.h +++ b/mpy-cross/mpconfigport.h @@ -32,6 +32,7 @@ #define MICROPY_ALLOC_PATH_MAX (PATH_MAX) #define MICROPY_PERSISTENT_CODE_LOAD (0) +#define MICROPY_PERSISTENT_CODE_LOAD_NATIVE (0) #define MICROPY_PERSISTENT_CODE_SAVE (1) #ifndef MICROPY_PERSISTENT_CODE_SAVE_FILE From c3ecca3d4272cf5fc81b2e521d21c3e85032f758 Mon Sep 17 00:00:00 2001 From: Mikey Sklar Date: Mon, 7 Sep 2026 09:47:02 -0700 Subject: [PATCH 5/5] py/emitglue: test the compiler target for the loader-only cache flush The cache flush conditions from upstream 9e9da6cb0d compare MPY_FEATURE_ARCH against MP_NATIVE_ARCH_* in preprocessor #if and #elif lines. Those are enum values, which the preprocessor reads as 0, and emitglue.c does not include persistentcode.h, so MPY_FEATURE_ARCH is undefined there as well. Upstream compiles because every identifier is 0 and the expressions are accidentally true; CircuitPython builds with -Werror=undef and stops, on the thumb line for ARM and on the RV32 line for any non-ARM target that falls through to it. Test the compiler defines that persistentcode.h itself now uses to pick the architecture, so a loader-only thumb build flushes the caches and a non-ARM build leaves the block out. Co-Authored-By: Claude Fable 5.1 --- py/emitglue.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/py/emitglue.c b/py/emitglue.c index 10931640999..d9a7fa29de8 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -113,7 +113,10 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, cons // 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 || (MP_NATIVE_ARCH_ARMV6M <= MPY_FEATURE_ARCH && MPY_FEATURE_ARCH <= MP_NATIVE_ARCH_ARMV7EMDP) + // CIRCUITPY-CHANGE: MP_NATIVE_ARCH_* are enum values, always 0 to the + // preprocessor, and MPY_FEATURE_ARCH is not defined here; test the + // compiler target instead. + #if MICROPY_EMIT_THUMB || MICROPY_EMIT_INLINE_THUMB || (MICROPY_PERSISTENT_CODE_LOAD_NATIVE && (defined(__thumb__) || defined(__thumb2__))) // CIRCUITPY-CHANGE: prevent warning #if defined(__ICACHE_PRESENT) && __ICACHE_PRESENT == 1 // Flush D-cache, so the code emitted is stored in RAM. @@ -121,7 +124,7 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, cons // Invalidate I-cache, so the newly-created code is reloaded from RAM. SCB_InvalidateICache(); #endif - #elif MICROPY_EMIT_ARM || (MPY_FEATURE_ARCH == MP_NATIVE_ARCH_ARMV6) + #elif MICROPY_EMIT_ARM || (MICROPY_PERSISTENT_CODE_LOAD_NATIVE && defined(__arm__) && !defined(__thumb__)) #if (defined(__linux__) && defined(__GNUC__)) || __ARM_ARCH == 7 __builtin___clear_cache((void *)fun_data, (char *)fun_data + fun_len); #elif defined(__arm__) @@ -134,7 +137,7 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, cons "mcr p15, 0, r0, c7, c7, 0\n" // invalidate I-cache and D-cache : : : "r0", "cc"); #endif - #elif (MICROPY_EMIT_RV32 || MICROPY_EMIT_INLINE_RV32 || (MPY_FEATURE_ARCH == MP_NATIVE_ARCH_RV32IMC)) && defined(MP_HAL_CLEAN_DCACHE) + #elif (MICROPY_EMIT_RV32 || MICROPY_EMIT_INLINE_RV32 || (MICROPY_PERSISTENT_CODE_LOAD_NATIVE && defined(__riscv))) && defined(MP_HAL_CLEAN_DCACHE) // Flush the D-cache. MP_HAL_CLEAN_DCACHE(fun_data, fun_len); #endif