8000 py/persistentcode.h: Split the native and bytecode ABI version. · micropython/micropython@bcd1ee7 · GitHub
[go: up one dir, main page]

Skip to content

Commit bcd1ee7

Browse files
committed
py/persistentcode.h: Split the native and bytecode ABI version.
The intent is to allow us to make breaking changes to the native ABI (e.g. changes to dynruntime.h) without needing the bytecode version to increment. With this commit, there's now a supported bytecode .mpy version and a native .mpy version. When generating an .mpy, if it has any native code then it gets the native version, otherwise the bytecode version. When loading an .mpy, if it has native code, then it must match the native version exactly. If it has only bytecode, then any version between the current bytecode version and native version will be loaded. e.g. MicroPython v1.19 will load any future bytecode .mpy until we increment `MPY_VERSION_BYTECODE`, but will only load v6 native mpy files. Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
1 parent 7480543 commit bcd1ee7

File tree

5 files changed

+35
-15
lines changed

5 files changed

+35
-15
lines changed

mpy-cross/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ MP_NOINLINE int main_(int argc, char **argv) {
228228
a += 1;
229229
} else if (strcmp(ar 8000 gv[a], "--version") == 0) {
230230
printf("MicroPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE
231-
"; mpy-cross emitting mpy v" MP_STRINGIFY(MPY_VERSION) "\n");
231+
"; mpy-cross emitting mpy v" MP_STRINGIFY(MPY_VERSION_BYTECODE) "-v" MP_STRINGIFY(MPY_VERSION_NATIVE) "\n");
232232
return 0;
233233
} else if (strcmp(argv[a], "-v") == 0) {
234234
mp_verbose_flag++;

py/persistentcode.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -393,14 +393,15 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *co
393393
mp_compiled_module_t mp_raw_code_load(mp_reader_t *reader, mp_module_context_t *context) {
394394
byte header[4];
395395
read_bytes(reader, header, sizeof(header));
396+
byte arch = MPY_FEATURE_DECODE_ARCH(header[2]);
396397
if (header[0] != 'M'
397-
|| header[1] != MPY_VERSION
398+
|| (arch == MP_NATIVE_ARCH_NONE && (header[1] < MPY_VERSION_BYTECODE || header[1] > MPY_VERSION_NATIVE))
399+
|| (arch != MP_NATIVE_ARCH_NONE && header[1] != MPY_VERSION_NATIVE)
398400
|| MPY_FEATURE_DECODE_FLAGS(header[2]) != MPY_FEATURE_FLAGS
399401
|| header[3] > MP_SMALL_INT_BITS) {
400402
mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file"));
401403
}
402-
if (MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE) {
403-
byte arch = MPY_FEATURE_DECODE_ARCH(header[2]);
404+
if (arch != MP_NATIVE_ARCH_NONE) {
404405
if (!MPY_FEATURE_ARCH_TEST(arch)) {
405406
if (MPY_FEATURE_ARCH_TEST(MP_NATIVE_ARCH_NONE)) {
406407
// 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) {
595596
// byte number of bits in a small int
596597
byte header[4] = {
597598
'M',
598-
MPY_VERSION,
599+
MPY_VERSION_BYTECODE,
599600
MPY_FEATURE_ENCODE_FLAGS(MPY_FEATURE_FLAGS_DYNAMIC),
600601
#if MICROPY_DYNAMIC_COMPILER
601602
mp_dynamic_compiler.small_int_bits,
@@ -604,6 +605,7 @@ void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print) {
604605
#endif
605606
};
606607
if (cm->has_native) {
608+
header[1] = MPY_VERSION_NATIVE;
607609
header[2] |= MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH_DYNAMIC);
608610
}
609611
mp_print_bytes(print, header, sizeof(header));

py/persistentcode.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,16 @@
3030
#include "py/reader.h"
3131
#include "py/emitglue.h"
3232

33-
// The current version of .mpy files
34-
#define MPY_VERSION 6
33+
// The current version of .mpy files. For a bytecode only .mpy file (i.e. arch
34+
// set to 0), we will load any file with a version between MPY_VERSION_BYTECODE
35+
// and MPY_VERSION_NATIVE. When an arch is set (i.e. it used emit=native, or the
36+
// native dectorator, or was a dynamic native module), then it must exactly
37+
// match MPY_VERSION_NATIVE.
38+
// This lets us advance the native ABI (e.g. dynruntime.h) without needing
39+
// to break compatibility for bytecode-only .mpy files.
40+
// Must be kept in sync with tools/mpy-tool.py and tools/mpy_ld.py.
41+
#define MPY_VERSION_BYTECODE 6
42+
#define MPY_VERSION_NATIVE 6
3543

3644
// Macros to encode/decode flags to/from the feature byte
3745
#define MPY_FEATURE_ENCODE_FLAGS(flags) (flags)
@@ -81,7 +89,7 @@
8189
#endif
8290

8391
// 16-bit little-endian integer with the second and third bytes of supported .mpy files
84-
#define MPY_FILE_HEADER_INT (MPY_VERSION \
92+
#define MPY_FILE_HEADER_INT (MPY_VERSION_NATIVE \
8593
| (MPY_FEATURE_ENCODE_FLAGS(MPY_FEATURE_FLAGS) | MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH)) << 8)
8694

8795
enum {

tools/mpy-tool.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,10 @@ def __str__(self):
8787

8888

8989
class Config:
90- 8000
MPY_VERSION = 6
90+
# Must be kept in sync with py/persistentcode.h.
91+
# See comments there for bytecode vs native version.
92+
MPY_VERSION_BYTECODE = 6
93+
MPY_VERSION_NATIVE = 6
9194
MICROPY_LONGINT_IMPL_NONE = 0
9295
MICROPY_LONGINT_IMPL_LONGLONG = 1
9396
MICROPY_LONGINT_IMPL_MPZ = 2
@@ -1330,11 +1333,14 @@ def read_mpy(filename):
13301333
header = reader.read_bytes(4)
13311334
if header[0] != ord("M"):
13321335
raise MPYReadError(filename, "not a valid .mpy file")
1333-
if header[1] != config.MPY_VERSION:
1334-
raise MPYReadError(filename, "incompatible .mpy version")
13351336
feature_byte = header[2]
13361337
mpy_native_arch = feature_byte >> 2
1337-
if mpy_native_arch != MP_NATIVE_ARCH_NONE:
1338+
if mpy_native_arch == MP_NATIVE_ARCH_NONE:
1339+
if header[1] < config.MPY_VERSION_BYTECODE or header[1] > config.MPY_VERSION_NATIVE:
1340+
raise MPYReadError(filename, "incompatible bytecode .mpy version")
1341+
else:
1342+
if header[1] != config.MPY_VERSION_NATIVE:
1343+
raise MPYReadError(filename, "incompatible native .mpy version")
13381344
if config.native_arch == MP_NATIVE_ARCH_NONE:
13391345
config.native_arch = mpy_native_arch
13401346
elif config.native_arch != mpy_native_arch:
@@ -1669,7 +1675,7 @@ def merge_mpy(compiled_modules, output_file):
16691675

16701676
header = bytearray(4)
16711677
header[0] = ord("M")
1672-
header[1] = config.MPY_VERSION
1678+
header[1] = config.MPY_VERSION_NATIVE if config.native_arch else config.MPY_VERSION_BYTECODE
16731679
header[2] = config.native_arch << 2
16741680
header[3] = config.mp_small_int_bits
16751681
merged_mpy.extend(header)

tools/mpy_ld.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,12 @@
3434
sys.path.append(os.path.dirname(__file__) + "/../py")
3535
import makeqstrdata as qstrutil
3636

37+
# Must be kept in sync with py/persistentcode.h.
38+
# See comments there for bytecode vs native version.
39+
MPY_VERSION_BYTECODE = 6
40+
MPY_VERSION_NATIVE = 6
41+
3742
# MicroPython constants
38-
MPY_VERSION = 6
3943
MP_CODE_BYTECODE = 2
4044
MP_CODE_NATIVE_VIPER = 4
4145
MP_NATIVE_ARCH_X86 = 1
@@ -917,7 +921,7 @@ def build_mpy(env, entry_offset, fmpy, native_qstr_vals, native_qstr_objs):
917921
out.open(fmpy)
918922

919923
# MPY: header
920-
out.write_bytes(bytearray([ord("M"), MPY_VERSION, env.arch.mpy_feature, MP_SMALL_INT_BITS]))
924+
out.write_bytes(bytearray([ord("M"), MPY_VERSION_NATIVE, env.arch.mpy_feature, MP_SMALL_INT_BITS]))
921925

922926
# MPY: n_qstr
923927
out.write_uint(1 + len(native_qstr_vals))

0 commit comments

Comments
 (0)
0