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

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: