Op Slot Rules¶
The authoritative rule for each slot of a T2 (L1-direct) op file: S1-S7 and S12-S20. Slots S8-S11
belong to T1 thin-wrapper subclasses and are not covered here. Each entry gives the rule, an
example, and the mistakes that rule prevents. ops-design.md § Scaffolding an Op from a Manifest Entry walks the same slots in the order
you write them.
Examples use the fictional ExampleCumsumFwdOp; none mirrors a shipped file.
Contracts these rules emit against — base-class attributes, protocol variables, naming, parameter
design, calling conventions — live in
ops-design-reference.md.
Slot S1: Module docstring¶
- Rule. Open the file with a triple-quoted docstring: one-line module summary, then an optional
Provides:block listing<ClassName>: <one-line semantics>per concrete op. Template the semantics from manifestref_apiandsignature. - Example.
- Common mistakes. Naming tile sizes or kernel internals; omitting the one-line purpose.
Slot S2: Import — Kernel base class¶
- Rule.
from tileops.kernels.kernel_base import Kernel, wheneverkernel_typesis annotated. Never alias it, never re-export it.
Slot S3: Import — concrete Kernel class¶
- Rule. One absolute
from tileops.kernels.* import <KernelClass>perkernel_typesvalue. Import nothing thatkernel_typesdoes not list. - Example.
from tileops.kernels.reduction.example_cumsum import ExampleCumsumKernel - Common mistakes. A relative import.
Slot S4: Import — Op base class¶
- Rule.
from tileops.ops.op_base import Op. Every import is absolute; ruffTID252enforces it.
Slot S5: __all__¶
- Rule.
__all__ = ["<ClassName>"]— the concrete op from S6, and nothing else. Never re-export the Kernel class.
Slot S6: Class name¶
- Rule.
{PascalCaseName}{Direction}Op,Direction∈ {Fwd,Bwd}, no exceptions. The manifest entry key IS the class name, verbatim. - Common mistakes. Missing direction suffix; mis-cased abbreviation (see Naming Conventions).
Slot S7: Class docstring¶
- Rule. One-sentence summary, then what a caller acts on: the semantics and any deviation
from the reference. Optional
Example:block. NoArgs:block: the S12 kwargs are documented in the__init__docstring, derived from manifestsignature.paramsand the execution-policy parameters of S12. - Example.
- Common mistakes. An
Args:block here, whichscripts/lint/op_docstrings_lint.pyrejects; listing tensor inputs (they belong toforward); documenting adtypekwarg — there is none, dtype comes from the input atforward.
Slot S12: __init__ signature¶
- Rule.
signature.paramsentries in manifest key order; then*and any param declaringkw_only: true, followed by the execution-policy parameters of manifest.md table 7:targeton every op, then the op's injected implementation objects where it takes them. - Example.
- Common mistakes. Parameters with no manifest source; taking an input dtype as
dtypeorin_dtypewhen the tensors carry it; making a param keyword-only that the manifest does not declarekw_only; a tuning flag, which no constructor takes (request_tune()puts a constructed op in tuned mode).
Slot S13: __init__ body¶
-
Rule. Sequence: (a)
self.<name> = <name>per manifest parameter; (b)super().__init__(target=target), which stores the target, checks the manifest parameters and installs the implementations, resolving each kernel class without a tensor; (c) any derived attribute or sub-op, after the check. Construct no kernel and declare no cache here: the kernel is specialized by what the call carries, and L1 owns get-or-build (Kernel caching). -
Example (arbitrary-rank).
- Common mistakes. Hard-coding the kernel class instead of declaring it in
kernel_types; assigningself.targetby hand; callingsuper().__init__before the manifest parameters it checks are assigned; storingself.dtypeat ctor time; a private cache dict in place ofOp.kernel_for.
Slot S14: kernel_types¶
- Rule. A class attribute declaring the op's dispatch keys:
snake_casekeys, Kernel-class values. It is the one declaration of the in-tree keys, and every instance installs all of it; a construction parameter that decides which implementation serves a call travels in the call spec, where the implementations'refusalreads it. The code owns it; the manifest does not list kernels. Beside it,interfacesmaps each place the op calls a kernel to its kernel interface, and every key's class inherits one of them. - Example.
- Common mistakes. Keys that echo the class name instead of being dispatch strings; a construction parameter that selects keys at construction instead of travelling in the call spec; an interface per shape regime instead of one interface whose implementations split the regimes.
Slot S15: forward signature¶
- Rule. The parameter list starts with the signature's call-time inputs not declaring
kw_only, insignature.inputsorder, optional inputs defaulting toNone, followed byoutwhen an output declaresbuffer: out(manifest.md § Effects), then, after*, each input declaringkw_only: true, defaulting toNone(manifest.md § Presence). Code-defined execution parameters may follow. The return matchessignature.outputs: one tensor, a tuple in declared order,Nonein a nullable position whose expression is false, andNonewhenoutputsis empty. - Common mistakes. A keyword-only tensor parameter the manifest does not declare
kw_only, or akw_onlyinput left positional; non-tensor contract parameters, which belong to__init__.
Slot S16: forward body¶
- Rule. The checks generated from the signature have run before the body. Sequence: (a)
normalize parameter-dependent axes with the manifest's axis rule
(
dim = normalize_axis(self.dim, x.ndim), which maps0and-1to the scalar axis at rank 0); (b) make contiguous each input the kernel needs contiguous, never amutatedinput, which is written in place; (c)self.kernel_for(<interface>, <call>); (d) call what it returned with the parameters the interface's abstractforwarddeclares, in that order — output buffers included,Nonefor an absent optional one.forwardis the body for every op. An op with a compile boundary runs it inside the operator its__call__enters — that operator is generated and outside the scaffold's scope, see Compile Dispatch Boundary. - Derivation. The first argument is a key of
interfaces; the call is that interface's call spec. The interface's abstractforwardstates what the kernel is handed, so the op passes those parameters and neither restates nor reorders them. A specialization that implies more than a dtype — a compute dtype differing from the semantic one, an output dtype no input supplies — makes the entry one frozen record rather than a bare kernel, and those fields never live inself.*(Forward keying). - What the op does not do. It states no device requirement — the kernel it fetched does that — and it does not reshape for the kernel: rank reduction, padding and their inverses belong to the kernel's own call wrapper, so a backend is handed the shapes the manifest declares.
- Example (arbitrary-rank).
- Common mistakes. Building a kernel in a traced
forward; holding the returned entry onselfinstead of askingkernel_foron every call; keying on shape alone, so a second dtype reuses the first dtype's kernel; calling the returned kernel with arguments the interface'sforwarddoes not declare, or out of its order; a.is_cudacheck in the op; repeating a check the signature states; reshaping before the fetch; passing an already-built kernel where a factory is expected, which rebuilds on every call; fetching a kernel under two interfaces in one op where one entry holding both would do; an op-levelentry_for, where the implementation states its own.
Slot S17: _infer_output_shapes¶
- Rule. Generated from the signature; the op file does not define it.
- Common mistakes. A hand-written override, which can disagree with the signature.
Slot S18: Signature check¶
- Rule. Generated from the signature, its dtype expressions and
dtype_combosamong it; it runs on every call and is the only place an op rejects a dtype. The op file does not define it. - Common mistakes. A hand-written override, or an inline dtype check in
forward.
Slot S19: eval_roofline¶
- Rule. Generated from the
rooflinefield over theindicesof the op's last call; seeroofline.md§4.4. It is defined only after at least oneforward(). - Common mistakes. Class-level roofline expression strings parsed at runtime, any
ast.parseor shared_safe_evalpath — prohibited byroofline.md§4.4.5; returningfloatornumpytypes when the contract istuple[int, int].
Slot S20: Package __init__.py registration¶
- Rule. Two imports, each with a matching
__all__entry: onefrom tileops.ops.{family}.<module> import <ClassName>insrc/tileops/ops/{family}/__init__.py, under the family's grouping comment, and onefrom tileops.ops.{family} import <ClassName>insrc/tileops/{family}.py, which is the public path. - Example.
- Common mistakes. Import placed outside its grouping comment; missing
__all__entry, which silently breaksimport *; registering only the implementation package, which leaves the op unreachable fromtileops.{family}.