feat(cc): call through function-pointer parameters and locals#493
Merged
Conversation
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>
aa6e9b6 to
26380d7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
int (*cmp)(...)declared as a function parameter could not be called:compute_safe_pin_registersonly fed VarDecl- and global-typed function-pointers into its indirect-callee set, so the pin-cost pre-pass raisedunknown function: cmpbeforegenerate_callever ran._collect_function_pointer_varsnow folds the function's parameter list in too, and both callers thread it through.generate_callpreviously requiredlen(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 innerin_registerannotations (the parser discards the inner parameter list), so the indirect-call path now falls back to standard cdecl whenfunction_pointer_in_regsis empty: push args right-to-left,call <acc>,add <sp>, N*int_sizecleanup.test_function_pointer_parameter_emits_indirect_call_with_stack_args) exercises the new path.Test plan
--bits 32 --objectand emitscall eax+add esp, 8cleanup.user/libbboeos/stdlib.cnow parses past the qsort indirect call at line 224; next failure is the array-of-function-pointers shapestatic 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 passedpython3 tests/test_cc_local_structs.py— 9 / 9python3 tests/test_cc_bits.py— 112 / 112timeout 240 python3 tests/test_asm.py— 42 / 42./make_os.sh— builds cleanRemaining gaps in the function-pointer story
static void (*_atexit_fns[N])(void);) — parser-level, separate work.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