Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@agatti
Copy link
Contributor

@agatti agatti commented Nov 13, 2025

Summary

This PR modifies the existing structure definitions for pin objects across selected ports, in order to reduce the overall footprint of each single pin instance.

There are two changes in this commit:

  • Some ports pin structure's usage of "qstr" (32 bits) has been changed to "qstr_short_t" (16 bits) which encodes the same string index in half the space, and given that pin names are precomputed and immutable this alone should help saving a few bytes without any noticeable slowdown
  • Structures affected by the earlier change have been reorganised to be more tightly packed, reducing the amount of padding bytes introduced by the compiler to keep structure elements aligned.

These changes should provide functionally equivalent binaries but with some minor size savings. The generated code for accessing a 16-bits quantity instead of a 32-bits one may or may not be much different from what was generated before, and there is a chance of either a very tiny speedup or a very tiny slowdown.

In any case, that would be probably detected in code that keeps creating pin instances that are looked up via name in a tight loop. That's very uncommon to see in production code anyway.

Results are promising, here's what I found:

Notes:

  • Arm ports were built using GCC 13.3.0 from Ubuntu 24.04 repos, RV32 ports were built using GCC 14.1.0 from Embecosm's Core-V 20240530 toolchain
  • Results are for the default board for the port except for RP2 and SAMD ports, with the boards being built being reported
  • "Before" refers to the current master (27544a2).
Port Before After Delta
CC3200 185328 185256 -72
MIMXRT 332876 331284 -1592
NRF 187040 186920 -120
RENESAS-RA 256616 256160 -456
RP2/RPI_PICO 316088 314984 -1104
RP2/RPI_PICO2 308852 307748 -1104
RP2/RPI_PICO2(RV32) 390996 389944 -1052
SAMD/ADAFRUIT_ITSYBITSY_M0_EXPRESS 235280 235128 -152
SAMD/ADAFRUIT_ITSYBITSY_M4_EXPRESS 268000 267852 -148

Size savings come from both the qstr type being narrow and in certain cases from structure repacking. In most cases structures contain mostly word-sized elements and there isn't much that can be done to maintain intra-structure member alignment.

Unless there are default GCC flags to let the compiler assume that all structures are packed, then repacking structures that will show up in .rodata could be an interesting source of size savings.

Testing

I've currently tested this on the two SAMD boards mentioned in the table, as the PR also touches the port's pins prefix file (see Octoprobe run 366 - the M0 failures are also present in the master branch, checked in Octoprobe run 367).

I plan to launch Octoprobe runs for each of the ports involved but I'll schedule them over time to not hog the server with a full 4hrs+ test run. The Reneses port has to be tested by somebody else as I have no means to test that in any reasonable way.

Trade-offs and Alternatives

The ports being affected still need more in-depth testing, although unless there's a compiler problem or non-standard structure member accesses happen when dealing with pin structures on MicroPython's side, these changes should be harmless.

@github-actions
Copy link

github-actions bot commented Nov 13, 2025

Code size report:

Reference:  esp32/boards: Add Silicognition ManT1S board definition. [27544a2]
Comparison: nrf/modules/machine/pin: Reduce footprint of pin structures. [merge of 6ecd3ad]
  mpy-cross:    +0 +0.000% 
   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
     mimxrt: -1592 -0.423% TEENSY40
        rp2: -1120 -0.121% RPI_PICO_W
       samd:  -148 -0.054% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +0 +0.000% VIRT_RV32

@Josverl
Copy link
Contributor

Josverl commented Nov 14, 2025

That is very substantial indeed.👏
What is the reason stm32 does not have the similar reduction ?

@agatti
Copy link
Contributor Author

agatti commented Nov 14, 2025

That's a very good question, for which I have two hypotheses.

The first one is that even though the overall structure is smaller, the linker decided to spread the instances around with a less granular alignment, which nullified the efforts (the linker is able to move things around to minimise the amount of fill bytes in .text/.rodata too).

The second one is that moving the offsets generated a set of larger structure member accessors that grew as much as the space saved.

As an uneducated guess I'd go for the first one, but I'll get an answer very soon with the help of ghidra and bindiff. If that's indeed the case, then putting all the loose instances together in an array (like the rp2 port does) could - in theory - unlock the savings for that port.

Incidentally, I've found more instances of oversized .rodata entries (some that aren't even port-specific), so there's still hope for the rest of the ports that either didn't manage to save anything or don't seem to store QSTRs in their pins structure (eg. esp32/esp8266) :)

Edit:

And that's exactly what happens. This is the structure on master (gray bytes are padding):

image

And this is with my changes:

image

The compiler/linker doesn't have enough room to shrink the structure and keep the following entry word-aligned. Words/pointers have to be on a word boundary so maybe splitting the bitfield into a halfword and a byte could fill more gaps in the structure?

I'll see what happens when moving things around for this specific port, but I'm not entirely sure there are enough fields - probably this is the same scenario for Alif and nRF too.

This commit performs a minor change to the "pin_af_t" structure,
changing the data type used to store alternate function pin name QSTRs
in order to have a smaller impact on the .rodata section of the
firmware.

The data type used to store the QSTR name of the pin is changed from
"qstr" to "qstr_short_t", that is able to store the same name string
index but in half the size.

Other pin-related structures in the port that also store QSTRs do not
benefit from a narrower data type, as their members are mostly
word-aligned and thus the linker is forced to insert padding bytes
between entries.

Signed-off-by: Alessandro Gatti <[email protected]>
This commit performs a few minor changes to the structures used to store
board pin information, in order to reduce the impact on the .rodata
section of the firmware of instances of those structures.

The data type used to store the QSTR name of alternate function pins
("machine_pin_af_obj_t") is changed from "qstr" to "qstr_short_t", that
is able to store the same name string index but in half the size.

Regular pin objects structure ("machine_pin_obj_t") instead has its QSTR
variable holder changed from "qstr" to "qstr_short_t", and some elements
of that structure have been rearranged to remove enough padding bytes
inside structure elements to let the linker allocate less data for
instances of that structure.

Signed-off-by: Alessandro Gatti <[email protected]>
This commit performs a few minor changes to the structures used to store
board pin information, in order to reduce the impact on the .rodata
section of the firmware of instances of those structures.

The pin objects structure ("machine_pin_obj_t") instead has its QSTR
variable holder changed from "qstr" to "qstr_short_t", and some elements
of that structure have been rearranged to remove enough padding bytes
inside structure elements to let the linker allocate less data for
instances of that structure.

Signed-off-by: Alessandro Gatti <[email protected]>
This commit performs a few minor changes to the structures used to store
board pin information, in order to reduce the impact on the .rodata
section of the firmware of instances of those structures.

The data type used to store the QSTR name of both regular and alternate
function pins ("machine_pin_obj_t" and "machine_pin_af_obj_t") is changed
from "qstr" to "qstr_short_t", that is able to store the same name string
index but in half the size.

Signed-off-by: Alessandro Gatti <[email protected]>
This commit performs a few minor changes to the structures used to store
board pin information, in order to reduce the impact on the .rodata
section of the firmware of instances of those structures.

The pin objects structure ("machine_pin_obj_t") instead has its QSTR
variable holder changed from "qstr" to "qstr_short_t", and some elements
of that structure have been rearranged to remove enough padding bytes
inside structure elements to let the linker allocate less data for
instances of that structure.

Signed-off-by: Alessandro Gatti <[email protected]>
@agatti
Copy link
Contributor Author

agatti commented Nov 14, 2025

So, I've tried all permutations of the changes in the initial PR iteration and turns out that having mostly word-sized elements doesn't really help. Having structure members of smaller sizes could have led to more savings as forced halfword alignment could have been exploited for the structure, moving the word-sized members to a word boundary to keep things from breaking.

Anyway, I've left the changes that had any real size impact, and I've split the initial single commit into per-port commits to provide better insight of the change in the repo's history.

@dpgeorge
Copy link
Member

Nice savings!

For stm32, I think machine_pin_obj_t:pin_mask can be made uint16_t (there are max 16 pins per port, and this is a bit mask). Then put that next to qstr_short_t. But that'll need thorough testing to make sure it doesn't break anything, so probably best for a separate PR.

@dpgeorge dpgeorge added the ports Relates to multiple ports, or a new/proposed port label Nov 15, 2025
This commit performs a few minor changes to the structures used to store
board pin information, in order to reduce the impact on the .rodata
section of the firmware of instances of those structures.

The "pin_obj_t" structure, holding pin information, had an unused
word-sized field ("pull") that isn't used by any of the board
definitions, yet it takes up space in each pin structure being compiled
in the final firmware image.

Images for all available boards were built successfully with this
change, so there should be no unwanted issues arising from shrinking
that structure.

Signed-off-by: Alessandro Gatti <[email protected]>
@agatti
Copy link
Contributor Author

agatti commented Nov 15, 2025

I'm not really familiar with each port's pin module implementation so I went for the low hanging fruit here, which also happens to be the safer route to take.

Besides the potential STM32 changes (thanks!) which I'll look into soon, I've also noticed that nRF's pin_obj_t.pull structure member doesn't seem to be used. I've built all the supported board firmware images without it so it should be safe enough to remove.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ports Relates to multiple ports, or a new/proposed port

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants