Skip to content

turbo: add CIRCUITPY_LOAD_NATIVE to load host-compiled native .mpy - #11332

Open
mikeysklar wants to merge 2 commits into
adafruit:backport-load-nativefrom
mikeysklar:load-native-plumbing
Open

turbo: add CIRCUITPY_LOAD_NATIVE to load host-compiled native .mpy#11332
mikeysklar wants to merge 2 commits into
adafruit:backport-load-nativefrom
mikeysklar:load-native-plumbing

Conversation

@mikeysklar

Copy link
Copy Markdown
Collaborator

Second of five PRs for turbo. #11331 backported the native .mpy loader but left it compiled out. This one adds the CIRCUITPY_LOAD_NATIVE build flag that turns it on, and enables it on five boards. Depends on #11331.

What

CIRCUITPY_LOAD_NATIVE=1 loads native and viper .mpy built on the host by mpy-cross, without compiling in the on-board emitter. On-board compiling of @native and @viper is untouched and still lives behind CIRCUITPY_ENABLE_MPY_NATIVE.

Why

The emitter costs 20 to 50 KB. On the Metro M4 AirLift Lite it does not fit at all: CIRCUITPY_ENABLE_MPY_NATIVE=1 fails to link, region FLASH_FIRMWARE overflowed by 13104 bytes (12.8 KB). The loader alone:

Board Loader cost Free after
Metro M4 AirLift Lite +1.9 KB 6.7 KB
Metro RP2040 +2.9 KB 56.8 KB
Metro RP2350 +2.7 KB 120.5 KB
Feather nRF52840 Express +2.4 KB 136.8 KB
Feather STM32F405 Express +2.4 KB 365.3 KB

Tested on hardware

All five flashed with 10.3.0-52-gcee7c17c08 built from this branch, then driven over the serial REPL.

Board _mpy arch bytecode viper .mpy
Metro M4 AirLift Lite armv7emsp 152 ms 10 ms
Metro RP2040 armv6m 176 ms 9 ms
Metro RP2350 armv7emsp 104 ms 5 ms
Feather nRF52840 Express armv7emsp 351 ms 18 ms
Feather STM32F405 Express armv7emsp 158 ms 9 ms

Both variants returned checksum 3264000 on every board, so the compiled module computes what the source does. On all five, a wrong-arch (xtensawin) .mpy raises ValueError: incompatible .mpy arch, and @micropython.viper in source raises SyntaxError: invalid micropython decorator.

Not tested: Cortex-M7, non-English translations.

How I tested it

mpy-cross -march=armv7emsp -o vsum.mpy vsum.py
mpy-cross -march=xtensawin -o vsum_wrong.mpy vsum.py
# vsum.py; vsum_bc.py is the same file without the decorator
import micropython

@micropython.viper
def sum_bytes(buf: ptr8, n: int) -> int:
    total = 0
    for i in range(n):
        total += buf[i]
    return total
>>> import time, vsum, vsum_bc
>>> b = bytes(range(256)) * 100        # 25,600 B, fits every board here
>>> for m in (vsum_bc, vsum):
...     t = time.monotonic_ns(); s = m.sum_bytes(b, len(b)); print(m.__name__, s, (time.monotonic_ns() - t) // 1000000, "ms")
vsum_bc 3264000 104 ms
vsum 3264000 5 ms

Scope

Metro M0 Express is left out, and it is closer than it looks. Stock leaves 1,660 bytes free and the loader needs about 1,868, so CIRCUITPY_LOAD_NATIVE=1 fails to link by 208 bytes:

region `FLASH_FIRMWARE' overflowed by 208 bytes

Dropping safemode.py clears it, but that is a poor trade for a shipping board and not something I want to propose. One maintainer thinks roughly 700 bytes could be recovered on the SAMD21, which would cover it three times over. Suggestions for where those 208 bytes should come from are very welcome, and I am happy to build and measure any candidate. If a way is found, the board is a one-line addition to this PR.

AI assistance

Claude Code wrote the config lines and test script. I chose the boards, ran each one, checked the numbers.

mikeysklar and others added 2 commits September 8, 2026 14:30
Expose MICROPY_PERSISTENT_CODE_LOAD_NATIVE as a CIRCUITPY_ make flag, the
same way CIRCUITPY_ENABLE_MPY_NATIVE exposes MICROPY_EMIT_THUMB. A board
that sets CIRCUITPY_LOAD_NATIVE = 1 loads and runs native and viper .mpy
files that mpy-cross compiled on the host, without carrying the on-board
emitter: @micropython.native and @micropython.viper from source still
raise SyntaxError, a .mpy for another architecture still raises
ValueError.

Boards that already enable the emitter keep loading native .mpy, since
the macro is also true when CIRCUITPY_ENABLE_MPY_NATIVE is set.

Default is off; no board changes in this commit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Turn on CIRCUITPY_LOAD_NATIVE for the Metro M4 AirLift Lite, Metro RP2040,
Metro RP2350, Feather nRF52840 Express and Feather STM32F405 Express.
Flash cost over the same build without the flag, and the time for a
host-compiled viper mandelbrot (160x120, 64 iterations) against the float
bytecode a user would write:

  Metro M4 AirLift Lite    +1,904 B   431 ms   (11,221 ms bytecode)
  Metro RP2040             +3,036 B   384 ms   (13,942 ms bytecode)
  Metro RP2350             +2,876 B   261 ms    (6,371 ms bytecode)
  Feather nRF52840 Express +2,432 B   781 ms   (20,788 ms bytecode)
  Feather STM32F405 Express +2,444 B  416 ms    (8,121 ms bytecode)

The SAMD51 board is the reason for the flag: the on-board emitter
overflows its flash by 12,804 B, the loader alone fits with 6,876 B free.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant