Skip to content

pbio/os: Number protothread checkpoints from one. - #487

Merged
dlech merged 1 commit into
pybricks:masterfrom
dlech:pbio-os-dense-checkpoint-labels
Sep 5, 2026
Merged

pbio/os: Number protothread checkpoints from one.#487
dlech merged 1 commit into
pybricks:masterfrom
dlech:pbio-os-dense-checkpoint-labels

Conversation

@dlech

@dlech dlech commented Sep 4, 2026

Copy link
Copy Markdown
Member

Protothread checkpoints used __LINE__ as the resume state, so the switch that dispatches to them spanned the whole line range of the function. GCC emits a jump table covering that range, one entry per line, with most entries pointing at the default case. Where the range exceeded 255 the table also doubled in width, since __gnu_thumb1_case_uqi can only address a byte.

Use __COUNTER__ instead, and capture its value in PBIO_OS_ASYNC_BEGIN() so that each protothread numbers its checkpoints from one. The case labels are then dense and start at zero, so the compiler indexes the table directly with no range offset.

On the Move Hub this takes the firmware from 105632 to 104112 bytes, which matters because only 864 bytes of the 104K region were left. For example, the dispatch in init_device_information_service() goes from a 42 entry table to 15 entries, and the one in pbdrv_bluetooth_spi_process_thread() from 79 two byte entries to 12 single byte entries.

This also means source line numbers no longer affect the size of the binary, so adding a comment to a protothread does not move code around.

Protothread checkpoints used __LINE__ as the resume state, so the switch
that dispatches to them spanned the whole line range of the function. GCC
emits a jump table covering that range, one entry per line, with most
entries pointing at the default case. Where the range exceeded 255 the
table also doubled in width, since __gnu_thumb1_case_uqi can only address
a byte.

Use __COUNTER__ instead, and capture its value in PBIO_OS_ASYNC_BEGIN() so
that each protothread numbers its checkpoints from one. The case labels are
then dense and start at zero, so the compiler indexes the table directly
with no range offset.

On the Move Hub this takes the firmware from 105632 to 104112 bytes, which
matters because only 864 bytes of the 104K region were left. For example,
the dispatch in init_device_information_service() goes from a 42 entry
table to 15 entries, and the one in pbdrv_bluetooth_spi_process_thread()
from 79 two byte entries to 12 single byte entries.

This also means source line numbers no longer affect the size of the
binary, so adding a comment to a protothread does not move code around.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dlech
dlech merged commit 678a9d6 into pybricks:master Sep 5, 2026
15 checks passed
@dlech
dlech deleted the pbio-os-dense-checkpoint-labels branch September 5, 2026 21:04
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