diff --git a/mpy-cross/main.c b/mpy-cross/main.c index f3ffff61fdf80..5ad4aab3dbe71 100644 --- a/mpy-cross/main.c +++ b/mpy-cross/main.c @@ -228,7 +228,7 @@ MP_NOINLINE int main_(int argc, char **argv) { a += 1; } else if (strcmp(argv[a], "--version") == 0) { printf("MicroPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE - "; mpy-cross emitting mpy v" MP_STRINGIFY(MPY_VERSION) "\n"); + "; mpy-cross emitting mpy v" MP_STRINGIFY(MPY_VERSION_BYTECODE) "-v" MP_STRINGIFY(MPY_VERSION_NATIVE) "\n"); return 0; } else if (strcmp(argv[a], "-v") == 0) { mp_verbose_flag++; diff --git a/py/persistentcode.c b/py/persistentcode.c index 1b1741f2614e2..d6355b692ba5f 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -393,14 +393,15 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *co mp_compiled_module_t mp_raw_code_load(mp_reader_t *reader, mp_module_context_t *context) { byte header[4]; read_bytes(reader, header, sizeof(header)); + byte arch = MPY_FEATURE_DECODE_ARCH(header[2]); if (header[0] != 'M' - || header[1] != MPY_VERSION + || (arch == MP_NATIVE_ARCH_NONE && (header[1] < MPY_VERSION_BYTECODE || header[1] > MPY_VERSION_NATIVE)) + || (arch != MP_NATIVE_ARCH_NONE && header[1] != MPY_VERSION_NATIVE) || MPY_FEATURE_DECODE_FLAGS(header[2]) != MPY_FEATURE_FLAGS || header[3] > MP_SMALL_INT_BITS) { mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file")); } - if (MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE) { - byte arch = MPY_FEATURE_DECODE_ARCH(header[2]); + if (arch != MP_NATIVE_ARCH_NONE) { if (!MPY_FEATURE_ARCH_TEST(arch)) { if (MPY_FEATURE_ARCH_TEST(MP_NATIVE_ARCH_NONE)) { // On supported ports this can be resolved by enabling feature, eg @@ -595,7 +596,7 @@ void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print) { // byte number of bits in a small int byte header[4] = { 'M', - MPY_VERSION, + MPY_VERSION_BYTECODE, MPY_FEATURE_ENCODE_FLAGS(MPY_FEATURE_FLAGS_DYNAMIC), #if MICROPY_DYNAMIC_COMPILER mp_dynamic_compiler.small_int_bits, @@ -604,6 +605,7 @@ void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print) { #endif }; if (cm->has_native) { + header[1] = MPY_VERSION_NATIVE; header[2] |= MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH_DYNAMIC); } mp_print_bytes(print, header, sizeof(header)); diff --git a/py/persistentcode.h b/py/persistentcode.h index 29ccce4a3d968..6f7a4820247eb 100644 --- a/py/persistentcode.h +++ b/py/persistentcode.h @@ -30,8 +30,16 @@ #include "py/reader.h" #include "py/emitglue.h" -// The current version of .mpy files -#define MPY_VERSION 6 +// The current version of .mpy files. For a bytecode only .mpy file (i.e. arch +// set to 0), we will load any file with a version between MPY_VERSION_BYTECODE +// and MPY_VERSION_NATIVE. When an arch is set (i.e. it used emit=native, or the +// native dectorator, or was a dynamic native module), then it must exactly +// match MPY_VERSION_NATIVE. +// This lets us advance the native ABI (e.g. dynruntime.h) without needing +// to break compatibility for bytecode-only .mpy files. +// Must be kept in sync with tools/mpy-tool.py and tools/mpy_ld.py. +#define MPY_VERSION_BYTECODE 6 +#define MPY_VERSION_NATIVE 6 // Macros to encode/decode flags to/from the feature byte #define MPY_FEATURE_ENCODE_FLAGS(flags) (flags) @@ -81,7 +89,7 @@ #endif // 16-bit little-endian integer with the second and third bytes of supported .mpy files -#define MPY_FILE_HEADER_INT (MPY_VERSION \ +#define MPY_FILE_HEADER_INT (MPY_VERSION_NATIVE \ | (MPY_FEATURE_ENCODE_FLAGS(MPY_FEATURE_FLAGS) | MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH)) << 8) enum { diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 31212fd5bdda8..b7d225ff1dd2c 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -87,7 +87,10 @@ def __str__(self): class Config: - MPY_VERSION = 6 + # Must be kept in sync with py/persistentcode.h. + # See comments there for bytecode vs native version. + MPY_VERSION_BYTECODE = 6 + MPY_VERSION_NATIVE = 6 MICROPY_LONGINT_IMPL_NONE = 0 MICROPY_LONGINT_IMPL_LONGLONG = 1 MICROPY_LONGINT_IMPL_MPZ = 2 @@ -1330,11 +1333,14 @@ def read_mpy(filename): header = reader.read_bytes(4) if header[0] != ord("M"): raise MPYReadError(filename, "not a valid .mpy file") - if header[1] != config.MPY_VERSION: - raise MPYReadError(filename, "incompatible .mpy version") feature_byte = header[2] mpy_native_arch = feature_byte >> 2 - if mpy_native_arch != MP_NATIVE_ARCH_NONE: + if mpy_native_arch == MP_NATIVE_ARCH_NONE: + if header[1] < config.MPY_VERSION_BYTECODE or header[1] > config.MPY_VERSION_NATIVE: + raise MPYReadError(filename, "incompatible bytecode .mpy version") + else: + if header[1] != config.MPY_VERSION_NATIVE: + raise MPYReadError(filename, "incompatible native .mpy version") if config.native_arch == MP_NATIVE_ARCH_NONE: config.native_arch = mpy_native_arch elif config.native_arch != mpy_native_arch: @@ -1669,7 +1675,9 @@ def merge_mpy(compiled_modules, output_file): header = bytearray(4) header[0] = ord("M") - header[1] = config.MPY_VERSION + header[1] = ( + config.MPY_VERSION_NATIVE if config.native_arch else config.MPY_VERSION_BYTECODE + ) header[2] = config.native_arch << 2 header[3] = config.mp_small_int_bits merged_mpy.extend(header) diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index 09ea90dcd1106..69f3cad5818c0 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -34,8 +34,12 @@ sys.path.append(os.path.dirname(__file__) + "/../py") import makeqstrdata as qstrutil +# Must be kept in sync with py/persistentcode.h. +# See comments there for bytecode vs native version. +MPY_VERSION_BYTECODE = 6 +MPY_VERSION_NATIVE = 6 + # MicroPython constants -MPY_VERSION = 6 MP_CODE_BYTECODE = 2 MP_CODE_NATIVE_VIPER = 4 MP_NATIVE_ARCH_X86 = 1 @@ -917,7 +921,9 @@ def build_mpy(env, entry_offset, fmpy, native_qstr_vals, native_qstr_objs): out.open(fmpy) # MPY: header - out.write_bytes(bytearray([ord("M"), MPY_VERSION, env.arch.mpy_feature, MP_SMALL_INT_BITS])) + out.write_bytes( + bytearray([ord("M"), MPY_VERSION_NATIVE, env.arch.mpy_feature, MP_SMALL_INT_BITS]) + ) # MPY: n_qstr out.write_uint(1 + len(native_qstr_vals))