4141// The current version of .mpy files
4242#define MPY_VERSION (3)
4343
44- // The feature flags byte encodes the compile-time config options that
44+ // Macros to encode/decode flags to/from the feature byte
45+ #define MPY_FEATURE_ENCODE_FLAGS (flags ) (flags)
46+ #define MPY_FEATURE_DECODE_FLAGS (feat ) ((feat) & 3)
47+
48+ // The feature flag bits encode the compile-time config options that
4549// affect the generate bytecode.
4650#define MPY_FEATURE_FLAGS ( \
4751 ((MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) << 0) \
@@ -68,6 +72,57 @@ STATIC int mp_small_int_bits(void) {
6872}
6973#endif
7074
75+ #define QSTR_WINDOW_SIZE (32)
76+
77+ typedef struct _qstr_window_t {
78+ uint16_t idx ; // indexes the head of the window
79+ uint16_t window [QSTR_WINDOW_SIZE ];
80+ } qstr_window_t ;
81+
82+ // Push a qstr to the head of the window, and the tail qstr is overwritten
83+ STATIC void qstr_window_push (qstr_window_t * qw , qstr qst ) {
84+ qw -> idx = (qw -> idx + 1 ) % QSTR_WINDOW_SIZE ;
85+ qw -> window [qw -> idx ] = qst ;
86+ }
87+
88+ // Pull an existing qstr from within the window to the head of the window
89+ STATIC qstr qstr_window_pull (qstr_window_t * qw , size_t idx ) {
90+ qstr qst = qw -> window [idx ];
91+ if (idx > qw -> idx ) {
92+ memmove (& qw -> window [idx ], & qw -> window [idx + 1 ], (QSTR_WINDOW_SIZE - idx - 1 ) * sizeof (uint16_t ));
93+ qw -> window [QSTR_WINDOW_SIZE - 1 ] = qw -> window [0 ];
94+ idx = 0 ;
95+ }
96+ memmove (& qw -> window [idx ], & qw -> window [idx + 1 ], (qw -> idx - idx ) * sizeof (uint16_t ));
97+ qw -> window [qw -> idx ] = qst ;
98+ return qst ;
99+ }
100+
101+ #if MICROPY_PERSISTENT_CODE_LOAD
102+
103+ // Access a qstr at the given index, relative to the head of the window (0=head)
104+ STATIC qstr qstr_window_access (qstr_window_t * qw , size_t idx ) {
105+ return qstr_window_pull (qw , (qw -> idx + QSTR_WINDOW_SIZE - idx ) % QSTR_WINDOW_SIZE );
106+ }
107+
108+ #endif
109+
110+ #if MICROPY_PERSISTENT_CODE_SAVE
111+
112+ // Insert a qstr at the head of the window, either by pulling an existing one or pushing a new one
113+ STATIC size_t qstr_window_insert (qstr_window_t * qw , qstr qst ) {
114+ for (size_t idx = 0 ; idx < QSTR_WINDOW_SIZE ; ++ idx ) {
115+ if (qw -> window [idx ] == qst ) {
116+ qstr_window_pull (qw , idx );
117+ return (qw -> idx + QSTR_WINDOW_SIZE - idx ) % QSTR_WINDOW_SIZE ;
118+ }
119+ }
120+ qstr_window_push (qw , qst );
121+ return QSTR_WINDOW_SIZE ;
122+ }
123+
124+ #endif
125+
71126typedef struct _bytecode_prelude_t {
72127 uint n_state ;
73128 uint n_exc_stack ;
@@ -122,12 +177,18 @@ STATIC size_t read_uint(mp_reader_t *reader) {
122177 return unum ;
123178}
124179
125- STATIC qstr load_qstr (mp_reader_t * reader ) {
180+ STATIC qstr load_qstr (mp_reader_t * reader , qstr_window_t * qw ) {
126181 size_t len = read_uint (reader );
182+ if (len & 1 ) {
183+ // qstr in window
184+ return qstr_window_access (qw , len >> 1 );
185+ }
186+ len >>= 1 ;
127187 char * str = m_new (char , len );
128188 read_bytes (reader , (byte * )str , len );
129189 qstr qst = qstr_from_strn (str , len );
130190 m_del (char , str , len );
191+ qstr_window_push (qw , qst );
131192 return qst ;
132193}
133194
@@ -151,20 +212,20 @@ STATIC mp_obj_t load_obj(mp_reader_t *reader) {
151212 }
152213}
153214
154- STATIC void load_bytecode_qstrs (mp_reader_t * reader , byte * ip , byte * ip_top ) {
215+ STATIC void load_bytecode_qstrs (mp_reader_t * reader , qstr_window_t * qw , byte * ip , byte * ip_top ) {
155216 while (ip < ip_top ) {
156217 size_t sz ;
157218 uint f = mp_opcode_format (ip , & sz );
158219 if (f == MP_OPCODE_QSTR ) {
159- qstr qst = load_qstr (reader );
220+ qstr qst = load_qstr (reader , qw );
160221 ip [1 ] = qst ;
161222 ip [2 ] = qst >> 8 ;
162223 }
163224 ip += sz ;
164225 }
165226}
166227
167- STATIC mp_raw_code_t * load_raw_code (mp_reader_t * reader ) {
228+ STATIC mp_raw_code_t * load_raw_code (mp_reader_t * reader , qstr_window_t * qw ) {
168229 // load bytecode
169230 size_t bc_len = read_uint (reader );
170231 byte * bytecode = m_new (byte , bc_len );
@@ -177,25 +238,25 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader) {
177238 extract_prelude (& ip , & ip2 , & prelude );
178239
179240 // load qstrs and link global qstr ids into bytecode
180- qstr simple_name = load_qstr (reader );
181- qstr source_file = load_qstr (reader );
241+ qstr simple_name = load_qstr (reader , qw );
242+ qstr source_file = load_qstr (reader , qw );
182243 ((byte * )ip2 )[0 ] = simple_name ; ((byte * )ip2 )[1 ] = simple_name >> 8 ;
183244 ((byte * )ip2 )[2 ] = source_file ; ((byte * )ip2 )[3 ] = source_file >> 8 ;
184- load_bytecode_qstrs (reader , (byte * )ip , bytecode + bc_len );
245+ load_bytecode_qstrs (reader , qw , (byte * )ip , bytecode + bc_len );
185246
186247 // load constant table
187248 size_t n_obj = read_uint (reader );
188249 size_t n_raw_code = read_uint (reader );
189250 mp_uint_t * const_table = m_new (mp_uint_t , prelude .n_pos_args + prelude .n_kwonly_args + n_obj + n_raw_code );
190251 mp_uint_t * ct = const_table ;
191252 for (size_t i = 0 ; i < prelude .n_pos_args + prelude .n_kwonly_args ; ++ i ) {
192- * ct ++ = (mp_uint_t )MP_OBJ_NEW_QSTR (load_qstr (reader ));
253+ * ct ++ = (mp_uint_t )MP_OBJ_NEW_QSTR (load_qstr (reader , qw ));
193254 }
194255 for (size_t i = 0 ; i < n_obj ; ++ i ) {
195256 * ct ++ = (mp_uint_t )load_obj (reader );
196257 }
197258 for (size_t i = 0 ; i < n_raw_code ; ++ i ) {
198- * ct ++ = (mp_uint_t )(uintptr_t )load_raw_code (reader );
259+ * ct ++ = (mp_uint_t )(uintptr_t )load_raw_code (reader , qw );
199260 }
200261
201262 // create raw_code and return it
@@ -217,11 +278,14 @@ mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader) {
217278 read_bytes (reader , header , sizeof (header ));
218279 if (header [0 ] != 'M'
219280 || header [1 ] != MPY_VERSION
220- || header [2 ] != MPY_FEATURE_FLAGS
221- || header [3 ] > mp_small_int_bits ()) {
281+ || MPY_FEATURE_DECODE_FLAGS (header [2 ]) != MPY_FEATURE_FLAGS
282+ || header [3 ] > mp_small_int_bits ()
283+ || read_uint (reader , NULL ) > QSTR_WINDOW_SIZE ) {
222284 mp_raise_ValueError ("incompatible .mpy file" );
223285 }
224- mp_raw_code_t * rc = load_raw_code (reader );
286+ qstr_window_t qw ;
287+ qw .idx = 0 ;
288+ mp_raw_code_t * rc = load_raw_code (reader , & qw );
225289 reader -> close (reader -> data );
226290 return rc ;
227291}
@@ -264,10 +328,16 @@ STATIC void mp_print_uint(mp_print_t *print, size_t n) {
264328 print -> print_strn (print -> data , (char * )p , buf + sizeof (buf ) - p );
265329}
266330
267- STATIC void save_qstr (mp_print_t * print , qstr qst ) {
331+ STATIC void save_qstr (mp_print_t * print , qstr_window_t * qw , qstr qst ) {
332+ size_t idx = qstr_window_insert (qw , qst );
333+ if (idx < QSTR_WINDOW_SIZE ) {
334+ // qstr found in window, encode index to it
335+ mp_print_uint (print , idx << 1 | 1 );
336+ return ;
337+ }
268338 size_t len ;
269339 const byte * str = qstr_data (qst , & len );
270- mp_print_uint (print , len );
340+ mp_print_uint (print , len << 1 );
271341 mp_print_bytes (print , str , len );
272342}
273343
@@ -312,19 +382,19 @@ STATIC void save_obj(mp_print_t *print, mp_obj_t o) {
312382 }
313383}
314384
315- STATIC void save_bytecode_qstrs (mp_print_t * print , const byte * ip , const byte * ip_top ) {
385+ STATIC void save_bytecode_qstrs (mp_print_t * print , qstr_window_t * qw , const byte * ip , const byte * ip_top ) {
316386 while (ip < ip_top ) {
317387 size_t sz ;
318388 uint f = mp_opcode_format (ip , & sz );
319389 if (f == MP_OPCODE_QSTR ) {
320390 qstr qst = ip [1 ] | (ip [2 ] << 8 );
321- save_qstr (print , qst );
391+ save_qstr (print , qw , qst );
322392 }
323393 ip += sz ;
324394 }
325395}
326396
327- STATIC void save_raw_code (mp_print_t * print , mp_raw_code_t * rc ) {
397+ STATIC void save_raw_code (mp_print_t * print , mp_raw_code_t * rc , qstr_window_t * qstr_window ) {
328398 if (rc -> kind != MP_CODE_BYTECODE ) {
329399 mp_raise_ValueError ("can only save bytecode" );
330400 }
@@ -340,23 +410,23 @@ STATIC void save_raw_code(mp_print_t *print, mp_raw_code_t *rc) {
340410 extract_prelude (& ip , & ip2 , & prelude );
341411
342412 // save qstrs
343- save_qstr (print , ip2 [0 ] | (ip2 [1 ] << 8 )); // simple_name
344- save_qstr (print , ip2 [2 ] | (ip2 [3 ] << 8 )); // source_file
345- save_bytecode_qstrs (print , ip , rc -> data .u_byte .bytecode + rc -> data .u_byte .bc_len );
413+ save_qstr (print , qstr_window , ip2 [0 ] | (ip2 [1 ] << 8 )); // simple_name
414+ save_qstr (print , qstr_window , ip2 [2 ] | (ip2 [3 ] << 8 )); // source_file
415+ save_bytecode_qstrs (print , qstr_window , ip , rc -> data .u_byte .bytecode + rc -> data .u_byte .bc_len );
346416
347417 // save constant table
348418 mp_print_uint (print , rc -> data .u_byte .n_obj );
349419 mp_print_uint (print , rc -> data .u_byte .n_raw_code );
350420 const mp_uint_t * const_table = rc -> data .u_byte .const_table ;
351421 for (uint i = 0 ; i < prelude .n_pos_args + prelude .n_kwonly_args ; ++ i ) {
352422 mp_obj_t o = (mp_obj_t )* const_table ++ ;
353- save_qstr (print , MP_OBJ_QSTR_VALUE (o ));
423+ save_qstr (print , qstr_window , MP_OBJ_QSTR_VALUE (o ));
354424 }
355425 for (uint i = 0 ; i < rc -> data .u_byte .n_obj ; ++ i ) {
356426 save_obj (print , (mp_obj_t )* const_table ++ );
357427 }
358428 for (uint i = 0 ; i < rc -> data .u_byte .n_raw_code ; ++ i ) {
359- save_raw_code (print , (mp_raw_code_t * )(uintptr_t )* const_table ++ );
429+ save_raw_code (print , (mp_raw_code_t * )(uintptr_t )* const_table ++ , qstr_window );
360430 }
361431}
362432
@@ -366,16 +436,24 @@ void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print) {
366436 // byte version
367437 // byte feature flags
368438 // byte number of bits in a small int
369- byte header [4 ] = {'M' , MPY_VERSION , MPY_FEATURE_FLAGS_DYNAMIC ,
439+ // uint size of qstr window
440+ byte header [4 ] = {
441+ 'M' ,
442+ MPY_VERSION ,
443+ MPY_FEATURE_ENCODE_FLAGS (MPY_FEATURE_FLAGS_DYNAMIC ),
370444 #if MICROPY_DYNAMIC_COMPILER
371445 mp_dynamic_compiler .small_int_bits ,
372446 #else
373447 mp_small_int_bits (),
374448 #endif
375449 };
376450 mp_print_bytes (print , header , sizeof (header ));
451+ mp_print_uint (print , QSTR_WINDOW_SIZE );
377452
378- save_raw_code (print , rc );
453+ qstr_window_t qw ;
454+ qw .idx = 0 ;
455+ memset (qw .window , 0 , sizeof (qw .window ));
456+ save_raw_code (print , rc , & qw );
379457}
380458
381459// here we define mp_raw_code_save_file depending on the port
0 commit comments