Add LTO support for nrf52840 while preserving interrupt handlers

This commit is contained in:
Ben Meadors
2026-06-08 11:01:35 -05:00
parent 360c54f1f9
commit 771018ca8f
2 changed files with 52 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
#!/usr/bin/env python3
# trunk-ignore-all(ruff/F821)
# trunk-ignore-all(flake8/F821)
#
# Whole-image LTO for nrf52840, EXCEPT the interrupt handlers.
#
# Every interrupt/exception handler is referenced only from the assembly vector
# table (gcc_startup_nrf52840.S), which LTO cannot see. So whole-program LTO judges
# the handlers dead, removes them, and the weak `b .` Default_Handler stubs prevail
# -> the first IRQ (FreeRTOS scheduler-start SVC, then GPIOTE/RTC/USBD/...) lands in
# an infinite loop and the chip hangs. Compiling the handler-bearing objects WITHOUT
# LTO lets ordinary linking keep the strong handlers (exactly like a non-LTO build):
# - framework core (/FrameworkArduino/, /cores/nRF5/): every nrfx ISR + the
# FreeRTOS SVC/PendSV port (vPortSVCHandler / xPortPendSVHandler).
# - TinyUSB's nrf port: USBD_IRQHandler -- without it USB enumerates but the data
# path hangs (the host's first transfer interrupt is never serviced).
# Everything else (app src + RadioLib/Crypto/nanopb/sensor libs/...) stays LTO'd.
import os
Import("env")
env.Append(LINKFLAGS=["-flto", "-flto-partition=1to1"])
# Private include dir for the TinyUSB nrf-port re-compile below (tusb_option.h etc.).
_fw = env.PioPlatform().get_package_dir("framework-arduinoadafruitnrf52") or ""
_tinyusb_src = os.path.join(_fw, "libraries", "Adafruit_TinyUSB_Arduino", "src")
FRAMEWORK = ("/FrameworkArduino/", "/cores/nRF5/")
USB_ISR = "Adafruit_TinyUSB_nrf" # defines USBD_IRQHandler
def _no_lto(node):
try:
path = node.get_abspath()
except Exception:
path = str(node)
if USB_ISR in path:
return env.Object(
node,
CCFLAGS=env["CCFLAGS"] + ["-fno-lto"],
CPPPATH=env["CPPPATH"] + [_tinyusb_src],
)
if any(s in path for s in FRAMEWORK):
return env.Object(node, CCFLAGS=env["CCFLAGS"] + ["-fno-lto"])
return node
env.AddBuildMiddleware(_no_lto)

View File

@@ -22,6 +22,10 @@ build_flags = ${nrf52840_base.build_flags}
-DRADIOLIB_EXCLUDE_SX127X=1
-DRADIOLIB_EXCLUDE_LR11X0=1
-DRADIOLIB_EXCLUDE_LR2021=1
-fmerge-all-constants ; fold identical constants across the image (~0.7KB; same flag stm32 uses)
-flto ; whole-image LTO (~-57KB); nrf52_lto.py keeps the hardware layer out of LTO so the vector ISRs survive
extra_scripts = ${nrf52_base.extra_scripts}
pre:extra_scripts/nrf52_lto.py
build_src_filter = ${nrf52_base.build_src_filter} \
+<../variants/nrf52840/rak4631> \
+<mesh/eth/> \