blob: a5112de9a1b9a64779ccc956315cb070be643e06 [file] [log] [blame]
Damien George0699c6b2016-01-31 21:45:22 +00001#!/usr/bin/env python3
2#
3# This file is part of the MicroPython project, http://micropython.org/
4#
5# The MIT License (MIT)
6#
Damien Georgefaf3d3e2019-06-04 22:13:32 +10007# Copyright (c) 2016-2019 Damien P. George
Damien George0699c6b2016-01-31 21:45:22 +00008#
9# Permission is hereby granted, free of charge, to any person obtaining a copy
10# of this software and associated documentation files (the "Software"), to deal
11# in the Software without restriction, including without limitation the rights
12# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13# copies of the Software, and to permit persons to whom the Software is
14# furnished to do so, subject to the following conditions:
15#
16# The above copyright notice and this permission notice shall be included in
17# all copies or substantial portions of the Software.
18#
19# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25# THE SOFTWARE.
26
Damien Georgec3beb162016-04-15 11:56:10 +010027# Python 2/3 compatibility code
28from __future__ import print_function
29import platform
Damien George69661f32020-02-27 15:36:53 +110030
31if platform.python_version_tuple()[0] == "2":
Damien Georgef2040bf2021-10-22 22:22:47 +110032 from binascii import hexlify as hexlify_py2
33
34 str_cons = lambda val, enc=None: str(val)
Damien Georgec3beb162016-04-15 11:56:10 +010035 bytes_cons = lambda val, enc=None: bytearray(val)
Angus Gratton597fcb42023-08-09 17:41:54 +100036 is_str_type = lambda o: isinstance(o, str)
Damien Georgec3beb162016-04-15 11:56:10 +010037 is_bytes_type = lambda o: type(o) is bytearray
Angus Gratton597fcb42023-08-09 17:41:54 +100038 is_int_type = lambda o: isinstance(o, int) or isinstance(o, long)
Damien Georgef2040bf2021-10-22 22:22:47 +110039
40 def hexlify_to_str(b):
41 x = hexlify_py2(b)
42 return ":".join(x[i : i + 2] for i in range(0, len(x), 2))
43
Damien Georgec3beb162016-04-15 11:56:10 +010044else:
Damien Georgef2040bf2021-10-22 22:22:47 +110045 from binascii import hexlify
46
Damien Georgec3beb162016-04-15 11:56:10 +010047 str_cons = str
48 bytes_cons = bytes
Angus Gratton597fcb42023-08-09 17:41:54 +100049 is_str_type = lambda o: isinstance(o, str)
50 is_bytes_type = lambda o: isinstance(o, bytes)
51 is_int_type = lambda o: isinstance(o, int)
Damien Georgef2040bf2021-10-22 22:22:47 +110052
53 def hexlify_to_str(b):
54 return str(hexlify(b, ":"), "ascii")
55
56
Damien Georgec3beb162016-04-15 11:56:10 +010057# end compatibility code
58
Damien George0699c6b2016-01-31 21:45:22 +000059import sys
Damien George72ae3c72016-08-10 13:26:11 +100060import struct
Damien George0699c6b2016-01-31 21:45:22 +000061
Damien George69661f32020-02-27 15:36:53 +110062sys.path.append(sys.path[0] + "/../py")
Damien George0699c6b2016-01-31 21:45:22 +000063import makeqstrdata as qstrutil
64
Damien George07f52602022-04-08 14:09:08 +100065# Threshold of str length below which it will be turned into a qstr when freezing.
66# This helps to reduce frozen code size because qstrs are more efficient to encode
67# as objects than full mp_obj_str_t instances.
68PERSISTENT_STR_INTERN_THRESHOLD = 25
69
Damien George69661f32020-02-27 15:36:53 +110070
Damien Georgef2040bf2021-10-22 22:22:47 +110071class MPYReadError(Exception):
72 def __init__(self, filename, msg):
73 self.filename = filename
74 self.msg = msg
75
76 def __str__(self):
77 return "%s: %s" % (self.filename, self.msg)
78
79
Damien George0699c6b2016-01-31 21:45:22 +000080class FreezeError(Exception):
81 def __init__(self, rawcode, msg):
82 self.rawcode = rawcode
83 self.msg = msg
84
85 def __str__(self):
Damien George69661f32020-02-27 15:36:53 +110086 return "error while freezing %s: %s" % (self.rawcode.source_file, self.msg)
87
Damien George0699c6b2016-01-31 21:45:22 +000088
89class Config:
Damien Georgef2040bf2021-10-22 22:22:47 +110090 MPY_VERSION = 6
Jim Mussaredd94141e2022-09-17 23:57:12 +100091 MPY_SUB_VERSION = 1
Damien George0699c6b2016-01-31 21:45:22 +000092 MICROPY_LONGINT_IMPL_NONE = 0
93 MICROPY_LONGINT_IMPL_LONGLONG = 1
94 MICROPY_LONGINT_IMPL_MPZ = 2
Damien George69661f32020-02-27 15:36:53 +110095
96
Damien George0699c6b2016-01-31 21:45:22 +000097config = Config()
98
Damien George69661f32020-02-27 15:36:53 +110099
Damien Georgeea3c80a2019-02-21 15:18:59 +1100100MP_CODE_BYTECODE = 2
101MP_CODE_NATIVE_PY = 3
102MP_CODE_NATIVE_VIPER = 4
103MP_CODE_NATIVE_ASM = 5
104
105MP_NATIVE_ARCH_NONE = 0
106MP_NATIVE_ARCH_X86 = 1
107MP_NATIVE_ARCH_X64 = 2
108MP_NATIVE_ARCH_ARMV6 = 3
109MP_NATIVE_ARCH_ARMV6M = 4
110MP_NATIVE_ARCH_ARMV7M = 5
111MP_NATIVE_ARCH_ARMV7EM = 6
112MP_NATIVE_ARCH_ARMV7EMSP = 7
113MP_NATIVE_ARCH_ARMV7EMDP = 8
114MP_NATIVE_ARCH_XTENSA = 9
Damien George9adedce2019-09-13 13:15:12 +1000115MP_NATIVE_ARCH_XTENSAWIN = 10
Damien Georgeea3c80a2019-02-21 15:18:59 +1100116
Damien George42d0bd22022-04-07 22:18:37 +1000117MP_PERSISTENT_OBJ_FUN_TABLE = 0
Damien George2a075cc2022-03-31 15:26:14 +1100118MP_PERSISTENT_OBJ_NONE = 1
119MP_PERSISTENT_OBJ_FALSE = 2
120MP_PERSISTENT_OBJ_TRUE = 3
121MP_PERSISTENT_OBJ_ELLIPSIS = 4
122MP_PERSISTENT_OBJ_STR = 5
123MP_PERSISTENT_OBJ_BYTES = 6
124MP_PERSISTENT_OBJ_INT = 7
125MP_PERSISTENT_OBJ_FLOAT = 8
126MP_PERSISTENT_OBJ_COMPLEX = 9
127MP_PERSISTENT_OBJ_TUPLE = 10
Damien George42d0bd22022-04-07 22:18:37 +1000128
Damien Georgef2040bf2021-10-22 22:22:47 +1100129MP_SCOPE_FLAG_VIPERRELOC = 0x10
130MP_SCOPE_FLAG_VIPERRODATA = 0x20
131MP_SCOPE_FLAG_VIPERBSS = 0x40
132
Damien George69661f32020-02-27 15:36:53 +1100133MP_BC_MASK_EXTRA_BYTE = 0x9E
Damien George0699c6b2016-01-31 21:45:22 +0000134
Damien George1f7202d2019-09-02 21:35:26 +1000135MP_BC_FORMAT_BYTE = 0
136MP_BC_FORMAT_QSTR = 1
137MP_BC_FORMAT_VAR_UINT = 2
138MP_BC_FORMAT_OFFSET = 3
139
Damien Georgef2040bf2021-10-22 22:22:47 +1100140mp_unary_op_method_name = (
141 "__pos__",
142 "__neg__",
143 "__invert__",
144 "<not>",
145)
146
147mp_binary_op_method_name = (
148 "__lt__",
149 "__gt__",
150 "__eq__",
151 "__le__",
152 "__ge__",
153 "__ne__",
154 "<in>",
155 "<is>",
156 "<exception match>",
157 "__ior__",
158 "__ixor__",
159 "__iand__",
160 "__ilshift__",
161 "__irshift__",
162 "__iadd__",
163 "__isub__",
164 "__imul__",
165 "__imatmul__",
166 "__ifloordiv__",
167 "__itruediv__",
168 "__imod__",
169 "__ipow__",
170 "__or__",
171 "__xor__",
172 "__and__",
173 "__lshift__",
174 "__rshift__",
175 "__add__",
176 "__sub__",
177 "__mul__",
178 "__matmul__",
179 "__floordiv__",
180 "__truediv__",
181 "__mod__",
182 "__pow__",
183)
184
185
Damien George599a22e2022-05-31 00:17:38 +1000186class Opcode:
Damien Georgef2040bf2021-10-22 22:22:47 +1100187 # fmt: off
188 # Load, Store, Delete, Import, Make, Build, Unpack, Call, Jump, Exception, For, sTack, Return, Yield, Op
189 MP_BC_BASE_RESERVED = (0x00) # ----------------
190 MP_BC_BASE_QSTR_O = (0x10) # LLLLLLSSSDDII---
191 MP_BC_BASE_VINT_E = (0x20) # MMLLLLSSDDBBBBBB
192 MP_BC_BASE_VINT_O = (0x30) # UUMMCCCC--------
193 MP_BC_BASE_JUMP_E = (0x40) # J-JJJJJEEEEF----
194 MP_BC_BASE_BYTE_O = (0x50) # LLLLSSDTTTTTEEFF
195 MP_BC_BASE_BYTE_E = (0x60) # --BREEEYYI------
196 MP_BC_LOAD_CONST_SMALL_INT_MULTI = (0x70) # LLLLLLLLLLLLLLLL
197 # = (0x80) # LLLLLLLLLLLLLLLL
198 # = (0x90) # LLLLLLLLLLLLLLLL
199 # = (0xa0) # LLLLLLLLLLLLLLLL
200 MP_BC_LOAD_FAST_MULTI = (0xb0) # LLLLLLLLLLLLLLLL
201 MP_BC_STORE_FAST_MULTI = (0xc0) # SSSSSSSSSSSSSSSS
202 MP_BC_UNARY_OP_MULTI = (0xd0) # OOOOOOO
203 MP_BC_BINARY_OP_MULTI = (0xd7) # OOOOOOOOO
204 # = (0xe0) # OOOOOOOOOOOOOOOO
205 # = (0xf0) # OOOOOOOOOO------
206
207 MP_BC_LOAD_CONST_SMALL_INT_MULTI_NUM = 64
208 MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS = 16
209 MP_BC_LOAD_FAST_MULTI_NUM = 16
210 MP_BC_STORE_FAST_MULTI_NUM = 16
211 MP_BC_UNARY_OP_MULTI_NUM = 4 # MP_UNARY_OP_NUM_BYTECODE
212 MP_BC_BINARY_OP_MULTI_NUM = 35 # MP_BINARY_OP_NUM_BYTECODE
213
214 MP_BC_LOAD_CONST_FALSE = (MP_BC_BASE_BYTE_O + 0x00)
215 MP_BC_LOAD_CONST_NONE = (MP_BC_BASE_BYTE_O + 0x01)
216 MP_BC_LOAD_CONST_TRUE = (MP_BC_BASE_BYTE_O + 0x02)
217 MP_BC_LOAD_CONST_SMALL_INT = (MP_BC_BASE_VINT_E + 0x02) # signed var-int
218 MP_BC_LOAD_CONST_STRING = (MP_BC_BASE_QSTR_O + 0x00) # qstr
219 MP_BC_LOAD_CONST_OBJ = (MP_BC_BASE_VINT_E + 0x03) # ptr
220 MP_BC_LOAD_NULL = (MP_BC_BASE_BYTE_O + 0x03)
221
222 MP_BC_LOAD_FAST_N = (MP_BC_BASE_VINT_E + 0x04) # uint
223 MP_BC_LOAD_DEREF = (MP_BC_BASE_VINT_E + 0x05) # uint
224 MP_BC_LOAD_NAME = (MP_BC_BASE_QSTR_O + 0x01) # qstr
225 MP_BC_LOAD_GLOBAL = (MP_BC_BASE_QSTR_O + 0x02) # qstr
226 MP_BC_LOAD_ATTR = (MP_BC_BASE_QSTR_O + 0x03) # qstr
227 MP_BC_LOAD_METHOD = (MP_BC_BASE_QSTR_O + 0x04) # qstr
228 MP_BC_LOAD_SUPER_METHOD = (MP_BC_BASE_QSTR_O + 0x05) # qstr
229 MP_BC_LOAD_BUILD_CLASS = (MP_BC_BASE_BYTE_O + 0x04)
230 MP_BC_LOAD_SUBSCR = (MP_BC_BASE_BYTE_O + 0x05)
231
232 MP_BC_STORE_FAST_N = (MP_BC_BASE_VINT_E + 0x06) # uint
233 MP_BC_STORE_DEREF = (MP_BC_BASE_VINT_E + 0x07) # uint
234 MP_BC_STORE_NAME = (MP_BC_BASE_QSTR_O + 0x06) # qstr
235 MP_BC_STORE_GLOBAL = (MP_BC_BASE_QSTR_O + 0x07) # qstr
236 MP_BC_STORE_ATTR = (MP_BC_BASE_QSTR_O + 0x08) # qstr
237 MP_BC_STORE_SUBSCR = (MP_BC_BASE_BYTE_O + 0x06)
238
239 MP_BC_DELETE_FAST = (MP_BC_BASE_VINT_E + 0x08) # uint
240 MP_BC_DELETE_DEREF = (MP_BC_BASE_VINT_E + 0x09) # uint
241 MP_BC_DELETE_NAME = (MP_BC_BASE_QSTR_O + 0x09) # qstr
242 MP_BC_DELETE_GLOBAL = (MP_BC_BASE_QSTR_O + 0x0a) # qstr
243
244 MP_BC_DUP_TOP = (MP_BC_BASE_BYTE_O + 0x07)
245 MP_BC_DUP_TOP_TWO = (MP_BC_BASE_BYTE_O + 0x08)
246 MP_BC_POP_TOP = (MP_BC_BASE_BYTE_O + 0x09)
247 MP_BC_ROT_TWO = (MP_BC_BASE_BYTE_O + 0x0a)
248 MP_BC_ROT_THREE = (MP_BC_BASE_BYTE_O + 0x0b)
249
Damien George538c3c02022-03-16 09:37:58 +1100250 MP_BC_UNWIND_JUMP = (MP_BC_BASE_JUMP_E + 0x00) # signed relative bytecode offset; then a byte
251 MP_BC_JUMP = (MP_BC_BASE_JUMP_E + 0x02) # signed relative bytecode offset
252 MP_BC_POP_JUMP_IF_TRUE = (MP_BC_BASE_JUMP_E + 0x03) # signed relative bytecode offset
253 MP_BC_POP_JUMP_IF_FALSE = (MP_BC_BASE_JUMP_E + 0x04) # signed relative bytecode offset
Damien George6d11c692022-03-21 16:36:13 +1100254 MP_BC_JUMP_IF_TRUE_OR_POP = (MP_BC_BASE_JUMP_E + 0x05) # unsigned relative bytecode offset
255 MP_BC_JUMP_IF_FALSE_OR_POP = (MP_BC_BASE_JUMP_E + 0x06) # unsigned relative bytecode offset
Damien George538c3c02022-03-16 09:37:58 +1100256 MP_BC_SETUP_WITH = (MP_BC_BASE_JUMP_E + 0x07) # unsigned relative bytecode offset
257 MP_BC_SETUP_EXCEPT = (MP_BC_BASE_JUMP_E + 0x08) # unsigned relative bytecode offset
258 MP_BC_SETUP_FINALLY = (MP_BC_BASE_JUMP_E + 0x09) # unsigned relative bytecode offset
259 MP_BC_POP_EXCEPT_JUMP = (MP_BC_BASE_JUMP_E + 0x0a) # unsigned relative bytecode offset
260 MP_BC_FOR_ITER = (MP_BC_BASE_JUMP_E + 0x0b) # unsigned relative bytecode offset
Damien Georgef2040bf2021-10-22 22:22:47 +1100261 MP_BC_WITH_CLEANUP = (MP_BC_BASE_BYTE_O + 0x0c)
262 MP_BC_END_FINALLY = (MP_BC_BASE_BYTE_O + 0x0d)
263 MP_BC_GET_ITER = (MP_BC_BASE_BYTE_O + 0x0e)
264 MP_BC_GET_ITER_STACK = (MP_BC_BASE_BYTE_O + 0x0f)
265
266 MP_BC_BUILD_TUPLE = (MP_BC_BASE_VINT_E + 0x0a) # uint
267 MP_BC_BUILD_LIST = (MP_BC_BASE_VINT_E + 0x0b) # uint
268 MP_BC_BUILD_MAP = (MP_BC_BASE_VINT_E + 0x0c) # uint
269 MP_BC_STORE_MAP = (MP_BC_BASE_BYTE_E + 0x02)
270 MP_BC_BUILD_SET = (MP_BC_BASE_VINT_E + 0x0d) # uint
271 MP_BC_BUILD_SLICE = (MP_BC_BASE_VINT_E + 0x0e) # uint
272 MP_BC_STORE_COMP = (MP_BC_BASE_VINT_E + 0x0f) # uint
273 MP_BC_UNPACK_SEQUENCE = (MP_BC_BASE_VINT_O + 0x00) # uint
274 MP_BC_UNPACK_EX = (MP_BC_BASE_VINT_O + 0x01) # uint
275
276 MP_BC_RETURN_VALUE = (MP_BC_BASE_BYTE_E + 0x03)
277 MP_BC_RAISE_LAST = (MP_BC_BASE_BYTE_E + 0x04)
278 MP_BC_RAISE_OBJ = (MP_BC_BASE_BYTE_E + 0x05)
279 MP_BC_RAISE_FROM = (MP_BC_BASE_BYTE_E + 0x06)
280 MP_BC_YIELD_VALUE = (MP_BC_BASE_BYTE_E + 0x07)
281 MP_BC_YIELD_FROM = (MP_BC_BASE_BYTE_E + 0x08)
282
283 MP_BC_MAKE_FUNCTION = (MP_BC_BASE_VINT_O + 0x02) # uint
284 MP_BC_MAKE_FUNCTION_DEFARGS = (MP_BC_BASE_VINT_O + 0x03) # uint
285 MP_BC_MAKE_CLOSURE = (MP_BC_BASE_VINT_E + 0x00) # uint; extra byte
286 MP_BC_MAKE_CLOSURE_DEFARGS = (MP_BC_BASE_VINT_E + 0x01) # uint; extra byte
287 MP_BC_CALL_FUNCTION = (MP_BC_BASE_VINT_O + 0x04) # uint
288 MP_BC_CALL_FUNCTION_VAR_KW = (MP_BC_BASE_VINT_O + 0x05) # uint
289 MP_BC_CALL_METHOD = (MP_BC_BASE_VINT_O + 0x06) # uint
290 MP_BC_CALL_METHOD_VAR_KW = (MP_BC_BASE_VINT_O + 0x07) # uint
291
292 MP_BC_IMPORT_NAME = (MP_BC_BASE_QSTR_O + 0x0b) # qstr
293 MP_BC_IMPORT_FROM = (MP_BC_BASE_QSTR_O + 0x0c) # qstr
294 MP_BC_IMPORT_STAR = (MP_BC_BASE_BYTE_E + 0x09)
295 # fmt: on
296
Damien George538c3c02022-03-16 09:37:58 +1100297 # Create sets of related opcodes.
298 ALL_OFFSET_SIGNED = (
299 MP_BC_UNWIND_JUMP,
300 MP_BC_JUMP,
301 MP_BC_POP_JUMP_IF_TRUE,
302 MP_BC_POP_JUMP_IF_FALSE,
Damien George538c3c02022-03-16 09:37:58 +1100303 )
304
Damien Georgef2040bf2021-10-22 22:22:47 +1100305 # Create a dict mapping opcode value to opcode name.
306 mapping = ["unknown" for _ in range(256)]
307 for op_name in list(locals()):
308 if op_name.startswith("MP_BC_"):
309 mapping[locals()[op_name]] = op_name[len("MP_BC_") :]
310 for i in range(MP_BC_LOAD_CONST_SMALL_INT_MULTI_NUM):
311 name = "LOAD_CONST_SMALL_INT %d" % (i - MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS)
312 mapping[MP_BC_LOAD_CONST_SMALL_INT_MULTI + i] = name
313 for i in range(MP_BC_LOAD_FAST_MULTI_NUM):
314 mapping[MP_BC_LOAD_FAST_MULTI + i] = "LOAD_FAST %d" % i
315 for i in range(MP_BC_STORE_FAST_MULTI_NUM):
316 mapping[MP_BC_STORE_FAST_MULTI + i] = "STORE_FAST %d" % i
317 for i in range(MP_BC_UNARY_OP_MULTI_NUM):
318 mapping[MP_BC_UNARY_OP_MULTI + i] = "UNARY_OP %d %s" % (i, mp_unary_op_method_name[i])
319 for i in range(MP_BC_BINARY_OP_MULTI_NUM):
320 mapping[MP_BC_BINARY_OP_MULTI + i] = "BINARY_OP %d %s" % (i, mp_binary_op_method_name[i])
321
Damien George599a22e2022-05-31 00:17:38 +1000322 def __init__(self, offset, fmt, opcode_byte, arg, extra_arg):
323 self.offset = offset
324 self.fmt = fmt
325 self.opcode_byte = opcode_byte
326 self.arg = arg
327 self.extra_arg = extra_arg
328
Damien George0699c6b2016-01-31 21:45:22 +0000329
Damien George9c8a5632022-04-07 23:53:37 +1000330# This definition of a small int covers all possible targets, in the sense that every
331# target can encode as a small int, an integer that passes this test. The minimum is set
332# by MICROPY_OBJ_REPR_B on a 16-bit machine, where there are 14 bits for the small int.
333def mp_small_int_fits(i):
334 return -0x2000 <= i <= 0x1FFF
335
336
Damien George599a22e2022-05-31 00:17:38 +1000337def mp_encode_uint(val, signed=False):
338 encoded = bytearray([val & 0x7F])
339 val >>= 7
340 while val != 0 and val != -1:
341 encoded.insert(0, 0x80 | (val & 0x7F))
342 val >>= 7
343 if signed:
344 if val == -1 and encoded[0] & 0x40 == 0:
345 encoded.insert(0, 0xFF)
346 elif val == 0 and encoded[0] & 0x40 != 0:
347 encoded.insert(0, 0x80)
348 return encoded
349
350
Damien Georgef2040bf2021-10-22 22:22:47 +1100351def mp_opcode_decode(bytecode, ip):
352 opcode = bytecode[ip]
353 ip_start = ip
354 f = (0x000003A4 >> (2 * ((opcode) >> 4))) & 3
Damien Georgef2040bf2021-10-22 22:22:47 +1100355 ip += 1
Damien George599a22e2022-05-31 00:17:38 +1000356 arg = None
357 extra_arg = None
Damien Georgef2040bf2021-10-22 22:22:47 +1100358 if f in (MP_BC_FORMAT_QSTR, MP_BC_FORMAT_VAR_UINT):
359 arg = bytecode[ip] & 0x7F
Damien George599a22e2022-05-31 00:17:38 +1000360 if opcode == Opcode.MP_BC_LOAD_CONST_SMALL_INT and arg & 0x40 != 0:
361 arg |= -1 << 7
Damien Georgef2040bf2021-10-22 22:22:47 +1100362 while bytecode[ip] & 0x80 != 0:
363 ip += 1
364 arg = arg << 7 | bytecode[ip] & 0x7F
365 ip += 1
366 elif f == MP_BC_FORMAT_OFFSET:
Damien George538c3c02022-03-16 09:37:58 +1100367 if bytecode[ip] & 0x80 == 0:
368 arg = bytecode[ip]
369 ip += 1
Damien George599a22e2022-05-31 00:17:38 +1000370 if opcode in Opcode.ALL_OFFSET_SIGNED:
Damien George538c3c02022-03-16 09:37:58 +1100371 arg -= 0x40
372 else:
373 arg = bytecode[ip] & 0x7F | bytecode[ip + 1] << 7
374 ip += 2
Damien George599a22e2022-05-31 00:17:38 +1000375 if opcode in Opcode.ALL_OFFSET_SIGNED:
Damien George538c3c02022-03-16 09:37:58 +1100376 arg -= 0x4000
Damien George599a22e2022-05-31 00:17:38 +1000377 if opcode & MP_BC_MASK_EXTRA_BYTE == 0:
378 extra_arg = bytecode[ip]
379 ip += 1
380 return f, ip - ip_start, arg, extra_arg
381
382
383def mp_opcode_encode(opcode):
384 overflow = False
385 encoded = bytearray([opcode.opcode_byte])
386 if opcode.fmt in (MP_BC_FORMAT_QSTR, MP_BC_FORMAT_VAR_UINT):
387 signed = opcode.opcode_byte == Opcode.MP_BC_LOAD_CONST_SMALL_INT
388 encoded.extend(mp_encode_uint(opcode.arg, signed))
389 elif opcode.fmt == MP_BC_FORMAT_OFFSET:
390 is_signed = opcode.opcode_byte in Opcode.ALL_OFFSET_SIGNED
391
392 # The -2 accounts for this jump opcode taking 2 bytes (at least).
393 bytecode_offset = opcode.target.offset - opcode.offset - 2
394
395 # Check if the bytecode_offset is small enough to use a 1-byte encoding.
396 if (is_signed and -64 <= bytecode_offset <= 63) or (
397 not is_signed and bytecode_offset <= 127
398 ):
399 # Use a 1-byte jump offset.
400 if is_signed:
401 bytecode_offset += 0x40
402 overflow = not (0 <= bytecode_offset <= 0x7F)
403 encoded.append(bytecode_offset & 0x7F)
404 else:
405 bytecode_offset -= 1
406 if is_signed:
407 bytecode_offset += 0x4000
408 overflow = not (0 <= bytecode_offset <= 0x7FFF)
409 encoded.append(0x80 | (bytecode_offset & 0x7F))
410 encoded.append((bytecode_offset >> 7) & 0xFF)
411 if opcode.extra_arg is not None:
412 encoded.append(opcode.extra_arg)
413 return overflow, encoded
Damien Georgef2040bf2021-10-22 22:22:47 +1100414
415
Damien Georgeb5ebfad2019-09-16 22:12:59 +1000416def read_prelude_sig(read_byte):
417 z = read_byte()
418 # xSSSSEAA
Damien George69661f32020-02-27 15:36:53 +1100419 S = (z >> 3) & 0xF
Damien Georgeb5ebfad2019-09-16 22:12:59 +1000420 E = (z >> 2) & 0x1
421 F = 0
422 A = z & 0x3
423 K = 0
424 D = 0
425 n = 0
426 while z & 0x80:
427 z = read_byte()
428 # xFSSKAED
429 S |= (z & 0x30) << (2 * n)
430 E |= (z & 0x02) << n
431 F |= ((z & 0x40) >> 6) << n
432 A |= (z & 0x4) << n
433 K |= ((z & 0x08) >> 3) << n
434 D |= (z & 0x1) << n
435 n += 1
436 S += 1
437 return S, E, F, A, K, D
438
Damien George69661f32020-02-27 15:36:53 +1100439
Damien Georgec8c0fd42019-09-25 15:45:47 +1000440def read_prelude_size(read_byte):
441 I = 0
442 C = 0
443 n = 0
444 while True:
445 z = read_byte()
446 # xIIIIIIC
Damien George69661f32020-02-27 15:36:53 +1100447 I |= ((z & 0x7E) >> 1) << (6 * n)
Damien Georgec8c0fd42019-09-25 15:45:47 +1000448 C |= (z & 1) << n
449 if not (z & 0x80):
450 break
451 n += 1
452 return I, C
453
Damien George69661f32020-02-27 15:36:53 +1100454
Damien George599a22e2022-05-31 00:17:38 +1000455# See py/bc.h:MP_BC_PRELUDE_SIZE_ENCODE macro.
456def encode_prelude_size(I, C):
457 # Encode bit-wise as: xIIIIIIC
458 encoded = bytearray()
459 while True:
460 z = (I & 0x3F) << 1 | (C & 1)
461 C >>= 1
462 I >>= 6
463 if C | I:
464 z |= 0x80
465 encoded.append(z)
466 if not C | I:
467 return encoded
468
469
Damien Georgeea3c80a2019-02-21 15:18:59 +1100470def extract_prelude(bytecode, ip):
Damien Georgeb5ebfad2019-09-16 22:12:59 +1000471 def local_read_byte():
472 b = bytecode[ip_ref[0]]
473 ip_ref[0] += 1
474 return b
Damien George69661f32020-02-27 15:36:53 +1100475
476 ip_ref = [ip] # to close over ip in Python 2 and 3
Damien George599a22e2022-05-31 00:17:38 +1000477
478 # Read prelude signature.
Damien George69661f32020-02-27 15:36:53 +1100479 (
480 n_state,
481 n_exc_stack,
482 scope_flags,
483 n_pos_args,
484 n_kwonly_args,
485 n_def_pos_args,
486 ) = read_prelude_sig(local_read_byte)
Damien Georgef2040bf2021-10-22 22:22:47 +1100487
Damien George599a22e2022-05-31 00:17:38 +1000488 offset_prelude_size = ip_ref[0]
Damien Georgeb5ebfad2019-09-16 22:12:59 +1000489
Damien George599a22e2022-05-31 00:17:38 +1000490 # Read prelude size.
491 n_info, n_cell = read_prelude_size(local_read_byte)
492
493 offset_source_info = ip_ref[0]
Damien Georgef2040bf2021-10-22 22:22:47 +1100494
495 # Extract simple_name and argument qstrs (var uints).
496 args = []
497 for arg_num in range(1 + n_pos_args + n_kwonly_args):
498 value = 0
499 while True:
500 b = local_read_byte()
501 value = (value << 7) | (b & 0x7F)
502 if b & 0x80 == 0:
503 break
504 args.append(value)
505
Damien George599a22e2022-05-31 00:17:38 +1000506 offset_line_info = ip_ref[0]
507 offset_closure_info = offset_source_info + n_info
508 offset_opcodes = offset_source_info + n_info + n_cell
509
Damien Georgef2040bf2021-10-22 22:22:47 +1100510 return (
Damien George599a22e2022-05-31 00:17:38 +1000511 offset_prelude_size,
512 offset_source_info,
513 offset_line_info,
514 offset_closure_info,
515 offset_opcodes,
Damien Georgef2040bf2021-10-22 22:22:47 +1100516 (n_state, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args),
Damien George599a22e2022-05-31 00:17:38 +1000517 (n_info, n_cell),
Damien Georgef2040bf2021-10-22 22:22:47 +1100518 args,
519 )
Damien George0699c6b2016-01-31 21:45:22 +0000520
Damien George69661f32020-02-27 15:36:53 +1100521
Damien Georgee6479662022-04-08 14:04:21 +1000522class QStrType:
523 def __init__(self, str):
524 self.str = str
525 self.qstr_esc = qstrutil.qstr_escape(self.str)
526 self.qstr_id = "MP_QSTR_" + self.qstr_esc
527
528
529class GlobalQStrList:
530 def __init__(self):
531 # Initialise global list of qstrs with static qstrs
532 self.qstrs = [None] # MP_QSTRnull should never be referenced
533 for n in qstrutil.static_qstr_list:
534 self.qstrs.append(QStrType(n))
535
536 def add(self, s):
537 q = QStrType(s)
538 self.qstrs.append(q)
539 return q
540
541 def get_by_index(self, i):
542 return self.qstrs[i]
543
Damien George40d431d2022-04-08 14:05:23 +1000544 def find_by_str(self, s):
545 for q in self.qstrs:
546 if q is not None and q.str == s:
547 return q
548 return None
549
Damien Georgee6479662022-04-08 14:04:21 +1000550
Damien Georgeea3c80a2019-02-21 15:18:59 +1100551class MPFunTable:
Damien Georgef2040bf2021-10-22 22:22:47 +1100552 def __repr__(self):
553 return "mp_fun_table"
Damien Georgeea3c80a2019-02-21 15:18:59 +1100554
Damien George69661f32020-02-27 15:36:53 +1100555
Damien Georgef2040bf2021-10-22 22:22:47 +1100556class CompiledModule:
557 def __init__(
558 self,
559 mpy_source_file,
560 mpy_segments,
561 header,
562 qstr_table,
563 obj_table,
564 raw_code,
Damien George599a22e2022-05-31 00:17:38 +1000565 qstr_table_file_offset,
566 obj_table_file_offset,
Damien Georgef2040bf2021-10-22 22:22:47 +1100567 raw_code_file_offset,
568 escaped_name,
569 ):
570 self.mpy_source_file = mpy_source_file
571 self.mpy_segments = mpy_segments
572 self.source_file = qstr_table[0]
573 self.header = header
574 self.qstr_table = qstr_table
575 self.obj_table = obj_table
Damien Georgef2040bf2021-10-22 22:22:47 +1100576 self.raw_code = raw_code
Damien George599a22e2022-05-31 00:17:38 +1000577 self.qstr_table_file_offset = qstr_table_file_offset
578 self.obj_table_file_offset = obj_table_file_offset
579 self.raw_code_file_offset = raw_code_file_offset
Damien Georgef2040bf2021-10-22 22:22:47 +1100580 self.escaped_name = escaped_name
Damien George0699c6b2016-01-31 21:45:22 +0000581
Damien Georgef2040bf2021-10-22 22:22:47 +1100582 def hexdump(self):
583 with open(self.mpy_source_file, "rb") as f:
584 WIDTH = 16
585 COL_OFF = "\033[0m"
586 COL_TABLE = (
587 ("", ""), # META
588 ("\033[0;31m", "\033[0;91m"), # QSTR
589 ("\033[0;32m", "\033[0;92m"), # OBJ
590 ("\033[0;34m", "\033[0;94m"), # CODE
591 )
592 cur_col = ""
593 cur_col_index = 0
594 offset = 0
595 segment_index = 0
596 while True:
597 data = bytes_cons(f.read(WIDTH))
598 if not data:
599 break
Damien George0699c6b2016-01-31 21:45:22 +0000600
Damien Georgef2040bf2021-10-22 22:22:47 +1100601 # Print out the hex dump of this line of data.
602 line_hex = cur_col
603 line_chr = cur_col
604 line_comment = ""
605 for i in range(len(data)):
606 # Determine the colour of the data, if any, and the line comment.
607 while segment_index < len(self.mpy_segments):
608 if offset + i == self.mpy_segments[segment_index].start:
609 cur_col = COL_TABLE[self.mpy_segments[segment_index].kind][
610 cur_col_index
611 ]
612 cur_col_index = 1 - cur_col_index
613 line_hex += cur_col
614 line_chr += cur_col
615 line_comment += " %s%s%s" % (
616 cur_col,
617 self.mpy_segments[segment_index].name,
618 COL_OFF,
619 )
620 if offset + i == self.mpy_segments[segment_index].end:
621 cur_col = ""
622 line_hex += COL_OFF
623 line_chr += COL_OFF
624 segment_index += 1
625 else:
626 break
Damien George0699c6b2016-01-31 21:45:22 +0000627
Damien Georgef2040bf2021-10-22 22:22:47 +1100628 # Add to the hex part of the line.
629 if i % 2 == 0:
630 line_hex += " "
631 line_hex += "%02x" % data[i]
Damien George02fd83b2016-05-03 12:24:39 +0100632
Damien Georgef2040bf2021-10-22 22:22:47 +1100633 # Add to the characters part of the line.
634 if 0x20 <= data[i] <= 0x7E:
635 line_chr += "%s" % chr(data[i])
636 else:
637 line_chr += "."
638
639 # Print out this line.
640 if cur_col:
641 line_hex += COL_OFF
642 line_chr += COL_OFF
643 pad = " " * ((WIDTH - len(data)) * 5 // 2)
644 print("%08x:%s%s %s %s" % (offset, line_hex, pad, line_chr, line_comment))
645 offset += WIDTH
646
647 def disassemble(self):
648 print("mpy_source_file:", self.mpy_source_file)
649 print("source_file:", self.source_file.str)
650 print("header:", hexlify_to_str(self.header))
651 print("qstr_table[%u]:" % len(self.qstr_table))
652 for q in self.qstr_table:
653 print(" %s" % q.str)
654 print("obj_table:", self.obj_table)
655 self.raw_code.disassemble()
656
657 def freeze(self, compiled_module_index):
658 print()
659 print("/" * 80)
660 print("// frozen module %s" % self.escaped_name)
661 print("// - original source file: %s" % self.mpy_source_file)
662 print("// - frozen file name: %s" % self.source_file.str)
663 print("// - .mpy header: %s" % ":".join("%02x" % b for b in self.header))
664 print()
665
666 self.raw_code.freeze()
667 print()
668
669 self.freeze_constants()
670
671 print()
672 print("static const mp_frozen_module_t frozen_module_%s = {" % self.escaped_name)
673 print(" .constants = {")
674 if len(self.qstr_table):
675 print(
676 " .qstr_table = (qstr_short_t *)&const_qstr_table_data_%s,"
677 % self.escaped_name
678 )
679 else:
680 print(" .qstr_table = NULL,")
681 if len(self.obj_table):
682 print(" .obj_table = (mp_obj_t *)&const_obj_table_data_%s," % self.escaped_name)
683 else:
684 print(" .obj_table = NULL,")
685 print(" },")
686 print(" .rc = &raw_code_%s," % self.raw_code.escaped_name)
687 print("};")
Damien George0699c6b2016-01-31 21:45:22 +0000688
Damien George68b3aee2022-03-31 16:20:23 +1100689 def freeze_constant_obj(self, obj_name, obj):
Damien Georgef2040bf2021-10-22 22:22:47 +1100690 global const_str_content, const_int_content, const_obj_content
691
Damien George68b3aee2022-03-31 16:20:23 +1100692 if isinstance(obj, MPFunTable):
693 return "&mp_fun_table"
694 elif obj is None:
695 return "MP_ROM_NONE"
696 elif obj is False:
697 return "MP_ROM_FALSE"
698 elif obj is True:
699 return "MP_ROM_TRUE"
700 elif obj is Ellipsis:
701 return "MP_ROM_PTR(&mp_const_ellipsis_obj)"
702 elif is_str_type(obj) or is_bytes_type(obj):
Damien Georgedfc6c6292022-04-08 13:07:25 +1000703 if len(obj) == 0:
704 if is_str_type(obj):
705 return "MP_ROM_QSTR(MP_QSTR_)"
706 else:
707 return "MP_ROM_PTR(&mp_const_empty_bytes_obj)"
Damien George68b3aee2022-03-31 16:20:23 +1100708 if is_str_type(obj):
Damien George40d431d2022-04-08 14:05:23 +1000709 q = global_qstrs.find_by_str(obj)
710 if q:
711 return "MP_ROM_QSTR(%s)" % q.qstr_id
Damien George68b3aee2022-03-31 16:20:23 +1100712 obj = bytes_cons(obj, "utf8")
713 obj_type = "mp_type_str"
714 else:
715 obj_type = "mp_type_bytes"
716 print(
717 'static const mp_obj_str_t %s = {{&%s}, %u, %u, (const byte*)"%s"};'
718 % (
719 obj_name,
720 obj_type,
721 qstrutil.compute_hash(obj, config.MICROPY_QSTR_BYTES_IN_HASH),
722 len(obj),
723 "".join(("\\x%02x" % b) for b in obj),
724 )
725 )
726 const_str_content += len(obj)
727 const_obj_content += 4 * 4
728 return "MP_ROM_PTR(&%s)" % obj_name
729 elif is_int_type(obj):
Damien George9c8a5632022-04-07 23:53:37 +1000730 if mp_small_int_fits(obj):
731 # Encode directly as a small integer object.
732 return "MP_ROM_INT(%d)" % obj
733 elif config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_NONE:
Damien George68b3aee2022-03-31 16:20:23 +1100734 raise FreezeError(self, "target does not support long int")
735 elif config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_LONGLONG:
736 # TODO
737 raise FreezeError(self, "freezing int to long-long is not implemented")
738 elif config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_MPZ:
739 neg = 0
740 if obj < 0:
741 obj = -obj
742 neg = 1
743 bits_per_dig = config.MPZ_DIG_SIZE
744 digs = []
745 z = obj
746 while z:
747 digs.append(z & ((1 << bits_per_dig) - 1))
748 z >>= bits_per_dig
749 ndigs = len(digs)
750 digs = ",".join(("%#x" % d) for d in digs)
751 print(
752 "static const mp_obj_int_t %s = {{&mp_type_int}, "
753 "{.neg=%u, .fixed_dig=1, .alloc=%u, .len=%u, .dig=(uint%u_t*)(const uint%u_t[]){%s}}};"
754 % (obj_name, neg, ndigs, ndigs, bits_per_dig, bits_per_dig, digs)
755 )
756 const_int_content += (digs.count(",") + 1) * bits_per_dig // 8
757 const_obj_content += 4 * 4
758 return "MP_ROM_PTR(&%s)" % obj_name
Angus Gratton597fcb42023-08-09 17:41:54 +1000759 elif isinstance(obj, float):
Damien George68b3aee2022-03-31 16:20:23 +1100760 macro_name = "%s_macro" % obj_name
761 print(
762 "#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B"
763 )
764 print(
765 "static const mp_obj_float_t %s = {{&mp_type_float}, (mp_float_t)%.16g};"
766 % (obj_name, obj)
767 )
768 print("#define %s MP_ROM_PTR(&%s)" % (macro_name, obj_name))
769 print("#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C")
770 n = struct.unpack("<I", struct.pack("<f", obj))[0]
771 n = ((n & ~0x3) | 2) + 0x80800000
772 print("#define %s ((mp_rom_obj_t)(0x%08x))" % (macro_name, n))
773 print("#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D")
774 n = struct.unpack("<Q", struct.pack("<d", obj))[0]
775 n += 0x8004000000000000
776 print("#define %s ((mp_rom_obj_t)(0x%016x))" % (macro_name, n))
777 print("#endif")
778 const_obj_content += 3 * 4
779 return macro_name
Angus Gratton597fcb42023-08-09 17:41:54 +1000780 elif isinstance(obj, complex):
Damien George68b3aee2022-03-31 16:20:23 +1100781 print(
782 "static const mp_obj_complex_t %s = {{&mp_type_complex}, (mp_float_t)%.16g, (mp_float_t)%.16g};"
783 % (obj_name, obj.real, obj.imag)
784 )
785 return "MP_ROM_PTR(&%s)" % obj_name
786 elif type(obj) is tuple:
787 if len(obj) == 0:
788 return "MP_ROM_PTR(&mp_const_empty_tuple_obj)"
789 else:
790 obj_refs = []
791 for i, sub_obj in enumerate(obj):
792 sub_obj_name = "%s_%u" % (obj_name, i)
793 obj_refs.append(self.freeze_constant_obj(sub_obj_name, sub_obj))
794 print(
795 "static const mp_rom_obj_tuple_t %s = {{&mp_type_tuple}, %d, {"
796 % (obj_name, len(obj))
797 )
798 for ref in obj_refs:
799 print(" %s," % ref)
800 print("}};")
801 return "MP_ROM_PTR(&%s)" % obj_name
802 else:
803 raise FreezeError(self, "freezing of object %r is not implemented" % (obj,))
804
805 def freeze_constants(self):
Damien Georgef2040bf2021-10-22 22:22:47 +1100806 if len(self.qstr_table):
807 print(
808 "static const qstr_short_t const_qstr_table_data_%s[%u] = {"
809 % (self.escaped_name, len(self.qstr_table))
810 )
811 for q in self.qstr_table:
812 print(" %s," % q.qstr_id)
813 print("};")
814
815 if not len(self.obj_table):
816 return
817
Damien George0699c6b2016-01-31 21:45:22 +0000818 # generate constant objects
Damien Georgef2040bf2021-10-22 22:22:47 +1100819 print()
820 print("// constants")
Damien George68b3aee2022-03-31 16:20:23 +1100821 obj_refs = []
Damien Georgef2040bf2021-10-22 22:22:47 +1100822 for i, obj in enumerate(self.obj_table):
Damien George69661f32020-02-27 15:36:53 +1100823 obj_name = "const_obj_%s_%u" % (self.escaped_name, i)
Damien George68b3aee2022-03-31 16:20:23 +1100824 obj_refs.append(self.freeze_constant_obj(obj_name, obj))
Damien George0699c6b2016-01-31 21:45:22 +0000825
Damien Georgef2040bf2021-10-22 22:22:47 +1100826 # generate constant table
827 print()
828 print("// constant table")
829 print(
830 "static const mp_rom_obj_t const_obj_table_data_%s[%u] = {"
831 % (self.escaped_name, len(self.obj_table))
832 )
Damien George68b3aee2022-03-31 16:20:23 +1100833 for ref in obj_refs:
834 print(" %s," % ref)
Damien Georgef2040bf2021-10-22 22:22:47 +1100835 print("};")
Damien George0699c6b2016-01-31 21:45:22 +0000836
Damien Georgef2040bf2021-10-22 22:22:47 +1100837 global const_table_ptr_content
838 const_table_ptr_content += len(self.obj_table)
839
840
841class RawCode(object):
842 # a set of all escaped names, to make sure they are unique
843 escaped_names = set()
844
845 # convert code kind number to string
846 code_kind_str = {
847 MP_CODE_BYTECODE: "MP_CODE_BYTECODE",
848 MP_CODE_NATIVE_PY: "MP_CODE_NATIVE_PY",
849 MP_CODE_NATIVE_VIPER: "MP_CODE_NATIVE_VIPER",
850 MP_CODE_NATIVE_ASM: "MP_CODE_NATIVE_ASM",
851 }
852
Damien George2fb413b2022-06-07 15:00:00 +1000853 def __init__(self, parent_name, qstr_table, fun_data, prelude_offset, code_kind):
Damien Georgef2040bf2021-10-22 22:22:47 +1100854 self.qstr_table = qstr_table
855 self.fun_data = fun_data
856 self.prelude_offset = prelude_offset
857 self.code_kind = code_kind
858
859 if code_kind in (MP_CODE_BYTECODE, MP_CODE_NATIVE_PY):
860 (
Damien George599a22e2022-05-31 00:17:38 +1000861 self.offset_prelude_size,
862 self.offset_source_info,
Damien Georgef2040bf2021-10-22 22:22:47 +1100863 self.offset_line_info,
Damien George599a22e2022-05-31 00:17:38 +1000864 self.offset_closure_info,
865 self.offset_opcodes,
866 self.prelude_signature,
867 self.prelude_size,
Damien Georgef2040bf2021-10-22 22:22:47 +1100868 self.names,
869 ) = extract_prelude(self.fun_data, prelude_offset)
Damien George599a22e2022-05-31 00:17:38 +1000870 self.scope_flags = self.prelude_signature[2]
871 self.n_pos_args = self.prelude_signature[3]
Damien Georgef2040bf2021-10-22 22:22:47 +1100872 self.simple_name = self.qstr_table[self.names[0]]
Damien Georgeb6a32892017-08-12 22:26:18 +1000873 else:
Damien Georgef2040bf2021-10-22 22:22:47 +1100874 self.simple_name = self.qstr_table[0]
875
Damien George2fb413b2022-06-07 15:00:00 +1000876 escaped_name = parent_name + "_" + self.simple_name.qstr_esc
Damien Georgef2040bf2021-10-22 22:22:47 +1100877
878 # make sure the escaped name is unique
879 i = 2
880 unique_escaped_name = escaped_name
881 while unique_escaped_name in self.escaped_names:
882 unique_escaped_name = escaped_name + str(i)
883 i += 1
884 self.escaped_names.add(unique_escaped_name)
885 self.escaped_name = unique_escaped_name
886
887 def disassemble_children(self):
888 print(" children:", [rc.simple_name.str for rc in self.children])
889 for rc in self.children:
890 rc.disassemble()
891
Damien George1fb01bd2022-05-10 13:56:24 +1000892 def freeze_children(self, prelude_ptr=None):
Damien Georgef2040bf2021-10-22 22:22:47 +1100893 # Freeze children and generate table of children.
894 if len(self.children):
895 for rc in self.children:
896 print("// child of %s" % self.escaped_name)
897 rc.freeze()
898 print()
899 print("static const mp_raw_code_t *const children_%s[] = {" % self.escaped_name)
900 for rc in self.children:
901 print(" &raw_code_%s," % rc.escaped_name)
Damien George1fb01bd2022-05-10 13:56:24 +1000902 if prelude_ptr:
903 print(" (void *)%s," % prelude_ptr)
Damien Georgef2040bf2021-10-22 22:22:47 +1100904 print("};")
905 print()
906
Damien Georged4d53e92022-05-20 14:31:56 +1000907 def freeze_raw_code(self, prelude_ptr=None, type_sig=0):
Damien Georgef2040bf2021-10-22 22:22:47 +1100908 # Generate mp_raw_code_t.
909 print("static const mp_raw_code_t raw_code_%s = {" % self.escaped_name)
910 print(" .kind = %s," % RawCode.code_kind_str[self.code_kind])
911 print(" .scope_flags = 0x%02x," % self.scope_flags)
912 print(" .n_pos_args = %u," % self.n_pos_args)
913 print(" .fun_data = fun_data_%s," % self.escaped_name)
914 print(" #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS")
915 print(" .fun_data_len = %u," % len(self.fun_data))
916 print(" #endif")
917 if len(self.children):
918 print(" .children = (void *)&children_%s," % self.escaped_name)
Damien George1fb01bd2022-05-10 13:56:24 +1000919 elif prelude_ptr:
920 print(" .children = (void *)%s," % prelude_ptr)
Damien Georgef2040bf2021-10-22 22:22:47 +1100921 else:
922 print(" .children = NULL,")
Damien George69661f32020-02-27 15:36:53 +1100923 print(" #if MICROPY_PERSISTENT_CODE_SAVE")
Damien Georgef2040bf2021-10-22 22:22:47 +1100924 print(" .n_children = %u," % len(self.children))
Damien Georgec69f58e2019-09-06 23:55:15 +1000925 if self.code_kind == MP_CODE_BYTECODE:
Damien George69661f32020-02-27 15:36:53 +1100926 print(" #if MICROPY_PY_SYS_SETTRACE")
927 print(" .prelude = {")
Damien George599a22e2022-05-31 00:17:38 +1000928 print(" .n_state = %u," % self.prelude_signature[0])
929 print(" .n_exc_stack = %u," % self.prelude_signature[1])
930 print(" .scope_flags = %u," % self.prelude_signature[2])
931 print(" .n_pos_args = %u," % self.prelude_signature[3])
932 print(" .n_kwonly_args = %u," % self.prelude_signature[4])
933 print(" .n_def_pos_args = %u," % self.prelude_signature[5])
Damien Georgef2040bf2021-10-22 22:22:47 +1100934 print(" .qstr_block_name_idx = %u," % self.names[0])
Martin Milata492cf342020-08-13 15:20:08 +0200935 print(
936 " .line_info = fun_data_%s + %u,"
Damien Georgef2040bf2021-10-22 22:22:47 +1100937 % (self.escaped_name, self.offset_line_info)
Martin Milata492cf342020-08-13 15:20:08 +0200938 )
Damien Georgef2040bf2021-10-22 22:22:47 +1100939 print(
Martin Milata850f09b2023-01-26 14:10:18 +0100940 " .line_info_top = fun_data_%s + %u,"
941 % (self.escaped_name, self.offset_closure_info)
942 )
943 print(
Damien Georgef2040bf2021-10-22 22:22:47 +1100944 " .opcodes = fun_data_%s + %u," % (self.escaped_name, self.offset_opcodes)
945 )
Damien George69661f32020-02-27 15:36:53 +1100946 print(" },")
947 print(" .line_of_definition = %u," % 0) # TODO
948 print(" #endif")
949 print(" #if MICROPY_EMIT_MACHINE_CODE")
950 print(" .prelude_offset = %u," % self.prelude_offset)
Damien George69661f32020-02-27 15:36:53 +1100951 print(" #endif")
952 print(" #endif")
953 print(" #if MICROPY_EMIT_MACHINE_CODE")
954 print(" .type_sig = %u," % type_sig)
955 print(" #endif")
956 print("};")
957
Damien Georgef2040bf2021-10-22 22:22:47 +1100958 global raw_code_count, raw_code_content
959 raw_code_count += 1
960 raw_code_content += 4 * 4
961
Damien George0699c6b2016-01-31 21:45:22 +0000962
Damien Georgeea3c80a2019-02-21 15:18:59 +1100963class RawCodeBytecode(RawCode):
Damien George2fb413b2022-06-07 15:00:00 +1000964 def __init__(self, parent_name, qstr_table, obj_table, fun_data):
Damien Georgef2040bf2021-10-22 22:22:47 +1100965 self.obj_table = obj_table
Damien George69661f32020-02-27 15:36:53 +1100966 super(RawCodeBytecode, self).__init__(
Damien George2fb413b2022-06-07 15:00:00 +1000967 parent_name, qstr_table, fun_data, 0, MP_CODE_BYTECODE
Damien George69661f32020-02-27 15:36:53 +1100968 )
Damien Georgeea3c80a2019-02-21 15:18:59 +1100969
Damien Georgef2040bf2021-10-22 22:22:47 +1100970 def disassemble(self):
971 bc = self.fun_data
972 print("simple_name:", self.simple_name.str)
973 print(" raw bytecode:", len(bc), hexlify_to_str(bc))
Damien George599a22e2022-05-31 00:17:38 +1000974 print(" prelude:", self.prelude_signature)
Damien Georgef2040bf2021-10-22 22:22:47 +1100975 print(" args:", [self.qstr_table[i].str for i in self.names[1:]])
976 print(" line info:", hexlify_to_str(bc[self.offset_line_info : self.offset_opcodes]))
977 ip = self.offset_opcodes
978 while ip < len(bc):
Damien George599a22e2022-05-31 00:17:38 +1000979 fmt, sz, arg, _ = mp_opcode_decode(bc, ip)
980 if bc[ip] == Opcode.MP_BC_LOAD_CONST_OBJ:
Damien George2a075cc2022-03-31 15:26:14 +1100981 arg = repr(self.obj_table[arg])
Damien Georgef2040bf2021-10-22 22:22:47 +1100982 if fmt == MP_BC_FORMAT_QSTR:
983 arg = self.qstr_table[arg].str
984 elif fmt in (MP_BC_FORMAT_VAR_UINT, MP_BC_FORMAT_OFFSET):
985 pass
Damien Georgeea3c80a2019-02-21 15:18:59 +1100986 else:
Damien Georgef2040bf2021-10-22 22:22:47 +1100987 arg = ""
988 print(
Damien George599a22e2022-05-31 00:17:38 +1000989 " %-11s %s %s" % (hexlify_to_str(bc[ip : ip + sz]), Opcode.mapping[bc[ip]], arg)
Damien Georgef2040bf2021-10-22 22:22:47 +1100990 )
Damien Georgeea3c80a2019-02-21 15:18:59 +1100991 ip += sz
Damien Georgef2040bf2021-10-22 22:22:47 +1100992 self.disassemble_children()
993
994 def freeze(self):
995 # generate bytecode data
996 bc = self.fun_data
997 print(
998 "// frozen bytecode for file %s, scope %s"
999 % (self.qstr_table[0].str, self.escaped_name)
1000 )
1001 print("static const byte fun_data_%s[%u] = {" % (self.escaped_name, len(bc)))
1002
1003 print(" ", end="")
Damien George599a22e2022-05-31 00:17:38 +10001004 for b in bc[: self.offset_source_info]:
Damien Georgef2040bf2021-10-22 22:22:47 +11001005 print("0x%02x," % b, end="")
1006 print(" // prelude")
1007
1008 print(" ", end="")
Damien George599a22e2022-05-31 00:17:38 +10001009 for b in bc[self.offset_source_info : self.offset_line_info]:
Damien Georgef2040bf2021-10-22 22:22:47 +11001010 print("0x%02x," % b, end="")
1011 print(" // names: %s" % ", ".join(self.qstr_table[i].str for i in self.names))
1012
1013 print(" ", end="")
1014 for b in bc[self.offset_line_info : self.offset_opcodes]:
1015 print("0x%02x," % b, end="")
1016 print(" // code info")
1017
1018 ip = self.offset_opcodes
1019 while ip < len(bc):
Damien George599a22e2022-05-31 00:17:38 +10001020 fmt, sz, arg, _ = mp_opcode_decode(bc, ip)
1021 opcode_name = Opcode.mapping[bc[ip]]
Damien Georgef2040bf2021-10-22 22:22:47 +11001022 if fmt == MP_BC_FORMAT_QSTR:
robert-hh5c467212022-02-26 07:55:53 +01001023 opcode_name += " " + repr(self.qstr_table[arg].str)
Damien Georgef2040bf2021-10-22 22:22:47 +11001024 elif fmt in (MP_BC_FORMAT_VAR_UINT, MP_BC_FORMAT_OFFSET):
1025 opcode_name += " %u" % arg
1026 print(
1027 " %s, // %s" % (",".join("0x%02x" % b for b in bc[ip : ip + sz]), opcode_name)
1028 )
1029 ip += sz
1030
Damien George69661f32020-02-27 15:36:53 +11001031 print("};")
Damien Georgeea3c80a2019-02-21 15:18:59 +11001032
Damien Georgef2040bf2021-10-22 22:22:47 +11001033 self.freeze_children()
1034 self.freeze_raw_code()
1035
1036 global bc_content
1037 bc_content += len(bc)
Damien Georgeea3c80a2019-02-21 15:18:59 +11001038
Damien George69661f32020-02-27 15:36:53 +11001039
Damien Georgeea3c80a2019-02-21 15:18:59 +11001040class RawCodeNative(RawCode):
Damien George69661f32020-02-27 15:36:53 +11001041 def __init__(
1042 self,
Damien George2fb413b2022-06-07 15:00:00 +10001043 parent_name,
Damien Georgef2040bf2021-10-22 22:22:47 +11001044 qstr_table,
1045 kind,
Damien George69661f32020-02-27 15:36:53 +11001046 fun_data,
1047 prelude_offset,
Damien Georgef2040bf2021-10-22 22:22:47 +11001048 scope_flags,
1049 n_pos_args,
Damien George69661f32020-02-27 15:36:53 +11001050 type_sig,
1051 ):
1052 super(RawCodeNative, self).__init__(
Damien George2fb413b2022-06-07 15:00:00 +10001053 parent_name, qstr_table, fun_data, prelude_offset, kind
Damien George69661f32020-02-27 15:36:53 +11001054 )
Damien Georgef2040bf2021-10-22 22:22:47 +11001055
1056 if kind in (MP_CODE_NATIVE_VIPER, MP_CODE_NATIVE_ASM):
1057 self.scope_flags = scope_flags
1058 self.n_pos_args = n_pos_args
1059
Damien Georgeea3c80a2019-02-21 15:18:59 +11001060 self.type_sig = type_sig
Damien George69661f32020-02-27 15:36:53 +11001061 if config.native_arch in (
1062 MP_NATIVE_ARCH_X86,
1063 MP_NATIVE_ARCH_X64,
1064 MP_NATIVE_ARCH_XTENSA,
1065 MP_NATIVE_ARCH_XTENSAWIN,
1066 ):
Damien Georgeea3c80a2019-02-21 15:18:59 +11001067 self.fun_data_attributes = '__attribute__((section(".text,\\"ax\\",@progbits # ")))'
1068 else:
1069 self.fun_data_attributes = '__attribute__((section(".text,\\"ax\\",%progbits @ ")))'
1070
Damien George7f24c292019-11-28 13:11:51 +11001071 # Allow single-byte alignment by default for x86/x64.
1072 # ARM needs word alignment, ARM Thumb needs halfword, due to instruction size.
1073 # Xtensa needs word alignment due to the 32-bit constant table embedded in the code.
Damien George69661f32020-02-27 15:36:53 +11001074 if config.native_arch in (
1075 MP_NATIVE_ARCH_ARMV6,
1076 MP_NATIVE_ARCH_XTENSA,
1077 MP_NATIVE_ARCH_XTENSAWIN,
1078 ):
Damien George7f24c292019-11-28 13:11:51 +11001079 # ARMV6 or Xtensa -- four byte align.
Damien George69661f32020-02-27 15:36:53 +11001080 self.fun_data_attributes += " __attribute__ ((aligned (4)))"
Jim Mussared4ab51562019-08-17 00:32:04 +10001081 elif MP_NATIVE_ARCH_ARMV6M <= config.native_arch <= MP_NATIVE_ARCH_ARMV7EMDP:
1082 # ARMVxxM -- two byte align.
Damien George69661f32020-02-27 15:36:53 +11001083 self.fun_data_attributes += " __attribute__ ((aligned (2)))"
Jim Mussared4ab51562019-08-17 00:32:04 +10001084
Damien Georgef2040bf2021-10-22 22:22:47 +11001085 def disassemble(self):
1086 fun_data = self.fun_data
1087 print("simple_name:", self.simple_name.str)
1088 print(
1089 " raw data:",
1090 len(fun_data),
1091 hexlify_to_str(fun_data[:32]),
1092 "..." if len(fun_data) > 32 else "",
1093 )
1094 if self.code_kind != MP_CODE_NATIVE_PY:
1095 return
Damien George599a22e2022-05-31 00:17:38 +10001096 print(" prelude:", self.prelude_signature)
Damien Georgef2040bf2021-10-22 22:22:47 +11001097 print(" args:", [self.qstr_table[i].str for i in self.names[1:]])
1098 print(" line info:", fun_data[self.offset_line_info : self.offset_opcodes])
1099 ip = 0
1100 while ip < self.prelude_offset:
1101 sz = 16
1102 print(" ", hexlify_to_str(fun_data[ip : min(ip + sz, self.prelude_offset)]))
1103 ip += sz
1104 self.disassemble_children()
1105
Damien Georgef2040bf2021-10-22 22:22:47 +11001106 def freeze(self):
1107 if self.scope_flags & ~0x0F:
Damien George69661f32020-02-27 15:36:53 +11001108 raise FreezeError("unable to freeze code with relocations")
Damien Georgefc97d6d2019-12-10 14:57:12 +11001109
Damien Georgeea3c80a2019-02-21 15:18:59 +11001110 # generate native code data
1111 print()
Damien George69661f32020-02-27 15:36:53 +11001112 print(
Damien Georgef2040bf2021-10-22 22:22:47 +11001113 "// frozen native code for file %s, scope %s"
1114 % (self.qstr_table[0].str, self.escaped_name)
1115 )
1116 print(
1117 "static const byte fun_data_%s[%u] %s = {"
1118 % (self.escaped_name, len(self.fun_data), self.fun_data_attributes)
Damien George69661f32020-02-27 15:36:53 +11001119 )
Damien Georgeea3c80a2019-02-21 15:18:59 +11001120
Damien Georgef2040bf2021-10-22 22:22:47 +11001121 i_top = len(self.fun_data)
Damien Georgeea3c80a2019-02-21 15:18:59 +11001122 i = 0
Damien Georgeea3c80a2019-02-21 15:18:59 +11001123 while i < i_top:
Damien Georged4d53e92022-05-20 14:31:56 +10001124 # copy machine code (max 16 bytes)
1125 i16 = min(i + 16, i_top)
1126 print(" ", end="")
1127 for ii in range(i, i16):
1128 print(" 0x%02x," % self.fun_data[ii], end="")
1129 print()
1130 i = i16
Damien Georgeea3c80a2019-02-21 15:18:59 +11001131
Damien George69661f32020-02-27 15:36:53 +11001132 print("};")
Damien Georgeea3c80a2019-02-21 15:18:59 +11001133
Damien George1fb01bd2022-05-10 13:56:24 +10001134 prelude_ptr = None
1135 if self.code_kind == MP_CODE_NATIVE_PY:
1136 prelude_ptr = "fun_data_%s_prelude_macro" % self.escaped_name
1137 print("#if MICROPY_EMIT_NATIVE_PRELUDE_SEPARATE_FROM_MACHINE_CODE")
1138 n = len(self.fun_data) - self.prelude_offset
1139 print("static const byte fun_data_%s_prelude[%u] = {" % (self.escaped_name, n), end="")
1140 for i in range(n):
1141 print(" 0x%02x," % self.fun_data[self.prelude_offset + i], end="")
1142 print("};")
1143 print("#define %s &fun_data_%s_prelude[0]" % (prelude_ptr, self.escaped_name))
1144 print("#else")
1145 print(
1146 "#define %s &fun_data_%s[%u]"
1147 % (prelude_ptr, self.escaped_name, self.prelude_offset)
1148 )
1149 print("#endif")
1150
1151 self.freeze_children(prelude_ptr)
Damien Georged4d53e92022-05-20 14:31:56 +10001152 self.freeze_raw_code(prelude_ptr, self.type_sig)
Damien Georgeea3c80a2019-02-21 15:18:59 +11001153
Damien George69661f32020-02-27 15:36:53 +11001154
Damien Georgef2040bf2021-10-22 22:22:47 +11001155class MPYSegment:
1156 META = 0
1157 QSTR = 1
1158 OBJ = 2
1159 CODE = 3
Damien George992a6e12019-03-01 14:03:10 +11001160
Damien Georgef2040bf2021-10-22 22:22:47 +11001161 def __init__(self, kind, name, start, end):
1162 self.kind = kind
1163 self.name = name
1164 self.start = start
1165 self.end = end
Damien George992a6e12019-03-01 14:03:10 +11001166
Damien George69661f32020-02-27 15:36:53 +11001167
Damien Georgef2040bf2021-10-22 22:22:47 +11001168class MPYReader:
1169 def __init__(self, filename, fileobj):
1170 self.filename = filename
1171 self.fileobj = fileobj
1172
1173 def tell(self):
1174 return self.fileobj.tell()
1175
1176 def read_byte(self):
1177 return bytes_cons(self.fileobj.read(1))[0]
1178
1179 def read_bytes(self, n):
1180 return bytes_cons(self.fileobj.read(n))
1181
1182 def read_uint(self):
1183 i = 0
1184 while True:
1185 b = self.read_byte()
1186 i = (i << 7) | (b & 0x7F)
1187 if b & 0x80 == 0:
1188 break
1189 return i
Damien George992a6e12019-03-01 14:03:10 +11001190
Damien George69661f32020-02-27 15:36:53 +11001191
Damien Georgef2040bf2021-10-22 22:22:47 +11001192def read_qstr(reader, segments):
1193 start_pos = reader.tell()
1194 ln = reader.read_uint()
Damien George5996eeb2019-02-25 23:15:51 +11001195 if ln & 1:
Damien Georgef2040bf2021-10-22 22:22:47 +11001196 # static qstr
Damien Georgee6479662022-04-08 14:04:21 +10001197 q = global_qstrs.get_by_index(ln >> 1)
1198 segments.append(MPYSegment(MPYSegment.META, q.str, start_pos, start_pos))
1199 return q
Damien George5996eeb2019-02-25 23:15:51 +11001200 ln >>= 1
Damien Georgef2040bf2021-10-22 22:22:47 +11001201 start_pos = reader.tell()
1202 data = str_cons(reader.read_bytes(ln), "utf8")
1203 reader.read_byte() # read and discard null terminator
1204 segments.append(MPYSegment(MPYSegment.QSTR, data, start_pos, reader.tell()))
Damien Georgee6479662022-04-08 14:04:21 +10001205 return global_qstrs.add(data)
Damien George0699c6b2016-01-31 21:45:22 +00001206
Damien George69661f32020-02-27 15:36:53 +11001207
Damien Georgef2040bf2021-10-22 22:22:47 +11001208def read_obj(reader, segments):
Damien George42d0bd22022-04-07 22:18:37 +10001209 obj_type = reader.read_byte()
1210 if obj_type == MP_PERSISTENT_OBJ_FUN_TABLE:
Damien Georgef2040bf2021-10-22 22:22:47 +11001211 return MPFunTable()
Damien George2a075cc2022-03-31 15:26:14 +11001212 elif obj_type == MP_PERSISTENT_OBJ_NONE:
1213 return None
1214 elif obj_type == MP_PERSISTENT_OBJ_FALSE:
1215 return False
1216 elif obj_type == MP_PERSISTENT_OBJ_TRUE:
1217 return True
Damien George42d0bd22022-04-07 22:18:37 +10001218 elif obj_type == MP_PERSISTENT_OBJ_ELLIPSIS:
Damien George0699c6b2016-01-31 21:45:22 +00001219 return Ellipsis
Damien George2a075cc2022-03-31 15:26:14 +11001220 elif obj_type == MP_PERSISTENT_OBJ_TUPLE:
1221 ln = reader.read_uint()
1222 return tuple(read_obj(reader, segments) for _ in range(ln))
Damien George0699c6b2016-01-31 21:45:22 +00001223 else:
Damien Georgef2040bf2021-10-22 22:22:47 +11001224 ln = reader.read_uint()
1225 start_pos = reader.tell()
1226 buf = reader.read_bytes(ln)
Damien George42d0bd22022-04-07 22:18:37 +10001227 if obj_type in (MP_PERSISTENT_OBJ_STR, MP_PERSISTENT_OBJ_BYTES):
Damien Georgef2040bf2021-10-22 22:22:47 +11001228 reader.read_byte() # read and discard null terminator
Damien George42d0bd22022-04-07 22:18:37 +10001229 if obj_type == MP_PERSISTENT_OBJ_STR:
Damien Georgef2040bf2021-10-22 22:22:47 +11001230 obj = str_cons(buf, "utf8")
Damien George07f52602022-04-08 14:09:08 +10001231 if len(obj) < PERSISTENT_STR_INTERN_THRESHOLD:
1232 if not global_qstrs.find_by_str(obj):
1233 global_qstrs.add(obj)
Damien George42d0bd22022-04-07 22:18:37 +10001234 elif obj_type == MP_PERSISTENT_OBJ_BYTES:
Damien Georgef2040bf2021-10-22 22:22:47 +11001235 obj = buf
Damien George42d0bd22022-04-07 22:18:37 +10001236 elif obj_type == MP_PERSISTENT_OBJ_INT:
Damien Georgef2040bf2021-10-22 22:22:47 +11001237 obj = int(str_cons(buf, "ascii"), 10)
Damien George42d0bd22022-04-07 22:18:37 +10001238 elif obj_type == MP_PERSISTENT_OBJ_FLOAT:
Damien Georgef2040bf2021-10-22 22:22:47 +11001239 obj = float(str_cons(buf, "ascii"))
Damien George42d0bd22022-04-07 22:18:37 +10001240 elif obj_type == MP_PERSISTENT_OBJ_COMPLEX:
Damien Georgef2040bf2021-10-22 22:22:47 +11001241 obj = complex(str_cons(buf, "ascii"))
Damien George0699c6b2016-01-31 21:45:22 +00001242 else:
Damien Georgef2040bf2021-10-22 22:22:47 +11001243 raise MPYReadError(reader.filename, "corrupt .mpy file")
1244 segments.append(MPYSegment(MPYSegment.OBJ, obj, start_pos, reader.tell()))
1245 return obj
Damien George0699c6b2016-01-31 21:45:22 +00001246
Damien George69661f32020-02-27 15:36:53 +11001247
Damien George2fb413b2022-06-07 15:00:00 +10001248def read_raw_code(reader, parent_name, qstr_table, obj_table, segments):
Damien Georgef2040bf2021-10-22 22:22:47 +11001249 # Read raw code header.
1250 kind_len = reader.read_uint()
Damien Georgeea3c80a2019-02-21 15:18:59 +11001251 kind = (kind_len & 3) + MP_CODE_BYTECODE
Damien Georgef2040bf2021-10-22 22:22:47 +11001252 has_children = (kind_len >> 2) & 1
1253 fun_data_len = kind_len >> 3
1254
1255 # Read the body of the raw code.
1256 file_offset = reader.tell()
1257 fun_data = reader.read_bytes(fun_data_len)
1258 segments_len = len(segments)
Damien Georgeea3c80a2019-02-21 15:18:59 +11001259
1260 if kind == MP_CODE_BYTECODE:
Damien Georgef2040bf2021-10-22 22:22:47 +11001261 # Create bytecode raw code.
Damien George2fb413b2022-06-07 15:00:00 +10001262 rc = RawCodeBytecode(parent_name, qstr_table, obj_table, fun_data)
Damien Georgeea3c80a2019-02-21 15:18:59 +11001263 else:
Damien Georgef2040bf2021-10-22 22:22:47 +11001264 # Create native raw code.
Damien Georgef2040bf2021-10-22 22:22:47 +11001265 native_scope_flags = 0
1266 native_n_pos_args = 0
1267 native_type_sig = 0
Damien Georgeea3c80a2019-02-21 15:18:59 +11001268 if kind == MP_CODE_NATIVE_PY:
Damien Georgef2040bf2021-10-22 22:22:47 +11001269 prelude_offset = reader.read_uint()
Damien Georgeea3c80a2019-02-21 15:18:59 +11001270 else:
Damien Georgef2040bf2021-10-22 22:22:47 +11001271 prelude_offset = 0
1272 native_scope_flags = reader.read_uint()
1273 if kind == MP_CODE_NATIVE_VIPER:
1274 # Read any additional sections for native viper.
1275 if native_scope_flags & MP_SCOPE_FLAG_VIPERRODATA:
1276 rodata_size = reader.read_uint()
1277 if native_scope_flags & MP_SCOPE_FLAG_VIPERBSS:
Christian Clauss2a1db772023-03-10 07:33:32 +01001278 reader.read_uint() # bss_size
Damien Georgef2040bf2021-10-22 22:22:47 +11001279 if native_scope_flags & MP_SCOPE_FLAG_VIPERRODATA:
1280 reader.read_bytes(rodata_size)
1281 if native_scope_flags & MP_SCOPE_FLAG_VIPERRELOC:
1282 while True:
1283 op = reader.read_byte()
1284 if op == 0xFF:
1285 break
1286 if op & 1:
Christian Clauss2a1db772023-03-10 07:33:32 +01001287 reader.read_uint() # addr
Damien Georgef2040bf2021-10-22 22:22:47 +11001288 op >>= 1
1289 if op <= 5 and op & 1:
Christian Clauss2a1db772023-03-10 07:33:32 +01001290 reader.read_uint() # n
Damien Georgef2040bf2021-10-22 22:22:47 +11001291 else:
1292 assert kind == MP_CODE_NATIVE_ASM
1293 native_n_pos_args = reader.read_uint()
1294 native_type_sig = reader.read_uint()
Damien Georgeea3c80a2019-02-21 15:18:59 +11001295
Damien Georgef2040bf2021-10-22 22:22:47 +11001296 rc = RawCodeNative(
Damien George2fb413b2022-06-07 15:00:00 +10001297 parent_name,
Damien Georgef2040bf2021-10-22 22:22:47 +11001298 qstr_table,
Damien George69661f32020-02-27 15:36:53 +11001299 kind,
Damien Georgef2040bf2021-10-22 22:22:47 +11001300 fun_data,
Damien George69661f32020-02-27 15:36:53 +11001301 prelude_offset,
Damien Georgef2040bf2021-10-22 22:22:47 +11001302 native_scope_flags,
1303 native_n_pos_args,
1304 native_type_sig,
Damien George69661f32020-02-27 15:36:53 +11001305 )
1306
Damien Georgef2040bf2021-10-22 22:22:47 +11001307 # Add a segment for the raw code data.
1308 segments.insert(
1309 segments_len,
1310 MPYSegment(MPYSegment.CODE, rc.simple_name.str, file_offset, file_offset + fun_data_len),
1311 )
1312
1313 # Read children, if there are any.
1314 rc.children = []
1315 if has_children:
Damien George2fb413b2022-06-07 15:00:00 +10001316 # Make a pretty parent name (otherwise all identifiers will include _lt_module_gt_).
1317 if not rc.escaped_name.endswith("_lt_module_gt_"):
1318 parent_name = rc.escaped_name
1319
1320 # Read all the child raw codes.
Damien Georgef2040bf2021-10-22 22:22:47 +11001321 n_children = reader.read_uint()
1322 for _ in range(n_children):
Damien George2fb413b2022-06-07 15:00:00 +10001323 rc.children.append(read_raw_code(reader, parent_name, qstr_table, obj_table, segments))
Damien Georgef2040bf2021-10-22 22:22:47 +11001324
1325 return rc
1326
Damien George0699c6b2016-01-31 21:45:22 +00001327
1328def read_mpy(filename):
Damien Georgef2040bf2021-10-22 22:22:47 +11001329 with open(filename, "rb") as fileobj:
1330 reader = MPYReader(filename, fileobj)
1331 segments = []
1332
1333 # Read and verify the header.
1334 header = reader.read_bytes(4)
Damien George69661f32020-02-27 15:36:53 +11001335 if header[0] != ord("M"):
Damien Georgef2040bf2021-10-22 22:22:47 +11001336 raise MPYReadError(filename, "not a valid .mpy file")
Damien George6a110482017-02-17 00:19:34 +11001337 if header[1] != config.MPY_VERSION:
Damien Georgef2040bf2021-10-22 22:22:47 +11001338 raise MPYReadError(filename, "incompatible .mpy version")
Damien George5996eeb2019-02-25 23:15:51 +11001339 feature_byte = header[2]
Damien Georgefaf3d3e2019-06-04 22:13:32 +10001340 mpy_native_arch = feature_byte >> 2
1341 if mpy_native_arch != MP_NATIVE_ARCH_NONE:
Jim Mussaredd94141e2022-09-17 23:57:12 +10001342 mpy_sub_version = feature_byte & 3
1343 if mpy_sub_version != config.MPY_SUB_VERSION:
1344 raise MPYReadError(filename, "incompatible .mpy sub-version")
Damien Georgefaf3d3e2019-06-04 22:13:32 +10001345 if config.native_arch == MP_NATIVE_ARCH_NONE:
1346 config.native_arch = mpy_native_arch
1347 elif config.native_arch != mpy_native_arch:
Damien Georgef2040bf2021-10-22 22:22:47 +11001348 raise MPYReadError(filename, "native architecture mismatch")
Damien George0699c6b2016-01-31 21:45:22 +00001349 config.mp_small_int_bits = header[3]
Damien Georgef2040bf2021-10-22 22:22:47 +11001350
1351 # Read number of qstrs, and number of objects.
1352 n_qstr = reader.read_uint()
1353 n_obj = reader.read_uint()
1354
1355 # Read qstrs and construct qstr table.
Damien George599a22e2022-05-31 00:17:38 +10001356 qstr_table_file_offset = reader.tell()
Damien Georgef2040bf2021-10-22 22:22:47 +11001357 qstr_table = []
1358 for i in range(n_qstr):
Damien Georgee6479662022-04-08 14:04:21 +10001359 qstr_table.append(read_qstr(reader, segments))
Damien Georgef2040bf2021-10-22 22:22:47 +11001360
1361 # Read objects and construct object table.
Damien George599a22e2022-05-31 00:17:38 +10001362 obj_table_file_offset = reader.tell()
Damien Georgef2040bf2021-10-22 22:22:47 +11001363 obj_table = []
1364 for i in range(n_obj):
1365 obj_table.append(read_obj(reader, segments))
1366
1367 # Compute the compiled-module escaped name.
1368 cm_escaped_name = qstr_table[0].str.replace("/", "_")[:-3]
1369
1370 # Read the outer raw code, which will in turn read all its children.
1371 raw_code_file_offset = reader.tell()
1372 raw_code = read_raw_code(reader, cm_escaped_name, qstr_table, obj_table, segments)
1373
1374 # Create the outer-level compiled module representing the whole .mpy file.
1375 return CompiledModule(
1376 filename,
1377 segments,
1378 header,
1379 qstr_table,
1380 obj_table,
1381 raw_code,
Damien George599a22e2022-05-31 00:17:38 +10001382 qstr_table_file_offset,
1383 obj_table_file_offset,
Damien Georgef2040bf2021-10-22 22:22:47 +11001384 raw_code_file_offset,
1385 cm_escaped_name,
1386 )
Damien George0699c6b2016-01-31 21:45:22 +00001387
Damien George69661f32020-02-27 15:36:53 +11001388
Damien Georgef2040bf2021-10-22 22:22:47 +11001389def hexdump_mpy(compiled_modules):
1390 for cm in compiled_modules:
1391 cm.hexdump()
Damien George0699c6b2016-01-31 21:45:22 +00001392
Damien George69661f32020-02-27 15:36:53 +11001393
Damien Georgef2040bf2021-10-22 22:22:47 +11001394def disassemble_mpy(compiled_modules):
1395 for cm in compiled_modules:
1396 cm.disassemble()
1397
1398
1399def freeze_mpy(base_qstrs, compiled_modules):
Damien George0699c6b2016-01-31 21:45:22 +00001400 # add to qstrs
1401 new = {}
Damien Georgee6479662022-04-08 14:04:21 +10001402 for q in global_qstrs.qstrs:
Damien George0699c6b2016-01-31 21:45:22 +00001403 # don't add duplicates
Damien George4f0931b2019-03-01 14:33:03 +11001404 if q is None or q.qstr_esc in base_qstrs or q.qstr_esc in new:
Damien George0699c6b2016-01-31 21:45:22 +00001405 continue
Artyom Skrobov18b1ba02021-05-03 14:17:36 -04001406 new[q.qstr_esc] = (len(new), q.qstr_esc, q.str, bytes_cons(q.str, "utf8"))
Damien George0699c6b2016-01-31 21:45:22 +00001407 new = sorted(new.values(), key=lambda x: x[0])
1408
1409 print('#include "py/mpconfig.h"')
1410 print('#include "py/objint.h"')
1411 print('#include "py/objstr.h"')
1412 print('#include "py/emitglue.h"')
Damien George360d9722019-10-07 11:56:24 +11001413 print('#include "py/nativeglue.h"')
Damien George0699c6b2016-01-31 21:45:22 +00001414 print()
1415
Damien George69661f32020-02-27 15:36:53 +11001416 print("#if MICROPY_LONGINT_IMPL != %u" % config.MICROPY_LONGINT_IMPL)
Damien George99b47192016-05-16 23:13:30 +01001417 print('#error "incompatible MICROPY_LONGINT_IMPL"')
Damien George69661f32020-02-27 15:36:53 +11001418 print("#endif")
Damien George99b47192016-05-16 23:13:30 +01001419 print()
1420
1421 if config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_MPZ:
Damien George69661f32020-02-27 15:36:53 +11001422 print("#if MPZ_DIG_SIZE != %u" % config.MPZ_DIG_SIZE)
Damien George99b47192016-05-16 23:13:30 +01001423 print('#error "incompatible MPZ_DIG_SIZE"')
Damien George69661f32020-02-27 15:36:53 +11001424 print("#endif")
Damien George99b47192016-05-16 23:13:30 +01001425 print()
1426
Damien George69661f32020-02-27 15:36:53 +11001427 print("#if MICROPY_PY_BUILTINS_FLOAT")
1428 print("typedef struct _mp_obj_float_t {")
1429 print(" mp_obj_base_t base;")
1430 print(" mp_float_t value;")
1431 print("} mp_obj_float_t;")
1432 print("#endif")
Damien George0699c6b2016-01-31 21:45:22 +00001433 print()
1434
Damien George69661f32020-02-27 15:36:53 +11001435 print("#if MICROPY_PY_BUILTINS_COMPLEX")
1436 print("typedef struct _mp_obj_complex_t {")
1437 print(" mp_obj_base_t base;")
1438 print(" mp_float_t real;")
1439 print(" mp_float_t imag;")
1440 print("} mp_obj_complex_t;")
1441 print("#endif")
Damien Georgec51c8832016-09-03 00:19:02 +10001442 print()
1443
Dave Hylands39eef272018-12-11 14:55:26 -08001444 if len(new) > 0:
Damien George69661f32020-02-27 15:36:53 +11001445 print("enum {")
Dave Hylands39eef272018-12-11 14:55:26 -08001446 for i in range(len(new)):
1447 if i == 0:
Damien George69661f32020-02-27 15:36:53 +11001448 print(" MP_QSTR_%s = MP_QSTRnumber_of," % new[i][1])
Dave Hylands39eef272018-12-11 14:55:26 -08001449 else:
Damien George69661f32020-02-27 15:36:53 +11001450 print(" MP_QSTR_%s," % new[i][1])
1451 print("};")
Damien George0699c6b2016-01-31 21:45:22 +00001452
Rich Barlow6e5a40c2018-07-19 12:42:26 +01001453 # As in qstr.c, set so that the first dynamically allocated pool is twice this size; must be <= the len
1454 qstr_pool_alloc = min(len(new), 10)
1455
Damien Georgef2040bf2021-10-22 22:22:47 +11001456 global bc_content, const_str_content, const_int_content, const_obj_content, const_table_qstr_content, const_table_ptr_content, raw_code_count, raw_code_content
1457 qstr_content = 0
1458 bc_content = 0
1459 const_str_content = 0
1460 const_int_content = 0
1461 const_obj_content = 0
1462 const_table_qstr_content = 0
1463 const_table_ptr_content = 0
1464 raw_code_count = 0
1465 raw_code_content = 0
1466
Damien George0699c6b2016-01-31 21:45:22 +00001467 print()
Artyom Skrobov18b1ba02021-05-03 14:17:36 -04001468 print("const qstr_hash_t mp_qstr_frozen_const_hashes[] = {")
1469 qstr_size = {"metadata": 0, "data": 0}
1470 for _, _, _, qbytes in new:
1471 qhash = qstrutil.compute_hash(qbytes, config.MICROPY_QSTR_BYTES_IN_HASH)
1472 print(" %d," % qhash)
1473 print("};")
1474 print()
1475 print("const qstr_len_t mp_qstr_frozen_const_lengths[] = {")
1476 for _, _, _, qbytes in new:
1477 print(" %d," % len(qbytes))
1478 qstr_size["metadata"] += (
1479 config.MICROPY_QSTR_BYTES_IN_LEN + config.MICROPY_QSTR_BYTES_IN_HASH
1480 )
1481 qstr_size["data"] += len(qbytes)
1482 print("};")
1483 print()
Damien George69661f32020-02-27 15:36:53 +11001484 print("extern const qstr_pool_t mp_qstr_const_pool;")
1485 print("const qstr_pool_t mp_qstr_frozen_const_pool = {")
Artyom Skrobovf46a7142021-05-04 03:35:45 -04001486 print(" &mp_qstr_const_pool, // previous pool")
Damien George69661f32020-02-27 15:36:53 +11001487 print(" MP_QSTRnumber_of, // previous pool size")
1488 print(" %u, // allocated entries" % qstr_pool_alloc)
1489 print(" %u, // used entries" % len(new))
Artyom Skrobov18b1ba02021-05-03 14:17:36 -04001490 print(" (qstr_hash_t *)mp_qstr_frozen_const_hashes,")
1491 print(" (qstr_len_t *)mp_qstr_frozen_const_lengths,")
Damien George69661f32020-02-27 15:36:53 +11001492 print(" {")
Artyom Skrobov18b1ba02021-05-03 14:17:36 -04001493 for _, _, qstr, qbytes in new:
1494 print(' "%s",' % qstrutil.escape_bytes(qstr, qbytes))
Damien Georgef2040bf2021-10-22 22:22:47 +11001495 qstr_content += (
1496 config.MICROPY_QSTR_BYTES_IN_LEN + config.MICROPY_QSTR_BYTES_IN_HASH + len(qbytes) + 1
1497 )
Damien George69661f32020-02-27 15:36:53 +11001498 print(" },")
1499 print("};")
Damien George0699c6b2016-01-31 21:45:22 +00001500
Damien Georgef2040bf2021-10-22 22:22:47 +11001501 # Freeze all modules.
1502 for idx, cm in enumerate(compiled_modules):
1503 cm.freeze(idx)
Damien George0699c6b2016-01-31 21:45:22 +00001504
Damien Georgef2040bf2021-10-22 22:22:47 +11001505 # Print separator, separating individual modules from global data structures.
1506 print()
1507 print("/" * 80)
1508 print("// collection of all frozen modules")
1509
1510 # Define the string of frozen module names.
Damien George0699c6b2016-01-31 21:45:22 +00001511 print()
Jim Mussarede0bf4612021-12-11 22:40:21 +11001512 print("const char mp_frozen_names[] = {")
Damien Georgef2040bf2021-10-22 22:22:47 +11001513 print(" #ifdef MP_FROZEN_STR_NAMES")
Jim Mussarede0bf4612021-12-11 22:40:21 +11001514 # makemanifest.py might also include some frozen string content.
Damien Georgef2040bf2021-10-22 22:22:47 +11001515 print(" MP_FROZEN_STR_NAMES")
1516 print(" #endif")
1517 mp_frozen_mpy_names_content = 1
1518 for cm in compiled_modules:
1519 module_name = cm.source_file.str
1520 print(' "%s\\0"' % module_name)
1521 mp_frozen_mpy_names_content += len(cm.source_file.str) + 1
1522 print(' "\\0"')
Damien George69661f32020-02-27 15:36:53 +11001523 print("};")
1524
Damien Georgef2040bf2021-10-22 22:22:47 +11001525 # Define the array of pointers to frozen module content.
1526 print()
1527 print("const mp_frozen_module_t *const mp_frozen_mpy_content[] = {")
1528 for cm in compiled_modules:
1529 print(" &frozen_module_%s," % cm.escaped_name)
1530 print("};")
1531 mp_frozen_mpy_content_size = len(compiled_modules * 4)
1532
Damien Georgefe16e782021-01-16 02:01:26 +11001533 # If a port defines MICROPY_FROZEN_LIST_ITEM then list all modules wrapped in that macro.
Damien Georgef2040bf2021-10-22 22:22:47 +11001534 print()
Damien Georgefe16e782021-01-16 02:01:26 +11001535 print("#ifdef MICROPY_FROZEN_LIST_ITEM")
Damien Georgef2040bf2021-10-22 22:22:47 +11001536 for cm in compiled_modules:
1537 module_name = cm.source_file.str
Damien Georgefe16e782021-01-16 02:01:26 +11001538 if module_name.endswith("/__init__.py"):
1539 short_name = module_name[: -len("/__init__.py")]
1540 else:
1541 short_name = module_name[: -len(".py")]
1542 print('MICROPY_FROZEN_LIST_ITEM("%s", "%s")' % (short_name, module_name))
1543 print("#endif")
1544
Damien Georgef2040bf2021-10-22 22:22:47 +11001545 print()
1546 print("/*")
1547 print("byte sizes:")
1548 print("qstr content: %d unique, %d bytes" % (len(new), qstr_content))
1549 print("bc content: %d" % bc_content)
1550 print("const str content: %d" % const_str_content)
1551 print("const int content: %d" % const_int_content)
1552 print("const obj content: %d" % const_obj_content)
1553 print(
1554 "const table qstr content: %d entries, %d bytes"
1555 % (const_table_qstr_content, const_table_qstr_content * 4)
1556 )
1557 print(
1558 "const table ptr content: %d entries, %d bytes"
1559 % (const_table_ptr_content, const_table_ptr_content * 4)
1560 )
1561 print("raw code content: %d * 4 = %d" % (raw_code_count, raw_code_content))
1562 print("mp_frozen_mpy_names_content: %d" % mp_frozen_mpy_names_content)
1563 print("mp_frozen_mpy_content_size: %d" % mp_frozen_mpy_content_size)
1564 print(
1565 "total: %d"
1566 % (
1567 qstr_content
1568 + bc_content
1569 + const_str_content
1570 + const_int_content
1571 + const_obj_content
1572 + const_table_qstr_content * 4
1573 + const_table_ptr_content * 4
1574 + raw_code_content
1575 + mp_frozen_mpy_names_content
1576 + mp_frozen_mpy_content_size
1577 )
1578 )
1579 print("*/")
1580
Damien George0699c6b2016-01-31 21:45:22 +00001581
Damien George599a22e2022-05-31 00:17:38 +10001582def adjust_bytecode_qstr_obj_indices(bytecode_in, qstr_table_base, obj_table_base):
1583 # Expand bytcode to a list of opcodes.
1584 opcodes = []
1585 labels = {}
1586 ip = 0
1587 while ip < len(bytecode_in):
1588 fmt, sz, arg, extra_arg = mp_opcode_decode(bytecode_in, ip)
1589 opcode = Opcode(ip, fmt, bytecode_in[ip], arg, extra_arg)
1590 labels[ip] = opcode
1591 opcodes.append(opcode)
1592 ip += sz
1593 if fmt == MP_BC_FORMAT_OFFSET:
1594 opcode.arg += ip
1595
1596 # Link jump opcodes to their destination.
1597 for opcode in opcodes:
1598 if opcode.fmt == MP_BC_FORMAT_OFFSET:
1599 opcode.target = labels[opcode.arg]
1600
1601 # Adjust bytcode as required.
1602 for opcode in opcodes:
1603 if opcode.fmt == MP_BC_FORMAT_QSTR:
1604 opcode.arg += qstr_table_base
1605 elif opcode.opcode_byte == Opcode.MP_BC_LOAD_CONST_OBJ:
1606 opcode.arg += obj_table_base
1607
1608 # Write out new bytecode.
1609 offset_changed = True
1610 while offset_changed:
1611 offset_changed = False
1612 overflow = False
1613 bytecode_out = b""
1614 for opcode in opcodes:
1615 ip = len(bytecode_out)
1616 if opcode.offset != ip:
1617 offset_changed = True
1618 opcode.offset = ip
1619 opcode_overflow, encoded_opcode = mp_opcode_encode(opcode)
1620 if opcode_overflow:
1621 overflow = True
1622 bytecode_out += encoded_opcode
1623
1624 if overflow:
1625 raise Exception("bytecode overflow")
1626
1627 return bytecode_out
1628
1629
1630def rewrite_raw_code(rc, qstr_table_base, obj_table_base):
1631 if rc.code_kind != MP_CODE_BYTECODE:
1632 raise Exception("can only rewrite bytecode")
1633
1634 source_info = bytearray()
1635 for arg in rc.names:
1636 source_info.extend(mp_encode_uint(qstr_table_base + arg))
1637
1638 closure_info = rc.fun_data[rc.offset_closure_info : rc.offset_opcodes]
1639
1640 bytecode_in = memoryview(rc.fun_data)[rc.offset_opcodes :]
1641 bytecode_out = adjust_bytecode_qstr_obj_indices(bytecode_in, qstr_table_base, obj_table_base)
1642
1643 prelude_signature = rc.fun_data[: rc.offset_prelude_size]
1644 prelude_size = encode_prelude_size(len(source_info), len(closure_info))
1645
1646 fun_data = prelude_signature + prelude_size + source_info + closure_info + bytecode_out
1647
1648 output = mp_encode_uint(len(fun_data) << 3 | bool(len(rc.children)) << 2)
1649 output += fun_data
1650
1651 if rc.children:
1652 output += mp_encode_uint(len(rc.children))
1653 for child in rc.children:
1654 output += rewrite_raw_code(child, qstr_table_base, obj_table_base)
1655
1656 return output
1657
1658
1659def merge_mpy(compiled_modules, output_file):
Damien George27879842019-10-09 14:23:15 +11001660 merged_mpy = bytearray()
1661
Damien George599a22e2022-05-31 00:17:38 +10001662 if len(compiled_modules) == 1:
1663 with open(compiled_modules[0].mpy_source_file, "rb") as f:
Damien George27879842019-10-09 14:23:15 +11001664 merged_mpy.extend(f.read())
1665 else:
Damien George599a22e2022-05-31 00:17:38 +10001666 main_cm_idx = None
1667 for idx, cm in enumerate(compiled_modules):
Jim Mussaredd94141e2022-09-17 23:57:12 +10001668 feature_byte = cm.header[2]
1669 mpy_native_arch = feature_byte >> 2
1670 if mpy_native_arch:
Damien Georgef2040bf2021-10-22 22:22:47 +11001671 # Must use qstr_table and obj_table from this raw_code
Damien George599a22e2022-05-31 00:17:38 +10001672 if main_cm_idx is not None:
1673 raise Exception("can't merge files when more than one contains native code")
1674 main_cm_idx = idx
1675 if main_cm_idx is not None:
1676 # Shift main_cm to front of list.
1677 compiled_modules.insert(0, compiled_modules.pop(main_cm_idx))
Damien Georgef2040bf2021-10-22 22:22:47 +11001678
1679 header = bytearray(4)
Damien George69661f32020-02-27 15:36:53 +11001680 header[0] = ord("M")
Damien George27879842019-10-09 14:23:15 +11001681 header[1] = config.MPY_VERSION
Jim Mussared1ba0e8f2022-10-20 13:14:25 +11001682 header[2] = config.native_arch << 2 | config.MPY_SUB_VERSION if config.native_arch else 0
Damien George27879842019-10-09 14:23:15 +11001683 header[3] = config.mp_small_int_bits
Damien George27879842019-10-09 14:23:15 +11001684 merged_mpy.extend(header)
1685
Damien George599a22e2022-05-31 00:17:38 +10001686 n_qstr = 0
1687 n_obj = 0
1688 for cm in compiled_modules:
1689 n_qstr += len(cm.qstr_table)
1690 n_obj += len(cm.obj_table)
1691 merged_mpy.extend(mp_encode_uint(n_qstr))
1692 merged_mpy.extend(mp_encode_uint(n_obj))
1693
1694 # Copy verbatim the qstr and object tables from all compiled modules.
1695 def copy_section(file, offset, offset2):
1696 with open(file, "rb") as f:
1697 f.seek(offset)
1698 merged_mpy.extend(f.read(offset2 - offset))
1699
1700 for cm in compiled_modules:
1701 copy_section(cm.mpy_source_file, cm.qstr_table_file_offset, cm.obj_table_file_offset)
1702 for cm in compiled_modules:
1703 copy_section(cm.mpy_source_file, cm.obj_table_file_offset, cm.raw_code_file_offset)
Damien Georgef2040bf2021-10-22 22:22:47 +11001704
Damien George27879842019-10-09 14:23:15 +11001705 bytecode = bytearray()
Damien George599a22e2022-05-31 00:17:38 +10001706 bytecode.append(0b00000000) # prelude signature
1707 bytecode.append(0b00000010) # prelude size (n_info=1, n_cell=0)
Damien Georgef2040bf2021-10-22 22:22:47 +11001708 bytecode.extend(b"\x00") # simple_name: qstr index 0 (will use source filename)
Damien George599a22e2022-05-31 00:17:38 +10001709 for idx in range(len(compiled_modules)):
Damien George69661f32020-02-27 15:36:53 +11001710 bytecode.append(0x32) # MP_BC_MAKE_FUNCTION
1711 bytecode.append(idx) # index raw code
Damien George4f2fe342020-09-04 16:12:09 +10001712 bytecode.extend(b"\x34\x00\x59") # MP_BC_CALL_FUNCTION, 0 args, MP_BC_POP_TOP
Damien George69661f32020-02-27 15:36:53 +11001713 bytecode.extend(b"\x51\x63") # MP_BC_LOAD_NONE, MP_BC_RETURN_VALUE
Damien George27879842019-10-09 14:23:15 +11001714
Damien George599a22e2022-05-31 00:17:38 +10001715 merged_mpy.extend(mp_encode_uint(len(bytecode) << 3 | 1 << 2)) # length, has_children
Damien George27879842019-10-09 14:23:15 +11001716 merged_mpy.extend(bytecode)
Damien George599a22e2022-05-31 00:17:38 +10001717 merged_mpy.extend(mp_encode_uint(len(compiled_modules))) # n_children
Damien George27879842019-10-09 14:23:15 +11001718
Damien George599a22e2022-05-31 00:17:38 +10001719 qstr_table_base = 0
1720 obj_table_base = 0
1721 for cm in compiled_modules:
1722 if qstr_table_base == 0 and obj_table_base == 0:
1723 with open(cm.mpy_source_file, "rb") as f:
1724 f.seek(cm.raw_code_file_offset)
1725 merged_mpy.extend(f.read())
1726 else:
1727 merged_mpy.extend(rewrite_raw_code(cm.raw_code, qstr_table_base, obj_table_base))
1728 qstr_table_base += len(cm.qstr_table)
1729 obj_table_base += len(cm.obj_table)
Damien George27879842019-10-09 14:23:15 +11001730
1731 if output_file is None:
1732 sys.stdout.buffer.write(merged_mpy)
1733 else:
Damien George69661f32020-02-27 15:36:53 +11001734 with open(output_file, "wb") as f:
Damien George27879842019-10-09 14:23:15 +11001735 f.write(merged_mpy)
1736
Damien George69661f32020-02-27 15:36:53 +11001737
Damien George0699c6b2016-01-31 21:45:22 +00001738def main():
Damien Georgee6479662022-04-08 14:04:21 +10001739 global global_qstrs
1740
Damien George0699c6b2016-01-31 21:45:22 +00001741 import argparse
Damien George69661f32020-02-27 15:36:53 +11001742
1743 cmd_parser = argparse.ArgumentParser(description="A tool to work with MicroPython .mpy files.")
Damien Georgef2040bf2021-10-22 22:22:47 +11001744 cmd_parser.add_argument(
1745 "-x", "--hexdump", action="store_true", help="output an annotated hex dump of files"
1746 )
1747 cmd_parser.add_argument(
1748 "-d", "--disassemble", action="store_true", help="output disassembled contents of files"
1749 )
Damien George69661f32020-02-27 15:36:53 +11001750 cmd_parser.add_argument("-f", "--freeze", action="store_true", help="freeze files")
1751 cmd_parser.add_argument(
1752 "--merge", action="store_true", help="merge multiple .mpy files into one"
1753 )
1754 cmd_parser.add_argument("-q", "--qstr-header", help="qstr header file to freeze against")
1755 cmd_parser.add_argument(
1756 "-mlongint-impl",
1757 choices=["none", "longlong", "mpz"],
1758 default="mpz",
1759 help="long-int implementation used by target (default mpz)",
1760 )
1761 cmd_parser.add_argument(
1762 "-mmpz-dig-size",
1763 metavar="N",
1764 type=int,
1765 default=16,
1766 help="mpz digit size used by target (default 16)",
1767 )
1768 cmd_parser.add_argument("-o", "--output", default=None, help="output file")
1769 cmd_parser.add_argument("files", nargs="+", help="input .mpy files")
Damien George0699c6b2016-01-31 21:45:22 +00001770 args = cmd_parser.parse_args()
1771
1772 # set config values relevant to target machine
1773 config.MICROPY_LONGINT_IMPL = {
Damien George69661f32020-02-27 15:36:53 +11001774 "none": config.MICROPY_LONGINT_IMPL_NONE,
1775 "longlong": config.MICROPY_LONGINT_IMPL_LONGLONG,
1776 "mpz": config.MICROPY_LONGINT_IMPL_MPZ,
Damien George0699c6b2016-01-31 21:45:22 +00001777 }[args.mlongint_impl]
1778 config.MPZ_DIG_SIZE = args.mmpz_dig_size
Damien Georgefaf3d3e2019-06-04 22:13:32 +10001779 config.native_arch = MP_NATIVE_ARCH_NONE
Damien George0699c6b2016-01-31 21:45:22 +00001780
Damien Georgeb4790af2016-09-02 15:09:21 +10001781 # set config values for qstrs, and get the existing base set of qstrs
Damien George0699c6b2016-01-31 21:45:22 +00001782 if args.qstr_header:
1783 qcfgs, base_qstrs = qstrutil.parse_input_headers([args.qstr_header])
Damien George69661f32020-02-27 15:36:53 +11001784 config.MICROPY_QSTR_BYTES_IN_LEN = int(qcfgs["BYTES_IN_LEN"])
1785 config.MICROPY_QSTR_BYTES_IN_HASH = int(qcfgs["BYTES_IN_HASH"])
Damien George0699c6b2016-01-31 21:45:22 +00001786 else:
Damien Georgeb4790af2016-09-02 15:09:21 +10001787 config.MICROPY_QSTR_BYTES_IN_LEN = 1
1788 config.MICROPY_QSTR_BYTES_IN_HASH = 1
Damien Georgef2040bf2021-10-22 22:22:47 +11001789 base_qstrs = list(qstrutil.static_qstr_list)
Damien George0699c6b2016-01-31 21:45:22 +00001790
Damien Georgee6479662022-04-08 14:04:21 +10001791 # Create initial list of global qstrs.
1792 global_qstrs = GlobalQStrList()
1793
Damien Georgef2040bf2021-10-22 22:22:47 +11001794 # Load all .mpy files.
1795 try:
1796 compiled_modules = [read_mpy(file) for file in args.files]
1797 except MPYReadError as er:
1798 print(er, file=sys.stderr)
1799 sys.exit(1)
Damien George0699c6b2016-01-31 21:45:22 +00001800
Damien Georgef2040bf2021-10-22 22:22:47 +11001801 if args.hexdump:
1802 hexdump_mpy(compiled_modules)
1803
1804 if args.disassemble:
1805 if args.hexdump:
1806 print()
1807 disassemble_mpy(compiled_modules)
1808
1809 if args.freeze:
Damien George0699c6b2016-01-31 21:45:22 +00001810 try:
Damien Georgef2040bf2021-10-22 22:22:47 +11001811 freeze_mpy(base_qstrs, compiled_modules)
Damien George0699c6b2016-01-31 21:45:22 +00001812 except FreezeError as er:
1813 print(er, file=sys.stderr)
1814 sys.exit(1)
Damien Georgef2040bf2021-10-22 22:22:47 +11001815
1816 if args.merge:
1817 merge_mpy(compiled_modules, args.output)
Damien George0699c6b2016-01-31 21:45:22 +00001818
Damien George69661f32020-02-27 15:36:53 +11001819
1820if __name__ == "__main__":
Damien George0699c6b2016-01-31 21:45:22 +00001821 main()