summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-03-01 14:54:22 -0700
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-03-01 17:28:14 -0700
commit18ad76a7a2baa9c78e01e85886f693e572fcb2a9 (patch)
tree5543f56fc89c548cf411f76e317894005a6b198c
parent6fedfe71cfab86aecf4168ecf35961bf88ce0438 (diff)
stack.c.gen: Fix analysis
-rwxr-xr-xbuild-aux/stack.c.gen229
1 files changed, 187 insertions, 42 deletions
diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen
index 2965c00..edc7bae 100755
--- a/build-aux/stack.c.gen
+++ b/build-aux/stack.c.gen
@@ -351,6 +351,7 @@ re_call_other = re.compile(r"(?P<func>[^(]+)\(.*")
class Plugin(typing.Protocol):
+ def is_intrhandler(self, name: str) -> bool: ...
def extra_nodes(self) -> typing.Collection[Node]: ...
def indirect_callees(
self, loc: str, line: str
@@ -404,6 +405,14 @@ class PluginApplication:
class AppPlugin:
+ def is_intrhandler(self, name: str) -> bool:
+ return name in [
+ "rp2040_hwtimer_intrhandler",
+ "_cr_gdb_intrhandler",
+ "hostclock_handle_sig_alarm",
+ "hostnet_handle_sig_io",
+ ]
+
def extra_nodes(self) -> typing.Collection[Node]:
return []
@@ -466,6 +475,9 @@ class LibObjPlugin:
objcalls[method_name].add(impl_name + "_" + method_name)
self.objcalls = objcalls
+ def is_intrhandler(self, name: str) -> bool:
+ return False
+
def extra_nodes(self) -> typing.Collection[Node]:
return []
@@ -476,7 +488,7 @@ class LibObjPlugin:
return None
if m := re_call_objcall.fullmatch(line):
if m.group("meth") in self.objcalls:
- return sorted(self.objcalls[m.group("meth")]), True
+ return sorted(self.objcalls[m.group("meth")]), False
return [
f"__indirect_call:{m.group('obj')}.vtable->{m.group('meth')}"
], False
@@ -487,6 +499,14 @@ class LibObjPlugin:
class LibHWPlugin:
+ pico_platform: str
+
+ def __init__(self, arg_pico_platform: str) -> None:
+ self.pico_platform = arg_pico_platform
+
+ def is_intrhandler(self, name: str) -> bool:
+ return False
+
def extra_nodes(self) -> typing.Collection[Node]:
return []
@@ -494,11 +514,15 @@ class LibHWPlugin:
if "/3rd-party/" in loc:
return None
if "trigger->cb(trigger->cb_arg)" in line:
- return [
+ ret = [
"alarmclock_sleep_intrhandler",
- "w5500_tcp_alarm_handler",
- "w5500_udp_alarm_handler",
- ], True
+ ]
+ if self.pico_platform == "rp2040":
+ ret += [
+ "w5500_tcp_alarm_handler",
+ "w5500_udp_alarm_handler",
+ ]
+ return ret, False
return None
def skip_call(self, chain: list[str], call: str) -> bool:
@@ -506,6 +530,9 @@ class LibHWPlugin:
class LibCRIPCPlugin:
+ def is_intrhandler(self, name: str) -> bool:
+ return False
+
def extra_nodes(self) -> typing.Collection[Node]:
return []
@@ -603,6 +630,9 @@ class Lib9PPlugin:
return self._CONFIG_9P_NUM_SOCKS * self.CONFIG_9P_SRV_MAX_REQS
return 1
+ def is_intrhandler(self, name: str) -> bool:
+ return False
+
def extra_nodes(self) -> typing.Collection[Node]:
return []
@@ -631,10 +661,23 @@ class Lib9PPlugin:
for c in chain[-self.CONFIG_9P_SRV_MAX_DEPTH :]
):
return True
+ re_msg_meth = re.compile(
+ r"^lib9p_(?P<grp>[TR])msg_(?P<meth>validate|unmarshal|marshal)$"
+ )
+ wrapper = next((c for c in chain if re_msg_meth.match(c)), None)
+ if wrapper:
+ m = re_msg_meth.match(wrapper)
+ assert m
+ deny = ":" + m.group("meth") + "_" + ("R" if m.group("grp") == "T" else "T")
+ if deny in call:
+ return True
return False
class LibMiscPlugin:
+ def is_intrhandler(self, name: str) -> bool:
+ return False
+
def extra_nodes(self) -> typing.Collection[Node]:
return []
@@ -654,9 +697,57 @@ class LibMiscPlugin:
class PicoSDKPlugin:
app_gpio_handlers: typing.Collection[str]
+ app_init_array: typing.Collection[str]
+ app_preinit_array: typing.Collection[str]
- def __init__(self, app_gpio_handlers: typing.Collection[str]) -> None:
+ def __init__(
+ self,
+ *,
+ app_gpio_handlers: typing.Collection[str],
+ app_init_array: typing.Collection[str],
+ ) -> None:
self.app_gpio_handlers = app_gpio_handlers
+ self.app_init_array = app_init_array
+
+ # git grep '^PICO_RUNTIME_INIT_FUNC\S*('
+ self.app_preinit_array = [
+ # "runtime_init_mutex", # pico_mutex
+ # "runtime_init_default_alarm_pool", # pico_time
+ # "runtime_init_boot_locks_reset", # hardware_boot_lock
+ "runtime_init_per_core_irq_priorities", # hardware_irq
+ # "spinlock_set_extexclall", # hardware_sync_spin_lock
+ "__aeabi_bits_init", # pico_bit_ops
+ # "runtime_init_bootrom_locking_enable", # pico_bootrom, rp2350-only
+ # "runtime_init_pre_core_tls_setup", # pico_clib_interface, picolibc-only
+ # "__aeabi_double_init", # pico_double
+ # "__aeabi_float_init", # pico_float
+ "__aeabi_mem_init", # pico_mem_ops
+ "first_per_core_initializer", # pico_runtime
+ # pico_runtime_init
+ # "runtime_init_bootrom_reset", # rp2350-only
+ # "runtime_init_per_core_bootrom_reset", # rp2350-only
+ # "runtime_init_per_core_h3_irq_registers", # rp2350-only
+ "runtime_init_early_resets",
+ "runtime_init_usb_power_down",
+ # "runtime_init_per_core_enable_coprocessors", # PICO_RUNTIME_SKIP_INIT_PER_CORE_ENABLE_COPROCESSORS
+ "runtime_init_clocks",
+ "runtime_init_post_clock_resets",
+ "runtime_init_rp2040_gpio_ie_disable",
+ "runtime_init_spin_locks_reset",
+ "runtime_init_install_ram_vector_table",
+ ]
+
+ def is_intrhandler(self, name: str) -> bool:
+ return name in [
+ "gpio_default_irq_handler",
+ "isr_invalid",
+ "isr_nmi",
+ "isr_hardfault",
+ "isr_svcall",
+ "isr_pendsv",
+ "isr_systick",
+ *[f"isr_irq{n}" for n in range(32)],
+ ]
def indirect_callees(self, loc: str, line: str) -> tuple[list[str], bool] | None:
if "/3rd-party/pico-sdk/" not in loc or "/3rd-party/pico-sdk/lib/" in loc:
@@ -678,7 +769,7 @@ class PicoSDKPlugin:
if "/flash.c:" in loc and "boot2_copyout" in line:
return ["_stage2_boot"], False
if "/gpio.c:" in loc and call == "callback":
- return sorted(self.app_gpio_handlers), True
+ return sorted(self.app_gpio_handlers), False
if "/printf.c:" in loc:
if call == "out":
return [
@@ -703,6 +794,11 @@ class PicoSDKPlugin:
return ["stdio_uart_out_flush"], False
case "in_chars":
return ["stdio_uart_in_chars"], False
+ if "/newlib_interface.c:" in loc:
+ if line == "*p)();":
+ return sorted(self.app_init_array), False
+ if "/pico_runtime/runtime.c:" in loc:
+ return sorted(self.app_preinit_array), False
return None
def skip_call(self, chain: list[str], call: str) -> bool:
@@ -723,18 +819,35 @@ class PicoSDKPlugin:
return False
def extra_nodes(self) -> typing.Collection[Node]:
+ ret = []
+
# src/rp2_common/hardware_divider/include/hardware/divider_helper.S
save_div_state_and_lr = 5 * 4
# src/rp2_common/pico_divider/divider_hardware.S
save_div_state_and_lr_64 = 5 * 4
- return [
+
+ # src/src/rp2_common/pico_crt0/crt0.S
+ for n in range(32):
+ ret += [synthetic_node(f"isr_irq{n}", 0, {"__unhandled_user_irq"})]
+ ret += [
+ synthetic_node("isr_invalid", 0, {"__unhandled_user_irq"}),
+ synthetic_node("isr_nmi", 0, {"__unhandled_user_irq"}),
+ synthetic_node("isr_hardfault", 0, {"__unhandled_user_irq"}),
+ synthetic_node("isr_svcall", 0, {"__unhandled_user_irq"}),
+ synthetic_node("isr_pendsv", 0, {"__unhandled_user_irq"}),
+ synthetic_node("isr_systick", 0, {"__unhandled_user_irq"}),
+ synthetic_node(f"__unhandled_user_irq", 0),
+ synthetic_node("_reset_handler", 0, {"runtime_init", "main", "exit"}),
+ ]
+
+ ret += [
# src/rp2_common/pico_int64_ops/pico_int64_ops_aeabi.S
- synthetic_node("__aeabi_lmul", 4),
+ synthetic_node("__wrap___aeabi_lmul", 4),
# src/rp2_common/pico_divider/divider_hardware.S
# s32 aliases
synthetic_node("div_s32s32", 0, {"divmod_s32s32"}),
- synthetic_node("__aeabi_idiv", 0, {"divmod_s32s32"}),
- synthetic_node("__aeabi_idivmod", 0, {"divmod_s32s32"}),
+ synthetic_node("__wrap___aeabi_idiv", 0, {"divmod_s32s32"}),
+ synthetic_node("__wrap___aeabi_idivmod", 0, {"divmod_s32s32"}),
# s32 impl
synthetic_node("divmod_s32s32", 0, {"divmod_s32s32_savestate"}),
synthetic_node(
@@ -745,8 +858,8 @@ class PicoSDKPlugin:
synthetic_node("divmod_s32s32_unsafe", 2 * 4, {"__aeabi_idiv0"}),
# u32 aliases
synthetic_node("div_u32u32", 0, {"divmod_u32u32"}),
- synthetic_node("__aeabi_uidiv", 0, {"divmod_u32u32"}),
- synthetic_node("__aeabi_uidivmod", 0, {"divmod_u32u32"}),
+ synthetic_node("__wrap___aeabi_uidiv", 0, {"divmod_u32u32"}),
+ synthetic_node("__wrap___aeabi_uidivmod", 0, {"divmod_u32u32"}),
# u32 impl
synthetic_node("divmod_u32u32", 0, {"divmod_u32u32_savestate"}),
synthetic_node(
@@ -757,8 +870,8 @@ class PicoSDKPlugin:
synthetic_node("divmod_u32u32_unsafe", 2 * 4, {"__aeabi_idiv0"}),
# s64 aliases
synthetic_node("div_s64s64", 0, {"divmod_s64s64"}),
- synthetic_node("__aeabi_ldiv", 0, {"divmod_s64s64"}),
- synthetic_node("__aeabi_ldivmod", 0, {"divmod_s64s64"}),
+ synthetic_node("__wrap___aeabi_ldiv", 0, {"divmod_s64s64"}),
+ synthetic_node("__wrap___aeabi_ldivmod", 0, {"divmod_s64s64"}),
# s64 impl
synthetic_node("divmod_s64s64", 0, {"divmod_s64s64_savestate"}),
synthetic_node(
@@ -771,8 +884,7 @@ class PicoSDKPlugin:
),
# u64 aliases
synthetic_node("div_u64u64", 0, {"divmod_u64u64"}),
- synthetic_node("__aeabi_uldiv", 0, {"divmod_u64u64"}),
- synthetic_node("__aeabi_uldivmod", 0, {"divmod_u64u64"}),
+ synthetic_node("__wrap___aeabi_uldivmod", 0, {"divmod_u64u64"}),
# u64 impl
synthetic_node("divmod_u64u64", 0, {"divmod_u64u64_savestate"}),
synthetic_node(
@@ -786,6 +898,42 @@ class PicoSDKPlugin:
# *_rem
synthetic_node("divod_s64s64_rem", 2 * 4, {"divmod_s64s64"}),
synthetic_node("divod_u64u64_rem", 2 * 4, {"divmod_u64u64"}),
+ # src/rp2_common/pico_mem_ops/mem_ops_aeabi.S
+ synthetic_node("__aeabi_mem_init", 0, {"rom_funcs_lookup"}),
+ synthetic_node(
+ "__wrap___aeabi_memset", 0, {"rom_func_lookup(ROM_FUNC_MEMSET)"}
+ ),
+ synthetic_node("__wrap___aeabi_memset4", 0, {"__wrap___aeabi_memset8"}),
+ synthetic_node(
+ "__wrap___aeabi_memset8", 0, {"rom_func_lookup(ROM_FUNC_MEMSET4)"}
+ ),
+ synthetic_node("__wrap___aeabi_memcpy4", 0, {"__wrap___aeabi_memcpy8"}),
+ synthetic_node(
+ "__wrap___aeabi_memcpy7", 0, {"rom_func_lookup(ROM_FUNC_MEMCPY4)"}
+ ),
+ synthetic_node("__wrap_memset", 0, {"rom_func_lookup(ROM_FUNC_MEMSET)"}),
+ synthetic_node("__wrap___aeabi_memcpy", 0, {"__wrap_memcpy"}),
+ synthetic_node("__wrap_memcpy", 0, {"rom_func_lookup(ROM_FUNC_MEMCPY)"}),
+ # src/rp2_common/pico_bit_ops/bit_ops_aeabi.S
+ synthetic_node("__aeabi_bits_init", 0, {"rom_funcs_lookup"}),
+ synthetic_node("__wrap___clz", 0, {"__wrap___clzsi2"}),
+ synthetic_node("__wrap___clzl", 0, {"__wrap___clzsi2"}),
+ synthetic_node("__wrap___clzsi2", 0, {"rom_func_lookup(ROM_FUNC_CLZ32"}),
+ synthetic_node("__wrap___ctzsi2", 0, {"rom_func_lookup(ROM_FUNC_CTZ32"}),
+ synthetic_node(
+ "__wrap___popcountsi2", 0, {"rom_func_lookup(ROM_FUNC_POPCOUNT32"}
+ ),
+ synthetic_node("__wrap___clzll", 0, {"__wrap___clzdi2"}),
+ synthetic_node("__wrap___clzdi2", 4, {"rom_func_lookup(ROM_FUNC_CLZ32"}),
+ synthetic_node("__wrap___ctzdi2", 4, {"rom_func_lookup(ROM_FUNC_CTZ32"}),
+ synthetic_node(
+ "__wrap___popcountdi2", 3 * 4, {"rom_func_lookup(ROM_FUNC_POPCOUNT32"}
+ ),
+ synthetic_node("__rev", 0, {"reverse32"}),
+ synthetic_node("__revl", 0, {"reverse32"}),
+ synthetic_node("reverse32", 0, {"rom_func_lookup(ROM_FUNC_REVERSE32"}),
+ synthetic_node("__revll", 0, {"reverse64"}),
+ synthetic_node("reverse64", 3 * 4, {"rom_func_lookup(ROM_FUNC_REVERSE32"}),
# src/rp2040/boot_stage2/boot2_${name,,}.S for name=W25Q080,
# controlled by `#define PICO_BOOT_STAGE2_{name} 1` in
# src/boards/include/boards/pico.h
@@ -796,6 +944,7 @@ class PicoSDKPlugin:
# synthetic_node("rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE)", 0), # TODO
# synthetic_node("rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET)", 0), # TODO
]
+ return ret
class TinyUSBDevicePlugin:
@@ -862,6 +1011,9 @@ class TinyUSBDevicePlugin:
in_table = True
self.tud_drivers = tud_drivers
+ def is_intrhandler(self, name: str) -> bool:
+ return False
+
def extra_nodes(self) -> typing.Collection[Node]:
return []
@@ -890,6 +1042,9 @@ class TinyUSBDevicePlugin:
class NewlibPlugin:
+ def is_intrhandler(self, name: str) -> bool:
+ return False
+
def extra_nodes(self) -> typing.Collection[Node]:
# This is accurate to
# /usr/arm-none-eabi/lib/thumb/v6-m/nofp/libg.a as of
@@ -922,6 +1077,7 @@ class NewlibPlugin:
synthetic_node("__errno", 0),
synthetic_node("_getpid_r", 8, {"_getpid"}),
synthetic_node("random", 8),
+ synthetic_node("register_fini", 8, {"atexit"}),
]
def indirect_callees(self, loc: str, line: str) -> tuple[list[str], bool] | None:
@@ -932,6 +1088,9 @@ class NewlibPlugin:
class LibGCCPlugin:
+ def is_intrhandler(self, name: str) -> bool:
+ return False
+
def extra_nodes(self) -> typing.Collection[Node]:
# This is accurate to
# /usr/lib/gcc/arm-none-eabi/14.2.0/thumb/v6-m/nofp/libgcc.a
@@ -958,7 +1117,6 @@ def main(
) -> None:
plugins: list[Plugin] = []
- hooks_is_intrhandler: list[typing.Callable[[str], bool]] = []
# sbc-harness ####################################################
@@ -973,24 +1131,14 @@ def main(
if "9p" in name:
return lib9p_plugin.thread_count(name)
return 1
- if name == "main":
+ if name == ("_reset_handler" if arg_pico_platform == "rp2040" else "main"):
return 1
return 0
- def sbc_is_intrhandler(name: str) -> bool:
- return name in [
- "rp2040_hwtimer_intrhandler",
- "_cr_gdb_intrhandler",
- "hostclock_handle_sig_alarm",
- "hostnet_handle_sig_io",
- ]
-
- hooks_is_intrhandler += [sbc_is_intrhandler]
-
plugins += [
AppPlugin(),
LibObjPlugin(arg_c_fnames),
- LibHWPlugin(),
+ LibHWPlugin(arg_pico_platform),
LibCRIPCPlugin(),
lib9p_plugin,
LibMiscPlugin(),
@@ -999,16 +1147,11 @@ def main(
# pico-sdk #######################################################
if arg_pico_platform == "rp2040":
-
- def pico_is_intrhandler(name: str) -> bool:
- return name in [
- "gpio_default_irq_handler",
- ]
-
- hooks_is_intrhandler += [pico_is_intrhandler]
-
plugins += [
- PicoSDKPlugin(sbc_gpio_handlers),
+ PicoSDKPlugin(
+ app_gpio_handlers=sbc_gpio_handlers,
+ app_init_array=["register_fini"],
+ ),
TinyUSBDevicePlugin(arg_c_fnames),
NewlibPlugin(),
LibGCCPlugin(),
@@ -1021,8 +1164,8 @@ def main(
def intrhandler_filter(name: str) -> int:
name = name.rsplit(":", 1)[-1]
- for hook in hooks_is_intrhandler:
- if hook(name):
+ for plugin in plugins:
+ if plugin.is_intrhandler(name):
return 1
return 0
@@ -1061,6 +1204,8 @@ def main(
# Print.
print("= " + grp_name + " " + sep1[len(grp_name) + 3 :])
for name, num in sorted(grp.rows.items()):
+ if num == 0:
+ continue
print(f"{name.ljust(namelen)} {str(num).rjust(numlen)}")
print(sep2)
print(f"{'Total'.ljust(namelen)} {str(grp.nsum).rjust(numlen)}")
@@ -1084,7 +1229,7 @@ def main(
baselen = max(len(str(r[1])) for r in rows)
sizelen = max(len(str(r[2])) for r in rows)
for row in sorted(rows):
- if row[0] == "main":
+ if row[0] in ("main", "_reset_handler"):
continue
print("const size_t CONFIG_COROUTINE_STACK_SIZE_", end="")
print(f"{row[0].ljust(namelen)} =", end="")