tools/mpy_ld.py: Write architecture flags to output natmod if needed.
Some checks failed
JavaScript code lint and formatting with Biome / eslint (push) Has been cancelled
Check code formatting / code-formatting (push) Has been cancelled
Check spelling with codespell / codespell (push) Has been cancelled
Build docs / build (push) Has been cancelled
Check examples / embedding (push) Has been cancelled
Package mpremote / build (push) Has been cancelled
.mpy file format and tools / test (push) Has been cancelled
Build ports metadata / build (push) Has been cancelled
alif port / build_alif (alif_ae3_build) (push) Has been cancelled
cc3200 port / build (push) Has been cancelled
esp32 port / build_idf (esp32_build_c2_c5_c6) (push) Has been cancelled
esp32 port / build_idf (esp32_build_cmod_spiram_s2) (push) Has been cancelled
esp32 port / build_idf (esp32_build_p4) (push) Has been cancelled
esp32 port / build_idf (esp32_build_s3_c3) (push) Has been cancelled
esp8266 port / build (push) Has been cancelled
mimxrt port / build (push) Has been cancelled
nrf port / build (push) Has been cancelled
powerpc port / build (push) Has been cancelled
qemu port / build_and_test_arm (bigendian) (push) Has been cancelled
qemu port / build_and_test_arm (sabrelite) (push) Has been cancelled
qemu port / build_and_test_arm (thumb_hardfp) (push) Has been cancelled
qemu port / build_and_test_arm (thumb_softfp) (push) Has been cancelled
qemu port / build_and_test_rv32 (push) Has been cancelled
qemu port / build_and_test_rv64 (push) Has been cancelled
renesas-ra port / build_renesas_ra_board (push) Has been cancelled
rp2 port / build (push) Has been cancelled
samd port / build (push) Has been cancelled
stm32 port / build_stm32 (stm32_misc_build) (push) Has been cancelled
stm32 port / build_stm32 (stm32_nucleo_build) (push) Has been cancelled
stm32 port / build_stm32 (stm32_pyb_build) (push) Has been cancelled
unix port / minimal (push) Has been cancelled
unix port / reproducible (push) Has been cancelled
unix port / standard (push) Has been cancelled
unix port / standard_v2 (push) Has been cancelled
unix port / coverage (push) Has been cancelled
unix port / coverage_32bit (push) Has been cancelled
unix port / nanbox (push) Has been cancelled
unix port / longlong (push) Has been cancelled
unix port / float (push) Has been cancelled
unix port / gil_enabled (push) Has been cancelled
unix port / stackless_clang (push) Has been cancelled
unix port / float_clang (push) Has been cancelled
unix port / settrace_stackless (push) Has been cancelled
unix port / repr_b (push) Has been cancelled
unix port / macos (push) Has been cancelled
unix port / qemu_mips (push) Has been cancelled
unix port / qemu_arm (push) Has been cancelled
unix port / qemu_riscv64 (push) Has been cancelled
unix port / sanitize_address (push) Has been cancelled
unix port / sanitize_undefined (push) Has been cancelled
webassembly port / build (push) Has been cancelled
windows port / build-vs (Debug, true, x64, dev, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Debug, true, x86, dev, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Debug, x64, dev, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Debug, x86, dev, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Release, true, x64, dev, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Release, true, x64, dev, 2019, [16, 17)) (push) Has been cancelled
windows port / build-vs (Release, true, x64, standard, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Release, true, x64, standard, 2019, [16, 17)) (push) Has been cancelled
windows port / build-vs (Release, true, x86, dev, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Release, true, x86, dev, 2019, [16, 17)) (push) Has been cancelled
windows port / build-vs (Release, true, x86, standard, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Release, true, x86, standard, 2019, [16, 17)) (push) Has been cancelled
windows port / build-vs (Release, x64, dev, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Release, x64, standard, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Release, x86, dev, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Release, x86, standard, 2022, [17, 18)) (push) Has been cancelled
windows port / build-mingw (i686, mingw32, dev) (push) Has been cancelled
windows port / build-mingw (i686, mingw32, standard) (push) Has been cancelled
windows port / build-mingw (x86_64, mingw64, dev) (push) Has been cancelled
windows port / build-mingw (x86_64, mingw64, standard) (push) Has been cancelled
windows port / cross-build-on-linux (push) Has been cancelled
zephyr port / build (push) Has been cancelled
Python code lint and formatting with ruff / ruff (push) Has been cancelled

This commit lets "tools/mpy_ld.py" store architecture flags in generated
MPY files if explicitly requested, like "mpy-cross" does.

To achieve this, a new command-line option ("--arch-flags") was added to
receive the architecture flags value, accepting the same arguments'
format as "mpy-cross", and performing the same input validation.

The rest of the MPY toolchain was also modified to let the user pass the
arch flags to standard native module makefiles.  Given that there's
already a well-established "ARCH" argument, "ARCH_FLAGS" was chosen to
pass the optional flags to "mpy_ld.py".

Finally, documentation was updated to mention the new variable.

Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
This commit is contained in:
Alessandro Gatti
2025-11-28 06:25:57 +01:00
parent 0fd084363a
commit b87d73f2e9
4 changed files with 62 additions and 11 deletions

View File

@@ -43,9 +43,14 @@ options for the ``ARCH`` variable, see below):
* ``rv32imc`` (RISC-V 32 bits with compressed instructions, eg ESP32C3, ESP32C6)
* ``rv64imc`` (RISC-V 64 bits with compressed instructions)
If the chosen platform supports explicit architecture flags and you want to let
the output .mpy file carry those flags' value, you must pass them to the
``ARCH_FLAGS`` flags variable when building the .mpy file.
When compiling and linking the native .mpy file the architecture must be chosen
and the corresponding file can only be imported on that architecture. For more
details about .mpy files see :ref:`mpy_files`.
and the corresponding file can only be imported on that architecture (and if
architecture flags are present, only if they match the target's capabilities).
For more details about .mpy files see :ref:`mpy_files`.
Native code must be compiled as position independent code (PIC) and use a global
offset table (GOT), although the details of this varies from architecture to
@@ -124,7 +129,8 @@ The filesystem layout consists of two main parts, the source files and the Makef
location of the MicroPython repository (to find header files, the relevant Makefile
fragment, and the ``mpy_ld.py`` tool), ``MOD`` as the name of the module, ``SRC``
as the list of source files, optionally specify the machine architecture via ``ARCH``,
and then include ``py/dynruntime.mk``.
along with optional machine architecture flags specified via ``ARCH_FLAGS``, and
then include ``py/dynruntime.mk``.
Minimal example
---------------
@@ -217,6 +223,10 @@ Without modifying the Makefile you can specify the target architecture via::
$ make ARCH=armv7m
Same applies for optional architecture flags via::
$ make ARCH=rv32imc ARCH_FLAGS=zba
Module usage in MicroPython
---------------------------

View File

@@ -193,7 +193,8 @@ MPY files is used to indicate that no specific extensions are needed, and saves
one byte in the final output binary.
See also the ``-march-flags`` command-line option in both ``mpy-tool.py`` and
``mpy-cross`` to set this value when creating MPY files.
``mpy-cross``, and the ``--arch-flags`` command-line option in ``mpy_ld.py`` to
set this value when creating MPY files.
The global qstr and constant tables
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -194,6 +194,9 @@ endif
ifneq ($(MPY_EXTERN_SYM_FILE),)
MPY_LD_FLAGS += --externs "$(realpath $(MPY_EXTERN_SYM_FILE))"
endif
ifneq ($(ARCH_FLAGS),)
MPY_LD_FLAGS += --arch-flags "$(ARCH_FLAGS)"
endif
CFLAGS += $(CFLAGS_EXTRA)

View File

@@ -38,6 +38,7 @@ import makeqstrdata as qstrutil
# MicroPython constants
MPY_VERSION = 6
MPY_SUB_VERSION = 3
MPY_ARCH_FLAGS = 0x40
MP_CODE_BYTECODE = 2
MP_CODE_NATIVE_VIPER = 4
MP_NATIVE_ARCH_X86 = 1
@@ -1379,7 +1380,7 @@ class MPYOutput:
self.write_uint(n)
def build_mpy(env, fmpy, native_qstr_vals):
def build_mpy(env, fmpy, native_qstr_vals, arch_flags):
# Rewrite the entry trampoline if the proper value isn't known earlier, and
# ensure the trampoline size remains the same.
if env.arch.delayed_entry_offset:
@@ -1399,12 +1400,16 @@ def build_mpy(env, fmpy, native_qstr_vals):
out = MPYOutput()
out.open(fmpy)
header_flags = env.arch.mpy_feature | MPY_SUB_VERSION
if arch_flags != 0:
header_flags |= MPY_ARCH_FLAGS
# MPY: header
out.write_bytes(
bytearray(
[ord("M"), MPY_VERSION, env.arch.mpy_feature | MPY_SUB_VERSION, MP_SMALL_INT_BITS]
)
)
out.write_bytes(bytearray([ord("M"), MPY_VERSION, header_flags, MP_SMALL_INT_BITS]))
# MPY: arch flags
if arch_flags != 0:
out.write_uint(arch_flags)
# MPY: n_qstr
out.write_uint(1 + len(native_qstr_vals))
@@ -1553,7 +1558,7 @@ def do_link(args):
load_object_file(env, f, obj_name)
link_objects(env, len(native_qstr_vals))
build_mpy(env, args.output, native_qstr_vals)
build_mpy(env, args.output, native_qstr_vals, args.arch_flags)
except LinkError as er:
print("LinkError:", er.args[0])
sys.exit(1)
@@ -1603,6 +1608,35 @@ def parse_linkerscript(source):
return symbols
RV32_EXTENSIONS = {
"zba": 1 << 0,
"zcmp": 1 << 1,
}
def validate_arch_flags(args):
if args.arch_flags is None:
args.arch_flags = 0
return
if args.arch != "rv32imc":
raise ValueError('Architecture "{}" does not support extra flags'.format(args.arch))
if (args.arch_flags.startswith("0") and len(args.arch_flags) > 2) or args.arch_flags.isdigit():
if args.arch_flags[1] in "bB":
base = 2
elif args.arch_flags[1] in "xX":
base = 16
else:
base = 10
args.arch_flags = int(args.arch_flags, base)
else:
flags_value = 0
for flag in args.arch_flags.lower().split(","):
if flag not in RV32_EXTENSIONS:
raise ValueError('Invalid architecture flags value "{}"'.format(flag))
flags_value |= RV32_EXTENSIONS[flag]
args.arch_flags = flags_value
def main():
import argparse
@@ -1611,6 +1645,7 @@ def main():
"--verbose", "-v", action="count", default=1, help="increase verbosity"
)
cmd_parser.add_argument("--arch", default="x64", help="architecture")
cmd_parser.add_argument("--arch-flags", default=None, help="optional architecture flags")
cmd_parser.add_argument("--preprocess", action="store_true", help="preprocess source files")
cmd_parser.add_argument("--qstrs", default=None, help="file defining additional qstrs")
cmd_parser.add_argument(
@@ -1632,6 +1667,8 @@ def main():
global log_level
log_level = args.verbose
validate_arch_flags(args)
if args.preprocess:
do_preprocess(args)
else: