feat(language): integrate hand-written C++ kernels via external_source#1955
Conversation
Allow an existing hand-written AICore kernel to be called from a PyPTO orchestration by declaring only its signature. A DSL function marked @pl.function(type=AIC/AIV, external_source=...) with an empty `...` body skips PyPTO tile codegen: the compiler assigns it a func_id, emits the orchestration submit as usual, and compiles the referenced .cpp as the InCore kernel. A mixed AIC+AIV kernel is a Group of one AIC + one AIV external member, dispatched as a single MixedKernels submit. Also exposed under @pl.jit via @pl.jit.extern (single core, or core_type="mixed" which auto-expands to the AIC+AIV+Group form); the referenced .cpp content participates in the JIT cache key so editing the kernel triggers recompilation. - decorator/parser: external_source kwarg + path resolution; header-only (empty-body) parse with AIC/AIV validation - verifier: exempt external kernels from ReturnParamsExplicit (a header has no ReturnStmt) -- the only pass invariant an empty body violates - backend: skip ptoas for external kernels, copy the .cpp into kernels/<ct>/<name>.cpp so kernel_config.py lists it like a generated one - jit: @pl.jit.extern decorator + specializer rendering + source hash - tests: @pl.program and @pl.jit unit tests; on-device ST (a2a3sim + a2a3) - docs: en + zh external-kernel integration guide
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR adds support for integrating hand-written external C++ kernels into PyPTO via a new ChangesExternal Kernel Feature
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Program as pl.program
participant Parser as ASTParser
participant Backend as pto_backend
participant FS as Filesystem
Program->>Parser: parse_function(external_source=...)
Parser->>Parser: validate AIC/AIV + empty body
Parser-->>Backend: func_attrs with EXTERNAL_SOURCE_ATTR
Backend->>FS: read referenced .cpp
Backend->>FS: copy to kernels/<core_type>/<name>.cpp
Backend-->>Backend: skip PTOCodegen and ptoas
sequenceDiagram
participant User as Caller
participant JIT as jit.extern decorator
participant Spec as Specializer
participant Ctx as SpecializeContext
User->>JIT: `@pl.jit.extern`(core_type, source/aic_source/aiv_source)
JIT->>JIT: resolve and validate source paths
JIT->>Ctx: build_specialize_context(external_core_type, ...)
Ctx->>Spec: _specialize_function(func_type="extern")
Spec->>Spec: _render_external() emits `@pl.function`(external_source=...)
Suggested labels: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Warning Gemini encountered an error creating the review. You can try again by commenting |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 11bace40cd
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
- pyright: assert external kernel source(s) non-None in the specializer's _render_external so `str | None` narrows to `str` - pyright: assert pl.parse(...) returns ir.Program before run_passes in the JIT extern test - ruff PLC0415: hoist in-function imports to top level in the JIT extern test - ruff PLR0913: mark build_specialize_context (pass-through assembler) noqa - cpplint build/include_subdir: NOLINT the runtime headers in the ST kernel fixture (they resolve via the runtime include path) - ruff-format / clang-format: apply formatting
- pyright: assert external kernel source(s) non-None in the specializer's _render_external so `str | None` narrows to `str` - pyright: assert pl.parse(...) returns ir.Program before run_passes in the JIT extern test - ruff PLC0415: hoist in-function imports to top level in the JIT extern test - ruff PLR0913: mark build_specialize_context (pass-through assembler) noqa - cpplint build/include_subdir: NOLINT the runtime headers in the ST kernel fixture (they resolve via the runtime include path) - ruff-format / clang-format: apply formatting
b9b4d9f to
4bc8e90
Compare
…ile sources External kernels are now referenced at their original .cpp path in the kernel_config.py manifest instead of being copied into kernels/<ct>/<name>.cpp. Copying a lone entry file broke multi-file kernels whose entry .cpp pulls in siblings via relative includes (e.g. #include "../kernel/impl.cce" -> "../tiling/params.h") — the copy lands in a different directory so the relative paths no longer resolve. Referencing in place keeps the entry file beside its siblings, so the whole relative-include chain compiles. Verified: a kernel whose entry includes a sibling header across a ../dir compiles to a valid binary via the runtime's compile_incore. - pto_backend: thread func_name_to_external_source into _generate_config_file; emit the original path as source; drop the copy (_emit_external_kernel) - test: assert reference-in-place (no copied file; manifest points at original) - docs (en/zh): document multi-file kernel support
The external-kernel ST now splits FLOATS_PER_CACHE_LINE into a sibling kernels/common/cacheline_offset.h that the entry kernels/aiv/spmd_write.cpp pulls in via a relative "../common/..." include. This exercises multi-file external kernels end-to-end (the entry .cpp is referenced at its original path, so the sibling header resolves) on a2a3sim and real a2a3. - harness: the "No kernels generated" guard now also accepts kernels listed in kernel_config.py, since external kernels are referenced (not generated as files under kernels/)
- system-tests: the fused-compile path (_compile_for_cache) had a second "No kernels generated" guard that also needs to accept manifest-referenced external kernels (the _run_inline copy was already relaxed) - unit-tests/a5sim: scikit-build-core 1.0 removed cmake.minimum-version; migrate to cmake.version = ">=3.15" so the wheel builds - typing: add external_source (and the already-missing attrs/auto_scope) to the @pl.function overloads in decorator.pyi - jit: recompute the source hash on every lookup when an extern dep is present, since the referenced .cpp is mutable on disk (Python source is not)
Hzfengsy
left a comment
There was a problem hiding this comment.
Approved. Review threads resolved; both P2 bot comments addressed by the author (stub + source-hash recomputation).
Summary
Adds a way to call an existing hand-written AICore kernel from a PyPTO orchestration by declaring only its signature — no PyPTO tile codegen for the kernel body.
A DSL function marked
@pl.function(type=AIC/AIV, external_source="...cpp")with an empty...body is a header-only declaration: the compiler assigns it afunc_id, emits the orchestration submit exactly as for a DSL kernel, and compiles the referenced.cppas the InCore kernel. A mixed AIC+AIV kernel is apl.FunctionType.Groupof one AIC + one AIV external member, dispatched as a singleMixedKernelssubmit.Also exposed under
@pl.jitvia@pl.jit.extern(single core, orcore_type="mixed"which auto-expands to the AIC+AIV+Group form). The referenced.cppcontent participates in the JIT cache key, so editing the kernel triggers recompilation.The hand-written kernel needs no adaptation: it already exports the same fixed
extern "C" void kernel_entry(__gm__ int64_t*)entry PyPTO-generated kernels use (runtime dispatches byfunc_id), so its.cppis copied verbatim intokernels/<ct>/<name>.cppand listed inkernel_config.pyidentically to a generated kernel.Changes
external_sourcekwarg + path resolution; header-only (empty...) parse with AIC/AIV validation.ReturnParamsExplicit(a header has noReturnStmt) — the only pass invariant an empty body violates; all other passes no-op on an empty body..cppintokernels/<ct>/<name>.cpp.@pl.jit.externdecorator, specializer rendering (single + mixed auto-group), and.cppcontent folded into the source hash.Testing
tests/ut/codegen/test_external_kernel.py(5) +tests/ut/jit/test_extern_kernel.py(6) — manifest, MixedKernels submit, error cases, JIT specialization, cache invalidation.tests/st/runtime/external_kernel/drives a hand-written AIV kernel across 4 SPMD blocks — passes on both a2a3sim and real a2a3 via the standard harness.