Skip to content

Add named manual kernel registration API#20658

Open
goutamadwant wants to merge 11 commits into
pytorch:mainfrom
goutamadwant:11221-named-manual-kernel-registration
Open

Add named manual kernel registration API#20658
goutamadwant wants to merge 11 commits into
pytorch:mainfrom
goutamadwant:11221-named-manual-kernel-registration

Conversation

@goutamadwant

@goutamadwant goutamadwant commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds an opt-in library-name parameter for manual kernel registration codegen so generated RegisterKernels.{h,cpp} can expose a library-specific registration API such as register_portable_ops_lib_kernels().

The default manual registration API remains register_all_kernels() when no library name is provided. Named registration requires a valid C++ identifier, preventing distinct library names from collapsing to the same generated symbol.

CMake records the registration mode during code generation and reuses it when creating the operator library, so callers do not need to repeat the option. The Buck macro supports the same optional name and rejects a name supplied without manual registration.

Fixes #11221.

Test plan

  • PYTHONPATH=.. python3 -m unittest codegen.test.test_executorch_gen codegen.test.test_executorch_signatures codegen.test.test_executorch_types codegen.test.test_executorch_unboxing codegen.test.test_selective_build
  • python3 -m py_compile codegen/gen.py codegen/test/test_executorch_gen.py
  • Changed-file lintrunner checks
  • Configured and installed the ExecuTorch CMake targets locally
  • Configured and built the named_manual_registration_test target with TEST_NAMED_MANUAL_REGISTRATION=ON
  • Ran named_manual_registration_test, which registers two generated libraries and verifies both operators are present
  • git diff --check

cc @larryliu0820 @JacobSzwejbka @lucylq

Signed-off-by: goutamadwant <workwithgoutam@gmail.com>
@pytorch-bot

pytorch-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/20658

Note: Links to docs will display an error until the docs builds have been completed.

⚠️ 17 Awaiting Approval

As of commit 5a2c01c with merge base a19d1ba (image):

AWAITING APPROVAL - The following workflows need approval before CI can run:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 1, 2026
@goutamadwant

Copy link
Copy Markdown
Contributor Author

@pytorchbot label "release notes: api"

@pytorch-bot pytorch-bot Bot added the release notes: api Changes to public facing apis (any interfaces, pybinded runtime methods, etc.) label Jul 1, 2026
@nil-is-all nil-is-all added the module: runtime Issues related to the core runtime and code under runtime/ label Jul 7, 2026
@nil-is-all

Copy link
Copy Markdown
Contributor

Hi @goutamadwant, thanks for the PR! Could you address the errors in the lintrunner logs? Should be simple fixes

@goutamadwant

Copy link
Copy Markdown
Contributor Author

Hi @goutamadwant, thanks for the PR! Could you address the errors in the lintrunner logs? Should be simple fixes

Sure will take a look @nil-is-all

goutamadwant and others added 2 commits July 7, 2026 23:22
@goutamadwant

Copy link
Copy Markdown
Contributor Author

@nil-is-all Addressed the lintrunner formatting issues and pushed the update. I reran the changed-file lintrunner check and the focused codegen unit tests locally. Let me know if there are any suggestions. thanks!

@digantdesai

Copy link
Copy Markdown
Contributor

@claude review this PR

@claude

claude Bot commented Jul 9, 2026

Copy link
Copy Markdown

Claude finished @digantdesai's task in 4m 34s —— View job


PR Review: Named manual kernel registration API

I reviewed the diff against origin/main. Overall this is a clean, well-scoped, backward-compatible change with good Python-level test coverage. Details below.

What's good

  • Backward compatible. get_manual_registration_function_name returns register_all_kernels when no lib name is given, and the templates default to that via the DEFAULT_MANUAL_REGISTRATION_FUNCTION_NAME env value, so existing consumers are unaffected.
  • Validation is sensible. Rejecting --manual-registration-lib-name without --manual-registration, and enforcing a valid C++ identifier, both fail fast with clear messages surfaced through parser.error.
  • Good unit tests. TestManualRegistrationFunctionName and TestManualRegistrationTemplates cover the default, named, and both error paths, plus actual template rendering for .h and .cpp.
  • Buck + CMake plumbing is threaded through consistently, and the write_sharded(..., base_env=...) approach is the right way to inject a non-sharded value into the aggregate RegisterKernelsEverything.cpp.

Issues / suggestions

1. CMake couples the C++ function name to the CMake target name (codegen/gen.py validation + tools/cmake/Codegen.cmake:210-212)

list(APPEND _gen_command --manual-registration
     --manual-registration-lib-name=${GEN_LIB_NAME})

GEN_LIB_NAME is a CMake target name, which is not guaranteed to be a valid C++ identifier (targets legitimately contain -, ., ::). If a caller enables MANUAL_REGISTRATION on such a target, codegen will parser.error("... must be a valid C++ identifier") and hard-fail the build. Worth either sanitizing the name before passing it (e.g. replace non-[A-Za-z0-9_] with _) or documenting the constraint next to the MANUAL_REGISTRATION option. Fix this →

2. The CMake MANUAL_REGISTRATION path has no in-tree caller and is therefore untested.
Grepping the repo, MANUAL_REGISTRATION is only referenced inside tools/cmake/Codegen.cmake itself — no CMakeLists.txt passes it to generate_bindings_for_kernels/gen_operators_lib. Combined with the author's note that CMake couldn't be run locally, the entire CMake branch (including the new RegisterKernelsEverything.cpp / RegisterKernels.h source list) is effectively unexercised. Two suggestions:

  • Confirm the sharded aggregate output filename really is RegisterKernelsEverything.cpp for the manual path (the Python test relies on this, so it should be correct — but the CMake add_custom_command OUTPUT must match exactly or the build will error on a missing output).
  • Consider wiring one existing lib (or a test lib) through the CMake path in a follow-up so CI covers it.

3. Very generic CLI aliases (codegen/gen.py:997-1004).
--lib-name / --lib_name are broad names for what is specifically the manual-registration lib name. Since codegen already has a LIB_NAME concept elsewhere, these short aliases could invite confusion. Consider dropping them and keeping just --manual-registration-lib-name (+ underscore variant).

4. Minor: redundant anchors in the regex.
re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$") combined with .fullmatch() double-anchors; fullmatch already anchors both ends. Harmless, just noise.

Test verification

I attempted to run codegen.test.test_executorch_gen locally but the sandbox blocked the Python invocation, so I could not independently execute the suite. The tests read correctly and the template/base_env wiring is sound on inspection. The CI Lint/pull jobs (currently awaiting approval) should confirm.

Verdict

No blocking correctness bugs in the Python codegen. The main thing I'd want addressed before merge is #1 (CMake target-name → C++ identifier coupling), since it can turn into a confusing build failure. #2 is a coverage gap worth a follow-up. The rest are minor/nits.
· branch 11221-named-manual-kernel-registration

@goutamadwant

Copy link
Copy Markdown
Contributor Author

@claude review this again

Addressed the CMake target-name concern by sanitizing the manual registration library name before it is embedded in the generated C++ symbol. For example, target/library names with characters like -, ., or :: now produce a valid registration function name instead of causing codegen to fail.

I also updated the focused codegen tests and reran the changed-file lintrunner check locally. The new workflow runs are currently waiting for approval.

@shoumikhin shoumikhin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for picking up #11221 — opt-in flag with the default preserved is the right shape, and doing Buck + CMake together is appreciated. Approving. A couple of non-blocking things worth a look:

  1. CMake↔codegen filename parity. The MANUAL_REGISTRATION branch swaps the generated sources to RegisterKernelsEverything.cpp / RegisterKernels.h (vs RegisterCodegenUnboxedKernelsEverything.cpp). Worth confirming codegen actually emits those names in that mode and the Buck path stays consistent — a mismatch here would be a silent build break.

  2. CMake passes the raw LIB_NAME as the identifier (--manual-registration-lib-name=${GEN_LIB_NAME}). The validator requires ^[A-Za-z_][A-Za-z0-9_]*$, so a target whose name has a - or . would hard-error codegen. Either sanitize -/._ or document that manual-registration targets need identifier-safe names.

Nit: the flag has four spellings (--manual-registration-lib-name / --manual_registration_lib_name / --lib-name / --lib_name) — one canonical plus the underscore alias is plenty.

Nice validation + codegen tests otherwise; this is the right fix for the multi-lib registration collision.

@goutamadwant

Copy link
Copy Markdown
Contributor Author

Thanks for the review @shoumikhin I removed the generic --lib-name and --lib_name aliases, keeping --manual-registration-lib-name and its underscore variant.

For your other two points:

  • The manual registration tests generate and read RegisterKernelsEverything.cpp and RegisterKernels.h, matching the filenames used by CMake.
  • CMake can continue passing the target name directly because codegen sanitizes it before embedding it in the C++ registration symbol. The sanitization is covered by the portable-ops.lib::debug test case.

I also reran the focused manual registration tests and the changed-file lintrunner checks. Let me know if any other suggestions. thanks!

@shoumikhin shoumikhin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline findings from a holistic design and implementation review.

Comment thread codegen/gen.py Outdated


def sanitize_manual_registration_lib_name(lib_name: str) -> str:
sanitized = MANUAL_REGISTRATION_LIB_NAME_SANITIZE_PATTERN.sub("_", lib_name).strip(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sanitization is lossy, so distinct libraries can still generate the same global symbol. For example, foo-bar, foo.bar, and foo_bar all become register_foo_bar_kernels(), causing duplicate definitions when those libraries are linked together. Could we either reject non-identifier names or use a collision-resistant encoding/hash suffix? Avoiding registration-symbol collisions is the primary purpose of this API.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shoumikhin I removed the lossy sanitization. --manual-registration-lib-name now requires a valid C++ identifier, so names such as foo-bar and foo.bar fail instead of collapsing to the same generated symbol. Valid identifier names also remain unchanged and I added tests for invalid punctuation and leading digits.

Comment thread tools/cmake/Codegen.cmake
set(_srcs_list ${_out_dir}/RegisterCodegenUnboxedKernelsEverything.cpp
${_out_dir}/Functions.h ${_out_dir}/NativeFunctions.h
)
if(GEN_MANUAL_REGISTRATION)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MANUAL_REGISTRATION must now be repeated independently in both generate_bindings_for_kernels() and gen_operators_lib(). If a caller enables it on only one call, codegen and target_sources() expect different filenames (RegisterKernelsEverything.cpp versus RegisterCodegenUnboxedKernelsEverything.cpp), resulting in a missing-source build failure. Could we derive/store this mode per LIB_NAME, combine the configuration, or at least fail at configure time when the two calls disagree?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated this so generate_bindings_for_kernels() records the manual-registration mode for each LIB_NAME, and gen_operators_lib() derives the mode from that configuration. Callers should no longer repeat MANUAL_REGISTRATION in both calls.

genrule_cmd = genrule_cmd + [
"--manual_registration",
]
if manual_registration_lib_name:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If manual_registration_lib_name is supplied without manual_registration = True, this macro silently ignores the name. Direct codegen rejects the same combination with --manual-registration-lib-name requires --manual-registration. Could we add a Starlark fail() here so the public Buck API has the same validation contract?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a Starlark fail() when manual_registration_lib_name is supplied without manual_registration = True, matching the validation contract of the Python codegen entry point.

Comment thread tools/cmake/Codegen.cmake
if(GEN_ADD_EXCEPTION_BOUNDARY)
set(_gen_command "${_gen_command}" --add-exception-boundary)
endif()
if(GEN_MANUAL_REGISTRATION)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add an end-to-end CMake test for this branch? No in-tree CMake caller currently enables MANUAL_REGISTRATION, and the added Python tests only verify template rendering. A useful regression test would build two manually registered libraries, include both generated headers, call both named functions, and verify both kernel sets register. That would also catch mismatches between custom-command outputs and target_sources().

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shoumikhin added an end-to-end CMake regression to the existing portable custom-ops workflow. It builds two independently named manual-registration libraries, includes both generated headers, calls both generated registration functions, and verifies that both operators are present in the runtime registry.
Let me know!

Signed-off-by: goutamadwant <workwithgoutam@gmail.com>
@goutamadwant

Copy link
Copy Markdown
Contributor Author

@shoumikhin addressed your comments / suggestions. let me know if this looks good. thanks!

@shoumikhin shoumikhin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two things I'd like addressed on this.

  1. The help text and the bzl docstring describe behavior the code does not implement. Both the --manual-registration-lib-name help in codegen/gen.py and the executorch_generated_lib docstring in shim_et/xplat/executorch/codegen/codegen.bzl say "Characters that are not valid in a C++ identifier are converted to underscores." The code does the opposite: get_manual_registration_function_name raises ValueError on such a name, and generate_bindings_for_kernels in tools/cmake/Codegen.cmake hits FATAL_ERROR. So a name like my-ops.lib is a hard build failure, not a sanitized register_my_ops_lib_kernels. Please update both strings to state that the lib name must already be a valid C++ identifier (I'd keep the strict validation and just correct the docs rather than add sanitization).

  2. In tools/cmake/Codegen.cmake, gen_operators_lib recovers the manual-registration flag by reading a GLOBAL property keyed on SHA256(LIB_NAME) that generate_bindings_for_kernels set earlier. This is an implicit contract between two separate function calls with an ordering hazard: if gen_operators_lib runs before generate_bindings_for_kernels for the same LIB_NAME, the property is empty, it silently selects RegisterCodegenUnboxedKernelsEverything.cpp (which is never generated in manual mode), and the build fails at compile time with no indication of the cause. Please pass this explicitly instead: add a MANUAL_REGISTRATION option to gen_operators_lib, symmetric with generate_bindings_for_kernels, and set it from the caller. That removes the hidden global state and the ordering trap. If you prefer to keep the property channel, at least drop the SHA256 hashing, since LIB_NAME is already validated as a C++ identifier and is a legal property suffix, so the hash only hides the key from cmake --trace.

@goutamadwant

Copy link
Copy Markdown
Contributor Author

Two things I'd like addressed on this.

  1. The help text and the bzl docstring describe behavior the code does not implement. Both the --manual-registration-lib-name help in codegen/gen.py and the executorch_generated_lib docstring in shim_et/xplat/executorch/codegen/codegen.bzl say "Characters that are not valid in a C++ identifier are converted to underscores." The code does the opposite: get_manual_registration_function_name raises ValueError on such a name, and generate_bindings_for_kernels in tools/cmake/Codegen.cmake hits FATAL_ERROR. So a name like my-ops.lib is a hard build failure, not a sanitized register_my_ops_lib_kernels. Please update both strings to state that the lib name must already be a valid C++ identifier (I'd keep the strict validation and just correct the docs rather than add sanitization).
  2. In tools/cmake/Codegen.cmake, gen_operators_lib recovers the manual-registration flag by reading a GLOBAL property keyed on SHA256(LIB_NAME) that generate_bindings_for_kernels set earlier. This is an implicit contract between two separate function calls with an ordering hazard: if gen_operators_lib runs before generate_bindings_for_kernels for the same LIB_NAME, the property is empty, it silently selects RegisterCodegenUnboxedKernelsEverything.cpp (which is never generated in manual mode), and the build fails at compile time with no indication of the cause. Please pass this explicitly instead: add a MANUAL_REGISTRATION option to gen_operators_lib, symmetric with generate_bindings_for_kernels, and set it from the caller. That removes the hidden global state and the ordering trap. If you prefer to keep the property channel, at least drop the SHA256 hashing, since LIB_NAME is already validated as a C++ identifier and is a legal property suffix, so the hash only hides the key from cmake --trace.

@shoumikhin Addressed both these points in the latest push. below are the highlights.

  • Updated the CLI help and Starlark documentation to describe the strict C++ identifier requirement.
  • Removed the global property and passed MANUAL_REGISTRATION explicitly to gen_operators_lib.
  • Updated the CMake caller to pass the option to both codegen functions.

test pass locally. Let me know if there are any other suggestions/comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. module: runtime Issues related to the core runtime and code under runtime/ release notes: api Changes to public facing apis (any interfaces, pybinded runtime methods, etc.)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Manual kernel registration to include library names in API

4 participants