Skip to content

feat: extend kernel port tool operations - #946

Draft
drbh wants to merge 6 commits into
mainfrom
kernel-port-declarative
Draft

drbh wants to merge 6 commits into
mainfrom
kernel-port-declarative

Conversation

@drbh

@drbh drbh commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator

this pr adds a few kernel-port ops and post-port checks so recipes can handle native extensions and catch upstream drift at port time instead of at build or import time

  • fix #includes when a glob move renames files
  • add rewrite, a structural find/replace
  • add extension to convert native extensions to ._ops
  • handle aliased and attribute-chain imports
  • add mark_tests only=
  • add follow_includes=#true for kernel and manifest
  • verify relative imports and torch-ext declarations
  • add expect blob= to pin overlay copies
  • only match import edits at statement start

drbh added 5 commits October 26, 2026 23:21
Adds the `rewrite` op. A rewrite deletes whole lines, and splits
identifiers before matching literal text, so a replacement cannot match
inside a longer identifier.
…hain imports

- Adds the `extension` op, deriving native extension conversions from a
  declaration. A declared module that upstream builds at import time with
  `var = torch.utils.cpp_extension.load(name="<module>", ...)` becomes
  `from ._ops import ops as var`, and the module-level statements that
  only fed the build (source paths, flags, the load import) are removed.
  Removing a trailing PYBIND11_MODULE no longer leaves blank lines at the
  end of the file.
- remap_module also remaps `import a.b as x`. An unaliased `import a.b`
  binds `a`, so remapping it is an error rather than silently skipped.
- convert_import converts an unaliased `import a.b.c` used as
  `a.b.c.attr` into `from a.b import c` with `c.attr` uses, refusing when
  `a` is used any other way or `c` is already a name in the module.
- mark_tests only="<test>,..." marks just the named tests.
`kernel ... follow_includes=#true` (and `manifest ... torch_follow_includes=#true`)
add every file the selected sources reach through #include, searched the
way the compiler does: quoted includes next to the includer and then in the
include directories, angle-bracket includes only in the include
directories. A recipe then names compilation units instead of maintaining
header lists that drift from what upstream actually includes.
- Relative imports must resolve: after the last op, every relative `from`
  import in the tree must name a module or package that is in the port
  (`torch-ext/<pkg>/_ops` is generated by the build). An upstream file
  that starts importing a module the recipe prunes now fails the port
  instead of failing at import time.
- Every torch-ext declaration must match its definition: each function
  declared in a C-family file under torch-ext must have a definition in
  the output with the same parameter types (names and default arguments
  may differ). The error shows both lists and the first parameter that
  differs.
- `expect blob=` pins the upstream file an overlay copy was made from. A
  find= guard only notices upstream changes to the lines it names, so an
  overlay copy of an upstream file could silently go stale. blob= pins the
  whole file by its git blob id; on a mismatch the error prints the git
  diff between the old and new blob, which is what to carry into the
  overlay copy.
- Python import edits are spliced into the text so untouched code keeps
  its layout. Occurrences were found by text search alone, so an import
  that also appeared in a string literal or comment made the op refuse,
  and `import b` could match inside `from a import b`. Only occurrences
  that begin a statement, per libcst's tokenizer, now count, and every
  rewrite is located in the original text before any is applied.
@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Coverage report — kernels/

Measured on: Python 3.10 / Torch 2.13.0.
Other CI configurations are not included in this number.
Hardware-gated code paths (ROCm/XPU/NPU/Darwin/Windows) are excluded or unreachable on the Linux+CUDA runner.

Total coverage: 88.5% — threshold: 80% — ✅

Per-file breakdown
Name Stmts Miss Cover Missing
src/kernels/__init__.py 14 0 100%
src/kernels/_system.py 6 1 83% 10
src/kernels/_versions.py 130 14 89% 53, 59-60, 63-64, 102, 165-170, 199, 219
src/kernels/archs.py 56 1 98% 94
src/kernels/backends.py 215 63 71% 42, 46, 50-53, 70, 92, 110, 119, 123, 127-129, 150, 159, 163, 167-169, 190, 201, 203, 210-213, 222, 226, 230-255, 263, 286-306
src/kernels/compat.py 9 1 89% 5
src/kernels/deps.py 70 1 99% 56
src/kernels/digest.py 58 5 91% 71-73, 136-137
src/kernels/hf_hub.py 62 2 97% 21, 23
src/kernels/importer.py 74 5 93% 158, 162, 165, 179-180
src/kernels/install.py 21 7 67% 76-100
src/kernels/layer/__init__.py 7 0 100%
src/kernels/layer/_interval_tree.py 103 4 96% 23, 52, 147, 150
src/kernels/layer/device.py 48 12 75% 42, 47-49, 91, 96-98, 101, 149, 152, 156
src/kernels/layer/func.py 93 5 95% 94, 119, 195, 315, 390
src/kernels/layer/globals.py 5 0 100%
src/kernels/layer/inspect.py 27 0 100%
src/kernels/layer/kernelize.py 83 7 92% 306, 314-315, 321, 325, 341-343
src/kernels/layer/layer.py 320 14 96% 212, 259, 447, 606-607, 628, 636, 647, 688, 692, 705, 760, 801, 876
src/kernels/layer/mode.py 14 0 100%
src/kernels/layer/repos.py 136 36 74% 32, 35-42, 73, 86, 90, 99-100, 103, 106-109, 116-117, 120, 123-126, 133-134, 137, 140-143, 150-151, 154, 157-160, 235
src/kernels/load.py 71 2 97% 338, 378
src/kernels/locking.py 89 64 28% 35-83, 91-98, 102-125, 137, 152-159, 165-175, 179-186
src/kernels/python_deps.py 58 6 90% 59-60, 64-65, 101, 104
src/kernels/resolver.py 162 1 99% 248
src/kernels/status.py 50 2 96% 25, 79
src/kernels/validate.py 104 7 93% 9, 101, 172, 195-196, 232-233
src/kernels/variants.py 278 17 94% 65, 96, 117, 147, 256-257, 299-302, 304, 388-394, 400-406, 455-461
src/kernels/verify.py 102 6 94% 44, 176-178, 266-267
TOTAL 2465 283 89%

Updated by the Test kernels workflow on commit b3c667eac2e1b49da7ced52b4f2514f40fc99849.

@sayakpaul

Copy link
Copy Markdown
Member

I know this is still draft but would be nice to substantiate this with a real example.

@drbh drbh changed the title Kernel port declarative feat: extend kernel port tool operations Oct 28, 2026
drbh added a commit to huggingface/kernels-community that referenced this pull request Oct 28, 2026
this generates `flash-attn3/src` from `flash-attn3/port/port.kdl` and
`flash-attn3/port/overlay`, pinned to the same upstream commit as the
hand port (`0816ef12`)

- the fixed `flash_attn_3` op namespace in `flash_api_stable.cpp` is
rewritten by `extension` and the module entry point now lives in
`torch-ext/torch_binding.cpp` (upstream's unused `PyInit__C` is kept)
- `flash_api.cpp` is no longer vendored since it was never compiled
- `flash_attn_interface.py` and the tests keep upstream's formatting and
dispatch through `flash_attn_3_gpu = ops`
- `build.toml` header lists come from `follow_includes`

note: check ports runs kernel-port from the `kernel-port-declarative`
branch until huggingface/kernels#946 is merged
@drbh

drbh commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator Author

just added an example that ports flash-attn3 huggingface/kernels-community#1283 it makes use of extension, rewrite, follow_includes=#true, mark_tests only= and the glob move that keeps #includes pointing at moved files, plus the post-port checks for relative imports and torch-ext declarations

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants