blob: 337f580b6e5b08170cc56f4aa752e5571eab425b [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 Georgec3beb162016-04-15 11:56:10 +010032 str_cons = lambda val, enc=None: val
33 bytes_cons = lambda val, enc=None: bytearray(val)
34 is_str_type = lambda o: type(o) is str
35 is_bytes_type = lambda o: type(o) is bytearray
36 is_int_type = lambda o: type(o) is int or type(o) is long
37else:
38 str_cons = str
39 bytes_cons = bytes
40 is_str_type = lambda o: type(o) is str
41 is_bytes_type = lambda o: type(o) is bytes
42 is_int_type = lambda o: type(o) is int
43# end compatibility code
44
Damien George0699c6b2016-01-31 21:45:22 +000045import sys
Damien George72ae3c72016-08-10 13:26:11 +100046import struct
Damien George0699c6b2016-01-31 21:45:22 +000047from collections import namedtuple
48
Damien George69661f32020-02-27 15:36:53 +110049sys.path.append(sys.path[0] + "/../py")
Damien George0699c6b2016-01-31 21:45:22 +000050import makeqstrdata as qstrutil
51
Damien George69661f32020-02-27 15:36:53 +110052
Damien George0699c6b2016-01-31 21:45:22 +000053class FreezeError(Exception):
54 def __init__(self, rawcode, msg):
55 self.rawcode = rawcode
56 self.msg = msg
57
58 def __str__(self):
Damien George69661f32020-02-27 15:36:53 +110059 return "error while freezing %s: %s" % (self.rawcode.source_file, self.msg)
60
Damien George0699c6b2016-01-31 21:45:22 +000061
62class Config:
Damien George5716c5c2019-09-26 16:39:37 +100063 MPY_VERSION = 5
Damien George0699c6b2016-01-31 21:45:22 +000064 MICROPY_LONGINT_IMPL_NONE = 0
65 MICROPY_LONGINT_IMPL_LONGLONG = 1
66 MICROPY_LONGINT_IMPL_MPZ = 2
Damien George69661f32020-02-27 15:36:53 +110067
68
Damien George0699c6b2016-01-31 21:45:22 +000069config = Config()
70
Damien George69661f32020-02-27 15:36:53 +110071
Damien George4f0931b2019-03-01 14:33:03 +110072class QStrType:
73 def __init__(self, str):
74 self.str = str
75 self.qstr_esc = qstrutil.qstr_escape(self.str)
Damien George69661f32020-02-27 15:36:53 +110076 self.qstr_id = "MP_QSTR_" + self.qstr_esc
77
Damien George4f0931b2019-03-01 14:33:03 +110078
79# Initialise global list of qstrs with static qstrs
Damien George69661f32020-02-27 15:36:53 +110080global_qstrs = [None] # MP_QSTRnull should never be referenced
Damien George4f0931b2019-03-01 14:33:03 +110081for n in qstrutil.static_qstr_list:
82 global_qstrs.append(QStrType(n))
83
Damien George69661f32020-02-27 15:36:53 +110084
Damien George5996eeb2019-02-25 23:15:51 +110085class QStrWindow:
Damien George74ed0682019-04-08 15:20:56 +100086 def __init__(self, size):
Damien George5996eeb2019-02-25 23:15:51 +110087 self.window = []
Damien George74ed0682019-04-08 15:20:56 +100088 self.size = size
Damien George5996eeb2019-02-25 23:15:51 +110089
90 def push(self, val):
Damien George69661f32020-02-27 15:36:53 +110091 self.window = [val] + self.window[: self.size - 1]
Damien George5996eeb2019-02-25 23:15:51 +110092
93 def access(self, idx):
94 val = self.window[idx]
Damien George69661f32020-02-27 15:36:53 +110095 self.window = [val] + self.window[:idx] + self.window[idx + 1 :]
Damien George5996eeb2019-02-25 23:15:51 +110096 return val
97
Damien George69661f32020-02-27 15:36:53 +110098
Damien Georgeea3c80a2019-02-21 15:18:59 +110099MP_CODE_BYTECODE = 2
100MP_CODE_NATIVE_PY = 3
101MP_CODE_NATIVE_VIPER = 4
102MP_CODE_NATIVE_ASM = 5
103
104MP_NATIVE_ARCH_NONE = 0
105MP_NATIVE_ARCH_X86 = 1
106MP_NATIVE_ARCH_X64 = 2
107MP_NATIVE_ARCH_ARMV6 = 3
108MP_NATIVE_ARCH_ARMV6M = 4
109MP_NATIVE_ARCH_ARMV7M = 5
110MP_NATIVE_ARCH_ARMV7EM = 6
111MP_NATIVE_ARCH_ARMV7EMSP = 7
112MP_NATIVE_ARCH_ARMV7EMDP = 8
113MP_NATIVE_ARCH_XTENSA = 9
Damien George9adedce2019-09-13 13:15:12 +1000114MP_NATIVE_ARCH_XTENSAWIN = 10
Damien Georgeea3c80a2019-02-21 15:18:59 +1100115
Damien George69661f32020-02-27 15:36:53 +1100116MP_BC_MASK_EXTRA_BYTE = 0x9E
Damien George0699c6b2016-01-31 21:45:22 +0000117
Damien George1f7202d2019-09-02 21:35:26 +1000118MP_BC_FORMAT_BYTE = 0
119MP_BC_FORMAT_QSTR = 1
120MP_BC_FORMAT_VAR_UINT = 2
121MP_BC_FORMAT_OFFSET = 3
122
Damien George0699c6b2016-01-31 21:45:22 +0000123# extra byte if caching enabled:
Damien George5889cf52019-09-02 20:24:01 +1000124MP_BC_LOAD_NAME = 0x11
125MP_BC_LOAD_GLOBAL = 0x12
126MP_BC_LOAD_ATTR = 0x13
127MP_BC_STORE_ATTR = 0x18
Damien George0699c6b2016-01-31 21:45:22 +0000128
Damien George0699c6b2016-01-31 21:45:22 +0000129# this function mirrors that in py/bc.c
Damien George1f7202d2019-09-02 21:35:26 +1000130def mp_opcode_format(bytecode, ip, count_var_uint):
Damien George0699c6b2016-01-31 21:45:22 +0000131 opcode = bytecode[ip]
132 ip_start = ip
Damien George69661f32020-02-27 15:36:53 +1100133 f = (0x000003A4 >> (2 * ((opcode) >> 4))) & 3
Damien George1f7202d2019-09-02 21:35:26 +1000134 if f == MP_BC_FORMAT_QSTR:
Damien George0699c6b2016-01-31 21:45:22 +0000135 ip += 3
136 else:
Damien George1f7202d2019-09-02 21:35:26 +1000137 extra_byte = (opcode & MP_BC_MASK_EXTRA_BYTE) == 0
Damien George0699c6b2016-01-31 21:45:22 +0000138 ip += 1
Damien George1f7202d2019-09-02 21:35:26 +1000139 if f == MP_BC_FORMAT_VAR_UINT:
Damien George992a6e12019-03-01 14:03:10 +1100140 if count_var_uint:
141 while bytecode[ip] & 0x80 != 0:
142 ip += 1
Damien George0699c6b2016-01-31 21:45:22 +0000143 ip += 1
Damien George1f7202d2019-09-02 21:35:26 +1000144 elif f == MP_BC_FORMAT_OFFSET:
Damien George0699c6b2016-01-31 21:45:22 +0000145 ip += 2
146 ip += extra_byte
147 return f, ip - ip_start
148
Damien George69661f32020-02-27 15:36:53 +1100149
Damien Georgeb5ebfad2019-09-16 22:12:59 +1000150def read_prelude_sig(read_byte):
151 z = read_byte()
152 # xSSSSEAA
Damien George69661f32020-02-27 15:36:53 +1100153 S = (z >> 3) & 0xF
Damien Georgeb5ebfad2019-09-16 22:12:59 +1000154 E = (z >> 2) & 0x1
155 F = 0
156 A = z & 0x3
157 K = 0
158 D = 0
159 n = 0
160 while z & 0x80:
161 z = read_byte()
162 # xFSSKAED
163 S |= (z & 0x30) << (2 * n)
164 E |= (z & 0x02) << n
165 F |= ((z & 0x40) >> 6) << n
166 A |= (z & 0x4) << n
167 K |= ((z & 0x08) >> 3) << n
168 D |= (z & 0x1) << n
169 n += 1
170 S += 1
171 return S, E, F, A, K, D
172
Damien George69661f32020-02-27 15:36:53 +1100173
Damien Georgec8c0fd42019-09-25 15:45:47 +1000174def read_prelude_size(read_byte):
175 I = 0
176 C = 0
177 n = 0
178 while True:
179 z = read_byte()
180 # xIIIIIIC
Damien George69661f32020-02-27 15:36:53 +1100181 I |= ((z & 0x7E) >> 1) << (6 * n)
Damien Georgec8c0fd42019-09-25 15:45:47 +1000182 C |= (z & 1) << n
183 if not (z & 0x80):
184 break
185 n += 1
186 return I, C
187
Damien George69661f32020-02-27 15:36:53 +1100188
Damien Georgeea3c80a2019-02-21 15:18:59 +1100189def extract_prelude(bytecode, ip):
Damien Georgeb5ebfad2019-09-16 22:12:59 +1000190 def local_read_byte():
191 b = bytecode[ip_ref[0]]
192 ip_ref[0] += 1
193 return b
Damien George69661f32020-02-27 15:36:53 +1100194
195 ip_ref = [ip] # to close over ip in Python 2 and 3
196 (
197 n_state,
198 n_exc_stack,
199 scope_flags,
200 n_pos_args,
201 n_kwonly_args,
202 n_def_pos_args,
203 ) = read_prelude_sig(local_read_byte)
Damien Georgec8c0fd42019-09-25 15:45:47 +1000204 n_info, n_cell = read_prelude_size(local_read_byte)
Damien Georgeb5ebfad2019-09-16 22:12:59 +1000205 ip = ip_ref[0]
206
Damien Georgec8c0fd42019-09-25 15:45:47 +1000207 ip2 = ip
208 ip = ip2 + n_info + n_cell
Damien George0699c6b2016-01-31 21:45:22 +0000209 # ip now points to first opcode
210 # ip2 points to simple_name qstr
Damien Georgec8c0fd42019-09-25 15:45:47 +1000211 return ip, ip2, (n_state, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args)
Damien George0699c6b2016-01-31 21:45:22 +0000212
Damien George69661f32020-02-27 15:36:53 +1100213
Damien Georgeea3c80a2019-02-21 15:18:59 +1100214class MPFunTable:
215 pass
216
Damien George69661f32020-02-27 15:36:53 +1100217
Damien George643d2a02019-04-08 11:21:18 +1000218class RawCode(object):
Damien George02fd83b2016-05-03 12:24:39 +0100219 # a set of all escaped names, to make sure they are unique
220 escaped_names = set()
221
Damien Georgeea3c80a2019-02-21 15:18:59 +1100222 # convert code kind number to string
223 code_kind_str = {
Damien George69661f32020-02-27 15:36:53 +1100224 MP_CODE_BYTECODE: "MP_CODE_BYTECODE",
225 MP_CODE_NATIVE_PY: "MP_CODE_NATIVE_PY",
226 MP_CODE_NATIVE_VIPER: "MP_CODE_NATIVE_VIPER",
227 MP_CODE_NATIVE_ASM: "MP_CODE_NATIVE_ASM",
Damien Georgeea3c80a2019-02-21 15:18:59 +1100228 }
229
230 def __init__(self, code_kind, bytecode, prelude_offset, qstrs, objs, raw_codes):
Damien George0699c6b2016-01-31 21:45:22 +0000231 # set core variables
Damien Georgeea3c80a2019-02-21 15:18:59 +1100232 self.code_kind = code_kind
Damien George0699c6b2016-01-31 21:45:22 +0000233 self.bytecode = bytecode
Damien Georgeea3c80a2019-02-21 15:18:59 +1100234 self.prelude_offset = prelude_offset
Damien George0699c6b2016-01-31 21:45:22 +0000235 self.qstrs = qstrs
236 self.objs = objs
237 self.raw_codes = raw_codes
238
Damien Georgeea3c80a2019-02-21 15:18:59 +1100239 if self.prelude_offset is None:
240 # no prelude, assign a dummy simple_name
241 self.prelude_offset = 0
242 self.simple_name = global_qstrs[1]
243 else:
244 # extract prelude
245 self.ip, self.ip2, self.prelude = extract_prelude(self.bytecode, self.prelude_offset)
246 self.simple_name = self._unpack_qstr(self.ip2)
247 self.source_file = self._unpack_qstr(self.ip2 + 2)
Martin Milata492cf342020-08-13 15:20:08 +0200248 self.line_info_offset = self.ip2 + 4
Damien George0699c6b2016-01-31 21:45:22 +0000249
250 def _unpack_qstr(self, ip):
251 qst = self.bytecode[ip] | self.bytecode[ip + 1] << 8
252 return global_qstrs[qst]
253
254 def dump(self):
255 # dump children first
256 for rc in self.raw_codes:
Damien George69661f32020-02-27 15:36:53 +1100257 rc.freeze("")
Damien George0699c6b2016-01-31 21:45:22 +0000258 # TODO
259
Damien Georgeea3c80a2019-02-21 15:18:59 +1100260 def freeze_children(self, parent_name):
Damien George0699c6b2016-01-31 21:45:22 +0000261 self.escaped_name = parent_name + self.simple_name.qstr_esc
262
Damien George02fd83b2016-05-03 12:24:39 +0100263 # make sure the escaped name is unique
264 i = 2
265 while self.escaped_name in RawCode.escaped_names:
266 self.escaped_name = parent_name + self.simple_name.qstr_esc + str(i)
267 i += 1
268 RawCode.escaped_names.add(self.escaped_name)
269
Damien George0699c6b2016-01-31 21:45:22 +0000270 # emit children first
271 for rc in self.raw_codes:
Damien George69661f32020-02-27 15:36:53 +1100272 rc.freeze(self.escaped_name + "_")
Damien George0699c6b2016-01-31 21:45:22 +0000273
Damien Georgeea3c80a2019-02-21 15:18:59 +1100274 def freeze_constants(self):
Damien George0699c6b2016-01-31 21:45:22 +0000275 # generate constant objects
276 for i, obj in enumerate(self.objs):
Damien George69661f32020-02-27 15:36:53 +1100277 obj_name = "const_obj_%s_%u" % (self.escaped_name, i)
Damien Georgeea3c80a2019-02-21 15:18:59 +1100278 if obj is MPFunTable:
279 pass
280 elif obj is Ellipsis:
Damien George69661f32020-02-27 15:36:53 +1100281 print("#define %s mp_const_ellipsis_obj" % obj_name)
Damien George9ba3de62017-11-15 12:46:08 +1100282 elif is_str_type(obj) or is_bytes_type(obj):
Damien Georgeb6bdf182016-09-02 15:10:45 +1000283 if is_str_type(obj):
Damien George69661f32020-02-27 15:36:53 +1100284 obj = bytes_cons(obj, "utf8")
285 obj_type = "mp_type_str"
Damien Georgeb6bdf182016-09-02 15:10:45 +1000286 else:
Damien George69661f32020-02-27 15:36:53 +1100287 obj_type = "mp_type_bytes"
288 print(
289 'STATIC const mp_obj_str_t %s = {{&%s}, %u, %u, (const byte*)"%s"};'
290 % (
291 obj_name,
292 obj_type,
293 qstrutil.compute_hash(obj, config.MICROPY_QSTR_BYTES_IN_HASH),
294 len(obj),
295 "".join(("\\x%02x" % b) for b in obj),
296 )
297 )
Damien Georgec3beb162016-04-15 11:56:10 +0100298 elif is_int_type(obj):
Damien George0699c6b2016-01-31 21:45:22 +0000299 if config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_NONE:
300 # TODO check if we can actually fit this long-int into a small-int
Damien George69661f32020-02-27 15:36:53 +1100301 raise FreezeError(self, "target does not support long int")
Damien George0699c6b2016-01-31 21:45:22 +0000302 elif config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_LONGLONG:
303 # TODO
Damien George69661f32020-02-27 15:36:53 +1100304 raise FreezeError(self, "freezing int to long-long is not implemented")
Damien George0699c6b2016-01-31 21:45:22 +0000305 elif config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_MPZ:
306 neg = 0
307 if obj < 0:
308 obj = -obj
309 neg = 1
310 bits_per_dig = config.MPZ_DIG_SIZE
311 digs = []
312 z = obj
313 while z:
314 digs.append(z & ((1 << bits_per_dig) - 1))
315 z >>= bits_per_dig
316 ndigs = len(digs)
Damien George69661f32020-02-27 15:36:53 +1100317 digs = ",".join(("%#x" % d) for d in digs)
318 print(
319 "STATIC const mp_obj_int_t %s = {{&mp_type_int}, "
320 "{.neg=%u, .fixed_dig=1, .alloc=%u, .len=%u, .dig=(uint%u_t*)(const uint%u_t[]){%s}}};"
321 % (obj_name, neg, ndigs, ndigs, bits_per_dig, bits_per_dig, digs)
322 )
Damien George0699c6b2016-01-31 21:45:22 +0000323 elif type(obj) is float:
Damien George69661f32020-02-27 15:36:53 +1100324 print(
325 "#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B"
326 )
327 print(
stijnbcf01d12020-03-31 14:48:08 +0200328 "STATIC const mp_obj_float_t %s = {{&mp_type_float}, (mp_float_t)%.16g};"
329 % (obj_name, obj)
Damien George69661f32020-02-27 15:36:53 +1100330 )
331 print("#endif")
Damien Georgec51c8832016-09-03 00:19:02 +1000332 elif type(obj) is complex:
Damien George69661f32020-02-27 15:36:53 +1100333 print(
stijnbcf01d12020-03-31 14:48:08 +0200334 "STATIC const mp_obj_complex_t %s = {{&mp_type_complex}, (mp_float_t)%.16g, (mp_float_t)%.16g};"
Damien George69661f32020-02-27 15:36:53 +1100335 % (obj_name, obj.real, obj.imag)
336 )
Damien George0699c6b2016-01-31 21:45:22 +0000337 else:
Damien George69661f32020-02-27 15:36:53 +1100338 raise FreezeError(self, "freezing of object %r is not implemented" % (obj,))
Damien George0699c6b2016-01-31 21:45:22 +0000339
Damien Georgeb6a32892017-08-12 22:26:18 +1000340 # generate constant table, if it has any entries
341 const_table_len = len(self.qstrs) + len(self.objs) + len(self.raw_codes)
342 if const_table_len:
Damien George69661f32020-02-27 15:36:53 +1100343 print(
344 "STATIC const mp_rom_obj_t const_table_data_%s[%u] = {"
345 % (self.escaped_name, const_table_len)
346 )
Damien Georgeb6a32892017-08-12 22:26:18 +1000347 for qst in self.qstrs:
Damien George69661f32020-02-27 15:36:53 +1100348 print(" MP_ROM_QSTR(%s)," % global_qstrs[qst].qstr_id)
Damien Georgeb6a32892017-08-12 22:26:18 +1000349 for i in range(len(self.objs)):
Damien Georgeea3c80a2019-02-21 15:18:59 +1100350 if self.objs[i] is MPFunTable:
Damien George69661f32020-02-27 15:36:53 +1100351 print(" &mp_fun_table,")
Damien Georgeea3c80a2019-02-21 15:18:59 +1100352 elif type(self.objs[i]) is float:
Damien George69661f32020-02-27 15:36:53 +1100353 print(
354 "#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B"
355 )
356 print(" MP_ROM_PTR(&const_obj_%s_%u)," % (self.escaped_name, i))
357 print("#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C")
358 n = struct.unpack("<I", struct.pack("<f", self.objs[i]))[0]
Damien Georgeb6a32892017-08-12 22:26:18 +1000359 n = ((n & ~0x3) | 2) + 0x80800000
Damien George69661f32020-02-27 15:36:53 +1100360 print(" (mp_rom_obj_t)(0x%08x)," % (n,))
361 print("#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D")
362 n = struct.unpack("<Q", struct.pack("<d", self.objs[i]))[0]
Damien George929d10a2018-07-09 12:22:40 +1000363 n += 0x8004000000000000
Damien George69661f32020-02-27 15:36:53 +1100364 print(" (mp_rom_obj_t)(0x%016x)," % (n,))
365 print("#endif")
Damien Georgeb6a32892017-08-12 22:26:18 +1000366 else:
Damien George69661f32020-02-27 15:36:53 +1100367 print(" MP_ROM_PTR(&const_obj_%s_%u)," % (self.escaped_name, i))
Damien Georgeb6a32892017-08-12 22:26:18 +1000368 for rc in self.raw_codes:
Damien George69661f32020-02-27 15:36:53 +1100369 print(" MP_ROM_PTR(&raw_code_%s)," % rc.escaped_name)
370 print("};")
Damien George0699c6b2016-01-31 21:45:22 +0000371
Damien Georgeea3c80a2019-02-21 15:18:59 +1100372 def freeze_module(self, qstr_links=(), type_sig=0):
Damien George0699c6b2016-01-31 21:45:22 +0000373 # generate module
Damien George69661f32020-02-27 15:36:53 +1100374 if self.simple_name.str != "<module>":
375 print("STATIC ", end="")
376 print("const mp_raw_code_t raw_code_%s = {" % self.escaped_name)
377 print(" .kind = %s," % RawCode.code_kind_str[self.code_kind])
378 print(" .scope_flags = 0x%02x," % self.prelude[2])
379 print(" .n_pos_args = %u," % self.prelude[3])
380 print(" .fun_data = fun_data_%s," % self.escaped_name)
Damien Georgeea3c80a2019-02-21 15:18:59 +1100381 if len(self.qstrs) + len(self.objs) + len(self.raw_codes):
Damien George69661f32020-02-27 15:36:53 +1100382 print(" .const_table = (mp_uint_t*)const_table_data_%s," % self.escaped_name)
Damien Georgeb6a32892017-08-12 22:26:18 +1000383 else:
Damien George69661f32020-02-27 15:36:53 +1100384 print(" .const_table = NULL,")
385 print(" #if MICROPY_PERSISTENT_CODE_SAVE")
386 print(" .fun_data_len = %u," % len(self.bytecode))
387 print(" .n_obj = %u," % len(self.objs))
388 print(" .n_raw_code = %u," % len(self.raw_codes))
Damien Georgec69f58e2019-09-06 23:55:15 +1000389 if self.code_kind == MP_CODE_BYTECODE:
Damien George69661f32020-02-27 15:36:53 +1100390 print(" #if MICROPY_PY_SYS_SETTRACE")
391 print(" .prelude = {")
392 print(" .n_state = %u," % self.prelude[0])
393 print(" .n_exc_stack = %u," % self.prelude[1])
394 print(" .scope_flags = %u," % self.prelude[2])
395 print(" .n_pos_args = %u," % self.prelude[3])
396 print(" .n_kwonly_args = %u," % self.prelude[4])
397 print(" .n_def_pos_args = %u," % self.prelude[5])
398 print(" .qstr_block_name = %s," % self.simple_name.qstr_id)
399 print(" .qstr_source_file = %s," % self.source_file.qstr_id)
Martin Milata492cf342020-08-13 15:20:08 +0200400 print(
401 " .line_info = fun_data_%s + %u,"
402 % (self.escaped_name, self.line_info_offset)
403 )
Damien George69661f32020-02-27 15:36:53 +1100404 print(" .opcodes = fun_data_%s + %u," % (self.escaped_name, self.ip))
405 print(" },")
406 print(" .line_of_definition = %u," % 0) # TODO
407 print(" #endif")
408 print(" #if MICROPY_EMIT_MACHINE_CODE")
409 print(" .prelude_offset = %u," % self.prelude_offset)
410 print(" .n_qstr = %u," % len(qstr_links))
411 print(" .qstr_link = NULL,") # TODO
412 print(" #endif")
413 print(" #endif")
414 print(" #if MICROPY_EMIT_MACHINE_CODE")
415 print(" .type_sig = %u," % type_sig)
416 print(" #endif")
417 print("};")
418
Damien George0699c6b2016-01-31 21:45:22 +0000419
Damien Georgeea3c80a2019-02-21 15:18:59 +1100420class RawCodeBytecode(RawCode):
421 def __init__(self, bytecode, qstrs, objs, raw_codes):
Damien George69661f32020-02-27 15:36:53 +1100422 super(RawCodeBytecode, self).__init__(
423 MP_CODE_BYTECODE, bytecode, 0, qstrs, objs, raw_codes
424 )
Damien Georgeea3c80a2019-02-21 15:18:59 +1100425
426 def freeze(self, parent_name):
427 self.freeze_children(parent_name)
428
429 # generate bytecode data
430 print()
Damien George69661f32020-02-27 15:36:53 +1100431 print(
432 "// frozen bytecode for file %s, scope %s%s"
433 % (self.source_file.str, parent_name, self.simple_name.str)
434 )
Jim Mussaredb326edf2021-09-06 12:28:06 +1000435 print("STATIC const byte fun_data_%s[%u] = {" % (self.escaped_name, len(self.bytecode)))
Damien George69661f32020-02-27 15:36:53 +1100436 print(" ", end="")
Damien Georgeea3c80a2019-02-21 15:18:59 +1100437 for i in range(self.ip2):
Damien George69661f32020-02-27 15:36:53 +1100438 print(" 0x%02x," % self.bytecode[i], end="")
Damien Georgeea3c80a2019-02-21 15:18:59 +1100439 print()
Damien George69661f32020-02-27 15:36:53 +1100440 print(" ", self.simple_name.qstr_id, "& 0xff,", self.simple_name.qstr_id, ">> 8,")
441 print(" ", self.source_file.qstr_id, "& 0xff,", self.source_file.qstr_id, ">> 8,")
442 print(" ", end="")
Damien Georgeea3c80a2019-02-21 15:18:59 +1100443 for i in range(self.ip2 + 4, self.ip):
Damien George69661f32020-02-27 15:36:53 +1100444 print(" 0x%02x," % self.bytecode[i], end="")
Damien Georgeea3c80a2019-02-21 15:18:59 +1100445 print()
446 ip = self.ip
447 while ip < len(self.bytecode):
448 f, sz = mp_opcode_format(self.bytecode, ip, True)
449 if f == 1:
450 qst = self._unpack_qstr(ip + 1).qstr_id
Damien George69661f32020-02-27 15:36:53 +1100451 extra = "" if sz == 3 else " 0x%02x," % self.bytecode[ip + 3]
452 print(" ", "0x%02x," % self.bytecode[ip], qst, "& 0xff,", qst, ">> 8,", extra)
Damien Georgeea3c80a2019-02-21 15:18:59 +1100453 else:
Damien George69661f32020-02-27 15:36:53 +1100454 print(" ", "".join("0x%02x, " % self.bytecode[ip + i] for i in range(sz)))
Damien Georgeea3c80a2019-02-21 15:18:59 +1100455 ip += sz
Damien George69661f32020-02-27 15:36:53 +1100456 print("};")
Damien Georgeea3c80a2019-02-21 15:18:59 +1100457
458 self.freeze_constants()
459 self.freeze_module()
460
Damien George69661f32020-02-27 15:36:53 +1100461
Damien Georgeea3c80a2019-02-21 15:18:59 +1100462class RawCodeNative(RawCode):
Damien George69661f32020-02-27 15:36:53 +1100463 def __init__(
464 self,
465 code_kind,
466 fun_data,
467 prelude_offset,
468 prelude,
469 qstr_links,
470 qstrs,
471 objs,
472 raw_codes,
473 type_sig,
474 ):
475 super(RawCodeNative, self).__init__(
476 code_kind, fun_data, prelude_offset, qstrs, objs, raw_codes
477 )
Damien Georgeea3c80a2019-02-21 15:18:59 +1100478 self.prelude = prelude
479 self.qstr_links = qstr_links
480 self.type_sig = type_sig
Damien George69661f32020-02-27 15:36:53 +1100481 if config.native_arch in (
482 MP_NATIVE_ARCH_X86,
483 MP_NATIVE_ARCH_X64,
484 MP_NATIVE_ARCH_XTENSA,
485 MP_NATIVE_ARCH_XTENSAWIN,
486 ):
Damien Georgeea3c80a2019-02-21 15:18:59 +1100487 self.fun_data_attributes = '__attribute__((section(".text,\\"ax\\",@progbits # ")))'
488 else:
489 self.fun_data_attributes = '__attribute__((section(".text,\\"ax\\",%progbits @ ")))'
490
Damien George7f24c292019-11-28 13:11:51 +1100491 # Allow single-byte alignment by default for x86/x64.
492 # ARM needs word alignment, ARM Thumb needs halfword, due to instruction size.
493 # Xtensa needs word alignment due to the 32-bit constant table embedded in the code.
Damien George69661f32020-02-27 15:36:53 +1100494 if config.native_arch in (
495 MP_NATIVE_ARCH_ARMV6,
496 MP_NATIVE_ARCH_XTENSA,
497 MP_NATIVE_ARCH_XTENSAWIN,
498 ):
Damien George7f24c292019-11-28 13:11:51 +1100499 # ARMV6 or Xtensa -- four byte align.
Damien George69661f32020-02-27 15:36:53 +1100500 self.fun_data_attributes += " __attribute__ ((aligned (4)))"
Jim Mussared4ab51562019-08-17 00:32:04 +1000501 elif MP_NATIVE_ARCH_ARMV6M <= config.native_arch <= MP_NATIVE_ARCH_ARMV7EMDP:
502 # ARMVxxM -- two byte align.
Damien George69661f32020-02-27 15:36:53 +1100503 self.fun_data_attributes += " __attribute__ ((aligned (2)))"
Jim Mussared4ab51562019-08-17 00:32:04 +1000504
Damien Georgeea3c80a2019-02-21 15:18:59 +1100505 def _asm_thumb_rewrite_mov(self, pc, val):
Damien George69661f32020-02-27 15:36:53 +1100506 print(" (%u & 0xf0) | (%s >> 12)," % (self.bytecode[pc], val), end="")
507 print(" (%u & 0xfb) | (%s >> 9 & 0x04)," % (self.bytecode[pc + 1], val), end="")
508 print(" (%s & 0xff)," % (val,), end="")
509 print(" (%u & 0x07) | (%s >> 4 & 0x70)," % (self.bytecode[pc + 3], val))
Damien Georgeea3c80a2019-02-21 15:18:59 +1100510
511 def _link_qstr(self, pc, kind, qst):
512 if kind == 0:
Damien Georgefaf3d3e2019-06-04 22:13:32 +1000513 # Generic 16-bit link
Damien George69661f32020-02-27 15:36:53 +1100514 print(" %s & 0xff, %s >> 8," % (qst, qst))
Damien George9d3031c2019-06-11 11:36:39 +1000515 return 2
Damien Georgeea3c80a2019-02-21 15:18:59 +1100516 else:
Damien Georgefaf3d3e2019-06-04 22:13:32 +1000517 # Architecture-specific link
518 is_obj = kind == 2
519 if is_obj:
Damien George69661f32020-02-27 15:36:53 +1100520 qst = "((uintptr_t)MP_OBJ_NEW_QSTR(%s))" % qst
Damien George7f24c292019-11-28 13:11:51 +1100521 if config.native_arch in (
Damien George69661f32020-02-27 15:36:53 +1100522 MP_NATIVE_ARCH_X86,
523 MP_NATIVE_ARCH_X64,
Damien George2c1a6a22021-05-25 22:16:06 +1000524 MP_NATIVE_ARCH_ARMV6,
Damien George69661f32020-02-27 15:36:53 +1100525 MP_NATIVE_ARCH_XTENSA,
526 MP_NATIVE_ARCH_XTENSAWIN,
527 ):
528 print(
529 " %s & 0xff, (%s >> 8) & 0xff, (%s >> 16) & 0xff, %s >> 24,"
530 % (qst, qst, qst, qst)
531 )
Damien George9d3031c2019-06-11 11:36:39 +1000532 return 4
Damien Georgeea3c80a2019-02-21 15:18:59 +1100533 elif MP_NATIVE_ARCH_ARMV6M <= config.native_arch <= MP_NATIVE_ARCH_ARMV7EMDP:
534 if is_obj:
Damien Georgefaf3d3e2019-06-04 22:13:32 +1000535 # qstr object, movw and movt
536 self._asm_thumb_rewrite_mov(pc, qst)
Damien George69661f32020-02-27 15:36:53 +1100537 self._asm_thumb_rewrite_mov(pc + 4, "(%s >> 16)" % qst)
Damien George9d3031c2019-06-11 11:36:39 +1000538 return 8
Damien Georgeea3c80a2019-02-21 15:18:59 +1100539 else:
Damien Georgefaf3d3e2019-06-04 22:13:32 +1000540 # qstr number, movw instruction
541 self._asm_thumb_rewrite_mov(pc, qst)
Damien George9d3031c2019-06-11 11:36:39 +1000542 return 4
Damien Georgeea3c80a2019-02-21 15:18:59 +1100543 else:
544 assert 0
545
546 def freeze(self, parent_name):
Damien George69661f32020-02-27 15:36:53 +1100547 if self.prelude[2] & ~0x0F:
548 raise FreezeError("unable to freeze code with relocations")
Damien Georgefc97d6d2019-12-10 14:57:12 +1100549
Damien Georgeea3c80a2019-02-21 15:18:59 +1100550 self.freeze_children(parent_name)
551
552 # generate native code data
553 print()
554 if self.code_kind == MP_CODE_NATIVE_PY:
Damien George69661f32020-02-27 15:36:53 +1100555 print(
556 "// frozen native code for file %s, scope %s%s"
557 % (self.source_file.str, parent_name, self.simple_name.str)
558 )
Damien Georgeea3c80a2019-02-21 15:18:59 +1100559 elif self.code_kind == MP_CODE_NATIVE_VIPER:
Damien George69661f32020-02-27 15:36:53 +1100560 print("// frozen viper code for scope %s" % (parent_name,))
Damien Georgeea3c80a2019-02-21 15:18:59 +1100561 else:
Damien George69661f32020-02-27 15:36:53 +1100562 print("// frozen assembler code for scope %s" % (parent_name,))
563 print(
564 "STATIC const byte fun_data_%s[%u] %s = {"
565 % (self.escaped_name, len(self.bytecode), self.fun_data_attributes)
566 )
Damien Georgeea3c80a2019-02-21 15:18:59 +1100567
568 if self.code_kind == MP_CODE_NATIVE_PY:
569 i_top = self.prelude_offset
570 else:
571 i_top = len(self.bytecode)
572 i = 0
573 qi = 0
574 while i < i_top:
575 if qi < len(self.qstr_links) and i == self.qstr_links[qi][0]:
576 # link qstr
577 qi_off, qi_kind, qi_val = self.qstr_links[qi]
578 qst = global_qstrs[qi_val].qstr_id
Damien George9d3031c2019-06-11 11:36:39 +1000579 i += self._link_qstr(i, qi_kind, qst)
Damien Georgeea3c80a2019-02-21 15:18:59 +1100580 qi += 1
581 else:
582 # copy machine code (max 16 bytes)
583 i16 = min(i + 16, i_top)
584 if qi < len(self.qstr_links):
585 i16 = min(i16, self.qstr_links[qi][0])
Damien George69661f32020-02-27 15:36:53 +1100586 print(" ", end="")
Damien Georgeea3c80a2019-02-21 15:18:59 +1100587 for ii in range(i, i16):
Damien George69661f32020-02-27 15:36:53 +1100588 print(" 0x%02x," % self.bytecode[ii], end="")
Damien Georgeea3c80a2019-02-21 15:18:59 +1100589 print()
590 i = i16
591
592 if self.code_kind == MP_CODE_NATIVE_PY:
Damien George69661f32020-02-27 15:36:53 +1100593 print(" ", end="")
Damien Georgeea3c80a2019-02-21 15:18:59 +1100594 for i in range(self.prelude_offset, self.ip2):
Damien George69661f32020-02-27 15:36:53 +1100595 print(" 0x%02x," % self.bytecode[i], end="")
Damien Georgeea3c80a2019-02-21 15:18:59 +1100596 print()
597
Damien George69661f32020-02-27 15:36:53 +1100598 print(" ", self.simple_name.qstr_id, "& 0xff,", self.simple_name.qstr_id, ">> 8,")
599 print(" ", self.source_file.qstr_id, "& 0xff,", self.source_file.qstr_id, ">> 8,")
Damien Georgeea3c80a2019-02-21 15:18:59 +1100600
Damien George69661f32020-02-27 15:36:53 +1100601 print(" ", end="")
Damien Georgeea3c80a2019-02-21 15:18:59 +1100602 for i in range(self.ip2 + 4, self.ip):
Damien George69661f32020-02-27 15:36:53 +1100603 print(" 0x%02x," % self.bytecode[i], end="")
Damien Georgeea3c80a2019-02-21 15:18:59 +1100604 print()
605
Damien George69661f32020-02-27 15:36:53 +1100606 print("};")
Damien Georgeea3c80a2019-02-21 15:18:59 +1100607
608 self.freeze_constants()
609 self.freeze_module(self.qstr_links, self.type_sig)
610
Damien George69661f32020-02-27 15:36:53 +1100611
Damien George992a6e12019-03-01 14:03:10 +1100612class BytecodeBuffer:
613 def __init__(self, size):
614 self.buf = bytearray(size)
615 self.idx = 0
616
617 def is_full(self):
618 return self.idx == len(self.buf)
619
620 def append(self, b):
621 self.buf[self.idx] = b
622 self.idx += 1
623
Damien George69661f32020-02-27 15:36:53 +1100624
Damien George992a6e12019-03-01 14:03:10 +1100625def read_byte(f, out=None):
626 b = bytes_cons(f.read(1))[0]
627 if out is not None:
628 out.append(b)
629 return b
630
Damien George69661f32020-02-27 15:36:53 +1100631
Damien George992a6e12019-03-01 14:03:10 +1100632def read_uint(f, out=None):
Damien George0699c6b2016-01-31 21:45:22 +0000633 i = 0
634 while True:
Damien George992a6e12019-03-01 14:03:10 +1100635 b = read_byte(f, out)
Damien George69661f32020-02-27 15:36:53 +1100636 i = (i << 7) | (b & 0x7F)
Damien George0699c6b2016-01-31 21:45:22 +0000637 if b & 0x80 == 0:
638 break
639 return i
640
Damien George69661f32020-02-27 15:36:53 +1100641
Damien George5996eeb2019-02-25 23:15:51 +1100642def read_qstr(f, qstr_win):
Damien George0699c6b2016-01-31 21:45:22 +0000643 ln = read_uint(f)
Damien George4f0931b2019-03-01 14:33:03 +1100644 if ln == 0:
645 # static qstr
646 return bytes_cons(f.read(1))[0]
Damien George5996eeb2019-02-25 23:15:51 +1100647 if ln & 1:
648 # qstr in table
649 return qstr_win.access(ln >> 1)
650 ln >>= 1
Damien George69661f32020-02-27 15:36:53 +1100651 data = str_cons(f.read(ln), "utf8")
Damien George4f0931b2019-03-01 14:33:03 +1100652 global_qstrs.append(QStrType(data))
Damien George5996eeb2019-02-25 23:15:51 +1100653 qstr_win.push(len(global_qstrs) - 1)
Damien George0699c6b2016-01-31 21:45:22 +0000654 return len(global_qstrs) - 1
655
Damien George69661f32020-02-27 15:36:53 +1100656
Damien George0699c6b2016-01-31 21:45:22 +0000657def read_obj(f):
658 obj_type = f.read(1)
Damien George69661f32020-02-27 15:36:53 +1100659 if obj_type == b"e":
Damien George0699c6b2016-01-31 21:45:22 +0000660 return Ellipsis
661 else:
662 buf = f.read(read_uint(f))
Damien George69661f32020-02-27 15:36:53 +1100663 if obj_type == b"s":
664 return str_cons(buf, "utf8")
665 elif obj_type == b"b":
Damien Georgec3beb162016-04-15 11:56:10 +0100666 return bytes_cons(buf)
Damien George69661f32020-02-27 15:36:53 +1100667 elif obj_type == b"i":
668 return int(str_cons(buf, "ascii"), 10)
669 elif obj_type == b"f":
670 return float(str_cons(buf, "ascii"))
671 elif obj_type == b"c":
672 return complex(str_cons(buf, "ascii"))
Damien George0699c6b2016-01-31 21:45:22 +0000673 else:
674 assert 0
675
Damien George69661f32020-02-27 15:36:53 +1100676
Damien George23f06912019-10-10 15:30:16 +1100677def read_prelude(f, bytecode, qstr_win):
Damien George69661f32020-02-27 15:36:53 +1100678 (
679 n_state,
680 n_exc_stack,
681 scope_flags,
682 n_pos_args,
683 n_kwonly_args,
684 n_def_pos_args,
685 ) = read_prelude_sig(lambda: read_byte(f, bytecode))
Damien Georgec8c0fd42019-09-25 15:45:47 +1000686 n_info, n_cell = read_prelude_size(lambda: read_byte(f, bytecode))
Damien George69661f32020-02-27 15:36:53 +1100687 read_qstr_and_pack(f, bytecode, qstr_win) # simple_name
688 read_qstr_and_pack(f, bytecode, qstr_win) # source_file
Damien George23f06912019-10-10 15:30:16 +1100689 for _ in range(n_info - 4 + n_cell):
Damien George992a6e12019-03-01 14:03:10 +1100690 read_byte(f, bytecode)
Damien George23f06912019-10-10 15:30:16 +1100691 return n_state, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args
Damien George0699c6b2016-01-31 21:45:22 +0000692
Damien George69661f32020-02-27 15:36:53 +1100693
Damien George992a6e12019-03-01 14:03:10 +1100694def read_qstr_and_pack(f, bytecode, qstr_win):
695 qst = read_qstr(f, qstr_win)
Damien George69661f32020-02-27 15:36:53 +1100696 bytecode.append(qst & 0xFF)
Damien George992a6e12019-03-01 14:03:10 +1100697 bytecode.append(qst >> 8)
698
Damien George69661f32020-02-27 15:36:53 +1100699
Damien George992a6e12019-03-01 14:03:10 +1100700def read_bytecode(file, bytecode, qstr_win):
701 while not bytecode.is_full():
702 op = read_byte(file, bytecode)
703 f, sz = mp_opcode_format(bytecode.buf, bytecode.idx - 1, False)
704 sz -= 1
Damien George1f7202d2019-09-02 21:35:26 +1000705 if f == MP_BC_FORMAT_QSTR:
Damien George992a6e12019-03-01 14:03:10 +1100706 read_qstr_and_pack(file, bytecode, qstr_win)
707 sz -= 2
Damien George1f7202d2019-09-02 21:35:26 +1000708 elif f == MP_BC_FORMAT_VAR_UINT:
Damien George992a6e12019-03-01 14:03:10 +1100709 while read_byte(file, bytecode) & 0x80:
710 pass
711 for _ in range(sz):
712 read_byte(file, bytecode)
Damien George0699c6b2016-01-31 21:45:22 +0000713
Damien George69661f32020-02-27 15:36:53 +1100714
Damien George5996eeb2019-02-25 23:15:51 +1100715def read_raw_code(f, qstr_win):
Damien Georgeea3c80a2019-02-21 15:18:59 +1100716 kind_len = read_uint(f)
717 kind = (kind_len & 3) + MP_CODE_BYTECODE
718 fun_data_len = kind_len >> 2
719 fun_data = BytecodeBuffer(fun_data_len)
720
721 if kind == MP_CODE_BYTECODE:
Damien George23f06912019-10-10 15:30:16 +1100722 prelude = read_prelude(f, fun_data, qstr_win)
Damien Georgeea3c80a2019-02-21 15:18:59 +1100723 read_bytecode(f, fun_data, qstr_win)
724 else:
725 fun_data.buf[:] = f.read(fun_data_len)
726
727 qstr_links = []
728 if kind in (MP_CODE_NATIVE_PY, MP_CODE_NATIVE_VIPER):
729 # load qstr link table
730 n_qstr_link = read_uint(f)
731 for _ in range(n_qstr_link):
Damien Georgefaf3d3e2019-06-04 22:13:32 +1000732 off = read_uint(f)
Damien Georgeea3c80a2019-02-21 15:18:59 +1100733 qst = read_qstr(f, qstr_win)
734 qstr_links.append((off >> 2, off & 3, qst))
735
736 type_sig = 0
737 if kind == MP_CODE_NATIVE_PY:
738 prelude_offset = read_uint(f)
739 _, name_idx, prelude = extract_prelude(fun_data.buf, prelude_offset)
Damien George69661f32020-02-27 15:36:53 +1100740 fun_data.idx = name_idx # rewind to where qstrs are in prelude
741 read_qstr_and_pack(f, fun_data, qstr_win) # simple_name
742 read_qstr_and_pack(f, fun_data, qstr_win) # source_file
Damien Georgeea3c80a2019-02-21 15:18:59 +1100743 else:
744 prelude_offset = None
745 scope_flags = read_uint(f)
746 n_pos_args = 0
747 if kind == MP_CODE_NATIVE_ASM:
748 n_pos_args = read_uint(f)
749 type_sig = read_uint(f)
750 prelude = (None, None, scope_flags, n_pos_args, 0)
751
Damien Georgeea3c80a2019-02-21 15:18:59 +1100752 qstrs = []
753 objs = []
754 raw_codes = []
755 if kind != MP_CODE_NATIVE_ASM:
756 # load constant table
757 n_obj = read_uint(f)
758 n_raw_code = read_uint(f)
759 qstrs = [read_qstr(f, qstr_win) for _ in range(prelude[3] + prelude[4])]
760 if kind != MP_CODE_BYTECODE:
761 objs.append(MPFunTable)
762 objs.extend([read_obj(f) for _ in range(n_obj)])
763 raw_codes = [read_raw_code(f, qstr_win) for _ in range(n_raw_code)]
764
765 if kind == MP_CODE_BYTECODE:
766 return RawCodeBytecode(fun_data.buf, qstrs, objs, raw_codes)
767 else:
Damien George69661f32020-02-27 15:36:53 +1100768 return RawCodeNative(
769 kind,
770 fun_data.buf,
771 prelude_offset,
772 prelude,
773 qstr_links,
774 qstrs,
775 objs,
776 raw_codes,
777 type_sig,
778 )
779
Damien George0699c6b2016-01-31 21:45:22 +0000780
781def read_mpy(filename):
Damien George69661f32020-02-27 15:36:53 +1100782 with open(filename, "rb") as f:
Damien Georgec3beb162016-04-15 11:56:10 +0100783 header = bytes_cons(f.read(4))
Damien George69661f32020-02-27 15:36:53 +1100784 if header[0] != ord("M"):
785 raise Exception("not a valid .mpy file")
Damien George6a110482017-02-17 00:19:34 +1100786 if header[1] != config.MPY_VERSION:
Damien George69661f32020-02-27 15:36:53 +1100787 raise Exception("incompatible .mpy version")
Damien George5996eeb2019-02-25 23:15:51 +1100788 feature_byte = header[2]
789 qw_size = read_uint(f)
Damien George5996eeb2019-02-25 23:15:51 +1100790 config.MICROPY_PY_BUILTINS_STR_UNICODE = (feature_byte & 2) != 0
Damien Georgefaf3d3e2019-06-04 22:13:32 +1000791 mpy_native_arch = feature_byte >> 2
792 if mpy_native_arch != MP_NATIVE_ARCH_NONE:
793 if config.native_arch == MP_NATIVE_ARCH_NONE:
794 config.native_arch = mpy_native_arch
795 elif config.native_arch != mpy_native_arch:
Damien George69661f32020-02-27 15:36:53 +1100796 raise Exception("native architecture mismatch")
Damien George0699c6b2016-01-31 21:45:22 +0000797 config.mp_small_int_bits = header[3]
Damien George5996eeb2019-02-25 23:15:51 +1100798 qstr_win = QStrWindow(qw_size)
Damien George27879842019-10-09 14:23:15 +1100799 rc = read_raw_code(f, qstr_win)
800 rc.mpy_source_file = filename
801 rc.qstr_win_size = qw_size
802 return rc
Damien George0699c6b2016-01-31 21:45:22 +0000803
Damien George69661f32020-02-27 15:36:53 +1100804
Damien George0699c6b2016-01-31 21:45:22 +0000805def dump_mpy(raw_codes):
806 for rc in raw_codes:
807 rc.dump()
808
Damien George69661f32020-02-27 15:36:53 +1100809
Damien Georgeb4790af2016-09-02 15:09:21 +1000810def freeze_mpy(base_qstrs, raw_codes):
Damien George0699c6b2016-01-31 21:45:22 +0000811 # add to qstrs
812 new = {}
813 for q in global_qstrs:
814 # don't add duplicates
Damien George4f0931b2019-03-01 14:33:03 +1100815 if q is None or q.qstr_esc in base_qstrs or q.qstr_esc in new:
Damien George0699c6b2016-01-31 21:45:22 +0000816 continue
Artyom Skrobov18b1ba02021-05-03 14:17:36 -0400817 new[q.qstr_esc] = (len(new), q.qstr_esc, q.str, bytes_cons(q.str, "utf8"))
Damien George0699c6b2016-01-31 21:45:22 +0000818 new = sorted(new.values(), key=lambda x: x[0])
819
820 print('#include "py/mpconfig.h"')
821 print('#include "py/objint.h"')
822 print('#include "py/objstr.h"')
823 print('#include "py/emitglue.h"')
Damien George360d9722019-10-07 11:56:24 +1100824 print('#include "py/nativeglue.h"')
Damien George0699c6b2016-01-31 21:45:22 +0000825 print()
826
Damien George69661f32020-02-27 15:36:53 +1100827 print("#if MICROPY_LONGINT_IMPL != %u" % config.MICROPY_LONGINT_IMPL)
Damien George99b47192016-05-16 23:13:30 +0100828 print('#error "incompatible MICROPY_LONGINT_IMPL"')
Damien George69661f32020-02-27 15:36:53 +1100829 print("#endif")
Damien George99b47192016-05-16 23:13:30 +0100830 print()
831
832 if config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_MPZ:
Damien George69661f32020-02-27 15:36:53 +1100833 print("#if MPZ_DIG_SIZE != %u" % config.MPZ_DIG_SIZE)
Damien George99b47192016-05-16 23:13:30 +0100834 print('#error "incompatible MPZ_DIG_SIZE"')
Damien George69661f32020-02-27 15:36:53 +1100835 print("#endif")
Damien George99b47192016-05-16 23:13:30 +0100836 print()
837
Damien George69661f32020-02-27 15:36:53 +1100838 print("#if MICROPY_PY_BUILTINS_FLOAT")
839 print("typedef struct _mp_obj_float_t {")
840 print(" mp_obj_base_t base;")
841 print(" mp_float_t value;")
842 print("} mp_obj_float_t;")
843 print("#endif")
Damien George0699c6b2016-01-31 21:45:22 +0000844 print()
845
Damien George69661f32020-02-27 15:36:53 +1100846 print("#if MICROPY_PY_BUILTINS_COMPLEX")
847 print("typedef struct _mp_obj_complex_t {")
848 print(" mp_obj_base_t base;")
849 print(" mp_float_t real;")
850 print(" mp_float_t imag;")
851 print("} mp_obj_complex_t;")
852 print("#endif")
Damien Georgec51c8832016-09-03 00:19:02 +1000853 print()
854
Dave Hylands39eef272018-12-11 14:55:26 -0800855 if len(new) > 0:
Damien George69661f32020-02-27 15:36:53 +1100856 print("enum {")
Dave Hylands39eef272018-12-11 14:55:26 -0800857 for i in range(len(new)):
858 if i == 0:
Damien George69661f32020-02-27 15:36:53 +1100859 print(" MP_QSTR_%s = MP_QSTRnumber_of," % new[i][1])
Dave Hylands39eef272018-12-11 14:55:26 -0800860 else:
Damien George69661f32020-02-27 15:36:53 +1100861 print(" MP_QSTR_%s," % new[i][1])
862 print("};")
Damien George0699c6b2016-01-31 21:45:22 +0000863
Rich Barlow6e5a40c2018-07-19 12:42:26 +0100864 # As in qstr.c, set so that the first dynamically allocated pool is twice this size; must be <= the len
865 qstr_pool_alloc = min(len(new), 10)
866
Damien George0699c6b2016-01-31 21:45:22 +0000867 print()
Artyom Skrobov18b1ba02021-05-03 14:17:36 -0400868 print("const qstr_hash_t mp_qstr_frozen_const_hashes[] = {")
869 qstr_size = {"metadata": 0, "data": 0}
870 for _, _, _, qbytes in new:
871 qhash = qstrutil.compute_hash(qbytes, config.MICROPY_QSTR_BYTES_IN_HASH)
872 print(" %d," % qhash)
873 print("};")
874 print()
875 print("const qstr_len_t mp_qstr_frozen_const_lengths[] = {")
876 for _, _, _, qbytes in new:
877 print(" %d," % len(qbytes))
878 qstr_size["metadata"] += (
879 config.MICROPY_QSTR_BYTES_IN_LEN + config.MICROPY_QSTR_BYTES_IN_HASH
880 )
881 qstr_size["data"] += len(qbytes)
882 print("};")
883 print()
Damien George69661f32020-02-27 15:36:53 +1100884 print("extern const qstr_pool_t mp_qstr_const_pool;")
885 print("const qstr_pool_t mp_qstr_frozen_const_pool = {")
886 print(" (qstr_pool_t*)&mp_qstr_const_pool, // previous pool")
887 print(" MP_QSTRnumber_of, // previous pool size")
888 print(" %u, // allocated entries" % qstr_pool_alloc)
889 print(" %u, // used entries" % len(new))
Artyom Skrobov18b1ba02021-05-03 14:17:36 -0400890 print(" (qstr_hash_t *)mp_qstr_frozen_const_hashes,")
891 print(" (qstr_len_t *)mp_qstr_frozen_const_lengths,")
Damien George69661f32020-02-27 15:36:53 +1100892 print(" {")
Artyom Skrobov18b1ba02021-05-03 14:17:36 -0400893 for _, _, qstr, qbytes in new:
894 print(' "%s",' % qstrutil.escape_bytes(qstr, qbytes))
Damien George69661f32020-02-27 15:36:53 +1100895 print(" },")
896 print("};")
Damien George0699c6b2016-01-31 21:45:22 +0000897
898 for rc in raw_codes:
Damien George69661f32020-02-27 15:36:53 +1100899 rc.freeze(rc.source_file.str.replace("/", "_")[:-3] + "_")
Damien George0699c6b2016-01-31 21:45:22 +0000900
901 print()
Jim Mussarede0bf4612021-12-11 22:40:21 +1100902 print("const char mp_frozen_names[] = {")
903 print("#ifdef MP_FROZEN_STR_NAMES")
904 # makemanifest.py might also include some frozen string content.
905 print("MP_FROZEN_STR_NAMES")
906 print("#endif")
Damien George0699c6b2016-01-31 21:45:22 +0000907 for rc in raw_codes:
Damien George9b4c0132016-05-23 12:46:02 +0100908 module_name = rc.source_file.str
Damien George0699c6b2016-01-31 21:45:22 +0000909 print('"%s\\0"' % module_name)
910 print('"\\0"};')
911
Damien George69661f32020-02-27 15:36:53 +1100912 print("const mp_raw_code_t *const mp_frozen_mpy_content[] = {")
Damien George0699c6b2016-01-31 21:45:22 +0000913 for rc in raw_codes:
Damien George69661f32020-02-27 15:36:53 +1100914 print(" &raw_code_%s," % rc.escaped_name)
915 print("};")
916
Damien Georgefe16e782021-01-16 02:01:26 +1100917 # If a port defines MICROPY_FROZEN_LIST_ITEM then list all modules wrapped in that macro.
918 print("#ifdef MICROPY_FROZEN_LIST_ITEM")
919 for rc in raw_codes:
920 module_name = rc.source_file.str
921 if module_name.endswith("/__init__.py"):
922 short_name = module_name[: -len("/__init__.py")]
923 else:
924 short_name = module_name[: -len(".py")]
925 print('MICROPY_FROZEN_LIST_ITEM("%s", "%s")' % (short_name, module_name))
926 print("#endif")
927
Damien George0699c6b2016-01-31 21:45:22 +0000928
Damien George27879842019-10-09 14:23:15 +1100929def merge_mpy(raw_codes, output_file):
Damien George69661f32020-02-27 15:36:53 +1100930 assert len(raw_codes) <= 31 # so var-uints all fit in 1 byte
Damien George27879842019-10-09 14:23:15 +1100931 merged_mpy = bytearray()
932
933 if len(raw_codes) == 1:
Damien George69661f32020-02-27 15:36:53 +1100934 with open(raw_codes[0].mpy_source_file, "rb") as f:
Damien George27879842019-10-09 14:23:15 +1100935 merged_mpy.extend(f.read())
936 else:
937 header = bytearray(5)
Damien George69661f32020-02-27 15:36:53 +1100938 header[0] = ord("M")
Damien George27879842019-10-09 14:23:15 +1100939 header[1] = config.MPY_VERSION
Jim Mussaredb326edf2021-09-06 12:28:06 +1000940 header[2] = config.native_arch << 2 | config.MICROPY_PY_BUILTINS_STR_UNICODE << 1
Damien George27879842019-10-09 14:23:15 +1100941 header[3] = config.mp_small_int_bits
Damien George69661f32020-02-27 15:36:53 +1100942 header[4] = 32 # qstr_win_size
Damien George27879842019-10-09 14:23:15 +1100943 merged_mpy.extend(header)
944
945 bytecode = bytearray()
Damien George4f2fe342020-09-04 16:12:09 +1000946 bytecode_len = 6 + len(raw_codes) * 5 + 2
Damien George69661f32020-02-27 15:36:53 +1100947 bytecode.append(bytecode_len << 2) # kind and length
948 bytecode.append(0b00000000) # signature prelude
949 bytecode.append(0b00001000) # size prelude
950 bytecode.extend(b"\x00\x01") # MP_QSTR_
951 bytecode.extend(b"\x00\x01") # MP_QSTR_
Damien George27879842019-10-09 14:23:15 +1100952 for idx in range(len(raw_codes)):
Damien George69661f32020-02-27 15:36:53 +1100953 bytecode.append(0x32) # MP_BC_MAKE_FUNCTION
954 bytecode.append(idx) # index raw code
Damien George4f2fe342020-09-04 16:12:09 +1000955 bytecode.extend(b"\x34\x00\x59") # MP_BC_CALL_FUNCTION, 0 args, MP_BC_POP_TOP
Damien George69661f32020-02-27 15:36:53 +1100956 bytecode.extend(b"\x51\x63") # MP_BC_LOAD_NONE, MP_BC_RETURN_VALUE
Damien George27879842019-10-09 14:23:15 +1100957
Damien George69661f32020-02-27 15:36:53 +1100958 bytecode.append(0) # n_obj
959 bytecode.append(len(raw_codes)) # n_raw_code
Damien George27879842019-10-09 14:23:15 +1100960
961 merged_mpy.extend(bytecode)
962
963 for rc in raw_codes:
Damien George69661f32020-02-27 15:36:53 +1100964 with open(rc.mpy_source_file, "rb") as f:
965 f.read(4) # skip header
966 read_uint(f) # skip qstr_win_size
967 data = f.read() # read rest of mpy file
Damien George27879842019-10-09 14:23:15 +1100968 merged_mpy.extend(data)
969
970 if output_file is None:
971 sys.stdout.buffer.write(merged_mpy)
972 else:
Damien George69661f32020-02-27 15:36:53 +1100973 with open(output_file, "wb") as f:
Damien George27879842019-10-09 14:23:15 +1100974 f.write(merged_mpy)
975
Damien George69661f32020-02-27 15:36:53 +1100976
Damien George0699c6b2016-01-31 21:45:22 +0000977def main():
978 import argparse
Damien George69661f32020-02-27 15:36:53 +1100979
980 cmd_parser = argparse.ArgumentParser(description="A tool to work with MicroPython .mpy files.")
981 cmd_parser.add_argument("-d", "--dump", action="store_true", help="dump contents of files")
982 cmd_parser.add_argument("-f", "--freeze", action="store_true", help="freeze files")
983 cmd_parser.add_argument(
984 "--merge", action="store_true", help="merge multiple .mpy files into one"
985 )
986 cmd_parser.add_argument("-q", "--qstr-header", help="qstr header file to freeze against")
987 cmd_parser.add_argument(
988 "-mlongint-impl",
989 choices=["none", "longlong", "mpz"],
990 default="mpz",
991 help="long-int implementation used by target (default mpz)",
992 )
993 cmd_parser.add_argument(
994 "-mmpz-dig-size",
995 metavar="N",
996 type=int,
997 default=16,
998 help="mpz digit size used by target (default 16)",
999 )
1000 cmd_parser.add_argument("-o", "--output", default=None, help="output file")
1001 cmd_parser.add_argument("files", nargs="+", help="input .mpy files")
Damien George0699c6b2016-01-31 21:45:22 +00001002 args = cmd_parser.parse_args()
1003
1004 # set config values relevant to target machine
1005 config.MICROPY_LONGINT_IMPL = {
Damien George69661f32020-02-27 15:36:53 +11001006 "none": config.MICROPY_LONGINT_IMPL_NONE,
1007 "longlong": config.MICROPY_LONGINT_IMPL_LONGLONG,
1008 "mpz": config.MICROPY_LONGINT_IMPL_MPZ,
Damien George0699c6b2016-01-31 21:45:22 +00001009 }[args.mlongint_impl]
1010 config.MPZ_DIG_SIZE = args.mmpz_dig_size
Damien Georgefaf3d3e2019-06-04 22:13:32 +10001011 config.native_arch = MP_NATIVE_ARCH_NONE
Damien George0699c6b2016-01-31 21:45:22 +00001012
Damien Georgeb4790af2016-09-02 15:09:21 +10001013 # set config values for qstrs, and get the existing base set of qstrs
Damien George0699c6b2016-01-31 21:45:22 +00001014 if args.qstr_header:
1015 qcfgs, base_qstrs = qstrutil.parse_input_headers([args.qstr_header])
Damien George69661f32020-02-27 15:36:53 +11001016 config.MICROPY_QSTR_BYTES_IN_LEN = int(qcfgs["BYTES_IN_LEN"])
1017 config.MICROPY_QSTR_BYTES_IN_HASH = int(qcfgs["BYTES_IN_HASH"])
Damien George0699c6b2016-01-31 21:45:22 +00001018 else:
Damien Georgeb4790af2016-09-02 15:09:21 +10001019 config.MICROPY_QSTR_BYTES_IN_LEN = 1
1020 config.MICROPY_QSTR_BYTES_IN_HASH = 1
1021 base_qstrs = {}
Damien George0699c6b2016-01-31 21:45:22 +00001022
1023 raw_codes = [read_mpy(file) for file in args.files]
1024
1025 if args.dump:
1026 dump_mpy(raw_codes)
1027 elif args.freeze:
1028 try:
Damien Georgeb4790af2016-09-02 15:09:21 +10001029 freeze_mpy(base_qstrs, raw_codes)
Damien George0699c6b2016-01-31 21:45:22 +00001030 except FreezeError as er:
1031 print(er, file=sys.stderr)
1032 sys.exit(1)
Damien George27879842019-10-09 14:23:15 +11001033 elif args.merge:
1034 merged_mpy = merge_mpy(raw_codes, args.output)
Damien George0699c6b2016-01-31 21:45:22 +00001035
Damien George69661f32020-02-27 15:36:53 +11001036
1037if __name__ == "__main__":
Damien George0699c6b2016-01-31 21:45:22 +00001038 main()