Skip to content

Fix BackendImplementationMissing: repair codegen registration skipped by circular-import partial cache - #3286

Open
mengluy0125 wants to merge 2 commits into
pytorch:mainfrom
mengluy0125:export-D114782445
Open

Fix BackendImplementationMissing: repair codegen registration skipped by circular-import partial cache#3286
mengluy0125 wants to merge 2 commits into
pytorch:mainfrom
mengluy0125:export-D114782445

Conversation

@mengluy0125

Copy link
Copy Markdown
Contributor

Summary:
Under torch.compile in packaged runtimes (e.g. aps_ads_vm training), Helion kernels
could fail at pt2_warmup with BackendImplementationMissing: Backend 'triton' is missing required implementation: codegen for API function load.

Root cause: import_backend_codegen() registers each backend's per-op codegen by
importlib.import_module("<pkg>._codegen_modules"). If that codegen module (or one
of its leaf modules such as triton/memory_ops.py) is already present in
sys.modules in a partially-initialized state -- a circular import during package
init cached it before its module-scope _decorators.codegen / register_codegen
handlers ran -- import_module returns the incomplete module and the registrations
are silently skipped, leaving APIFunc._codegen empty for that backend. This is
import-order dependent (nondeterministic) and surfaces only at codegen time.

Fix:

  • CodegenDict.__missing__: when a backend codegen key is absent, attempt a
    one-time global repair (imports are settled by codegen time) and retry before
    raising KeyError.
  • backend_registry.repair_backend_codegen(): reload each backend's codegen leaf
    modules so their registration decorators re-run.
  • Make codegen registration idempotent (_decorators.codegen,
    aten_lowering.register_codegen) so the repair reload cannot trip the
    "already registered" assert.

Differential Revision: D114782445

Summary:

`_find_device` scans kernel arguments for a `torch.device`/`torch.Tensor` to determine the launch device, recursing into nested `tuple`/`list`/`dict` containers. The recursion passed each element directly as `_find_device(item)`.

`_find_device` treats its argument as a tuple of args and iterates it (`for arg in args`). Passing a bare `item` that is itself a `torch.Tensor` made the recursive call iterate over the tensor (`for arg in tensor`), materializing the rows of the tensor on the host before the first row matched the `isinstance(arg, torch.Tensor)` check -- a real per-call host overhead on the hot argument-processing path. A 0-dim scalar tensor also fails to iterate at all.

Wrap the recursed element in a single-element tuple, `_find_device((item,))`, so the recursion sees exactly one arg and hits the `torch.Tensor` fast path directly. Behavior is unchanged for every previously-working case; the tensor is no longer iterated.

Differential Revision: D114780871
… by circular-import partial cache

Summary:
Under torch.compile in packaged runtimes (e.g. aps_ads_vm training), Helion kernels
could fail at pt2_warmup with `BackendImplementationMissing: Backend 'triton' is
missing required implementation: codegen for API function load`.

Root cause: `import_backend_codegen()` registers each backend's per-op codegen by
`importlib.import_module("<pkg>._codegen_modules")`. If that codegen module (or one
of its leaf modules such as `triton/memory_ops.py`) is already present in
`sys.modules` in a partially-initialized state -- a circular import during package
init cached it before its module-scope `_decorators.codegen` / `register_codegen`
handlers ran -- `import_module` returns the incomplete module and the registrations
are silently skipped, leaving `APIFunc._codegen` empty for that backend. This is
import-order dependent (nondeterministic) and surfaces only at codegen time.

Fix:
- `CodegenDict.__missing__`: when a backend codegen key is absent, attempt a
  one-time global repair (imports are settled by codegen time) and retry before
  raising KeyError.
- `backend_registry.repair_backend_codegen()`: reload each backend's codegen leaf
  modules so their registration decorators re-run.
- Make codegen registration idempotent (`_decorators.codegen`,
  `aten_lowering.register_codegen`) so the repair reload cannot trip the
  "already registered" assert.

Differential Revision: D114782445
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Aug 5, 2026
@meta-codesync

meta-codesync Bot commented Aug 5, 2026

Copy link
Copy Markdown

@mengluy0125 has exported this pull request. If you are a Meta employee, you can view the originating Diff in D114782445.

@ethche ethche 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.

Looks good but marked some areas of concern. Could you take a look?

I asked codex, and it suggested something like this:

  • Add a locked ensure_backend_codegen() in backend_registry.
  • Set its completion flag only after successful reload.
  • Call it before falling back to "common" in CodegenDict.
  • Call it when AtenLowering cannot find a backend handler, then retry.

def __missing__(self, key: str) -> Callable[[CodegenState], object]:
if key != "common" and "common" in self:
return self["common"]
# A backend codegen can be absent because its codegen module was cached

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 repair happen before falling back to common? Otherwise a missing backend-specific registration silently uses the common implementation and never triggers repair. Maybe double check

assert backend not in self.codegen_impls, (
f"codegen already registered for backend {backend!r}"
)
# Idempotent: repair_backend_codegen() may reload this codegen module

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.

What triggers repair when an Aten codegen registration is missing?

# this dict empty). By codegen time imports are settled, so force-complete
# the registrations once and retry before giving up.
if key != "common" and not CodegenDict._repaired:
CodegenDict._repaired = True

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.

Should _repaired be set only after repair succeeds, with synchronization? As written, an exception or concurrent lookup permanently skips repair.

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 Meta Open Source bot. meta-exported

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants