"""Phase 1 spec tests for ADR-0065 P3 — flat-ops PE_SCHEDULER plan. P3 rewrites tile-plan generation to scan the flat ops list for the GEMM (≤1) and place pre-GEMM KERNEL ops as single-shot prologue MATH stages (ADR-0065 D3). A new tiling entry ``generate_plan_from_ops(ops, ...)`` produces a ``PipelinePlan`` with ``prologue_stages`` / ``epilogue_stages``. DMA insertion keeps the existing **``pinned``** signal (already-in-TCM → skip DMA; not-pinned → stream from HBM), NOT a ``space`` flip — so existing bench Stage sequences stay byte-equal. Recipe TCM scratch / primary-out handles are marked ``pinned=True`` so the head GEMM's auto-bound ``a`` (= the recipe's ``P``) is consumed in place, while ``b`` (a ``tl.ref``) is streamed. These tests target the tiling entry directly (no engine), building OpSpecs by hand so each operand's ``pinned`` flag is controlled precisely. Phase 1 (this commit): tests only. FAIL until P3: - ``generate_plan_from_ops`` does not exist, - ``PipelinePlan`` has no ``prologue_stages`` field. """ from __future__ import annotations import math from kernbench.common.pe_commands import OpSpec, Scope, TensorHandle PE = "sip0.cube0.pe0" TM = TK = TN = 64 def _h(addr: int, shape: tuple[int, ...], pinned: bool = False) -> TensorHandle: return TensorHandle( id=f"h{addr:x}", addr=addr, shape=shape, dtype="f16", nbytes=2 * math.prod(shape), space="tcm", pinned=pinned, ) def _gemm_op(a, b, out) -> OpSpec: return OpSpec( kind="gemm", scope=Scope.OUTPUT_TILE, operands={"a": a, "b": b}, out=out, extra={"m": a.shape[-2], "k": a.shape[-1], "n": b.shape[-1]}, ) def _plan(ops): from kernbench.components.builtin.tiling import generate_plan_from_ops return generate_plan_from_ops( ops=tuple(ops), tile_m=TM, tile_k=TK, tile_n=TN, bytes_per_element=2, pe_prefix=PE, ) def _stage_types(stages): return [s.stage_type.name for s in stages] # ── DMA from the pinned flag (byte-equal with generate_gemm_plan) ───── def test_unpinned_operand_gets_dma_read(): from kernbench.components.builtin.pe_types import StageType a = _h(0x1000, (TM, TK), pinned=True) # already in TCM b = _h(0x2000, (TK, TN), pinned=False) # streamed from HBM out = _h(0x3000, (TM, TN)) tile = _plan([_gemm_op(a, b, out)]).tiles[0] reads = [s for s in tile.stages if s.stage_type == StageType.DMA_READ] assert len(reads) == 1, _stage_types(tile.stages) # only B def test_pinned_operands_skip_dma(): from kernbench.components.builtin.pe_types import StageType a = _h(0x1000, (TM, TK), pinned=True) b = _h(0x2000, (TK, TN), pinned=True) out = _h(0x3000, (TM, TN)) tile = _plan([_gemm_op(a, b, out)]).tiles[0] assert not [s for s in tile.stages if s.stage_type == StageType.DMA_READ] # ── prologue placement (ADR-0065 D3) ───────────────────────────────── def test_recipe_prologue_math_ops_become_prologue_stages(): from kernbench.components.builtin.pe_types import StageType # 8 KERNEL-scope MATH ops, then the head GEMM whose `a` is the recipe's # primary-out P (pinned → in place) and `b` is a streamed ref. sc = _h(0x100000, (8, 64), pinned=True) pre = [ OpSpec(kind=k, scope=Scope.KERNEL, operands={"src": sc}, out=sc) for k in ("rmax", "max_elem", "exp_diff", "exp_diff", "rsum", "fma", "mul_bcast", "copy") ] P = _h(0x100400, (8, 64), pinned=True) # primary-out, in TCM V = _h(0x2000, (64, 128), pinned=False) # streamed out = _h(0x3000, (8, 128)) plan = _plan([*pre, _gemm_op(P, V, out)]) # 8 single-shot MATH prologue stages, none of them DMA. assert len(plan.prologue_stages) == 8, _stage_types(plan.prologue_stages) assert all(s.stage_type == StageType.MATH for s in plan.prologue_stages) assert not any(s.stage_type in (StageType.DMA_READ, StageType.DMA_WRITE) for s in plan.prologue_stages) # Head GEMM drives the tile loop; only V (not pinned) gets a DMA_READ. reads = [s for s in plan.tiles[0].stages if s.stage_type == StageType.DMA_READ] assert len(reads) == 1, _stage_types(plan.tiles[0].stages) def test_math_only_composite_has_no_gemm_tiles(): """A legacy MATH composite (no GEMM op) → math tile plan, no GEMM stage. Shaped like ``tl.composite(op="math", a=..., math_op="exp")`` — head operand keyed ``"a"`` with the op kind in ``extra["math_op"]``.""" from kernbench.components.builtin.pe_types import StageType src = _h(0x1000, (TM, TN), pinned=False) out = _h(0x3000, (TM, TN)) op = OpSpec(kind="math", scope=Scope.OUTPUT_TILE, operands={"a": src}, extra={"math_op": "exp"}, out=out) plan = _plan([op]) all_stages = [] for t in plan.tiles: all_stages.extend(t.stages) assert plan.tiles, "math-only composite must still produce tiles" assert not any(s.stage_type == StageType.GEMM for s in all_stages)