Skip to content

feat(cc): call through function-pointer parameters and locals#493

Merged
bboe merged 1 commit into
mainfrom
bboe/cc-fnptr-param-call
May 24, 2026
Merged

feat(cc): call through function-pointer parameters and locals#493
bboe merged 1 commit into
mainfrom
bboe/cc-fnptr-param-call

Conversation

@bboe
Copy link
Copy Markdown
Owner

@bboe bboe commented May 24, 2026

Summary

  • int (*cmp)(...) declared as a function parameter could not be called: compute_safe_pin_registers only fed VarDecl- and global-typed function-pointers into its indirect-callee set, so the pin-cost pre-pass raised unknown function: cmp before generate_call ever ran. _collect_function_pointer_vars now folds the function's parameter list in too, and both callers thread it through.
  • The indirect-call site in generate_call previously required len(arguments) == len(function_pointer_in_regs) and emitted no stack pushes, so every fn-pointer call had to be routed through registers. Function-pointer parameters from qsort/bsearch-style prototypes carry no inner in_register annotations (the parser discards the inner parameter list), so the indirect-call path now falls back to standard cdecl when function_pointer_in_regs is empty: push args right-to-left, call <acc>, add <sp>, N*int_size cleanup.
  • A new unit test (test_function_pointer_parameter_emits_indirect_call_with_stack_args) exercises the new path.

Test plan

  • Probe from the task description compiles under --bits 32 --object and emits call eax + add esp, 8 cleanup.
  • user/libbboeos/stdlib.c now parses past the qsort indirect call at line 224; next failure is the array-of-function-pointers shape static void (*_atexit_fns[N])(void); (out of scope, parser-level).
  • python3 -m pytest tests/unit/ tests/test_ccobj.py tests/test_ccld.py tests/test_ccar.py -x -q — 559 passed
  • python3 tests/test_cc_local_structs.py — 9 / 9
  • python3 tests/test_cc_bits.py — 112 / 112
  • timeout 240 python3 tests/test_asm.py — 42 / 42
  • ./make_os.sh — builds clean

Remaining gaps in the function-pointer story

  • File-scope arrays of function pointers (static void (*_atexit_fns[N])(void);) — parser-level, separate work.
  • Function-pointer parameters discard their inner parameter list at parse time, so an int (*cmp)(int x __attribute__((in_register("bx"))), int y) parameter would still call via the stack, not via BX. Routing in_register parameters through fn-pointer params would need the inner Param list preserved on the outer Param node.

🤖 Generated with Claude Code

A call site whose callee name resolves to a function-pointer
parameter (e.g. ``int (*cmp)(const void *, const void *)``) used
to raise ``unknown function: cmp`` because
``compute_safe_pin_registers`` only treated VarDecl-introduced
function pointers (and file-scope function-pointer globals) as
indirect callees.  Function parameters carry the same
``"function_pointer"`` spelling on the Param node but were never
seeded into that set, so the pin-cost pre-pass classified the call
as "unknown" before ``generate_call`` ever ran.

``_collect_function_pointer_vars`` now also folds in any parameter
whose ``Param.type`` is ``"function_pointer"``; both callers
(``compute_safe_pin_registers`` and ``_select_auto_pin_candidates``)
thread the function's parameter list through to it.

The indirect-call emission in ``generate_call`` previously assumed
every fn-pointer call routed arguments through registers — it
required ``len(arguments) == len(function_pointer_in_regs)`` and
emitted no stack pushes.  Function-pointer parameters from
qsort/bsearch-style prototypes carry no inner ``in_register``
annotations (the parser discards the inner parameter list), so the
indirect-call path now falls back to standard cdecl when
``function_pointer_in_regs`` is empty: push args right-to-left,
``call <acc>``, ``add <sp>, N*int_size`` cleanup.

stdlib.c now parses past line 224 (the qsort fn-pointer call site);
the next failure is the array-of-function-pointers shape
``static void (*_atexit_fns[N])(void);`` which is out of scope here.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@bboe bboe force-pushed the bboe/cc-fnptr-param-call branch from aa6e9b6 to 26380d7 Compare May 24, 2026 09:50
@bboe bboe merged commit f648141 into main May 24, 2026
27 checks passed
@bboe bboe deleted the bboe/cc-fnptr-param-call branch May 24, 2026 10:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant